Beispiel #1
0
 internal Windscreen(int numberOfDrops, double dropLife, TrainManager.Car car)
 {
     RainDrops    = new Raindrop[numberOfDrops];
     DropLife     = dropLife;
     currentDrops = 0;
     Car          = car;
 }
Beispiel #2
0
        internal static void DebugTouchArea()
        {
            if (!Loading.SimulationSetup)
            {
                return;
            }

            if (World.CameraMode != CameraViewMode.Interior && World.CameraMode != CameraViewMode.InteriorLookAhead)
            {
                return;
            }

            TrainManager.Car Car = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar];
            int add = Car.CarSections[0].CurrentAdditionalGroup + 1;

            if (add < Car.CarSections[0].Groups.Length)
            {
                TrainManager.TouchElement[] TouchElements = Car.CarSections[0].Groups[add].TouchElements;

                if (TouchElements != null)
                {
                    foreach (var TouchElement in TouchElements)
                    {
                        int o = TouchElement.Element.ObjectIndex;
                        ShowObjectSelection(o);
                    }

                    ResetOpenGlState();
                    GL.PushMatrix();

                    UpdateViewport(ViewPortChangeMode.ChangeToCab);

                    RenderSceneSelection(true);

                    GL.PopMatrix();

                    foreach (var TouchElement in TouchElements)
                    {
                        int o = TouchElement.Element.ObjectIndex;
                        HideObjectSelection(o);
                    }
                }
            }
        }
