diff --git a/Source/Documentation/Manual/sound.rst b/Source/Documentation/Manual/sound.rst index da7b313d1..1bfad7285 100644 --- a/Source/Documentation/Manual/sound.rst +++ b/Source/Documentation/Manual/sound.rst @@ -651,4 +651,51 @@ where the number in parenthesis may be anyone from 0 (nothing heard ) to 100 (internal track sound reproduced at volume as defined in .sms file). If the parameter is not present, the internal track sound is -reproduced at the volume as defined in .sms file. \ No newline at end of file +reproduced at the volume as defined in .sms file. + +Conditional sound +================= + +In the real world some sounds are present only at a specific time (season and/or +time of day) and/or with a specific weather. + +OR provides the parameters to actuate that for any set of sound streams. Consider the +sound stream here below:: + + Stream ( + Priority ( 6 ) + ORTSSeason ( Spring () winter () ) + ORTSWeather ( rain () ) + ORTSTimeOfDay ( 7 12 ) + ORTSTimeOfDay ( 13 20 ) + Triggers ( 2 + Variable_Trigger ( Distance_Dec_Past 400.0 + StartLoop ( 1 + File ( "Somma_annunci_loud.wav" -1 ) + SelectionMethod ( SequentialSelection ) + ) + ) + Variable_Trigger ( Distance_Inc_Past 400.0 ReleaseLoopRelease ()) + ) + + VolumeCurve( + DistanceControlled + CurvePoints ( 4 + 0.0 1.0 + 230.0 1.0 + 260.0 0.0 + 2000.0 0.0 + ) + Granularity (0.01) + ) + ) + + +As can be seen, there are three keywords, that are ORTSSeason, ORTSWeather and +ORTSTimeOfDay. If one or more of the three keywords is not present in the stream, +the sound does not depend from that keyword. + +In the example shown, the sound is played if all conditions are met, that is season +is spring or winter and weather is rain and time of day is within one of the two +intervals 7-12 or 13-20. There may be as many TimeOfDay lines as wanted, +but the granularity is one hour. \ No newline at end of file diff --git a/Source/Orts.Formats.Msts/SoundManagmentFile.cs b/Source/Orts.Formats.Msts/SoundManagmentFile.cs index eba7993e5..81327c038 100644 --- a/Source/Orts.Formats.Msts/SoundManagmentFile.cs +++ b/Source/Orts.Formats.Msts/SoundManagmentFile.cs @@ -176,6 +176,12 @@ public class SMSStream public float Volume = 1.0f; public List VolumeCurves = new List(); public FrequencyCurve FrequencyCurve; + public bool[] Season; + public bool[] Weather; + public int[] TimeInterval; + public List TimeIntervals; + + public SMSStream(STFReader stf) { @@ -186,6 +192,42 @@ public SMSStream(STFReader stf) new STFReader.TokenProcessor("volumecurve", ()=>{ VolumeCurves.Add(new VolumeCurve(stf)); }), new STFReader.TokenProcessor("frequencycurve", ()=>{ FrequencyCurve = new FrequencyCurve(stf); }), new STFReader.TokenProcessor("volume", ()=>{ Volume = stf.ReadFloatBlock(STFReader.UNITS.None, Volume); }), + new STFReader.TokenProcessor("ortstimeofday", ()=>{ + if (TimeIntervals == null) + TimeIntervals = new List(); + var timeInterval = new int[2]; + stf.MustMatch("("); + timeInterval[0] = stf.ReadInt(null); + timeInterval[1] = stf.ReadInt(null); + TimeIntervals.Add(timeInterval); + stf.SkipRestOfBlock(); + }), + new STFReader.TokenProcessor("ortsseason", ()=>{ + Season = new bool[4]; + stf.MustMatch("("); + stf.ParseBlock(new STFReader.TokenProcessor[] { + new STFReader.TokenProcessor("spring", ()=>{ if(stf.ReadBoolBlock(true)) + Season[(int)SeasonType.Spring] = true; }), + new STFReader.TokenProcessor("summer", ()=>{ if(stf.ReadBoolBlock(true)) + Season[(int)SeasonType.Summer] = true; }), + new STFReader.TokenProcessor("autumn", ()=>{ if(stf.ReadBoolBlock(true)) + Season[(int)SeasonType.Autumn] = true; }), + new STFReader.TokenProcessor("winter", ()=>{ if(stf.ReadBoolBlock(true)) + Season[(int)SeasonType.Winter] = true; }), + }); + }), + new STFReader.TokenProcessor("ortsweather", ()=>{ + Weather = new bool[3]; + stf.MustMatch("("); + stf.ParseBlock(new STFReader.TokenProcessor[] { + new STFReader.TokenProcessor("clear", ()=>{ if(stf.ReadBoolBlock(true)) + Weather[(int)WeatherType.Clear] = true; }), + new STFReader.TokenProcessor("snow", ()=>{ if(stf.ReadBoolBlock(true)) + Weather[(int)WeatherType.Snow] = true; }), + new STFReader.TokenProcessor("rain", ()=>{ if(stf.ReadBoolBlock(true)) + Weather[(int)WeatherType.Rain] = true; }), + }); + }), }); //if (Volume > 1) Volume /= 100f; } diff --git a/Source/RunActivity/Viewer3D/Sound.cs b/Source/RunActivity/Viewer3D/Sound.cs index d17e9cc98..cadd5c978 100644 --- a/Source/RunActivity/Viewer3D/Sound.cs +++ b/Source/RunActivity/Viewer3D/Sound.cs @@ -1444,6 +1444,32 @@ private void SetFreqAndVolume() volume *= ((MSTSWagon)SoundSource.Viewer.Camera.AttachedCar).TrackSoundPassThruPercent * 0.01f; } + // check if time of day, season and weather enable the sound; if not, set volume to zero + if (MSTSStream?.TimeIntervals != null) + { + var outOfInterval = true; + foreach (var timeInterval in MSTSStream.TimeIntervals) + { + int hourOfDay = (int)SoundSource.Viewer.Simulator.ClockTime / 3600; + if (hourOfDay >= timeInterval[0] && hourOfDay < timeInterval[1]) + { + outOfInterval = false; + break; + } + } + if (outOfInterval) + volume = 0; + } + if (MSTSStream?.Season != null ) + { + if (!MSTSStream.Season[(int)(SoundSource.Viewer.Simulator.Season)]) + volume = 0; + } + if (MSTSStream?.Weather != null ) + { + if (!MSTSStream.Weather[(int)(SoundSource.Viewer.Simulator.WeatherType)]) + volume = 0; + } ALSoundSource.Volume = volume; }