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