Beispiel #3
0
        internal static void Parse(string fileName, ref TrainManager.Car car)
        {
            //3D center of the car
            Vector3 center = new Vector3(0.0, 0.0, 0.0);
            //Positioned to the left of the car, but centered Y & Z
            Vector3 left = new Vector3(-1.3, 0.0, 0.0);
            //Positioned to the right of the car, but centered Y & Z
            Vector3 right = new Vector3(1.3, 0.0, 0.0);
            //Positioned at the front of the car, centered X and Y
            Vector3 front = new Vector3(0.0, 0.0, 0.5 * car.Length);
            //Positioned at the position of the panel / 3D cab (Remember that the panel is just an object in the world...)
            Vector3 panel = new Vector3(car.Driver.X, car.Driver.Y, car.Driver.Z + 1.0);



            //The current XML file to load
            XmlDocument currentXML = new XmlDocument();

            //Load the marker's XML file
            currentXML.Load(fileName);
            currentPath = System.IO.Path.GetDirectoryName(fileName);
            if (currentXML.DocumentElement != null)
            {
                XmlNodeList DocumentNodes = currentXML.DocumentElement.SelectNodes("/openBVE/CarSounds");
                if (DocumentNodes == null || DocumentNodes.Count == 0)
                {
                    Interface.AddMessage(Interface.MessageType.Error, false, "No car sound nodes defined in XML file " + fileName);
                    //If we have no appropriate nodes specified, return false and fallback to loading the legacy Sound.cfg file
                    throw new Exception("Empty sound.xml file");
                }
                foreach (XmlNode n in DocumentNodes)
                {
                    if (n.HasChildNodes)
                    {
                        foreach (XmlNode c in n.ChildNodes)
                        {
                            switch (c.Name.ToLowerInvariant())
                            {
                            case "ats":
                            case "plugin":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of plugin sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                if (!car.Specs.IsDriverCar)
                                {
                                    break;
                                }
                                ParseArrayNode(c, out car.Sounds.Plugin, center, SoundCfgParser.mediumRadius);
                                break;

                            case "brake":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of brake sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                foreach (XmlNode cc in c.ChildNodes)
                                {
                                    switch (cc.Name.ToLowerInvariant())
                                    {
                                    case "releasehigh":
                                        //Release brakes from high pressure
                                        ParseNode(cc, out car.Sounds.AirHigh, center, SoundCfgParser.smallRadius);
                                        break;

                                    case "release":
                                        //Release brakes from normal pressure
                                        ParseNode(cc, out car.Sounds.Air, center, SoundCfgParser.smallRadius);
                                        break;

                                    case "releasefull":
                                        //Release brakes from full pressure
                                        ParseNode(cc, out car.Sounds.AirZero, center, SoundCfgParser.smallRadius);
                                        break;

                                    case "emergency":
                                        //Apply EB
                                        ParseNode(cc, out car.Sounds.EmrBrake, center, SoundCfgParser.smallRadius);
                                        break;

                                    case "application":
                                        //Standard application
                                        ParseNode(cc, out car.Sounds.Brake, center, SoundCfgParser.smallRadius);
                                        break;

                                    default:
                                        Interface.AddMessage(Interface.MessageType.Error, false, "Declaration " + cc.Name + " is unsupported in a " + c.Name + " node.");
                                        break;
                                    }
                                }
                                break;

                            case "brakehandle":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of brake handle sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                if (!car.Specs.IsDriverCar)
                                {
                                    break;
                                }
                                foreach (XmlNode cc in c.ChildNodes)
                                {
                                    switch (cc.Name.ToLowerInvariant())
                                    {
                                    case "apply":
                                        ParseNode(cc, out car.Sounds.BrakeHandleApply, panel, SoundCfgParser.tinyRadius);
                                        break;

                                    case "release":
                                        ParseNode(cc, out car.Sounds.BrakeHandleRelease, panel, SoundCfgParser.tinyRadius);
                                        break;

                                    case "min":
                                    case "minimum":
                                        ParseNode(cc, out car.Sounds.BrakeHandleMin, panel, SoundCfgParser.tinyRadius);
                                        break;

                                    case "max":
                                    case "maximum":
                                        ParseNode(cc, out car.Sounds.BrakeHandleMax, panel, SoundCfgParser.tinyRadius);
                                        break;

                                    default:
                                        Interface.AddMessage(Interface.MessageType.Error, false, "Declaration " + cc.Name + " is unsupported in a " + c.Name + " node.");
                                        break;
                                    }
                                }
                                break;

                            case "breaker":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of breaker sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                if (!car.Specs.IsDriverCar)
                                {
                                    break;
                                }
                                foreach (XmlNode cc in c.ChildNodes)
                                {
                                    switch (cc.Name.ToLowerInvariant())
                                    {
                                    case "on":
                                        ParseNode(cc, out car.Sounds.BreakerResume, panel, SoundCfgParser.smallRadius);
                                        break;

                                    case "off":
                                        ParseNode(cc, out car.Sounds.BreakerResumeOrInterrupt, panel, SoundCfgParser.smallRadius);
                                        break;

                                    default:
                                        Interface.AddMessage(Interface.MessageType.Error, false, "Declaration " + cc.Name + " is unsupported in a " + c.Name + " node.");
                                        break;
                                    }
                                }
                                break;

                            case "buzzer":
                                if (!car.Specs.IsDriverCar)
                                {
                                    break;
                                }
                                ParseNode(c, out car.Sounds.Adjust, panel, SoundCfgParser.tinyRadius);
                                break;

                            case "compressor":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of compressor sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                if (car.Specs.AirBrake.Type != TrainManager.AirBrakeType.Main)
                                {
                                    break;
                                }
                                foreach (XmlNode cc in c.ChildNodes)
                                {
                                    switch (cc.Name.ToLowerInvariant())
                                    {
                                    case "attack":
                                    case "start":
                                        //Compressor starting sound
                                        ParseNode(cc, out car.Sounds.CpStart, center, SoundCfgParser.mediumRadius);
                                        break;

                                    case "loop":
                                        //Compressor loop sound
                                        ParseNode(cc, out car.Sounds.CpLoop, center, SoundCfgParser.mediumRadius);
                                        break;

                                    case "release":
                                    case "stop":
                                    case "end":
                                        //Compressor end sound
                                        ParseNode(cc, out car.Sounds.CpEnd, center, SoundCfgParser.mediumRadius);
                                        break;

                                    default:
                                        Interface.AddMessage(Interface.MessageType.Error, false, "Declaration " + cc.Name + " is unsupported in a " + c.Name + " node.");
                                        break;
                                    }
                                }
                                break;

                            case "door":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of door sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                foreach (XmlNode cc in c.ChildNodes)
                                {
                                    switch (cc.Name.ToLowerInvariant())
                                    {
                                    case "openleft":
                                    case "leftopen":
                                        ParseNode(cc, out car.Sounds.DoorOpenL, left, SoundCfgParser.smallRadius);
                                        break;

                                    case "openright":
                                    case "rightopen":
                                        ParseNode(cc, out car.Sounds.DoorOpenR, right, SoundCfgParser.smallRadius);
                                        break;

                                    case "closeleft":
                                    case "leftclose":
                                        ParseNode(cc, out car.Sounds.DoorCloseL, left, SoundCfgParser.smallRadius);
                                        break;

                                    case "closeright":
                                    case "rightclose":
                                        ParseNode(cc, out car.Sounds.DoorCloseR, right, SoundCfgParser.smallRadius);
                                        break;

                                    default:
                                        Interface.AddMessage(Interface.MessageType.Error, false, "Declaration " + cc.Name + " is unsupported in a " + c.Name + " node.");
                                        break;
                                    }
                                }
                                break;

                            case "flange":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of flange sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                ParseArrayNode(c, out car.Sounds.Flange, center, SoundCfgParser.mediumRadius);
                                break;

                            case "horn":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of horn sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                if (!car.Specs.IsDriverCar)
                                {
                                    break;
                                }
                                foreach (XmlNode cc in c.ChildNodes)
                                {
                                    switch (cc.Name.ToLowerInvariant())
                                    {
                                    case "primary":
                                        //Primary horn
                                        ParseHornNode(cc, out car.Sounds.Horns[0], front, SoundCfgParser.largeRadius);
                                        break;

                                    case "secondary":
                                        //Secondary horn
                                        ParseHornNode(cc, out car.Sounds.Horns[1], front, SoundCfgParser.largeRadius);
                                        break;

                                    case "music":
                                        //Music horn
                                        ParseHornNode(cc, out car.Sounds.Horns[2], front, SoundCfgParser.largeRadius);
                                        break;

                                    default:
                                        Interface.AddMessage(Interface.MessageType.Error, false, "Declaration " + cc.Name + " is unsupported in a " + c.Name + " node.");
                                        break;
                                    }
                                }
                                break;

                            case "loop":
                            case "noise":
                                ParseNode(c, out car.Sounds.Loop, center, SoundCfgParser.mediumRadius);
                                break;

                            case "mastercontroller":
                            case "powerhandle":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of power handle sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                if (!car.Specs.IsDriverCar)
                                {
                                    break;
                                }
                                foreach (XmlNode cc in c.ChildNodes)
                                {
                                    switch (cc.Name.ToLowerInvariant())
                                    {
                                    case "up":
                                    case "increase":
                                        ParseNode(cc, out car.Sounds.MasterControllerUp, panel, SoundCfgParser.tinyRadius);
                                        break;

                                    case "down":
                                    case "decrease":
                                        ParseNode(cc, out car.Sounds.MasterControllerDown, panel, SoundCfgParser.tinyRadius);
                                        break;

                                    case "min":
                                    case "minimum":
                                        ParseNode(cc, out car.Sounds.MasterControllerMin, panel, SoundCfgParser.tinyRadius);
                                        break;

                                    case "max":
                                    case "maximum":
                                        ParseNode(cc, out car.Sounds.MasterControllerMax, panel, SoundCfgParser.tinyRadius);
                                        break;

                                    default:
                                        Interface.AddMessage(Interface.MessageType.Error, false, "Declaration " + cc.Name + " is unsupported in a " + c.Name + " node.");
                                        break;
                                    }
                                }
                                break;

                            case "motor":
                                if (!car.Specs.IsMotorCar)
                                {
                                    break;
                                }
                                ParseMotorSoundTableNode(c, ref car.Sounds.Motor.Tables, center, SoundCfgParser.mediumRadius);
                                break;

                            case "pilotlamp":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of pilot-lamp sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                if (!car.Specs.IsDriverCar)
                                {
                                    break;
                                }
                                foreach (XmlNode cc in c.ChildNodes)
                                {
                                    switch (cc.Name.ToLowerInvariant())
                                    {
                                    case "on":
                                        ParseNode(cc, out car.Sounds.PilotLampOn, panel, SoundCfgParser.tinyRadius);
                                        break;

                                    case "off":
                                        ParseNode(cc, out car.Sounds.PilotLampOff, panel, SoundCfgParser.tinyRadius);
                                        break;

                                    default:
                                        Interface.AddMessage(Interface.MessageType.Error, false, "Declaration " + cc.Name + " is unsupported in a " + c.Name + " node.");
                                        break;
                                    }
                                }
                                break;

                            case "pointfrontaxle":
                            case "switchfrontaxle":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of point front axle sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                ParseArrayNode(c, out car.Sounds.PointFrontAxle, new Vector3(0.0, 0.0, car.FrontAxle.Position), SoundCfgParser.smallRadius);
                                break;

                            case "pointrearaxle":
                            case "switchrearaxle":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of point rear axle sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                ParseArrayNode(c, out car.Sounds.PointRearAxle, new Vector3(0.0, 0.0, car.FrontAxle.Position), SoundCfgParser.smallRadius);
                                break;

                            case "reverser":
                            case "reverserhandle":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of reverser sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                if (!car.Specs.IsDriverCar)
                                {
                                    break;
                                }
                                foreach (XmlNode cc in c.ChildNodes)
                                {
                                    switch (cc.Name.ToLowerInvariant())
                                    {
                                    case "on":
                                        ParseNode(cc, out car.Sounds.ReverserOn, panel, SoundCfgParser.tinyRadius);
                                        break;

                                    case "off":
                                        ParseNode(cc, out car.Sounds.ReverserOff, panel, SoundCfgParser.tinyRadius);
                                        break;

                                    default:
                                        Interface.AddMessage(Interface.MessageType.Error, false, "Declaration " + cc.Name + " is unsupported in a " + c.Name + " node.");
                                        break;
                                    }
                                }
                                break;

                            case "run":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of run sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                ParseArrayNode(c, out car.Sounds.Run, center, SoundCfgParser.mediumRadius);
                                break;

                            case "shoe":
                            case "rub":
                                ParseNode(c, out car.Sounds.Rub, center, SoundCfgParser.mediumRadius);
                                break;

                            case "suspension":
                            case "spring":
                                if (!c.HasChildNodes)
                                {
                                    Interface.AddMessage(Interface.MessageType.Error, false, "An empty list of suspension sounds was defined in in XML file " + fileName);
                                    break;
                                }
                                foreach (XmlNode cc in c.ChildNodes)
                                {
                                    switch (cc.Name.ToLowerInvariant())
                                    {
                                    case "left":
                                        //Left suspension springs
                                        ParseNode(cc, out car.Sounds.SpringL, left, SoundCfgParser.smallRadius);
                                        break;

                                    case "right":
                                        //right suspension springs
                                        ParseNode(cc, out car.Sounds.SpringR, right, SoundCfgParser.smallRadius);
                                        break;

                                    default:
                                        Interface.AddMessage(Interface.MessageType.Error, false, "Declaration " + cc.Name + " is unsupported in a " + c.Name + " node.");
                                        break;
                                    }
                                }
                                break;
                            }
                        }
                    }
                }
                car.Sounds.RunVolume    = new double[car.Sounds.Run.Length];
                car.Sounds.FlangeVolume = new double[car.Sounds.FlangeVolume.Length];
            }
        }
