1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "ags/shared/core/platform.h"
24 #include "ags/engine/media/audio/audio.h"
25 #include "ags/shared/ac/audio_clip_type.h"
26 #include "ags/shared/ac/game_setup_struct.h"
27 #include "ags/engine/ac/dynobj/cc_audio_clip.h"
28 #include "ags/engine/ac/dynobj/cc_audio_channel.h"
29 #include "ags/engine/ac/game_state.h"
30 #include "ags/engine/script/script_runtime.h"
31 #include "ags/engine/ac/audio_channel.h"
32 #include "ags/engine/ac/audio_clip.h"
33 #include "ags/engine/ac/game_setup.h"
34 #include "ags/engine/ac/path_helper.h"
35 #include "ags/engine/media/audio/sound.h"
36 #include "ags/engine/debugging/debug_log.h"
37 #include "ags/engine/debugging/debugger.h"
38 #include "ags/shared/ac/common.h"
39 #include "ags/engine/ac/file.h"
40 #include "ags/engine/ac/global_audio.h"
41 #include "ags/shared/util/stream.h"
42 #include "ags/shared/core/asset_manager.h"
43 #include "ags/engine/ac/timer.h"
44 #include "ags/engine/main/game_run.h"
45 #include "ags/globals.h"
46 #include "ags/ags.h"
47
48 namespace AGS3 {
49
50 using namespace AGS::Shared;
51 using namespace AGS::Engine;
52
53 //-----------------------
54 //sound channel management; all access goes through here, which can't be done without a lock
55
AudioChannelsLock()56 AudioChannelsLock::AudioChannelsLock() : MutexLock(::AGS::g_vm->_sMutex) {
57 }
58
GetChannel(int index)59 SOUNDCLIP *AudioChannelsLock::GetChannel(int index) {
60 return _GP(audioChannels)[index];
61 }
62
GetChannelIfPlaying(int index)63 SOUNDCLIP *AudioChannelsLock::GetChannelIfPlaying(int index) {
64 auto *ch = _GP(audioChannels)[index];
65 return (ch != nullptr && ch->is_playing()) ? ch : nullptr;
66 }
67
SetChannel(int index,SOUNDCLIP * ch)68 SOUNDCLIP *AudioChannelsLock::SetChannel(int index, SOUNDCLIP *ch) {
69 SoundClipWaveBase *wavClip = dynamic_cast<SoundClipWaveBase *>(ch);
70 if (wavClip) {
71 switch (index) {
72 case SCHAN_SPEECH:
73 wavClip->setType(Audio::Mixer::kSpeechSoundType);
74 break;
75 case SCHAN_MUSIC:
76 wavClip->setType(Audio::Mixer::kMusicSoundType);
77 break;
78 default:
79 wavClip->setType(Audio::Mixer::kSFXSoundType);
80 break;
81 }
82 }
83
84 // TODO: store clips in smart pointers
85 if (_GP(audioChannels)[index] == ch)
86 Debug::Printf(kDbgMsg_Warn, "WARNING: channel %d - same clip assigned", index);
87 else if (_GP(audioChannels)[index] != nullptr && ch != nullptr)
88 Debug::Printf(kDbgMsg_Warn, "WARNING: channel %d - clip overwritten", index);
89 _GP(audioChannels)[index] = ch;
90 return ch;
91 }
92
MoveChannel(int to,int from)93 SOUNDCLIP *AudioChannelsLock::MoveChannel(int to, int from) {
94 auto from_ch = _GP(audioChannels)[from];
95 _GP(audioChannels)[from] = nullptr;
96 return SetChannel(to, from_ch);
97 }
98
99 //-----------------------
100 // Channel helpers
101
channel_has_clip(int chanid)102 bool channel_has_clip(int chanid) {
103 AudioChannelsLock lock;
104 return lock.GetChannel(chanid) != nullptr;
105 }
106
channel_is_playing(int chanid)107 bool channel_is_playing(int chanid) {
108 AudioChannelsLock lock;
109 return lock.GetChannelIfPlaying(chanid) != nullptr;
110 }
111
set_clip_to_channel(int chanid,SOUNDCLIP * clip)112 void set_clip_to_channel(int chanid, SOUNDCLIP *clip) {
113 AudioChannelsLock lock;
114 lock.SetChannel(chanid, clip);
115 }
116 //-----------------------
117
calculate_reserved_channel_count()118 void calculate_reserved_channel_count() {
119 int reservedChannels = 0;
120 for (size_t i = 0; i < _GP(game).audioClipTypes.size(); i++) {
121 reservedChannels += _GP(game).audioClipTypes[i].reservedChannels;
122 }
123 _G(reserved_channel_count) = reservedChannels;
124 }
125
update_clip_default_volume(ScriptAudioClip * audioClip)126 void update_clip_default_volume(ScriptAudioClip *audioClip) {
127 if (_GP(play).default_audio_type_volumes[audioClip->type] >= 0) {
128 audioClip->defaultVolume = _GP(play).default_audio_type_volumes[audioClip->type];
129 }
130 }
131
start_fading_in_new_track_if_applicable(int fadeInChannel,ScriptAudioClip * newSound)132 void start_fading_in_new_track_if_applicable(int fadeInChannel, ScriptAudioClip *newSound) {
133 int crossfadeSpeed = _GP(game).audioClipTypes[newSound->type].crossfadeSpeed;
134 if (crossfadeSpeed > 0) {
135 update_clip_default_volume(newSound);
136 _GP(play).crossfade_in_volume_per_step = crossfadeSpeed;
137 _GP(play).crossfade_final_volume_in = newSound->defaultVolume;
138 _GP(play).crossfading_in_channel = fadeInChannel;
139 }
140 }
141
move_track_to_crossfade_channel(int currentChannel,int crossfadeSpeed,int fadeInChannel,ScriptAudioClip * newSound)142 static void move_track_to_crossfade_channel(int currentChannel, int crossfadeSpeed, int fadeInChannel, ScriptAudioClip *newSound) {
143 AudioChannelsLock lock;
144 stop_and_destroy_channel(SPECIAL_CROSSFADE_CHANNEL);
145 auto *cfade_clip = lock.MoveChannel(SPECIAL_CROSSFADE_CHANNEL, currentChannel);
146 if (!cfade_clip)
147 return;
148
149 _GP(play).crossfading_out_channel = SPECIAL_CROSSFADE_CHANNEL;
150 _GP(play).crossfade_step = 0;
151 _GP(play).crossfade_initial_volume_out = cfade_clip->get_volume();
152 _GP(play).crossfade_out_volume_per_step = crossfadeSpeed;
153
154 _GP(play).crossfading_in_channel = fadeInChannel;
155 if (newSound != nullptr) {
156 start_fading_in_new_track_if_applicable(fadeInChannel, newSound);
157 }
158 }
159
stop_or_fade_out_channel(int fadeOutChannel,int fadeInChannel,ScriptAudioClip * newSound)160 void stop_or_fade_out_channel(int fadeOutChannel, int fadeInChannel, ScriptAudioClip *newSound) {
161 ScriptAudioClip *sourceClip = AudioChannel_GetPlayingClip(&_G(scrAudioChannel)[fadeOutChannel]);
162 if ((sourceClip != nullptr) && (_GP(game).audioClipTypes[sourceClip->type].crossfadeSpeed > 0)) {
163 move_track_to_crossfade_channel(fadeOutChannel, _GP(game).audioClipTypes[sourceClip->type].crossfadeSpeed, fadeInChannel, newSound);
164 } else {
165 stop_and_destroy_channel(fadeOutChannel);
166 }
167 }
168
find_free_audio_channel(ScriptAudioClip * clip,int priority,bool interruptEqualPriority)169 static int find_free_audio_channel(ScriptAudioClip *clip, int priority, bool interruptEqualPriority) {
170 AudioChannelsLock lock;
171
172 int lowestPrioritySoFar = 9999999;
173 int lowestPriorityID = -1;
174 int channelToUse = -1;
175
176 if (!interruptEqualPriority)
177 priority--;
178
179 int startAtChannel = _G(reserved_channel_count);
180 int endBeforeChannel = MAX_SOUND_CHANNELS;
181
182 if (_GP(game).audioClipTypes[clip->type].reservedChannels > 0) {
183 startAtChannel = 0;
184 for (int i = 0; i < clip->type; i++) {
185 startAtChannel += _GP(game).audioClipTypes[i].reservedChannels;
186 }
187 endBeforeChannel = startAtChannel + _GP(game).audioClipTypes[clip->type].reservedChannels;
188 }
189
190 for (int i = startAtChannel; i < endBeforeChannel; i++) {
191 auto *ch = lock.GetChannelIfPlaying(i);
192 if (ch == nullptr) {
193 channelToUse = i;
194 stop_and_destroy_channel(i);
195 break;
196 }
197 if ((ch->_priority < lowestPrioritySoFar) &&
198 (ch->_sourceClipType == clip->type)) {
199 lowestPrioritySoFar = ch->_priority;
200 lowestPriorityID = i;
201 }
202 }
203
204 if ((channelToUse < 0) && (lowestPriorityID >= 0) &&
205 (lowestPrioritySoFar <= priority)) {
206 stop_or_fade_out_channel(lowestPriorityID, lowestPriorityID, clip);
207 channelToUse = lowestPriorityID;
208 } else if ((channelToUse >= 0) && (_GP(play).crossfading_in_channel < 1)) {
209 start_fading_in_new_track_if_applicable(channelToUse, clip);
210 }
211 return channelToUse;
212 }
213
is_audiotype_allowed_to_play(AudioFileType type)214 bool is_audiotype_allowed_to_play(AudioFileType type) {
215 return _GP(usetup).audio_backend != 0;
216 }
217
load_sound_clip(ScriptAudioClip * audioClip,bool repeat)218 SOUNDCLIP *load_sound_clip(ScriptAudioClip *audioClip, bool repeat) {
219 if (!is_audiotype_allowed_to_play((AudioFileType)audioClip->fileType)) {
220 return nullptr;
221 }
222
223 update_clip_default_volume(audioClip);
224
225 SOUNDCLIP *soundClip = nullptr;
226 AssetPath asset_name = get_audio_clip_assetpath(audioClip->bundlingType, audioClip->fileName);
227 switch (audioClip->fileType) {
228 case eAudioFileOGG:
229 soundClip = my_load_static_ogg(asset_name, audioClip->defaultVolume, repeat);
230 break;
231 case eAudioFileMP3:
232 soundClip = my_load_static_mp3(asset_name, audioClip->defaultVolume, repeat);
233 break;
234 case eAudioFileWAV:
235 case eAudioFileVOC:
236 soundClip = my_load_wave(asset_name, audioClip->defaultVolume, repeat);
237 break;
238 case eAudioFileMIDI:
239 soundClip = my_load_midi(asset_name, repeat);
240 break;
241 case eAudioFileMOD:
242 soundClip = my_load_mod(asset_name, repeat);
243 break;
244 default:
245 quitprintf("AudioClip.Play: invalid audio file type encountered: %d", audioClip->fileType);
246 }
247 if (soundClip != nullptr) {
248 soundClip->set_volume_percent(audioClip->defaultVolume);
249 soundClip->_sourceClip = audioClip;
250 soundClip->_sourceClipType = audioClip->type;
251 }
252 return soundClip;
253 }
254
audio_update_polled_stuff()255 static void audio_update_polled_stuff() {
256 ///////////////////////////////////////////////////////////////////////////
257 // Do crossfade
258 _GP(play).crossfade_step++;
259
260 AudioChannelsLock lock;
261
262 if (_GP(play).crossfading_out_channel > 0 && !lock.GetChannelIfPlaying(_GP(play).crossfading_out_channel))
263 _GP(play).crossfading_out_channel = 0;
264
265 if (_GP(play).crossfading_out_channel > 0) {
266 SOUNDCLIP *ch = lock.GetChannel(_GP(play).crossfading_out_channel);
267 int newVolume = ch ? ch->get_volume() - _GP(play).crossfade_out_volume_per_step : 0;
268 if (newVolume > 0) {
269 AudioChannel_SetVolume(&_G(scrAudioChannel)[_GP(play).crossfading_out_channel], newVolume);
270 } else {
271 stop_and_destroy_channel(_GP(play).crossfading_out_channel);
272 _GP(play).crossfading_out_channel = 0;
273 }
274 }
275
276 if (_GP(play).crossfading_in_channel > 0 && !lock.GetChannelIfPlaying(_GP(play).crossfading_in_channel))
277 _GP(play).crossfading_in_channel = 0;
278
279 if (_GP(play).crossfading_in_channel > 0) {
280 SOUNDCLIP *ch = lock.GetChannel(_GP(play).crossfading_in_channel);
281 int newVolume = ch ? ch->get_volume() + _GP(play).crossfade_in_volume_per_step : 0;
282 if (newVolume > _GP(play).crossfade_final_volume_in) {
283 newVolume = _GP(play).crossfade_final_volume_in;
284 }
285
286 AudioChannel_SetVolume(&_G(scrAudioChannel)[_GP(play).crossfading_in_channel], newVolume);
287
288 if (newVolume >= _GP(play).crossfade_final_volume_in) {
289 _GP(play).crossfading_in_channel = 0;
290 }
291 }
292
293 ///////////////////////////////////////////////////////////////////////////
294 // Do audio queue
295 if (_GP(play).new_music_queue_size > 0) {
296 for (int i = 0; i < _GP(play).new_music_queue_size; i++) {
297 ScriptAudioClip *clip = &_GP(game).audioClips[_GP(play).new_music_queue[i].audioClipIndex];
298 int channel = find_free_audio_channel(clip, clip->defaultPriority, false);
299 if (channel >= 0) {
300 QueuedAudioItem itemToPlay = _GP(play).new_music_queue[i];
301
302 _GP(play).new_music_queue_size--;
303 for (int j = i; j < _GP(play).new_music_queue_size; j++) {
304 _GP(play).new_music_queue[j] = _GP(play).new_music_queue[j + 1];
305 }
306
307 play_audio_clip_on_channel(channel, clip, itemToPlay.priority, itemToPlay.repeat, 0, itemToPlay.cachedClip);
308 i--;
309 }
310 }
311 }
312
313 ///////////////////////////////////////////////////////////////////////////
314 // Do non-blocking voice speech
315 // NOTE: there's only one speech channel, therefore it's either blocking
316 // or non-blocking at any given time. If it's changed, we'd need to keep
317 // record of every channel, or keep a count of active channels.
318 if (_GP(play).IsNonBlockingVoiceSpeech()) {
319 if (!channel_is_playing(SCHAN_SPEECH)) {
320 stop_voice_nonblocking();
321 }
322 }
323 }
324
325 // Applies a volume drop modifier to the clip, in accordance to its audio type
apply_volume_drop_to_clip(SOUNDCLIP * clip)326 static void apply_volume_drop_to_clip(SOUNDCLIP *clip) {
327 int audiotype = clip->_sourceClipType;
328 clip->apply_volume_modifier(-(_GP(game).audioClipTypes[audiotype].volume_reduction_while_speech_playing * 255 / 100));
329 }
330
queue_audio_clip_to_play(ScriptAudioClip * clip,int priority,int repeat)331 static void queue_audio_clip_to_play(ScriptAudioClip *clip, int priority, int repeat) {
332 if (_GP(play).new_music_queue_size >= MAX_QUEUED_MUSIC) {
333 debug_script_log("Too many queued music, cannot add %s", clip->scriptName.GetCStr());
334 return;
335 }
336
337 SOUNDCLIP *cachedClip = load_sound_clip(clip, (repeat != 0));
338 if (cachedClip != nullptr) {
339 _GP(play).new_music_queue[_GP(play).new_music_queue_size].audioClipIndex = clip->id;
340 _GP(play).new_music_queue[_GP(play).new_music_queue_size].priority = priority;
341 _GP(play).new_music_queue[_GP(play).new_music_queue_size].repeat = (repeat != 0);
342 _GP(play).new_music_queue[_GP(play).new_music_queue_size].cachedClip = cachedClip;
343 _GP(play).new_music_queue_size++;
344 }
345 }
346
play_audio_clip_on_channel(int channel,ScriptAudioClip * clip,int priority,int repeat,int fromOffset,SOUNDCLIP * soundfx)347 ScriptAudioChannel *play_audio_clip_on_channel(int channel, ScriptAudioClip *clip, int priority, int repeat, int fromOffset, SOUNDCLIP *soundfx) {
348 if (soundfx == nullptr) {
349 soundfx = load_sound_clip(clip, (repeat) ? true : false);
350 }
351 if (soundfx == nullptr) {
352 debug_script_log("AudioClip.Play: unable to load sound file");
353 if (_GP(play).crossfading_in_channel == channel) {
354 _GP(play).crossfading_in_channel = 0;
355 }
356 return nullptr;
357 }
358 soundfx->_priority = priority;
359
360 if (_GP(play).crossfading_in_channel == channel) {
361 soundfx->set_volume_percent(0);
362 }
363
364 // Mute the audio clip if fast-forwarding the cutscene
365 if (_GP(play).fast_forward) {
366 soundfx->set_mute(true);
367
368 // CHECKME!!
369 // [IKM] According to the 3.2.1 logic the clip will restore
370 // its value after cutscene, but only if originalVolAsPercentage
371 // is not zeroed. Something I am not sure about: why does it
372 // disable the clip under condition that there's more than one
373 // channel for this audio type? It does not even check if
374 // anything of this type is currently playing.
375 if (_GP(game).audioClipTypes[clip->type].reservedChannels != 1)
376 soundfx->set_volume_percent(0);
377 }
378
379 if (soundfx->play_from(fromOffset) == 0) {
380 // not assigned to a channel, so clean up manually.
381 soundfx->destroy();
382 delete soundfx;
383 soundfx = nullptr;
384 debug_script_log("AudioClip.Play: failed to play sound file");
385 return nullptr;
386 }
387
388 // Apply volume drop if any speech voice-over is currently playing
389 // NOTE: there is a confusing logic in sound clip classes, that they do not use
390 // any modifiers when begin playing, therefore we must apply this only after
391 // playback was started.
392 if (!_GP(play).fast_forward && _GP(play).speech_has_voice)
393 apply_volume_drop_to_clip(soundfx);
394
395 set_clip_to_channel(channel, soundfx);
396 return &_G(scrAudioChannel)[channel];
397 }
398
remove_clips_of_type_from_queue(int audioType)399 void remove_clips_of_type_from_queue(int audioType) {
400 int aa;
401 for (aa = 0; aa < _GP(play).new_music_queue_size; aa++) {
402 ScriptAudioClip *clip = &_GP(game).audioClips[_GP(play).new_music_queue[aa].audioClipIndex];
403 if (clip->type == audioType) {
404 _GP(play).new_music_queue_size--;
405 for (int bb = aa; bb < _GP(play).new_music_queue_size; bb++)
406 _GP(play).new_music_queue[bb] = _GP(play).new_music_queue[bb + 1];
407 aa--;
408 }
409 }
410 }
411
update_queued_clips_volume(int audioType,int new_vol)412 void update_queued_clips_volume(int audioType, int new_vol) {
413 for (int i = 0; i < _GP(play).new_music_queue_size; ++i) {
414 // NOTE: if clip is uncached, the volume will be set from defaults when it is loaded
415 SOUNDCLIP *sndclip = _GP(play).new_music_queue[i].cachedClip;
416 if (sndclip) {
417 ScriptAudioClip *clip = &_GP(game).audioClips[_GP(play).new_music_queue[i].audioClipIndex];
418 if (clip->type == audioType)
419 sndclip->set_volume_percent(new_vol);
420 }
421 }
422 }
423
play_audio_clip(ScriptAudioClip * clip,int priority,int repeat,int fromOffset,bool queueIfNoChannel)424 ScriptAudioChannel *play_audio_clip(ScriptAudioClip *clip, int priority, int repeat, int fromOffset, bool queueIfNoChannel) {
425 if (!queueIfNoChannel)
426 remove_clips_of_type_from_queue(clip->type);
427
428 if (priority == SCR_NO_VALUE)
429 priority = clip->defaultPriority;
430 if (repeat == SCR_NO_VALUE)
431 repeat = clip->defaultRepeat;
432
433 int channel = find_free_audio_channel(clip, priority, !queueIfNoChannel);
434 if (channel < 0) {
435 if (queueIfNoChannel)
436 queue_audio_clip_to_play(clip, priority, repeat);
437 else
438 debug_script_log("AudioClip.Play: no channels available to interrupt PRI:%d TYPE:%d", priority, clip->type);
439
440 return nullptr;
441 }
442
443 return play_audio_clip_on_channel(channel, clip, priority, repeat, fromOffset);
444 }
445
play_audio_clip_by_index(int audioClipIndex)446 ScriptAudioChannel *play_audio_clip_by_index(int audioClipIndex) {
447 if ((audioClipIndex >= 0) && ((size_t)audioClipIndex < _GP(game).audioClips.size()))
448 return AudioClip_Play(&_GP(game).audioClips[audioClipIndex], SCR_NO_VALUE, SCR_NO_VALUE);
449 else
450 return nullptr;
451 }
452
stop_and_destroy_channel_ex(int chid,bool resetLegacyMusicSettings)453 void stop_and_destroy_channel_ex(int chid, bool resetLegacyMusicSettings) {
454 if ((chid < 0) || (chid > MAX_SOUND_CHANNELS))
455 quit("!StopChannel: invalid channel ID");
456
457 AudioChannelsLock lock;
458 SOUNDCLIP *ch = lock.GetChannel(chid);
459
460 if (ch != nullptr) {
461 ch->destroy();
462 delete ch;
463 lock.SetChannel(chid, nullptr);
464 ch = nullptr;
465 }
466
467 if (_GP(play).crossfading_in_channel == chid)
468 _GP(play).crossfading_in_channel = 0;
469 if (_GP(play).crossfading_out_channel == chid)
470 _GP(play).crossfading_out_channel = 0;
471 // don't update '_G(crossFading)' here as it is updated in all the cross-fading functions.
472
473 // destroyed an ambient sound channel
474 if (_GP(ambient)[chid].channel > 0)
475 _GP(ambient)[chid].channel = 0;
476
477 if ((chid == SCHAN_MUSIC) && (resetLegacyMusicSettings)) {
478 _GP(play).cur_music_number = -1;
479 _G(current_music_type) = 0;
480 }
481 }
482
stop_and_destroy_channel(int chid)483 void stop_and_destroy_channel(int chid) {
484 stop_and_destroy_channel_ex(chid, true);
485 }
486
487
488
489 // ***** BACKWARDS COMPATIBILITY WITH OLD AUDIO SYSTEM ***** //
490
get_old_style_number_for_sound(int sound_number)491 int get_old_style_number_for_sound(int sound_number) {
492 // In the legacy audio system treat sound_number as an old style number
493 if (_GP(game).IsLegacyAudioSystem()) {
494 return sound_number;
495 }
496
497 // Treat sound number as a real clip index
498 if (sound_number >= 0) {
499 int old_style_number = 0;
500 if (sscanf(_GP(game).audioClips[sound_number].scriptName.GetCStr(), "aSound%d", &old_style_number) == 1)
501 return old_style_number;
502 }
503
504 return 0;
505 }
506
load_sound_clip_from_old_style_number(bool isMusic,int indexNumber,bool repeat)507 SOUNDCLIP *load_sound_clip_from_old_style_number(bool isMusic, int indexNumber, bool repeat) {
508 ScriptAudioClip *audioClip = GetAudioClipForOldStyleNumber(_GP(game), isMusic, indexNumber);
509
510 if (audioClip != nullptr) {
511 return load_sound_clip(audioClip, repeat);
512 }
513
514 return nullptr;
515 }
516
517 //=============================================================================
518
get_volume_adjusted_for_distance(int volume,int sndX,int sndY,int sndMaxDist)519 int get_volume_adjusted_for_distance(int volume, int sndX, int sndY, int sndMaxDist) {
520 int distx = _G(playerchar)->x - sndX;
521 int disty = _G(playerchar)->y - sndY;
522 // it uses Allegro's "fix" sqrt without the ::
523 int dist = (int)::sqrt((double)(distx * distx + disty * disty));
524
525 // if they're quite close, full volume
526 int wantvol = volume;
527
528 if (dist >= AMBIENCE_FULL_DIST) {
529 // get the relative volume
530 wantvol = ((dist - AMBIENCE_FULL_DIST) * volume) / sndMaxDist;
531 // closer is louder
532 wantvol = volume - wantvol;
533 }
534
535 return wantvol;
536 }
537
update_directional_sound_vol()538 void update_directional_sound_vol() {
539 AudioChannelsLock lock;
540
541 for (int chnum = 1; chnum < MAX_SOUND_CHANNELS; chnum++) {
542 auto *ch = lock.GetChannelIfPlaying(chnum);
543 if ((ch != nullptr) && (ch->_xSource >= 0)) {
544 ch->apply_directional_modifier(
545 get_volume_adjusted_for_distance(ch->_vol,
546 ch->_xSource,
547 ch->_ySource,
548 ch->_maximumPossibleDistanceAway) -
549 ch->_vol);
550 }
551 }
552 }
553
update_ambient_sound_vol()554 void update_ambient_sound_vol() {
555 AudioChannelsLock lock;
556
557 for (int chan = 1; chan < MAX_SOUND_CHANNELS; chan++) {
558
559 AmbientSound *thisSound = &_GP(ambient)[chan];
560
561 if (thisSound->channel == 0)
562 continue;
563
564 int sourceVolume = thisSound->vol;
565
566 if (_GP(play).speech_has_voice) {
567 // Negative value means set exactly; positive means drop that amount
568 if (_GP(play).speech_music_drop < 0)
569 sourceVolume = -_GP(play).speech_music_drop;
570 else
571 sourceVolume -= _GP(play).speech_music_drop;
572
573 if (sourceVolume < 0)
574 sourceVolume = 0;
575 if (sourceVolume > 255)
576 sourceVolume = 255;
577 }
578
579 // Adjust ambient volume so it maxes out at overall sound volume
580 int ambientvol = (sourceVolume * _GP(play).sound_volume) / 255;
581
582 int wantvol;
583
584 if ((thisSound->x == 0) && (thisSound->y == 0)) {
585 wantvol = ambientvol;
586 } else {
587 wantvol = get_volume_adjusted_for_distance(ambientvol, thisSound->x, thisSound->y, thisSound->maxdist);
588 }
589
590 auto *ch = lock.GetChannelIfPlaying(thisSound->channel);
591 if (ch)
592 ch->set_volume(wantvol);
593 }
594 }
595
load_sound_and_play(ScriptAudioClip * aclip,bool repeat)596 SOUNDCLIP *load_sound_and_play(ScriptAudioClip *aclip, bool repeat) {
597 SOUNDCLIP *soundfx = load_sound_clip(aclip, repeat);
598 if (!soundfx) {
599 return nullptr;
600 }
601
602 if (soundfx->play() == 0) {
603 // not assigned to a channel, so clean up manually.
604 soundfx->destroy();
605 delete soundfx;
606 return nullptr;
607 }
608
609 return soundfx;
610 }
611
stop_all_sound_and_music()612 void stop_all_sound_and_music() {
613 stopmusic();
614 stop_voice_nonblocking();
615 // make sure it doesn't start crossfading when it comes back
616 _G(crossFading) = 0;
617 // any ambient sound will be aborted
618 for (int i = 0; i <= MAX_SOUND_CHANNELS; i++)
619 stop_and_destroy_channel(i);
620 }
621
shutdown_sound()622 void shutdown_sound() {
623 stop_all_sound_and_music(); // game logic
624 #if !AGS_PLATFORM_SCUMMVM
625 audio_core_shutdown(); // audio core system
626 #endif
627 }
628
629 // the sound will only be played if there is a free channel or
630 // it has a priority >= an existing sound to override
play_sound_priority(int val1,int priority)631 static int play_sound_priority(int val1, int priority) {
632 int lowest_pri = 9999, lowest_pri_id = -1;
633
634 AudioChannelsLock lock;
635
636 // find a free channel to play it on
637 for (int i = SCHAN_NORMAL; i < MAX_SOUND_CHANNELS; i++) {
638 auto *ch = lock.GetChannelIfPlaying(i);
639 if (val1 < 0) {
640 // Playing sound -1 means iterate through and stop all sound
641 if (ch)
642 stop_and_destroy_channel(i);
643 } else if (ch == nullptr || !ch->is_playing()) {
644 // PlaySoundEx will destroy the previous channel value.
645 const int usechan = PlaySoundEx(val1, i);
646 if (usechan >= 0) {
647 // channel will hold a different clip here
648 assert(usechan == i);
649 auto *chan = lock.GetChannel(usechan);
650 if (chan)
651 chan->_priority = priority;
652 }
653 return usechan;
654 } else if (ch->_priority < lowest_pri) {
655 lowest_pri = ch->_priority;
656 lowest_pri_id = i;
657 }
658
659 }
660 if (val1 < 0)
661 return -1;
662
663 // no free channels, see if we have a high enough priority
664 // to override one
665 if (priority >= lowest_pri) {
666 const int usechan = PlaySoundEx(val1, lowest_pri_id);
667 if (usechan >= 0) {
668 assert(usechan == lowest_pri_id);
669 auto *ch = lock.GetChannel(usechan);
670 if (ch)
671 ch->_priority = priority;
672 return usechan;
673 }
674 }
675
676 return -1;
677 }
678
play_sound(int val1)679 int play_sound(int val1) {
680 return play_sound_priority(val1, 10);
681 }
682
683 //=============================================================================
684
cancel_scheduled_music_update()685 void cancel_scheduled_music_update() {
686 _G(music_update_scheduled) = false;
687 }
688
schedule_music_update_at(AGS_Clock::time_point at)689 void schedule_music_update_at(AGS_Clock::time_point at) {
690 _G(music_update_scheduled) = true;
691 _G(music_update_at) = at;
692 }
693
postpone_scheduled_music_update_by(std::chrono::milliseconds duration)694 void postpone_scheduled_music_update_by(std::chrono::milliseconds duration) {
695 if (!_G(music_update_scheduled)) {
696 return;
697 }
698 _G(music_update_at) += duration;
699 }
700
process_scheduled_music_update()701 void process_scheduled_music_update() {
702 if (!_G(music_update_scheduled)) {
703 return;
704 }
705 if (_G(music_update_at) > AGS_Clock::now()) {
706 return;
707 }
708 cancel_scheduled_music_update();
709 update_music_volume();
710 apply_volume_drop_modifier(false);
711 update_ambient_sound_vol();
712 }
713 // end scheduled music update functions
714 //=============================================================================
715
clear_music_cache()716 void clear_music_cache() {
717
718 if (_G(cachedQueuedMusic) != nullptr) {
719 _G(cachedQueuedMusic)->destroy();
720 delete _G(cachedQueuedMusic);
721 _G(cachedQueuedMusic) = nullptr;
722 }
723
724 }
725
726 static void play_new_music(int mnum, SOUNDCLIP *music);
727
play_next_queued()728 void play_next_queued() {
729 // check if there's a queued one to play
730 if (_GP(play).music_queue_size > 0) {
731
732 int tuneToPlay = _GP(play).music_queue[0];
733
734 if (tuneToPlay >= QUEUED_MUSIC_REPEAT) {
735 // Loop it!
736 _GP(play).music_repeat++;
737 play_new_music(tuneToPlay - QUEUED_MUSIC_REPEAT, _G(cachedQueuedMusic));
738 _GP(play).music_repeat--;
739 } else {
740 // Don't loop it!
741 int repeatWas = _GP(play).music_repeat;
742 _GP(play).music_repeat = 0;
743 play_new_music(tuneToPlay, _G(cachedQueuedMusic));
744 _GP(play).music_repeat = repeatWas;
745 }
746
747 // don't free the memory, as it has been transferred onto the
748 // main music channel
749 _G(cachedQueuedMusic) = nullptr;
750
751 _GP(play).music_queue_size--;
752 for (int i = 0; i < _GP(play).music_queue_size; i++)
753 _GP(play).music_queue[i] = _GP(play).music_queue[i + 1];
754
755 if (_GP(play).music_queue_size > 0)
756 _G(cachedQueuedMusic) = load_music_from_disk(_GP(play).music_queue[0], 0);
757 }
758
759 }
760
calculate_max_volume()761 int calculate_max_volume() {
762 // quieter so that sounds can be heard better
763 int newvol = _GP(play).music_master_volume + ((int)_GP(thisroom).Options.MusicVolume) * LegacyRoomVolumeFactor;
764 if (newvol > 255) newvol = 255;
765 if (newvol < 0) newvol = 0;
766
767 if (_GP(play).fast_forward)
768 newvol = 0;
769
770 return newvol;
771 }
772
773 // add/remove the volume drop to the audio channels while speech is playing
apply_volume_drop_modifier(bool applyModifier)774 void apply_volume_drop_modifier(bool applyModifier) {
775 AudioChannelsLock lock;
776
777 for (int i = 0; i < MAX_SOUND_CHANNELS; i++) {
778 auto *ch = lock.GetChannelIfPlaying(i);
779 if (ch && ch->_sourceClip != nullptr) {
780 if (applyModifier)
781 apply_volume_drop_to_clip(ch);
782 else
783 ch->apply_volume_modifier(0); // reset modifier
784 }
785 }
786 }
787
788 // Checks if speech voice-over is currently playing, and reapply volume drop to all other active clips
update_volume_drop_if_voiceover()789 void update_volume_drop_if_voiceover() {
790 apply_volume_drop_modifier(_GP(play).speech_has_voice);
791 }
792
793 // Update the music, and advance the crossfade on a step
794 // (this should only be called once per game loop)
update_audio_system_on_game_loop()795 void update_audio_system_on_game_loop() {
796 update_polled_stuff_if_runtime();
797
798 AudioChannelsLock lock;
799
800 process_scheduled_music_update();
801
802 _G(audio_doing_crossfade) = true;
803
804 audio_update_polled_stuff();
805
806 if (_G(crossFading)) {
807 _G(crossFadeStep)++;
808 update_music_volume();
809 }
810
811 // Check if the current music has finished playing
812 if ((_GP(play).cur_music_number >= 0) && (_GP(play).fast_forward == 0)) {
813 if (IsMusicPlaying() == 0) {
814 // The current music has finished
815 _GP(play).cur_music_number = -1;
816 play_next_queued();
817 } else if ((_GP(game).options[OPT_CROSSFADEMUSIC] > 0) &&
818 (_GP(play).music_queue_size > 0) && (!_G(crossFading))) {
819 // want to crossfade, and new tune in the queue
820 auto *ch = lock.GetChannel(SCHAN_MUSIC);
821 if (ch) {
822 int curpos = ch->get_pos_ms();
823 int muslen = ch->get_length_ms();
824 if ((curpos > 0) && (muslen > 0)) {
825 // we want to crossfade, and we know how far through
826 // the tune we are
827 int takesSteps = calculate_max_volume() / _GP(game).options[OPT_CROSSFADEMUSIC];
828 int takesMs = ::lround(takesSteps * 1000.0f / get_current_fps());
829 if (curpos >= muslen - takesMs)
830 play_next_queued();
831 }
832 }
833 }
834 }
835
836 _G(audio_doing_crossfade) = false;
837
838 }
839
stopmusic()840 void stopmusic() {
841 AudioChannelsLock lock;
842
843 if (_G(crossFading) > 0) {
844 // stop in the middle of a new track fading in
845 // Abort the new track, and let the old one finish fading out
846 stop_and_destroy_channel(_G(crossFading));
847 _G(crossFading) = -1;
848 } else if (_G(crossFading) < 0) {
849 // the music is already fading out
850 if (_GP(game).options[OPT_CROSSFADEMUSIC] <= 0) {
851 // If they have since disabled crossfading, stop the fadeout
852 stop_and_destroy_channel(SCHAN_MUSIC);
853 _G(crossFading) = 0;
854 _G(crossFadeStep) = 0;
855 update_music_volume();
856 }
857 } else if ((_GP(game).options[OPT_CROSSFADEMUSIC] > 0)
858 && (lock.GetChannelIfPlaying(SCHAN_MUSIC) != nullptr)
859 && (_G(current_music_type) != 0)
860 && (_G(current_music_type) != MUS_MIDI)
861 && (_G(current_music_type) != MUS_MOD)) {
862
863 _G(crossFading) = -1;
864 _G(crossFadeStep) = 0;
865 _G(crossFadeVolumePerStep) = _GP(game).options[OPT_CROSSFADEMUSIC];
866 _G(crossFadeVolumeAtStart) = calculate_max_volume();
867 } else
868 stop_and_destroy_channel(SCHAN_MUSIC);
869
870 _GP(play).cur_music_number = -1;
871 _G(current_music_type) = 0;
872 }
873
update_music_volume()874 void update_music_volume() {
875 AudioChannelsLock lock;
876
877 if ((_G(current_music_type)) || (_G(crossFading) < 0)) {
878 // targetVol is the maximum volume we're fading in to
879 // newvol is the starting volume that we faded out from
880 int targetVol = calculate_max_volume();
881 int newvol;
882 if (_G(crossFading))
883 newvol = _G(crossFadeVolumeAtStart);
884 else
885 newvol = targetVol;
886
887 // fading out old track, target volume is silence
888 if (_G(crossFading) < 0)
889 targetVol = 0;
890
891 if (_G(crossFading)) {
892 int curvol = _G(crossFadeVolumePerStep) * _G(crossFadeStep);
893
894 if ((curvol > targetVol) && (curvol > newvol)) {
895 // it has fully faded to the new track
896 newvol = targetVol;
897 stop_and_destroy_channel_ex(SCHAN_MUSIC, false);
898 if (_G(crossFading) > 0) {
899 lock.MoveChannel(SCHAN_MUSIC, _G(crossFading));
900 }
901 _G(crossFading) = 0;
902 } else {
903 if (_G(crossFading) > 0) {
904 auto *ch = lock.GetChannel(_G(crossFading));
905 if (ch)
906 ch->set_volume((curvol > targetVol) ? targetVol : curvol);
907 }
908
909 newvol -= curvol;
910 if (newvol < 0)
911 newvol = 0;
912 }
913 }
914 auto *ch = lock.GetChannel(SCHAN_MUSIC);
915 if (ch)
916 ch->set_volume(newvol);
917 }
918 }
919
920 // Ensures crossfader is stable after loading (or failing to load)
921 // new music
post_new_music_check(int newchannel)922 void post_new_music_check(int newchannel) {
923 AudioChannelsLock lock;
924 if ((_G(crossFading) > 0) && (lock.GetChannel(_G(crossFading)) == nullptr)) {
925 _G(crossFading) = 0;
926 // Was fading out but then they played invalid music, continue to fade out
927 if (lock.GetChannel(SCHAN_MUSIC) != nullptr)
928 _G(crossFading) = -1;
929 }
930
931 }
932
prepare_for_new_music()933 int prepare_for_new_music() {
934 AudioChannelsLock lock;
935
936 int useChannel = SCHAN_MUSIC;
937
938 if ((_GP(game).options[OPT_CROSSFADEMUSIC] > 0)
939 && (lock.GetChannelIfPlaying(SCHAN_MUSIC) != nullptr)
940 && (_G(current_music_type) != MUS_MIDI)
941 && (_G(current_music_type) != MUS_MOD)) {
942
943 if (_G(crossFading) > 0) {
944 // It's still crossfading to the previous track
945 stop_and_destroy_channel_ex(SCHAN_MUSIC, false);
946 lock.MoveChannel(SCHAN_MUSIC, _G(crossFading));
947 _G(crossFading) = 0;
948 update_music_volume();
949 } else if (_G(crossFading) < 0) {
950 // an old track is still fading out, no new music yet
951 // Do nothing, and keep the current crossfade step
952 } else {
953 // start crossfading
954 _G(crossFadeStep) = 0;
955 _G(crossFadeVolumePerStep) = _GP(game).options[OPT_CROSSFADEMUSIC];
956 _G(crossFadeVolumeAtStart) = calculate_max_volume();
957 }
958 useChannel = SPECIAL_CROSSFADE_CHANNEL;
959 _G(crossFading) = useChannel;
960 } else {
961 // crossfading is now turned off
962 stopmusic();
963 // ensure that any traces of old tunes fading are eliminated
964 // (otherwise the new track will be faded out)
965 _G(crossFading) = 0;
966 }
967
968 // Just make sure, because it will be overwritten in a sec
969 if (lock.GetChannel(useChannel) != nullptr)
970 stop_and_destroy_channel(useChannel);
971
972 return useChannel;
973 }
974
get_audio_clip_for_music(int mnum)975 ScriptAudioClip *get_audio_clip_for_music(int mnum) {
976 if (mnum >= QUEUED_MUSIC_REPEAT)
977 mnum -= QUEUED_MUSIC_REPEAT;
978 return GetAudioClipForOldStyleNumber(_GP(game), true, mnum);
979 }
980
load_music_from_disk(int mnum,bool doRepeat)981 SOUNDCLIP *load_music_from_disk(int mnum, bool doRepeat) {
982
983 if (mnum >= QUEUED_MUSIC_REPEAT) {
984 mnum -= QUEUED_MUSIC_REPEAT;
985 doRepeat = true;
986 }
987
988 SOUNDCLIP *loaded = load_sound_clip_from_old_style_number(true, mnum, doRepeat);
989
990 if ((loaded == nullptr) && (mnum > 0)) {
991 debug_script_warn("Music %d not found", mnum);
992 debug_script_log("FAILED to load music %d", mnum);
993 }
994
995 return loaded;
996 }
997
play_new_music(int mnum,SOUNDCLIP * music)998 static void play_new_music(int mnum, SOUNDCLIP *music) {
999 if (_G(debug_flags) & DBG_NOMUSIC)
1000 return;
1001
1002 if ((_GP(play).cur_music_number == mnum) && (music == nullptr)) {
1003 debug_script_log("PlayMusic %d but already playing", mnum);
1004 return; // don't play the music if it's already playing
1005 }
1006
1007 ScriptAudioClip *aclip = get_audio_clip_for_music(mnum);
1008 if (aclip && !is_audiotype_allowed_to_play((AudioFileType)aclip->fileType))
1009 return;
1010
1011 int useChannel = SCHAN_MUSIC;
1012 debug_script_log("Playing music %d", mnum);
1013
1014 if (mnum < 0) {
1015 stopmusic();
1016 return;
1017 }
1018
1019 if (_GP(play).fast_forward) {
1020 // while skipping cutscene, don't change the music
1021 _GP(play).end_cutscene_music = mnum;
1022 return;
1023 }
1024
1025 useChannel = prepare_for_new_music();
1026 _GP(play).cur_music_number = mnum;
1027 _G(current_music_type) = 0;
1028
1029 _GP(play).current_music_repeating = _GP(play).music_repeat;
1030 // now that all the previous music is unloaded, load in the new one
1031
1032 SOUNDCLIP *new_clip;
1033 if (music != nullptr)
1034 new_clip = music;
1035 else
1036 new_clip = load_music_from_disk(mnum, (_GP(play).music_repeat > 0));
1037
1038 AudioChannelsLock lock;
1039 auto *ch = lock.SetChannel(useChannel, new_clip);
1040 if (ch != nullptr) {
1041 if (!ch->play()) {
1042 // previous behavior was to set channel[] to null on error, so continue to do that here.
1043 ch->destroy();
1044 delete ch;
1045 ch = nullptr;
1046 lock.SetChannel(useChannel, nullptr);
1047 } else
1048 _G(current_music_type) = ch->get_sound_type();
1049 }
1050
1051 post_new_music_check(useChannel);
1052 update_music_volume();
1053 }
1054
newmusic(int mnum)1055 void newmusic(int mnum) {
1056 play_new_music(mnum, nullptr);
1057 }
1058
1059 } // namespace AGS3
1060