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