Beispiel #4
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;
                    }
                }
            }
        }
Beispiel #5
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.Vector3D     listenerPosition    = World.AbsoluteCameraPosition;
            OpenBveApi.Math.Orientation3 listenerOrientation = new OpenBveApi.Math.Orientation3(World.AbsoluteCameraSide, World.AbsoluteCameraUp, World.AbsoluteCameraDirection);
            OpenBveApi.Math.Vector3D     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.Vector3D diff = car.FrontAxle.Follower.WorldPosition - car.RearAxle.Follower.WorldPosition;
                listenerVelocity = car.Specs.CurrentSpeed * OpenBveApi.Math.Vector3D.Normalize(diff) + World.CameraAlignmentSpeed.Position;
            }
            else
            {
                listenerVelocity = World.CameraAlignmentSpeed.Position;
            }
            float[] vectors =
            {
                (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(ALListener3f.Position, 0.0f, 0.0f, 0.0f);
            AL.Listener(ALListener3f.Velocity, (float)listenerVelocity.X, (float)listenerVelocity.Y, (float)listenerVelocity.Z);
            AL.Listener(ALListenerfv.Orientation, ref vectors);

            /*
             * 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.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.Vector3D position;
                    OpenBveApi.Math.Vector3D velocity;
                    if (Sources[i].Train != null)
                    {
                        OpenBveApi.Math.Vector3D 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.Vector3D.Null;
                    }
                    OpenBveApi.Math.Vector3D 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.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 >= Options.Current.SoundNumber - 2)
            {
                /*
                 * Too many sounds are playing.
                 * Reduce the outer radius factor.
                 * */
                OuterRadiusFactorSpeed -= timeElapsed;
                if (OuterRadiusFactorSpeed < -OuterRadiusFactorMaximumSpeed)
                {
                    OuterRadiusFactorSpeed = -OuterRadiusFactorMaximumSpeed;
                }
            }
            else if (actuallyPlaying <= Options.Current.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;
            }
        }
