aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/sound/soundcmd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/sound/soundcmd.cpp')
-rw-r--r--engines/sci/sound/soundcmd.cpp1090
1 files changed, 1090 insertions, 0 deletions
diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp
new file mode 100644
index 0000000000..06b87afdfd
--- /dev/null
+++ b/engines/sci/sound/soundcmd.cpp
@@ -0,0 +1,1090 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+#include "sci/sound/iterator/iterator.h" // for SongIteratorStatus
+#endif
+
+#include "sci/sound/music.h"
+#include "sci/sound/soundcmd.h"
+
+namespace Sci {
+
+#define SCI1_SOUND_FLAG_MAY_PAUSE 1 /* Only here for completeness; The interpreter doesn't touch this bit */
+#define SCI1_SOUND_FLAG_SCRIPTED_PRI 2 /* but does touch this */
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+#define FROBNICATE_HANDLE(reg) ((reg).segment << 16 | (reg).offset)
+#define DEFROBNICATE_HANDLE(handle) (make_reg((handle >> 16) & 0xffff, handle & 0xffff))
+#endif
+
+#define SOUNDCOMMAND(x) _soundCommands.push_back(new MusicEntryCommand(#x, &SoundCommandParser::x))
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+static void script_set_priority(ResourceManager *resMan, SegManager *segMan, SfxState *state, reg_t obj, int priority) {
+ int song_nr = GET_SEL32V(segMan, obj, number);
+ Resource *song = resMan->findResource(ResourceId(kResourceTypeSound, song_nr), 0);
+ int flags = GET_SEL32V(segMan, obj, flags);
+
+ if (priority == -1) {
+ if (song->data[0] == 0xf0)
+ priority = song->data[1];
+ else
+ warning("Attempt to unset song priority when there is no built-in value");
+
+ flags &= ~SCI1_SOUND_FLAG_SCRIPTED_PRI;
+ } else flags |= SCI1_SOUND_FLAG_SCRIPTED_PRI;
+
+ state->sfx_song_renice(FROBNICATE_HANDLE(obj), priority);
+ PUT_SEL32V(segMan, obj, flags, flags);
+}
+
+SongIterator *build_iterator(ResourceManager *resMan, int song_nr, SongIteratorType type, songit_id_t id) {
+ Resource *song = resMan->findResource(ResourceId(kResourceTypeSound, song_nr), 0);
+
+ if (!song)
+ return NULL;
+
+ return songit_new(song->data, song->size, type, id);
+}
+
+void process_sound_events(EngineState *s) { /* Get all sound events, apply their changes to the heap */
+ int result;
+ SongHandle handle;
+ int cue;
+ SegManager *segMan = s->_segMan;
+
+ if (getSciVersion() > SCI_VERSION_01)
+ return;
+ // SCI1 and later explicitly poll for everything
+
+ while ((result = s->_sound.sfx_poll(&handle, &cue))) {
+ reg_t obj = DEFROBNICATE_HANDLE(handle);
+ if (!s->_segMan->isObject(obj)) {
+ warning("Non-object %04x:%04x received sound signal (%d/%d)", PRINT_REG(obj), result, cue);
+ return;
+ }
+
+ switch (result) {
+
+ case SI_LOOP:
+ debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x looped (to %d)\n",
+ PRINT_REG(obj), cue);
+ /* PUT_SEL32V(segMan, obj, loops, GET_SEL32V(segMan, obj, loop) - 1);*/
+ PUT_SEL32V(segMan, obj, signal, SIGNAL_OFFSET);
+ break;
+
+ case SI_RELATIVE_CUE:
+ debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x received relative cue %d\n",
+ PRINT_REG(obj), cue);
+ PUT_SEL32V(segMan, obj, signal, cue + 0x7f);
+ break;
+
+ case SI_ABSOLUTE_CUE:
+ debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x received absolute cue %d\n",
+ PRINT_REG(obj), cue);
+ PUT_SEL32V(segMan, obj, signal, cue);
+ break;
+
+ case SI_FINISHED:
+ debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x finished\n",
+ PRINT_REG(obj));
+ PUT_SEL32V(segMan, obj, signal, SIGNAL_OFFSET);
+ PUT_SEL32V(segMan, obj, state, kSoundStopped);
+ break;
+
+ default:
+ warning("Unexpected result from sfx_poll: %d", result);
+ break;
+ }
+ }
+}
+
+#endif
+SoundCommandParser::SoundCommandParser(ResourceManager *resMan, SegManager *segMan, AudioPlayer *audio, SciVersion soundVersion) :
+ _resMan(resMan), _segMan(segMan), _audio(audio), _soundVersion(soundVersion) {
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ // The following hack is needed to ease the change from old to new sound code (because the new sound code does not use SfxState)
+ _state = &((SciEngine *)g_engine)->getEngineState()->_sound; // HACK
+#endif
+
+ #ifndef USE_OLD_MUSIC_FUNCTIONS
+ _music = new SciMusic(_soundVersion);
+ _music->init();
+ #endif
+
+ switch (_soundVersion) {
+ case SCI_VERSION_0_EARLY:
+ case SCI_VERSION_0_LATE:
+ SOUNDCOMMAND(cmdInitSound);
+ SOUNDCOMMAND(cmdPlaySound);
+ SOUNDCOMMAND(cmdDummy);
+ SOUNDCOMMAND(cmdDisposeSound);
+ SOUNDCOMMAND(cmdMuteSound);
+ SOUNDCOMMAND(cmdStopSound);
+ SOUNDCOMMAND(cmdPauseSound);
+ SOUNDCOMMAND(cmdResumeSound);
+ SOUNDCOMMAND(cmdMasterVolume);
+ SOUNDCOMMAND(cmdUpdateSound);
+ SOUNDCOMMAND(cmdFadeSound);
+ SOUNDCOMMAND(cmdGetPolyphony);
+ SOUNDCOMMAND(cmdStopAllSounds);
+ _cmdUpdateCuesIndex = -1;
+ break;
+ case SCI_VERSION_1_EARLY:
+ SOUNDCOMMAND(cmdMasterVolume);
+ SOUNDCOMMAND(cmdMuteSound);
+ SOUNDCOMMAND(cmdDummy);
+ SOUNDCOMMAND(cmdGetPolyphony);
+ SOUNDCOMMAND(cmdUpdateSound);
+ SOUNDCOMMAND(cmdInitSound);
+ SOUNDCOMMAND(cmdDisposeSound);
+ SOUNDCOMMAND(cmdPlaySound);
+ SOUNDCOMMAND(cmdStopSound);
+ SOUNDCOMMAND(cmdPauseSound);
+ SOUNDCOMMAND(cmdFadeSound);
+ SOUNDCOMMAND(cmdUpdateCues);
+ SOUNDCOMMAND(cmdSendMidi);
+ SOUNDCOMMAND(cmdReverb);
+ SOUNDCOMMAND(cmdSetSoundHold);
+ _cmdUpdateCuesIndex = 11;
+ break;
+ case SCI_VERSION_1_LATE:
+ SOUNDCOMMAND(cmdMasterVolume);
+ SOUNDCOMMAND(cmdMuteSound);
+ SOUNDCOMMAND(cmdDummy);
+ SOUNDCOMMAND(cmdGetPolyphony);
+ SOUNDCOMMAND(cmdGetAudioCapability);
+ SOUNDCOMMAND(cmdSuspendSound);
+ SOUNDCOMMAND(cmdInitSound);
+ SOUNDCOMMAND(cmdDisposeSound);
+ SOUNDCOMMAND(cmdPlaySound);
+ SOUNDCOMMAND(cmdStopSound);
+ SOUNDCOMMAND(cmdPauseSound);
+ SOUNDCOMMAND(cmdFadeSound);
+ SOUNDCOMMAND(cmdSetSoundHold);
+ SOUNDCOMMAND(cmdDummy);
+ SOUNDCOMMAND(cmdSetSoundVolume);
+ SOUNDCOMMAND(cmdSetSoundPriority);
+ SOUNDCOMMAND(cmdSetSoundLoop);
+ SOUNDCOMMAND(cmdUpdateCues);
+ SOUNDCOMMAND(cmdSendMidi);
+ SOUNDCOMMAND(cmdReverb);
+ SOUNDCOMMAND(cmdUpdateSound);
+ _cmdUpdateCuesIndex = 17;
+ break;
+ default:
+ warning("Sound command parser: unknown sound version %d", _soundVersion);
+ break;
+ }
+}
+
+SoundCommandParser::~SoundCommandParser() {
+}
+
+reg_t SoundCommandParser::parseCommand(int argc, reg_t *argv, reg_t acc) {
+ uint16 command = argv[0].toUint16();
+ reg_t obj = (argc > 1) ? argv[1] : NULL_REG;
+ int16 value = (argc > 2) ? argv[2].toSint16() : 0;
+ _acc = acc;
+ _argc = argc;
+ _argv = argv;
+
+ if (argc == 6) { // cmdSendMidi
+ byte channel = argv[2].toUint16() & 0xf;
+ byte midiCmd = argv[3].toUint16() & 0xff;
+
+ uint16 controller = argv[4].toUint16();
+ uint16 param = argv[5].toUint16();
+
+ _midiCommand = (channel | midiCmd) | ((uint32)controller << 8) | ((uint32)param << 16);
+ }
+
+ if (command < _soundCommands.size()) {
+ if (command != _cmdUpdateCuesIndex) {
+ //printf("%s, object %04x:%04x\n", _soundCommands[command]->desc, PRINT_REG(obj)); // debug
+ debugC(2, kDebugLevelSound, "%s, object %04x:%04x", _soundCommands[command]->desc, PRINT_REG(obj));
+ }
+
+ (this->*(_soundCommands[command]->sndCmd))(obj, value);
+ } else {
+ warning("Invalid sound command requested (%d), valid range is 0-%d", command, _soundCommands.size() - 1);
+ }
+
+ return _acc;
+}
+
+void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) {
+ if (!obj.segment)
+ return;
+
+ int number = obj.segment ? GET_SEL32V(_segMan, obj, number) : 0;
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+
+ SongHandle handle = FROBNICATE_HANDLE(obj);
+
+ if (_soundVersion != SCI_VERSION_1_LATE) {
+ if (!obj.segment)
+ return;
+ }
+
+ SongIteratorType type = (_soundVersion <= SCI_VERSION_0_LATE) ? SCI_SONG_ITERATOR_TYPE_SCI0 : SCI_SONG_ITERATOR_TYPE_SCI1;
+
+ if (_soundVersion <= SCI_VERSION_0_LATE) {
+ if (GET_SEL32V(_segMan, obj, nodePtr)) {
+ _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
+ _state->sfx_remove_song(handle);
+ }
+ }
+
+ if (!obj.segment || !_resMan->testResource(ResourceId(kResourceTypeSound, number)))
+ return;
+
+ _state->sfx_add_song(build_iterator(_resMan, number, type, handle), 0, handle, number);
+
+
+ // Notify the engine
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ PUT_SEL32V(_segMan, obj, state, kSoundInitialized);
+ else
+ PUT_SEL32(_segMan, obj, nodePtr, obj);
+
+ PUT_SEL32(_segMan, obj, handle, obj);
+
+#else
+
+ MusicEntry *newSound = new MusicEntry();
+ newSound->resnum = number;
+ if (number && _resMan->testResource(ResourceId(kResourceTypeSound, number)))
+ newSound->soundRes = new SoundResource(number, _resMan, _soundVersion);
+ else
+ newSound->soundRes = 0;
+
+ newSound->soundObj = obj;
+ newSound->loop = GET_SEL32V(_segMan, obj, loop);
+ newSound->prio = GET_SEL32V(_segMan, obj, pri) & 0xFF;
+ if (_soundVersion >= SCI_VERSION_1_LATE)
+ newSound->volume = CLIP<int>(GET_SEL32V(_segMan, obj, vol), 0, MUSIC_VOLUME_MAX);
+
+ // Check if a track with the same sound object is already playing
+ MusicEntry *oldSound = _music->getSlot(obj);
+ if (oldSound)
+ cmdDisposeSound(obj, value);
+
+ // In SCI1.1 games, sound effects are started from here. If we can find
+ // a relevant audio resource, play it, otherwise switch to synthesized
+ // effects. If the resource exists, play it using map 65535 (sound
+ // effects map)
+
+ if (getSciVersion() >= SCI_VERSION_1_1 && _resMan->testResource(ResourceId(kResourceTypeAudio, number))) {
+ // Found a relevant audio resource, play it
+ int sampleLen;
+ newSound->pStreamAud = _audio->getAudioStream(number, 65535, &sampleLen);
+ newSound->soundType = Audio::Mixer::kSpeechSoundType;
+ } else {
+ if (newSound->soundRes)
+ _music->soundInitSnd(newSound);
+ }
+
+ _music->pushBackSlot(newSound);
+
+ if (newSound->soundRes || newSound->pStreamAud) {
+ // Notify the engine
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ PUT_SEL32V(_segMan, obj, state, kSoundInitialized);
+ else
+ PUT_SEL32(_segMan, obj, nodePtr, obj);
+
+ PUT_SEL32(_segMan, obj, handle, obj);
+ }
+#endif
+
+}
+
+void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) {
+ if (!obj.segment)
+ return;
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ SongHandle handle = FROBNICATE_HANDLE(obj);
+
+ if (_soundVersion <= SCI_VERSION_0_LATE) {
+ _state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING);
+ _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, loop));
+ PUT_SEL32V(_segMan, obj, state, kSoundPlaying);
+ } else if (_soundVersion == SCI_VERSION_1_EARLY) {
+ _state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING);
+ _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, loop));
+ _state->sfx_song_renice(handle, GET_SEL32V(_segMan, obj, pri));
+ RESTORE_BEHAVIOR rb = (RESTORE_BEHAVIOR) value; /* Too lazy to look up a default value for this */
+ _state->_songlib.setSongRestoreBehavior(handle, rb);
+ PUT_SEL32V(_segMan, obj, signal, 0);
+ } else if (_soundVersion == SCI_VERSION_1_LATE) {
+ int looping = GET_SEL32V(_segMan, obj, loop);
+ //int vol = GET_SEL32V(_segMan, obj, vol);
+ int pri = GET_SEL32V(_segMan, obj, pri);
+ int sampleLen = 0;
+ Song *song = _state->_songlib.findSong(handle);
+ int songNumber = GET_SEL32V(_segMan, obj, number);
+
+ if (GET_SEL32V(_segMan, obj, nodePtr) && (song && songNumber != song->_resourceNum)) {
+ _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
+ _state->sfx_remove_song(handle);
+ PUT_SEL32(_segMan, obj, nodePtr, NULL_REG);
+ }
+
+ if (!GET_SEL32V(_segMan, obj, nodePtr) && obj.segment) {
+ // In SCI1.1 games, sound effects are started from here. If we can find
+ // a relevant audio resource, play it, otherwise switch to synthesized
+ // effects. If the resource exists, play it using map 65535 (sound
+ // effects map)
+ if (_resMan->testResource(ResourceId(kResourceTypeAudio, songNumber)) &&
+ getSciVersion() >= SCI_VERSION_1_1) {
+ // Found a relevant audio resource, play it
+ _audio->stopAudio();
+ warning("Initializing audio resource instead of requested sound resource %d", songNumber);
+ sampleLen = _audio->startAudio(65535, songNumber);
+ // Also create iterator, that will fire SI_FINISHED event, when the sound is done playing
+ _state->sfx_add_song(new_timer_iterator(sampleLen), 0, handle, songNumber);
+ } else {
+ if (!_resMan->testResource(ResourceId(kResourceTypeSound, songNumber))) {
+ warning("Could not open song number %d", songNumber);
+ // Send a "stop handle" event so that the engine won't wait forever here
+ _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
+ PUT_SEL32V(_segMan, obj, signal, SIGNAL_OFFSET);
+ return;
+ }
+ debugC(2, kDebugLevelSound, "Initializing song number %d\n", songNumber);
+ _state->sfx_add_song(build_iterator(_resMan, songNumber, SCI_SONG_ITERATOR_TYPE_SCI1,
+ handle), 0, handle, songNumber);
+ }
+
+ PUT_SEL32(_segMan, obj, nodePtr, obj);
+ PUT_SEL32(_segMan, obj, handle, obj);
+ }
+
+ if (obj.segment) {
+ _state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING);
+ _state->sfx_song_set_loops(handle, looping);
+ _state->sfx_song_renice(handle, pri);
+ PUT_SEL32V(_segMan, obj, signal, 0);
+ }
+ }
+
+#else
+
+ MusicEntry *musicSlot = _music->getSlot(obj);
+ if (!musicSlot) {
+ warning("cmdPlaySound: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return;
+ }
+
+ int number = obj.segment ? GET_SEL32V(_segMan, obj, number) : -1;
+
+ if (musicSlot->resnum != number) { // another sound loaded into struct
+ cmdDisposeSound(obj, value);
+ cmdInitSound(obj, value);
+ // Find slot again :)
+ musicSlot = _music->getSlot(obj);
+ }
+ int16 loop = GET_SEL32V(_segMan, obj, loop);
+ debugC(2, kDebugLevelSound, "cmdPlaySound: resource number %d, loop %d", number, loop);
+
+ PUT_SEL32(_segMan, obj, handle, obj);
+
+ if (_soundVersion >= SCI_VERSION_1_EARLY) {
+ PUT_SEL32(_segMan, obj, nodePtr, obj);
+ PUT_SEL32V(_segMan, obj, min, 0);
+ PUT_SEL32V(_segMan, obj, sec, 0);
+ PUT_SEL32V(_segMan, obj, frame, 0);
+ PUT_SEL32V(_segMan, obj, signal, 0);
+ } else {
+ PUT_SEL32V(_segMan, obj, state, kSoundPlaying);
+ }
+
+ musicSlot->loop = GET_SEL32V(_segMan, obj, loop);
+ musicSlot->prio = GET_SEL32V(_segMan, obj, priority);
+ if (_soundVersion >= SCI_VERSION_1_LATE)
+ musicSlot->volume = GET_SEL32V(_segMan, obj, vol);
+ _music->soundPlay(musicSlot);
+
+#endif
+
+}
+
+void SoundCommandParser::cmdDummy(reg_t obj, int16 value) {
+ warning("cmdDummy invoked"); // not supposed to occur
+}
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+void SoundCommandParser::changeSoundStatus(reg_t obj, int newStatus) {
+ SongHandle handle = FROBNICATE_HANDLE(obj);
+ if (obj.segment) {
+ _state->sfx_song_set_status(handle, newStatus);
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ PUT_SEL32V(_segMan, obj, state, newStatus);
+ }
+}
+#endif
+
+void SoundCommandParser::cmdDisposeSound(reg_t obj, int16 value) {
+ if (!obj.segment)
+ return;
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ SongHandle handle = FROBNICATE_HANDLE(obj);
+ changeSoundStatus(obj, SOUND_STATUS_STOPPED);
+
+ if (obj.segment) {
+ _state->sfx_remove_song(handle);
+
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ PUT_SEL32V(_segMan, obj, handle, 0x0000);
+ }
+
+#else
+
+ MusicEntry *musicSlot = _music->getSlot(obj);
+ if (!musicSlot) {
+ warning("cmdDisposeSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return;
+ }
+
+ cmdStopSound(obj, value);
+
+ _music->soundKill(musicSlot);
+ if (_soundVersion >= SCI_VERSION_1_EARLY)
+ PUT_SEL32(_segMan, obj, nodePtr, NULL_REG);
+ else
+ PUT_SEL32V(_segMan, obj, state, kSoundStopped);
+#endif
+}
+
+void SoundCommandParser::cmdStopSound(reg_t obj, int16 value) {
+ if (!obj.segment)
+ return;
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ changeSoundStatus(obj, SOUND_STATUS_STOPPED);
+
+ if (_soundVersion >= SCI_VERSION_1_EARLY)
+ PUT_SEL32V(_segMan, obj, signal, SIGNAL_OFFSET);
+#else
+ MusicEntry *musicSlot = _music->getSlot(obj);
+ if (!musicSlot) {
+ warning("cmdStopSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return;
+ }
+
+ PUT_SEL32V(_segMan, obj, handle, 0);
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ PUT_SEL32V(_segMan, obj, state, kSoundStopped);
+ else
+ PUT_SEL32V(_segMan, obj, signal, SIGNAL_OFFSET);
+
+ musicSlot->dataInc = 0;
+ musicSlot->signal = 0;
+ _music->soundStop(musicSlot);
+#endif
+}
+
+void SoundCommandParser::cmdPauseSound(reg_t obj, int16 value) {
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ if (!obj.segment)
+ return;
+
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ changeSoundStatus(obj, SOUND_STATUS_SUSPENDED);
+ else
+ changeSoundStatus(obj, value ? SOUND_STATUS_SUSPENDED : SOUND_STATUS_PLAYING);
+#else
+
+ MusicEntry *musicSlot = NULL;
+ MusicList::iterator slotLoop = NULL;
+ MusicList::iterator listEnd = NULL;
+
+ if (!obj.segment) {
+ // Pausing/Resuming the whole playlist was introduced
+ // in the SCI1 late sound scheme
+ if (_soundVersion <= SCI_VERSION_1_EARLY)
+ return;
+ _music->_mutex.lock();
+ slotLoop = _music->getPlayListStart();
+ listEnd = _music->getPlayListEnd();
+ musicSlot = *slotLoop;
+ _music->_mutex.unlock();
+ } else {
+ _music->_mutex.lock();
+ musicSlot = _music->getSlot(obj);
+ _music->_mutex.unlock();
+ if (!musicSlot) {
+ warning("cmdPauseSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return;
+ }
+ }
+
+ do {
+ if (_soundVersion <= SCI_VERSION_0_LATE) {
+ PUT_SEL32V(_segMan, musicSlot->soundObj, state, kSoundPaused);
+ _music->soundPause(musicSlot);
+ } else {
+ if (value)
+ _music->soundPause(musicSlot);
+ else
+ _music->soundResume(musicSlot);
+ }
+
+ if (slotLoop) {
+ if (slotLoop == listEnd) {
+ break;
+ } else {
+ _music->_mutex.lock();
+ musicSlot = *(slotLoop++);
+ _music->_mutex.unlock();
+ }
+ }
+ } while (slotLoop);
+
+#endif
+}
+
+void SoundCommandParser::cmdResumeSound(reg_t obj, int16 value) {
+ // SCI0 only command
+
+ if (!obj.segment)
+ return;
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ changeSoundStatus(obj, SOUND_STATUS_PLAYING);
+#else
+ MusicEntry *musicSlot = _music->getSlot(obj);
+ if (!musicSlot) {
+ warning("cmdResumeSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return;
+ }
+
+ PUT_SEL32V(_segMan, musicSlot->soundObj, state, kSoundPlaying);
+ _music->soundResume(musicSlot);
+#endif
+}
+
+void SoundCommandParser::cmdMuteSound(reg_t obj, int16 value) {
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+ if (_argc > 1) // the first parameter is the sound command
+ _music->soundSetSoundOn(obj.toUint16());
+ _acc = make_reg(0, _music->soundGetSoundOn());
+#endif
+}
+
+void SoundCommandParser::cmdMasterVolume(reg_t obj, int16 value) {
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ if (obj != SIGNAL_REG)
+ _state->sfx_setVolume(obj.toSint16());
+
+ _acc = make_reg(0, _state->sfx_getVolume());
+#else
+ debugC(2, kDebugLevelSound, "cmdMasterVolume: %d", value);
+ if (_argc > 1) // the first parameter is the sound command
+ _music->soundSetMasterVolume(obj.toSint16());
+ _acc = make_reg(0, _music->soundGetMasterVolume());
+#endif
+}
+
+void SoundCommandParser::cmdFadeSound(reg_t obj, int16 value) {
+ if (!obj.segment)
+ return;
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ SongHandle handle = FROBNICATE_HANDLE(obj);
+ if (_soundVersion != SCI_VERSION_1_LATE) {
+ /*s->sound_server->command(s, SOUND_COMMAND_FADE_HANDLE, obj, 120);*/ /* Fade out in 2 secs */
+ /* FIXME: The next couple of lines actually STOP the handle, rather
+ ** than fading it! */
+ _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ PUT_SEL32V(_segMan, obj, state, SOUND_STATUS_STOPPED);
+ PUT_SEL32V(_segMan, obj, signal, SIGNAL_OFFSET);
+ } else {
+ fade_params_t fade;
+ fade.final_volume = _argv[2].toUint16();
+ fade.ticks_per_step = _argv[3].toUint16();
+ fade.step_size = _argv[4].toUint16();
+ fade.action = _argv[5].toUint16() ?
+ FADE_ACTION_FADE_AND_STOP :
+ FADE_ACTION_FADE_AND_CONT;
+
+ _state->sfx_song_set_fade(handle, &fade);
+
+ /* FIXME: The next couple of lines actually STOP the handle, rather
+ ** than fading it! */
+ if (_argv[5].toUint16()) {
+ PUT_SEL32V(_segMan, obj, signal, SIGNAL_OFFSET);
+ _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
+ } else {
+ // FIXME: Support fade-and-continue. For now, send signal right away.
+ PUT_SEL32V(_segMan, obj, signal, SIGNAL_OFFSET);
+ }
+ }
+#else
+ MusicEntry *musicSlot = _music->getSlot(obj);
+ if (!musicSlot) {
+ warning("cmdFadeSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return;
+ }
+
+ int volume = musicSlot->volume;
+
+ switch (_argc) {
+ case 2: // SCI0
+ // SCI0 fades out all the time and when fadeout is done it will also stop the music from playing
+ musicSlot->fadeTo = 0;
+ musicSlot->fadeStep = -5;
+ musicSlot->fadeTickerStep = 10 * 16667 / _music->soundGetTempo();
+ musicSlot->fadeTicker = 0;
+ break;
+
+ case 5: // Possibly SCI1?!
+ case 6: // SCI 1.1 TODO: find out what additional parameter is
+ musicSlot->fadeTo = CLIP<uint16>(_argv[2].toUint16(), 0, MUSIC_VOLUME_MAX);
+ musicSlot->fadeStep = volume > _argv[2].toUint16() ? -_argv[4].toUint16() : _argv[4].toUint16();
+ musicSlot->fadeTickerStep = _argv[3].toUint16() * 16667 / _music->soundGetTempo();
+ musicSlot->fadeTicker = 0;
+ break;
+
+ default:
+ error("cmdFadeSound: unsupported argc %d", _argc);
+ }
+
+ debugC(2, kDebugLevelSound, "cmdFadeSound: to %d, step %d, ticker %d", musicSlot->fadeTo, musicSlot->fadeStep, musicSlot->fadeTickerStep);
+#endif
+}
+
+void SoundCommandParser::cmdGetPolyphony(reg_t obj, int16 value) {
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ _acc = make_reg(0, _state->sfx_get_player_polyphony());
+#else
+ _acc = make_reg(0, _music->soundGetVoices()); // Get the number of voices
+#endif
+}
+
+void SoundCommandParser::cmdUpdateSound(reg_t obj, int16 value) {
+ if (!obj.segment)
+ return;
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ SongHandle handle = FROBNICATE_HANDLE(obj);
+ if (_soundVersion <= SCI_VERSION_0_LATE && obj.segment) {
+ _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, loop));
+ script_set_priority(_resMan, _segMan, _state, obj, GET_SEL32V(_segMan, obj, pri));
+ }
+#else
+ MusicEntry *musicSlot = _music->getSlot(obj);
+ if (!musicSlot) {
+ warning("cmdUpdateSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return;
+ }
+
+ musicSlot->loop = GET_SEL32V(_segMan, obj, loop);
+ int16 objVol = CLIP<int>(GET_SEL32V(_segMan, obj, vol), 0, 255);
+ if (objVol != musicSlot->volume)
+ _music->soundSetVolume(musicSlot, objVol);
+ uint32 objPrio = GET_SEL32V(_segMan, obj, pri);
+ if (objPrio != musicSlot->prio)
+ _music->soundSetPriority(musicSlot, objPrio);
+
+#endif
+}
+
+void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
+ if (!obj.segment)
+ return;
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ int signal = 0;
+ int min = 0;
+ int sec = 0;
+ int frame = 0;
+ int result = SI_LOOP; // small hack
+ SongHandle handle = FROBNICATE_HANDLE(obj);
+
+ while (result == SI_LOOP)
+ result = _state->sfx_poll_specific(handle, &signal);
+
+ switch (result) {
+ case SI_ABSOLUTE_CUE:
+ debugC(2, kDebugLevelSound, "--- [CUE] %04x:%04x Absolute Cue: %d\n",
+ PRINT_REG(obj), signal);
+ debugC(2, kDebugLevelSound, "abs-signal %04X\n", signal);
+ PUT_SEL32V(_segMan, obj, signal, signal);
+ break;
+
+ case SI_RELATIVE_CUE:
+ debugC(2, kDebugLevelSound, "--- [CUE] %04x:%04x Relative Cue: %d\n",
+ PRINT_REG(obj), signal);
+
+ /* FIXME to match commented-out semantics
+ * below, with proper storage of dataInc and
+ * signal in the iterator code. */
+ PUT_SEL32V(_segMan, obj, dataInc, signal);
+ debugC(2, kDebugLevelSound, "rel-signal %04X\n", signal);
+ if (_soundVersion == SCI_VERSION_1_EARLY)
+ PUT_SEL32V(_segMan, obj, signal, signal);
+ else
+ PUT_SEL32V(_segMan, obj, signal, signal + 127);
+ break;
+
+ case SI_FINISHED:
+ debugC(2, kDebugLevelSound, "--- [FINISHED] %04x:%04x\n", PRINT_REG(obj));
+ PUT_SEL32V(_segMan, obj, signal, SIGNAL_OFFSET);
+ break;
+
+ case SI_LOOP:
+ break; // Doesn't happen
+ }
+
+ //switch (signal) {
+ //case 0x00:
+ // if (dataInc!=GET_SEL32V(segMan, obj, dataInc)) {
+ // PUT_SEL32V(segMan, obj, dataInc, dataInc);
+ // PUT_SEL32V(segMan, obj, signal, dataInc+0x7f);
+ // } else {
+ // PUT_SEL32V(segMan, obj, signal, signal);
+ // }
+ // break;
+ //case 0xFF: // May be unnecessary
+ // s->_sound.sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
+ // break;
+ //default :
+ // if (dataInc!=GET_SEL32V(segMan, obj, dataInc)) {
+ // PUT_SEL32V(segMan, obj, dataInc, dataInc);
+ // PUT_SEL32V(segMan, obj, signal, dataInc + 0x7f);
+ // } else {
+ // PUT_SEL32V(segMan, obj, signal, signal);
+ // }
+ // break;
+ //}
+
+ if (_soundVersion == SCI_VERSION_1_EARLY) {
+ PUT_SEL32V(_segMan, obj, min, min);
+ PUT_SEL32V(_segMan, obj, sec, sec);
+ PUT_SEL32V(_segMan, obj, frame, frame);
+ }
+#else
+ MusicEntry *musicSlot = _music->getSlot(obj);
+ if (!musicSlot) {
+ warning("cmdUpdateCues: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return;
+ }
+
+ if (musicSlot->pStreamAud) {
+ // Update digital sound effect slots here
+ Audio::Mixer *mixer = g_system->getMixer();
+
+ uint currentLoopCounter = musicSlot->pStreamAud->getNumPlayedLoops();
+ if (currentLoopCounter != musicSlot->sampleLoopCounter) {
+ // during last time we looped at least one time, update loop accordingly
+ musicSlot->loop -= currentLoopCounter - musicSlot->sampleLoopCounter;
+ musicSlot->sampleLoopCounter = currentLoopCounter;
+ }
+ if (!mixer->isSoundHandleActive(musicSlot->hCurrentAud)) {
+ cmdStopSound(obj, 0);
+ } else {
+ musicSlot->ticker = (uint16)(mixer->getSoundElapsedTime(musicSlot->hCurrentAud) * 0.06);
+ }
+ // We get a flag from MusicEntry::doFade() here to set volume for the stream
+ if (musicSlot->fadeSetVolume) {
+ mixer->setChannelVolume(musicSlot->hCurrentAud, musicSlot->volume);
+ musicSlot->fadeSetVolume = false;
+ }
+ } else {
+ switch (musicSlot->signal) {
+ case 0:
+ if (musicSlot->dataInc != GET_SEL32V(_segMan, obj, dataInc)) {
+ PUT_SEL32V(_segMan, obj, dataInc, musicSlot->dataInc);
+ PUT_SEL32V(_segMan, obj, signal, musicSlot->dataInc + 127);
+ }
+ break;
+ case SIGNAL_OFFSET:
+ PUT_SEL32V(_segMan, obj, signal, SIGNAL_OFFSET);
+ break;
+ default:
+ // Sync the signal of the sound object
+ PUT_SEL32V(_segMan, obj, signal, musicSlot->signal);
+ break;
+ }
+ }
+
+ if (musicSlot->fadeCompleted) {
+ musicSlot->fadeCompleted = false;
+ if (_soundVersion <= SCI_VERSION_0_LATE) {
+ cmdStopSound(obj, 0);
+ } else {
+ PUT_SEL32V(_segMan, obj, signal, SIGNAL_OFFSET);
+ }
+ }
+
+ // Sync loop selector for SCI0
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ PUT_SEL32V(_segMan, obj, loop, musicSlot->loop);
+
+ musicSlot->signal = 0;
+
+ if (_soundVersion >= SCI_VERSION_1_EARLY) {
+ PUT_SEL32V(_segMan, obj, min, musicSlot->ticker / 3600);
+ PUT_SEL32V(_segMan, obj, sec, musicSlot->ticker % 3600 / 60);
+ PUT_SEL32V(_segMan, obj, frame, musicSlot->ticker);
+ }
+
+#endif
+}
+
+void SoundCommandParser::cmdSendMidi(reg_t obj, int16 value) {
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ //SongHandle handle = FROBNICATE_HANDLE(obj);
+ //_state->sfx_send_midi(handle, value, _midiCmd, _controller, _param);
+#else
+ _music->sendMidiCommand(_midiCommand);
+#endif
+}
+
+void SoundCommandParser::cmdReverb(reg_t obj, int16 value) {
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+ _music->setReverb(obj.toUint16() & 0xF);
+#endif
+}
+
+void SoundCommandParser::cmdSetSoundHold(reg_t obj, int16 value) {
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ SongHandle handle = FROBNICATE_HANDLE(obj);
+ _state->sfx_song_set_hold(handle, value);
+#else
+ MusicEntry *musicSlot = _music->getSlot(obj);
+ if (!musicSlot) {
+ warning("cmdSetSoundHold: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return;
+ }
+
+ // Set the special hold marker ID where the song should be looped at.
+ musicSlot->hold = value;
+#endif
+}
+
+void SoundCommandParser::cmdGetAudioCapability(reg_t obj, int16 value) {
+ // Tests for digital audio support
+ _acc = make_reg(0, 1);
+}
+
+void SoundCommandParser::cmdStopAllSounds(reg_t obj, int16 value) {
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+ Common::StackLock(_music->_mutex);
+
+ const MusicList::iterator end = _music->getPlayListEnd();
+ for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ PUT_SEL32V(_segMan, (*i)->soundObj, state, kSoundStopped);
+ else
+ PUT_SEL32V(_segMan, (*i)->soundObj, signal, SIGNAL_OFFSET);
+
+ (*i)->dataInc = 0;
+ _music->soundStop(*i);
+ }
+#endif
+}
+
+void SoundCommandParser::cmdSetSoundVolume(reg_t obj, int16 value) {
+ if (!obj.segment)
+ return;
+
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+ MusicEntry *musicSlot = _music->getSlot(obj);
+ if (!musicSlot) {
+ // Do not throw a warning if the sound can't be found, as in some games
+ // this is called before the actual sound is loaded (e.g. SQ4CD, with the
+ // drum sounds of the energizer bunny at the beginning), so this is normal
+ // behavior
+ //warning("cmdSetSoundVolume: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return;
+ }
+
+ debugC(2, kDebugLevelSound, "cmdSetSoundVolume: %d", value);
+
+ value = CLIP<int>(value, 0, MUSIC_VOLUME_MAX);
+
+ if (musicSlot->volume != value) {
+ musicSlot->volume = value;
+ _music->soundSetVolume(musicSlot, value);
+ PUT_SEL32V(_segMan, obj, vol, value);
+ }
+#endif
+}
+
+void SoundCommandParser::cmdSetSoundPriority(reg_t obj, int16 value) {
+ if (!obj.segment)
+ return;
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ script_set_priority(_resMan, _segMan, _state, obj, value);
+#else
+ MusicEntry *musicSlot = _music->getSlot(obj);
+ if (!musicSlot) {
+ warning("cmdSetSoundPriority: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return;
+ }
+
+ if (value == -1) {
+ // Set priority from the song data
+ Resource *song = _resMan->findResource(ResourceId(kResourceTypeSound, musicSlot->resnum), 0);
+ if (song->data[0] == 0xf0)
+ _music->soundSetPriority(musicSlot, song->data[1]);
+ else
+ warning("cmdSetSoundPriority: Attempt to unset song priority when there is no built-in value");
+
+ //pSnd->prio=0;field_15B=0
+ PUT_SEL32V(_segMan, obj, flags, GET_SEL32V(_segMan, obj, flags) & 0xFD);
+ } else {
+ // Scripted priority
+
+ //pSnd->field_15B=1;
+ PUT_SEL32V(_segMan, obj, flags, GET_SEL32V(_segMan, obj, flags) | 2);
+ //DoSOund(0xF,hobj,w)
+ }
+#endif
+}
+
+void SoundCommandParser::cmdSetSoundLoop(reg_t obj, int16 value) {
+ if (!obj.segment)
+ return;
+
+#ifdef USE_OLD_MUSIC_FUNCTIONS
+ if (!GET_SEL32(_segMan, obj, nodePtr).isNull()) {
+ SongHandle handle = FROBNICATE_HANDLE(obj);
+ _state->sfx_song_set_loops(handle, value);
+ }
+#else
+ MusicEntry *musicSlot = _music->getSlot(obj);
+ if (!musicSlot) {
+ // Apparently, it's perfectly normal for a game to call cmdSetSoundLoop
+ // before actually initializing the sound and adding it to the playlist
+ // with cmdInitSound. Usually, it doesn't matter if the game doesn't
+ // request to loop the sound, so in this case, don't throw any warning,
+ // otherwise do, because the sound won't be looped
+ if (value == -1) {
+ warning("cmdSetSoundLoop: Slot not found (%04x:%04x) and the song was requested to be looped", PRINT_REG(obj));
+ } else {
+ // Doesn't really matter
+ }
+ return;
+ }
+ if (value == -1) {
+ musicSlot->loop = 0xFFFF;
+ } else {
+ musicSlot->loop = 1; // actually plays the music once
+ }
+
+ PUT_SEL32V(_segMan, obj, loop, musicSlot->loop);
+#endif
+}
+
+void SoundCommandParser::cmdSuspendSound(reg_t obj, int16 value) {
+ // TODO
+ warning("STUB: cmdSuspendSound");
+}
+
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+
+void SoundCommandParser::updateSci0Cues() {
+ Common::StackLock(_music->_mutex);
+
+ const MusicList::iterator end = _music->getPlayListEnd();
+ for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
+ // Is the sound stopped, and the sound object updated too? If yes, skip
+ // this sound, as SCI0 only allows one active song
+ if ((*i)->signal == 0 && (*i)->status != kSoundPlaying)
+ continue;
+
+ cmdUpdateCues((*i)->soundObj, 0);
+ }
+}
+
+#endif
+
+void SoundCommandParser::clearPlayList() {
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+ _music->clearPlayList();
+#endif
+}
+
+void SoundCommandParser::syncPlayList(Common::Serializer &s) {
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+ _music->saveLoadWithSerializer(s);
+#endif
+}
+
+void SoundCommandParser::reconstructPlayList(int savegame_version) {
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+ Common::StackLock lock(_music->_mutex);
+
+ _music->resetDriver();
+
+ const MusicList::iterator end = _music->getPlayListEnd();
+ for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
+ if (savegame_version < 14) {
+ (*i)->dataInc = GET_SEL32V(_segMan, (*i)->soundObj, dataInc);
+ (*i)->signal = GET_SEL32V(_segMan, (*i)->soundObj, signal);
+
+ if (_soundVersion >= SCI_VERSION_1_LATE)
+ (*i)->volume = GET_SEL32V(_segMan, (*i)->soundObj, vol);
+ }
+
+ if ((*i)->resnum && _resMan->testResource(ResourceId(kResourceTypeSound, (*i)->resnum))) {
+ (*i)->soundRes = new SoundResource((*i)->resnum, _resMan, _soundVersion);
+ _music->soundInitSnd(*i);
+ } else {
+ (*i)->soundRes = 0;
+ }
+ if ((*i)->status == kSoundPlaying)
+ cmdPlaySound((*i)->soundObj, 0);
+ }
+
+#endif
+}
+
+void SoundCommandParser::printPlayList(Console *con) {
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+ _music->printPlayList(con);
+#endif
+}
+
+void SoundCommandParser::resetDriver() {
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+ _music->resetDriver();
+#endif
+}
+
+} // End of namespace Sci