1 /*****************************************************************************
2  * Copyright (c) 2014-2020 OpenRCT2 developers
3  *
4  * For a complete list of all authors, please refer to contributors.md
5  * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6  *
7  * OpenRCT2 is licensed under the GNU General Public License version 3.
8  *****************************************************************************/
9 
10 #include "audio.h"
11 
12 #include "../Context.h"
13 #include "../Intro.h"
14 #include "../OpenRCT2.h"
15 #include "../config/Config.h"
16 #include "../core/File.h"
17 #include "../core/FileStream.h"
18 #include "../core/Memory.hpp"
19 #include "../core/String.hpp"
20 #include "../interface/Viewport.h"
21 #include "../localisation/Language.h"
22 #include "../localisation/StringIds.h"
23 #include "../peep/Peep.h"
24 #include "../ride/Ride.h"
25 #include "../ride/RideAudio.h"
26 #include "../ui/UiContext.h"
27 #include "../util/Util.h"
28 #include "AudioContext.h"
29 #include "AudioMixer.h"
30 
31 #include <algorithm>
32 #include <vector>
33 
34 namespace OpenRCT2::Audio
35 {
36     struct AudioParams
37     {
38         bool in_range;
39         int32_t volume;
40         int32_t pan;
41     };
42 
43     static std::vector<std::string> _audioDevices;
44     static int32_t _currentAudioDevice = -1;
45 
46     bool gGameSoundsOff = false;
47     int32_t gVolumeAdjustZoom = 0;
48 
49     void* gTitleMusicChannel = nullptr;
50     void* gWeatherSoundChannel = nullptr;
51 
52     VehicleSound gVehicleSoundList[MaxVehicleSounds];
53 
54     // clang-format off
55     static int32_t SoundVolumeAdjust[RCT2SoundCount] =
56     {
57         0,      // LiftClassic
58         0,      // TrackFrictionClassicWood
59         0,      // FrictionClassic
60         0,      // Scream1
61         0,      // Click1
62         0,      // Click2
63         0,      // PlaceItem
64         0,      // Scream2
65         0,      // Scream3
66         0,      // Scream4
67         0,      // Scream5
68         0,      // Scream6
69         0,      // LiftFrictionWheels
70         -400,   // Purchase
71         0,      // Crash
72         0,      // LayingOutWater
73         0,      // Water1
74         0,      // Water2
75         0,      // TrainWhistle
76         0,      // TrainDeparting
77         -1000,  // WaterSplash
78         0,      // GoKartEngine
79         -800,   // RideLaunch1
80         -1700,  // RideLaunch2
81         -700,   // Cough1
82         -700,   // Cough2
83         -700,   // Cough3
84         -700,   // Cough4
85         0,      // Rain
86         0,      // Thunder1
87         0,      // Thunder2
88         0,      // TrackFrictionTrain
89         0,      // TrackFrictionWater
90         0,      // BalloonPop
91         -700,   // MechanicFix
92         0,      // Scream7
93         -2500,  // ToiletFlush original value: -1000
94         0,      // Click3
95         0,      // Quack
96         0,      // NewsItem
97         0,      // WindowOpen
98         -900,   // Laugh1
99         -900,   // Laugh2
100         -900,   // Laugh3
101         0,      // Applause
102         -600,   // HauntedHouseScare
103         -700,   // HauntedHouseScream1
104         -700,   // HauntedHouseScream2
105         -2550,  // BlockBrakeClose
106         -2900,  // BlockBrakeRelease
107         0,      // Error
108         -3400,  // BrakeRelease
109         0,      // LiftArrow
110         0,      // LiftWood
111         0,      // TrackFrictionWood
112         0,      // LiftWildMouse
113         0,      // LiftBM
114         0,      // TrackFrictionBM
115         0,      // Scream8
116         0,      // Tram
117         -2000,  // DoorOpen
118         -2700,  // DoorClose
119         -700    // Portcullis
120     };
121     // clang-format on
122 
123     static AudioParams GetParametersFromLocation(SoundId soundId, const CoordsXYZ& location);
124 
IsAvailable()125     bool IsAvailable()
126     {
127         if (_currentAudioDevice == -1)
128             return false;
129         if (gGameSoundsOff)
130             return false;
131         if (!gConfigSound.sound_enabled)
132             return false;
133         if (gOpenRCT2Headless)
134             return false;
135         return true;
136     }
137 
Init()138     void Init()
139     {
140         if (str_is_null_or_empty(gConfigSound.device))
141         {
142             Mixer_Init(nullptr);
143             _currentAudioDevice = 0;
144         }
145         else
146         {
147             Mixer_Init(gConfigSound.device);
148 
149             PopulateDevices();
150             for (int32_t i = 0; i < GetDeviceCount(); i++)
151             {
152                 if (_audioDevices[i] == gConfigSound.device)
153                 {
154                     _currentAudioDevice = i;
155                 }
156             }
157         }
158     }
159 
PopulateDevices()160     void PopulateDevices()
161     {
162         auto audioContext = OpenRCT2::GetContext()->GetAudioContext();
163         std::vector<std::string> devices = audioContext->GetOutputDevices();
164 
165         // Replace blanks with localised unknown string
166         for (auto& device : devices)
167         {
168             if (device.empty())
169             {
170                 device = language_get_string(STR_OPTIONS_SOUND_VALUE_DEFAULT);
171             }
172         }
173 
174 #ifndef __linux__
175         // The first device is always system default on Windows and macOS
176         std::string defaultDevice = language_get_string(STR_OPTIONS_SOUND_VALUE_DEFAULT);
177         devices.insert(devices.begin(), defaultDevice);
178 #endif
179 
180         _audioDevices = devices;
181     }
182 
Play3D(SoundId soundId,const CoordsXYZ & loc)183     void Play3D(SoundId soundId, const CoordsXYZ& loc)
184     {
185         if (!IsAvailable())
186             return;
187 
188         AudioParams params = GetParametersFromLocation(soundId, loc);
189         if (params.in_range)
190         {
191             Play(soundId, params.volume, params.pan);
192         }
193     }
194 
195     /**
196      * Returns the audio parameters to use when playing the specified sound at a virtual location.
197      * @param soundId The sound effect to be played.
198      * @param location The location at which the sound effect is to be played.
199      * @return The audio parameters to be used when playing this sound effect.
200      */
GetParametersFromLocation(SoundId soundId,const CoordsXYZ & location)201     static AudioParams GetParametersFromLocation(SoundId soundId, const CoordsXYZ& location)
202     {
203         int32_t volumeDown = 0;
204         AudioParams params;
205         params.in_range = true;
206         params.volume = 0;
207         params.pan = 0;
208 
209         auto element = map_get_surface_element_at(location);
210         if (element != nullptr && (element->GetBaseZ()) - 5 > location.z)
211         {
212             volumeDown = 10;
213         }
214 
215         uint8_t rotation = get_current_rotation();
216         auto pos2 = translate_3d_to_2d_with_z(rotation, location);
217 
218         rct_viewport* viewport = nullptr;
219         while ((viewport = window_get_previous_viewport(viewport)) != nullptr)
220         {
221             if (viewport->flags & VIEWPORT_FLAG_SOUND_ON)
222             {
223                 int16_t vx = pos2.x - viewport->viewPos.x;
224                 params.pan = viewport->pos.x + (vx / viewport->zoom);
225                 params.volume = SoundVolumeAdjust[static_cast<uint8_t>(soundId)]
226                     + ((-1024 * viewport->zoom - 1) * (1 << volumeDown)) + 1;
227 
228                 if (!viewport->Contains(pos2) || params.volume < -10000)
229                 {
230                     params.in_range = false;
231                     return params;
232                 }
233             }
234         }
235 
236         return params;
237     }
238 
Play(SoundId soundId,int32_t volume,int32_t pan)239     void Play(SoundId soundId, int32_t volume, int32_t pan)
240     {
241         if (gGameSoundsOff)
242             return;
243 
244         int32_t mixerPan = 0;
245         if (pan != AUDIO_PLAY_AT_CENTRE)
246         {
247             int32_t x2 = pan << 16;
248             uint16_t screenWidth = std::max<int32_t>(64, OpenRCT2::GetContext()->GetUiContext()->GetWidth());
249             mixerPan = ((x2 / screenWidth) - 0x8000) >> 4;
250         }
251 
252         Mixer_Play_Effect(soundId, MIXER_LOOP_NONE, DStoMixerVolume(volume), DStoMixerPan(mixerPan), 1, 1);
253     }
254 
PlayTitleMusic()255     void PlayTitleMusic()
256     {
257         if (gGameSoundsOff || !(gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) || gIntroState != IntroState::None)
258         {
259             StopTitleMusic();
260             return;
261         }
262 
263         if (gTitleMusicChannel != nullptr)
264         {
265             return;
266         }
267 
268         int32_t pathId;
269         switch (gConfigSound.title_music)
270         {
271             case 1:
272                 pathId = PATH_ID_CSS50;
273                 break;
274             case 2:
275                 pathId = PATH_ID_CSS17;
276                 break;
277             case 3:
278                 pathId = (util_rand() & 1) ? PATH_ID_CSS50 : PATH_ID_CSS17;
279                 break;
280             default:
281                 return;
282         }
283 
284         gTitleMusicChannel = Mixer_Play_Music(pathId, MIXER_LOOP_INFINITE, true);
285         if (gTitleMusicChannel != nullptr)
286         {
287             Mixer_Channel_SetGroup(gTitleMusicChannel, OpenRCT2::Audio::MixerGroup::TitleMusic);
288         }
289     }
290 
StopAll()291     void StopAll()
292     {
293         StopTitleMusic();
294         StopVehicleSounds();
295         RideAudio::StopAllChannels();
296         peep_stop_crowd_noise();
297         StopWeatherSound();
298     }
299 
GetDeviceCount()300     int32_t GetDeviceCount()
301     {
302         return static_cast<int32_t>(_audioDevices.size());
303     }
304 
GetDeviceName(int32_t index)305     const std::string& GetDeviceName(int32_t index)
306     {
307         if (index < 0 || index >= GetDeviceCount())
308         {
309             static std::string InvalidDevice = "Invalid Device";
310             return InvalidDevice;
311         }
312         return _audioDevices[index];
313     }
314 
GetCurrentDeviceIndex()315     int32_t GetCurrentDeviceIndex()
316     {
317         return _currentAudioDevice;
318     }
319 
StopTitleMusic()320     void StopTitleMusic()
321     {
322         if (gTitleMusicChannel != nullptr)
323         {
324             Mixer_Stop_Channel(gTitleMusicChannel);
325             gTitleMusicChannel = nullptr;
326         }
327     }
328 
StopWeatherSound()329     void StopWeatherSound()
330     {
331         if (gWeatherSoundChannel != nullptr)
332         {
333             Mixer_Stop_Channel(gWeatherSoundChannel);
334             gWeatherSoundChannel = nullptr;
335         }
336     }
337 
InitRideSoundsAndInfo()338     void InitRideSoundsAndInfo()
339     {
340         InitRideSounds(0);
341     }
342 
InitRideSounds(int32_t device)343     void InitRideSounds(int32_t device)
344     {
345         Close();
346         for (auto& vehicleSound : gVehicleSoundList)
347         {
348             vehicleSound.id = SoundIdNull;
349         }
350 
351         _currentAudioDevice = device;
352         config_save_default();
353     }
354 
Close()355     void Close()
356     {
357         peep_stop_crowd_noise();
358         StopTitleMusic();
359         RideAudio::StopAllChannels();
360         StopWeatherSound();
361         _currentAudioDevice = -1;
362     }
363 
ToggleAllSounds()364     void ToggleAllSounds()
365     {
366         gConfigSound.master_sound_enabled = !gConfigSound.master_sound_enabled;
367         if (gConfigSound.master_sound_enabled)
368         {
369             Resume();
370         }
371         else
372         {
373             StopTitleMusic();
374             Pause();
375         }
376 
377         window_invalidate_by_class(WC_OPTIONS);
378     }
379 
Pause()380     void Pause()
381     {
382         gGameSoundsOff = true;
383         StopVehicleSounds();
384         RideAudio::StopAllChannels();
385         peep_stop_crowd_noise();
386         StopWeatherSound();
387     }
388 
Resume()389     void Resume()
390     {
391         gGameSoundsOff = false;
392     }
393 
StopVehicleSounds()394     void StopVehicleSounds()
395     {
396         if (!IsAvailable())
397             return;
398 
399         for (auto& vehicleSound : gVehicleSoundList)
400         {
401             if (vehicleSound.id != SoundIdNull)
402             {
403                 vehicleSound.id = SoundIdNull;
404                 if (vehicleSound.TrackSound.Id != SoundId::Null)
405                 {
406                     Mixer_Stop_Channel(vehicleSound.TrackSound.Channel);
407                 }
408                 if (vehicleSound.OtherSound.Id != SoundId::Null)
409                 {
410                     Mixer_Stop_Channel(vehicleSound.OtherSound.Channel);
411                 }
412             }
413         }
414     }
415 
416 } // namespace OpenRCT2::Audio
417