Beispiel #6
0
        internal static void LeaveCheck(Vector2 Point)
        {
            if (!Loading.SimulationSetup)
            {
                return;
            }

            if (World.CameraMode != CameraViewMode.Interior && World.CameraMode != CameraViewMode.InteriorLookAhead)
            {
                return;
            }

            TrainManager.Car Car = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar];
            int add = Car.CarSections[0].CurrentAdditionalGroup + 1;

            if (add < Car.CarSections[0].Groups.Length)
            {
                TrainManager.TouchElement[] TouchElements = Car.CarSections[0].Groups[add].TouchElements;

                if (TouchElements != null)
                {
                    foreach (var TouchElement in TouchElements)
                    {
                        int o = TouchElement.Element.ObjectIndex;
                        ShowObjectSelection(o);
                    }

                    int[] SelectBuffer = new int[2048];

                    PickPre(SelectBuffer, Point, new Vector2(5));

                    RenderSceneSelection();

                    int PickedObjectIndex = PickPost(SelectBuffer);

                    foreach (var TouchElement in TouchElements)
                    {
                        int o = TouchElement.Element.ObjectIndex;
                        HideObjectSelection(o);
                        if (o == PickedObjectIndex)
                        {
                            Car.CarSections[0].CurrentAdditionalGroup = TouchElement.JumpScreenIndex;
                            Car.ChangeCarSection(TrainManager.CarSectionType.Interior);
                            if (TouchElement.SoundIndex >= 0 && TouchElement.SoundIndex < Car.Sounds.Touch.Length)
                            {
                                Sounds.SoundBuffer      Buffer   = Car.Sounds.Touch[TouchElement.SoundIndex].Buffer;
                                OpenBveApi.Math.Vector3 Position = Car.Sounds.Touch[TouchElement.SoundIndex].Position;
                                Sounds.PlaySound(Buffer, 1.0, 1.0, Position, TrainManager.PlayerTrain, TrainManager.PlayerTrain.DriverCar, false);
                            }
                        }

                        // HACK: Normally terminate the command issued once.
                        if (o == PickedObjectIndex || (PickedObjectIndex != PrePickedObjectIndex && o == PrePickedObjectIndex))
                        {
                            for (int i = 0; i < Interface.CurrentControls.Length; i++)
                            {
                                if (Interface.CurrentControls[i].Method != Interface.ControlMethod.Touch)
                                {
                                    continue;
                                }
                                bool EnableOption = false;
                                for (int j = 0; j < Translations.CommandInfos.Length; j++)
                                {
                                    if (Interface.CurrentControls[i].Command == Translations.CommandInfos[j].Command)
                                    {
                                        EnableOption = Translations.CommandInfos[j].EnableOption;
                                        break;
                                    }
                                }
                                if (TouchElement.Command == Interface.CurrentControls[i].Command)
                                {
                                    if (EnableOption && TouchElement.CommandOption != Interface.CurrentControls[i].Option)
                                    {
                                        continue;
                                    }
                                    Interface.CurrentControls[i].AnalogState  = 0.0;
                                    Interface.CurrentControls[i].DigitalState = Interface.DigitalControlState.Released;
                                    MainLoop.RemoveControlRepeat(i);
                                }
                            }
                        }
                    }
                }
            }
        }
