/// <summary>Updates the sound component. Should be called every frame.</summary> /// <param name="timeElapsed">The time in seconds that elapsed since the last call to this function.</param> private static void UpdateLinearModel(double timeElapsed) { /* * Set up the listener * */ OpenBveApi.Math.Vector3 listenerPosition = World.AbsoluteCameraPosition; OpenBveApi.Math.Orientation3 listenerOrientation = new OpenBveApi.Math.Orientation3(World.AbsoluteCameraSide, World.AbsoluteCameraUp, World.AbsoluteCameraDirection); OpenBveApi.Math.Vector3 listenerVelocity = World.CameraAlignmentSpeed.Position; AL.Listener(ALListener3f.Position, 0.0f, 0.0f, 0.0f); AL.Listener(ALListener3f.Velocity, (float)listenerVelocity.X, (float)listenerVelocity.Y, (float)listenerVelocity.Z); var Orientation = new[] { (float)listenerOrientation.Z.X, (float)listenerOrientation.Z.Y, (float)listenerOrientation.Z.Z, -(float)listenerOrientation.Y.X, -(float)listenerOrientation.Y.Y, -(float)listenerOrientation.Y.Z }; AL.Listener(ALListenerfv.Orientation, ref Orientation); /* * Set up the atmospheric attributes * */ double elevation = World.AbsoluteCameraPosition.Y + Game.RouteInitialElevation; double airTemperature = Game.GetAirTemperature(elevation); double airPressure = Game.GetAirPressure(elevation, airTemperature); double speedOfSound = Game.GetSpeedOfSound(airPressure, airTemperature); try { AL.SpeedOfSound((float)speedOfSound); } catch { } /* * Update the sound sources * */ int actuallyPlaying = 0; for (int i = 0; i < SourceCount; i++) { if (Sources[i].State == SoundSourceState.StopPending) { /* * The sound is still playing but is to be stopped. * Stop the sound, then remove it from the list of * sound sources. * */ AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (Sources[i].State == SoundSourceState.Stopped) { /* * The sound was already stopped. Remove it from * the list of sound sources. * */ Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (GlobalMute) { /* * The sound is playing or about to be played, but * the global mute option is enabled. Stop the sound * sound if necessary, then remove it from the list * of sound sources if the sound is not looping. * */ if (Sources[i].State == SoundSourceState.Playing) { AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * The sound is to be played or is already playing. * Calculate the sound gain. * */ OpenBveApi.Math.Vector3 position; OpenBveApi.Math.Vector3 velocity; switch (Sources[i].Type) { case SoundType.TrainCar: OpenBveApi.Math.Vector3 direction; Sources[i].Train.Cars[Sources[i].Car].CreateWorldCoordinates(Sources[i].Position, out position, out direction); velocity = Sources[i].Train.Cars[Sources[i].Car].Specs.CurrentSpeed * direction; break; default: position = Sources[i].Position; velocity = OpenBveApi.Math.Vector3.Zero; break; } OpenBveApi.Math.Vector3 positionDifference = position - listenerPosition; double gain; if (GlobalMute) { gain = 0.0; } else { double distance = positionDifference.Norm(); double innerRadius = Sources[i].Radius; if (World.CameraMode == CameraViewMode.Interior | World.CameraMode == CameraViewMode.InteriorLookAhead) { if (Sources[i].Train != TrainManager.PlayerTrain || Sources[i].Car != TrainManager.PlayerTrain.DriverCar) { innerRadius *= 0.5; } } double outerRadius = OuterRadiusFactor * innerRadius; if (distance < outerRadius) { if (distance <= innerRadius) { gain = Sources[i].Volume; } else { gain = (distance - outerRadius) / (innerRadius - outerRadius); gain *= Sources[i].Volume; } gain = 3.0 * gain * gain - 2.0 * gain * gain * gain; } else { gain = 0.0; } } if (gain <= GainThreshold) { /* * If the gain is too low to be audible, stop the sound. * If the sound is not looping, stop it if necessary, * then remove it from the list of sound sources. * */ if (Sources[i].State == SoundSourceState.Playing) { AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * Play the sound and update position, velocity, pitch and gain. * For non-looping sounds, check if the sound is still playing. * */ gain = (gain - GainThreshold) / (1.0 - GainThreshold); if (Sources[i].State != SoundSourceState.Playing) { LoadBuffer(Sources[i].Buffer); if (Sources[i].Buffer.Loaded) { AL.GenSources(1, out Sources[i].OpenAlSourceName); AL.Source(Sources[i].OpenAlSourceName, ALSourcei.Buffer, Sources[i].Buffer.OpenAlBufferName); } else { /* * We cannot play the sound because * the buffer could not be loaded. * */ Sources[i].State = SoundSourceState.Stopped; continue; } } AL.Source(Sources[i].OpenAlSourceName, ALSource3f.Position, (float)positionDifference.X, (float)positionDifference.Y, (float)positionDifference.Z); AL.Source(Sources[i].OpenAlSourceName, ALSource3f.Velocity, (float)velocity.X, (float)velocity.Y, (float)velocity.Z); AL.Source(Sources[i].OpenAlSourceName, ALSourcef.Pitch, (float)Sources[i].Pitch); AL.Source(Sources[i].OpenAlSourceName, ALSourcef.Gain, (float)gain); if (Sources[i].State != SoundSourceState.Playing) { AL.Source(Sources[i].OpenAlSourceName, ALSourceb.Looping, Sources[i].Looped); AL.SourcePlay(Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Playing; } if (!Sources[i].Looped) { int state; AL.GetSource(Sources[i].OpenAlSourceName, ALGetSourcei.SourceState, out state); if (state != (int)ALSourceState.Initial & state != (int)ALSourceState.Playing) { /* * The sound is not playing any longer. * Remove it from the list of sound sources. * */ AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else { actuallyPlaying++; } } else { actuallyPlaying++; } } } } /* * Adjust the outer radius factor / the clamp factor. * */ if (actuallyPlaying >= Interface.CurrentOptions.SoundNumber - 2) { /* * Too many sounds are playing. * Reduce the outer radius factor. * */ OuterRadiusFactorSpeed -= timeElapsed; if (OuterRadiusFactorSpeed < -OuterRadiusFactorMaximumSpeed) { OuterRadiusFactorSpeed = -OuterRadiusFactorMaximumSpeed; } } else if (actuallyPlaying <= Interface.CurrentOptions.SoundNumber - 6) { /* * Only few sounds are playing. * Increase the outer radius factor. * */ OuterRadiusFactorSpeed += timeElapsed; if (OuterRadiusFactorSpeed > OuterRadiusFactorMaximumSpeed) { OuterRadiusFactorSpeed = OuterRadiusFactorMaximumSpeed; } } else { /* * Neither too many nor too few sounds are playing. * Stabilize the outer radius factor. * */ if (OuterRadiusFactorSpeed < 0.0) { OuterRadiusFactorSpeed += timeElapsed; if (OuterRadiusFactorSpeed > 0.0) { OuterRadiusFactorSpeed = 0.0; } } else { OuterRadiusFactorSpeed -= timeElapsed; if (OuterRadiusFactorSpeed < 0.0) { OuterRadiusFactorSpeed = 0.0; } } } OuterRadiusFactor += OuterRadiusFactorSpeed * timeElapsed; if (OuterRadiusFactor < OuterRadiusFactorMinimum) { OuterRadiusFactor = OuterRadiusFactorMinimum; OuterRadiusFactorSpeed = 0.0; } else if (OuterRadiusFactor > OuterRadiusFactorMaximum) { OuterRadiusFactor = OuterRadiusFactorMaximum; OuterRadiusFactorSpeed = 0.0; } }
/// <summary>Updates the sound component. Should be called every frame.</summary> /// <param name="timeElapsed">The time in seconds that elapsed since the last call to this function.</param> private static void UpdateInverseModel(double timeElapsed) { /* * Set up the listener. * */ OpenBveApi.Math.Vector3 listenerPosition = World.AbsoluteCameraPosition; OpenBveApi.Math.Orientation3 listenerOrientation = new OpenBveApi.Math.Orientation3(World.AbsoluteCameraSide, World.AbsoluteCameraUp, World.AbsoluteCameraDirection); OpenBveApi.Math.Vector3 listenerVelocity; if (World.CameraMode == CameraViewMode.Interior | World.CameraMode == CameraViewMode.InteriorLookAhead | World.CameraMode == CameraViewMode.Exterior) { TrainManager.Car car = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar]; OpenBveApi.Math.Vector3 diff = car.FrontAxle.Follower.WorldPosition - car.RearAxle.Follower.WorldPosition; if (diff.IsNullVector()) { listenerVelocity = car.Specs.CurrentSpeed * OpenBveApi.Math.Vector3.Forward; } else { listenerVelocity = car.Specs.CurrentSpeed * OpenBveApi.Math.Vector3.Normalize(diff); } } else { listenerVelocity = OpenBveApi.Math.Vector3.Zero; } AL.Listener(ALListener3f.Position, 0.0f, 0.0f, 0.0f); AL.Listener(ALListener3f.Velocity, (float)listenerVelocity.X, (float)listenerVelocity.Y, (float)listenerVelocity.Z); var 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 }; AL.Listener(ALListenerfv.Orientation, ref Orientation); /* * Set up the atmospheric attributes. * */ double elevation = World.AbsoluteCameraPosition.Y + Game.RouteInitialElevation; double airTemperature = Game.GetAirTemperature(elevation); double airPressure = Game.GetAirPressure(elevation, airTemperature); double speedOfSound = Game.GetSpeedOfSound(airPressure, airTemperature); try { AL.SpeedOfSound((float)speedOfSound); } catch { } /* * Collect all sounds that are to be played * and ensure that all others are stopped. * */ List <SoundSourceAttenuation> toBePlayed = new List <SoundSourceAttenuation>(); for (int i = 0; i < SourceCount; i++) { if (Sources[i].State == SoundSourceState.StopPending) { /* * The sound is still playing but is to be stopped. * Stop the sound, then remove it from the list of * sound sources. * */ AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (Sources[i].State == SoundSourceState.Stopped) { /* * The sound was already stopped. Remove it from * the list of sound sources. * */ Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (GlobalMute) { /* * The sound is playing or about to be played, but * the global mute option is enabled. Stop the sound * sound if necessary, then remove it from the list * of sound sources if the sound is not looping. * */ if (Sources[i].State == SoundSourceState.Playing) { AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * The sound is to be played or is already playing. * */ if (Sources[i].State == SoundSourceState.Playing) { int state; AL.GetSource(Sources[i].OpenAlSourceName, ALGetSourcei.SourceState, out state); if (state != (int)ALSourceState.Initial & state != (int)ALSourceState.Playing) { /* * The sound is not playing any longer. * Remove it from the list of sound sources. * */ AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; continue; } } /* * Calculate the gain, then add the sound * to the list of sounds to be played. * */ OpenBveApi.Math.Vector3 position; if (Sources[i].Train != null) { OpenBveApi.Math.Vector3 direction; Sources[i].Train.Cars[Sources[i].Car].CreateWorldCoordinates(Sources[i].Position, out position, out direction); } else { position = Sources[i].Position; } OpenBveApi.Math.Vector3 positionDifference = position - listenerPosition; double distance = positionDifference.Norm(); double radius = Sources[i].Radius; if (World.CameraMode == CameraViewMode.Interior | World.CameraMode == CameraViewMode.InteriorLookAhead) { if (Sources[i].Train != TrainManager.PlayerTrain || Sources[i].Car != TrainManager.PlayerTrain.DriverCar) { radius *= 0.5; } } double gain; if (distance < 2.0 * radius) { gain = 1.0 - distance * distance * (4.0 * radius - distance) / (16.0 * radius * radius * radius); } else { gain = radius / distance; } gain *= Sources[i].Volume; if (gain <= 0.0) { /* * The gain is too low. Stop the sound if playing, * but keep looping sounds pending. * */ if (Sources[i].State == SoundSourceState.Playing) { AL.DeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * Add the source. * */ toBePlayed.Add(new SoundSourceAttenuation(Sources[i], gain, distance)); } } } /* * Now that we have the list of sounds that are to be played, * sort them by their gain so that we can determine and * adjust the clamp factor. * */ double clampFactor = Math.Exp(LogClampFactor); for (int i = 0; i < toBePlayed.Count; i++) { toBePlayed[i].Gain -= clampFactor * toBePlayed[i].Distance * toBePlayed[i].Distance; } toBePlayed.Sort(); for (int i = 0; i < toBePlayed.Count; i++) { toBePlayed[i].Gain += clampFactor * toBePlayed[i].Distance * toBePlayed[i].Distance; } double desiredLogClampFactor; int index = Interface.CurrentOptions.SoundNumber; if (toBePlayed.Count <= index) { desiredLogClampFactor = MinLogClampFactor; } else { double cutoffDistance = toBePlayed[index].Distance; if (cutoffDistance <= 0.0) { desiredLogClampFactor = MaxLogClampFactor; } else { double cutoffGain = toBePlayed[index].Gain; desiredLogClampFactor = Math.Log(cutoffGain / (cutoffDistance * cutoffDistance)); if (desiredLogClampFactor < MinLogClampFactor) { desiredLogClampFactor = MinLogClampFactor; } else if (desiredLogClampFactor > MaxLogClampFactor) { desiredLogClampFactor = MaxLogClampFactor; } } } const double rate = 3.0; if (LogClampFactor < desiredLogClampFactor) { LogClampFactor += timeElapsed * rate; if (LogClampFactor > desiredLogClampFactor) { LogClampFactor = desiredLogClampFactor; } } else if (LogClampFactor > desiredLogClampFactor) { LogClampFactor -= timeElapsed * rate; if (LogClampFactor < desiredLogClampFactor) { LogClampFactor = desiredLogClampFactor; } } /* * Play the sounds. * */ clampFactor = Math.Exp(LogClampFactor); for (int i = index; i < toBePlayed.Count; i++) { toBePlayed[i].Gain = 0.0; } for (int i = 0; i < toBePlayed.Count; i++) { SoundSource source = toBePlayed[i].Source; double gain = toBePlayed[i].Gain - clampFactor * toBePlayed[i].Distance * toBePlayed[i].Distance; if (gain <= 0.0) { /* * Stop the sound. * */ if (source.State == SoundSourceState.Playing) { AL.DeleteSources(1, ref source.OpenAlSourceName); source.State = SoundSourceState.PlayPending; source.OpenAlSourceName = 0; } if (!source.Looped) { source.State = SoundSourceState.Stopped; source.OpenAlSourceName = 0; } } else { /* * Ensure the buffer is loaded, then play the sound. * */ if (source.State != SoundSourceState.Playing) { LoadBuffer(source.Buffer); if (source.Buffer.Loaded) { AL.GenSources(1, out source.OpenAlSourceName); AL.Source(source.OpenAlSourceName, ALSourcei.Buffer, source.Buffer.OpenAlBufferName); } else { /* * We cannot play the sound because * the buffer could not be loaded. * */ source.State = SoundSourceState.Stopped; continue; } } OpenBveApi.Math.Vector3 position; OpenBveApi.Math.Vector3 velocity; if (source.Train != null) { OpenBveApi.Math.Vector3 direction; source.Train.Cars[source.Car].CreateWorldCoordinates(source.Position, out position, out direction); velocity = source.Train.Cars[source.Car].Specs.CurrentSpeed * direction; } else { position = source.Position; velocity = OpenBveApi.Math.Vector3.Zero; } position -= listenerPosition; AL.Source(source.OpenAlSourceName, ALSource3f.Position, (float)position.X, (float)position.Y, (float)position.Z); AL.Source(source.OpenAlSourceName, ALSource3f.Velocity, (float)velocity.X, (float)velocity.Y, (float)velocity.Z); AL.Source(source.OpenAlSourceName, ALSourcef.Pitch, (float)source.Pitch); AL.Source(source.OpenAlSourceName, ALSourcef.Gain, (float)gain); if (source.State != SoundSourceState.Playing) { AL.Source(source.OpenAlSourceName, ALSourceb.Looping, source.Looped); AL.SourcePlay(source.OpenAlSourceName); source.State = SoundSourceState.Playing; } } } }
// update internal static void Update(double TimeElapsed) { if (OpenAlContext != ContextHandle.Zero) { // listener double vx = World.CameraTrackFollower.WorldDirection.X * World.CameraSpeed; double vy = World.CameraTrackFollower.WorldDirection.Y * World.CameraSpeed; double vz = World.CameraTrackFollower.WorldDirection.Z * World.CameraSpeed; if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) { ListenerVelocity[0] = 0.0f; ListenerVelocity[1] = 0.0f; ListenerVelocity[2] = 0.0f; } else { ListenerVelocity[0] = (float)vx; ListenerVelocity[1] = (float)vy; ListenerVelocity[2] = (float)vz; } ListenerOrientation[0] = (float)World.AbsoluteCameraDirection.X; ListenerOrientation[1] = (float)World.AbsoluteCameraDirection.Y; ListenerOrientation[2] = (float)World.AbsoluteCameraDirection.Z; ListenerOrientation[3] = (float)-World.AbsoluteCameraUp.X; ListenerOrientation[4] = (float)-World.AbsoluteCameraUp.Y; ListenerOrientation[5] = (float)-World.AbsoluteCameraUp.Z; AL.Listener(ALListener3f.Position, ListenerPosition[0], ListenerPosition[1], ListenerPosition[2]); AL.Listener(ALListener3f.Velocity, ListenerVelocity[0], ListenerVelocity[1], ListenerVelocity[2]); AL.Listener(ALListenerfv.Orientation, ref ListenerOrientation); double cx = World.AbsoluteCameraPosition.X; double cy = World.AbsoluteCameraPosition.Y; double cz = World.AbsoluteCameraPosition.Z; if (Mute) { // mute for (int i = 0; i < SoundSources.Length; i++) { if (SoundSources[i] != null && !SoundSources[i].FinishedPlaying) { if (!SoundSources[i].Suppressed) { if (SoundSources[i].Looped) { if (SoundSources[i].OpenAlSourceIndex.Valid) { int j = SoundSources[i].OpenAlSourceIndex.Index; AL.SourceStop(j); AL.DeleteSources(1, ref j); } SoundSources[i].OpenAlSourceIndex = new OpenAlIndex(0, false); SoundSources[i].Suppressed = true; } else { StopSound(i, false); } } else if (!SoundSources[i].Looped) { StopSound(i, false); } } } } else { // outer radius int n = Interface.CurrentOptions.SoundNumber - 3; if (SoundsActuallyPlaying >= n) { OuterRadiusSpeed -= OuterRadiusDeceleration * TimeElapsed; if (OuterRadiusSpeed < -1.0) { OuterRadiusSpeed = -1.0; } } else if (SoundsQueriedPlaying < n) { OuterRadiusSpeed += OuterRadiusAcceleration * TimeElapsed; if (OuterRadiusSpeed > 1.0) { OuterRadiusSpeed = 1.0; } } else { OuterRadiusSpeed -= (double)Math.Sign(OuterRadiusSpeed) * OuterRadiusDeceleration * TimeElapsed; if (OuterRadiusSpeed * OuterRadiusSpeed <= TimeElapsed * TimeElapsed) { OuterRadiusSpeed = 0.0; } } OuterRadiusFactor += OuterRadiusSpeed * TimeElapsed; if (OuterRadiusFactor < OuterRadiusFactorMinimum) { OuterRadiusFactor = OuterRadiusFactorMinimum; } else if (OuterRadiusFactor > OuterRadiusFactorMaximum) { OuterRadiusFactor = OuterRadiusFactorMaximum; } // sources SoundsQueriedPlaying = 0; SoundsActuallyPlaying = 0; for (int i = 0; i < SoundSources.Length; i++) { if (SoundSources[i] != null && !SoundSources[i].FinishedPlaying) { double rx = SoundSources[i].Position.X; double ry = SoundSources[i].Position.Y; double rz = SoundSources[i].Position.Z; double px, py, pz; if (SoundSources[i].Train != null) { int c = SoundSources[i].CarIndex; double tx, ty, tz; TrainManager.CreateWorldCoordinates(SoundSources[i].Train, c, rx, ry, rz, out px, out py, out pz, out tx, out ty, out tz); px -= cx; py -= cy; pz -= cz; double sp = SoundSources[i].Train.Specs.CurrentAverageSpeed; if (World.CameraMode != World.CameraViewMode.Interior & World.CameraMode != World.CameraViewMode.InteriorLookAhead) { SoundSources[i].OpenAlVelocity[0] = (float)(tx * sp); SoundSources[i].OpenAlVelocity[1] = (float)(ty * sp); SoundSources[i].OpenAlVelocity[2] = (float)(tz * sp); } else { SoundSources[i].OpenAlVelocity[0] = (float)(tx * sp - vx); SoundSources[i].OpenAlVelocity[1] = (float)(ty * sp - vy); SoundSources[i].OpenAlVelocity[2] = (float)(tz * sp - vz); } } else { px = rx - cx; py = ry - cy; pz = rz - cz; if (World.CameraMode != World.CameraViewMode.Interior & World.CameraMode != World.CameraViewMode.InteriorLookAhead) { SoundSources[i].OpenAlVelocity[0] = 0.0f; SoundSources[i].OpenAlVelocity[1] = 0.0f; SoundSources[i].OpenAlVelocity[2] = 0.0f; } else { SoundSources[i].OpenAlVelocity[0] = (float)-vx; SoundSources[i].OpenAlVelocity[1] = (float)-vy; SoundSources[i].OpenAlVelocity[2] = (float)-vz; } } // play the sound only if within the outer radius double distanceSquared = px * px + py * py + pz * pz; double distance = Math.Sqrt(distanceSquared); double innerRadius = SoundSources[i].Radius; double outerRadius = OuterRadiusFactor * innerRadius; double outerRadiusSquared = outerRadius * outerRadius; if (distanceSquared < outerRadiusSquared) { // sound is in range double gain; double innerRadiusSquared = innerRadius * innerRadius; const double rollOffFactor = 0.9; if (distanceSquared < innerRadiusSquared) { gain = 1.0 - (1.0 - rollOffFactor) * distanceSquared / innerRadiusSquared; } else { double value = distance / outerRadius; gain = innerRadius * rollOffFactor * (1.0 - value * value * value) / distance; } SoundsQueriedPlaying++; SoundsActuallyPlaying++; bool startPlaying = false; // play sound if currently suppressed if (SoundSources[i].Suppressed) { if (SoundSources[i].SoundBufferIndex >= 0) { UseSoundBuffer(SoundSources[i].SoundBufferIndex); if (SoundBuffers[SoundSources[i].SoundBufferIndex].OpenAlBufferIndex.Valid) { int j; AL.GetError(); AL.GenSources(1, out j); ALError err = AL.GetError(); if (err == ALError.NoError) { SoundSources[i].OpenAlSourceIndex = new OpenAlIndex(j, true); AL.Source(j, ALSourcei.Buffer, SoundBuffers[SoundSources[i].SoundBufferIndex].OpenAlBufferIndex.Index); SoundSources[i].Suppressed = false; startPlaying = true; } else { continue; } } else { StopSound(i, false); continue; } } else { StopSound(i, false); continue; } } // play or stop sound if (startPlaying || IsPlaying(i)) { SoundSources[i].OpenAlPosition[0] = (float)px; SoundSources[i].OpenAlPosition[1] = (float)py; SoundSources[i].OpenAlPosition[2] = (float)pz; if (!SoundSources[i].OpenAlSourceIndex.Valid) { throw new InvalidOperationException("A bug in the sound manager. (9431)"); } int j = SoundSources[i].OpenAlSourceIndex.Index; AL.Source(j, ALSource3f.Position, SoundSources[i].OpenAlPosition[0], SoundSources[i].OpenAlPosition[1], SoundSources[i].OpenAlPosition[2]); AL.Source(j, ALSource3f.Velocity, SoundSources[i].OpenAlVelocity[0], SoundSources[i].OpenAlVelocity[1], SoundSources[i].OpenAlVelocity[2]); AL.Source(j, ALSourcef.Pitch, SoundSources[i].Pitch); float g = SoundSources[i].Gain * SoundSources[i].Gain * (float)gain; if (g > 1.0f) { g = 1.0f; } AL.Source(j, ALSourcef.Gain, g); } else { StopSound(i, false); continue; } // update position and velocity of sound if (startPlaying) { if (!SoundSources[i].OpenAlSourceIndex.Valid) { throw new InvalidOperationException("A bug in the sound manager. (7625)"); } int j = SoundSources[i].OpenAlSourceIndex.Index; AL.Source(j, ALSourceb.Looping, SoundSources[i].Looped); AL.Source(j, ALSourcef.ReferenceDistance, SoundBuffers[SoundSources[i].SoundBufferIndex].Radius); AL.SourcePlay(j); } } else { // sound is not in range if (!SoundSources[i].Suppressed) { if (SoundSources[i].Looped) { if (SoundSources[i].OpenAlSourceIndex.Valid) { int j = SoundSources[i].OpenAlSourceIndex.Index; AL.SourceStop(j); AL.DeleteSources(1, ref j); } SoundSources[i].OpenAlSourceIndex = new OpenAlIndex(0, false); SoundSources[i].Suppressed = true; } else { StopSound(i, false); } } else if (!SoundSources[i].Looped) { StopSound(i, false); } } } } } // infrequent updates InternalTimer += TimeElapsed; if (InternalTimer > 1.0) { InternalTimer = 0.0; double Elevation = World.AbsoluteCameraPosition.Y + Game.RouteInitialElevation; double AirTemperature = Game.GetAirTemperature(Elevation); double AirPressure = Game.GetAirPressure(Elevation, AirTemperature); double SpeedOfSound = Game.GetSpeedOfSound(AirPressure, AirTemperature); AL.SpeedOfSound((float)SpeedOfSound); } } }
/// <summary>Renders the debug (F10) overlay</summary> private static void RenderDebugOverlays() { System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; // debug GL.Color4(0.5, 0.5, 0.5, 0.5); LibRender.Renderer.RenderOverlaySolid(0.0f, 0.0f, (double)LibRender.Screen.Width, (double)LibRender.Screen.Height); // actual handles { string t = "actual: " + (TrainManager.PlayerTrain.Handles.Reverser.Actual == TrainManager.ReverserPosition.Reverse ? "B" : TrainManager.PlayerTrain.Handles.Reverser.Actual == TrainManager.ReverserPosition.Forwards ? "F" : "N"); if (TrainManager.PlayerTrain.Handles.SingleHandle) { t += " - " + (TrainManager.PlayerTrain.Handles.EmergencyBrake.Actual ? "EMG" : TrainManager.PlayerTrain.Handles.Brake.Actual != 0 ? "B" + TrainManager.PlayerTrain.Handles.Brake.Actual.ToString(Culture) : TrainManager.PlayerTrain.Handles.HoldBrake.Actual ? "HLD" : TrainManager.PlayerTrain.Handles.Power.Actual != 0 ? "P" + TrainManager.PlayerTrain.Handles.Power.Actual.ToString(Culture) : "N"); } else if (TrainManager.PlayerTrain.Handles.Brake is TrainManager.AirBrakeHandle) { t += " - " + (TrainManager.PlayerTrain.Handles.Power.Actual != 0 ? "P" + TrainManager.PlayerTrain.Handles.Power.Actual.ToString(Culture) : "N"); t += " - " + (TrainManager.PlayerTrain.Handles.EmergencyBrake.Actual ? "EMG" : TrainManager.PlayerTrain.Handles.Brake.Actual == (int)TrainManager.AirBrakeHandleState.Service ? "SRV" : TrainManager.PlayerTrain.Handles.Brake.Actual == (int)TrainManager.AirBrakeHandleState.Lap ? "LAP" : "REL"); } else { t += " - " + (TrainManager.PlayerTrain.Handles.Power.Actual != 0 ? "P" + TrainManager.PlayerTrain.Handles.Power.Actual.ToString(Culture) : "N"); t += " - " + (TrainManager.PlayerTrain.Handles.EmergencyBrake.Actual ? "EMG" : TrainManager.PlayerTrain.Handles.Brake.Actual != 0 ? "B" + TrainManager.PlayerTrain.Handles.Brake.Actual.ToString(Culture) : TrainManager.PlayerTrain.Handles.HoldBrake.Actual ? "HLD" : "N"); } if (TrainManager.PlayerTrain.Handles.HasLocoBrake) { if (TrainManager.PlayerTrain.Handles.LocoBrake is TrainManager.LocoAirBrakeHandle) { t += " - " + (TrainManager.PlayerTrain.Handles.LocoBrake.Actual == (int)TrainManager.AirBrakeHandleState.Service ? "SRV" : TrainManager.PlayerTrain.Handles.LocoBrake.Actual == (int)TrainManager.AirBrakeHandleState.Lap ? "LAP" : "REL"); } else { t += " - " + (TrainManager.PlayerTrain.Handles.LocoBrake.Actual != 0 ? "L" + TrainManager.PlayerTrain.Handles.LocoBrake.Actual.ToString(Culture) : "N"); } } LibRender.Renderer.DrawString(Fonts.SmallFont, t, new System.Drawing.Point(2, LibRender.Screen.Height - 46), TextAlignment.TopLeft, Color128.White, true); } // safety handles { string t = "safety: " + (TrainManager.PlayerTrain.Handles.Reverser.Actual == TrainManager.ReverserPosition.Reverse ? "B" : TrainManager.PlayerTrain.Handles.Reverser.Actual == TrainManager.ReverserPosition.Forwards ? "F" : "N"); if (TrainManager.PlayerTrain.Handles.SingleHandle) { t += " - " + (TrainManager.PlayerTrain.Handles.EmergencyBrake.Safety ? "EMG" : TrainManager.PlayerTrain.Handles.Brake.Safety != 0 ? "B" + TrainManager.PlayerTrain.Handles.Brake.Safety.ToString(Culture) : TrainManager.PlayerTrain.Handles.HoldBrake.Actual ? "HLD" : TrainManager.PlayerTrain.Handles.Power.Safety != 0 ? "P" + TrainManager.PlayerTrain.Handles.Power.Safety.ToString(Culture) : "N"); } else if (TrainManager.PlayerTrain.Handles.Brake is TrainManager.AirBrakeHandle) { t += " - " + (TrainManager.PlayerTrain.Handles.Power.Safety != 0 ? "P" + TrainManager.PlayerTrain.Handles.Power.Safety.ToString(Culture) : "N"); t += " - " + (TrainManager.PlayerTrain.Handles.EmergencyBrake.Safety ? "EMG" : TrainManager.PlayerTrain.Handles.Brake.Safety == (int)TrainManager.AirBrakeHandleState.Service ? "SRV" : TrainManager.PlayerTrain.Handles.Brake.Safety == (int)TrainManager.AirBrakeHandleState.Lap ? "LAP" : "REL"); } else { t += " - " + (TrainManager.PlayerTrain.Handles.Power.Safety != 0 ? "P" + TrainManager.PlayerTrain.Handles.Power.Safety.ToString(Culture) : "N"); t += " - " + (TrainManager.PlayerTrain.Handles.EmergencyBrake.Safety ? "EMG" : TrainManager.PlayerTrain.Handles.Brake.Safety != 0 ? "B" + TrainManager.PlayerTrain.Handles.Brake.Safety.ToString(Culture) : TrainManager.PlayerTrain.Handles.HoldBrake.Actual ? "HLD" : "N"); } if (TrainManager.PlayerTrain.Handles.HasLocoBrake) { if (TrainManager.PlayerTrain.Handles.LocoBrake is TrainManager.LocoAirBrakeHandle) { t += " - " + (TrainManager.PlayerTrain.Handles.LocoBrake.Actual == (int)TrainManager.AirBrakeHandleState.Service ? "SRV" : TrainManager.PlayerTrain.Handles.LocoBrake.Actual == (int)TrainManager.AirBrakeHandleState.Lap ? "LAP" : "REL"); } else { t += " - " + (TrainManager.PlayerTrain.Handles.LocoBrake.Actual != 0 ? "L" + TrainManager.PlayerTrain.Handles.LocoBrake.Actual.ToString(Culture) : "N"); } } LibRender.Renderer.DrawString(Fonts.SmallFont, t, new System.Drawing.Point(2, LibRender.Screen.Height - 32), TextAlignment.TopLeft, Color128.White, true); } // driver handles { string t = "driver: " + (TrainManager.PlayerTrain.Handles.Reverser.Driver == TrainManager.ReverserPosition.Reverse ? "B" : TrainManager.PlayerTrain.Handles.Reverser.Driver == TrainManager.ReverserPosition.Forwards ? "F" : "N"); if (TrainManager.PlayerTrain.Handles.SingleHandle) { t += " - " + (TrainManager.PlayerTrain.Handles.EmergencyBrake.Driver ? "EMG" : TrainManager.PlayerTrain.Handles.Brake.Driver != 0 ? "B" + TrainManager.PlayerTrain.Handles.Brake.Driver.ToString(Culture) : TrainManager.PlayerTrain.Handles.HoldBrake.Driver ? "HLD" : TrainManager.PlayerTrain.Handles.Power.Driver != 0 ? "P" + TrainManager.PlayerTrain.Handles.Power.Driver.ToString(Culture) : "N"); } else if (TrainManager.PlayerTrain.Handles.Brake is TrainManager.AirBrakeHandle) { t += " - " + (TrainManager.PlayerTrain.Handles.Power.Driver != 0 ? "P" + TrainManager.PlayerTrain.Handles.Power.Driver.ToString(Culture) : "N"); t += " - " + (TrainManager.PlayerTrain.Handles.EmergencyBrake.Driver ? "EMG" : TrainManager.PlayerTrain.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Service ? "SRV" : TrainManager.PlayerTrain.Handles.Brake.Driver == (int)TrainManager.AirBrakeHandleState.Lap ? "LAP" : "REL"); } else { t += " - " + (TrainManager.PlayerTrain.Handles.Power.Driver != 0 ? "P" + TrainManager.PlayerTrain.Handles.Power.Driver.ToString(Culture) : "N"); t += " - " + (TrainManager.PlayerTrain.Handles.EmergencyBrake.Driver ? "EMG" : TrainManager.PlayerTrain.Handles.Brake.Driver != 0 ? "B" + TrainManager.PlayerTrain.Handles.Brake.Driver.ToString(Culture) : TrainManager.PlayerTrain.Handles.HoldBrake.Driver ? "HLD" : "N"); } if (TrainManager.PlayerTrain.Handles.HasLocoBrake) { if (TrainManager.PlayerTrain.Handles.LocoBrake is TrainManager.LocoAirBrakeHandle) { t += " - " + (TrainManager.PlayerTrain.Handles.LocoBrake.Actual == (int)TrainManager.AirBrakeHandleState.Service ? "SRV" : TrainManager.PlayerTrain.Handles.LocoBrake.Actual == (int)TrainManager.AirBrakeHandleState.Lap ? "LAP" : "REL"); } else { t += " - " + (TrainManager.PlayerTrain.Handles.LocoBrake.Actual != 0 ? "L" + TrainManager.PlayerTrain.Handles.LocoBrake.Actual.ToString(Culture) : "N"); } } LibRender.Renderer.DrawString(Fonts.SmallFont, t, new System.Drawing.Point(2, LibRender.Screen.Height - 18), TextAlignment.TopLeft, Color128.White, true); } // debug information int texturesLoaded = TextureManager.GetNumberOfLoadedTextures(); int texturesRegistered = TextureManager.GetNumberOfRegisteredTextures(); int soundBuffersRegistered = Sounds.GetNumberOfLoadedBuffers(); int soundBuffersLoaded = Sounds.GetNumberOfLoadedBuffers(); int soundSourcesRegistered = Sounds.GetNumberOfRegisteredSources(); int soundSourcesPlaying = Sounds.GetNumberOfPlayingSources(); int car = 0; for (int i = 0; i < TrainManager.PlayerTrain.Cars.Length; i++) { if (TrainManager.PlayerTrain.Cars[i].Specs.IsMotorCar) { car = i; break; } } double mass = 0.0; for (int i = 0; i < TrainManager.PlayerTrain.Cars.Length; i++) { mass += TrainManager.PlayerTrain.Cars[i].Specs.MassCurrent; } int hours = (int)Game.SecondsSinceMidnight / 3600, remainder = (int)Game.SecondsSinceMidnight % 3600, minutes = remainder / 60, seconds = remainder % 60; string[] Lines = new string[] { "=system", "fps: " + LibRender.Renderer.FrameRate.ToString("0.0", Culture) + (MainLoop.LimitFramerate ? " (low cpu)" : ""), "time:" + hours.ToString("00") + ":" + minutes.ToString("00") + ":" + seconds.ToString("00"), "score: " + Game.CurrentScore.CurrentValue.ToString(Culture), "", "=train", "speed: " + (Math.Abs(TrainManager.PlayerTrain.CurrentSpeed) * 3.6).ToString("0.00", Culture) + " km/h", "power (car " + car.ToString(Culture) + "): " + (TrainManager.PlayerTrain.Cars[car].Specs.CurrentAccelerationOutput < 0.0 ? TrainManager.PlayerTrain.Cars[car].Specs.CurrentAccelerationOutput * (double)Math.Sign(TrainManager.PlayerTrain.Cars[car].Specs.CurrentSpeed) : TrainManager.PlayerTrain.Cars[car].Specs.CurrentAccelerationOutput * (double)TrainManager.PlayerTrain.Handles.Reverser.Actual).ToString("0.0000", Culture) + " m/s²", "acceleration: " + TrainManager.PlayerTrain.Specs.CurrentAverageAcceleration.ToString("0.0000", Culture) + " m/s²", "position: " + TrainManager.PlayerTrain.FrontCarTrackPosition().ToString("0.00", Culture) + " m", "elevation: " + (Game.RouteInitialElevation + TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].FrontAxle.Follower.WorldPosition.Y).ToString("0.00", Culture) + " m", "temperature: " + (TrainManager.PlayerTrain.Specs.CurrentAirTemperature - 273.15).ToString("0.00", Culture) + " °C", "air pressure: " + (0.001 * TrainManager.PlayerTrain.Specs.CurrentAirPressure).ToString("0.00", Culture) + " kPa", "air density: " + TrainManager.PlayerTrain.Specs.CurrentAirDensity.ToString("0.0000", Culture) + " kg/m³", "speed of sound: " + (Game.GetSpeedOfSound(TrainManager.PlayerTrain.Specs.CurrentAirDensity) * 3.6).ToString("0.00", Culture) + " km/h", "passenger ratio: " + TrainManager.PlayerTrain.Passengers.PassengerRatio.ToString("0.00"), "total mass: " + mass.ToString("0.00", Culture) + " kg", "", "=route", "track limit: " + (TrainManager.PlayerTrain.CurrentRouteLimit == double.PositiveInfinity ? "unlimited" : ((TrainManager.PlayerTrain.CurrentRouteLimit * 3.6).ToString("0.0", Culture) + " km/h")), "signal limit: " + (TrainManager.PlayerTrain.CurrentSectionLimit == double.PositiveInfinity ? "unlimited" : ((TrainManager.PlayerTrain.CurrentSectionLimit * 3.6).ToString("0.0", Culture) + " km/h")), "total static objects: " + ObjectManager.ObjectsUsed.ToString(Culture), "total static GL_TRIANGLES: " + Game.InfoTotalTriangles.ToString(Culture), "total static GL_TRIANGLE_STRIP: " + Game.InfoTotalTriangleStrip.ToString(Culture), "total static GL_QUADS: " + Game.InfoTotalQuads.ToString(Culture), "total static GL_QUAD_STRIP: " + Game.InfoTotalQuadStrip.ToString(Culture), "total static GL_POLYGON: " + Game.InfoTotalPolygon.ToString(Culture), "total animated objects: " + ObjectManager.AnimatedWorldObjectsUsed.ToString(Culture), "", "=renderer", "static opaque faces: " + Game.InfoStaticOpaqueFaceCount.ToString(Culture), "dynamic opaque faces: " + DynamicOpaque.FaceCount.ToString(Culture), "dynamic alpha faces: " + DynamicAlpha.FaceCount.ToString(Culture), "overlay opaque faces: " + OverlayOpaque.FaceCount.ToString(Culture), "overlay alpha faces: " + OverlayAlpha.FaceCount.ToString(Culture), "textures loaded: " + texturesLoaded.ToString(Culture) + " / " + texturesRegistered.ToString(Culture), "", "=camera", "position: " + World.CameraTrackFollower.TrackPosition.ToString("0.00", Culture) + " m", "curve radius: " + World.CameraTrackFollower.CurveRadius.ToString("0.00", Culture) + " m", "curve cant: " + (1000.0 * Math.Abs(World.CameraTrackFollower.CurveCant)).ToString("0.00", Culture) + " mm" + (World.CameraTrackFollower.CurveCant <0.0 ? " (left)" : World.CameraTrackFollower.CurveCant> 0.0 ? " (right)" : ""), "pitch: " + World.CameraTrackFollower.Pitch.ToString("0.00", Culture), "", "=sound", "sound buffers: " + soundBuffersLoaded.ToString(Culture) + " / " + soundBuffersRegistered.ToString(Culture), "sound sources: " + soundSourcesPlaying.ToString(Culture) + " / " + soundSourcesRegistered.ToString(Culture), (Interface.CurrentOptions.SoundModel == Sounds.SoundModels.Inverse ? "log clamp factor: " + Sounds.LogClampFactor.ToString("0.00") : "outer radius factor: " + Sounds.OuterRadiusFactor.ToString("0.00", Culture)), "", "=debug", "train plugin status: " + (TrainManager.PlayerTrain.Plugin != null ? (TrainManager.PlayerTrain.Plugin.PluginValid ? "ok" : "error") : "n/a"), "train plugin message: " + (TrainManager.PlayerTrain.Plugin != null ? (TrainManager.PlayerTrain.Plugin.PluginMessage ?? "n/a") : "n/a"), Game.InfoDebugString ?? "" }; double x = 4.0; double y = 4.0; for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length != 0) { if (Lines[i][0] == '=') { string text = Lines[i].Substring(1); System.Drawing.Size size = Fonts.SmallFont.MeasureString(text); GL.Color4(0.35f, 0.65f, 0.90f, 0.8f); LibRender.Renderer.RenderOverlaySolid(x, y, x + size.Width + 6.0f, y + size.Height + 2.0f); LibRender.Renderer.DrawString(Fonts.SmallFont, text, new System.Drawing.Point((int)x + 3, (int)y), TextAlignment.TopLeft, Color128.White); } else { LibRender.Renderer.DrawString(Fonts.SmallFont, Lines[i], new System.Drawing.Point((int)x, (int)y), TextAlignment.TopLeft, Color128.White, true); } y += 14.0; } else if (y >= (double)LibRender.Screen.Height - 240.0) { x += 280.0; y = 4.0; } else { y += 14.0; } } }
/// <summary>Updates the sound component. Should be called every frame.</summary> /// <param name="timeElapsed">The time in seconds that elapsed since the last call to this function.</param> private static void UpdateLinearModel(double timeElapsed) { /* * Set up the listener * */ OpenBveApi.Math.Vector3 listenerPosition = World.AbsoluteCameraPosition; OpenBveApi.Math.Orientation3 listenerOrientation = new OpenBveApi.Math.Orientation3(World.AbsoluteCameraSide, World.AbsoluteCameraUp, World.AbsoluteCameraDirection); OpenBveApi.Math.Vector3 listenerVelocity; if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead | World.CameraMode == World.CameraViewMode.Exterior) { TrainManager.Car car = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar]; OpenBveApi.Math.Vector3 diff = car.FrontAxle.Follower.WorldPosition - car.RearAxle.Follower.WorldPosition; listenerVelocity = car.Specs.CurrentSpeed * OpenBveApi.Math.Vector3.Normalize(diff) + World.CameraAlignmentSpeed.Position; } else { listenerVelocity = World.CameraAlignmentSpeed.Position; } Al.alListener3f(Al.AL_POSITION, 0.0f, 0.0f, 0.0f); Al.alListener3f(Al.AL_VELOCITY, (float)listenerVelocity.X, (float)listenerVelocity.Y, (float)listenerVelocity.Z); Al.alListenerfv(Al.AL_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 }); /* * Set up the atmospheric attributes * */ double elevation = World.AbsoluteCameraPosition.Y + Game.RouteInitialElevation; double airTemperature = Game.GetAirTemperature(elevation); double airPressure = Game.GetAirPressure(elevation, airTemperature); double airDensity = Game.GetAirDensity(airPressure, airTemperature); double speedOfSound = Game.GetSpeedOfSound(airPressure, airTemperature); try { Al.alSpeedOfSound((float)speedOfSound); } catch { } /* * Update the sound sources * */ int actuallyPlaying = 0; for (int i = 0; i < SourceCount; i++) { if (Sources[i].State == SoundSourceState.StopPending) { /* * The sound is still playing but is to be stopped. * Stop the sound, then remove it from the list of * sound sources. * */ Al.alDeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (Sources[i].State == SoundSourceState.Stopped) { /* * The sound was already stopped. Remove it from * the list of sound sources. * */ Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else if (GlobalMute) { /* * The sound is playing or about to be played, but * the global mute option is enabled. Stop the sound * sound if necessary, then remove it from the list * of sound sources if the sound is not looping. * */ if (Sources[i].State == SoundSourceState.Playing) { Al.alDeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * The sound is to be played or is already playing. * Calculate the sound gain. * */ OpenBveApi.Math.Vector3 position; OpenBveApi.Math.Vector3 velocity; if (Sources[i].Train != null) { OpenBveApi.Math.Vector3 direction; TrainManager.CreateWorldCoordinates(Sources[i].Train, Sources[i].Car, Sources[i].Position.X, Sources[i].Position.Y, Sources[i].Position.Z, out position.X, out position.Y, out position.Z, out direction.X, out direction.Y, out direction.Z); velocity = Sources[i].Train.Cars[Sources[i].Car].Specs.CurrentSpeed * direction; } else { position = Sources[i].Position; velocity = OpenBveApi.Math.Vector3.Null; } OpenBveApi.Math.Vector3 positionDifference = position - listenerPosition; double gain; if (GlobalMute) { gain = 0.0; } else { double distance = positionDifference.Norm(); double innerRadius = Sources[i].Radius; if (World.CameraMode == World.CameraViewMode.Interior | World.CameraMode == World.CameraViewMode.InteriorLookAhead) { if (Sources[i].Train != TrainManager.PlayerTrain || Sources[i].Car != TrainManager.PlayerTrain.DriverCar) { innerRadius *= 0.5; } } double outerRadius = OuterRadiusFactor * innerRadius; if (distance < outerRadius) { if (distance <= innerRadius) { gain = Sources[i].Volume; } else { gain = (distance - outerRadius) / (innerRadius - outerRadius); gain *= Sources[i].Volume; } gain = 3.0 * gain * gain - 2.0 * gain * gain * gain; } else { gain = 0.0; } } if (gain <= GainThreshold) { /* * If the gain is too low to be audible, stop the sound. * If the sound is not looping, stop it if necessary, * then remove it from the list of sound sources. * */ if (Sources[i].State == SoundSourceState.Playing) { Al.alDeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.PlayPending; Sources[i].OpenAlSourceName = 0; } if (!Sources[i].Looped) { Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } } else { /* * Play the sound and update position, velocity, pitch and gain. * For non-looping sounds, check if the sound is still playing. * */ gain = (gain - GainThreshold) / (1.0 - GainThreshold); if (Sources[i].State != SoundSourceState.Playing) { LoadBuffer(Sources[i].Buffer); if (Sources[i].Buffer.Loaded) { Al.alGenSources(1, out Sources[i].OpenAlSourceName); Al.alSourcei(Sources[i].OpenAlSourceName, Al.AL_BUFFER, Sources[i].Buffer.OpenAlBufferName); } else { /* * We cannot play the sound because * the buffer could not be loaded. * */ Sources[i].State = SoundSourceState.Stopped; continue; } } Al.alSource3f(Sources[i].OpenAlSourceName, Al.AL_POSITION, (float)positionDifference.X, (float)positionDifference.Y, (float)positionDifference.Z); Al.alSource3f(Sources[i].OpenAlSourceName, Al.AL_VELOCITY, (float)velocity.X, (float)velocity.Y, (float)velocity.Z); Al.alSourcef(Sources[i].OpenAlSourceName, Al.AL_PITCH, (float)Sources[i].Pitch); Al.alSourcef(Sources[i].OpenAlSourceName, Al.AL_GAIN, (float)gain); if (Sources[i].State != SoundSourceState.Playing) { Al.alSourcei(Sources[i].OpenAlSourceName, Al.AL_LOOPING, Sources[i].Looped ? Al.AL_TRUE : Al.AL_FALSE); Al.alSourcePlay(Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Playing; } if (!Sources[i].Looped) { int state; Al.alGetSourcei(Sources[i].OpenAlSourceName, Al.AL_SOURCE_STATE, out state); if (state != Al.AL_INITIAL & state != Al.AL_PLAYING) { /* * The sound is not playing any longer. * Remove it from the list of sound sources. * */ Al.alDeleteSources(1, ref Sources[i].OpenAlSourceName); Sources[i].State = SoundSourceState.Stopped; Sources[i].OpenAlSourceName = 0; Sources[i] = Sources[SourceCount - 1]; SourceCount--; i--; } else { actuallyPlaying++; } } else { actuallyPlaying++; } } } } /* * Adjust the outer radius factor / the clamp factor. * */ if (actuallyPlaying >= Interface.CurrentOptions.SoundNumber - 2) { /* * Too many sounds are playing. * Reduce the outer radius factor. * */ OuterRadiusFactorSpeed -= timeElapsed; if (OuterRadiusFactorSpeed < -OuterRadiusFactorMaximumSpeed) { OuterRadiusFactorSpeed = -OuterRadiusFactorMaximumSpeed; } } else if (actuallyPlaying <= Interface.CurrentOptions.SoundNumber - 6) { /* * Only few sounds are playing. * Increase the outer radius factor. * */ OuterRadiusFactorSpeed += timeElapsed; if (OuterRadiusFactorSpeed > OuterRadiusFactorMaximumSpeed) { OuterRadiusFactorSpeed = OuterRadiusFactorMaximumSpeed; } } else { /* * Neither too many nor too few sounds are playing. * Stabilize the outer radius factor. * */ if (OuterRadiusFactorSpeed < 0.0) { OuterRadiusFactorSpeed += timeElapsed; if (OuterRadiusFactorSpeed > 0.0) { OuterRadiusFactorSpeed = 0.0; } } else { OuterRadiusFactorSpeed -= timeElapsed; if (OuterRadiusFactorSpeed < 0.0) { OuterRadiusFactorSpeed = 0.0; } } } OuterRadiusFactor += OuterRadiusFactorSpeed * timeElapsed; if (OuterRadiusFactor < OuterRadiusFactorMinimum) { OuterRadiusFactor = OuterRadiusFactorMinimum; OuterRadiusFactorSpeed = 0.0; } else if (OuterRadiusFactor > OuterRadiusFactorMaximum) { OuterRadiusFactor = OuterRadiusFactorMaximum; OuterRadiusFactorSpeed = 0.0; } }