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 "common/config-manager.h"
24 #include "audio/audiostream.h"
25 #include "audio/mixer.h"
26 #include "sci/resource/resource.h"
27 #include "sci/sound/audio.h"
28 #include "sci/sound/music.h"
29 #include "sci/sound/soundcmd.h"
30 
31 #include "sci/engine/features.h"
32 #include "sci/engine/guest_additions.h"
33 #include "sci/engine/kernel.h"
34 #include "sci/engine/object.h"
35 #include "sci/engine/selector.h"
36 
37 namespace Sci {
38 
SoundCommandParser(ResourceManager * resMan,SegManager * segMan,Kernel * kernel,AudioPlayer * audio,SciVersion soundVersion)39 SoundCommandParser::SoundCommandParser(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, AudioPlayer *audio, SciVersion soundVersion) :
40 	_resMan(resMan), _segMan(segMan), _kernel(kernel), _audio(audio), _soundVersion(soundVersion) {
41 
42 	// Check if the user wants synthesized or digital sound effects in SCI1.1
43 	// games based on the prefer_digitalsfx config setting
44 
45 	// In SCI2 and later games, this check should always be true - there was
46 	// always only one version of each sound effect or digital music track
47 	// (e.g. the menu music in GK1 - there is a sound effect with the same
48 	// resource number, but it's totally unrelated to the menu music).
49 	// The GK1 demo (very late SCI1.1) does the same thing
50 	// TODO: Check the QFG4 demo
51 	_useDigitalSFX = (_soundVersion >= SCI_VERSION_2 || g_sci->getGameId() == GID_GK1DEMO || ConfMan.getBool("prefer_digitalsfx"));
52 
53 	_music = new SciMusic(_soundVersion, _useDigitalSFX);
54 	_music->init();
55 }
56 
~SoundCommandParser()57 SoundCommandParser::~SoundCommandParser() {
58 	delete _music;
59 }
60 
kDoSoundInit(EngineState * s,int argc,reg_t * argv)61 reg_t SoundCommandParser::kDoSoundInit(EngineState *s, int argc, reg_t *argv) {
62 	debugC(kDebugLevelSound, "kDoSound(init): %04x:%04x", PRINT_REG(argv[0]));
63 	processInitSound(argv[0]);
64 	return s->r_acc;
65 }
66 
getSoundResourceId(reg_t obj)67 int SoundCommandParser::getSoundResourceId(reg_t obj) {
68 	int resourceId = obj.getSegment() ? (int)readSelectorValue(_segMan, obj, SELECTOR(number)) : -1;
69 	// Modify the resourceId for the Windows versions that have an alternate MIDI soundtrack, like SSCI did.
70 	if (g_sci->_features->useAltWinGMSound()) {
71 		// Check if the alternate MIDI song actually exists...
72 		// There are cases where it just doesn't exist (e.g. SQ4, room 530 -
73 		// bug #5829). In these cases, use the DOS tracks instead.
74 		if (resourceId && _resMan->testResource(ResourceId(kResourceTypeSound, resourceId + 1000)))
75 			resourceId += 1000;
76 	}
77 	if (g_sci->getGameId() == GID_SQ4 &&
78 		g_sci->getPlatform() == Common::kPlatformWindows &&
79 		_useDigitalSFX &&
80 		resourceId < 1000) {
81 		// SQ4 CD Windows played Windows-exclusive digital samples instead of MIDI sounds
82 		// when available. It did this when the "sound1000" was set to true in sierra.ini,
83 		// which was always the case. If there was no audio resource with the given id,
84 		// it would add 1000 and use that if it existed. Otherwise, it would fall back on
85 		// the MIDI sound resource. Although this behavior existed in other Windows
86 		// SCI 1.1 interpreters, SQ4 CD is the only game known to enable sound1000 in
87 		// sierra.ini and include Windows-exclusive audio resources.
88 		if (!_resMan->testResource(ResourceId(kResourceTypeAudio, resourceId)) &&
89 			_resMan->testResource(ResourceId(kResourceTypeAudio, resourceId + 1000))) {
90 			resourceId += 1000;
91 		}
92 	}
93 
94 	return resourceId;
95 }
96 
initSoundResource(MusicEntry * newSound)97 void SoundCommandParser::initSoundResource(MusicEntry *newSound) {
98 	if (newSound->resourceId) {
99 		newSound->soundRes = new SoundResource(newSound->resourceId, _resMan, _soundVersion);
100 		if (!newSound->soundRes->exists()) {
101 			delete newSound->soundRes;
102 			newSound->soundRes = nullptr;
103 		}
104 	} else {
105 		newSound->soundRes = nullptr;
106 	}
107 
108 	// In SCI1.1 games, sound effects are started from here. If we can find
109 	// a relevant audio resource, play it, otherwise switch to synthesized
110 	// effects. If the resource exists, play it using map 65535 (sound
111 	// effects map)
112 	if (getSciVersion() >= SCI_VERSION_1_1 && _resMan->testResource(ResourceId(kResourceTypeAudio, newSound->resourceId))) {
113 		// Found a relevant audio resource, create an audio stream if there is
114 		// no associated sound resource, or if both resources exist and the
115 		// user wants the digital version.
116 		if (_useDigitalSFX || !newSound->soundRes) {
117 			int sampleLen;
118 #ifdef ENABLE_SCI32
119 			if (_soundVersion >= SCI_VERSION_2) {
120 				newSound->isSample = g_sci->getResMan()->testResource(ResourceId(kResourceTypeAudio, newSound->resourceId)) != nullptr;
121 			} else {
122 #endif
123 				newSound->pStreamAud = _audio->getAudioStream(newSound->resourceId, 65535, &sampleLen);
124 				newSound->soundType = Audio::Mixer::kSFXSoundType;
125 				newSound->isSample = newSound->pStreamAud != nullptr;
126 #ifdef ENABLE_SCI32
127 			}
128 #endif
129 		}
130 	}
131 
132 	if (!newSound->isSample && newSound->soundRes)
133 		_music->soundInitSnd(newSound);
134 }
135 
processInitSound(reg_t obj)136 void SoundCommandParser::processInitSound(reg_t obj) {
137 	int resourceId = getSoundResourceId(obj);
138 
139 	// Check if a track with the same sound object is already playing
140 	MusicEntry *oldSound = _music->getSlot(obj);
141 	if (oldSound)
142 		processDisposeSound(obj);
143 
144 	MusicEntry *newSound = new MusicEntry();
145 	newSound->resourceId = resourceId;
146 	newSound->soundObj = obj;
147 	newSound->loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
148 	newSound->overridePriority = false;
149 	if (_soundVersion <= SCI_VERSION_0_LATE)
150 		newSound->priority = readSelectorValue(_segMan, obj, SELECTOR(priority));
151 	else
152 		newSound->priority = readSelectorValue(_segMan, obj, SELECTOR(priority)) & 0xFF;
153 	if (_soundVersion >= SCI_VERSION_1_EARLY)
154 		newSound->volume = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, MUSIC_VOLUME_MAX);
155 	newSound->reverb = -1;	// initialize to SCI invalid, it'll be set correctly in soundInitSnd() below
156 
157 	debugC(kDebugLevelSound, "kDoSound(init): %04x:%04x number %d, loop %d, prio %d, vol %d", PRINT_REG(obj),
158 			resourceId,	newSound->loop, newSound->priority, newSound->volume);
159 
160 	initSoundResource(newSound);
161 
162 	_music->pushBackSlot(newSound);
163 
164 	if (newSound->soundRes || newSound->isSample) {
165 		// Notify the engine
166 		if (_soundVersion <= SCI_VERSION_0_LATE)
167 			writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundInitialized);
168 		else
169 			writeSelector(_segMan, obj, SELECTOR(nodePtr), obj);
170 	}
171 }
172 
kDoSoundPlay(EngineState * s,int argc,reg_t * argv)173 reg_t SoundCommandParser::kDoSoundPlay(EngineState *s, int argc, reg_t *argv) {
174 	debugC(kDebugLevelSound, "kDoSound(play): %04x:%04x", PRINT_REG(argv[0]));
175 	bool playBed = false;
176 	if (argc >= 2 && !argv[1].isNull())
177 		playBed = true;
178 	processPlaySound(argv[0], playBed);
179 	return s->r_acc;
180 }
181 
processPlaySound(reg_t obj,bool playBed,bool restoring)182 void SoundCommandParser::processPlaySound(reg_t obj, bool playBed, bool restoring) {
183 	MusicEntry *musicSlot = _music->getSlot(obj);
184 	if (!musicSlot) {
185 		warning("kDoSound(play): Slot not found (%04x:%04x), initializing it manually", PRINT_REG(obj));
186 		// The sound hasn't been initialized for some reason, so initialize it
187 		// here. Happens in KQ6, room 460, when giving the creature (child) to
188 		// the bookworm. Fixes bugs #5849 and #5868.
189 		processInitSound(obj);
190 		musicSlot = _music->getSlot(obj);
191 		if (!musicSlot)
192 			error("Failed to initialize uninitialized sound slot");
193 	}
194 
195 	int resourceId;
196 	if (!restoring)
197 		resourceId = getSoundResourceId(obj);
198 	else
199 		// Handle cases where a game was saved while track A was playing, but track B was initialized, waiting to be played later.
200 		// In such cases, musicSlot->resourceId contains the actual track that was playing (A), while getSoundResourceId(obj)
201 		// contains the track that's waiting to be played later (B) - bug #10907.
202 		resourceId = musicSlot->resourceId;
203 
204 	if (musicSlot->resourceId != resourceId) { // another sound loaded into struct
205 		processDisposeSound(obj);
206 		processInitSound(obj);
207 		// Find slot again :)
208 		musicSlot = _music->getSlot(obj);
209 	}
210 
211 	writeSelector(_segMan, obj, SELECTOR(handle), obj);
212 
213 	if (_soundVersion >= SCI_VERSION_1_EARLY) {
214 		writeSelector(_segMan, obj, SELECTOR(nodePtr), obj);
215 		writeSelectorValue(_segMan, obj, SELECTOR(min), 0);
216 		writeSelectorValue(_segMan, obj, SELECTOR(sec), 0);
217 		writeSelectorValue(_segMan, obj, SELECTOR(frame), 0);
218 		writeSelectorValue(_segMan, obj, SELECTOR(signal), 0);
219 	} else {
220 		writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundPlaying);
221 	}
222 
223 	musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
224 
225 	// Get song priority from either obj or soundRes
226 	byte resourcePriority = 0xFF;
227 	if (musicSlot->soundRes)
228 		resourcePriority = musicSlot->soundRes->getSoundPriority();
229 	if (!musicSlot->overridePriority && resourcePriority != 0xFF) {
230 		musicSlot->priority = resourcePriority;
231 	} else {
232 		musicSlot->priority = readSelectorValue(_segMan, obj, SELECTOR(priority));
233 	}
234 
235 	// Reset hold when starting a new song. kDoSoundSetHold is always called after
236 	// kDoSoundPlay to set it properly, if needed. Fixes bug #5851.
237 	musicSlot->hold = -1;
238 	musicSlot->playBed = playBed;
239 	if (_soundVersion >= SCI_VERSION_1_EARLY)
240 		musicSlot->volume = readSelectorValue(_segMan, obj, SELECTOR(vol));
241 
242 	debugC(kDebugLevelSound, "kDoSound(play): %04x:%04x number %d, loop %d, prio %d, vol %d, bed %d", PRINT_REG(obj),
243 			resourceId, musicSlot->loop, musicSlot->priority, musicSlot->volume, playBed ? 1 : 0);
244 
245 	_music->soundPlay(musicSlot, restoring);
246 
247 	// Increment the sample play counter used by Pharkas CD.
248 	// SSCI calls kDoAudio(Play) which did this. See kDoAudio(13).
249 	if (_audio != nullptr && musicSlot->isSample) {
250 		_audio->incrementPlayCounter();
251 	}
252 
253 	// Reset any left-over signals
254 	musicSlot->signal = 0;
255 	musicSlot->fadeStep = 0;
256 }
257 
kDoSoundDispose(EngineState * s,int argc,reg_t * argv)258 reg_t SoundCommandParser::kDoSoundDispose(EngineState *s, int argc, reg_t *argv) {
259 	debugC(kDebugLevelSound, "kDoSound(dispose): %04x:%04x", PRINT_REG(argv[0]));
260 	processDisposeSound(argv[0]);
261 	return s->r_acc;
262 }
263 
processDisposeSound(reg_t obj)264 void SoundCommandParser::processDisposeSound(reg_t obj) {
265 	MusicEntry *musicSlot = _music->getSlot(obj);
266 	if (!musicSlot) {
267 		warning("kDoSound(dispose): Slot not found (%04x:%04x)", PRINT_REG(obj));
268 		return;
269 	}
270 
271 	processStopSound(obj, false);
272 
273 	_music->soundKill(musicSlot);
274 	writeSelectorValue(_segMan, obj, SELECTOR(handle), 0);
275 	if (_soundVersion >= SCI_VERSION_1_EARLY)
276 		writeSelector(_segMan, obj, SELECTOR(nodePtr), NULL_REG);
277 	else
278 		writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundStopped);
279 }
280 
kDoSoundStop(EngineState * s,int argc,reg_t * argv)281 reg_t SoundCommandParser::kDoSoundStop(EngineState *s, int argc, reg_t *argv) {
282 	debugC(kDebugLevelSound, "kDoSound(stop): %04x:%04x", PRINT_REG(argv[0]));
283 	processStopSound(argv[0], false);
284 	return s->r_acc;
285 }
286 
processStopSound(reg_t obj,bool sampleFinishedPlaying)287 void SoundCommandParser::processStopSound(reg_t obj, bool sampleFinishedPlaying) {
288 	MusicEntry *musicSlot = _music->getSlot(obj);
289 	if (!musicSlot) {
290 		warning("kDoSound(stop): Slot not found (%04x:%04x)", PRINT_REG(obj));
291 		return;
292 	}
293 
294 	if (_soundVersion <= SCI_VERSION_0_LATE) {
295 		writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundStopped);
296 	} else {
297 		writeSelectorValue(_segMan, obj, SELECTOR(handle), 0);
298 	}
299 
300 	// Set signal selector in sound SCI0 games only, when the sample has
301 	// finished playing. If we don't set it at all, we get a problem when using
302 	// vaporizer on the 2 guys. If we set it all the time, we get no music in
303 	// sq3new and kq1.
304 	// FIXME: This *may* be wrong, it's impossible to find out in Sierra DOS
305 	//        SCI, because SCI0 under DOS didn't have sfx drivers included.
306 	// We need to set signal in sound SCI1+ games all the time.
307 	if ((_soundVersion > SCI_VERSION_0_LATE) || sampleFinishedPlaying)
308 		writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
309 
310 	musicSlot->dataInc = 0;
311 	musicSlot->signal = SIGNAL_OFFSET;
312 	_music->soundStop(musicSlot);
313 
314 	if (_soundVersion <= SCI_VERSION_0_LATE && (musicSlot = _music->getFirstSlotWithStatus(kSoundPlaying)))
315 		writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying);
316 }
317 
kDoSoundPause(EngineState * s,int argc,reg_t * argv)318 reg_t SoundCommandParser::kDoSoundPause(EngineState *s, int argc, reg_t *argv) {
319 	if (argc == 1)
320 		debugC(kDebugLevelSound, "kDoSound(pause): %04x:%04x", PRINT_REG(argv[0]));
321 	else
322 		debugC(kDebugLevelSound, "kDoSound(pause): %04x:%04x, %04x:%04x", PRINT_REG(argv[0]), PRINT_REG(argv[1]));
323 
324 	if (_soundVersion <= SCI_VERSION_0_LATE) {
325 		// SCI0 games give us 0/1 for either resuming or pausing the current music
326 		//  this one doesn't count, so pausing 2 times and resuming once means here that we are supposed to resume
327 		uint16 value = argv[0].toUint16();
328 		MusicEntry *musicSlot = _music->getFirstSlotWithStatus(kSoundPlaying);
329 		switch (value) {
330 		case 1:
331 			if (musicSlot) {
332 				_music->soundPause(musicSlot);
333 				writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPaused);
334 			}
335 			return make_reg(0, 0);
336 		case 0:
337 			if (!musicSlot && (musicSlot = _music->getFirstSlotWithStatus(kSoundPaused))) {
338 				_music->soundResume(musicSlot);
339 				writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying);
340 				return make_reg(0, 1);
341 			}
342 			return make_reg(0, 0);
343 		default:
344 			error("kDoSound(pause): parameter 0 is invalid for sound-sci0");
345 		}
346 	}
347 
348 	reg_t obj = argv[0];
349 	const bool shouldPause = argc > 1 ? argv[1].toUint16() : false;
350 	if (
351 		(_soundVersion < SCI_VERSION_2 && !obj.getSegment()) ||
352 		(_soundVersion >= SCI_VERSION_2 && obj.isNull())
353 	) {
354 		_music->pauseAll(shouldPause);
355 #ifdef ENABLE_SCI32
356 		if (_soundVersion >= SCI_VERSION_2_1_EARLY) {
357 			if (shouldPause) {
358 				g_sci->_audio32->pause(kAllChannels);
359 			} else {
360 				g_sci->_audio32->resume(kAllChannels);
361 			}
362 		}
363 #endif
364 	} else {
365 		MusicEntry *musicSlot = _music->getSlot(obj);
366 		if (!musicSlot) {
367 			// This happens quite frequently
368 			debugC(kDebugLevelSound, "kDoSound(pause): Slot not found (%04x:%04x)", PRINT_REG(obj));
369 			return s->r_acc;
370 		}
371 
372 #ifdef ENABLE_SCI32
373 		// SSCI also expected a global "kernel call" flag to be true in order to
374 		// perform this action, but the architecture of the ScummVM
375 		// implementation is so different that it doesn't matter here
376 		if (_soundVersion >= SCI_VERSION_2_1_EARLY && musicSlot->isSample) {
377 			if (shouldPause) {
378 				g_sci->_audio32->pause(ResourceId(kResourceTypeAudio, musicSlot->resourceId), musicSlot->soundObj);
379 			} else {
380 				g_sci->_audio32->resume(ResourceId(kResourceTypeAudio, musicSlot->resourceId), musicSlot->soundObj);
381 			}
382 		} else
383 #endif
384 			_music->soundToggle(musicSlot, shouldPause);
385 	}
386 	return s->r_acc;
387 }
388 
389 // SCI0 only command
390 //  It's called right after restoring a game - it's responsible to kick off playing music again
391 //  we don't need this at all, so we don't do anything here
kDoSoundResumeAfterRestore(EngineState * s,int argc,reg_t * argv)392 reg_t SoundCommandParser::kDoSoundResumeAfterRestore(EngineState *s, int argc, reg_t *argv) {
393 	return s->r_acc;
394 }
395 
kDoSoundMute(EngineState * s,int argc,reg_t * argv)396 reg_t SoundCommandParser::kDoSoundMute(EngineState *s, int argc, reg_t *argv) {
397 	uint16 previousState = _music->soundGetSoundOn();
398 	if (argc > 0) {
399 		debugC(kDebugLevelSound, "kDoSound(mute): %d", argv[0].toUint16());
400 		_music->soundSetSoundOn(argv[0].toUint16());
401 	}
402 
403 	return make_reg(0, previousState);
404 }
405 
kDoSoundMasterVolume(EngineState * s,int argc,reg_t * argv)406 reg_t SoundCommandParser::kDoSoundMasterVolume(EngineState *s, int argc, reg_t *argv) {
407 	s->r_acc = make_reg(0, _music->soundGetMasterVolume());
408 
409 	if (argc > 0) {
410 		debugC(kDebugLevelSound, "kDoSound(masterVolume): %d", argv[0].toSint16());
411 		int vol = CLIP<int16>(argv[0].toSint16(), 0, MUSIC_MASTERVOLUME_MAX);
412 
413 		if (!g_sci->_guestAdditions->kDoSoundMasterVolumeHook(vol)) {
414 			setMasterVolume(vol);
415 		}
416 	}
417 	return s->r_acc;
418 }
419 
kDoSoundFade(EngineState * s,int argc,reg_t * argv)420 reg_t SoundCommandParser::kDoSoundFade(EngineState *s, int argc, reg_t *argv) {
421 	reg_t obj = argv[0];
422 
423 	// The object can be null in several SCI0 games (e.g. Camelot, KQ1, KQ4, MUMG).
424 	// Check bugs #4984, #5045 and #6163.
425 	// In this case, we just ignore the call.
426 	if (obj.isNull() && argc == 1)
427 		return s->r_acc;
428 
429 	MusicEntry *musicSlot = _music->getSlot(obj);
430 	if (!musicSlot) {
431 		debugC(kDebugLevelSound, "kDoSound(fade): Slot not found (%04x:%04x)", PRINT_REG(obj));
432 		return s->r_acc;
433 	}
434 
435 	int volume = musicSlot->volume;
436 
437 #ifdef ENABLE_SCI32
438 	if (_soundVersion >= SCI_VERSION_2_1_EARLY && musicSlot->isSample) {
439 		g_sci->_audio32->fadeChannel(ResourceId(kResourceTypeAudio, musicSlot->resourceId), musicSlot->soundObj, argv[1].toSint16(), argv[2].toSint16(), argv[3].toSint16(), argc > 4 ? (bool)argv[4].toSint16() : false);
440 		return s->r_acc;
441 	}
442 #endif
443 
444 	// If sound is not playing currently, set signal directly
445 	if (musicSlot->status != kSoundPlaying) {
446 		debugC(kDebugLevelSound, "kDoSound(fade): %04x:%04x fading requested, but sound is currently not playing", PRINT_REG(obj));
447 		writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
448 		return s->r_acc;
449 	}
450 
451 	switch (argc) {
452 	case 1: // SCI0
453 		// SCI0 fades out all the time and when fadeout is done it will also
454 		// stop the music from playing
455 		musicSlot->fadeTo = 0;
456 		musicSlot->fadeStep = -5;
457 		musicSlot->fadeTickerStep = 10 * 16667 / _music->soundGetTempo();
458 		musicSlot->fadeTicker = 0;
459 		break;
460 
461 	case 4: // SCI01+
462 	case 5: // SCI1+ (SCI1 late sound scheme), with fade and continue
463 		musicSlot->fadeTo = CLIP<uint16>(argv[1].toUint16(), 0, MUSIC_VOLUME_MAX);
464 		// Check if the song is already at the requested volume. If it is, don't
465 		// perform any fading. Happens for example during the intro of Longbow.
466 		if (musicSlot->fadeTo == musicSlot->volume)
467 			return s->r_acc;
468 
469 		// Sometimes we get objects in that position, so fix the value (refer to workarounds.cpp)
470 		if (!argv[1].getSegment())
471 			musicSlot->fadeStep = volume > musicSlot->fadeTo ? -argv[3].toUint16() : argv[3].toUint16();
472 		else
473 			musicSlot->fadeStep = volume > musicSlot->fadeTo ? -5 : 5;
474 		musicSlot->fadeTickerStep = argv[2].toUint16() * 16667 / _music->soundGetTempo();
475 		musicSlot->fadeTicker = 0;
476 
477 		// argv[4] is a boolean. Scripts sometimes pass strange values,
478 		// but SSCI only checks for zero/non-zero. (Verified in KQ6).
479 		// KQ6 room 460 even passes an object, but treating this as 'true'
480 		// seems fine in that case.
481 		if (argc == 5)
482 			musicSlot->stopAfterFading = !argv[4].isNull();
483 		else
484 			musicSlot->stopAfterFading = false;
485 		break;
486 
487 	default:
488 		error("kDoSound(fade): unsupported argc %d", argc);
489 	}
490 
491 	debugC(kDebugLevelSound, "kDoSound(fade): %04x:%04x to %d, step %d, ticker %d", PRINT_REG(obj), musicSlot->fadeTo, musicSlot->fadeStep, musicSlot->fadeTickerStep);
492 	return s->r_acc;
493 }
494 
kDoSoundGetPolyphony(EngineState * s,int argc,reg_t * argv)495 reg_t SoundCommandParser::kDoSoundGetPolyphony(EngineState *s, int argc, reg_t *argv) {
496 	return make_reg(0, _music->soundGetVoices());	// Get the number of voices
497 }
498 
kDoSoundUpdate(EngineState * s,int argc,reg_t * argv)499 reg_t SoundCommandParser::kDoSoundUpdate(EngineState *s, int argc, reg_t *argv) {
500 	reg_t obj = argv[0];
501 
502 	debugC(kDebugLevelSound, "kDoSound(update): %04x:%04x", PRINT_REG(argv[0]));
503 
504 	MusicEntry *musicSlot = _music->getSlot(obj);
505 	if (!musicSlot) {
506 		warning("kDoSound(update): Slot not found (%04x:%04x)", PRINT_REG(obj));
507 		return s->r_acc;
508 	}
509 
510 	musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
511 	int16 objVol = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, 255);
512 	if (objVol != musicSlot->volume)
513 		_music->soundSetVolume(musicSlot, objVol);
514 	int16 objPrio = readSelectorValue(_segMan, obj, SELECTOR(priority));
515 	if (objPrio != musicSlot->priority)
516 		_music->soundSetPriority(musicSlot, objPrio);
517 	return s->r_acc;
518 }
519 
kDoSoundUpdateCues(EngineState * s,int argc,reg_t * argv)520 reg_t SoundCommandParser::kDoSoundUpdateCues(EngineState *s, int argc, reg_t *argv) {
521 	processUpdateCues(argv[0]);
522 	return s->r_acc;
523 }
524 
processUpdateCues(reg_t obj)525 void SoundCommandParser::processUpdateCues(reg_t obj) {
526 	MusicEntry *musicSlot = _music->getSlot(obj);
527 	if (!musicSlot) {
528 		warning("kDoSound(updateCues): Slot not found (%04x:%04x)", PRINT_REG(obj));
529 		return;
530 	}
531 
532 	if (musicSlot->isSample) {
533 #ifdef ENABLE_SCI32
534 		if (_soundVersion >= SCI_VERSION_2) {
535 			const ResourceId audioId = ResourceId(kResourceTypeAudio, musicSlot->resourceId);
536 
537 			if (getSciVersion() == SCI_VERSION_3) {
538 				// In SSCI the volume is first set to -1 and then reset later if
539 				// a sample is playing in the audio player, but since our audio
540 				// code returns -1 for not-found samples, the extra check is not
541 				// needed and we can just always set it to the return value of
542 				// the getVolume call
543 				const int16 volume = g_sci->_audio32->getVolume(audioId, musicSlot->soundObj);
544 				writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(vol), volume);
545 			}
546 
547 			const int16 position = g_sci->_audio32->getPosition(audioId, musicSlot->soundObj);
548 			if (position == -1) {
549 				processStopSound(musicSlot->soundObj, true);
550 			}
551 
552 			return;
553 		}
554 #endif
555 		// Update digital sound effect slots
556 		uint currentLoopCounter = 0;
557 
558 		if (musicSlot->pLoopStream)
559 			currentLoopCounter = musicSlot->pLoopStream->getCompleteIterations();
560 
561 		if (currentLoopCounter != musicSlot->sampleLoopCounter) {
562 			// during last time we looped at least one time, update loop accordingly
563 			musicSlot->loop -= currentLoopCounter - musicSlot->sampleLoopCounter;
564 			musicSlot->sampleLoopCounter = currentLoopCounter;
565 		}
566 		if (musicSlot->status == kSoundPlaying) {
567 			if (!_music->soundIsActive(musicSlot)) {
568 				processStopSound(obj, true);
569 			} else {
570 				_music->updateAudioStreamTicker(musicSlot);
571 			}
572 		} else if (musicSlot->status == kSoundPaused) {
573 			_music->updateAudioStreamTicker(musicSlot);
574 		}
575 		// We get a flag from MusicEntry::doFade() here to set volume for the stream
576 		if (musicSlot->fadeSetVolume) {
577 			_music->soundSetSampleVolume(musicSlot, musicSlot->volume);
578 			musicSlot->fadeSetVolume = false;
579 		}
580 	} else if (musicSlot->pMidiParser) {
581 		// Update MIDI slots
582 		if (musicSlot->signal == 0) {
583 			if (musicSlot->dataInc != readSelectorValue(_segMan, obj, SELECTOR(dataInc))) {
584 				if (SELECTOR(dataInc) > -1)
585 					writeSelectorValue(_segMan, obj, SELECTOR(dataInc), musicSlot->dataInc);
586 				writeSelectorValue(_segMan, obj, SELECTOR(signal), musicSlot->dataInc + 127);
587 			}
588 		} else {
589 			// Sync the signal of the sound object
590 			writeSelectorValue(_segMan, obj, SELECTOR(signal), musicSlot->signal);
591 			// We need to do this especially because state selector needs to get updated
592 			if (musicSlot->signal == SIGNAL_OFFSET)
593 				processStopSound(obj, false);
594 		}
595 	} else {
596 		// The sound slot has no data for the currently selected sound card.
597 		// An example can be found during the mud wrestling scene in LSL5, room
598 		// 730: sound 744 (a splat sound heard when Lana Luscious jumps in the
599 		// mud) only contains MIDI channel data. If a non-MIDI sound card is
600 		// selected (like Adlib), then the scene freezes. We also need to stop
601 		// the sound at this point, otherwise KQ6 Mac breaks because the rest
602 		// of the object needs to be reset to avoid a continuous stream of
603 		// sound cues.
604 		processStopSound(obj, true);	// this also sets the signal selector
605 	}
606 
607 	if (musicSlot->fadeCompleted) {
608 		musicSlot->fadeCompleted = false;
609 		// We need signal for sci0 at least in iceman as well (room 14,
610 		// fireworks).
611 		// It is also needed in other games, e.g. LSL6 when talking to the
612 		// receptionist (bug #5601).
613 		// TODO: More thorougly check the different SCI version:
614 		// * SCI1late sets signal to 0xFE here. (With signal 0xFF
615 		//       duplicate music plays in LauraBow2CD - bug #6462)
616 		//   SCI1middle LSL1 1.000.510 does not have the 0xFE;
617 		//   SCI1late CastleDrBrain demo 1.000.005 does have the 0xFE.
618 		// * Other SCI1 games seem to rely on processStopSound to set the signal
619 		// * Need to check SCI0 behaviour.
620 		uint16 sig;
621 		if (getSciVersion() >= SCI_VERSION_1_LATE)
622 			sig = 0xFFFE;
623 		else
624 			sig = SIGNAL_OFFSET;
625 		writeSelectorValue(_segMan, obj, SELECTOR(signal), sig);
626 		if (_soundVersion <= SCI_VERSION_0_LATE) {
627 			processStopSound(obj, false);
628 		} else {
629 			if (musicSlot->stopAfterFading)
630 				processStopSound(obj, false);
631 		}
632 	}
633 
634 	// Sync loop selector for SCI0
635 	if (_soundVersion <= SCI_VERSION_0_LATE)
636 		writeSelectorValue(_segMan, obj, SELECTOR(loop), musicSlot->loop);
637 
638 	musicSlot->signal = 0;
639 
640 	if (_soundVersion >= SCI_VERSION_1_EARLY) {
641 		writeSelectorValue(_segMan, obj, SELECTOR(min), musicSlot->ticker / 3600);
642 		writeSelectorValue(_segMan, obj, SELECTOR(sec), musicSlot->ticker % 3600 / 60);
643 		writeSelectorValue(_segMan, obj, SELECTOR(frame), musicSlot->ticker % 60 / 2);
644 
645 		if (_soundVersion >= SCI_VERSION_1_MIDDLE) {
646 			writeSelectorValue(_segMan, obj, SELECTOR(vol), musicSlot->volume);
647 		}
648 	}
649 }
650 
kDoSoundSendMidi(EngineState * s,int argc,reg_t * argv)651 reg_t SoundCommandParser::kDoSoundSendMidi(EngineState *s, int argc, reg_t *argv) {
652 	// The 4 parameter variant of this call is used in at least LSL1VGA, room
653 	// 110 (Lefty's bar), to distort the music when Larry is drunk and stands
654 	// up - bug #6349.
655 	reg_t obj = argv[0];
656 	byte channel = argv[1].toUint16() & 0xf;
657 	byte midiCmd = (argc == 5) ? argv[2].toUint16() & 0xff : 0xB0;	// 0xB0: controller
658 	uint16 controller = (argc == 5) ? argv[3].toUint16() : argv[2].toUint16();
659 	uint16 param = (argc == 5) ? argv[4].toUint16() : argv[3].toUint16();
660 
661 	// This call is made in Hoyle 5 when toggling the music from the main menu.
662 	// Ignore it for this game, since it doesn't use MIDI audio, and this call
663 	// looks to be a leftover in Sound::mute (script 64989).
664 	if (g_sci->getGameId() == GID_HOYLE5)
665 		return s->r_acc;
666 
667 	if (argc == 4 && controller == 0xFF) {
668 		midiCmd = 0xE0;	// 0xE0: pitch wheel
669 		uint16 pitch = CLIP<uint16>(argv[3].toSint16() + 0x2000, 0x0000, 0x3FFF);
670 		controller = pitch & 0x7F;
671 		param = pitch >> 7;
672 	}
673 
674 	debugC(kDebugLevelSound, "kDoSound(sendMidi): %04x:%04x, %d, %d, %d, %d", PRINT_REG(obj), channel, midiCmd, controller, param);
675 	if (channel)
676 		channel--; // channel is given 1-based, we are using 0-based
677 
678 	uint32 midiCommand = (channel | midiCmd) | ((uint32)controller << 8) | ((uint32)param << 16);
679 
680 	MusicEntry *musicSlot = _music->getSlot(obj);
681 	if (!musicSlot) {
682 		// TODO: maybe it's possible to call this with obj == 0:0 and send directly?!
683 		// if so, allow it
684 		//_music->sendMidiCommand(_midiCommand);
685 		warning("kDoSound(sendMidi): Slot not found (%04x:%04x)", PRINT_REG(obj));
686 		return s->r_acc;
687 	}
688 	_music->sendMidiCommand(musicSlot, midiCommand);
689 	return s->r_acc;
690 }
691 
kDoSoundGlobalReverb(EngineState * s,int argc,reg_t * argv)692 reg_t SoundCommandParser::kDoSoundGlobalReverb(EngineState *s, int argc, reg_t *argv) {
693 	byte prevReverb = _music->getCurrentReverb();
694 	byte reverb = argv[0].toUint16() & 0xF;
695 
696 	if (argc == 1) {
697 		debugC(kDebugLevelSound, "doSoundGlobalReverb: %d", argv[0].toUint16() & 0xF);
698 		if (reverb <= 10)
699 			_music->setGlobalReverb(reverb);
700 	}
701 
702 	return make_reg(0, prevReverb);
703 }
704 
kDoSoundSetHold(EngineState * s,int argc,reg_t * argv)705 reg_t SoundCommandParser::kDoSoundSetHold(EngineState *s, int argc, reg_t *argv) {
706 	reg_t obj = argv[0];
707 
708 	debugC(kDebugLevelSound, "doSoundSetHold: %04x:%04x, %d", PRINT_REG(argv[0]), argv[1].toUint16());
709 
710 	MusicEntry *musicSlot = _music->getSlot(obj);
711 	if (!musicSlot) {
712 		warning("kDoSound(setHold): Slot not found (%04x:%04x)", PRINT_REG(obj));
713 		return s->r_acc;
714 	}
715 
716 	// Set the special hold marker ID where the song should be looped at.
717 	musicSlot->hold = argv[1].toSint16();
718 	return s->r_acc;
719 }
720 
kDoSoundGetAudioCapability(EngineState * s,int argc,reg_t * argv)721 reg_t SoundCommandParser::kDoSoundGetAudioCapability(EngineState *s, int argc, reg_t *argv) {
722 	// Tests for digital audio support
723 	return make_reg(0, 1);
724 }
725 
kDoSoundStopAll(EngineState * s,int argc,reg_t * argv)726 reg_t SoundCommandParser::kDoSoundStopAll(EngineState *s, int argc, reg_t *argv) {
727 	// TODO: this can't be right, this gets called in kq1 - e.g. being in witch house, getting the note
728 	//  now the point jingle plays and after a messagebox they call this - and would stop the background effects with it
729 	//  this doesn't make sense, so i disable it for now
730 	return s->r_acc;
731 
732 #if 0
733 	Common::StackLock(_music->_mutex);
734 
735 	const MusicList::iterator end = _music->getPlayListEnd();
736 	for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
737 		if (_soundVersion <= SCI_VERSION_0_LATE) {
738 			writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(state), kSoundStopped);
739 		} else {
740 			writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(handle), 0);
741 			writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal), SIGNAL_OFFSET);
742 		}
743 
744 		(*i)->dataInc = 0;
745 		_music->soundStop(*i);
746 	}
747 	return s->r_acc;
748 #endif
749 }
750 
kDoSoundSetVolume(EngineState * s,int argc,reg_t * argv)751 reg_t SoundCommandParser::kDoSoundSetVolume(EngineState *s, int argc, reg_t *argv) {
752 	reg_t obj = argv[0];
753 	int16 value = argv[1].toSint16();
754 
755 	MusicEntry *musicSlot = _music->getSlot(obj);
756 	if (!musicSlot) {
757 		// Do not throw a warning if the sound can't be found, as in some games
758 		// this is called before the actual sound is loaded (e.g. SQ4CD, with
759 		// the drum sounds of the energizer bunny at the beginning), so this is
760 		// normal behavior.
761 		//warning("cmdSetSoundVolume: Slot not found (%04x:%04x)", PRINT_REG(obj));
762 		return s->r_acc;
763 	}
764 
765 	debugC(kDebugLevelSound, "kDoSound(setVolume): %d", value);
766 
767 	value = CLIP<int>(value, 0, MUSIC_VOLUME_MAX);
768 
769 #ifdef ENABLE_SCI32
770 	// SSCI unconditionally sets volume if it is digital audio
771 	if (_soundVersion >= SCI_VERSION_2_1_EARLY && musicSlot->isSample) {
772 		g_sci->_audio32->setVolume(ResourceId(kResourceTypeAudio, musicSlot->resourceId), musicSlot->soundObj, value);
773 	}
774 #endif
775 	if (musicSlot->volume != value) {
776 		musicSlot->volume = value;
777 		_music->soundSetVolume(musicSlot, value);
778 		writeSelectorValue(_segMan, obj, SELECTOR(vol), value);
779 #ifdef ENABLE_SCI32
780 		g_sci->_guestAdditions->kDoSoundSetVolumeHook(obj, value);
781 #endif
782 	}
783 
784 	return s->r_acc;
785 }
786 
kDoSoundSetPriority(EngineState * s,int argc,reg_t * argv)787 reg_t SoundCommandParser::kDoSoundSetPriority(EngineState *s, int argc, reg_t *argv) {
788 	reg_t obj = argv[0];
789 	int16 value = argv[1].toSint16();
790 
791 	debugC(kDebugLevelSound, "kDoSound(setPriority): %04x:%04x, %d", PRINT_REG(obj), value);
792 
793 	MusicEntry *musicSlot = _music->getSlot(obj);
794 	if (!musicSlot) {
795 		debugC(kDebugLevelSound, "kDoSound(setPriority): Slot not found (%04x:%04x)", PRINT_REG(obj));
796 		return s->r_acc;
797 	}
798 
799 	if (value == -1) {
800 		musicSlot->overridePriority = false;
801 		musicSlot->priority = 0;
802 
803 		// NB: It seems SSCI doesn't actually reset the priority here.
804 
805 		writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) & 0xFD);
806 	} else {
807 		// Scripted priority
808 		musicSlot->overridePriority = true;
809 
810 		writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) | 2);
811 
812 		_music->soundSetPriority(musicSlot, value);
813 	}
814 	return s->r_acc;
815 }
816 
kDoSoundSetLoop(EngineState * s,int argc,reg_t * argv)817 reg_t SoundCommandParser::kDoSoundSetLoop(EngineState *s, int argc, reg_t *argv) {
818 	reg_t obj = argv[0];
819 	int16 value = argv[1].toSint16();
820 
821 	debugC(kDebugLevelSound, "kDoSound(setLoop): %04x:%04x, %d", PRINT_REG(obj), value);
822 
823 	const uint16 loopCount = value == -1 ? 0xFFFF : 1;
824 	writeSelectorValue(_segMan, obj, SELECTOR(loop), loopCount);
825 
826 	MusicEntry *musicSlot = _music->getSlot(obj);
827 	if (!musicSlot) {
828 		// Apparently, it's perfectly normal for a game to call cmdSetSoundLoop
829 		// before actually initializing the sound and adding it to the playlist
830 		// with cmdInitSound. Usually, it doesn't matter if the game doesn't
831 		// request to loop the sound, so in this case, don't throw any warning,
832 		// otherwise do, because the sound won't be looped.
833 		if (value == -1) {
834 			warning("kDoSound(setLoop): Slot not found (%04x:%04x) and the song was requested to be looped", PRINT_REG(obj));
835 		} else {
836 			// Doesn't really matter
837 		}
838 		return s->r_acc;
839 	}
840 
841 #ifdef ENABLE_SCI32
842 	if (_soundVersion >= SCI_VERSION_2_1_MIDDLE && musicSlot->isSample) {
843 		g_sci->_audio32->setLoop(ResourceId(kResourceTypeAudio, musicSlot->resourceId), musicSlot->soundObj, value == -1);
844 	} else
845 #endif
846 		musicSlot->loop = loopCount;
847 
848 	return s->r_acc;
849 }
850 
kDoSoundSuspend(EngineState * s,int argc,reg_t * argv)851 reg_t SoundCommandParser::kDoSoundSuspend(EngineState *s, int argc, reg_t *argv) {
852 	// TODO
853 	warning("kDoSound(suspend): STUB");
854 	return s->r_acc;
855 }
856 
updateSci0Cues()857 void SoundCommandParser::updateSci0Cues() {
858 	for (MusicList::iterator i = _music->getPlayListStart(); i != _music->getPlayListEnd(); ++i) {
859 		if ((*i)->status == kSoundPlaying || (*i)->signal)
860 			processUpdateCues((*i)->soundObj);
861 	}
862 }
863 
clearPlayList()864 void SoundCommandParser::clearPlayList() {
865 	_music->clearPlayList();
866 }
867 
printPlayList(Console * con)868 void SoundCommandParser::printPlayList(Console *con) {
869 	_music->printPlayList(con);
870 }
871 
printSongInfo(reg_t obj,Console * con)872 void SoundCommandParser::printSongInfo(reg_t obj, Console *con) {
873 	_music->printSongInfo(obj, con);
874 }
875 
stopAllSounds()876 void SoundCommandParser::stopAllSounds() {
877 	_music->stopAll();
878 }
879 
stopAllSamples()880 void SoundCommandParser::stopAllSamples() {
881 	_music->stopAllSamples();
882 }
883 
startNewSound(int number)884 void SoundCommandParser::startNewSound(int number) {
885 	// NB: This is only used by the debugging console.
886 
887 	Common::StackLock lock(_music->_mutex);
888 
889 	// Overwrite the first sound in the playlist
890 	MusicEntry *song = *_music->getPlayListStart();
891 	reg_t soundObj = song->soundObj;
892 	processDisposeSound(soundObj);
893 	writeSelectorValue(_segMan, soundObj, SELECTOR(number), number);
894 	processInitSound(soundObj);
895 	processPlaySound(soundObj, false);
896 }
897 
setMasterVolume(int vol)898 void SoundCommandParser::setMasterVolume(int vol) {
899 	// 0...15
900 	_music->soundSetMasterVolume(vol);
901 }
902 
903 #ifdef ENABLE_SCI32
setVolume(const reg_t obj,const int volume)904 void SoundCommandParser::setVolume(const reg_t obj, const int volume) {
905 	MusicEntry *slot = _music->getSlot(obj);
906 	if (slot != nullptr) {
907 		slot->volume = volume;
908 		writeSelectorValue(_segMan, obj, SELECTOR(vol), volume);
909 		_music->soundSetVolume(slot, volume);
910 	}
911 }
912 #endif
913 
pauseAll(bool pause)914 void SoundCommandParser::pauseAll(bool pause) {
915 	_music->pauseAll(pause);
916 }
917 
getMusicType() const918 MusicType SoundCommandParser::getMusicType() const {
919 	assert(_music);
920 	return _music->soundGetMusicType();
921 }
922 
923 } // End of namespace Sci
924