Beispiel #7
0
        internal static void TouchCheck(Vector2 Point)
        {
            if (!Loading.SimulationSetup)
            {
                return;
            }

            if (World.CameraMode != CameraViewMode.Interior && World.CameraMode != CameraViewMode.InteriorLookAhead)
            {
                return;
            }

            TrainManager.Car Car = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar];
            int add = Car.CarSections[0].CurrentAdditionalGroup + 1;

            if (add < Car.CarSections[0].Groups.Length)
            {
                TrainManager.TouchElement[] TouchElements = Car.CarSections[0].Groups[add].TouchElements;

                if (TouchElements != null)
                {
                    foreach (var TouchElement in TouchElements)
                    {
                        int o = TouchElement.Element.ObjectIndex;
                        ShowObjectSelection(o);
                    }

                    int[] SelectBuffer = new int[2048];

                    PickPre(SelectBuffer, Point, new Vector2(5));

                    RenderSceneSelection();

                    int PickedObjectIndex = PickPost(SelectBuffer);

                    foreach (var TouchElement in TouchElements)
                    {
                        int o = TouchElement.Element.ObjectIndex;
                        HideObjectSelection(o);
                        if (o == PickedObjectIndex)
                        {
                            for (int i = 0; i < Interface.CurrentControls.Length; i++)
                            {
                                if (Interface.CurrentControls[i].Method != Interface.ControlMethod.Touch)
                                {
                                    continue;
                                }
                                bool EnableOption = false;
                                for (int j = 0; j < Translations.CommandInfos.Length; j++)
                                {
                                    if (Interface.CurrentControls[i].Command == Translations.CommandInfos[j].Command)
                                    {
                                        EnableOption = Translations.CommandInfos[j].EnableOption;
                                        break;
                                    }
                                }
                                if (TouchElement.Command == Interface.CurrentControls[i].Command)
                                {
                                    if (EnableOption && TouchElement.CommandOption != Interface.CurrentControls[i].Option)
                                    {
                                        continue;
                                    }
                                    Interface.CurrentControls[i].AnalogState  = 1.0;
                                    Interface.CurrentControls[i].DigitalState = Interface.DigitalControlState.Pressed;
                                    MainLoop.AddControlRepeat(i);
                                }
                            }
                        }
                    }

                    PrePickedObjectIndex = PickedObjectIndex;
                }
            }
        }
