1 using System; 2 using System.Collections.Generic; 3 using OpenBveApi.Math; 4 using OpenBveApi.Sounds; 5 using OpenTK.Audio.OpenAL; 6 using SoundManager; 7 8 namespace TrainEditor2.Audio 9 { 10 internal partial class SoundApi 11 { UpdateLinearModel(double timeElapsed)12 protected override void UpdateLinearModel(double timeElapsed) 13 { 14 throw new NotImplementedException(); 15 } 16 17 /// <summary>Updates the sound component. Should be called every frame.</summary> 18 /// <param name="timeElapsed">The time in seconds that elapsed since the last call to this function.</param> UpdateInverseModel(double timeElapsed)19 protected override void UpdateInverseModel(double timeElapsed) 20 { 21 /* 22 * Set up the listener. 23 * */ 24 Vector3 listenerPosition = Vector3.Zero; 25 Orientation3 listenerOrientation = new Orientation3(Vector3.Right, Vector3.Up, Vector3.Forward); 26 Vector3 listenerVelocity = Vector3.Zero; 27 AL.Listener(ALListener3f.Position, 0.0f, 0.0f, 0.0f); 28 AL.Listener(ALListener3f.Velocity, (float)listenerVelocity.X, (float)listenerVelocity.Y, (float)listenerVelocity.Z); 29 float[] Orientation = new float[] { (float)listenerOrientation.Z.X, (float)listenerOrientation.Z.Y, (float)listenerOrientation.Z.Z, -(float)listenerOrientation.Y.X, -(float)listenerOrientation.Y.Y, -(float)listenerOrientation.Y.Z }; 30 AL.Listener(ALListenerfv.Orientation, ref Orientation); 31 32 /* 33 * Collect all sounds that are to be played 34 * and ensure that all others are stopped. 35 * */ 36 List<SoundSourceAttenuation> toBePlayed = new List<SoundSourceAttenuation>(); 37 for (int i = 0; i < SourceCount; i++) 38 { 39 if (Sources[i].State == SoundSourceState.StopPending) 40 { 41 /* 42 * The sound is still playing but is to be stopped. 43 * Stop the sound, then remove it from the list of 44 * sound sources. 45 * */ 46 AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); 47 Sources[i].State = SoundSourceState.Stopped; 48 Sources[i].OpenAlSourceName = 0; 49 Sources[i] = Sources[SourceCount - 1]; 50 SourceCount--; 51 i--; 52 } 53 else if (Sources[i].State == SoundSourceState.Stopped) 54 { 55 /* 56 * The sound was already stopped. Remove it from 57 * the list of sound sources. 58 * */ 59 Sources[i] = Sources[SourceCount - 1]; 60 SourceCount--; 61 i--; 62 } 63 else if (GlobalMute) 64 { 65 /* 66 * The sound is playing or about to be played, but 67 * the global mute option is enabled. Stop the sound 68 * sound if necessary, then remove it from the list 69 * of sound sources if the sound is not looping. 70 * */ 71 if (Sources[i].State == SoundSourceState.Playing) 72 { 73 AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); 74 Sources[i].State = SoundSourceState.PlayPending; 75 Sources[i].OpenAlSourceName = 0; 76 } 77 if (!Sources[i].Looped) 78 { 79 Sources[i].State = SoundSourceState.Stopped; 80 Sources[i].OpenAlSourceName = 0; 81 Sources[i] = Sources[SourceCount - 1]; 82 SourceCount--; 83 i--; 84 } 85 } 86 else 87 { 88 /* 89 * The sound is to be played or is already playing. 90 * */ 91 if (Sources[i].State == SoundSourceState.Playing) 92 { 93 int state; 94 AL.GetSource(Sources[i].OpenAlSourceName, ALGetSourcei.SourceState, out state); 95 if (state != (int)ALSourceState.Initial & state != (int)ALSourceState.Playing) 96 { 97 /* 98 * The sound is not playing any longer. 99 * Remove it from the list of sound sources. 100 * */ 101 AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); 102 Sources[i].State = SoundSourceState.Stopped; 103 Sources[i].OpenAlSourceName = 0; 104 Sources[i] = Sources[SourceCount - 1]; 105 SourceCount--; 106 i--; 107 continue; 108 } 109 } 110 111 /* 112 * Calculate the gain, then add the sound 113 * to the list of sounds to be played. 114 * */ 115 Vector3 position = Sources[i].Position; 116 Vector3 positionDifference = position - listenerPosition; 117 double distance = positionDifference.Norm(); 118 double radius = Sources[i].Radius; 119 double gain; 120 if (distance < 2.0 * radius) 121 { 122 gain = 1.0 - distance * distance * (4.0 * radius - distance) / (16.0 * radius * radius * radius); 123 } 124 else 125 { 126 gain = radius / distance; 127 } 128 gain *= Sources[i].Volume; 129 if (gain <= 0.0) 130 { 131 /* 132 * The gain is too low. Stop the sound if playing, 133 * but keep looping sounds pending. 134 * */ 135 if (Sources[i].State == SoundSourceState.Playing) 136 { 137 AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); 138 Sources[i].State = SoundSourceState.PlayPending; 139 Sources[i].OpenAlSourceName = 0; 140 } 141 if (!Sources[i].Looped) 142 { 143 Sources[i].State = SoundSourceState.Stopped; 144 Sources[i].OpenAlSourceName = 0; 145 Sources[i] = Sources[SourceCount - 1]; 146 SourceCount--; 147 i--; 148 } 149 } 150 else 151 { 152 /* 153 * Add the source. 154 * */ 155 toBePlayed.Add(new SoundSourceAttenuation(Sources[i], gain, distance)); 156 } 157 } 158 } 159 160 /* 161 * Now that we have the list of sounds that are to be played, 162 * sort them by their gain so that we can determine and 163 * adjust the clamp factor. 164 * */ 165 double clampFactor = Math.Exp(LogClampFactor); 166 for (int i = 0; i < toBePlayed.Count; i++) 167 { 168 toBePlayed[i].Gain -= clampFactor * toBePlayed[i].Distance * toBePlayed[i].Distance; 169 } 170 toBePlayed.Sort(); 171 for (int i = 0; i < toBePlayed.Count; i++) 172 { 173 toBePlayed[i].Gain += clampFactor * toBePlayed[i].Distance * toBePlayed[i].Distance; 174 } 175 double desiredLogClampFactor; 176 int index = 16; 177 if (toBePlayed.Count <= index) 178 { 179 desiredLogClampFactor = MinLogClampFactor; 180 } 181 else 182 { 183 double cutoffDistance = toBePlayed[index].Distance; 184 if (cutoffDistance <= 0.0) 185 { 186 desiredLogClampFactor = MaxLogClampFactor; 187 } 188 else 189 { 190 double cutoffGain = toBePlayed[index].Gain; 191 desiredLogClampFactor = Math.Log(cutoffGain / (cutoffDistance * cutoffDistance)); 192 if (desiredLogClampFactor < MinLogClampFactor) 193 { 194 desiredLogClampFactor = MinLogClampFactor; 195 } 196 else if (desiredLogClampFactor > MaxLogClampFactor) 197 { 198 desiredLogClampFactor = MaxLogClampFactor; 199 } 200 } 201 } 202 const double rate = 3.0; 203 if (LogClampFactor < desiredLogClampFactor) 204 { 205 LogClampFactor += timeElapsed * rate; 206 if (LogClampFactor > desiredLogClampFactor) 207 { 208 LogClampFactor = desiredLogClampFactor; 209 } 210 } 211 else if (LogClampFactor > desiredLogClampFactor) 212 { 213 LogClampFactor -= timeElapsed * rate; 214 if (LogClampFactor < desiredLogClampFactor) 215 { 216 LogClampFactor = desiredLogClampFactor; 217 } 218 } 219 220 /* 221 * Play the sounds. 222 * */ 223 clampFactor = Math.Exp(LogClampFactor); 224 for (int i = index; i < toBePlayed.Count; i++) 225 { 226 toBePlayed[i].Gain = 0.0; 227 } 228 for (int i = 0; i < toBePlayed.Count; i++) 229 { 230 SoundSource source = toBePlayed[i].Source; 231 double gain = toBePlayed[i].Gain - clampFactor * toBePlayed[i].Distance * toBePlayed[i].Distance; 232 if (gain <= 0.0) 233 { 234 /* 235 * Stop the sound. 236 * */ 237 if (source.State == SoundSourceState.Playing) 238 { 239 AL.DeleteSources(1, ref source.OpenAlSourceName); 240 source.State = SoundSourceState.PlayPending; 241 source.OpenAlSourceName = 0; 242 } 243 if (!source.Looped) 244 { 245 source.State = SoundSourceState.Stopped; 246 source.OpenAlSourceName = 0; 247 } 248 } 249 else 250 { 251 /* 252 * Ensure the buffer is loaded, then play the sound. 253 * */ 254 if (source.State != SoundSourceState.Playing) 255 { 256 LoadBuffer(source.Buffer); 257 if (source.Buffer.Loaded) 258 { 259 AL.GenSources(1, out source.OpenAlSourceName); 260 AL.Source(source.OpenAlSourceName, ALSourcei.Buffer, source.Buffer.OpenAlBufferName); 261 } 262 else 263 { 264 /* 265 * We cannot play the sound because 266 * the buffer could not be loaded. 267 * */ 268 source.State = SoundSourceState.Stopped; 269 continue; 270 } 271 } 272 273 Vector3 position = source.Position; 274 Vector3 velocity = Vector3.Zero; 275 position -= listenerPosition; 276 AL.Source(source.OpenAlSourceName, ALSource3f.Position, (float)position.X, (float)position.Y, (float)position.Z); 277 AL.Source(source.OpenAlSourceName, ALSource3f.Velocity, (float)velocity.X, (float)velocity.Y, (float)velocity.Z); 278 AL.Source(source.OpenAlSourceName, ALSourcef.Pitch, (float)source.Pitch); 279 AL.Source(source.OpenAlSourceName, ALSourcef.Gain, (float)gain); 280 if (source.State != SoundSourceState.Playing) 281 { 282 AL.Source(source.OpenAlSourceName, ALSourceb.Looping, source.Looped); 283 AL.SourcePlay(source.OpenAlSourceName); 284 source.State = SoundSourceState.Playing; 285 } 286 } 287 } 288 } 289 } 290 } 291