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 "sci/sci.h"
24 #include "sci/engine/features.h"
25 #include "sci/engine/state.h"
26 #include "sci/engine/kernel.h"
27 #include "sci/engine/vm.h" // for Object
28 #include "sci/sound/audio.h"
29 #ifdef ENABLE_SCI32
30 #include "sci/sound/audio32.h"
31 #endif
32 #include "sci/sound/soundcmd.h"
33 #include "sci/sound/sync.h"
34
35 #include "audio/mixer.h"
36 #include "common/system.h"
37
38 namespace Sci {
39
40 /**
41 * Used for synthesized music playback
42 */
kDoSound(EngineState * s,int argc,reg_t * argv)43 reg_t kDoSound(EngineState *s, int argc, reg_t *argv) {
44 if (!s)
45 return make_reg(0, g_sci->_features->detectDoSoundType());
46 error("not supposed to call this");
47 }
48
49 #define CREATE_DOSOUND_FORWARD(_name_) reg_t k##_name_(EngineState *s, int argc, reg_t *argv) { return g_sci->_soundCmd->k##_name_(s, argc, argv); }
50
51 CREATE_DOSOUND_FORWARD(DoSoundInit)
CREATE_DOSOUND_FORWARD(DoSoundPlay)52 CREATE_DOSOUND_FORWARD(DoSoundPlay)
53 CREATE_DOSOUND_FORWARD(DoSoundDispose)
54 CREATE_DOSOUND_FORWARD(DoSoundMute)
55 CREATE_DOSOUND_FORWARD(DoSoundStop)
56 CREATE_DOSOUND_FORWARD(DoSoundStopAll)
57 CREATE_DOSOUND_FORWARD(DoSoundPause)
58 CREATE_DOSOUND_FORWARD(DoSoundResumeAfterRestore)
59 CREATE_DOSOUND_FORWARD(DoSoundMasterVolume)
60 CREATE_DOSOUND_FORWARD(DoSoundUpdate)
61 CREATE_DOSOUND_FORWARD(DoSoundFade)
62 CREATE_DOSOUND_FORWARD(DoSoundGetPolyphony)
63 CREATE_DOSOUND_FORWARD(DoSoundUpdateCues)
64 CREATE_DOSOUND_FORWARD(DoSoundSendMidi)
65 CREATE_DOSOUND_FORWARD(DoSoundGlobalReverb)
66 CREATE_DOSOUND_FORWARD(DoSoundSetHold)
67 CREATE_DOSOUND_FORWARD(DoSoundGetAudioCapability)
68 CREATE_DOSOUND_FORWARD(DoSoundSuspend)
69 CREATE_DOSOUND_FORWARD(DoSoundSetVolume)
70 CREATE_DOSOUND_FORWARD(DoSoundSetPriority)
71 CREATE_DOSOUND_FORWARD(DoSoundSetLoop)
72
73 #ifdef ENABLE_SCI32
74 reg_t kDoSoundMac32(EngineState *s, int argc, reg_t *argv) {
75 // Several SCI 2.1 Middle Mac games, but not all, contain a modified kDoSound
76 // in which all but eleven subops were removed, changing their subop values to
77 // zero through ten. PQSWAT then restored all of the subops, but kept the new
78 // subop values that removing caused in the first place, and assigned new
79 // values to the restored subops. It is the only game that does this and it
80 // only uses two of them.
81 switch (argv[0].toUint16()) {
82 case 0:
83 return g_sci->_soundCmd->kDoSoundMasterVolume(s, argc - 1, argv + 1);
84 case 1:
85 return g_sci->_soundCmd->kDoSoundGetAudioCapability(s, argc - 1, argv + 1);
86 case 2:
87 return g_sci->_soundCmd->kDoSoundInit(s, argc - 1, argv + 1);
88 case 3:
89 return g_sci->_soundCmd->kDoSoundDispose(s, argc - 1, argv + 1);
90 case 4:
91 return g_sci->_soundCmd->kDoSoundPlay(s, argc - 1, argv + 1);
92 case 5:
93 return g_sci->_soundCmd->kDoSoundStop(s, argc - 1, argv + 1);
94 case 6:
95 return g_sci->_soundCmd->kDoSoundPause(s, argc - 1, argv + 1);
96 case 7:
97 return g_sci->_soundCmd->kDoSoundFade(s, argc - 1, argv + 1);
98 case 8:
99 return g_sci->_soundCmd->kDoSoundSetVolume(s, argc - 1, argv + 1);
100 case 9:
101 return g_sci->_soundCmd->kDoSoundSetLoop(s, argc - 1, argv + 1);
102 case 10:
103 return g_sci->_soundCmd->kDoSoundUpdateCues(s, argc - 1, argv + 1);
104 // PQSWAT only
105 case 12: // kDoSoundRestore
106 return kEmpty(s, argc - 1, argv + 1);
107 case 13:
108 return g_sci->_soundCmd->kDoSoundGetPolyphony(s, argc - 1, argv + 1);
109 default:
110 break;
111 }
112
113 error("Unknown kDoSoundMac32 subop %d", argv[0].toUint16());
114 return s->r_acc;
115 }
116 #endif
117
kDoCdAudio(EngineState * s,int argc,reg_t * argv)118 reg_t kDoCdAudio(EngineState *s, int argc, reg_t *argv) {
119 switch (argv[0].toUint16()) {
120 case kSciAudioPlay: {
121 if (argc < 2)
122 return NULL_REG;
123
124 uint16 track = argv[1].toUint16();
125 uint32 startFrame = (argc > 2) ? argv[2].toUint16() * 75 : 0;
126 uint32 totalFrames = (argc > 3) ? argv[3].toUint16() * 75 : 0;
127
128 return make_reg(0, g_sci->_audio->audioCdPlay(track, startFrame, totalFrames));
129 }
130 case kSciAudioStop:
131 g_sci->_audio->audioCdStop();
132
133 if (getSciVersion() == SCI_VERSION_1_1)
134 return make_reg(0, 1);
135
136 break;
137 case kSciAudioPause:
138 warning("Can't pause CD Audio");
139 break;
140 case kSciAudioResume:
141 // This seems to be hacked up to update the CD instead of resuming
142 // audio like kDoAudio does.
143 g_sci->_audio->audioCdUpdate();
144 break;
145 case kSciAudioPosition:
146 return make_reg(0, g_sci->_audio->audioCdPosition());
147 case kSciAudioWPlay: // CD Audio can't be preloaded
148 case kSciAudioRate: // No need to set the audio rate
149 case kSciAudioVolume: // The speech setting isn't used by CD Audio
150 case kSciAudioLanguage: // No need to set the language
151 break;
152 case kSciAudioCD:
153 // Init
154 return make_reg(0, 1);
155 default:
156 error("kCdDoAudio: Unhandled case %d", argv[0].toUint16());
157 }
158
159 return s->r_acc;
160 }
161
162 /**
163 * Used for speech playback and digital soundtracks in CD games.
164 * This is the SCI16 version; SCI32 is handled separately.
165 */
kDoAudio(EngineState * s,int argc,reg_t * argv)166 reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) {
167 // JonesCD and Mothergoose256 CD use different functions
168 // based on the cdaudio.map file to use red book tracks.
169 if (g_sci->_features->usesCdTrack()) {
170 if (g_sci->getGameId() == GID_MOTHERGOOSE256) {
171 // The CD audio version of Mothergoose256 CD is unique with a
172 // custom interpreter for its audio. English is only in the CD
173 // audio track while the other four languages are only in audio
174 // resource files. This is transparent to the scripts which are
175 // the same in all versions. The interpreter detected when
176 // English was selected and used CD audio in that case.
177 if (g_sci->getSciLanguage() == K_LANG_ENGLISH &&
178 argv[0].toUint16() != kSciAudioLanguage) {
179 return kDoCdAudio(s, argc, argv);
180 }
181 } else {
182 return kDoCdAudio(s, argc, argv);
183 }
184 }
185
186 Audio::Mixer *mixer = g_system->getMixer();
187
188 switch (argv[0].toUint16()) {
189 case kSciAudioWPlay:
190 case kSciAudioPlay: {
191 uint16 module;
192 uint32 number;
193
194 g_sci->_audio->stopAudio();
195 // In SSCI, kDoAudio handles all samples, even if started by kDoSound.
196 // We manage samples started by kDoSound in SoundCommandParser.
197 g_sci->_soundCmd->stopAllSamples();
198
199 if (argc == 2) {
200 module = 65535;
201 number = argv[1].toUint16();
202 } else if (argc == 6 || argc == 8) {
203 module = argv[1].toUint16();
204 number = ((argv[2].toUint16() & 0xff) << 24) |
205 ((argv[3].toUint16() & 0xff) << 16) |
206 ((argv[4].toUint16() & 0xff) << 8) |
207 (argv[5].toUint16() & 0xff);
208 } else {
209 warning("kDoAudio: Play called with an unknown number of parameters (%d)", argc);
210 return NULL_REG;
211 }
212
213 if (argv[0].toUint16() == kSciAudioPlay) {
214 g_sci->_audio->incrementPlayCounter();
215 }
216
217 debugC(kDebugLevelSound, "kDoAudio: play sample %d, module %d", number, module);
218
219 // return sample length in ticks
220 if (argv[0].toUint16() == kSciAudioWPlay)
221 return make_reg(0, g_sci->_audio->wPlayAudio(module, number));
222 else
223 return make_reg(0, g_sci->_audio->startAudio(module, number));
224 }
225 case kSciAudioStop:
226 debugC(kDebugLevelSound, "kDoAudio: stop");
227 g_sci->_audio->stopAudio();
228 // In SSCI, kDoAudio handles all samples, even if started by kDoSound.
229 // We manage samples started by kDoSound in SoundCommandParser.
230 g_sci->_soundCmd->stopAllSamples();
231 break;
232 case kSciAudioPause:
233 debugC(kDebugLevelSound, "kDoAudio: pause");
234 g_sci->_audio->pauseAudio();
235 break;
236 case kSciAudioResume:
237 debugC(kDebugLevelSound, "kDoAudio: resume");
238 g_sci->_audio->resumeAudio();
239 break;
240 case kSciAudioPosition:
241 //debugC(kDebugLevelSound, "kDoAudio: get position"); // too verbose
242 return make_reg(0, g_sci->_audio->getAudioPosition());
243 case kSciAudioRate:
244 debugC(kDebugLevelSound, "kDoAudio: set audio rate to %d", argv[1].toUint16());
245 g_sci->_audio->setAudioRate(argv[1].toUint16());
246 break;
247 case kSciAudioVolume: {
248 int16 volume = argv[1].toUint16();
249 volume = CLIP<int16>(volume, 0, AUDIO_VOLUME_MAX);
250 debugC(kDebugLevelSound, "kDoAudio: set volume to %d", volume);
251 mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume * 2);
252 break;
253 }
254 case kSciAudioLanguage:
255 // In SCI1.1: tests for digital audio support
256 if (getSciVersion() == SCI_VERSION_1_1) {
257 debugC(kDebugLevelSound, "kDoAudio: audio capability test");
258 return make_reg(0, 1);
259 } else {
260 int16 language = argv[1].toSint16();
261
262 // athrxx: It seems from disasm that the original KQ5 FM-Towns loads a default language (Japanese) audio map at the beginning
263 // right after loading the video and audio drivers. The -1 language argument in here simply means that the original will stick
264 // with Japanese. Instead of doing that we switch to the language selected in the launcher.
265 if (g_sci->getPlatform() == Common::kPlatformFMTowns && language == -1) {
266 // FM-Towns calls us to get the current language / also set the default language
267 // This doesn't just happen right at the start, but also when the user clicks on the Sierra logo in the game menu
268 // It uses the result of this call to either show "English Voices" or "Japanese Voices".
269
270 // Language should have been set by setLauncherLanguage() already (or could have been modified by the scripts).
271 // Get this language setting, so that the chosen language will get set for resource manager.
272 language = g_sci->getSciLanguage();
273 }
274
275 debugC(kDebugLevelSound, "kDoAudio: set language to %d", language);
276
277 if (language != -1)
278 g_sci->getResMan()->setAudioLanguage(language);
279
280 kLanguage kLang = g_sci->getSciLanguage();
281 if (g_sci->_features->usesCdTrack() && language == K_LANG_ENGLISH) {
282 // Mothergoose 256 CD has a multi-lingual version with English only on CD audio,
283 // so setAudioLanguage() will fail because there are no English resource files.
284 // The scripts cycle through languages to test which are available for the main
285 // menu, so setting English must succeed. This was handled by a custom interpreter.
286 kLang = K_LANG_ENGLISH;
287 }
288 g_sci->setSciLanguage(kLang);
289
290 return make_reg(0, kLang);
291 }
292 break;
293 case kSciAudioCD:
294 debugC(kDebugLevelSound, "kDoAudio: CD audio subop");
295 return kDoCdAudio(s, argc - 1, argv + 1);
296
297 // 3 new subops in Pharkas CD (including CD demo). kDoAudio in Pharkas sits at seg026:038C
298 case 11:
299 // Not sure where this is used yet
300 warning("kDoAudio: Unhandled case 11, %d extra arguments passed", argc - 1);
301 break;
302 case 12:
303 // SSCI calls this function with no parameters from
304 // the TalkRandCycle class and branches on the return
305 // value like a boolean. The conjectured purpose of
306 // this function is to ensure that the talker's mouth
307 // does not move if there is read jitter (slow CD
308 // drive, scratched CD). The old behavior here of not
309 // doing anything caused a nonzero value to be left in
310 // the accumulator by chance. This is equivalent, but
311 // more explicit.
312
313 return make_reg(0, 1);
314 case 13:
315 // SSCI returns a counter that increments each time a sample
316 // is played. This is used by the PointsSound and Narrator
317 // classes to detect if their sound was interrupted by
318 // another sample playing, since only one can play at a time.
319 // The counter is incremented on each kDoAudio(Play) and on
320 // each kDoSound(Play) when the sound is a sample, since SSCI
321 // just passes those on to kDoAudio.
322 //
323 // When awarding points, the icon bar is often disabled, and
324 // PointsSound:check polls its signal to detect when the
325 // sound is completed so that it can re-enable the icon bar.
326 // If the sound is interrupted by another sample then the icon
327 // bar could remain permanently disabled, so the play counter
328 // is stored before playing and PointsSound:check polls for
329 // a change. The Narrator class also does this to detect
330 // if speech was interrupted so that the game can continue.
331 return make_reg(0, g_sci->_audio->getPlayCounter());
332 case 17:
333 // Seems to be some sort of audio sync, used in SQ6. Silenced the
334 // warning due to the high level of spam it produces. (takes no params)
335 //warning("kDoAudio: Unhandled case 17, %d extra arguments passed", argc - 1);
336 break;
337 default:
338 warning("kDoAudio: Unhandled case %d, %d extra arguments passed", argv[0].toUint16(), argc - 1);
339 }
340
341 return s->r_acc;
342 }
343
kDoSync(EngineState * s,int argc,reg_t * argv)344 reg_t kDoSync(EngineState *s, int argc, reg_t *argv) {
345 switch (argv[0].toUint16()) {
346 case kSciAudioSyncStart: {
347 ResourceId id;
348
349 g_sci->_sync->stop();
350
351 if (argc == 3) {
352 id = ResourceId(kResourceTypeSync, argv[2].toUint16());
353 } else if (argc == 7) {
354 id = ResourceId(kResourceTypeSync36, argv[2].toUint16(), argv[3].toUint16(), argv[4].toUint16(),
355 argv[5].toUint16(), argv[6].toUint16());
356 } else {
357 warning("kDoSync: Start called with an unknown number of parameters (%d)", argc);
358 return s->r_acc;
359 }
360
361 g_sci->_sync->start(id, argv[1]);
362 break;
363 }
364 case kSciAudioSyncNext:
365 g_sci->_sync->next(argv[1]);
366 break;
367 case kSciAudioSyncStop:
368 g_sci->_sync->stop();
369 break;
370 default:
371 error("DoSync: Unhandled subfunction %d", argv[0].toUint16());
372 }
373
374 return s->r_acc;
375 }
376
377 #ifdef ENABLE_SCI32
kDoAudio32(EngineState * s,int argc,reg_t * argv)378 reg_t kDoAudio32(EngineState *s, int argc, reg_t *argv) {
379 if (!s)
380 return make_reg(0, getSciVersion());
381 error("not supposed to call this");
382 }
383
kDoAudioInit(EngineState * s,int argc,reg_t * argv)384 reg_t kDoAudioInit(EngineState *s, int argc, reg_t *argv) {
385 return make_reg(0, 0);
386 }
387
kDoAudioWaitForPlay(EngineState * s,int argc,reg_t * argv)388 reg_t kDoAudioWaitForPlay(EngineState *s, int argc, reg_t *argv) {
389 if (argc == 0) {
390 if (g_sci->_features->hasSci3Audio()) {
391 return make_reg(0, g_sci->_audio32->getNumUnlockedChannels());
392 } else {
393 return make_reg(0, g_sci->_audio32->getNumActiveChannels());
394 }
395 }
396
397 return g_sci->_audio32->kernelPlay(false, s, argc, argv);
398 }
399
kDoAudioPlay(EngineState * s,int argc,reg_t * argv)400 reg_t kDoAudioPlay(EngineState *s, int argc, reg_t *argv) {
401 if (argc == 0) {
402 return make_reg(0, g_sci->_audio32->getNumActiveChannels());
403 }
404
405 return g_sci->_audio32->kernelPlay(true, s, argc, argv);
406 }
407
kDoAudioStop(EngineState * s,int argc,reg_t * argv)408 reg_t kDoAudioStop(EngineState *s, int argc, reg_t *argv) {
409 return g_sci->_audio32->kernelStop(s, argc, argv);
410 }
411
kDoAudioPause(EngineState * s,int argc,reg_t * argv)412 reg_t kDoAudioPause(EngineState *s, int argc, reg_t *argv) {
413 return g_sci->_audio32->kernelPause(s, argc, argv);
414 }
415
kDoAudioResume(EngineState * s,int argc,reg_t * argv)416 reg_t kDoAudioResume(EngineState *s, int argc, reg_t *argv) {
417 return g_sci->_audio32->kernelResume(s, argc, argv);
418 }
419
kDoAudioPosition(EngineState * s,int argc,reg_t * argv)420 reg_t kDoAudioPosition(EngineState *s, int argc, reg_t *argv) {
421 return g_sci->_audio32->kernelPosition(s, argc, argv);
422 }
423
kDoAudioRate(EngineState * s,int argc,reg_t * argv)424 reg_t kDoAudioRate(EngineState *s, int argc, reg_t *argv) {
425 if (argc > 0) {
426 const uint16 sampleRate = argv[0].toUint16();
427 if (sampleRate != 0) {
428 g_sci->_audio32->setSampleRate(sampleRate);
429 }
430 }
431
432 return make_reg(0, g_sci->_audio32->getSampleRate());
433 }
434
kDoAudioVolume(EngineState * s,int argc,reg_t * argv)435 reg_t kDoAudioVolume(EngineState *s, int argc, reg_t *argv) {
436 return g_sci->_audio32->kernelVolume(s, argc, argv);
437 }
438
kDoAudioGetCapability(EngineState * s,int argc,reg_t * argv)439 reg_t kDoAudioGetCapability(EngineState *s, int argc, reg_t *argv) {
440 return make_reg(0, 1);
441 }
442
kDoAudioBitDepth(EngineState * s,int argc,reg_t * argv)443 reg_t kDoAudioBitDepth(EngineState *s, int argc, reg_t *argv) {
444 if (argc > 0) {
445 const uint16 bitDepth = argv[0].toUint16();
446 if (bitDepth != 0) {
447 g_sci->_audio32->setBitDepth(bitDepth);
448 }
449 }
450
451 return make_reg(0, g_sci->_audio32->getBitDepth());
452 }
453
kDoAudioMixing(EngineState * s,int argc,reg_t * argv)454 reg_t kDoAudioMixing(EngineState *s, int argc, reg_t *argv) {
455 return g_sci->_audio32->kernelMixing(argc, argv);
456 }
457
kDoAudioChannels(EngineState * s,int argc,reg_t * argv)458 reg_t kDoAudioChannels(EngineState *s, int argc, reg_t *argv) {
459 if (argc > 0) {
460 const int16 numChannels = argv[0].toSint16();
461 if (numChannels != 0) {
462 g_sci->_audio32->setNumOutputChannels(numChannels);
463 }
464 }
465
466 return make_reg(0, g_sci->_audio32->getNumOutputChannels());
467 }
468
kDoAudioPreload(EngineState * s,int argc,reg_t * argv)469 reg_t kDoAudioPreload(EngineState *s, int argc, reg_t *argv) {
470 if (argc > 0) {
471 g_sci->_audio32->setPreload(argv[0].toUint16());
472 }
473
474 return make_reg(0, g_sci->_audio32->getPreload());
475 }
476
kDoAudioFade(EngineState * s,int argc,reg_t * argv)477 reg_t kDoAudioFade(EngineState *s, int argc, reg_t *argv) {
478 return g_sci->_audio32->kernelFade(s, argc, argv);
479 }
480
kDoAudioHasSignal(EngineState * s,int argc,reg_t * argv)481 reg_t kDoAudioHasSignal(EngineState *s, int argc, reg_t *argv) {
482 return make_reg(0, g_sci->_audio32->hasSignal());
483 }
484
kDoAudioSetLoop(EngineState * s,int argc,reg_t * argv)485 reg_t kDoAudioSetLoop(EngineState *s, int argc, reg_t *argv) {
486 g_sci->_audio32->kernelLoop(s, argc, argv);
487 return s->r_acc;
488 }
489
kDoAudioPan(EngineState * s,int argc,reg_t * argv)490 reg_t kDoAudioPan(EngineState *s, int argc, reg_t *argv) {
491 g_sci->_audio32->kernelPan(s, argc, argv);
492 return s->r_acc;
493 }
494
kDoAudioPanOff(EngineState * s,int argc,reg_t * argv)495 reg_t kDoAudioPanOff(EngineState *s, int argc, reg_t *argv) {
496 g_sci->_audio32->kernelPanOff(s, argc, argv);
497 return s->r_acc;
498 }
499
kSetLanguage(EngineState * s,int argc,reg_t * argv)500 reg_t kSetLanguage(EngineState *s, int argc, reg_t *argv) {
501 // Used by script 90 of MUMG Deluxe from the main menu to toggle between
502 // English and Spanish in some versions and English and Spanish and
503 // French and German in others.
504 const Common::String audioDirectory = s->_segMan->getString(argv[0]);
505 if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
506 g_sci->getResMan()->changeMacAudioDirectory(audioDirectory);
507 } else {
508 g_sci->getResMan()->changeAudioDirectory(audioDirectory);
509 }
510 return s->r_acc;
511 }
512
513 #endif
514
515 } // End of namespace Sci
516