Beispiel #8
0
        internal static bool MoveCheck(Vector2 Point, out Cursor.Status Status)
        {
            if (!Loading.SimulationSetup)
            {
                Status = Cursor.Status.Default;
                return(false);
            }

            if (World.CameraMode != CameraViewMode.Interior && World.CameraMode != CameraViewMode.InteriorLookAhead)
            {
                Status = Cursor.Status.Default;
                return(false);
            }

            Status = Cursor.Status.Default;

            TrainManager.Car Car = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar];
            int add = Car.CarSections[0].CurrentAdditionalGroup + 1;

            if (add < Car.CarSections[0].Groups.Length)
            {
                TrainManager.TouchElement[] TouchElements = Car.CarSections[0].Groups[add].TouchElements;

                if (TouchElements != null)
                {
                    foreach (var TouchElement in TouchElements)
                    {
                        int o = TouchElement.Element.ObjectIndex;
                        ShowObjectSelection(o);
                    }

                    int[] SelectBuffer = new int[2048];

                    PickPre(SelectBuffer, Point, new Vector2(5));

                    RenderSceneSelection();

                    int PickedObjectIndex = PickPost(SelectBuffer);

                    foreach (var TouchElement in TouchElements)
                    {
                        int o = TouchElement.Element.ObjectIndex;
                        HideObjectSelection(o);

                        if (o == PickedObjectIndex)
                        {
                            switch (TouchElement.Command)
                            {
                            case Translations.Command.PowerIncrease:
                            case Translations.Command.BrakeIncrease:
                            case Translations.Command.ReverserForward:
                                Status = Cursor.Status.Plus;
                                break;

                            case Translations.Command.PowerDecrease:
                            case Translations.Command.BrakeDecrease:
                            case Translations.Command.ReverserBackward:
                                Status = Cursor.Status.Minus;
                                break;
                            }
                        }
                    }

                    if (PickedObjectIndex >= 0)
                    {
                        return(true);
                    }
                }
            }
            return(false);
        }
