diff options
Diffstat (limited to 'engines/sci/engine/ksound.cpp')
-rw-r--r-- | engines/sci/engine/ksound.cpp | 280 |
1 files changed, 222 insertions, 58 deletions
diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp index 6a1708d21a..ed53b8d52f 100644 --- a/engines/sci/engine/ksound.cpp +++ b/engines/sci/engine/ksound.cpp @@ -26,7 +26,11 @@ #include "sci/engine/kernel.h" #include "sci/engine/vm.h" // for Object #include "sci/sound/audio.h" +#ifdef ENABLE_SCI32 +#include "sci/sound/audio32.h" +#endif #include "sci/sound/soundcmd.h" +#include "sci/sound/sync.h" #include "audio/mixer.h" #include "common/system.h" @@ -46,7 +50,6 @@ reg_t kDoSound(EngineState *s, int argc, reg_t *argv) { CREATE_DOSOUND_FORWARD(DoSoundInit) CREATE_DOSOUND_FORWARD(DoSoundPlay) -CREATE_DOSOUND_FORWARD(DoSoundRestore) CREATE_DOSOUND_FORWARD(DoSoundDispose) CREATE_DOSOUND_FORWARD(DoSoundMute) CREATE_DOSOUND_FORWARD(DoSoundStop) @@ -61,13 +64,41 @@ CREATE_DOSOUND_FORWARD(DoSoundUpdateCues) CREATE_DOSOUND_FORWARD(DoSoundSendMidi) CREATE_DOSOUND_FORWARD(DoSoundGlobalReverb) CREATE_DOSOUND_FORWARD(DoSoundSetHold) -CREATE_DOSOUND_FORWARD(DoSoundDummy) CREATE_DOSOUND_FORWARD(DoSoundGetAudioCapability) CREATE_DOSOUND_FORWARD(DoSoundSuspend) CREATE_DOSOUND_FORWARD(DoSoundSetVolume) CREATE_DOSOUND_FORWARD(DoSoundSetPriority) CREATE_DOSOUND_FORWARD(DoSoundSetLoop) +#ifdef ENABLE_SCI32 +reg_t kDoSoundPhantasmagoriaMac(EngineState *s, int argc, reg_t *argv) { + // Phantasmagoria Mac (and seemingly no other game (!)) uses this + // cutdown version of kDoSound. + + switch (argv[0].toUint16()) { + case 0: + return g_sci->_soundCmd->kDoSoundMasterVolume(argc - 1, argv + 1, s->r_acc); + case 2: + return g_sci->_soundCmd->kDoSoundInit(argc - 1, argv + 1, s->r_acc); + case 3: + return g_sci->_soundCmd->kDoSoundDispose(argc - 1, argv + 1, s->r_acc); + case 4: + return g_sci->_soundCmd->kDoSoundPlay(argc - 1, argv + 1, s->r_acc); + case 5: + return g_sci->_soundCmd->kDoSoundStop(argc - 1, argv + 1, s->r_acc); + case 8: + return g_sci->_soundCmd->kDoSoundSetVolume(argc - 1, argv + 1, s->r_acc); + case 9: + return g_sci->_soundCmd->kDoSoundSetLoop(argc - 1, argv + 1, s->r_acc); + case 10: + return g_sci->_soundCmd->kDoSoundUpdateCues(argc - 1, argv + 1, s->r_acc); + } + + error("Unknown kDoSound Phantasmagoria Mac subop %d", argv[0].toUint16()); + return s->r_acc; +} +#endif + reg_t kDoCdAudio(EngineState *s, int argc, reg_t *argv) { switch (argv[0].toUint16()) { case kSciAudioPlay: { @@ -113,7 +144,8 @@ reg_t kDoCdAudio(EngineState *s, int argc, reg_t *argv) { } /** - * Used for speech playback and digital soundtracks in CD games + * Used for speech playback and digital soundtracks in CD games. + * This is the SCI16 version; SCI32 is handled separately. */ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) { // JonesCD uses different functions based on the cdaudio.map file @@ -184,14 +216,6 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) { int16 volume = argv[1].toUint16(); volume = CLIP<int16>(volume, 0, AUDIO_VOLUME_MAX); debugC(kDebugLevelSound, "kDoAudio: set volume to %d", volume); -#ifdef ENABLE_SCI32 - if (getSciVersion() >= SCI_VERSION_2_1) { - int16 volumePrev = mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType) / 2; - volumePrev = CLIP<int16>(volumePrev, 0, AUDIO_VOLUME_MAX); - mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume * 2); - return make_reg(0, volumePrev); - } else -#endif mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume * 2); break; } @@ -206,8 +230,15 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) { // athrxx: It seems from disasm that the original KQ5 FM-Towns loads a default language (Japanese) audio map at the beginning // right after loading the video and audio drivers. The -1 language argument in here simply means that the original will stick // with Japanese. Instead of doing that we switch to the language selected in the launcher. - if (g_sci->getPlatform() == Common::kPlatformFMTowns && language == -1) - language = (g_sci->getLanguage() == Common::JA_JPN) ? K_LANG_JAPANESE : K_LANG_ENGLISH; + if (g_sci->getPlatform() == Common::kPlatformFMTowns && language == -1) { + // FM-Towns calls us to get the current language / also set the default language + // This doesn't just happen right at the start, but also when the user clicks on the Sierra logo in the game menu + // It uses the result of this call to either show "English Voices" or "Japanese Voices". + + // Language should have been set by setLauncherLanguage() already (or could have been modified by the scripts). + // Get this language setting, so that the chosen language will get set for resource manager. + language = g_sci->getSciLanguage(); + } debugC(kDebugLevelSound, "kDoAudio: set language to %d", language); @@ -225,27 +256,40 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) { if (getSciVersion() <= SCI_VERSION_1_1) { debugC(kDebugLevelSound, "kDoAudio: CD audio subop"); return kDoCdAudio(s, argc - 1, argv + 1); -#ifdef ENABLE_SCI32 - } else { - // TODO: This isn't CD Audio in SCI32 anymore - warning("kDoAudio: Unhandled case 10, %d extra arguments passed", argc - 1); - break; -#endif } - // 3 new subops in Pharkas. kDoAudio in Pharkas sits at seg026:038C + // 3 new subops in Pharkas CD (including CD demo). kDoAudio in Pharkas sits at seg026:038C case 11: // Not sure where this is used yet warning("kDoAudio: Unhandled case 11, %d extra arguments passed", argc - 1); break; case 12: - // Seems to be some sort of audio sync, used in Pharkas. Silenced the - // warning due to the high level of spam it produces. (takes no params) - //warning("kDoAudio: Unhandled case 12, %d extra arguments passed", argc - 1); + // SSCI calls this function with no parameters from + // the TalkRandCycle class and branches on the return + // value like a boolean. The conjectured purpose of + // this function is to ensure that the talker's mouth + // does not move if there is read jitter (slow CD + // drive, scratched CD). The old behavior here of not + // doing anything caused a nonzero value to be left in + // the accumulator by chance. This is equivalent, but + // more explicit. + + return make_reg(0, 1); break; case 13: - // Used in Pharkas whenever a speech sample starts (takes no params) - //warning("kDoAudio: Unhandled case 13, %d extra arguments passed", argc - 1); + // SSCI returns a serial number for the played audio + // here, used in the PointsSound class. The reason is severalfold: + + // 1. SSCI does not support multiple wave effects at once + // 2. FPFP may disable its icon bar during the points sound. + // 3. Each new sound preempts any sound already playing. + // 4. If the points sound is interrupted before completion, + // the icon bar could remain disabled. + + // Since points (1) and (3) do not apply to us, we can simply + // return a constant here. This is equivalent to the + // old behavior, as above. + return make_reg(0, 1); break; case 17: // Seems to be some sort of audio sync, used in SQ6. Silenced the @@ -260,14 +304,12 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) { } reg_t kDoSync(EngineState *s, int argc, reg_t *argv) { - SegManager *segMan = s->_segMan; switch (argv[0].toUint16()) { case kSciAudioSyncStart: { ResourceId id; - g_sci->_audio->stopSoundSync(); + g_sci->_sync->stop(); - // Load sound sync resource and lock it if (argc == 3) { id = ResourceId(kResourceTypeSync, argv[2].toUint16()); } else if (argc == 7) { @@ -278,14 +320,14 @@ reg_t kDoSync(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } - g_sci->_audio->setSoundSync(id, argv[1], segMan); + g_sci->_sync->start(id, argv[1]); break; } case kSciAudioSyncNext: - g_sci->_audio->doSoundSync(argv[1], segMan); + g_sci->_sync->next(argv[1]); break; case kSciAudioSyncStop: - g_sci->_audio->stopSoundSync(); + g_sci->_sync->stop(); break; default: error("DoSync: Unhandled subfunction %d", argv[0].toUint16()); @@ -295,6 +337,155 @@ reg_t kDoSync(EngineState *s, int argc, reg_t *argv) { } #ifdef ENABLE_SCI32 +reg_t kDoAudio32(EngineState *s, int argc, reg_t *argv) { + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} + +reg_t kDoAudioInit(EngineState *s, int argc, reg_t *argv) { + return make_reg(0, 0); +} + +reg_t kDoAudioWaitForPlay(EngineState *s, int argc, reg_t *argv) { + return g_sci->_audio32->kernelPlay(false, argc, argv); +} + +reg_t kDoAudioPlay(EngineState *s, int argc, reg_t *argv) { + return g_sci->_audio32->kernelPlay(true, argc, argv); +} + +reg_t kDoAudioStop(EngineState *s, int argc, reg_t *argv) { + const int16 channelIndex = g_sci->_audio32->findChannelByArgs(argc, argv, 0, argc > 1 ? argv[1] : NULL_REG); + return make_reg(0, g_sci->_audio32->stop(channelIndex)); +} + +reg_t kDoAudioPause(EngineState *s, int argc, reg_t *argv) { + const int16 channelIndex = g_sci->_audio32->findChannelByArgs(argc, argv, 0, argc > 1 ? argv[1] : NULL_REG); + return make_reg(0, g_sci->_audio32->pause(channelIndex)); +} + +reg_t kDoAudioResume(EngineState *s, int argc, reg_t *argv) { + const int16 channelIndex = g_sci->_audio32->findChannelByArgs(argc, argv, 0, argc > 1 ? argv[1] : NULL_REG); + return make_reg(0, g_sci->_audio32->resume(channelIndex)); +} + +reg_t kDoAudioPosition(EngineState *s, int argc, reg_t *argv) { + const int16 channelIndex = g_sci->_audio32->findChannelByArgs(argc, argv, 0, argc > 1 ? argv[1] : NULL_REG); + return make_reg(0, g_sci->_audio32->getPosition(channelIndex)); +} + +reg_t kDoAudioRate(EngineState *s, int argc, reg_t *argv) { + // NOTE: In the original engine this would set the hardware + // DSP sampling rate; ScummVM mixer does not need this, so + // we only store the value to satisfy engine compatibility. + + if (argc > 0) { + const uint16 sampleRate = argv[0].toUint16(); + if (sampleRate != 0) { + g_sci->_audio32->setSampleRate(sampleRate); + } + } + + return make_reg(0, g_sci->_audio32->getSampleRate()); +} + +reg_t kDoAudioVolume(EngineState *s, int argc, reg_t *argv) { + const int16 volume = argc > 0 ? argv[0].toSint16() : -1; + const int16 channelIndex = g_sci->_audio32->findChannelByArgs(argc, argv, 1, argc > 2 ? argv[2] : NULL_REG); + + if (volume != -1) { + g_sci->_audio32->setVolume(channelIndex, volume); + } + + return make_reg(0, g_sci->_audio32->getVolume(channelIndex)); +} + +reg_t kDoAudioGetCapability(EngineState *s, int argc, reg_t *argv) { + return make_reg(0, 1); +} + +reg_t kDoAudioBitDepth(EngineState *s, int argc, reg_t *argv) { + // NOTE: In the original engine this would set the hardware + // DSP bit depth; ScummVM mixer does not need this, so + // we only store the value to satisfy engine compatibility. + + if (argc > 0) { + const uint16 bitDepth = argv[0].toUint16(); + if (bitDepth != 0) { + g_sci->_audio32->setBitDepth(bitDepth); + } + } + + return make_reg(0, g_sci->_audio32->getBitDepth()); +} + +reg_t kDoAudioMixing(EngineState *s, int argc, reg_t *argv) { + if (argc > 0) { + g_sci->_audio32->setAttenuatedMixing(argv[0].toUint16()); + } + + return make_reg(0, g_sci->_audio32->getAttenuatedMixing()); +} + +reg_t kDoAudioChannels(EngineState *s, int argc, reg_t *argv) { + // NOTE: In the original engine this would set the hardware + // DSP stereo output; ScummVM mixer does not need this, so + // we only store the value to satisfy engine compatibility. + + if (argc > 0) { + const int16 numChannels = argv[0].toSint16(); + if (numChannels != 0) { + g_sci->_audio32->setNumOutputChannels(numChannels); + } + } + + return make_reg(0, g_sci->_audio32->getNumOutputChannels()); +} + +reg_t kDoAudioPreload(EngineState *s, int argc, reg_t *argv) { + // NOTE: In the original engine this would cause audio + // data for new channels to be preloaded to memory when + // the channel was initialized; we do not need this, so + // we only store the value to satisfy engine compatibility. + + if (argc > 0) { + g_sci->_audio32->setPreload(argv[0].toUint16()); + } + + return make_reg(0, g_sci->_audio32->getPreload()); +} + +reg_t kDoAudioFade(EngineState *s, int argc, reg_t *argv) { + if (argc < 4) { + return make_reg(0, 0); + } + + // NOTE: Sierra did a nightmarish hack here, temporarily replacing + // the argc of the kernel arguments with 2 and then restoring it + // after findChannelByArgs was called. + const int16 channelIndex = g_sci->_audio32->findChannelByArgs(2, argv, 0, argc > 5 ? argv[5] : NULL_REG); + + const int16 volume = argv[1].toSint16(); + const int16 speed = argv[2].toSint16(); + const int16 steps = argv[3].toSint16(); + const bool stopAfterFade = argc > 4 ? (bool)argv[4].toUint16() : false; + + return make_reg(0, g_sci->_audio32->fadeChannel(channelIndex, volume, speed, steps, stopAfterFade)); +} + +reg_t kDoAudioHasSignal(EngineState *s, int argc, reg_t *argv) { + return make_reg(0, g_sci->_audio32->hasSignal()); +} + +reg_t kDoAudioSetLoop(EngineState *s, int argc, reg_t *argv) { + const int16 channelIndex = g_sci->_audio32->findChannelByArgs(argc, argv, 0, argc == 3 ? argv[2] : NULL_REG); + + const bool loop = argv[0].toSint16() != 0 && argv[0].toSint16() != 1; + + g_sci->_audio32->setLoop(channelIndex, loop); + return s->r_acc; +} reg_t kSetLanguage(EngineState *s, int argc, reg_t *argv) { // This is used by script 90 of MUMG Deluxe from the main menu to toggle @@ -309,33 +500,6 @@ reg_t kSetLanguage(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -reg_t kDoSoundPhantasmagoriaMac(EngineState *s, int argc, reg_t *argv) { - // Phantasmagoria Mac (and seemingly no other game (!)) uses this - // cutdown version of kDoSound. - - switch (argv[0].toUint16()) { - case 0: - return g_sci->_soundCmd->kDoSoundMasterVolume(argc - 1, argv + 1, s->r_acc); - case 2: - return g_sci->_soundCmd->kDoSoundInit(argc - 1, argv + 1, s->r_acc); - case 3: - return g_sci->_soundCmd->kDoSoundDispose(argc - 1, argv + 1, s->r_acc); - case 4: - return g_sci->_soundCmd->kDoSoundPlay(argc - 1, argv + 1, s->r_acc); - case 5: - return g_sci->_soundCmd->kDoSoundStop(argc - 1, argv + 1, s->r_acc); - case 8: - return g_sci->_soundCmd->kDoSoundSetVolume(argc - 1, argv + 1, s->r_acc); - case 9: - return g_sci->_soundCmd->kDoSoundSetLoop(argc - 1, argv + 1, s->r_acc); - case 10: - return g_sci->_soundCmd->kDoSoundUpdateCues(argc - 1, argv + 1, s->r_acc); - } - - error("Unknown kDoSound Phantasmagoria Mac subop %d", argv[0].toUint16()); - return s->r_acc; -} - #endif } // End of namespace Sci |