Beispiel #9
0
 internal Axle(TrainManager.Train Train, TrainManager.Car Car)
 {
     Follower = new TrackFollower(Program.CurrentHost, Train, Car);
 }
Beispiel #10
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>
        protected override void UpdateLinearModel(double timeElapsed)
        {
            /*
             * Set up the listener
             * */
            Vector3      listenerPosition    = Program.Renderer.Camera.AbsolutePosition;
            Orientation3 listenerOrientation = new Orientation3(Program.Renderer.Camera.AbsoluteSide, Program.Renderer.Camera.AbsoluteUp, Program.Renderer.Camera.AbsoluteDirection);
            Vector3      listenerVelocity;

            if (Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior | Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead | Program.Renderer.Camera.CurrentMode == CameraViewMode.Exterior)
            {
                TrainManager.Car car  = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar];
                Vector3          diff = car.FrontAxle.Follower.WorldPosition - car.RearAxle.Follower.WorldPosition;
                listenerVelocity = car.CurrentSpeed * Vector3.Normalize(diff) + Program.Renderer.Camera.AlignmentSpeed.Position;
            }
            else
            {
                listenerVelocity = Program.Renderer.Camera.AlignmentSpeed.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      = Program.Renderer.Camera.AbsolutePosition.Y + Program.CurrentRoute.Atmosphere.InitialElevation;
            double airTemperature = Program.CurrentRoute.Atmosphere.GetAirTemperature(elevation);
            double airPressure    = Program.CurrentRoute.Atmosphere.GetAirPressure(elevation, airTemperature);
            double speedOfSound   = Program.CurrentRoute.Atmosphere.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.
                     * */

                    Vector3 position;
                    Vector3 velocity;

                    switch (Sources[i].Type)
                    {
                    case SoundType.TrainCar:
                        Vector3 direction;                                 //only relevant for moving objects
                        var     Car = (AbstractCar)Sources[i].Parent;
                        Car.CreateWorldCoordinates(Sources[i].Position, out position, out direction);
                        velocity = Car.CurrentSpeed * direction;
                        break;

                    case SoundType.AnimatedObject:
                        var WorldSound = (WorldSound)Sources[i].Parent;
                        //TODO: Calculate speed...
                        position = WorldSound.Follower.WorldPosition + WorldSound.Position;
                        velocity = Vector3.Zero;
                        break;

                    default:
                        position = Sources[i].Position;
                        velocity = Vector3.Zero;
                        break;
                    }
                    Vector3 positionDifference = position - listenerPosition;
                    double  gain;
                    if (GlobalMute)
                    {
                        gain = 0.0;
                    }
                    else
                    {
                        double distance    = positionDifference.Norm();
                        double innerRadius = Sources[i].Radius;
                        if (Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior | Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead)
                        {
                            if (Sources[i].Parent != TrainManager.PlayerTrain.Cars[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;
            }

            RecAndPlay(listenerPosition, true, 0.0);
        }