diff options
author | Alejandro Marzini | 2010-07-13 04:31:15 +0000 |
---|---|---|
committer | Alejandro Marzini | 2010-07-13 04:31:15 +0000 |
commit | 609e08d5dbae3179eddf981abe73d69009432de4 (patch) | |
tree | cddbd0a0e69eaa53b85f98f96dc410a307773f08 /engines/sci/sound | |
parent | 8b6a670391f1b5103e3761d78eef8f41d64cf8cd (diff) | |
parent | 03c0faa5d76f547603ee6389cdf958e2a6f0f43d (diff) | |
download | scummvm-rg350-609e08d5dbae3179eddf981abe73d69009432de4.tar.gz scummvm-rg350-609e08d5dbae3179eddf981abe73d69009432de4.tar.bz2 scummvm-rg350-609e08d5dbae3179eddf981abe73d69009432de4.zip |
Merged from trunk, from Rev 49499 to HEAD
svn-id: r50840
Diffstat (limited to 'engines/sci/sound')
-rw-r--r-- | engines/sci/sound/audio.cpp | 83 | ||||
-rw-r--r-- | engines/sci/sound/audio.h | 5 | ||||
-rw-r--r-- | engines/sci/sound/drivers/adlib.cpp | 14 | ||||
-rw-r--r-- | engines/sci/sound/drivers/fb01.cpp | 4 | ||||
-rw-r--r-- | engines/sci/sound/drivers/midi.cpp | 25 | ||||
-rw-r--r-- | engines/sci/sound/drivers/mididriver.h | 1 | ||||
-rw-r--r-- | engines/sci/sound/iterator/core.cpp | 1013 | ||||
-rw-r--r-- | engines/sci/sound/iterator/core.h | 209 | ||||
-rw-r--r-- | engines/sci/sound/iterator/iterator.cpp | 1686 | ||||
-rw-r--r-- | engines/sci/sound/iterator/iterator.h | 326 | ||||
-rw-r--r-- | engines/sci/sound/iterator/iterator_internal.h | 276 | ||||
-rw-r--r-- | engines/sci/sound/iterator/songlib.cpp | 189 | ||||
-rw-r--r-- | engines/sci/sound/iterator/songlib.h | 171 | ||||
-rw-r--r-- | engines/sci/sound/iterator/test-iterator.cpp | 423 | ||||
-rw-r--r-- | engines/sci/sound/midiparser_sci.cpp | 659 | ||||
-rw-r--r-- | engines/sci/sound/midiparser_sci.h | 40 | ||||
-rw-r--r-- | engines/sci/sound/music.cpp | 266 | ||||
-rw-r--r-- | engines/sci/sound/music.h | 60 | ||||
-rw-r--r-- | engines/sci/sound/soundcmd.cpp | 919 | ||||
-rw-r--r-- | engines/sci/sound/soundcmd.h | 97 |
20 files changed, 960 insertions, 5506 deletions
diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp index 7ecce3b2a3..d9e9d2e8db 100644 --- a/engines/sci/sound/audio.cpp +++ b/engines/sci/sound/audio.cpp @@ -24,20 +24,21 @@ */ #include "sci/resource.h" -#include "sci/engine/selector.h" #include "sci/engine/kernel.h" +#include "sci/engine/selector.h" #include "sci/engine/seg_manager.h" #include "sci/sound/audio.h" -#include "common/system.h" #include "common/file.h" +#include "common/system.h" #include "sound/audiostream.h" -#include "sound/decoders/raw.h" -#include "sound/decoders/wave.h" +#include "sound/decoders/aiff.h" #include "sound/decoders/flac.h" #include "sound/decoders/mp3.h" +#include "sound/decoders/raw.h" #include "sound/decoders/vorbis.h" +#include "sound/decoders/wave.h" namespace Sci { @@ -45,6 +46,7 @@ AudioPlayer::AudioPlayer(ResourceManager *resMan) : _resMan(resMan), _audioRate( _syncResource(NULL), _syncOffset(0), _audioCdStart(0) { _mixer = g_system->getMixer(); + _wPlayFlag = false; } AudioPlayer::~AudioPlayer() { @@ -63,11 +65,30 @@ int AudioPlayer::startAudio(uint16 module, uint32 number) { Audio::AudioStream *audioStream = getAudioStream(number, module, &sampleLen); if (audioStream) { + _wPlayFlag = false; _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_audioHandle, audioStream); return sampleLen; + } else { + // Don't throw a warning in this case. getAudioStream() already has. Some games + // do miss audio entries (perhaps because of a typo, or because they were simply + // forgotten). + return 0; } +} - return 0; +int AudioPlayer::wPlayAudio(uint16 module, uint32 tuple) { + // Get the audio sample length and set the wPlay flag so we return 0 on + // position. SSCI pre-loads the audio here, but it's much easier for us to + // just get the sample length and return that. wPlayAudio should *not* + // actually start the sample. + + int sampleLen = 0; + Audio::AudioStream *audioStream = getAudioStream(tuple, module, &sampleLen); + if (!audioStream) + warning("wPlayAudio: unable to create stream for audio tuple %d, module %d", tuple, module); + delete audioStream; + _wPlayFlag = true; + return sampleLen; } void AudioPlayer::stopAudio() { @@ -85,6 +106,8 @@ void AudioPlayer::resumeAudio() { int AudioPlayer::getAudioPosition() { if (_mixer->isSoundHandleActive(_audioHandle)) return _mixer->getSoundElapsedTime(_audioHandle) * 6 / 100; // return elapsed time in ticks + else if (_wPlayFlag) + return 0; // Sound has "loaded" so return that it hasn't started else return -1; // Sound finished } @@ -160,17 +183,28 @@ static void deDPCM8(byte *soundBuf, Common::SeekableReadStream &audioStream, uin // Sierra SOL audio file reader // Check here for more info: http://wiki.multimedia.cx/index.php?title=Sierra_Audio -static bool readSOLHeader(Common::SeekableReadStream *audioStream, int headerSize, uint32 &size, uint16 &audioRate, byte &audioFlags) { - if (headerSize != 11 && headerSize != 12) { +static bool readSOLHeader(Common::SeekableReadStream *audioStream, int headerSize, uint32 &size, uint16 &audioRate, byte &audioFlags, uint32 resSize) { + if (headerSize != 7 && headerSize != 11 && headerSize != 12) { warning("SOL audio header of size %i not supported", headerSize); return false; } - audioStream->readUint32LE(); // skip "SOL" + 0 (4 bytes) + uint32 tag = audioStream->readUint32BE(); + + if (tag != MKID_BE('SOL\0')) { + warning("No 'SOL' FourCC found"); + return false; + } + audioRate = audioStream->readUint16LE(); audioFlags = audioStream->readByte(); - size = audioStream->readUint32LE(); + // For the QFG3 demo format, just use the resource size + // Otherwise, load it from the header + if (headerSize == 7) + size = resSize; + else + size = audioStream->readUint32LE(); return true; } @@ -238,9 +272,11 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32 // Compressed audio made by our tool byte *compressedData = (byte *)malloc(audioRes->size); assert(compressedData); - // We copy over the compressed data in our own buffer. If we don't do this resourcemanager may free the data - // later. All other compression-types already decompress completely into an additional buffer here. - // MP3/OGG/FLAC decompression works on-the-fly instead. + // We copy over the compressed data in our own buffer. We have to do + // this, because ResourceManager may free the original data late. All + // other compression types already decompress completely into an + // additional buffer here. MP3/OGG/FLAC decompression works on-the-fly + // instead. memcpy(compressedData, audioRes->data, audioRes->size); Common::MemoryReadStream *compressedStream = new Common::MemoryReadStream(compressedData, audioRes->size, DisposeAfterUse::YES); @@ -270,7 +306,7 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32 // SCI1.1 Common::MemoryReadStream headerStream(audioRes->_header, audioRes->_headerSize, DisposeAfterUse::NO); - if (readSOLHeader(&headerStream, audioRes->_headerSize, size, _audioRate, audioFlags)) { + if (readSOLHeader(&headerStream, audioRes->_headerSize, size, _audioRate, audioFlags, audioRes->size)) { Common::MemoryReadStream dataStream(audioRes->data, audioRes->size, DisposeAfterUse::NO); data = readSOLAudio(&dataStream, size, audioFlags, flags); } @@ -286,6 +322,18 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32 waveStream->seek(0, SEEK_SET); audioStream = Audio::makeWAVStream(waveStream, DisposeAfterUse::YES); + } else if (audioRes->size > 4 && READ_BE_UINT32(audioRes->data) == MKID_BE('FORM')) { + // AIFF detected + Common::MemoryReadStream *waveStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO); + + // Calculate samplelen from AIFF header + int waveSize = 0, waveRate = 0; + byte waveFlags = 0; + Audio::loadAIFFFromStream(*waveStream, waveSize, waveRate, waveFlags); + *sampleLen = (waveFlags & Audio::FLAG_16BITS ? waveSize >> 1 : waveSize) * 60 / waveRate; + + waveStream->seek(0, SEEK_SET); + audioStream = Audio::makeAIFFStream(waveStream, DisposeAfterUse::YES); } else if (audioRes->size > 14 && READ_BE_UINT16(audioRes->data) == 1 && READ_BE_UINT16(audioRes->data + 2) == 1 && READ_BE_UINT16(audioRes->data + 4) == 5 && READ_BE_UINT32(audioRes->data + 10) == 0x00018051) { // Mac snd detected @@ -317,13 +365,12 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32 } if (audioSeekStream) { - *sampleLen = (audioSeekStream->getLength().msecs() * 10000) / 166666; // we translate msecs to ticks - // Original code - //*sampleLen = (flags & Audio::FLAG_16BITS ? size >> 1 : size) * 60 / _audioRate; + *sampleLen = (audioSeekStream->getLength().msecs() * 60) / 1000; // we translate msecs to ticks audioStream = audioSeekStream; } - // We have to make sure that we don't depend on resource manager pointers after this point, because the actual - // audio resource may get unloaded by resource manager at any time + // We have to make sure that we don't depend on resource manager pointers + // after this point, because the actual audio resource may get unloaded by + // resource manager at any time. if (audioStream) return audioStream; diff --git a/engines/sci/sound/audio.h b/engines/sci/sound/audio.h index 9fc3cbac51..7c1221fc4c 100644 --- a/engines/sci/sound/audio.h +++ b/engines/sci/sound/audio.h @@ -33,8 +33,7 @@ namespace Sci { enum AudioCommands { - // TODO: find the difference between kSci1AudioWPlay and kSci1AudioPlay - kSciAudioWPlay = 1, /* Plays an audio stream */ + kSciAudioWPlay = 1, /* Loads an audio stream */ kSciAudioPlay = 2, /* Plays an audio stream */ kSciAudioStop = 3, /* Stops an audio stream */ kSciAudioPause = 4, /* Pauses an audio stream */ @@ -69,6 +68,7 @@ public: Audio::RewindableAudioStream *getAudioStream(uint32 number, uint32 volume, int *sampleLen); int getAudioPosition(); int startAudio(uint16 module, uint32 tuple); + int wPlayAudio(uint16 module, uint32 tuple); void stopAudio(); void pauseAudio(); void resumeAudio(); @@ -92,6 +92,7 @@ private: Resource *_syncResource; /**< Used by kDoSync for speech syncing in CD talkie games */ uint _syncOffset; uint32 _audioCdStart; + bool _wPlayFlag; }; } // End of namespace Sci diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp index a743e4b5d9..55c3640c9d 100644 --- a/engines/sci/sound/drivers/adlib.cpp +++ b/engines/sci/sound/drivers/adlib.cpp @@ -704,6 +704,7 @@ void MidiDriver_AdLib::setVelocityReg(int regOffset, int velocity, int kbScaleLe void MidiDriver_AdLib::setPatch(int voice, int patch) { if ((patch < 0) || ((uint)patch >= _patches.size())) { warning("ADLIB: Invalid patch %i requested", patch); + // Substitute instrument 0 patch = 0; } @@ -749,17 +750,24 @@ void MidiDriver_AdLib::playSwitch(bool play) { bool MidiDriver_AdLib::loadResource(const byte *data, uint size) { if ((size != 1344) && (size != 2690) && (size != 5382)) { - warning("ADLIB: Unsupported patch format (%i bytes)", size); + error("ADLIB: Unsupported patch format (%i bytes)", size); return false; } for (int i = 0; i < 48; i++) loadInstrument(data + (28 * i)); - if (size == 2690) { + if (size == 1344) { + byte dummy[28] = {0}; + + // Only 48 instruments, add dummies + for (int i = 0; i < 48; i++) + loadInstrument(dummy); + } else if (size == 2690) { for (int i = 48; i < 96; i++) loadInstrument(data + 2 + (28 * i)); - } else if (size == 5382) { + } else { + // SCI1.1 and later for (int i = 48; i < 190; i++) loadInstrument(data + (28 * i)); _rhythmKeyMap = new byte[kRhythmKeys]; diff --git a/engines/sci/sound/drivers/fb01.cpp b/engines/sci/sound/drivers/fb01.cpp index 7e9fbd51a1..ab9b2e3df5 100644 --- a/engines/sci/sound/drivers/fb01.cpp +++ b/engines/sci/sound/drivers/fb01.cpp @@ -128,8 +128,8 @@ private: }; MidiPlayer_Fb01::MidiPlayer_Fb01(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _timerParam(NULL), _timerProc(NULL) { - MidiDriverType midiType = MidiDriver::detectMusicDriver(MDT_MIDI); - _driver = createMidi(midiType); + MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI); + _driver = createMidi(dev); _sysExBuf[0] = 0x43; _sysExBuf[1] = 0x75; diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp index 2432a8fab0..1ef0781906 100644 --- a/engines/sci/sound/drivers/midi.cpp +++ b/engines/sci/sound/drivers/midi.cpp @@ -55,6 +55,7 @@ public: bool hasRhythmChannel() const { return true; } byte getPlayId(); int getPolyphony() const { return kVoices; } + int getFirstChannel(); void setVolume(byte volume); int getVolume(); void setReverb(byte reverb); @@ -119,10 +120,10 @@ private: }; MidiPlayer_Midi::MidiPlayer_Midi(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _isMt32(false), _hasReverb(false), _isOldPatchFormat(true) { - MidiDriverType midiType = MidiDriver::detectMusicDriver(MDT_MIDI); - _driver = createMidi(midiType); + MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI); + _driver = createMidi(dev); - if (midiType == MD_MT32 || ConfMan.getBool("native_mt32")) + if (MidiDriver::getMusicType(dev) == MT_MT32 || ConfMan.getBool("native_mt32")) _isMt32 = true; _sysExBuf[0] = 0x41; @@ -271,6 +272,17 @@ void MidiPlayer_Midi::setPatch(int channel, int patch) { _driver->setPitchBendRange(channel, bendRange); _driver->send(0xc0 | channel, _patchMap[patch], 0); + + // Send a pointless command to work around a firmware bug in common + // USB-MIDI cables. If the first MIDI command in a USB packet is a + // Cx or Dx command, the second command in the packet is dropped + // somewhere. + // FIXME: consider putting a workaround in the MIDI backend drivers + // instead. + // Known to be affected: alsa, coremidi + // Known *not* to be affected: windows (only seems to send one MIDI + // command per USB packet even if the device allows larger packets). + _driver->send(0xb0 | channel, 0x0a, _channels[channel].pan); } void MidiPlayer_Midi::send(uint32 b) { @@ -306,6 +318,13 @@ void MidiPlayer_Midi::send(uint32 b) { } } +// We return 1 for mt32, because if we remap channels to 0 for mt32, those won't get played at all +int MidiPlayer_Midi::getFirstChannel() { + if (_isMt32) + return 1; + return 0; +} + void MidiPlayer_Midi::setVolume(byte volume) { _masterVolume = volume; diff --git a/engines/sci/sound/drivers/mididriver.h b/engines/sci/sound/drivers/mididriver.h index 12d3e57f5d..58803db260 100644 --- a/engines/sci/sound/drivers/mididriver.h +++ b/engines/sci/sound/drivers/mididriver.h @@ -86,6 +86,7 @@ public: virtual byte getPlayId() = 0; virtual int getPolyphony() const = 0; + virtual int getFirstChannel() { return 0; }; virtual void setVolume(byte volume) { if(_driver) diff --git a/engines/sci/sound/iterator/core.cpp b/engines/sci/sound/iterator/core.cpp deleted file mode 100644 index 7cd730b3e2..0000000000 --- a/engines/sci/sound/iterator/core.cpp +++ /dev/null @@ -1,1013 +0,0 @@ -/* 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$ - * - */ - -/* Sound subsystem core: Event handler, sound player dispatching */ - -#include "sci/sci.h" -#ifdef USE_OLD_MUSIC_FUNCTIONS - -#include "sci/sound/iterator/core.h" -#include "sci/sound/iterator/iterator.h" -#include "sci/sound/drivers/mididriver.h" - -#include "common/system.h" -#include "common/timer.h" - -#include "sound/mixer.h" - -namespace Sci { - -/* Plays a song iterator that found a PCM through a PCM device, if possible -** Parameters: (SongIterator *) it: The iterator to play -** (SongHandle) handle: Debug handle -** Returns : (int) 0 if the effect will not be played, nonzero if it will -** This assumes that the last call to 'it->next()' returned SI_PCM. -*/ -static int sfx_play_iterator_pcm(SongIterator *it, SongHandle handle); - - -#pragma mark - - - -class SfxPlayer { -public: - /** Number of voices that can play simultaneously */ - int _polyphony; - -protected: - SciVersion _soundVersion; - MidiPlayer *_mididrv; - - SongIterator *_iterator; - Audio::Timestamp _wakeupTime; - Audio::Timestamp _currentTime; - uint32 _pauseTimeDiff; - - bool _paused; - bool _iteratorIsDone; - uint32 _tempo; - - Common::Mutex _mutex; - int _volume; - - void play_song(SongIterator *it); - static void player_timer_callback(void *refCon); - -public: - SfxPlayer(SciVersion soundVersion); - ~SfxPlayer(); - - /** - * Initializes the player. - * @param resMan a resource manager for driver initialization - * @param expected_latency expected delay in between calls to 'maintenance' (in microseconds) - * @return Common::kNoError on success, Common::kUnknownError on failure - */ - Common::Error init(ResourceManager *resMan, int expected_latency); - - /** - * Adds an iterator to the song player - * @param it The iterator to play - * @param start_time The time to assume as the time the first MIDI command executes at - * @return Common::kNoError on success, Common::kUnknownError on failure - * - * The iterator should not be cloned (to avoid memory leaks) and - * may be modified according to the needs of the player. - * Implementors may use the 'sfx_iterator_combine()' function - * to add iterators onto their already existing iterators. - */ - Common::Error add_iterator(SongIterator *it, uint32 start_time); - - /** - * Stops the currently playing song and deletes the associated iterator. - * @return Common::kNoError on success, Common::kUnknownError on failure - */ - Common::Error stop(); - - /** - * Transmits a song iterator message to the active song. - * @param msg the message to transmit - * @return Common::kNoError on success, Common::kUnknownError on failure - */ - Common::Error iterator_message(const SongIterator::Message &msg); - - /** - * Pauses song playing. - * @return Common::kNoError on success, Common::kUnknownError on failure - */ - Common::Error pause(); - - /** - * Resumes song playing after a pause. - * @return Common::kNoError on success, Common::kUnknownError on failure - */ - Common::Error resume(); - - /** - * Pass a raw MIDI event to the synth. - * @param argc length of buffer holding the midi event - * @param argv the buffer itself - */ - void tell_synth(int buf_nr, byte *buf); - - void setVolume(int vol); - - int getVolume(); -}; - -SfxPlayer::SfxPlayer(SciVersion soundVersion) - : _soundVersion(soundVersion), _wakeupTime(0, SFX_TICKS_PER_SEC), _currentTime(0, 1) { - _polyphony = 0; - - _mididrv = 0; - - _iterator = NULL; - _pauseTimeDiff = 0; - - _paused = false; - _iteratorIsDone = false; - _tempo = 0; - - _volume = 15; -} - -SfxPlayer::~SfxPlayer() { - if (_mididrv) { - _mididrv->close(); - delete _mididrv; - } - delete _iterator; - _iterator = NULL; -} - -void SfxPlayer::play_song(SongIterator *it) { - while (_iterator && _wakeupTime.msecsDiff(_currentTime) <= 0) { - int delay; - byte buf[8]; - int result; - - switch ((delay = songit_next(&(_iterator), - buf, &result, - IT_READER_MASK_ALL - | IT_READER_MAY_FREE - | IT_READER_MAY_CLEAN))) { - - case SI_FINISHED: - delete _iterator; - _iterator = NULL; - _iteratorIsDone = true; - return; - - case SI_IGNORE: - case SI_LOOP: - case SI_RELATIVE_CUE: - case SI_ABSOLUTE_CUE: - break; - - case SI_PCM: - sfx_play_iterator_pcm(_iterator, 0); - break; - - case 0: - static_cast<MidiDriver *>(_mididrv)->send(buf[0], buf[1], buf[2]); - - break; - - default: - _wakeupTime = _wakeupTime.addFrames(delay); - } - } -} - -void SfxPlayer::tell_synth(int buf_nr, byte *buf) { - byte op1 = (buf_nr < 2 ? 0 : buf[1]); - byte op2 = (buf_nr < 3 ? 0 : buf[2]); - - static_cast<MidiDriver *>(_mididrv)->send(buf[0], op1, op2); -} - -void SfxPlayer::player_timer_callback(void *refCon) { - SfxPlayer *thePlayer = (SfxPlayer *)refCon; - assert(refCon); - Common::StackLock lock(thePlayer->_mutex); - - if (thePlayer->_iterator && !thePlayer->_iteratorIsDone && !thePlayer->_paused) { - thePlayer->play_song(thePlayer->_iterator); - } - - thePlayer->_currentTime = thePlayer->_currentTime.addFrames(1); -} - -/* API implementation */ - -Common::Error SfxPlayer::init(ResourceManager *resMan, int expected_latency) { - MidiDriverType musicDriver = MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB); - - switch (musicDriver) { - case MD_ADLIB: - // FIXME: There's no Amiga sound option, so we hook it up to AdLib - if (g_sci->getPlatform() == Common::kPlatformAmiga) - _mididrv = MidiPlayer_Amiga_create(_soundVersion); - else - _mididrv = MidiPlayer_AdLib_create(_soundVersion); - break; - case MD_PCJR: - _mididrv = MidiPlayer_PCJr_create(_soundVersion); - break; - case MD_PCSPK: - _mididrv = MidiPlayer_PCSpeaker_create(_soundVersion); - break; - default: - break; - } - - assert(_mididrv); - - _polyphony = _mididrv->getPolyphony(); - - _tempo = _mididrv->getBaseTempo(); - uint32 time = g_system->getMillis(); - _currentTime = Audio::Timestamp(time, 1000000 / _tempo); - _wakeupTime = Audio::Timestamp(time, SFX_TICKS_PER_SEC); - - _mididrv->setTimerCallback(this, player_timer_callback); - _mididrv->open(resMan); - _mididrv->setVolume(_volume); - - return Common::kNoError; -} - -Common::Error SfxPlayer::add_iterator(SongIterator *it, uint32 start_time) { - Common::StackLock lock(_mutex); - SIMSG_SEND(it, SIMSG_SET_PLAYMASK(_mididrv->getPlayId())); - SIMSG_SEND(it, SIMSG_SET_RHYTHM(_mididrv->hasRhythmChannel())); - - if (_iterator == NULL) { - // Resync with clock - _currentTime = Audio::Timestamp(g_system->getMillis(), 1000000 / _tempo); - _wakeupTime = Audio::Timestamp(start_time, SFX_TICKS_PER_SEC); - } - - _iterator = sfx_iterator_combine(_iterator, it); - _iteratorIsDone = false; - - return Common::kNoError; -} - -Common::Error SfxPlayer::stop() { - debug(3, "Player: Stopping song iterator %p", (void *)_iterator); - Common::StackLock lock(_mutex); - delete _iterator; - _iterator = NULL; - for (int i = 0; i < MIDI_CHANNELS; i++) - static_cast<MidiDriver *>(_mididrv)->send(0xb0 + i, SCI_MIDI_CHANNEL_NOTES_OFF, 0); - - return Common::kNoError; -} - -Common::Error SfxPlayer::iterator_message(const SongIterator::Message &msg) { - Common::StackLock lock(_mutex); - if (!_iterator) { - return Common::kUnknownError; - } - - songit_handle_message(&_iterator, msg); - - return Common::kNoError; -} - -Common::Error SfxPlayer::pause() { - Common::StackLock lock(_mutex); - - _paused = true; - _pauseTimeDiff = _wakeupTime.msecsDiff(_currentTime); - - _mididrv->playSwitch(false); - - return Common::kNoError; -} - -Common::Error SfxPlayer::resume() { - Common::StackLock lock(_mutex); - - _wakeupTime = Audio::Timestamp(_currentTime.msecs() + _pauseTimeDiff, SFX_TICKS_PER_SEC); - _mididrv->playSwitch(true); - _paused = false; - - return Common::kNoError; -} - -void SfxPlayer::setVolume(int vol) { - _mididrv->setVolume(vol); -} - -int SfxPlayer::getVolume() { - return _mididrv->getVolume(); -} - -#pragma mark - - -void SfxState::sfx_reset_player() { - if (_player) - _player->stop(); -} - -void SfxState::sfx_player_tell_synth(int buf_nr, byte *buf) { - if (_player) - _player->tell_synth(buf_nr, buf); -} - -int SfxState::sfx_get_player_polyphony() { - if (_player) - return _player->_polyphony; - else - return 0; -} - -SfxState::SfxState() { - _player = NULL; - _it = NULL; - _flags = 0; - _song = NULL; - _suspended = 0; -} - -SfxState::~SfxState() { -} - - -void SfxState::freezeTime() { - /* Freezes the top song delay time */ - const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC); - Song *song = _song; - - while (song) { - song->_delay = song->_wakeupTime.frameDiff(ctime); - if (song->_delay < 0) - song->_delay = 0; - - song = song->_nextPlaying; - } -} - -void SfxState::thawTime() { - /* inverse of freezeTime() */ - const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC); - Song *song = _song; - - while (song) { - song->_wakeupTime = ctime.addFrames(song->_delay); - - song = song->_nextPlaying; - } -} - -#if 0 -// Unreferenced - removed -static void _dump_playing_list(SfxState *self, char *msg) { - Song *song = self->_song; - - fprintf(stderr, "[] Song list : [ "); - song = *(self->_songlib.lib); - while (song) { - fprintf(stderr, "%08lx:%d ", song->handle, song->_status); - song = song->_nextPlaying; - } - fprintf(stderr, "]\n"); - - fprintf(stderr, "[] Play list (%s) : [ " , msg); - - while (song) { - fprintf(stderr, "%08lx ", song->handle); - song = song->_nextPlaying; - } - - fprintf(stderr, "]\n"); -} -#endif - -#if 0 -static void _dump_songs(SfxState *self) { - Song *song = self->_song; - - fprintf(stderr, "Cue iterators:\n"); - song = *(self->_songlib.lib); - while (song) { - fprintf(stderr, " **\tHandle %08x (p%d): status %d\n", - song->handle, song->_priority, song->_status); - SIMSG_SEND(song->_it, SIMSG_PRINT(1)); - song = song->_next; - } - - if (self->_player) { - fprintf(stderr, "Audio iterator:\n"); - self->_player->iterator_message(SongIterator::Message(0, SIMSG_PRINT(1))); - } -} -#endif - -bool SfxState::isPlaying(Song *song) { - Song *playing_song = _song; - - /* _dump_playing_list(this, "is-playing");*/ - - while (playing_song) { - if (playing_song == song) - return true; - playing_song = playing_song->_nextPlaying; - } - return false; -} - -void SfxState::setSongStatus(Song *song, int status) { - const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC); - - switch (status) { - - case SOUND_STATUS_STOPPED: - // Reset - song->_it->init(); - break; - - case SOUND_STATUS_SUSPENDED: - case SOUND_STATUS_WAITING: - if (song->_status == SOUND_STATUS_PLAYING) { - // Update delay, set wakeup_time - song->_delay += song->_wakeupTime.frameDiff(ctime); - song->_wakeupTime = ctime; - } - if (status == SOUND_STATUS_SUSPENDED) - break; - - /* otherwise... */ - - case SOUND_STATUS_PLAYING: - if (song->_status == SOUND_STATUS_STOPPED) { - // Starting anew - song->_wakeupTime = ctime; - } - - if (isPlaying(song)) - status = SOUND_STATUS_PLAYING; - else - status = SOUND_STATUS_WAITING; - break; - - default: - fprintf(stderr, "%s L%d: Attempt to set invalid song" - " state %d!\n", __FILE__, __LINE__, status); - return; - - } - song->_status = status; -} - -/* Update internal state iff only one song may be played */ -void SfxState::updateSingleSong() { - Song *newsong = _songlib.findFirstActive(); - - if (newsong != _song) { - freezeTime(); /* Store song delay time */ - - if (_player) - _player->stop(); - - if (newsong) { - if (!newsong->_it) - return; /* Restore in progress and not ready for this yet */ - - /* Change song */ - if (newsong->_status == SOUND_STATUS_WAITING) - setSongStatus(newsong, SOUND_STATUS_PLAYING); - - /* Change instrument mappings */ - } else { - /* Turn off sound */ - } - if (_song) { - if (_song->_status == SOUND_STATUS_PLAYING) - setSongStatus(newsong, SOUND_STATUS_WAITING); - } - - Common::String debugMessage = "[SFX] Changing active song:"; - if (!_song) { - debugMessage += " New song:"; - } else { - char tmp[50]; - sprintf(tmp, " pausing %08lx, now playing ", _song->_handle); - debugMessage += tmp; - } - - if (newsong) { - char tmp[20]; - sprintf(tmp, "%08lx\n", newsong->_handle); - debugMessage += tmp; - } else { - debugMessage += " none\n"; - } - - debugC(2, kDebugLevelSound, "%s", debugMessage.c_str()); - - _song = newsong; - thawTime(); /* Recover song delay time */ - - if (newsong && _player) { - SongIterator *clonesong = newsong->_it->clone(newsong->_delay); - - _player->add_iterator(clonesong, newsong->_wakeupTime.msecs()); - } - } -} - - -void SfxState::updateMultiSong() { - Song *oldfirst = _song; - Song *oldseeker; - Song *newsong = _songlib.findFirstActive(); - Song *newseeker; - Song not_playing_anymore; /* Dummy object, referenced by - ** songs which are no longer - ** active. */ - - /* _dump_playing_list(this, "before");*/ - freezeTime(); /* Store song delay time */ - - // WORKAROUND: sometimes, newsong can be NULL (e.g. in SQ4). - // Handle this here, so that we avoid a crash - if (!newsong) { - // Iterators should get freed when there's only one song left playing - if(oldfirst && oldfirst->_status == SOUND_STATUS_STOPPED) { - debugC(2, kDebugLevelSound, "[SFX] Stopping song %lx", oldfirst->_handle); - if (_player && oldfirst->_it) - _player->iterator_message(SongIterator::Message(oldfirst->_it->ID, SIMSG_STOP)); - } - return; - } - - for (newseeker = newsong; newseeker; - newseeker = newseeker->_nextPlaying) { - if (!newseeker || !newseeker->_it) - return; /* Restore in progress and not ready for this yet */ - } - - /* First, put all old songs into the 'stopping' list and - ** mark their 'next-playing' as not_playing_anymore. */ - for (oldseeker = oldfirst; oldseeker; - oldseeker = oldseeker->_nextStopping) { - oldseeker->_nextStopping = oldseeker->_nextPlaying; - oldseeker->_nextPlaying = ¬_playing_anymore; - - if (oldseeker == oldseeker->_nextPlaying) { - error("updateMultiSong() failed. Breakpoint in %s, line %d", __FILE__, __LINE__); - } - } - - /* Second, re-generate the new song queue. */ - for (newseeker = newsong; newseeker; newseeker = newseeker->_nextPlaying) { - newseeker->_nextPlaying = _songlib.findNextActive(newseeker); - - if (newseeker == newseeker->_nextPlaying) { - error("updateMultiSong() failed. Breakpoint in %s, line %d", __FILE__, __LINE__); - } - } - /* We now need to update the currently playing song list, because we're - ** going to use some functions that require this list to be in a sane - ** state (particularly isPlaying(), indirectly */ - _song = newsong; - - /* Third, stop all old songs */ - for (oldseeker = oldfirst; oldseeker; - oldseeker = oldseeker->_nextStopping) - if (oldseeker->_nextPlaying == ¬_playing_anymore) { - setSongStatus(oldseeker, SOUND_STATUS_SUSPENDED); - debugC(2, kDebugLevelSound, "[SFX] Stopping song %lx", oldseeker->_handle); - - if (_player && oldseeker->_it) - _player->iterator_message(SongIterator::Message(oldseeker->_it->ID, SIMSG_STOP)); - oldseeker->_nextPlaying = NULL; /* Clear this pointer; we don't need the tag anymore */ - } - - for (newseeker = newsong; newseeker; newseeker = newseeker->_nextPlaying) { - if (newseeker->_status != SOUND_STATUS_PLAYING && _player) { - debugC(2, kDebugLevelSound, "[SFX] Adding song %lx", newseeker->_it->ID); - - SongIterator *clonesong = newseeker->_it->clone(newseeker->_delay); - _player->add_iterator(clonesong, g_system->getMillis()); - } - setSongStatus(newseeker, SOUND_STATUS_PLAYING); - } - - _song = newsong; - thawTime(); - /* _dump_playing_list(this, "after");*/ -} - -/* Update internal state */ -void SfxState::update() { - if (_flags & SFX_STATE_FLAG_MULTIPLAY) - updateMultiSong(); - else - updateSingleSong(); -} - -static int sfx_play_iterator_pcm(SongIterator *it, SongHandle handle) { -#ifdef DEBUG_SONG_API - fprintf(stderr, "[sfx-core] Playing PCM: %08lx\n", handle); -#endif - if (g_system->getMixer()->isReady()) { - Audio::AudioStream *newfeed = it->getAudioStream(); - if (newfeed) { - g_system->getMixer()->playStream(Audio::Mixer::kSFXSoundType, 0, newfeed); - return 1; - } - } - return 0; -} - -#define DELAY (1000000 / SFX_TICKS_PER_SEC) - -void SfxState::sfx_init(ResourceManager *resMan, int flags, SciVersion soundVersion) { - _songlib._lib = 0; - _song = NULL; - _flags = flags; - - _player = NULL; - - if (flags & SFX_STATE_FLAG_NOSOUND) { - warning("[SFX] Sound disabled"); - return; - } - -#ifdef DEBUG_SONG_API - fprintf(stderr, "[sfx-core] Initialising: flags=%x\n", flags); -#endif - - /*-------------------*/ - /* Initialise player */ - /*-------------------*/ - - if (!resMan) { - warning("[SFX] Warning: No resource manager present, cannot initialise player"); - return; - } - - _player = new SfxPlayer(soundVersion); - - if (!_player) { - warning("[SFX] No song player found"); - return; - } - - if (_player->init(resMan, DELAY / 1000)) { - warning("[SFX] Song player reported error, disabled"); - delete _player; - _player = NULL; - } - - _resMan = resMan; -} - -void SfxState::sfx_exit() { -#ifdef DEBUG_SONG_API - fprintf(stderr, "[sfx-core] Uninitialising\n"); -#endif - - delete _player; - _player = 0; - - g_system->getMixer()->stopAll(); - - _songlib.freeSounds(); -} - -void SfxState::sfx_suspend(bool suspend) { -#ifdef DEBUG_SONG_API - fprintf(stderr, "[sfx-core] Suspending? = %d\n", suspend); -#endif - if (suspend && (!_suspended)) { - /* suspend */ - - freezeTime(); - if (_player) - _player->pause(); - /* Suspend song player */ - - } else if (!suspend && (_suspended)) { - /* unsuspend */ - - thawTime(); - if (_player) - _player->resume(); - - /* Unsuspend song player */ - } - - _suspended = suspend; -} - -int SfxState::sfx_poll(SongHandle *handle, int *cue) { - if (!_song) - return 0; /* No milk today */ - - *handle = _song->_handle; - -#ifdef DEBUG_SONG_API - fprintf(stderr, "[sfx-core] Polling any (%08lx)\n", *handle); -#endif - return sfx_poll_specific(*handle, cue); -} - -int SfxState::sfx_poll_specific(SongHandle handle, int *cue) { - const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC); - Song *song = _song; - - while (song && song->_handle != handle) - song = song->_nextPlaying; - - if (!song) - return 0; /* Song not playing */ - - debugC(2, kDebugLevelSound, "[SFX:CUE] Polled song %08lx ", handle); - - while (1) { - if (song->_wakeupTime.frameDiff(ctime) > 0) - return 0; /* Patience, young hacker! */ - - byte buf[8]; - int result = songit_next(&(song->_it), buf, cue, IT_READER_MASK_ALL); - - switch (result) { - - case SI_FINISHED: - setSongStatus(song, SOUND_STATUS_STOPPED); - update(); - /* ...fall through... */ - case SI_LOOP: - case SI_RELATIVE_CUE: - case SI_ABSOLUTE_CUE: - if (result == SI_FINISHED) - debugC(2, kDebugLevelSound, " => finished"); - else { - if (result == SI_LOOP) - debugC(2, kDebugLevelSound, " => Loop: %d (0x%x)", *cue, *cue); - else - debugC(2, kDebugLevelSound, " => Cue: %d (0x%x)", *cue, *cue); - - } - return result; - - default: - if (result > 0) - song->_wakeupTime = song->_wakeupTime.addFrames(result); - - /* Delay */ - break; - } - } - -} - - -/*****************/ -/* Song basics */ -/*****************/ - -void SfxState::sfx_add_song(SongIterator *it, int priority, SongHandle handle, int number) { - Song *song = _songlib.findSong(handle); - -#ifdef DEBUG_SONG_API - fprintf(stderr, "[sfx-core] Adding song: %08lx at %d, it=%p\n", handle, priority, it); -#endif - if (!it) { - error("[SFX] Attempt to add empty song with handle %08lx", handle); - return; - } - - it->init(); - - /* If we're already playing this, stop it */ - /* Tell player to shut up */ -// _dump_songs(this); - - if (_player) - _player->iterator_message(SongIterator::Message(handle, SIMSG_STOP)); - - if (song) { - setSongStatus( song, SOUND_STATUS_STOPPED); - - fprintf(stderr, "Overwriting old song (%08lx) ...\n", handle); - if (song->_status == SOUND_STATUS_PLAYING || song->_status == SOUND_STATUS_SUSPENDED) { - delete it; - error("Unexpected (error): Song %ld still playing/suspended (%d)", - handle, song->_status); - return; - } else { - _songlib.removeSong(handle); /* No duplicates */ - } - - } - - song = new Song(handle, it, priority); - song->_resourceNum = number; - song->_hold = 0; - song->_loops = 0; - song->_wakeupTime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC); - _songlib.addSong(song); - _song = NULL; /* As above */ - update(); - - return; -} - -void SfxState::sfx_remove_song(SongHandle handle) { -#ifdef DEBUG_SONG_API - fprintf(stderr, "[sfx-core] Removing song: %08lx\n", handle); -#endif - if (_song && _song->_handle == handle) - _song = NULL; - - _songlib.removeSong(handle); - update(); -} - - - -/**********************/ -/* Song modifications */ -/**********************/ - -#define ASSERT_SONG(s) if (!(s)) { warning("Looking up song handle %08lx failed in %s, L%d", handle, __FILE__, __LINE__); return; } - -void SfxState::sfx_song_set_status(SongHandle handle, int status) { - Song *song = _songlib.findSong(handle); - ASSERT_SONG(song); -#ifdef DEBUG_SONG_API - fprintf(stderr, "[sfx-core] Setting song status to %d" - " (0:stop, 1:play, 2:susp, 3:wait): %08lx\n", status, handle); -#endif - - setSongStatus(song, status); - - update(); -} - -void SfxState::sfx_song_set_fade(SongHandle handle, fade_params_t *params) { -#ifdef DEBUG_SONG_API - static const char *stopmsg[] = {"??? Should not happen", "Do not stop afterwards", "Stop afterwards"}; -#endif - Song *song = _songlib.findSong(handle); - - ASSERT_SONG(song); - -#ifdef DEBUG_SONG_API - fprintf(stderr, "[sfx-core] Setting fade params of %08lx to " - "final volume %d in steps of %d per %d ticks. %s.", - handle, fade->final_volume, fade->step_size, fade->ticks_per_step, - stopmsg[fade->action]); -#endif - - SIMSG_SEND_FADE(song->_it, params); - - update(); -} - -void SfxState::sfx_song_renice(SongHandle handle, int priority) { - Song *song = _songlib.findSong(handle); - ASSERT_SONG(song); -#ifdef DEBUG_SONG_API - fprintf(stderr, "[sfx-core] Renicing song %08lx to %d\n", - handle, priority); -#endif - - song->_priority = priority; - - update(); -} - -void SfxState::sfx_song_set_loops(SongHandle handle, int loops) { - Song *song = _songlib.findSong(handle); - SongIterator::Message msg = SongIterator::Message(handle, SIMSG_SET_LOOPS(loops)); - ASSERT_SONG(song); - - song->_loops = loops; -#ifdef DEBUG_SONG_API - fprintf(stderr, "[sfx-core] Setting loops on %08lx to %d\n", - handle, loops); -#endif - songit_handle_message(&(song->_it), msg); - - if (_player/* && _player->send_iterator_message*/) - /* FIXME: The above should be optional! */ - _player->iterator_message(msg); -} - -void SfxState::sfx_song_set_hold(SongHandle handle, int hold) { - Song *song = _songlib.findSong(handle); - SongIterator::Message msg = SongIterator::Message(handle, SIMSG_SET_HOLD(hold)); - ASSERT_SONG(song); - - song->_hold = hold; -#ifdef DEBUG_SONG_API - fprintf(stderr, "[sfx-core] Setting hold on %08lx to %d\n", - handle, hold); -#endif - songit_handle_message(&(song->_it), msg); - - if (_player/* && _player->send_iterator_message*/) - /* FIXME: The above should be optional! */ - _player->iterator_message(msg); -} - -/* Different from the one in iterator.c */ -static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0, - 3, 3, 0, 3, 2, 0, 3, 0 - }; - -static const SongHandle midi_send_base = 0xffff0000; - -Common::Error SfxState::sfx_send_midi(SongHandle handle, int channel, - int command, int arg1, int arg2) { - byte buffer[5]; - - /* Yes, in that order. SCI channel mutes are actually done via - a counting semaphore. 0 means to decrement the counter, 1 - to increment it. */ - static const char *channel_state[] = {"ON", "OFF"}; - - if (command == 0xb0 && - arg1 == SCI_MIDI_CHANNEL_MUTE) { - warning("TODO: channel mute (channel %d %s)", channel, channel_state[arg2]); - /* We need to have a GET_PLAYMASK interface to use - here. SET_PLAYMASK we've got. - */ - return Common::kNoError; - } - - buffer[0] = channel | command; /* No channel remapping yet */ - - switch (command) { - case 0x80 : - case 0x90 : - case 0xb0 : - buffer[1] = arg1 & 0xff; - buffer[2] = arg2 & 0xff; - break; - case 0xc0 : - buffer[1] = arg1 & 0xff; - break; - case 0xe0 : - buffer[1] = (arg1 & 0x7f) | 0x80; - buffer[2] = (arg1 & 0xff00) >> 7; - break; - default: - warning("Unexpected explicit MIDI command %02x", command); - return Common::kUnknownError; - } - - if (_player) - _player->tell_synth(MIDI_cmdlen[command >> 4], buffer); - return Common::kNoError; -} - -int SfxState::sfx_getVolume() { - return _player->getVolume(); -} - -void SfxState::sfx_setVolume(int volume) { - _player->setVolume(volume); -} - -void SfxState::sfx_all_stop() { -#ifdef DEBUG_SONG_API - fprintf(stderr, "[sfx-core] All stop\n"); -#endif - - _songlib.freeSounds(); - update(); -} - -} // End of namespace Sci - -#endif // USE_OLD_MUSIC_FUNCTIONS diff --git a/engines/sci/sound/iterator/core.h b/engines/sci/sound/iterator/core.h deleted file mode 100644 index a44fe2ecae..0000000000 --- a/engines/sci/sound/iterator/core.h +++ /dev/null @@ -1,209 +0,0 @@ -/* 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$ - * - */ - -/* Sound engine */ -#ifndef SCI_SFX_CORE_H -#define SCI_SFX_CORE_H - -#include "common/error.h" - -#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS - -#ifdef USE_OLD_MUSIC_FUNCTIONS -#include "sci/sound/iterator/songlib.h" -#include "sci/resource.h" - -namespace Sci { - -class SfxPlayer; -class SongIterator; -struct fade_params_t; - -#define SFX_TICKS_PER_SEC 60 /* MIDI ticks per second */ - - -#define SFX_STATE_FLAG_MULTIPLAY (1 << 0) /* More than one song playable -** simultaneously ? */ -#define SFX_STATE_FLAG_NOSOUND (1 << 1) /* Completely disable sound playing */ - -class SfxState { -private: - SfxPlayer *_player; - -public: // FIXME, make private - SongIterator *_it; /**< The song iterator at the heart of things */ - uint _flags; /**< SFX_STATE_FLAG_* */ - SongLibrary _songlib; /**< Song library */ - Song *_song; /**< Active song, or start of active song chain */ - bool _suspended; /**< Whether we are suspended */ - ResourceManager *_resMan; - -public: - SfxState(); - ~SfxState(); - - /***********/ - /* General */ - /***********/ - - /* Initializes the sound engine - ** Parameters: (ResourceManager *) resMan: Resource manager for initialization - ** (int) flags: SFX_STATE_FLAG_* - */ - void sfx_init(ResourceManager *resMan, int flags, SciVersion soundVersion); - - /** Deinitializes the sound subsystem. */ - void sfx_exit(); - - /* Suspends/unsuspends the sound sybsystem - ** Parameters: (int) suspend: Whether to suspend (non-null) or to unsuspend - */ - void sfx_suspend(bool suspend); - - /* Polls the sound server for cues etc. - ** Returns : (int) 0 if the cue queue is empty, SI_LOOP, SI_CUE, or SI_FINISHED otherwise - ** (SongHandle) *handle: The affected handle - ** (int) *cue: The sound cue number (if SI_CUE), or the loop number (if SI_LOOP) - */ - int sfx_poll(SongHandle *handle, int *cue); - - /* Polls the sound server for cues etc. - ** Parameters: (SongHandle) handle: The handle to poll - ** Returns : (int) 0 if the cue queue is empty, SI_LOOP, SI_CUE, or SI_FINISHED otherwise - ** (int) *cue: The sound cue number (if SI_CUE), or the loop number (if SI_LOOP) - */ - int sfx_poll_specific(SongHandle handle, int *cue); - - /* Determines the current global volume settings - ** Returns : (int) The global volume, between 0 (silent) and 127 (max. volume) - */ - int sfx_getVolume(); - - /* Determines the current global volume settings - ** Parameters: (int) volume: The new global volume, between 0 and 127 (see above) - */ - void sfx_setVolume(int volume); - - /* Stops all songs currently playing, purges song library - */ - void sfx_all_stop(); - - - /*****************/ - /* Song basics */ - /*****************/ - - /* Adds a song to the internal sound library - ** Parameters: (SongIterator *) it: The iterator describing the song - ** (int) priority: Initial song priority (higher <-> more important) - ** (SongHandle) handle: The handle to associate with the song - */ - void sfx_add_song(SongIterator *it, int priority, SongHandle handle, int resnum); - - - /* Deletes a song and its associated song iterator from the song queue - ** Parameters: (SongHandle) handle: The song to remove - */ - void sfx_remove_song(SongHandle handle); - - - /**********************/ - /* Song modifications */ - /**********************/ - - - /* Sets the song status, i.e. whether it is playing, suspended, or stopped. - ** Parameters: (SongHandle) handle: Handle of the song to modify - ** (int) status: The song status the song should assume - ** WAITING and PLAYING are set implicitly and essentially describe the same state - ** as far as this function is concerned. - */ - void sfx_song_set_status(SongHandle handle, int status); - - /* Sets the new song priority - ** Parameters: (SongHandle) handle: The handle to modify - ** (int) priority: The priority to set - */ - void sfx_song_renice(SongHandle handle, int priority); - - /* Sets the number of loops for the specified song - ** Parameters: (SongHandle) handle: The song handle to reference - ** (int) loops: Number of loops to set - */ - void sfx_song_set_loops(SongHandle handle, int loops); - - /* Sets the number of loops for the specified song - ** Parameters: (SongHandle) handle: The song handle to reference - ** (int) hold: Number of loops to setn - */ - void sfx_song_set_hold(SongHandle handle, int hold); - - /* Instructs a song to be faded out - ** Parameters: (SongHandle) handle: The song handle to reference - ** (fade_params_t *) fade_setup: The precise fade-out configuration to use - */ - void sfx_song_set_fade(SongHandle handle, fade_params_t *fade_setup); - - - // Previously undocumented: - Common::Error sfx_send_midi(SongHandle handle, int channel, - int command, int arg1, int arg2); - - // misc - - /** - * Determines the polyphony of the player in use. - * @return Number of voices the active player can emit - */ - int sfx_get_player_polyphony(); - - /** - * Tells the player to stop its internal iterator. - */ - void sfx_reset_player(); - - /** - * Pass a raw MIDI event to the synth of the player. - * @param argc Length of buffer holding the midi event - * @param argv The buffer itself - */ - void sfx_player_tell_synth(int buf_nr, byte *buf); - -protected: - void freezeTime(); - void thawTime(); - - bool isPlaying(Song *song); - void setSongStatus(Song *song, int status); - void updateSingleSong(); - void updateMultiSong(); - void update(); -}; - -} // End of namespace Sci - -#endif // USE_OLD_MUSIC_FUNCTIONS - -#endif // SCI_SFX_CORE_H diff --git a/engines/sci/sound/iterator/iterator.cpp b/engines/sci/sound/iterator/iterator.cpp deleted file mode 100644 index 5d9d63e5af..0000000000 --- a/engines/sci/sound/iterator/iterator.cpp +++ /dev/null @@ -1,1686 +0,0 @@ -/* 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$ - * - */ - -/* Song iterators */ - -#include "common/util.h" - -#include "sci/sci.h" -#ifdef USE_OLD_MUSIC_FUNCTIONS - -#include "sci/sound/iterator/iterator_internal.h" -#include "sci/engine/state.h" // for sfx_player_tell_synth :/ -#include "sci/sound/iterator/core.h" // for sfx_player_tell_synth - -#include "sound/audiostream.h" -#include "sound/mixer.h" -#include "sound/decoders/raw.h" - -namespace Sci { - - -static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0, - 2, 2, 2, 2, 1, 1, 2, 0 - }; - -/*#define DEBUG_DECODING*/ -/*#define DEBUG_VERBOSE*/ - -/** Find first set bit in bits and return its index. Returns 0 if bits is 0. */ -static int sci_ffs(int bits) { - if (!bits) - return 0; - - int retval = 1; - - while (!(bits & 1)) { - retval++; - bits >>= 1; - } - - return retval; -} - -static void print_tabs_id(int nr, songit_id_t id) { - while (nr-- > 0) - fprintf(stderr, "\t"); - - fprintf(stderr, "[%08lx] ", id); -} - -BaseSongIterator::BaseSongIterator(byte *data, uint size, songit_id_t id) - : _data(data, size) { - ID = id; -} - -/************************************/ -/*-- SCI0 iterator implementation --*/ -/************************************/ - -#define SCI0_MIDI_OFFSET 33 -#define SCI0_END_OF_SONG 0xfc /* proprietary MIDI command */ - -#define SCI0_PCM_SAMPLE_RATE_OFFSET 0x0e -#define SCI0_PCM_SIZE_OFFSET 0x20 -#define SCI0_PCM_DATA_OFFSET 0x2c - -#define CHECK_FOR_END_ABSOLUTE(offset) \ - if (offset > _data.size()) { \ - warning("Reached end of song without terminator (%x/%x) at %d", offset, _data.size(), __LINE__); \ - return SI_FINISHED; \ - } - -#define CHECK_FOR_END(offset_augment) \ - if ((channel->offset + (offset_augment)) > channel->end) { \ - channel->state = SI_STATE_FINISHED; \ - warning("Reached end of track %d without terminator (%x+%x/%x) at %d", channel->id, channel->offset, offset_augment, channel->end, __LINE__); \ - return SI_FINISHED; \ - } - - -static int _parse_ticks(byte *data, int *offset_p, int size) { - int ticks = 0; - int tempticks; - int offset = 0; - - do { - tempticks = data[offset++]; - ticks += (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX) ? - SCI_MIDI_TIME_EXPANSION_LENGTH : tempticks; - } while (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX - && offset < size); - - if (offset_p) - *offset_p = offset; - - return ticks; -} - - -static int _sci0_get_pcm_data(Sci0SongIterator *self, int *rate, int *xoffset, uint *xsize); - - -#define PARSE_FLAG_LOOPS_UNLIMITED (1 << 0) /* Unlimited # of loops? */ -#define PARSE_FLAG_PARAMETRIC_CUE (1 << 1) /* Assume that cues take an additional "cue value" argument */ -/* This implements a difference between SCI0 and SCI1 cues. */ - -void SongIteratorChannel::init(int id_, int offset_, int end_) { - playmask = PLAYMASK_NONE; /* Disable all channels */ - id = id_; - state = SI_STATE_DELTA_TIME; - loop_timepos = 0; - total_timepos = 0; - timepos_increment = 0; - delay = 0; /* Only used for more than one channel */ - last_cmd = 0xfe; - - offset = loop_offset = initial_offset = offset_; - end = end_; -} - -void SongIteratorChannel::resetSynthChannels() { - byte buf[5]; - - // FIXME: Evil hack - SfxState &sound = g_sci->getEngineState()->_sound; - - for (int i = 0; i < MIDI_CHANNELS; i++) { - if (playmask & (1 << i)) { - buf[0] = 0xe0 | i; /* Pitch bend */ - buf[1] = 0x80; /* Wheel center */ - buf[2] = 0x40; - sound.sfx_player_tell_synth(3, buf); - - buf[0] = 0xb0 | i; // Set control - buf[1] = 0x40; // Hold pedal - buf[2] = 0x00; // Off - sound.sfx_player_tell_synth(3, buf); - /* TODO: Reset other controls? */ - } - } -} - -int BaseSongIterator::parseMidiCommand(byte *buf, int *result, SongIteratorChannel *channel, int flags) { - byte cmd; - int paramsleft; - int midi_op; - int midi_channel; - - channel->state = SI_STATE_DELTA_TIME; - - cmd = _data[channel->offset++]; - - if (!(cmd & 0x80)) { - /* 'Running status' mode */ - channel->offset--; - cmd = channel->last_cmd; - } - - if (cmd == 0xfe) { - warning("song iterator subsystem: Corrupted sound resource detected."); - return SI_FINISHED; - } - - midi_op = cmd >> 4; - midi_channel = cmd & 0xf; - paramsleft = MIDI_cmdlen[midi_op]; - -#if 0 - if (1) { - fprintf(stderr, "[IT]: off=%x, cmd=%02x, takes %d args ", - channel->offset - 1, cmd, paramsleft); - fprintf(stderr, "[%02x %02x <%02x> %02x %02x %02x]\n", - _data[channel->offset-3], - _data[channel->offset-2], - _data[channel->offset-1], - _data[channel->offset], - _data[channel->offset+1], - _data[channel->offset+2]); - } -#endif - - buf[0] = cmd; - - - CHECK_FOR_END(paramsleft); - memcpy(buf + 1, _data.begin() + channel->offset, paramsleft); - *result = 1 + paramsleft; - - channel->offset += paramsleft; - - channel->last_cmd = cmd; - - /* Are we supposed to play this channel? */ - if ( - /* First, exclude "global" properties-- such as cues-- from consideration */ - (midi_op < 0xf - && !(cmd == SCI_MIDI_SET_SIGNAL) - && !(SCI_MIDI_CONTROLLER(cmd) - && buf[1] == SCI_MIDI_CUMULATIVE_CUE)) - - /* Next, check if the channel is allowed */ - && (!((1 << midi_channel) & channel->playmask))) - return /* Execute next command */ - nextCommand(buf, result); - - - if (cmd == SCI_MIDI_EOT) { - /* End of track? */ - channel->resetSynthChannels(); - if (_loops > 1) { - /* If allowed, decrement the number of loops */ - if (!(flags & PARSE_FLAG_LOOPS_UNLIMITED)) - *result = --_loops; - -#ifdef DEBUG_DECODING - fprintf(stderr, "%s L%d: (%p):%d Looping ", __FILE__, __LINE__, this, channel->id); - if (flags & PARSE_FLAG_LOOPS_UNLIMITED) - fprintf(stderr, "(indef.)"); - else - fprintf(stderr, "(%d)", _loops); - fprintf(stderr, " %x -> %x\n", - channel->offset, channel->loop_offset); -#endif - channel->offset = channel->loop_offset; - channel->state = SI_STATE_DELTA_TIME; - channel->total_timepos = channel->loop_timepos; - channel->last_cmd = 0xfe; - debugC(2, kDebugLevelSound, "Looping song iterator %08lx.", ID); - return SI_LOOP; - } else { - channel->state = SI_STATE_FINISHED; - return SI_FINISHED; - } - - } else if (cmd == SCI_MIDI_SET_SIGNAL) { - if (buf[1] == SCI_MIDI_SET_SIGNAL_LOOP) { - channel->loop_offset = channel->offset; - channel->loop_timepos = channel->total_timepos; - - return /* Execute next command */ - nextCommand(buf, result); - } else { - /* Used to be conditional <= 127 */ - *result = buf[1]; /* Absolute cue */ - return SI_ABSOLUTE_CUE; - } - } else if (SCI_MIDI_CONTROLLER(cmd)) { - switch (buf[1]) { - - case SCI_MIDI_CUMULATIVE_CUE: - if (flags & PARSE_FLAG_PARAMETRIC_CUE) - _ccc += buf[2]; - else { /* No parameter to CC */ - _ccc++; - /* channel->offset--; */ - } - *result = _ccc; - return SI_RELATIVE_CUE; - - case SCI_MIDI_RESET_ON_SUSPEND: - _resetflag = buf[2]; - break; - - case SCI_MIDI_SET_POLYPHONY: - _polyphony[midi_channel] = buf[2]; - -#if 0 - { - Sci1SongIterator *self1 = (Sci1SongIterator *)this; - int i; - int voices = 0; - for (i = 0; i < self1->_numChannels; i++) { - voices += _polyphony[i]; - } - - printf("SET_POLYPHONY(%d, %d) for a total of %d voices\n", midi_channel, buf[2], voices); - printf("[iterator] DEBUG: Polyphony = [ "); - for (i = 0; i < self1->_numChannels; i++) - printf("%d ", _polyphony[i]); - printf("]\n"); - printf("[iterator] DEBUG: Importance = [ "); - printf("]\n"); - } -#endif - break; - - case SCI_MIDI_SET_REVERB: - break; - - case SCI_MIDI_CHANNEL_MUTE: - warning("CHANNEL_MUTE(%d, %d)", midi_channel, buf[2]); - break; - - case SCI_MIDI_HOLD: { - // Safe cast: This controller is only used in SCI1 - Sci1SongIterator *self1 = (Sci1SongIterator *)this; - - if (buf[2] == self1->_hold) { - channel->offset = channel->initial_offset; - channel->state = SI_STATE_COMMAND; - channel->total_timepos = 0; - - self1->_numLoopedChannels = self1->_numActiveChannels - 1; - - // FIXME: - // This implementation of hold breaks getting out of the - // limo when visiting the airport near the start of LSL5. - // It seems like all channels should be reset here somehow, - // but not sure how. - // Forcing all channel offsets to 0 seems to fix the hang, - // but somehow slows the exit sequence down to take 20 seconds - // instead of about 3. - - return SI_LOOP; - } - - break; - } - case 0x04: /* UNKNOWN NYI (happens in LSL2 gameshow) */ - case 0x46: /* UNKNOWN NYI (happens in LSL3 binoculars) */ - case 0x61: /* UNKNOWN NYI (special for AdLib? Iceman) */ - case 0x73: /* UNKNOWN NYI (happens in Hoyle) */ - case 0xd1: /* UNKNOWN NYI (happens in KQ4 when riding the unicorn) */ - return /* Execute next command */ - nextCommand(buf, result); - - case 0x01: /* modulation */ - case 0x07: /* volume */ - case 0x0a: /* panpot */ - case 0x0b: /* expression */ - case 0x40: /* hold */ - case 0x79: /* reset all */ - /* No special treatment neccessary */ - break; - - } - return 0; - - } else { -#if 0 - /* Perform remapping, if neccessary */ - if (cmd != SCI_MIDI_SET_SIGNAL - && cmd < 0xf0) { /* Not a generic command */ - int chan = cmd & 0xf; - int op = cmd & 0xf0; - - chan = channel_remap[chan]; - buf[0] = chan | op; - } -#endif - - /* Process as normal MIDI operation */ - return 0; - } -} - -int BaseSongIterator::processMidi(byte *buf, int *result, - SongIteratorChannel *channel, int flags) { - CHECK_FOR_END(0); - - switch (channel->state) { - - case SI_STATE_PCM: { - if (_data[channel->offset] == 0 - && _data[channel->offset + 1] == SCI_MIDI_EOT) - /* Fake one extra tick to trick the interpreter into not killing the song iterator right away */ - channel->state = SI_STATE_PCM_MAGIC_DELTA; - else - channel->state = SI_STATE_DELTA_TIME; - return SI_PCM; - } - - case SI_STATE_PCM_MAGIC_DELTA: { - int rate; - int offset; - uint size; - int delay; - if (_sci0_get_pcm_data((Sci0SongIterator *)this, &rate, &offset, &size)) - return SI_FINISHED; /* 'tis broken */ - channel->state = SI_STATE_FINISHED; - delay = (size * 50 + rate - 1) / rate; /* number of ticks to completion*/ - - debugC(2, kDebugLevelSound, "delaying %d ticks", delay); - return delay; - } - - case SI_STATE_UNINITIALISED: - warning("Attempt to read command from uninitialized iterator"); - init(); - return nextCommand(buf, result); - - case SI_STATE_FINISHED: - return SI_FINISHED; - - case SI_STATE_DELTA_TIME: { - int offset; - int ticks = _parse_ticks(_data.begin() + channel->offset, - &offset, - _data.size() - channel->offset); - - channel->offset += offset; - channel->delay += ticks; - channel->timepos_increment = ticks; - - CHECK_FOR_END(0); - - channel->state = SI_STATE_COMMAND; - - if (ticks) - return ticks; - } - - /* continute otherwise... */ - - case SI_STATE_COMMAND: { - int retval; - channel->total_timepos += channel->timepos_increment; - channel->timepos_increment = 0; - - retval = parseMidiCommand(buf, result, channel, flags); - - if (retval == SI_FINISHED) { - if (_numActiveChannels) - --(_numActiveChannels); -#ifdef DEBUG_DECODING - fprintf(stderr, "%s L%d: (%p):%d Finished channel, %d channels left\n", - __FILE__, __LINE__, this, channel->id, - _numActiveChannels); -#endif - /* If we still have channels left... */ - if (_numActiveChannels) { - return nextCommand(buf, result); - } - - /* Otherwise, we have reached the end */ - _loops = 0; - } - - return retval; - } - - default: - error("Invalid iterator state %d", channel->state); - return SI_FINISHED; - } -} - -int Sci0SongIterator::nextCommand(byte *buf, int *result) { - return processMidi(buf, result, &_channel, PARSE_FLAG_PARAMETRIC_CUE); -} - -static int _sci0_header_magic_p(byte *data, int offset, int size) { - if (offset + 0x10 > size) - return 0; - return (data[offset] == 0x1a) - && (data[offset + 1] == 0x00) - && (data[offset + 2] == 0x01) - && (data[offset + 3] == 0x00); -} - - -static int _sci0_get_pcm_data(Sci0SongIterator *self, - int *rate, int *xoffset, uint *xsize) { - int tries = 2; - bool found_it = false; - byte *pcm_data; - int size; - uint offset = SCI0_MIDI_OFFSET; - - if (self->_data[0] != 2) - return 1; - /* No such luck */ - - while ((tries--) && (offset < self->_data.size()) && (!found_it)) { - // Search through the garbage manually - // FIXME: Replace offset by an iterator - Common::Array<byte>::iterator iter = Common::find(self->_data.begin() + offset, self->_data.end(), SCI0_END_OF_SONG); - - if (iter == self->_data.end()) { - warning("Playing unterminated song"); - return 1; - } - - // add one to move it past the END_OF_SONG marker - iter++; - offset = iter - self->_data.begin(); // FIXME - - - if (_sci0_header_magic_p(self->_data.begin(), offset, self->_data.size())) - found_it = true; - } - - if (!found_it) { - warning("Song indicates presence of PCM, but" - " none found (finally at offset %04x)", offset); - - return 1; - } - - pcm_data = self->_data.begin() + offset; - - size = READ_LE_UINT16(pcm_data + SCI0_PCM_SIZE_OFFSET); - - /* Two of the format parameters are fixed by design: */ - *rate = READ_LE_UINT16(pcm_data + SCI0_PCM_SAMPLE_RATE_OFFSET); - - if (offset + SCI0_PCM_DATA_OFFSET + size != self->_data.size()) { - int d = offset + SCI0_PCM_DATA_OFFSET + size - self->_data.size(); - - warning("PCM advertizes %d bytes of data, but %d" - " bytes are trailing in the resource", - size, self->_data.size() - (offset + SCI0_PCM_DATA_OFFSET)); - - if (d > 0) - size -= d; /* Fix this */ - } - - *xoffset = offset; - *xsize = size; - - return 0; -} - -static Audio::AudioStream *makeStream(byte *data, int size, int rate) { - debugC(2, kDebugLevelSound, "Playing PCM data of size %d, rate %d", size, rate); - - // Duplicate the data - byte *sound = (byte *)malloc(size); - memcpy(sound, data, size); - - // Convert stream format flags - int flags = Audio::FLAG_UNSIGNED; - return Audio::makeRawStream(sound, size, rate, flags); -} - -Audio::AudioStream *Sci0SongIterator::getAudioStream() { - int rate; - int offset; - uint size; - if (_sci0_get_pcm_data(this, &rate, &offset, &size)) - return NULL; - - _channel.state = SI_STATE_FINISHED; /* Don't play both PCM and music */ - - return makeStream(_data.begin() + offset + SCI0_PCM_DATA_OFFSET, size, rate); -} - -SongIterator *Sci0SongIterator::handleMessage(Message msg) { - if (msg._class == _SIMSG_BASE) { - switch (msg._type) { - - case _SIMSG_BASEMSG_PRINT: - print_tabs_id(msg._arg.i, ID); - debugC(2, kDebugLevelSound, "SCI0: dev=%d, active-chan=%d, size=%d, loops=%d", - _deviceId, _numActiveChannels, _data.size(), _loops); - break; - - case _SIMSG_BASEMSG_SET_LOOPS: - _loops = msg._arg.i; - break; - - case _SIMSG_BASEMSG_STOP: { - songit_id_t sought_id = msg.ID; - - if (sought_id == ID) - _channel.state = SI_STATE_FINISHED; - break; - } - - case _SIMSG_BASEMSG_SET_PLAYMASK: { - int i; - _deviceId = msg._arg.i; - - /* Set all but the rhytm channel mask bits */ - _channel.playmask &= ~(1 << MIDI_RHYTHM_CHANNEL); - - for (i = 0; i < MIDI_CHANNELS; i++) - if (_data[2 + (i << 1)] & _deviceId - && i != MIDI_RHYTHM_CHANNEL) - _channel.playmask |= (1 << i); - } - break; - - case _SIMSG_BASEMSG_SET_RHYTHM: - _channel.playmask &= ~(1 << MIDI_RHYTHM_CHANNEL); - if (msg._arg.i) - _channel.playmask |= (1 << MIDI_RHYTHM_CHANNEL); - break; - - case _SIMSG_BASEMSG_SET_FADE: { - fade_params_t *fp = (fade_params_t *) msg._arg.p; - fade.action = fp->action; - fade.final_volume = fp->final_volume; - fade.ticks_per_step = fp->ticks_per_step; - fade.step_size = fp->step_size; - break; - } - - default: - return NULL; - } - - return this; - } - return NULL; -} - -int Sci0SongIterator::getTimepos() { - return _channel.total_timepos; -} - -Sci0SongIterator::Sci0SongIterator(byte *data, uint size, songit_id_t id) - : BaseSongIterator(data, size, id) { - channel_mask = 0xffff; // Allocate all channels by default - _channel.state = SI_STATE_UNINITIALISED; - - for (int i = 0; i < MIDI_CHANNELS; i++) - _polyphony[i] = data[1 + (i << 1)]; - - init(); -} - -void Sci0SongIterator::init() { - fade.action = FADE_ACTION_NONE; - _resetflag = 0; - _loops = 0; - priority = 0; - - _ccc = 0; /* Reset cumulative cue counter */ - _numActiveChannels = 1; - _channel.init(0, SCI0_MIDI_OFFSET, _data.size()); - _channel.resetSynthChannels(); - - if (_data[0] == 2) /* Do we have an embedded PCM? */ - _channel.state = SI_STATE_PCM; -} - -SongIterator *Sci0SongIterator::clone(int delta) { - Sci0SongIterator *newit = new Sci0SongIterator(*this); - return newit; -} - - -/***************************/ -/*-- SCI1 song iterators --*/ -/***************************/ - -int Sci1SongIterator::initSample(const int offset) { - Sci1Sample sample; - int rate; - int length; - int begin; - int end; - - CHECK_FOR_END_ABSOLUTE((uint)offset + 10); - if (_data[offset + 1] != 0) - warning("[iterator-1] In sample at offset 0x04x: Byte #1 is %02x instead of zero", - _data[offset + 1]); - - rate = (int16)READ_LE_UINT16(_data.begin() + offset + 2); - length = READ_LE_UINT16(_data.begin() + offset + 4); - begin = (int16)READ_LE_UINT16(_data.begin() + offset + 6); - end = (int16)READ_LE_UINT16(_data.begin() + offset + 8); - - CHECK_FOR_END_ABSOLUTE((uint)(offset + 10 + length)); - - sample.delta = begin; - sample.size = length; - sample._data = _data.begin() + offset + 10; - -#ifdef DEBUG_VERBOSE - fprintf(stderr, "[SAMPLE] %x/%x/%x/%x l=%x\n", - offset + 10, begin, end, _data.size(), length); -#endif - - sample.rate = rate; - - sample.announced = false; - - /* Insert into the sample list at the right spot, keeping it sorted by delta */ - Common::List<Sci1Sample>::iterator seeker = _samples.begin(); - while (seeker != _samples.end() && seeker->delta < begin) - ++seeker; - _samples.insert(seeker, sample); - - return 0; /* Everything's fine */ -} - -int Sci1SongIterator::initSong() { - int last_time; - uint offset = 0; - _numChannels = 0; - _samples.clear(); -// _deviceId = 0x0c; - - if (_data[offset] == 0xf0) { - priority = _data[offset + 1]; - - offset += 8; - } - - while (_data[offset] != 0xff - && _data[offset] != _deviceId) { - offset++; - CHECK_FOR_END_ABSOLUTE(offset + 1); - while (_data[offset] != 0xff) { - CHECK_FOR_END_ABSOLUTE(offset + 7); - offset += 6; - } - offset++; - } - - if (_data[offset] == 0xff) { - warning("[iterator] Song does not support hardware 0x%02x", _deviceId); - return 1; - } - - offset++; - - while (_data[offset] != 0xff) { /* End of list? */ - uint track_offset; - int end; - offset += 2; - - CHECK_FOR_END_ABSOLUTE(offset + 4); - - track_offset = READ_LE_UINT16(_data.begin() + offset); - end = READ_LE_UINT16(_data.begin() + offset + 2); - - CHECK_FOR_END_ABSOLUTE(track_offset - 1); - - if (_data[track_offset] == 0xfe) { - if (initSample(track_offset)) - return 1; /* Error */ - } else { - /* Regular MIDI channel */ - if (_numChannels >= MIDI_CHANNELS) { - warning("[iterator] Song has more than %d channels, cutting them off", - MIDI_CHANNELS); - break; /* Scan for remaining samples */ - } else { - int channel_nr = _data[track_offset] & 0xf; - SongIteratorChannel &channel = _channels[_numChannels++]; - - /* - if (_data[track_offset] & 0xf0) - printf("Channel %d has mapping bits %02x\n", - channel_nr, _data[track_offset] & 0xf0); - */ - - // Add 2 to skip over header bytes */ - channel.init(channel_nr, track_offset + 2, track_offset + end); - channel.resetSynthChannels(); - - _polyphony[_numChannels - 1] = _data[channel.offset - 1] & 15; - - channel.playmask = ~0; /* Enable all */ - channel_mask |= (1 << channel_nr); - - CHECK_FOR_END_ABSOLUTE(offset + end); - } - } - offset += 4; - CHECK_FOR_END_ABSOLUTE(offset); - } - - /* Now ensure that sample deltas are relative to the previous sample */ - last_time = 0; - _numActiveChannels = _numChannels; - _numLoopedChannels = 0; - - for (Common::List<Sci1Sample>::iterator seeker = _samples.begin(); - seeker != _samples.end(); ++seeker) { - int prev_last_time = last_time; - //printf("[iterator] Detected sample: %d Hz, %d bytes at time %d\n", - // seeker->format.rate, seeker->size, seeker->delta); - last_time = seeker->delta; - seeker->delta -= prev_last_time; - } - - return 0; /* Success */ -} - -int Sci1SongIterator::getSmallestDelta() const { - int d = -1; - for (int i = 0; i < _numChannels; i++) - if (_channels[i].state == SI_STATE_COMMAND - && (d == -1 || _channels[i].delay < d)) - d = _channels[i].delay; - - if (!_samples.empty() && _samples.begin()->delta < d) - return _samples.begin()->delta; - else - return d; -} - -void Sci1SongIterator::updateDelta(int delta) { - if (!_samples.empty()) - _samples.begin()->delta -= delta; - - for (int i = 0; i < _numChannels; i++) - if (_channels[i].state == SI_STATE_COMMAND) - _channels[i].delay -= delta; -} - -bool Sci1SongIterator::noDeltaTime() const { - for (int i = 0; i < _numChannels; i++) - if (_channels[i].state == SI_STATE_DELTA_TIME) - return false; - return true; -} - -#define COMMAND_INDEX_NONE -1 -#define COMMAND_INDEX_PCM -2 - -int Sci1SongIterator::getCommandIndex() const { - /* Determine the channel # of the next active event, or -1 */ - int i; - int base_delay = 0x7ffffff; - int best_chan = COMMAND_INDEX_NONE; - - for (i = 0; i < _numChannels; i++) - if ((_channels[i].state != SI_STATE_PENDING) - && (_channels[i].state != SI_STATE_FINISHED)) { - - if ((_channels[i].state == SI_STATE_DELTA_TIME) - && (_channels[i].delay == 0)) - return i; - /* First, read all unknown delta times */ - - if (_channels[i].delay < base_delay) { - best_chan = i; - base_delay = _channels[i].delay; - } - } - - if (!_samples.empty() && base_delay >= _samples.begin()->delta) - return COMMAND_INDEX_PCM; - - return best_chan; -} - - -Audio::AudioStream *Sci1SongIterator::getAudioStream() { - Common::List<Sci1Sample>::iterator sample = _samples.begin(); - if (sample != _samples.end() && sample->delta <= 0) { - Audio::AudioStream *feed = makeStream(sample->_data, sample->size, sample->rate); - _samples.erase(sample); - - return feed; - } else - return NULL; -} - -int Sci1SongIterator::nextCommand(byte *buf, int *result) { - - if (!_initialised) { - //printf("[iterator] DEBUG: Initialising for %d\n", _deviceId); - _initialised = true; - if (initSong()) - return SI_FINISHED; - } - - - if (_delayRemaining) { - int delay = _delayRemaining; - _delayRemaining = 0; - return delay; - } - - int retval = 0; - do { /* All delays must be processed separately */ - int chan = getCommandIndex(); - - if (chan == COMMAND_INDEX_NONE) { - return SI_FINISHED; - } - - if (chan == COMMAND_INDEX_PCM) { - - if (_samples.begin()->announced) { - /* Already announced; let's discard it */ - Audio::AudioStream *feed = getAudioStream(); - delete feed; - } else { - int delay = _samples.begin()->delta; - - if (delay) { - updateDelta(delay); - return delay; - } - /* otherwise we're touching a PCM */ - _samples.begin()->announced = true; - return SI_PCM; - } - } else { /* Not a PCM */ - - retval = processMidi(buf, result, - &(_channels[chan]), - PARSE_FLAG_LOOPS_UNLIMITED); - - if (retval == SI_LOOP) { - _numLoopedChannels++; - _channels[chan].state = SI_STATE_PENDING; - _channels[chan].delay = 0; - - if (_numLoopedChannels == _numActiveChannels) { - int i; - - /* Everyone's ready: Let's loop */ - for (i = 0; i < _numChannels; i++) - if (_channels[i].state == SI_STATE_PENDING) - _channels[i].state = SI_STATE_DELTA_TIME; - - _numLoopedChannels = 0; - return SI_LOOP; - } - } else if (retval == SI_FINISHED) { -#ifdef DEBUG - fprintf(stderr, "FINISHED some channel\n"); -#endif - } else if (retval > 0) { - int sd ; - sd = getSmallestDelta(); - - if (noDeltaTime() && sd) { - /* No other channel is ready */ - updateDelta(sd); - - /* Only from here do we return delta times */ - return sd; - } - } - - } /* Not a PCM */ - - } while (retval > 0); - - return retval; -} - -SongIterator *Sci1SongIterator::handleMessage(Message msg) { - if (msg._class == _SIMSG_BASE) { /* May extend this in the future */ - switch (msg._type) { - - case _SIMSG_BASEMSG_PRINT: { - int playmask = 0; - int i; - - for (i = 0; i < _numChannels; i++) - playmask |= _channels[i].playmask; - - print_tabs_id(msg._arg.i, ID); - debugC(2, kDebugLevelSound, "SCI1: chan-nr=%d, playmask=%04x", - _numChannels, playmask); - } - break; - - case _SIMSG_BASEMSG_STOP: { - songit_id_t sought_id = msg.ID; - int i; - - if (sought_id == ID) { - ID = 0; - - for (i = 0; i < _numChannels; i++) - _channels[i].state = SI_STATE_FINISHED; - } - break; - } - - case _SIMSG_BASEMSG_SET_PLAYMASK: - if (msg.ID == ID) { - channel_mask = 0; - - _deviceId = msg._arg.i; - - if (_initialised) { - int i; - int toffset = -1; - - for (i = 0; i < _numChannels; i++) - if (_channels[i].state != SI_STATE_FINISHED - && _channels[i].total_timepos > toffset) { - toffset = _channels[i].total_timepos - + _channels[i].timepos_increment - - _channels[i].delay; - } - - /* Find an active channel so that we can - ** get the correct time offset */ - - initSong(); - - toffset -= _delayRemaining; - _delayRemaining = 0; - - if (toffset > 0) - return new_fast_forward_iterator(this, toffset); - } else { - initSong(); - _initialised = true; - } - - break; - - } - - case _SIMSG_BASEMSG_SET_LOOPS: - if (msg.ID == ID) - _loops = (msg._arg.i > 32767) ? 99 : 0; - /* 99 is arbitrary, but we can't use '1' because of - ** the way we're testing in the decoding section. */ - break; - - case _SIMSG_BASEMSG_SET_HOLD: - _hold = msg._arg.i; - break; - case _SIMSG_BASEMSG_SET_RHYTHM: - /* Ignore */ - break; - - case _SIMSG_BASEMSG_SET_FADE: { - fade_params_t *fp = (fade_params_t *) msg._arg.p; - fade.action = fp->action; - fade.final_volume = fp->final_volume; - fade.ticks_per_step = fp->ticks_per_step; - fade.step_size = fp->step_size; - break; - } - - default: - warning("Unsupported command %d to SCI1 iterator", msg._type); - } - return this; - } - return NULL; -} - -Sci1SongIterator::Sci1SongIterator(byte *data, uint size, songit_id_t id) - : BaseSongIterator(data, size, id) { - channel_mask = 0; // Defer channel allocation - - for (int i = 0; i < MIDI_CHANNELS; i++) - _polyphony[i] = 0; // Unknown - - init(); -} - -void Sci1SongIterator::init() { - fade.action = FADE_ACTION_NONE; - _resetflag = 0; - _loops = 0; - priority = 0; - - _ccc = 0; - _deviceId = 0x00; // Default to Sound Blaster/AdLib for purposes of cue computation - _numChannels = 0; - _initialised = false; - _delayRemaining = 0; - _loops = 0; - _hold = 0; - memset(_polyphony, 0, sizeof(_polyphony)); -} - -Sci1SongIterator::~Sci1SongIterator() { -} - - -SongIterator *Sci1SongIterator::clone(int delta) { - Sci1SongIterator *newit = new Sci1SongIterator(*this); - newit->_delayRemaining = delta; - return newit; -} - -int Sci1SongIterator::getTimepos() { - int max = 0; - int i; - - for (i = 0; i < _numChannels; i++) - if (_channels[i].total_timepos > max) - max = _channels[i].total_timepos; - - return max; -} - -/** - * A song iterator with the purpose of sending notes-off channel commands. - */ -class CleanupSongIterator : public SongIterator { -public: - CleanupSongIterator(uint channels) { - channel_mask = channels; - ID = 17; - } - - int nextCommand(byte *buf, int *result); - Audio::AudioStream *getAudioStream() { return NULL; } - SongIterator *handleMessage(Message msg); - int getTimepos() { return 0; } - SongIterator *clone(int delta) { return new CleanupSongIterator(*this); } -}; - -SongIterator *CleanupSongIterator::handleMessage(Message msg) { - if (msg._class == _SIMSG_BASEMSG_PRINT && msg._type == _SIMSG_BASEMSG_PRINT) { - print_tabs_id(msg._arg.i, ID); - debugC(2, kDebugLevelSound, "CLEANUP"); - } - - return NULL; -} - -int CleanupSongIterator::nextCommand(byte *buf, int *result) { - /* Task: Return channel-notes-off for each channel */ - if (channel_mask) { - int bs = sci_ffs(channel_mask) - 1; - - channel_mask &= ~(1 << bs); - buf[0] = 0xb0 | bs; /* Controller */ - buf[1] = SCI_MIDI_CHANNEL_NOTES_OFF; - buf[2] = 0; /* Hmm... */ - *result = 3; - return 0; - } else - return SI_FINISHED; -} - -/**********************/ -/*-- Timer iterator --*/ -/**********************/ -int TimerSongIterator::nextCommand(byte *buf, int *result) { - if (_delta) { - int d = _delta; - _delta = 0; - return d; - } - return SI_FINISHED; -} - -SongIterator *new_timer_iterator(int delta) { - return new TimerSongIterator(delta); -} - -/**********************************/ -/*-- Fast-forward song iterator --*/ -/**********************************/ - -int FastForwardSongIterator::nextCommand(byte *buf, int *result) { - if (_delta <= 0) - return SI_MORPH; /* Did our duty */ - - while (1) { - int rv = _delegate->nextCommand(buf, result); - - if (rv > 0) { - /* Subtract from the delta we want to wait */ - _delta -= rv; - - /* Done */ - if (_delta < 0) - return -_delta; - } - - if (rv <= 0) - return rv; - } -} - -Audio::AudioStream *FastForwardSongIterator::getAudioStream() { - return _delegate->getAudioStream(); -} - -SongIterator *FastForwardSongIterator::handleMessage(Message msg) { - if (msg._class == _SIMSG_PLASTICWRAP) { - assert(msg._type == _SIMSG_PLASTICWRAP_ACK_MORPH); - - if (_delta <= 0) { - SongIterator *it = _delegate; - delete this; - return it; - } - - warning("[ff-iterator] Morphing without need"); - return this; - } - - if (msg._class == _SIMSG_BASE && msg._type == _SIMSG_BASEMSG_PRINT) { - print_tabs_id(msg._arg.i, ID); - debugC(2, kDebugLevelSound, "FASTFORWARD:"); - msg._arg.i++; - } - - // And continue with the delegate - songit_handle_message(&_delegate, msg); - - return NULL; -} - - -int FastForwardSongIterator::getTimepos() { - return _delegate->getTimepos(); -} - -FastForwardSongIterator::FastForwardSongIterator(SongIterator *capsit, int delta) - : _delegate(capsit), _delta(delta) { - - channel_mask = capsit->channel_mask; -} - -SongIterator *FastForwardSongIterator::clone(int delta) { - FastForwardSongIterator *newit = new FastForwardSongIterator(*this); - newit->_delegate = _delegate->clone(delta); - return newit; -} - -SongIterator *new_fast_forward_iterator(SongIterator *capsit, int delta) { - if (capsit == NULL) - return NULL; - - FastForwardSongIterator *it = new FastForwardSongIterator(capsit, delta); - return it; -} - - -/********************/ -/*-- Tee iterator --*/ -/********************/ - - -static void song_iterator_add_death_listener(SongIterator *it, TeeSongIterator *client) { - for (int i = 0; i < SONGIT_MAX_LISTENERS; ++i) { - if (it->_deathListeners[i] == 0) { - it->_deathListeners[i] = client; - return; - } - } - error("FATAL: Too many death listeners for song iterator"); -} - -static void song_iterator_remove_death_listener(SongIterator *it, TeeSongIterator *client) { - for (int i = 0; i < SONGIT_MAX_LISTENERS; ++i) { - if (it->_deathListeners[i] == client) { - it->_deathListeners[i] = 0; - return; - } - } -} - -static void song_iterator_transfer_death_listeners(SongIterator *it, SongIterator *it_from) { - for (int i = 0; i < SONGIT_MAX_LISTENERS; ++i) { - if (it_from->_deathListeners[i]) - song_iterator_add_death_listener(it, it_from->_deathListeners[i]); - it_from->_deathListeners[i] = 0; - } -} - -static void songit_tee_death_notification(TeeSongIterator *self, SongIterator *corpse) { - if (corpse == self->_children[TEE_LEFT].it) { - self->_status &= ~TEE_LEFT_ACTIVE; - self->_children[TEE_LEFT].it = NULL; - } else if (corpse == self->_children[TEE_RIGHT].it) { - self->_status &= ~TEE_RIGHT_ACTIVE; - self->_children[TEE_RIGHT].it = NULL; - } else { - error("songit_tee_death_notification() failed: Breakpoint in %s, line %d", __FILE__, __LINE__); - } -} - -TeeSongIterator::TeeSongIterator(SongIterator *left, SongIterator *right) { - int i; - int firstfree = 1; /* First free channel */ - int incomplete_map = 0; - - _readyToMorph = false; - _status = TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE; - - _children[TEE_LEFT].it = left; - _children[TEE_RIGHT].it = right; - - /* Default to lhs channels */ - channel_mask = left->channel_mask; - for (i = 0; i < 16; i++) - if (channel_mask & (1 << i) & right->channel_mask - && (i != MIDI_RHYTHM_CHANNEL) /* Share rhythm */) { /*conflict*/ - while ((firstfree == MIDI_RHYTHM_CHANNEL) - /* Either if it's the rhythm channel or if it's taken */ - || (firstfree < MIDI_CHANNELS - && ((1 << firstfree) & channel_mask))) - ++firstfree; - - if (firstfree == MIDI_CHANNELS) { - incomplete_map = 1; - //warning("[songit-tee <%08lx,%08lx>] Could not remap right channel #%d: Out of channels", - // left->ID, right->ID, i); - } else { - _children[TEE_RIGHT].it->channel_remap[i] = firstfree; - - channel_mask |= (1 << firstfree); - } - } -#ifdef DEBUG_TEE_ITERATOR - if (incomplete_map) { - int c; - fprintf(stderr, "[songit-tee <%08lx,%08lx>] Channels:" - " %04x <- %04x | %04x\n", - left->ID, right->ID, - channel_mask, - left->channel_mask, right->channel_mask); - for (c = 0 ; c < 2; c++) - for (i = 0 ; i < 16; i++) - fprintf(stderr, " map [%d][%d] -> %d\n", - c, i, _children[c].it->channel_remap[i]); - } -#endif - - - song_iterator_add_death_listener(left, this); - song_iterator_add_death_listener(right, this); -} - -TeeSongIterator::~TeeSongIterator() { - // When we die, remove any listeners from our children - if (_children[TEE_LEFT].it) { - song_iterator_remove_death_listener(_children[TEE_LEFT].it, this); - } - - if (_children[TEE_RIGHT].it) { - song_iterator_remove_death_listener(_children[TEE_RIGHT].it, this); - } -} - - -int TeeSongIterator::nextCommand(byte *buf, int *result) { - static const int ready_masks[2] = {TEE_LEFT_READY, TEE_RIGHT_READY}; - static const int active_masks[2] = {TEE_LEFT_ACTIVE, TEE_RIGHT_ACTIVE}; - static const int pcm_masks[2] = {TEE_LEFT_PCM, TEE_RIGHT_PCM}; - int i; - int retid; - -#ifdef DEBUG_TEE_ITERATOR - fprintf(stderr, "[Tee] %02x\n", _status); -#endif - - if (!(_status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE))) - /* None is active? */ - return SI_FINISHED; - - if (_readyToMorph) - return SI_MORPH; - - if ((_status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE)) - != (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE)) { - /* Not all are is active? */ - int which = 0; -#ifdef DEBUG_TEE_ITERATOR - fprintf(stderr, "\tRequesting transformation...\n"); -#endif - if (_status & TEE_LEFT_ACTIVE) - which = TEE_LEFT; - else if (_status & TEE_RIGHT_ACTIVE) - which = TEE_RIGHT; - memcpy(buf, _children[which].buf, sizeof(buf)); - *result = _children[which].result; - _readyToMorph = true; - return _children[which].retval; - } - - /* First, check for unreported PCMs */ - for (i = TEE_LEFT; i <= TEE_RIGHT; i++) - if ((_status & (ready_masks[i] | pcm_masks[i])) - == (ready_masks[i] | pcm_masks[i])) { - _status &= ~ready_masks[i]; - return SI_PCM; - } - - for (i = TEE_LEFT; i <= TEE_RIGHT; i++) - if (!(_status & ready_masks[i])) { - - /* Buffers aren't ready yet */ - _children[i].retval = - songit_next(&(_children[i].it), - _children[i].buf, - &(_children[i].result), - IT_READER_MASK_ALL - | IT_READER_MAY_FREE - | IT_READER_MAY_CLEAN); - - _status |= ready_masks[i]; -#ifdef DEBUG_TEE_ITERATOR - fprintf(stderr, "\t Must check %d: %d\n", i, _children[i].retval); -#endif - - if (_children[i].retval == SI_ABSOLUTE_CUE || - _children[i].retval == SI_RELATIVE_CUE) - return _children[i].retval; - if (_children[i].retval == SI_FINISHED) { - _status &= ~active_masks[i]; - /* Recurse to complete */ -#ifdef DEBUG_TEE_ITERATOR - fprintf(stderr, "\t Child %d signalled completion, recursing w/ status %02x\n", i, _status); -#endif - return nextCommand(buf, result); - } else if (_children[i].retval == SI_PCM) { - _status |= pcm_masks[i]; - _status &= ~ready_masks[i]; - return SI_PCM; - } - } - - - /* We've already handled PCM, MORPH and FINISHED, CUEs & LOOP remain */ - - retid = TEE_LEFT; - if ((_children[TEE_LEFT].retval > 0) - /* Asked to delay */ - && (_children[TEE_RIGHT].retval <= _children[TEE_LEFT].retval)) - /* Is not delaying or not delaying as much */ - retid = TEE_RIGHT; - -#ifdef DEBUG_TEE_ITERATOR - fprintf(stderr, "\tl:%d / r:%d / chose %d\n", - _children[TEE_LEFT].retval, _children[TEE_RIGHT].retval, retid); -#endif - - /* Adjust delta times */ - if (_children[retid].retval > 0 - && _children[1-retid].retval > 0) { - if (_children[1-retid].retval - == _children[retid].retval) - /* If both _children wait the same amount of time, - ** we have to re-fetch commands from both */ - _status &= ~ready_masks[1-retid]; - else - /* If they don't, we can/must re-use the other - ** child's delay time */ - _children[1-retid].retval - -= _children[retid].retval; - } - - _status &= ~ready_masks[retid]; - memcpy(buf, _children[retid].buf, sizeof(buf)); - *result = _children[retid].result; - - return _children[retid].retval; -} - -Audio::AudioStream *TeeSongIterator::getAudioStream() { - static const int pcm_masks[2] = {TEE_LEFT_PCM, TEE_RIGHT_PCM}; - int i; - - for (i = TEE_LEFT; i <= TEE_RIGHT; i++) - if (_status & pcm_masks[i]) { - _status &= ~pcm_masks[i]; - return _children[i].it->getAudioStream(); - } - - return NULL; // No iterator -} - -SongIterator *TeeSongIterator::handleMessage(Message msg) { - if (msg._class == _SIMSG_PLASTICWRAP) { - assert(msg._type == _SIMSG_PLASTICWRAP_ACK_MORPH); - - SongIterator *old_it; - if (!(_status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE))) { - delete this; - return NULL; - } else if (!(_status & TEE_LEFT_ACTIVE)) { - delete _children[TEE_LEFT].it; - _children[TEE_LEFT].it = 0; - old_it = _children[TEE_RIGHT].it; - song_iterator_remove_death_listener(old_it, this); - song_iterator_transfer_death_listeners(old_it, this); - delete this; - return old_it; - } else if (!(_status & TEE_RIGHT_ACTIVE)) { - delete _children[TEE_RIGHT].it; - _children[TEE_RIGHT].it = 0; - old_it = _children[TEE_LEFT].it; - song_iterator_remove_death_listener(old_it, this); - song_iterator_transfer_death_listeners(old_it, this); - delete this; - return old_it; - } - - warning("[tee-iterator] Morphing without need"); - return this; - } - - if (msg._class == _SIMSG_BASE && msg._type == _SIMSG_BASEMSG_PRINT) { - print_tabs_id(msg._arg.i, ID); - debugC(2, kDebugLevelSound, "TEE:"); - msg._arg.i++; - } - - // And continue with the children - if (_children[TEE_LEFT].it) - songit_handle_message(&(_children[TEE_LEFT].it), msg); - if (_children[TEE_RIGHT].it) - songit_handle_message(&(_children[TEE_RIGHT].it), msg); - - return NULL; -} - -void TeeSongIterator::init() { - _status = TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE; - _children[TEE_LEFT].it->init(); - _children[TEE_RIGHT].it->init(); -} - -SongIterator *TeeSongIterator::clone(int delta) { - TeeSongIterator *newit = new TeeSongIterator(*this); - - if (_children[TEE_LEFT].it) - newit->_children[TEE_LEFT].it = _children[TEE_LEFT].it->clone(delta); - if (_children[TEE_RIGHT].it) - newit->_children[TEE_RIGHT].it = _children[TEE_RIGHT].it->clone(delta); - - return newit; -} - - -/*************************************/ -/*-- General purpose functionality --*/ -/*************************************/ - -int songit_next(SongIterator **it, byte *buf, int *result, int mask) { - int retval; - - if (!*it) - return SI_FINISHED; - - do { - retval = (*it)->nextCommand(buf, result); - if (retval == SI_MORPH) { - debugC(2, kDebugLevelSound, " Morphing %p (stored at %p)", (void *)*it, (void *)it); - if (!SIMSG_SEND((*it), SIMSG_ACK_MORPH)) { - error("SI_MORPH failed. Breakpoint in %s, line %d", __FILE__, __LINE__); - } else - debugC(2, kDebugLevelSound, "SI_MORPH successful"); - } - - if (retval == SI_FINISHED) - debugC(2, kDebugLevelSound, "[song-iterator] Song finished. mask = %04x, cm=%04x", - mask, (*it)->channel_mask); - if (retval == SI_FINISHED - && (mask & IT_READER_MAY_CLEAN) - && (*it)->channel_mask) { /* This last test will fail - ** with a terminated - ** cleanup iterator */ - int channel_mask = (*it)->channel_mask; - - SongIterator *old_it = *it; - *it = new CleanupSongIterator(channel_mask); - for(uint i = 0; i < MIDI_CHANNELS; i++) - (*it)->channel_remap[i] = old_it->channel_remap[i]; - song_iterator_transfer_death_listeners(*it, old_it); - if (mask & IT_READER_MAY_FREE) - delete old_it; - retval = -9999; /* Continue */ - } - } while (!( /* Until one of the following holds */ - (retval > 0 && (mask & IT_READER_MASK_DELAY)) - || (retval == 0 && (mask & IT_READER_MASK_MIDI)) - || (retval == SI_LOOP && (mask & IT_READER_MASK_LOOP)) - || (retval == SI_ABSOLUTE_CUE && - (mask & IT_READER_MASK_CUE)) - || (retval == SI_RELATIVE_CUE && - (mask & IT_READER_MASK_CUE)) - || (retval == SI_PCM && (mask & IT_READER_MASK_PCM)) - || (retval == SI_FINISHED) - )); - - if (retval == SI_FINISHED && (mask & IT_READER_MAY_FREE)) { - delete *it; - *it = NULL; - } - - return retval; -} - -SongIterator::SongIterator() { - ID = 0; - channel_mask = 0; - fade.action = FADE_ACTION_NONE; - priority = 0; - memset(_deathListeners, 0, sizeof(_deathListeners)); - - // By default, don't remap - for (uint i = 0; i < 16; i++) - channel_remap[i] = i; -} - -SongIterator::SongIterator(const SongIterator &si) { - ID = si.ID; - channel_mask = si.channel_mask; - fade = si.fade; - priority = si.priority; - memset(_deathListeners, 0, sizeof(_deathListeners)); - - for (uint i = 0; i < 16; i++) - channel_remap[i] = si.channel_remap[i]; -} - - -SongIterator::~SongIterator() { - for (int i = 0; i < SONGIT_MAX_LISTENERS; ++i) - if (_deathListeners[i]) - songit_tee_death_notification(_deathListeners[i], this); -} - -SongIterator *songit_new(byte *data, uint size, SongIteratorType type, songit_id_t id) { - BaseSongIterator *it; - - if (!data || size < 22) { - warning("Attempt to instantiate song iterator for null song data"); - return NULL; - } - - - switch (type) { - case SCI_SONG_ITERATOR_TYPE_SCI0: - it = new Sci0SongIterator(data, size, id); - break; - - case SCI_SONG_ITERATOR_TYPE_SCI1: - it = new Sci1SongIterator(data, size, id); - break; - - default: - /**-- Invalid/unsupported sound resources --**/ - warning("Attempt to instantiate invalid/unknown song iterator type %d", type); - return NULL; - } - - return it; -} - -int songit_handle_message(SongIterator **it_reg_p, SongIterator::Message msg) { - SongIterator *it = *it_reg_p; - SongIterator *newit; - - newit = it->handleMessage(msg); - - if (!newit) - return 0; /* Couldn't handle */ - - *it_reg_p = newit; /* Might have self-morphed */ - return 1; -} - -SongIterator *sfx_iterator_combine(SongIterator *it1, SongIterator *it2) { - if (it1 == NULL) - return it2; - if (it2 == NULL) - return it1; - - /* Both are non-NULL: */ - return new TeeSongIterator(it1, it2); -} - -} // End of namespace Sci - -#endif // USE_OLD_MUSIC_FUNCTIONS diff --git a/engines/sci/sound/iterator/iterator.h b/engines/sci/sound/iterator/iterator.h deleted file mode 100644 index e5c8f50702..0000000000 --- a/engines/sci/sound/iterator/iterator.h +++ /dev/null @@ -1,326 +0,0 @@ -/* 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$ - * - */ - -/* Song iterator declarations */ - -#ifndef SCI_SFX_SFX_ITERATOR_H -#define SCI_SFX_SFX_ITERATOR_H - -#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS - -#ifdef USE_OLD_MUSIC_FUNCTIONS -#include "sci/sound/drivers/mididriver.h" - -namespace Audio { - class AudioStream; -} - -namespace Sci { - -enum SongIteratorStatus { - SI_FINISHED = -1, /**< Song finished playing */ - SI_LOOP = -2, /**< Song just looped */ - SI_ABSOLUTE_CUE = -3, /**< Found a song cue (absolute) */ - SI_RELATIVE_CUE = -4, /**< Found a song cue (relative) */ - SI_PCM = -5, /**< Found a PCM */ - SI_IGNORE = -6, /**< This event got edited out by the remapper */ - SI_MORPH = -255 /**< Song iterator requested self-morph. */ -}; - -#define FADE_ACTION_NONE 0 -#define FADE_ACTION_FADE_AND_STOP 1 -#define FADE_ACTION_FADE_AND_CONT 2 - -struct fade_params_t { - int ticks_per_step; - int final_volume; - int step_size; - int action; -}; - -/* Helper defs for messages */ -enum { - _SIMSG_BASE, /* Any base decoder */ - _SIMSG_PLASTICWRAP /* Any "Plastic" (discardable) wrapper decoder */ -}; - -/* Base messages */ -enum { - _SIMSG_BASEMSG_SET_LOOPS, /* Set loops */ - _SIMSG_BASEMSG_SET_PLAYMASK, /* Set the current playmask for filtering */ - _SIMSG_BASEMSG_SET_RHYTHM, /* Activate/deactivate rhythm channel */ - _SIMSG_BASEMSG_ACK_MORPH, /* Acknowledge self-morph */ - _SIMSG_BASEMSG_STOP, /* Stop iterator */ - _SIMSG_BASEMSG_PRINT, /* Print self to stderr, after printing param1 tabs */ - _SIMSG_BASEMSG_SET_HOLD, /* Set value of hold parameter to expect */ - _SIMSG_BASEMSG_SET_FADE /* Set fade parameters */ -}; - -/* "Plastic" (discardable) wrapper messages */ -enum { - _SIMSG_PLASTICWRAP_ACK_MORPH = _SIMSG_BASEMSG_ACK_MORPH /* Acknowledge self-morph */ -}; - -/* Messages */ -#define SIMSG_SET_LOOPS(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_LOOPS,(x) -#define SIMSG_SET_PLAYMASK(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_PLAYMASK,(x) -#define SIMSG_SET_RHYTHM(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_RHYTHM,(x) -#define SIMSG_ACK_MORPH _SIMSG_PLASTICWRAP,_SIMSG_PLASTICWRAP_ACK_MORPH,0 -#define SIMSG_STOP _SIMSG_BASE,_SIMSG_BASEMSG_STOP,0 -#define SIMSG_PRINT(indentation) _SIMSG_BASE,_SIMSG_BASEMSG_PRINT,(indentation) -#define SIMSG_SET_HOLD(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_HOLD,(x) - -/* Message transmission macro: Takes song reference, message reference */ -#define SIMSG_SEND(o, m) songit_handle_message(&(o), SongIterator::Message((o)->ID, m)) -#define SIMSG_SEND_FADE(o, m) songit_handle_message(&(o), SongIterator::Message((o)->ID, _SIMSG_BASE, _SIMSG_BASEMSG_SET_FADE, m)) - -typedef unsigned long songit_id_t; - - -#define SONGIT_MAX_LISTENERS 2 - -class TeeSongIterator; - -class SongIterator { -public: - struct Message { - songit_id_t ID; - uint _class; /* Type of iterator supposed to receive this */ - uint _type; - union { - uint i; - void *p; - } _arg; - - Message() : ID(0), _class(0xFFFF), _type(0xFFFF) {} - - /** - * Create a song iterator message. - * - * @param id: song ID the message is targeted to - * @param recipient_class: Message recipient class - * @param type message type - * @param a argument - * - * @note You should only use this with the SIMSG_* macros - */ - Message(songit_id_t id, int recipient_class, int type, int a) - : ID(id), _class(recipient_class), _type(type) { - _arg.i = a; - } - - /** - * Create a song iterator message, wherein the first parameter is a pointer. - * - * @param id: song ID the message is targeted to - * @param recipient_class: Message recipient class - * @param type message type - * @param a argument - * - * @note You should only use this with the SIMSG_* macros - */ - Message(songit_id_t id, int recipient_class, int type, void *a) - : ID(id), _class(recipient_class), _type(type) { - _arg.p = a; - } - }; - -public: - songit_id_t ID; - uint16 channel_mask; /* Bitmask of all channels this iterator will use */ - fade_params_t fade; - int priority; - - /* Death listeners */ - /* These are not reset during initialisation */ - TeeSongIterator *_deathListeners[SONGIT_MAX_LISTENERS]; - - /* See songit_* for the constructor and non-virtual member functions */ - - byte channel_remap[MIDI_CHANNELS]; ///< Remapping for channels - -public: - SongIterator(); - SongIterator(const SongIterator &); - virtual ~SongIterator(); - - /** - * Resets/initializes the sound iterator. - */ - virtual void init() {} - - /** - * Reads the next MIDI operation _or_ delta time. - * @param buf The buffer to write to (needs to be able to store at least 4 bytes) - * @param result Number of bytes written to the buffer - * (equals the number of bytes that need to be passed - * to the lower layers) for 0, the cue value for SI_CUE, - * or the number of loops remaining for SI_LOOP. - * @return zero if a MIDI operation was written, SI_FINISHED - * if the song has finished playing, SI_LOOP if looping - * (after updating the loop variable), SI_CUE if we found - * a cue, SI_PCM if a PCM was found, or the number of ticks - * to wait before this function should be called next. - * - * @note If SI_PCM is returned, get_pcm() may be used to retrieve the associated - * PCM, but this must be done before any subsequent calls to next(). - * - * @todo The actual buffer size should either be specified or passed in, so that - * we can detect buffer overruns. - */ - virtual int nextCommand(byte *buf, int *result) = 0; - - /** - Checks for the presence of a pcm sample. - * @return NULL if no PCM data was found, an AudioStream otherwise. - */ - virtual Audio::AudioStream *getAudioStream() = 0; - - /** - * Handles a message to the song iterator. - * @param msg the message to handle - * @return NULL if the message was not understood, - * this if the message could be handled, or a new song iterator - * if the current iterator had to be morphed (but the message could - * still be handled) - * - * @note This function is not supposed to be called directly; use - * songit_handle_message() instead. It should not recurse, since songit_handle_message() - * takes care of that and makes sure that its delegate received the message (and - * was morphed) before self. - */ - virtual SongIterator *handleMessage(Message msg) = 0; - - /** - * Gets the song position to store in a savegame. - */ - virtual int getTimepos() = 0; - - /** - * Clone this song iterator. - * @param delta number of ticks that still need to elapse until the - * next item should be read from the song iterator - */ - virtual SongIterator *clone(int delta) = 0; - - -private: - // Make the assignment operator unreachable, just in case... - SongIterator& operator=(const SongIterator&); -}; - - -/********************************/ -/*-- Song iterator operations --*/ -/********************************/ - -enum SongIteratorType { - SCI_SONG_ITERATOR_TYPE_SCI0 = 0, - SCI_SONG_ITERATOR_TYPE_SCI1 = 1 -}; - -#define IT_READER_MASK_MIDI (1 << 0) -#define IT_READER_MASK_DELAY (1 << 1) -#define IT_READER_MASK_LOOP (1 << 2) -#define IT_READER_MASK_CUE (1 << 3) -#define IT_READER_MASK_PCM (1 << 4) -#define IT_READER_MAY_FREE (1 << 10) /* Free SI_FINISHED iterators */ -#define IT_READER_MAY_CLEAN (1 << 11) -/* MAY_CLEAN: May instantiate cleanup iterators -** (use for players; this closes open channels at the end of a song) */ - -#define IT_READER_MASK_ALL ( IT_READER_MASK_MIDI \ - | IT_READER_MASK_DELAY \ - | IT_READER_MASK_LOOP \ - | IT_READER_MASK_CUE \ - | IT_READER_MASK_PCM ) - -/* Convenience wrapper around it->next -** Parameters: (SongIterator **it) Reference to the iterator to access -** (byte *) buf: The buffer to write to (needs to be able to -** store at least 4 bytes) -** (int) mask: IT_READER_MASK options specifying the events to -** listen for -** Returns : (int) zero if a MIDI operation was written, SI_FINISHED -** if the song has finished playing, SI_LOOP if looping -** (after updating the loop variable), SI_CUE if we found -** a cue, SI_PCM if a PCM was found, or the number of ticks -** to wait before this function should be called next. -** (int) *result: Number of bytes written to the buffer -** (equals the number of bytes that need to be passed -** to the lower layers) for 0, the cue value for SI_CUE, -** or the number of loops remaining for SI_LOOP. -*/ -int songit_next(SongIterator **it, byte *buf, int *result, int mask); - -/* Constructs a new song iterator object -** Parameters: (byte *) data: The song data to iterate over -** (uint) size: Number of bytes in the song -** (int) type: One of the SCI_SONG_ITERATOR_TYPEs -** (songit_id_t) id: An ID for addressing the song iterator -** Returns : (SongIterator *) A newly allocated but uninitialized song -** iterator, or NULL if 'type' was invalid or unsupported -*/ -SongIterator *songit_new(byte *data, uint size, SongIteratorType type, songit_id_t id); - -/* Constructs a new song timer iterator object -** Parameters: (int) delta: The delta after which to fire SI_FINISHED -** Returns : (SongIterator *) A newly allocated but uninitialized song -** iterator -*/ -SongIterator *new_timer_iterator(int delta); - -/* Handles a message to the song iterator -** Parameters: (SongIterator **): A reference to the variable storing the song iterator -** Returns : (int) Non-zero if the message was understood -** The song iterator may polymorph as result of msg, so a writeable reference is required. -*/ -int songit_handle_message(SongIterator **it_reg, SongIterator::Message msg); - - -/* Creates a new song iterator which fast-forwards -** Parameters: (SongIterator *) it: The iterator to wrap -** (int) delta: The number of ticks to skip -** Returns : (SongIterator) A newly created song iterator -** which skips all delta times -** until 'delta' has been used up -*/ -SongIterator *new_fast_forward_iterator(SongIterator *it, int delta); - -/* Combines two song iterators into one -** Parameters: (sfx_iterator_t *) it1: One of the two iterators, or NULL -** (sfx_iterator_t *) it2: The other iterator, or NULL -** Returns : (sfx_iterator_t *) A combined iterator -** If a combined iterator is returned, it will be flagged to be allowed to -** dispose of 'it1' and 'it2', where applicable. This means that this -** call should be used by song players, but not by the core sound system -*/ -SongIterator *sfx_iterator_combine(SongIterator *it1, SongIterator *it2); - -} // End of namespace Sci - -#endif // USE_OLD_MUSIC_FUNCTIONS - -#endif // SCI_SFX_SFX_ITERATOR_H diff --git a/engines/sci/sound/iterator/iterator_internal.h b/engines/sci/sound/iterator/iterator_internal.h deleted file mode 100644 index 5a0f0d3ec9..0000000000 --- a/engines/sci/sound/iterator/iterator_internal.h +++ /dev/null @@ -1,276 +0,0 @@ -/* 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$ - * - */ - -#ifndef SCI_SFX_SFX_ITERATOR_INTERNAL -#define SCI_SFX_SFX_ITERATOR_INTERNAL - -#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS - -#ifdef USE_OLD_MUSIC_FUNCTIONS -#include "sci/sound/iterator/iterator.h" -#include "sci/sound/drivers/mididriver.h" - -#include "common/array.h" -#include "common/list.h" - -namespace Sci { - -/* Iterator types */ - -enum { - SI_STATE_UNINITIALISED = -1, - SI_STATE_DELTA_TIME = 0, ///< Now at a delta time - SI_STATE_COMMAND = 1, ///< Now at a MIDI operation - SI_STATE_PENDING = 2, ///< Pending for loop - SI_STATE_FINISHED = 3, ///< End of song - SI_STATE_PCM = 4, ///< Should report a PCM next (-> DELTA_TIME) - SI_STATE_PCM_MAGIC_DELTA = 5 ///< Should report a ``magic'' one tick delta time next (goes on to FINISHED) -}; - -struct SongIteratorChannel { - - int state; ///< State of this song iterator channel - int offset; ///< Offset into the data chunk */ - int end; ///< Last allowed byte in track */ - int id; ///< Some channel ID */ - - /** - * Number of ticks before the specified channel is next used, or - * CHANNEL_DELAY_MISSING to indicate that the delay has not yet - * been read. - */ - int delay; - - /* Two additional offsets for recovering: */ - int loop_offset; - int initial_offset; - - int playmask; ///< Active playmask (MIDI channels to play in here) */ - int loop_timepos; ///< Total delay for this channel's loop marker */ - int total_timepos; ///< Number of ticks since the beginning, ignoring loops */ - int timepos_increment; ///< Number of ticks until the next command (to add) */ - - byte last_cmd; ///< Last operation executed, for running status */ - -public: - void init(int id, int offset, int end); - void resetSynthChannels(); -}; - -class BaseSongIterator : public SongIterator { -public: - int _polyphony[MIDI_CHANNELS]; ///< # of simultaneous notes on each - - int _ccc; ///< Cumulative cue counter, for those who need it - byte _resetflag; ///< for 0x4C -- on DoSound StopSound, do we return to start? - int _deviceId; ///< ID of the device we generating events for - int _numActiveChannels; ///< Number of active channels - Common::Array<byte> _data; ///< Song data - - int _loops; ///< Number of loops remaining - -public: - BaseSongIterator(byte *data, uint size, songit_id_t id); - -protected: - int parseMidiCommand(byte *buf, int *result, SongIteratorChannel *channel, int flags); - int processMidi(byte *buf, int *result, SongIteratorChannel *channel, int flags); -}; - -/********************************/ -/*--------- SCI 0 --------------*/ -/********************************/ - -class Sci0SongIterator : public BaseSongIterator { -public: - SongIteratorChannel _channel; - -public: - Sci0SongIterator(byte *data, uint size, songit_id_t id); - - int nextCommand(byte *buf, int *result); - Audio::AudioStream *getAudioStream(); - SongIterator *handleMessage(Message msg); - void init(); - int getTimepos(); - SongIterator *clone(int delta); -}; - - -/********************************/ -/*--------- SCI 1 --------------*/ -/********************************/ - - -struct Sci1Sample { - /** - * Time left-- initially, this is 'Sample point 1'. - * After initialisation, it is 'sample point 1 minus the sample - * point of the previous sample' - */ - int delta; - int size; - bool announced; /* Announced for download (SI_PCM) */ - int rate; - byte *_data; -}; - -class Sci1SongIterator : public BaseSongIterator { -public: - SongIteratorChannel _channels[MIDI_CHANNELS]; - - /* Invariant: Whenever channels[i].delay == CHANNEL_DELAY_MISSING, - ** channel_offset[i] points to a delta time object. */ - - bool _initialised; /**!< Whether the MIDI channel setup has been initialised */ - int _numChannels; /**!< Number of channels actually used */ - Common::List<Sci1Sample> _samples; - int _numLoopedChannels; /**!< Number of channels that are ready to loop */ - - int _delayRemaining; /**!< Number of ticks that haven't been polled yet */ - int _hold; - -public: - Sci1SongIterator(byte *data, uint size, songit_id_t id); - ~Sci1SongIterator(); - - int nextCommand(byte *buf, int *result); - Audio::AudioStream *getAudioStream(); - SongIterator *handleMessage(Message msg); - void init(); - int getTimepos(); - SongIterator *clone(int delta); - -private: - int initSample(const int offset); - int initSong(); - - int getSmallestDelta() const; - - void updateDelta(int delta); - - /** Checks that none of the channels is waiting for its delta to be read */ - bool noDeltaTime() const; - - /** Determine the channel # of the next active event, or -1 */ - int getCommandIndex() const; -}; - -#define PLAYMASK_NONE 0x0 - -/***************************/ -/*--------- Timer ---------*/ -/***************************/ - -/** - * A song iterator which waits a specified time and then fires - * SI_FINISHED. Used by DoSound, where audio resources are played (SCI1) - */ -class TimerSongIterator : public SongIterator { -protected: - int _delta; /**!< Remaining time */ - -public: - TimerSongIterator(int delta) : _delta(delta) {} - - int nextCommand(byte *buf, int *result); - Audio::AudioStream *getAudioStream() { return NULL; } - SongIterator *handleMessage(Message msg) { return NULL; } - int getTimepos() { return 0; } - SongIterator *clone(int delta) { return new TimerSongIterator(*this); } -}; - -/**********************************/ -/*--------- Fast Forward ---------*/ -/**********************************/ - -/** - * A song iterator which fast-forwards another iterator. - * Skips all delta times until a specified 'delta' has been used up. - */ -class FastForwardSongIterator : public SongIterator { -protected: - SongIterator *_delegate; - int _delta; /**!< Remaining time */ - -public: - FastForwardSongIterator(SongIterator *capsit, int delta); - - int nextCommand(byte *buf, int *result); - Audio::AudioStream *getAudioStream(); - SongIterator *handleMessage(Message msg); - int getTimepos(); - SongIterator *clone(int delta); -}; - - -/**********************************/ -/*--------- Tee iterator ---------*/ -/**********************************/ - -enum { - TEE_LEFT = 0, - TEE_RIGHT = 1, - TEE_LEFT_ACTIVE = (1<<0), - TEE_RIGHT_ACTIVE = (1<<1), - TEE_LEFT_READY = (1<<2), /**!< left result is ready */ - TEE_RIGHT_READY = (1<<3), /**!< right result is ready */ - TEE_LEFT_PCM = (1<<4), - TEE_RIGHT_PCM = (1<<5) -}; - -/** - * This iterator combines two iterators, returns the next event available from either. - */ -class TeeSongIterator : public SongIterator { -public: - int _status; - - bool _readyToMorph; /**!< One of TEE_MORPH_* above */ - - struct { - SongIterator *it; - byte buf[4]; - int result; - int retval; - } _children[2]; - -public: - TeeSongIterator(SongIterator *left, SongIterator *right); - ~TeeSongIterator(); - - int nextCommand(byte *buf, int *result); - Audio::AudioStream *getAudioStream(); - SongIterator *handleMessage(Message msg); - void init(); - int getTimepos() { return 0; } - SongIterator *clone(int delta); -}; - -} // End of namespace Sci - -#endif // USE_OLD_MUSIC_FUNCTIONS - -#endif // SCI_SFX_SFX_ITERATOR_INTERNAL diff --git a/engines/sci/sound/iterator/songlib.cpp b/engines/sci/sound/iterator/songlib.cpp deleted file mode 100644 index 8bc2e8f476..0000000000 --- a/engines/sci/sound/iterator/songlib.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* 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/core.h" -#include "sci/sound/iterator/iterator.h" - -namespace Sci { - -#define debug_stream stderr - -Song::Song() : _wakeupTime(0, SFX_TICKS_PER_SEC) { - _handle = 0; - _resourceNum = 0; - _priority = 0; - _status = SOUND_STATUS_STOPPED; - - _restoreBehavior = RESTORE_BEHAVIOR_CONTINUE; - _restoreTime = 0; - - _loops = 0; - _hold = 0; - - _it = 0; - _delay = 0; - - _next = NULL; - _nextPlaying = NULL; - _nextStopping = NULL; -} - -Song::Song(SongHandle handle, SongIterator *it, int priority) : _wakeupTime(0, SFX_TICKS_PER_SEC) { - _handle = handle; - _resourceNum = 0; - _priority = priority; - _status = SOUND_STATUS_STOPPED; - - _restoreBehavior = RESTORE_BEHAVIOR_CONTINUE; - _restoreTime = 0; - - _loops = 0; - _hold = 0; - - _it = it; - _delay = 0; - - _next = NULL; - _nextPlaying = NULL; - _nextStopping = NULL; -} - -void SongLibrary::addSong(Song *song) { - Song **seeker = NULL; - int pri = song->_priority; - - if (NULL == song) { - warning("addSong(): NULL passed for song"); - return; - } - - seeker = &_lib; - while (*seeker && ((*seeker)->_priority > pri)) - seeker = &((*seeker)->_next); - - song->_next = *seeker; - *seeker = song; -} - -void SongLibrary::freeSounds() { - Song *next = _lib; - while (next) { - Song *song = next; - delete song->_it; - song->_it = NULL; - next = song->_next; - delete song; - } - _lib = NULL; -} - - -Song *SongLibrary::findSong(SongHandle handle) { - Song *seeker = _lib; - - while (seeker) { - if (seeker->_handle == handle) - break; - seeker = seeker->_next; - } - - return seeker; -} - -Song *SongLibrary::findNextActive(Song *other) { - Song *seeker = other ? other->_next : _lib; - - while (seeker) { - if ((seeker->_status == SOUND_STATUS_WAITING) || - (seeker->_status == SOUND_STATUS_PLAYING)) - break; - seeker = seeker->_next; - } - - /* Only return songs that have equal priority */ - if (other && seeker && other->_priority > seeker->_priority) - return NULL; - - return seeker; -} - -Song *SongLibrary::findFirstActive() { - return findNextActive(NULL); -} - -int SongLibrary::removeSong(SongHandle handle) { - int retval; - Song *goner = _lib; - - if (!goner) - return -1; - - if (goner->_handle == handle) - _lib = goner->_next; - - else { - while ((goner->_next) && (goner->_next->_handle != handle)) - goner = goner->_next; - - if (goner->_next) { /* Found him? */ - Song *oldnext = goner->_next; - - goner->_next = goner->_next->_next; - goner = oldnext; - } else return -1; /* No. */ - } - - retval = goner->_status; - - delete goner->_it; - delete goner; - - return retval; -} - -int SongLibrary::countSongs() { - Song *seeker = _lib; - int retval = 0; - - while (seeker) { - retval++; - seeker = seeker->_next; - } - - return retval; -} - -void SongLibrary::setSongRestoreBehavior(SongHandle handle, RESTORE_BEHAVIOR action) { - Song *seeker = findSong(handle); - - seeker->_restoreBehavior = action; -} - -} // End of namespace Sci - -#endif // USE_OLD_MUSIC_FUNCTIONS diff --git a/engines/sci/sound/iterator/songlib.h b/engines/sci/sound/iterator/songlib.h deleted file mode 100644 index acb704edaa..0000000000 --- a/engines/sci/sound/iterator/songlib.h +++ /dev/null @@ -1,171 +0,0 @@ -/* 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$ - * - */ - -/* Song library */ - -#ifndef SCI_SFX_SFX_SONGLIB_H -#define SCI_SFX_SFX_SONGLIB_H - -#include "common/scummsys.h" -#include "sound/timestamp.h" - -#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS -#ifdef USE_OLD_MUSIC_FUNCTIONS - -namespace Sci { - -class SongIterator; - -#define SOUND_STATUS_STOPPED 0 -#define SOUND_STATUS_PLAYING 1 -#define SOUND_STATUS_SUSPENDED 2 -/* suspended: only if ordered from kernel space */ -#define SOUND_STATUS_WAITING 3 -/* "waiting" means "tagged for playing, but not active right now" */ - -typedef unsigned long SongHandle; - -enum RESTORE_BEHAVIOR { - RESTORE_BEHAVIOR_CONTINUE, /* restart a song when restored from - a saved game */ - RESTORE_BEHAVIOR_RESTART /* continue it from where it was */ -}; - -class Song { -public: - SongHandle _handle; - int _resourceNum; /**<! Resource number */ - int _priority; /**!< Song priority (more important if priority is higher) */ - int _status; /* See above */ - - int _restoreBehavior; - int _restoreTime; - - /* Grabbed from the sound iterator, for save/restore purposes */ - int _loops; - int _hold; - - SongIterator *_it; - int _delay; /**!< Delay before accessing the iterator, in ticks */ - - Audio::Timestamp _wakeupTime; /**!< Timestamp indicating the next MIDI event */ - - Song *_next; /**!< Next song or NULL if this is the last one */ - - /** - * Next playing song. Used by the core song system. - */ - Song *_nextPlaying; - - /** - * Next song pending stopping. Used exclusively by the core song system's - * _update_multi_song() - */ - Song *_nextStopping; - -public: - - Song(); - - /** - * Initializes a new song. - * @param handle the sound handle - * @param it the song - * @param priority the song's priority - * @return a freshly allocated song - */ - Song(SongHandle handle, SongIterator *it, int priority); -}; - - -class SongLibrary { -public: - Song *_lib; - -public: - SongLibrary() : _lib(0) {} - - /** Frees a song library. */ - void freeSounds(); - - /** - * Adds a song to a song library. - * @param song song to add - */ - void addSong(Song *song); - - /** - * Looks up the song with the specified handle. - * @param handle sound handle to look for - * @return the song or NULL if it wasn't found - */ - Song *findSong(SongHandle handle); - - /** - * Finds the first song playing with the highest priority. - * @return the song that should be played next, or NULL if there is none - */ - Song *findFirstActive(); - - /** - * Finds the next song playing with the highest priority. - * - * The functions 'findFirstActive' and 'findNextActive' - * allow to iterate over all songs that satisfy the requirement of - * being 'playable'. - * - * @param song a song previously returned from the song library - * @return the next song to play relative to 'song', or NULL if none are left - */ - Song *findNextActive(Song *song); - - /** - * Removes a song from the library. - * @param handle handle of the song to remove - * @return the status of the song that was removed - */ - int removeSong(SongHandle handle); - - /** - * Counts the number of songs in a song library. - * @return the number of songs - */ - int countSongs(); - - /** - * Determines what should be done with the song "handle" when restoring - * it from a saved game. - * @param handle sound handle being restored - * @param action desired action - */ - void setSongRestoreBehavior(SongHandle handle, - RESTORE_BEHAVIOR action); -}; - -} // End of namespace Sci - -#endif // USE_OLD_MUSIC_FUNCTIONS - -#endif // SCI_SSFX_SFX_SONGLIB_H diff --git a/engines/sci/sound/iterator/test-iterator.cpp b/engines/sci/sound/iterator/test-iterator.cpp deleted file mode 100644 index 0d603a89fd..0000000000 --- a/engines/sci/sound/iterator/test-iterator.cpp +++ /dev/null @@ -1,423 +0,0 @@ -/* 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 "iterator.h" -#include "iterator_internal.h" -#include <stdarg.h> -#include <stdio.h> - -using namespace Sci; - -#define ASSERT_S(x) if (!(x)) { error("Failed assertion in L%d: " #x, __LINE__); return; } -#define ASSERT(x) ASSERT_S(x) - -/* Tests the song iterators */ - -int errors = 0; - -void error(char *fmt, ...) { - va_list ap; - - fprintf(stderr, "[ERROR] "); - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - - ++errors; -} - - -/* The simple iterator will finish after a fixed amount of time. Before that, -** it emits (absolute) cues in ascending order. */ -struct simple_iterator : public SongIterator { - int lifetime_remaining; - char *cues; - int cue_counter; - int cue_progress; - int cues_nr; -}; - -int simple_it_next(SongIterator *_self, unsigned char *buf, int *result) { - simple_iterator *self = (simple_iterator *)_self; - - if (self->lifetime_remaining == -1) { - error("Song iterator called post mortem"); - return SI_FINISHED; - } - - if (self->lifetime_remaining) { - - if (self->cue_counter < self->cues_nr) { - int time_to_cue = self->cues[self->cue_counter]; - - if (self->cue_progress == time_to_cue) { - ++self->cue_counter; - self->cue_progress = 0; - *result = self->cue_counter; - return SI_ABSOLUTE_CUE; - } else { - int retval = time_to_cue - self->cue_progress; - self->cue_progress = time_to_cue; - - if (retval > self->lifetime_remaining) { - retval = self->lifetime_remaining; - self->lifetime_remaining = 0; - self->cue_progress = retval; - return retval; - } - - self->lifetime_remaining -= retval; - return retval; - } - } else { - int retval = self->lifetime_remaining; - self->lifetime_remaining = 0; - return retval; - } - - } else { - self->lifetime_remaining = -1; - return SI_FINISHED; - } -} - -Audio::AudioStream *simple_it_pcm_feed(SongIterator *_self) { - error("No PCM feed"); - return NULL; -} - -void simple_it_init(SongIterator *_self) { -} - -SongIterator *simple_it_handle_message(SongIterator *_self, SongIterator::Message msg) { - return NULL; -} - -void simple_it_cleanup(SongIterator *_self) { -} - -/* Initialises the simple iterator. -** Parameters: (int) delay: Number of ticks until the iterator finishes -** (int *) cues: An array of cue delays (cue values are [1,2...]) -** (int) cues_nr: Number of cues in ``cues'' -** The first cue is emitted after cues[0] ticks, and it is 1. After cues[1] additional ticks -** the next cue is emitted, and so on. */ -SongIterator *setup_simple_iterator(int delay, char *cues, int cues_nr) { - simple_iterator.lifetime_remaining = delay; - simple_iterator.cues = cues; - simple_iterator.cue_counter = 0; - simple_iterator.cues_nr = cues_nr; - simple_iterator.cue_progress = 0; - - simple_iterator.ID = 42; - simple_iterator.channel_mask = 0x004f; - simple_iterator.flags = 0; - simple_iterator.priority = 1; - - simple_iterator.death_listeners_nr = 0; - - simple_iterator.cleanup = simple_it_cleanup; - simple_iterator.init = simple_it_init; - simple_iterator.handle_message = simple_it_handle_message; - simple_iterator.get_pcm_feed = simple_it_pcm_feed; - simple_iterator.next = simple_it_next; - - return (SongIterator *) &simple_iterator; -} - -#define ASSERT_SIT ASSERT(it == simple_it) -#define ASSERT_FFIT ASSERT(it == ff_it) -#define ASSERT_NEXT(n) ASSERT(songit_next(&it, data, &result, IT_READER_MASK_ALL) == n) -#define ASSERT_RESULT(n) ASSERT(result == n) -#define ASSERT_CUE(n) ASSERT_NEXT(SI_ABSOLUTE_CUE); ASSERT_RESULT(n) - -void test_simple_it() { - SongIterator *it; - SongIterator *simple_it = (SongIterator *) & simple_iterator; - unsigned char data[4]; - int result; - puts("[TEST] simple iterator (test artifact)"); - - it = setup_simple_iterator(42, NULL, 0); - - ASSERT_SIT; - ASSERT_NEXT(42); - ASSERT_SIT; - ASSERT_NEXT(SI_FINISHED); - ASSERT_SIT; - - it = setup_simple_iterator(42, "\003\004", 2); - ASSERT_SIT; - ASSERT_NEXT(3); - ASSERT_CUE(1); - ASSERT_SIT; - ASSERT_NEXT(4); - ASSERT_CUE(2); - ASSERT_SIT; -// warning("XXX => %d", songit_next(&it, data, &result, IT_READER_MASK_ALL)); - ASSERT_NEXT(35); - ASSERT_NEXT(SI_FINISHED); - ASSERT_SIT; - - puts("[TEST] Test OK."); -} - -void test_fastforward() { - SongIterator *it; - SongIterator *simple_it = (SongIterator *) & simple_iterator; - SongIterator *ff_it; - unsigned char data[4]; - int result; - puts("[TEST] fast-forward iterator"); - - it = setup_simple_iterator(42, NULL, 0); - ff_it = it = new_fast_forward_iterator(it, 0); - ASSERT_FFIT; - ASSERT_NEXT(42); - ASSERT_SIT; /* Must have morphed back */ - ASSERT_NEXT(SI_FINISHED); - ASSERT_SIT; - - it = setup_simple_iterator(42, NULL, 0); - ff_it = it = new_fast_forward_iterator(it, 1); - ASSERT_FFIT; - ASSERT_NEXT(41); - /* May or may not have morphed back here */ - ASSERT_NEXT(SI_FINISHED); - ASSERT_SIT; - - it = setup_simple_iterator(42, NULL, 0); - ff_it = it = new_fast_forward_iterator(it, 41); - ASSERT_FFIT; - ASSERT_NEXT(1); - /* May or may not have morphed back here */ - ASSERT_NEXT(SI_FINISHED); - ASSERT_SIT; - - it = setup_simple_iterator(42, NULL, 0); - ff_it = it = new_fast_forward_iterator(it, 42); - ASSERT_NEXT(SI_FINISHED); - /* May or may not have morphed back here */ - - it = setup_simple_iterator(42, NULL, 0); - ff_it = it = new_fast_forward_iterator(it, 10000); - ASSERT_NEXT(SI_FINISHED); - /* May or may not have morphed back here */ - - it = setup_simple_iterator(42, "\003\004", 2); - ff_it = it = new_fast_forward_iterator(it, 2); - ASSERT_FFIT; - ASSERT_NEXT(1); - ASSERT_CUE(1); - ASSERT_SIT; - ASSERT_NEXT(4); - ASSERT_CUE(2); - ASSERT_SIT; - ASSERT_NEXT(35); - ASSERT_NEXT(SI_FINISHED); - ASSERT_SIT; - - it = setup_simple_iterator(42, "\003\004", 2); - ff_it = it = new_fast_forward_iterator(it, 5); - ASSERT_FFIT; - ASSERT_CUE(1); - ASSERT_FFIT; - ASSERT_NEXT(2); - ASSERT_CUE(2); - ASSERT_SIT; - ASSERT_NEXT(35); - ASSERT_NEXT(SI_FINISHED); - ASSERT_SIT; - - it = setup_simple_iterator(42, "\003\004", 2); - ff_it = it = new_fast_forward_iterator(it, 41); - ASSERT_FFIT; - ASSERT_CUE(1); - ASSERT_FFIT; - ASSERT_CUE(2); - ASSERT_FFIT; - ASSERT_NEXT(1); - ASSERT_NEXT(SI_FINISHED); - ASSERT_SIT; - - puts("[TEST] Test OK."); -} - -#define SIMPLE_SONG_SIZE 50 - -static unsigned char simple_song[SIMPLE_SONG_SIZE] = { - 0x00, /* Regular song */ - /* Only use channel 0 for all devices */ - 0x02, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* Song begins here */ - 42, 0x90, 60, 0x7f, /* Play C after 42 ticks */ - 02, 64, 0x42, /* Play E after 2 more ticks, using running status mode */ - 0xf8, 10, 0x80, 60, 0x02, /* Stop C after 250 ticks */ - 0, 64, 0x00, /* Stop E immediately */ - 00, 0xfc /* Stop song */ -}; - -#define ASSERT_MIDI3(cmd, arg0, arg1) \ - ASSERT(data[0] == cmd); \ - ASSERT(data[1] == arg0); \ - ASSERT(data[2] == arg1); - -void test_iterator_sci0() { - SongIterator *it = songit_new(simple_song, SIMPLE_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l); - unsigned char data[4]; - int result; - SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */ - - puts("[TEST] SCI0-style song"); - ASSERT_NEXT(42); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x90, 60, 0x7f); - ASSERT_NEXT(2); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x90, 64, 0x42); - ASSERT_NEXT(250); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x80, 60, 0x02); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x80, 64, 0x00); - ASSERT_NEXT(SI_FINISHED); - puts("[TEST] Test OK."); -} - - - -void test_iterator_sci0_loop() { - SongIterator *it = songit_new(simple_song, SIMPLE_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l); - unsigned char data[4]; - int result; - SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */ - SIMSG_SEND(it, SIMSG_SET_LOOPS(2)); /* Loop one additional time */ - - puts("[TEST] SCI0-style song with looping"); - ASSERT_NEXT(42); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x90, 60, 0x7f); - ASSERT_NEXT(2); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x90, 64, 0x42); - ASSERT_NEXT(250); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x80, 60, 0x02); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x80, 64, 0x00); - ASSERT_NEXT(SI_LOOP); - ASSERT_NEXT(42); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x90, 60, 0x7f); - ASSERT_NEXT(2); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x90, 64, 0x42); - ASSERT_NEXT(250); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x80, 60, 0x02); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x80, 64, 0x00); - ASSERT_NEXT(SI_FINISHED); - puts("[TEST] Test OK."); -} - - - -#define LOOP_SONG_SIZE 54 - -unsigned char loop_song[LOOP_SONG_SIZE] = { - 0x00, /* Regular song song */ - /* Only use channel 0 for all devices */ - 0x02, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* Song begins here */ - 42, 0x90, 60, 0x7f, /* Play C after 42 ticks */ - 13, 0x80, 60, 0x00, /* Stop C after 13 ticks */ - 00, 0xCF, 0x7f, /* Set loop point */ - 02, 0x90, 64, 0x42, /* Play E after 2 more ticks, using running status mode */ - 03, 0x80, 64, 0x00, /* Stop E after 3 ticks */ - 00, 0xfc /* Stop song/loop */ -}; - - -void test_iterator_sci0_mark_loop() { - SongIterator *it = songit_new(loop_song, LOOP_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l); - unsigned char data[4]; - int result; - SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */ - SIMSG_SEND(it, SIMSG_SET_LOOPS(3)); /* Loop once more */ - - puts("[TEST] SCI0-style song with loop mark, looping"); - ASSERT_NEXT(42); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x90, 60, 0x7f); - ASSERT_NEXT(13); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x80, 60, 0x00); - /* Loop point here: we don't observe that in the iterator interface yet, though */ - ASSERT_NEXT(2); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x90, 64, 0x42); - ASSERT_NEXT(3); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x80, 64, 0x00); - /* Now we loop back to the loop pont */ - ASSERT_NEXT(SI_LOOP); - ASSERT_NEXT(2); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x90, 64, 0x42); - ASSERT_NEXT(3); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x80, 64, 0x00); - /* ...and one final time */ - ASSERT_NEXT(SI_LOOP); - ASSERT_NEXT(2); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x90, 64, 0x42); - ASSERT_NEXT(3); - ASSERT_NEXT(0); - ASSERT_MIDI3(0x80, 64, 0x00); - - ASSERT_NEXT(SI_FINISHED); - puts("[TEST] Test OK."); -} - - - -int main(int argc, char **argv) { - test_simple_it(); - test_fastforward(); - test_iterator_sci0(); - test_iterator_sci0_loop(); - test_iterator_sci0_mark_loop(); - if (errors != 0) - warning("[ERROR] %d errors total", errors); - return (errors != 0); -} diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp index 3ee8a3a83d..cc09ba79f0 100644 --- a/engines/sci/sound/midiparser_sci.cpp +++ b/engines/sci/sound/midiparser_sci.cpp @@ -43,30 +43,39 @@ enum SciMidiCommands { // MidiParser_SCI // -MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion) : +MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion, SciMusic *music) : MidiParser() { _soundVersion = soundVersion; + _music = music; _mixedData = NULL; // mididata contains delta in 1/60th second // values of ppqn and tempo are found experimentally and may be wrong _ppqn = 1; setTempo(16667); - _volume = 0; + _volume = 127; _signalSet = false; _signalToSet = 0; _dataincAdd = false; _dataincToAdd = 0; _resetOnPause = false; - _channelsUsed = 0; - - for (int i = 0; i < 16; i++) - _channelRemap[i] = i; + _pSnd = 0; } MidiParser_SCI::~MidiParser_SCI() { unloadMusic(); + // we do this, so that MidiParser won't be able to call his own ::allNotesOff() + // this one would affect all channels and we can't let that happen + _driver = 0; +} + +void MidiParser_SCI::mainThreadBegin() { + _mainThreadCalled = true; +} + +void MidiParser_SCI::mainThreadEnd() { + _mainThreadCalled = false; } bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, int channelFilterMask, SciVersion soundVersion) { @@ -75,7 +84,14 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in _pSnd = psnd; _soundVersion = soundVersion; - setVolume(psnd->volume); + for (int i = 0; i < 16; i++) { + _channelUsed[i] = false; + _channelRemap[i] = -1; + _channelMuted[i] = false; + _channelVolume[i] = 127; + } + _channelRemap[9] = 9; // never map channel 9, because that's used for percussion + _channelRemap[15] = 15; // never map channel 15, because thats used by sierra internally if (channelFilterMask) { // SCI0 only has 1 data stream, but we need to filter out channels depending on music hardware selection @@ -86,30 +102,272 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in _num_tracks = 1; _tracks[0] = _mixedData; - setTrack(0); + if (_pSnd) + setTrack(0); _loopTick = 0; - if (_soundVersion <= SCI_VERSION_0_LATE) { - // Set initial voice count - for (int i = 0; i < 16; ++i) { - byte voiceCount = 0; - if (channelFilterMask & (1 << i)) - voiceCount = psnd->soundRes->getInitialVoiceCount(i); - _driver->send(0xB0 | i, 0x4B, voiceCount); + return true; +} + +byte MidiParser_SCI::midiGetNextChannel(long ticker) { + byte curr = 0xFF; + long closest = ticker + 1000000, next = 0; + + for (int i = 0; i < _track->channelCount; i++) { + if (_track->channels[i].time == -1) // channel ended + continue; + SoundResource::Channel *curChannel = &_track->channels[i]; + if (curChannel->curPos >= curChannel->size) + continue; + next = curChannel->data[curChannel->curPos]; // when the next event should occur + if (next == 0xF8) // 0xF8 means 240 ticks delay + next = 240; + next += _track->channels[i].time; + if (next < closest) { + curr = i; + closest = next; + } + } + + return curr; +} + +byte *MidiParser_SCI::midiMixChannels() { + int totalSize = 0; + + for (int i = 0; i < _track->channelCount; i++) { + _track->channels[i].time = 0; + _track->channels[i].prev = 0; + _track->channels[i].curPos = 0; + totalSize += _track->channels[i].size; + } + + byte *outData = new byte[totalSize * 2]; // FIXME: creates overhead and still may be not enough to hold all data + _mixedData = outData; + long ticker = 0; + byte channelNr, curDelta; + byte midiCommand = 0, midiParam, global_prev = 0; + long newDelta; + SoundResource::Channel *channel; + + while ((channelNr = midiGetNextChannel(ticker)) != 0xFF) { // there is still an active channel + channel = &_track->channels[channelNr]; + curDelta = channel->data[channel->curPos++]; + channel->time += (curDelta == 0xF8 ? 240 : curDelta); // when the command is supposed to occur + if (curDelta == 0xF8) + continue; + newDelta = channel->time - ticker; + ticker += newDelta; + + midiCommand = channel->data[channel->curPos++]; + if (midiCommand != kEndOfTrack) { + // Write delta + while (newDelta > 240) { + *outData++ = 0xF8; + newDelta -= 240; + } + *outData++ = (byte)newDelta; + } + // Write command + switch (midiCommand) { + case 0xF0: // sysEx + *outData++ = midiCommand; + do { + midiParam = channel->data[channel->curPos++]; + *outData++ = midiParam; + } while (midiParam != 0xF7); + break; + case kEndOfTrack: // end of channel + channel->time = -1; + break; + default: // MIDI command + if (midiCommand & 0x80) { + midiParam = channel->data[channel->curPos++]; + } else {// running status + midiParam = midiCommand; + midiCommand = channel->prev; + } + + // remember which channel got used for channel remapping + byte midiChannel = midiCommand & 0xF; + _channelUsed[midiChannel] = true; + + if (midiCommand != global_prev) + *outData++ = midiCommand; + *outData++ = midiParam; + if (nMidiParams[(midiCommand >> 4) - 8] == 2) + *outData++ = channel->data[channel->curPos++]; + channel->prev = midiCommand; + global_prev = midiCommand; + } + } + + // Insert stop event + *outData++ = 0; // Delta + *outData++ = 0xFF; // Meta event + *outData++ = 0x2F; // End of track (EOT) + *outData++ = 0x00; + *outData++ = 0x00; + return _mixedData; +} + +// This is used for SCI0 sound-data. SCI0 only has one stream that may +// contain several channels and according to output device we remove +// certain channels from that data. +byte *MidiParser_SCI::midiFilterChannels(int channelMask) { + SoundResource::Channel *channel = &_track->channels[0]; + byte *channelData = channel->data; + byte *channelDataEnd = channel->data + channel->size; + byte *outData = new byte[channel->size + 5]; + byte curChannel = 15, curByte, curDelta; + byte command = 0, lastCommand = 0; + int delta = 0; + int midiParamCount = 0; + + _mixedData = outData; + + while (channelData < channelDataEnd) { + curDelta = *channelData++; + if (curDelta == 0xF8) { + delta += 240; + continue; + } + delta += curDelta; + curByte = *channelData++; + + switch (curByte) { + case 0xF0: // sysEx + case kEndOfTrack: // end of channel + command = curByte; + curChannel = 15; + break; + default: + if (curByte & 0x80) { + command = curByte; + curChannel = command & 0x0F; + midiParamCount = nMidiParams[(command >> 4) - 8]; + } + } + if ((1 << curChannel) & channelMask) { + if (command != kEndOfTrack) { + // Write delta + while (delta > 240) { + *outData++ = 0xF8; + delta -= 240; + } + *outData++ = (byte)delta; + delta = 0; + } + // Write command + switch (command) { + case 0xF0: // sysEx + *outData++ = command; + do { + curByte = *channelData++; + *outData++ = curByte; // out + } while (curByte != 0xF7); + lastCommand = command; + break; + + case kEndOfTrack: // end of channel + break; + + default: // MIDI command + // remember which channel got used for channel remapping + byte midiChannel = command & 0xF; + _channelUsed[midiChannel] = true; + + if (lastCommand != command) { + *outData++ = command; + lastCommand = command; + } + if (midiParamCount > 0) { + if (curByte & 0x80) + *outData++ = *channelData++; + else + *outData++ = curByte; + } + if (midiParamCount > 1) { + *outData++ = *channelData++; + } + } + } else { + if (curByte & 0x80) + channelData += midiParamCount; + else + channelData += midiParamCount - 1; + } + } + + // Insert stop event + *outData++ = 0; // Delta + *outData++ = 0xFF; // Meta event + *outData++ = 0x2F; // End of track (EOT) + *outData++ = 0x00; + *outData++ = 0x00; + + return _mixedData; +} + +// This will get called right before actual playing and will try to own the used channels +void MidiParser_SCI::tryToOwnChannels() { + // We don't have SciMusic in case debug command show_instruments is used + if (!_music) + return; + for (int curChannel = 0; curChannel < 15; curChannel++) { + if (_channelUsed[curChannel]) { + if (_channelRemap[curChannel] == -1) { + _channelRemap[curChannel] = _music->tryToOwnChannel(_pSnd, curChannel); + } + } + } +} + +void MidiParser_SCI::lostChannels() { + for (int curChannel = 0; curChannel < 15; curChannel++) + if ((_channelUsed[curChannel]) && (curChannel != 9)) + _channelRemap[curChannel] = -1; +} + +void MidiParser_SCI::sendInitCommands() { + // reset our "global" volume and channel volumes + _volume = 127; + for (int i = 0; i < 16; i++) + _channelVolume[i] = 127; + + // Set initial voice count + if (_pSnd) { + if (_soundVersion <= SCI_VERSION_0_LATE) { + for (int i = 0; i < 15; ++i) { + byte voiceCount = 0; + if (_channelUsed[i]) { + voiceCount = _pSnd->soundRes->getInitialVoiceCount(i); + sendToDriver(0xB0 | i, 0x4B, voiceCount); + } + } } } // Send a velocity off signal to all channels - for (int i = 0; i < 16; ++i) { - _driver->send(0xB0 | i, 0x4E, 0); // Reset velocity + for (int i = 0; i < 15; ++i) { + if (_channelUsed[i]) + sendToDriver(0xB0 | i, 0x4E, 0); // Reset velocity } - return true; + // Center the pitch wheels and hold pedal in preparation for the next piece of music + for (int i = 0; i < 16; ++i) { + if (_channelUsed[i]) { + sendToDriver(0xE0 | i, 0, 0x40); // Reset pitch wheel + sendToDriver(0xB0 | i, 0x40, 0); // Reset hold pedal + } + } } void MidiParser_SCI::unloadMusic() { - resetTracking(); - allNotesOff(); + if (_pSnd) { + resetTracking(); + allNotesOff(); + } _num_tracks = 0; _active_track = 255; _resetOnPause = false; @@ -118,36 +376,76 @@ void MidiParser_SCI::unloadMusic() { delete[] _mixedData; _mixedData = NULL; } +} - // Center the pitch wheels and hold pedal in preparation for the next piece of music - if (_driver) { - for (int i = 0; i < 16; ++i) { - if (isChannelUsed(i)) { - _driver->send(0xE0 | i, 0, 0x40); // Reset pitch wheel - _driver->send(0xB0 | i, 0x40, 0); // Reset hold pedal - } +// this is used for scripts sending midi commands to us. we verify in that case that the channel is actually +// used, so that channel remapping will work as well and then send them on +void MidiParser_SCI::sendFromScriptToDriver(uint32 midi) { + byte midiChannel = midi & 0xf; + + if (!_channelUsed[midiChannel]) { + // trying to send to an unused channel + // this happens for cmdSendMidi at least in sq1vga right at the start, it's a script issue + return; + } + if (_channelRemap[midiChannel] == -1) { + // trying to send to an unmapped channel + // this happens for cmdSendMidi at least in sq1vga right at the start, scripts are pausing the sound + // and then sending manually. it's a script issue + return; + } + sendToDriver(midi); +} + +void MidiParser_SCI::sendToDriver(uint32 midi) { + byte midiChannel = midi & 0xf; + + if ((midi & 0xFFF0) == 0x4EB0) { + // this is channel mute only for sci1 + // it's velocity control for sci0 + if (_soundVersion >= SCI_VERSION_1_EARLY) { + _channelMuted[midiChannel] = midi & 0xFF0000 ? true : false; + return; // don't send this to driver at all } } - for (int i = 0; i < 16; i++) - _channelRemap[i] = i; + // Is channel muted? if so, don't send command + if (_channelMuted[midiChannel]) + return; + + if ((midi & 0xFFF0) == 0x07B0) { + // someone trying to set channel volume? + int channelVolume = (midi >> 16) & 0xFF; + // Remember, if we need to set it ourselves + _channelVolume[midiChannel] = channelVolume; + // Adjust volume accordingly to current "global" volume + channelVolume = channelVolume * _volume / 127; + midi = (midi & 0xFFF0) | ((channelVolume & 0xFF) << 16); + } + + // Channel remapping + int16 realChannel = _channelRemap[midiChannel]; + assert(realChannel != -1); + + midi = (midi & 0xFFFFFFF0) | realChannel; + if (_mainThreadCalled) + _music->putMidiCommandInQueue(midi); + else + _driver->send(midi); } void MidiParser_SCI::parseNextEvent(EventInfo &info) { - // Monitor which channels are used by this song - setChannelUsed(info.channel()); - // Set signal AFTER waiting for delta, otherwise we would set signal too soon resulting in all sorts of bugs if (_dataincAdd) { _dataincAdd = false; _pSnd->dataInc += _dataincToAdd; _pSnd->signal = 0x7f + _pSnd->dataInc; - debugC(2, kDebugLevelSound, "datainc %04x", _dataincToAdd); + debugC(4, kDebugLevelSound, "datainc %04x", _dataincToAdd); } if (_signalSet) { _signalSet = false; _pSnd->signal = _signalToSet; - debugC(2, kDebugLevelSound, "signal %04x", _signalToSet); + debugC(4, kDebugLevelSound, "signal %04x", _signalToSet); } info.start = _position._play_pos; @@ -173,10 +471,16 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { info.basic.param2 = 0; if (info.channel() == 0xF) {// SCI special case if (info.basic.param1 != kSetSignalLoop) { - _signalSet = true; - _signalToSet = info.basic.param1; + // at least in kq5/french&mac the first scene in the intro has a song that sets signal to 4 immediately + // on tick 0. Signal isn't set at that point by sierra sci and it would cause the castle daventry text to + // get immediately removed, so we currently filter it. + // Sierra SCI ignores them as well at that time + if (_position._play_tick) { + _signalSet = true; + _signalToSet = info.basic.param1; + } } else { - _loopTick = _position._play_tick; + _loopTick = _position._play_tick + info.delta; } } break; @@ -235,7 +539,6 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { case 0x0A: // pan case 0x0B: // expression case 0x40: // sustain - case 0x4E: // velocity control case 0x79: // reset all case 0x7B: // notes off // These are all handled by the music driver, so ignore them @@ -249,8 +552,6 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { break; } } - if (info.basic.param1 == 7) // channel volume change -scale it - info.basic.param2 = info.basic.param2 * _volume / MUSIC_VOLUME_MAX; info.length = 0; break; @@ -307,7 +608,7 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { _pSnd->status = kSoundStopped; _pSnd->signal = SIGNAL_OFFSET; - debugC(2, kDebugLevelSound, "signal EOT"); + debugC(4, kDebugLevelSound, "signal EOT"); } } break; @@ -319,254 +620,66 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) { }// switch (info.command()) } +void MidiParser_SCI::allNotesOff() { + if (!_driver) + return; -byte MidiParser_SCI::midiGetNextChannel(long ticker) { - byte curr = 0xFF; - long closest = ticker + 1000000, next = 0; - - for (int i = 0; i < _track->channelCount; i++) { - if (_track->channels[i].time == -1) // channel ended - continue; - next = *_track->channels[i].data; // when the next event should occur - if (next == 0xF8) // 0xF8 means 240 ticks delay - next = 240; - next += _track->channels[i].time; - if (next < closest) { - curr = i; - closest = next; - } - } - - return curr; -} - -byte *MidiParser_SCI::midiMixChannels() { - int totalSize = 0; - byte **dataPtr = new byte *[_track->channelCount]; - - for (int i = 0; i < _track->channelCount; i++) { - dataPtr[i] = _track->channels[i].data; - _track->channels[i].time = 0; - _track->channels[i].prev = 0; - totalSize += _track->channels[i].size; - } - - byte *outData = new byte[totalSize * 2]; // FIXME: creates overhead and still may be not enough to hold all data - _mixedData = outData; - long ticker = 0; - byte curr, curDelta; - byte command = 0, par1, global_prev = 0; - long new_delta; - SoundResource::Channel *channel; - - while ((curr = midiGetNextChannel(ticker)) != 0xFF) { // there is still active channel - channel = &_track->channels[curr]; - curDelta = *channel->data++; - channel->time += (curDelta == 0xF8 ? 240 : curDelta); // when the comamnd is supposed to occur - if (curDelta == 0xF8) - continue; - new_delta = channel->time - ticker; - ticker += new_delta; - - command = *channel->data++; - if (command != kEndOfTrack) { - debugC(2, kDebugLevelSound, "\nDELTA "); - // Write delta - while (new_delta > 240) { - *outData++ = 0xF8; - debugC(2, kDebugLevelSound, "F8 "); - new_delta -= 240; - } - *outData++ = (byte)new_delta; - debugC(2, kDebugLevelSound, "%02X ", (uint32)new_delta); - } - // Write command - switch (command) { - case 0xF0: // sysEx - *outData++ = command; - debugC(2, kDebugLevelSound, "%02X ", command); - do { - par1 = *channel->data++; - *outData++ = par1; // out - } while (par1 != 0xF7); - break; - case kEndOfTrack: // end of channel - channel->time = -1; // FIXME - break; - default: // MIDI command - if (command & 0x80) { - par1 = *channel->data++; - - // TODO: Fix remapping + int i, j; -#if 0 - // Remap channel. Keep the upper 4 bits (command code) and change - // the lower 4 bits (channel) - byte remappedChannel = _channelRemap[par1 & 0xF]; - par1 = (par1 & 0xF0) | (remappedChannel & 0xF); -#endif - } else {// running status - par1 = command; - command = channel->prev; + // Turn off all active notes + for (i = 0; i < 128; ++i) { + for (j = 0; j < 16; ++j) { + if ((_active_notes[i] & (1 << j)) && (_channelRemap[j] != -1)){ + sendToDriver(0x80 | j, i, 0); } - if (command != global_prev) - *outData++ = command; // out command - *outData++ = par1;// pout par1 - if (nMidiParams[(command >> 4) - 8] == 2) - *outData++ = *channel->data++; // out par2 - channel->prev = command; - global_prev = command; - }// switch(command) - }// while (curr) - - // Insert stop event - *outData++ = 0; // Delta - *outData++ = 0xFF; // Meta event - *outData++ = 0x2F; // End of track (EOT) - *outData++ = 0x00; - *outData++ = 0x00; - - for (int channelNr = 0; channelNr < _track->channelCount; channelNr++) - _track->channels[channelNr].data = dataPtr[channelNr]; - - delete[] dataPtr; - return _mixedData; -} - -// This is used for SCI0 sound-data. SCI0 only has one stream that may -// contain several channels and according to output device we remove -// certain channels from that data. -byte *MidiParser_SCI::midiFilterChannels(int channelMask) { - SoundResource::Channel *channel = &_track->channels[0]; - byte *channelData = channel->data; - byte *channelDataEnd = channel->data + channel->size; - byte *outData = new byte[channel->size + 5]; - byte curChannel = 15, curByte, curDelta; - byte command = 0, lastCommand = 0; - int delta = 0; - int midiParamCount = 0; - - _mixedData = outData; - - while (channelData < channelDataEnd) { - curDelta = *channelData++; - if (curDelta == 0xF8) { - delta += 240; - continue; } - delta += curDelta; - curByte = *channelData++; + } - switch (curByte) { - case 0xF0: // sysEx - case kEndOfTrack: // end of channel - command = curByte; - curChannel = 15; - break; - default: - if (curByte & 0x80) { - command = curByte; - curChannel = command & 0x0F; - midiParamCount = nMidiParams[(command >> 4) - 8]; - } + // Turn off all hanging notes + for (i = 0; i < ARRAYSIZE(_hanging_notes); i++) { + byte midiChannel = _hanging_notes[i].channel; + if ((_hanging_notes[i].time_left) && (_channelRemap[midiChannel] != -1)) { + sendToDriver(0x80 | midiChannel, _hanging_notes[i].note, 0); + _hanging_notes[i].time_left = 0; } - if ((1 << curChannel) & channelMask) { - if (command != kEndOfTrack) { - debugC(2, kDebugLevelSound, "\nDELTA "); - // Write delta - while (delta > 240) { - *outData++ = 0xF8; - debugC(2, kDebugLevelSound, "F8 "); - delta -= 240; - } - *outData++ = (byte)delta; - debugC(2, kDebugLevelSound, "%02X ", delta); - delta = 0; - } - // Write command - switch (command) { - case 0xF0: // sysEx - *outData++ = command; - debugC(2, kDebugLevelSound, "%02X ", command); - do { - curByte = *channelData++; - *outData++ = curByte; // out - } while (curByte != 0xF7); - lastCommand = command; - break; + } + _hanging_notes_count = 0; - case kEndOfTrack: // end of channel - break; + // To be sure, send an "All Note Off" event (but not all MIDI devices + // support this...). - default: // MIDI command - if (lastCommand != command) { - *outData++ = command; - debugC(2, kDebugLevelSound, "%02X ", command); - lastCommand = command; - } - if (midiParamCount > 0) { - if (curByte & 0x80) { - debugC(2, kDebugLevelSound, "%02X ", *channelData); - *outData++ = *channelData++; - } else { - debugC(2, kDebugLevelSound, "%02X ", curByte); - *outData++ = curByte; - } - } - if (midiParamCount > 1) { - debugC(2, kDebugLevelSound, "%02X ", *channelData); - *outData++ = *channelData++; - } - } - } else { - if (curByte & 0x80) { - channelData += midiParamCount; - } else { - channelData += midiParamCount - 1; - } - } + for (i = 0; i < 16; ++i) { + if (_channelRemap[i] != -1) + sendToDriver(0xB0 | i, 0x7b, 0); // All notes off } - // Insert stop event - *outData++ = 0; // Delta - *outData++ = 0xFF; // Meta event - *outData++ = 0x2F; // End of track (EOT) - *outData++ = 0x00; - *outData++ = 0x00; - - return _mixedData; + memset(_active_notes, 0, sizeof(_active_notes)); } void MidiParser_SCI::setVolume(byte volume) { - // FIXME: This receives values > 127... throw a warning for now and clip the variable - if (volume > MUSIC_VOLUME_MAX) { - warning("attempted to set an invalid volume(%d)", volume); - volume = MUSIC_VOLUME_MAX; // reset - } - assert(volume <= MUSIC_VOLUME_MAX); - if (_volume != volume) { - _volume = volume; - - switch (_soundVersion) { - case SCI_VERSION_0_EARLY: - case SCI_VERSION_0_LATE: { - int16 globalVolume = _volume * 15 / 127; - ((MidiPlayer *)_driver)->setVolume(globalVolume); - break; - } + _volume = volume; + + switch (_soundVersion) { + case SCI_VERSION_0_EARLY: + case SCI_VERSION_0_LATE: { + // SCI0 adlib driver doesn't support channel volumes, so we need to go this way + // TODO: this should take the actual master volume into account + int16 globalVolume = _volume * 15 / 127; + ((MidiPlayer *)_driver)->setVolume(globalVolume); + break; + } - case SCI_VERSION_1_EARLY: - case SCI_VERSION_1_LATE: - // sending volume change to all active channels - for (int i = 0; i < _track->channelCount; i++) - if (_track->channels[i].number <= 0xF) - _driver->send(0xB0 + _track->channels[i].number, 7, _volume); - break; + case SCI_VERSION_1_EARLY: + case SCI_VERSION_1_LATE: + // Send previous channel volumes again to actually update the volume + for (int i = 0; i < 15; i++) + if (_channelRemap[i] != -1) + sendToDriver(0xB0 + i, 7, _channelVolume[i]); + break; - default: - error("MidiParser_SCI::setVolume: Unsupported soundVersion"); - } + default: + error("MidiParser_SCI::setVolume: Unsupported soundVersion"); } } diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h index 9d4b5a39da..90db06e539 100644 --- a/engines/sci/sound/midiparser_sci.h +++ b/engines/sci/sound/midiparser_sci.h @@ -53,12 +53,17 @@ namespace Sci { */ class MidiParser_SCI : public MidiParser { public: - MidiParser_SCI(SciVersion soundVersion); + MidiParser_SCI(SciVersion soundVersion, SciMusic *music); ~MidiParser_SCI(); + + void mainThreadBegin(); + void mainThreadEnd(); + bool loadMusic(SoundResource::Track *track, MusicEntry *psnd, int channelFilterMask, SciVersion soundVersion); bool loadMusic(byte *, uint32) { return false; } + void sendInitCommands(); void unloadMusic(); void setVolume(byte volume); void stop() { @@ -71,23 +76,29 @@ public: jumpToTick(0); } - void remapChannel(byte channel, byte newChannel) { - assert(channel < 0xF); // don't touch special SCI channel 15 - assert(newChannel < 0xF); // don't touch special SCI channel 15 - _channelRemap[channel] = newChannel; - } + void allNotesOff(); - void clearUsedChannels() { _channelsUsed = 0; } + const byte *getMixedData() const { return _mixedData; } -protected: - bool isChannelUsed(byte channel) const { return _channelsUsed & (1 << channel); } - void setChannelUsed(byte channel) { _channelsUsed |= (1 << channel); } + void tryToOwnChannels(); + void lostChannels(); + void sendFromScriptToDriver(uint32 midi); + void sendToDriver(uint32 midi); + void sendToDriver(byte status, byte firstOp, byte secondOp) { + sendToDriver(status | ((uint32)firstOp << 8) | ((uint32)secondOp << 16)); + } +protected: void parseNextEvent(EventInfo &info); byte *midiMixChannels(); byte *midiFilterChannels(int channelMask); byte midiGetNextChannel(long ticker); + SciMusic *_music; + + // this is set, when main thread calls us -> we send commands to queue instead to driver + bool _mainThreadCalled; + SciVersion _soundVersion; byte *_mixedData; SoundResource::Track *_track; @@ -101,11 +112,10 @@ protected: int16 _dataincToAdd; bool _resetOnPause; - // A 16-bit mask, containing the channels used - // by the currently parsed song - uint16 _channelsUsed; - - byte _channelRemap[16]; + bool _channelUsed[16]; + int16 _channelRemap[16]; + bool _channelMuted[16]; + byte _channelVolume[16]; }; } // End of namespace Sci diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index fa5716e7cc..55a7e1fdc4 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -43,6 +43,11 @@ SciMusic::SciMusic(SciVersion soundVersion) // Reserve some space in the playlist, to avoid expensive insertion // operations _playList.reserve(10); + + for (int i = 0; i < 16; i++) + _usedChannel[i] = 0; + + _queuedCommands.reserve(1000); } SciMusic::~SciMusic() { @@ -58,31 +63,30 @@ void SciMusic::init() { // SCI sound init _dwTempo = 0; - MidiDriverType midiType; - // Default to MIDI in SCI32 games, as many don't have AdLib support. - // WORKAROUND: Default to MIDI in Amiga SCI1_EGA+ games as we don't support those patches yet. - // We also don't yet support the 7.pat file of SCI1+ Mac games or SCI0 Mac patches, so we - // default to MIDI in those games to let them run. + // WORKAROUND: Default to MIDI in Amiga SCI1_EGA+ games as we don't support + // those patches yet. We also don't yet support the 7.pat file of SCI1+ Mac + // games or SCI0 Mac patches, so we default to MIDI in those games to let + // them run. Common::Platform platform = g_sci->getPlatform(); - - if (getSciVersion() >= SCI_VERSION_2 || platform == Common::kPlatformMacintosh || (platform == Common::kPlatformAmiga && getSciVersion() >= SCI_VERSION_1_EGA)) - midiType = MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MIDI); - else - midiType = MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB | MDT_MIDI); - - switch (midiType) { - case MD_ADLIB: + uint32 dev = MidiDriver::detectDevice( + (getSciVersion() >= SCI_VERSION_2 || platform == Common::kPlatformMacintosh || + (platform == Common::kPlatformAmiga && getSciVersion() >= SCI_VERSION_1_EGA)) + ? (MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM) + : (MDT_PCSPK | MDT_ADLIB | MDT_MIDI)); + + switch (MidiDriver::getMusicType(dev)) { + case MT_ADLIB: // FIXME: There's no Amiga sound option, so we hook it up to AdLib if (g_sci->getPlatform() == Common::kPlatformAmiga) _pMidiDrv = MidiPlayer_Amiga_create(_soundVersion); else _pMidiDrv = MidiPlayer_AdLib_create(_soundVersion); break; - case MD_PCJR: + case MT_PCJR: _pMidiDrv = MidiPlayer_PCJr_create(_soundVersion); break; - case MD_PCSPK: + case MT_PCSPK: _pMidiDrv = MidiPlayer_PCSpeaker_create(_soundVersion); break; default: @@ -100,6 +104,49 @@ void SciMusic::init() { } _bMultiMidi = ConfMan.getBool("multi_midi"); + + // Find out what the first possible channel is (used, when doing channel + // remapping). + _driverFirstChannel = _pMidiDrv->getFirstChannel(); +} + +void SciMusic::miditimerCallback(void *p) { + SciMusic *sciMusic = (SciMusic *)p; + + Common::StackLock lock(sciMusic->_mutex); + sciMusic->onTimer(); +} + +void SciMusic::onTimer() { + const MusicList::iterator end = _playList.end(); + // sending out queued commands that were "sent" via main thread + sendMidiCommandsFromQueue(); + + for (MusicList::iterator i = _playList.begin(); i != end; ++i) + (*i)->onTimer(); +} + +void SciMusic::putMidiCommandInQueue(byte status, byte firstOp, byte secondOp) { + putMidiCommandInQueue(status | ((uint32)firstOp << 8) | ((uint32)secondOp << 16)); +} + +void SciMusic::putMidiCommandInQueue(uint32 midi) { + _queuedCommands.push_back(midi); +} + +// This sends the stored commands from queue to driver (is supposed to get +// called only during onTimer()). At least mt32 emulation doesn't like getting +// note-on commands from main thread (if we directly send, we would get a crash +// during piano scene in lsl5). +void SciMusic::sendMidiCommandsFromQueue() { + uint curCommand = 0; + uint commandCount = _queuedCommands.size(); + + while (curCommand < commandCount) { + _pMidiDrv->send(_queuedCommands[curCommand]); + curCommand++; + } + _queuedCommands.clear(); } void SciMusic::clearPlayList() { @@ -119,22 +166,12 @@ void SciMusic::pauseAll(bool pause) { } void SciMusic::stopAll() { - Common::StackLock lock(_mutex); - const MusicList::iterator end = _playList.end(); for (MusicList::iterator i = _playList.begin(); i != end; ++i) { soundStop(*i); } } - -void SciMusic::miditimerCallback(void *p) { - SciMusic *aud = (SciMusic *)p; - - Common::StackLock lock(aud->_mutex); - aud->onTimer(); -} - void SciMusic::soundSetSoundOn(bool soundOnFlag) { Common::StackLock lock(_mutex); @@ -160,6 +197,24 @@ MusicEntry *SciMusic::getSlot(reg_t obj) { return NULL; } +// We return the currently active music slot for SCI0 +MusicEntry *SciMusic::getActiveSci0MusicSlot() { + const MusicList::iterator end = _playList.end(); + MusicEntry *highestPrioritySlot = NULL; + for (MusicList::iterator i = _playList.begin(); i != end; ++i) { + MusicEntry *playSlot = *i; + if (playSlot->pMidiParser) { + if (playSlot->status == kSoundPlaying) + return playSlot; + if (playSlot->status == kSoundPaused) { + if ((!highestPrioritySlot) || (highestPrioritySlot->priority < playSlot->priority)) + highestPrioritySlot = playSlot; + } + } + } + return highestPrioritySlot; +} + void SciMusic::setReverb(byte reverb) { Common::StackLock lock(_mutex); _pMidiDrv->setReverb(reverb); @@ -174,28 +229,14 @@ void SciMusic::sortPlayList() { Common::sort(_playList.begin(), _playList.end(), musicEntryCompare); } -void SciMusic::findUsedChannels() { - // Reset list - for (int k = 0; k < 16; k++) - _usedChannels[k] = false; - - const MusicList::const_iterator end = _playList.end(); - for (MusicList::const_iterator i = _playList.begin(); i != end; ++i) { - for (int channel = 0; channel < 16; channel++) { - if ((*i)->soundRes && (*i)->soundRes->isChannelUsed(channel)) - _usedChannels[channel] = true; - } - } -} - void SciMusic::soundInitSnd(MusicEntry *pSnd) { int channelFilterMask = 0; SoundResource::Track *track = pSnd->soundRes->getTrackByType(_pMidiDrv->getPlayId()); - // If MIDI device is selected but there is no digital track in sound resource - // try to use adlib's digital sample if possible - // Also, if the track couldn't be found, load the digital track, as some games - // depend on this (e.g. the Longbow demo) + // If MIDI device is selected but there is no digital track in sound + // resource try to use adlib's digital sample if possible. Also, if the + // track couldn't be found, load the digital track, as some games depend on + // this (e.g. the Longbow demo). if (!track || (_bMultiMidi && track->digitalChannelNr == -1)) { SoundResource::Track *digital = pSnd->soundRes->getDigitalTrack(); if (digital) @@ -224,49 +265,53 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) { _mutex.lock(); pSnd->soundType = Audio::Mixer::kMusicSoundType; if (pSnd->pMidiParser == NULL) { - pSnd->pMidiParser = new MidiParser_SCI(_soundVersion); + pSnd->pMidiParser = new MidiParser_SCI(_soundVersion, this); pSnd->pMidiParser->setMidiDriver(_pMidiDrv); pSnd->pMidiParser->setTimerRate(_dwTempo); } pSnd->pauseCounter = 0; - // TODO: Fix channel remapping. This doesn't quite work... (e.g. no difference in LSL1VGA) -#if 0 - // Remap channels - findUsedChannels(); - - pSnd->pMidiParser->clearUsedChannels(); - - for (int i = 0; i < 16; i++) { - if (_usedChannels[i] && pSnd->soundRes->isChannelUsed(i)) { - int16 newChannel = getNextUnusedChannel(); - if (newChannel >= 0) { - _usedChannels[newChannel] = true; - debug("Remapping channel %d to %d\n", i, newChannel); - pSnd->pMidiParser->remapChannel(i, newChannel); - } else { - warning("Attempt to remap channel %d, but no unused channels exist", i); - } - } - } -#endif - // Find out what channels to filter for SCI0 channelFilterMask = pSnd->soundRes->getChannelFilterMask(_pMidiDrv->getPlayId(), _pMidiDrv->hasRhythmChannel()); - pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion); - // Fast forward to the last position and perform associated events when loading - pSnd->pMidiParser->jumpToTick(pSnd->ticker, true); + pSnd->pMidiParser->mainThreadBegin(); + pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion); + pSnd->pMidiParser->mainThreadEnd(); _mutex.unlock(); } } } -void SciMusic::onTimer() { - const MusicList::iterator end = _playList.end(); - for (MusicList::iterator i = _playList.begin(); i != end; ++i) - (*i)->onTimer(); +// This one checks, if requested channel is available -> in that case give +// caller that channel. Otherwise look for an unused one +int16 SciMusic::tryToOwnChannel(MusicEntry *caller, int16 bestChannel) { + // Don't even try this for SCI0 + if (_soundVersion <= SCI_VERSION_0_LATE) + return bestChannel; + if (!_usedChannel[bestChannel]) { + // currently unused, so give it to caller directly + _usedChannel[bestChannel] = caller; + return bestChannel; + } + // otherwise look for unused channel + for (int channelNr = _driverFirstChannel; channelNr < 15; channelNr++) { + if (!_usedChannel[channelNr]) { + _usedChannel[channelNr] = caller; + return channelNr; + } + } + error("no free channels"); +} + +void SciMusic::freeChannels(MusicEntry *caller) { + // Remove used channels + for (int i = 0; i < 15; i++) { + if (_usedChannel[i] == caller) + _usedChannel[i] = 0; + } + // Also tell midiparser, that he lost ownership + caller->pMidiParser->lostChannels(); } void SciMusic::soundPlay(MusicEntry *pSnd) { @@ -294,7 +339,7 @@ void SciMusic::soundPlay(MusicEntry *pSnd) { if ((_soundVersion <= SCI_VERSION_0_LATE) && (alreadyPlaying)) { // Music already playing in SCI0? if (pSnd->priority > alreadyPlaying->priority) { - // And new priority higher? pause previous music and play new one immediately + // And new priority higher? pause previous music and play new one immediately. // Example of such case: lsl3, when getting points (jingle is played then) soundPause(alreadyPlaying); alreadyPlaying->isQueued = true; @@ -317,34 +362,54 @@ void SciMusic::soundPlay(MusicEntry *pSnd) { pSnd->pLoopStream, -1, pSnd->volume, 0, DisposeAfterUse::NO); } else { + // Rewind in case we play the same sample multiple times + // (non-looped) like in pharkas right at the start + pSnd->pStreamAud->rewind(); _pMixer->playStream(pSnd->soundType, &pSnd->hCurrentAud, pSnd->pStreamAud, -1, pSnd->volume, 0, DisposeAfterUse::NO); } } else { - _mutex.lock(); if (pSnd->pMidiParser) { - pSnd->pMidiParser->setVolume(pSnd->volume); + _mutex.lock(); + pSnd->pMidiParser->mainThreadBegin(); + pSnd->pMidiParser->tryToOwnChannels(); if (pSnd->status == kSoundStopped) + pSnd->pMidiParser->sendInitCommands(); + pSnd->pMidiParser->setVolume(pSnd->volume); + if (pSnd->status == kSoundStopped) { pSnd->pMidiParser->jumpToTick(0); + } else { + // Fast forward to the last position and perform associated events when loading + pSnd->pMidiParser->jumpToTick(pSnd->ticker, true); + } + pSnd->pMidiParser->mainThreadEnd(); + _mutex.unlock(); } - _mutex.unlock(); } pSnd->status = kSoundPlaying; } void SciMusic::soundStop(MusicEntry *pSnd) { + SoundStatus previousStatus = pSnd->status; pSnd->status = kSoundStopped; if (_soundVersion <= SCI_VERSION_0_LATE) pSnd->isQueued = false; if (pSnd->pStreamAud) _pMixer->stopHandle(pSnd->hCurrentAud); - _mutex.lock(); - if (pSnd->pMidiParser) - pSnd->pMidiParser->stop(); - _mutex.unlock(); + if (pSnd->pMidiParser) { + _mutex.lock(); + pSnd->pMidiParser->mainThreadBegin(); + // We shouldn't call stop in case it's paused, otherwise we would send + // allNotesOff() again + if (previousStatus == kSoundPlaying) + pSnd->pMidiParser->stop(); + freeChannels(pSnd); + pSnd->pMidiParser->mainThreadEnd(); + _mutex.unlock(); + } } void SciMusic::soundSetVolume(MusicEntry *pSnd, byte volume) { @@ -353,7 +418,9 @@ void SciMusic::soundSetVolume(MusicEntry *pSnd, byte volume) { _pMixer->setChannelVolume(pSnd->hCurrentAud, volume * 2); // Mixer is 0-255, SCI is 0-127 } else if (pSnd->pMidiParser) { _mutex.lock(); + pSnd->pMidiParser->mainThreadBegin(); pSnd->pMidiParser->setVolume(volume); + pSnd->pMidiParser->mainThreadEnd(); _mutex.unlock(); } } @@ -368,13 +435,15 @@ void SciMusic::soundSetPriority(MusicEntry *pSnd, byte prio) { void SciMusic::soundKill(MusicEntry *pSnd) { pSnd->status = kSoundStopped; - _mutex.lock(); if (pSnd->pMidiParser) { + _mutex.lock(); + pSnd->pMidiParser->mainThreadBegin(); pSnd->pMidiParser->unloadMusic(); + pSnd->pMidiParser->mainThreadEnd(); delete pSnd->pMidiParser; pSnd->pMidiParser = NULL; + _mutex.unlock(); } - _mutex.unlock(); if (pSnd->pStreamAud) { _pMixer->stopHandle(pSnd->hCurrentAud); @@ -406,10 +475,14 @@ void SciMusic::soundPause(MusicEntry *pSnd) { if (pSnd->pStreamAud) { _pMixer->pauseHandle(pSnd->hCurrentAud, true); } else { - _mutex.lock(); - if (pSnd->pMidiParser) + if (pSnd->pMidiParser) { + _mutex.lock(); + pSnd->pMidiParser->mainThreadBegin(); pSnd->pMidiParser->pause(); - _mutex.unlock(); + freeChannels(pSnd); + pSnd->pMidiParser->mainThreadEnd(); + _mutex.unlock(); + } } } @@ -448,6 +521,21 @@ void SciMusic::soundSetMasterVolume(uint16 vol) { _pMidiDrv->setVolume(vol); } +void SciMusic::sendMidiCommand(uint32 cmd) { + Common::StackLock lock(_mutex); + _pMidiDrv->send(cmd); +} + +void SciMusic::sendMidiCommand(MusicEntry *pSnd, uint32 cmd) { + Common::StackLock lock(_mutex); + if (!pSnd->pMidiParser) + error("tried to cmdSendMidi on non midi slot (%04x:%04x)", PRINT_REG(pSnd->soundObj)); + + pSnd->pMidiParser->mainThreadBegin(); + pSnd->pMidiParser->sendFromScriptToDriver(cmd); + pSnd->pMidiParser->mainThreadEnd(); +} + void SciMusic::printPlayList(Console *con) { Common::StackLock lock(_mutex); @@ -567,14 +655,6 @@ void MusicEntry::doFade() { fadeStep = 0; fadeCompleted = true; } -#ifdef ENABLE_SCI32 - // Disable fading for SCI32 - sound drivers have issues when fading in (gabriel knight 1 sierra title) - if (getSciVersion() >= SCI_VERSION_2) { - volume = fadeTo; - fadeStep = 0; - fadeCompleted = true; - } -#endif // Only process MIDI streams in this thread, not digital sound effects if (pMidiParser) { diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h index 83cd59e89b..943a5bd2a8 100644 --- a/engines/sci/sound/music.h +++ b/engines/sci/sound/music.h @@ -26,15 +26,11 @@ #ifndef SCI_MUSIC_H #define SCI_MUSIC_H -#ifndef USE_OLD_MUSIC_FUNCTIONS #include "common/serializer.h" -#endif #include "common/mutex.h" #include "sound/mixer.h" #include "sound/audiostream.h" -//#include "sound/mididrv.h" -//#include "sound/midiparser.h" #include "sci/sci.h" #include "sci/resource.h" @@ -55,11 +51,7 @@ enum SoundStatus { class MidiParser_SCI; class SegManager; -class MusicEntry -#ifndef USE_OLD_MUSIC_FUNCTIONS - : public Common::Serializable -#endif -{ +class MusicEntry : public Common::Serializable { public: // Do not get these directly for the sound objects! // It's a bad idea, as the sound code (i.e. the SciMusic @@ -96,9 +88,6 @@ public: Audio::Mixer::SoundType soundType; -#ifndef USE_OLD_MUSIC_FUNCTIONS -//protected: -#endif MidiParser_SCI *pMidiParser; // TODO: We need to revise how we store the different @@ -114,25 +103,28 @@ public: void doFade(); void onTimer(); -#ifndef USE_OLD_MUSIC_FUNCTIONS virtual void saveLoadWithSerializer(Common::Serializer &ser); -#endif }; typedef Common::Array<MusicEntry *> MusicList; +typedef Common::Array<uint32> MidiCommandQueue; -class SciMusic -#ifndef USE_OLD_MUSIC_FUNCTIONS - : public Common::Serializable -#endif -{ +class SciMusic : public Common::Serializable { public: SciMusic(SciVersion soundVersion); ~SciMusic(); void init(); + void onTimer(); + void putMidiCommandInQueue(byte status, byte firstOp, byte secondOp); + void putMidiCommandInQueue(uint32 midi); +private: + static void miditimerCallback(void *p); + void sendMidiCommandsFromQueue(); + +public: void clearPlayList(); void pauseAll(bool pause); void stopAll(); @@ -165,6 +157,7 @@ public: } MusicEntry *getSlot(reg_t obj); + MusicEntry *getActiveSci0MusicSlot(); void pushBackSlot(MusicEntry *slotEntry) { Common::StackLock lock(_mutex); @@ -179,16 +172,12 @@ public: MusicList::iterator getPlayListStart() { return _playList.begin(); } MusicList::iterator getPlayListEnd() { return _playList.end(); } - void sendMidiCommand(uint32 cmd) { - Common::StackLock lock(_mutex); - _pMidiDrv->send(cmd); - } + void sendMidiCommand(uint32 cmd); + void sendMidiCommand(MusicEntry *pSnd, uint32 cmd); void setReverb(byte reverb); -#ifndef USE_OLD_MUSIC_FUNCTIONS virtual void saveLoadWithSerializer(Common::Serializer &ser); -#endif // Mutex for music code. Used to guard access to the song playlist, to the // MIDI parser and to the MIDI driver/player. Note that guarded code must NOT @@ -196,6 +185,9 @@ public: // where a deadlock can occur Common::Mutex _mutex; + int16 tryToOwnChannel(MusicEntry *caller, int16 bestChannel); + void freeChannels(MusicEntry *caller); + protected: void sortPlayList(); @@ -208,22 +200,16 @@ protected: // Mixed AdLib/MIDI mode: when enabled from the ScummVM sound options screen, // and a sound has a digital track, the sound from the AdLib track is played bool _bMultiMidi; -private: - static void miditimerCallback(void *p); - void findUsedChannels(); - int16 getNextUnusedChannel() const { - for (int i = 0; i < 16; i++) { - if (!_usedChannels[i]) - return i; - } - - return -1; - } +private: MusicList _playList; bool _soundOn; byte _masterVolume; - bool _usedChannels[16]; + MusicEntry *_usedChannel[16]; + + MidiCommandQueue _queuedCommands; + + int _driverFirstChannel; }; } // End of namespace Sci diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index ece4c1430c..51832af09f 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -23,17 +23,12 @@ * */ -#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS - -#ifdef USE_OLD_MUSIC_FUNCTIONS -#include "sci/sound/iterator/iterator.h" // for SongIteratorStatus -#endif - #include "common/config-manager.h" #include "sci/sound/audio.h" #include "sci/sound/music.h" #include "sci/sound/soundcmd.h" +#include "sci/engine/kernel.h" #include "sci/engine/selector.h" namespace Sci { @@ -41,258 +36,29 @@ 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 = readSelectorValue(segMan, obj, SELECTOR(number)); - Resource *song = resMan->findResource(ResourceId(kResourceTypeSound, song_nr), 0); - int flags = readSelectorValue(segMan, obj, SELECTOR(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); - writeSelectorValue(segMan, obj, SELECTOR(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)", - PRINT_REG(obj), cue); - /* writeSelectorValue(segMan, obj, SELECTOR(loops), readSelectorValue(segMan, obj, SELECTOR(loop));; - 1);*/ - writeSelectorValue(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); - break; - - case SI_RELATIVE_CUE: - debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x received relative cue %d", - PRINT_REG(obj), cue); - writeSelectorValue(segMan, obj, SELECTOR(signal), cue + 0x7f); - break; - - case SI_ABSOLUTE_CUE: - debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x received absolute cue %d", - PRINT_REG(obj), cue); - writeSelectorValue(segMan, obj, SELECTOR(signal), cue); - break; - - case SI_FINISHED: - debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x finished", - PRINT_REG(obj)); - writeSelectorValue(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); - writeSelectorValue(segMan, obj, SELECTOR(state), kSoundStopped); - break; - - default: - warning("Unexpected result from sfx_poll: %d", result); - break; - } - } -} - -#endif SoundCommandParser::SoundCommandParser(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, AudioPlayer *audio, SciVersion soundVersion) : _resMan(resMan), _segMan(segMan), _kernel(kernel), _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 = &g_sci->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; - } + _music = new SciMusic(_soundVersion); + _music->init(); } SoundCommandParser::~SoundCommandParser() { - for (SoundCommandContainer::iterator i = _soundCommands.begin(); i != _soundCommands.end(); ++i) - delete *i; - -#ifndef USE_OLD_MUSIC_FUNCTIONS delete _music; -#endif } -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; +reg_t SoundCommandParser::kDoSoundInit(int argc, reg_t *argv, reg_t acc) { + processInitSound(argv[0]); + return acc; } -void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) { - if (!obj.segment) - return; - +void SoundCommandParser::processInitSound(reg_t obj) { int resourceId = readSelectorValue(_segMan, obj, SELECTOR(number)); -#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 (readSelectorValue(_segMan, obj, SELECTOR(nodePtr))) { - _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED); - _state->sfx_remove_song(handle); - } - } - - if (!obj.segment || !_resMan->testResource(ResourceId(kResourceTypeSound, resourceId))) - return; - - _state->sfx_add_song(build_iterator(_resMan, resourceId, type, handle), 0, handle, resourceId); - - - // Notify the engine - if (_soundVersion <= SCI_VERSION_0_LATE) - writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundInitialized); - else - writeSelector(_segMan, obj, SELECTOR(nodePtr), obj); - - writeSelector(_segMan, obj, SELECTOR(handle), obj); - -#else - // Check if a track with the same sound object is already playing MusicEntry *oldSound = _music->getSlot(obj); if (oldSound) - cmdDisposeSound(obj, value); + processDisposeSound(obj); MusicEntry *newSound = new MusicEntry(); newSound->resourceId = resourceId; @@ -307,6 +73,9 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) { if (_soundVersion >= SCI_VERSION_1_EARLY) newSound->volume = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, MUSIC_VOLUME_MAX); + debugC(2, kDebugLevelSound, "kDoSound(init): number %d, loop %d, prio %d, vol %d", resourceId, + newSound->loop, newSound->priority, newSound->volume); + // 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 @@ -333,98 +102,28 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) { writeSelector(_segMan, obj, SELECTOR(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, readSelectorValue(_segMan, obj, SELECTOR(loop))); - writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundPlaying); - } else if (_soundVersion == SCI_VERSION_1_EARLY) { - _state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING); - _state->sfx_song_set_loops(handle, readSelectorValue(_segMan, obj, SELECTOR(loop))); - _state->sfx_song_renice(handle, readSelectorValue(_segMan, obj, SELECTOR(pri))); - RESTORE_BEHAVIOR rb = (RESTORE_BEHAVIOR) value; /* Too lazy to look up a default value for this */ - _state->_songlib.setSongRestoreBehavior(handle, rb); - writeSelectorValue(_segMan, obj, SELECTOR(signal), 0); - } else if (_soundVersion == SCI_VERSION_1_LATE) { - int looping = readSelectorValue(_segMan, obj, SELECTOR(loop)); - //int vol = readSelectorValue(_segMan, obj, SELECTOR(vol)); - int pri = readSelectorValue(_segMan, obj, SELECTOR(pri)); - int sampleLen = 0; - Song *song = _state->_songlib.findSong(handle); - int songNumber = readSelectorValue(_segMan, obj, SELECTOR(number)); - - if (readSelectorValue(_segMan, obj, SELECTOR(nodePtr)) && (song && songNumber != song->_resourceNum)) { - _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED); - _state->sfx_remove_song(handle); - writeSelector(_segMan, obj, SELECTOR(nodePtr), NULL_REG); - } - - if (!readSelectorValue(_segMan, obj, SELECTOR(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); - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); - return; - } - debugC(2, kDebugLevelSound, "Initializing song number %d", songNumber); - _state->sfx_add_song(build_iterator(_resMan, songNumber, SCI_SONG_ITERATOR_TYPE_SCI1, - handle), 0, handle, songNumber); - } - - writeSelector(_segMan, obj, SELECTOR(nodePtr), obj); - writeSelector(_segMan, obj, SELECTOR(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); - writeSelectorValue(_segMan, obj, SELECTOR(signal), 0); - } - } - -#else +reg_t SoundCommandParser::kDoSoundPlay(int argc, reg_t *argv, reg_t acc) { + processPlaySound(argv[0]); + return acc; +} +void SoundCommandParser::processPlaySound(reg_t obj) { MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { - warning("cmdPlaySound: Slot not found (%04x:%04x)", PRINT_REG(obj)); + warning("kDoSound(play): Slot not found (%04x:%04x)", PRINT_REG(obj)); return; } int resourceId = obj.segment ? readSelectorValue(_segMan, obj, SELECTOR(number)) : -1; if (musicSlot->resourceId != resourceId) { // another sound loaded into struct - cmdDisposeSound(obj, value); - cmdInitSound(obj, value); + processDisposeSound(obj); + processInitSound(obj); // Find slot again :) musicSlot = _music->getSlot(obj); } - int16 loop = readSelectorValue(_segMan, obj, SELECTOR(loop)); - debugC(2, kDebugLevelSound, "cmdPlaySound: resource number %d, loop %d", resourceId, loop); writeSelector(_segMan, obj, SELECTOR(handle), obj); @@ -442,51 +141,31 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) { musicSlot->priority = readSelectorValue(_segMan, obj, SELECTOR(priority)); if (_soundVersion >= SCI_VERSION_1_EARLY) musicSlot->volume = readSelectorValue(_segMan, obj, SELECTOR(vol)); - _music->soundPlay(musicSlot); -#endif + debugC(2, kDebugLevelSound, "kDoSound(play): number %d, loop %d, prio %d, vol %d", resourceId, + musicSlot->loop, musicSlot->priority, musicSlot->volume); + _music->soundPlay(musicSlot); } -void SoundCommandParser::cmdDummy(reg_t obj, int16 value) { +reg_t SoundCommandParser::kDoSoundDummy(int argc, reg_t *argv, reg_t acc) { warning("cmdDummy invoked"); // not supposed to occur + return acc; } -#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) - writeSelectorValue(_segMan, obj, SELECTOR(state), newStatus); - } +reg_t SoundCommandParser::kDoSoundDispose(int argc, reg_t *argv, reg_t acc) { + processDisposeSound(argv[0]); + return acc; } -#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) - writeSelectorValue(_segMan, obj, SELECTOR(handle), 0x0000); - } - -#else +void SoundCommandParser::processDisposeSound(reg_t obj) { MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { - warning("cmdDisposeSound: Slot not found (%04x:%04x)", PRINT_REG(obj)); + warning("kDoSound(dispose): Slot not found (%04x:%04x)", PRINT_REG(obj)); return; } - cmdStopSound(obj, value); + processStopSound(obj, false); _music->soundKill(musicSlot); writeSelectorValue(_segMan, obj, SELECTOR(handle), 0); @@ -494,26 +173,17 @@ void SoundCommandParser::cmdDisposeSound(reg_t obj, int16 value) { writeSelector(_segMan, obj, SELECTOR(nodePtr), NULL_REG); else writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundStopped); -#endif } -void SoundCommandParser::cmdStopSound(reg_t obj, int16 value) { - processStopSound(obj, value, false); +reg_t SoundCommandParser::kDoSoundStop(int argc, reg_t *argv, reg_t acc) { + processStopSound(argv[0], false); + return acc; } -void SoundCommandParser::processStopSound(reg_t obj, int16 value, bool sampleFinishedPlaying) { - if (!obj.segment) - return; - -#ifdef USE_OLD_MUSIC_FUNCTIONS - changeSoundStatus(obj, SOUND_STATUS_STOPPED); - - if (_soundVersion >= SCI_VERSION_1_EARLY) - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); -#else +void SoundCommandParser::processStopSound(reg_t obj, bool sampleFinishedPlaying) { MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { - warning("cmdStopSound: Slot not found (%04x:%04x)", PRINT_REG(obj)); + warning("kDoSound(stop): Slot not found (%04x:%04x)", PRINT_REG(obj)); return; } @@ -523,204 +193,156 @@ void SoundCommandParser::processStopSound(reg_t obj, int16 value, bool sampleFin writeSelectorValue(_segMan, obj, SELECTOR(handle), 0); } - // Set signal selector in sound SCI0 games only, when the sample has finished playing - // If we don't set it at all, we get a problem when using vaporizer on the 2 guys - // If we set it all the time, we get no music in sq3new and kq1 - // FIXME: this *may* be wrong, it's impossible to find out in sierra DOS sci, because SCI0 under DOS didn't have - // sfx drivers included - // We need to set signal in sound SCI1+ games all the time + // Set signal selector in sound SCI0 games only, when the sample has + // finished playing. If we don't set it at all, we get a problem when using + // vaporizer on the 2 guys. If we set it all the time, we get no music in + // sq3new and kq1. + // FIXME: This *may* be wrong, it's impossible to find out in Sierra DOS + // SCI, because SCI0 under DOS didn't have sfx drivers included. + // We need to set signal in sound SCI1+ games all the time. if ((_soundVersion > SCI_VERSION_0_LATE) || sampleFinishedPlaying) writeSelectorValue(_segMan, obj, SELECTOR(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 +reg_t SoundCommandParser::kDoSoundPause(int argc, reg_t *argv, reg_t acc) { + if (_soundVersion <= SCI_VERSION_0_LATE) { + // SCI0 games give us 0/1 for either resuming or pausing the current music + // this one doesn't count, so pausing 2 times and resuming once means here that we are supposed to resume + uint16 value = argv[0].toUint16(); + MusicEntry *musicSlot = _music->getActiveSci0MusicSlot(); + switch (value) { + case 1: + if ((musicSlot) && (musicSlot->status == kSoundPlaying)) { + _music->soundPause(musicSlot); + writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPaused); + } + return make_reg(0, 0); + case 0: + if ((musicSlot) && (musicSlot->status == kSoundPaused)) { + _music->soundResume(musicSlot); + writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying); + return make_reg(0, 1); + } + return make_reg(0, 0); + default: + error("kDoSound(pause): parameter 0 is invalid for sound-sci0"); + } + } + reg_t obj = argv[0]; + uint16 value = argc > 1 ? argv[1].toUint16() : 0; if (!obj.segment) { // pause the whole playlist - // Pausing/Resuming the whole playlist was introduced - // in the SCI1 late sound scheme - if (_soundVersion <= SCI_VERSION_1_EARLY) - return; - _music->pauseAll(value); } else { // pause a playlist slot MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { - warning("cmdPauseSound: Slot not found (%04x:%04x)", PRINT_REG(obj)); - return; + warning("kDoSound(pause): Slot not found (%04x:%04x)", PRINT_REG(obj)); + return acc; } - if (_soundVersion <= SCI_VERSION_0_LATE) { - // Always pause the sound in SCI0 games. It's resumed in cmdResumeSound() - writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPaused); - _music->soundPause(musicSlot); - } else { - _music->soundToggle(musicSlot, value); - } + _music->soundToggle(musicSlot, value); } - -#endif + return acc; } -void SoundCommandParser::cmdResumeSound(reg_t obj, int16 value) { - // SCI0 only command - - if (!obj.segment) - return; +// SCI0 only command +reg_t SoundCommandParser::kDoSoundResume(int argc, reg_t *argv, reg_t acc) { + // this doesn't seem to do what we think it's doing + // it's called with no arguments at all (just restore a game in qfg1) + return acc; + reg_t obj = argv[0]; -#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; + warning("kDoSound(resume):: Slot not found (%04x:%04x)", PRINT_REG(obj)); + return acc; } writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying); _music->soundResume(musicSlot); -#endif + return acc; } -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 +reg_t SoundCommandParser::kDoSoundMute(int argc, reg_t *argv, reg_t acc) { + if (argc > 0) + _music->soundSetSoundOn(argv[0].toUint16()); + return make_reg(0, _music->soundGetSoundOn()); } -void SoundCommandParser::cmdMasterVolume(reg_t obj, int16 value) { -#ifdef USE_OLD_MUSIC_FUNCTIONS - _acc = make_reg(0, _state->sfx_getVolume()); - - if (obj != SIGNAL_REG) - _state->sfx_setVolume(obj.toSint16()); -#else - debugC(2, kDebugLevelSound, "cmdMasterVolume: %d", value); - _acc = make_reg(0, _music->soundGetMasterVolume()); +reg_t SoundCommandParser::kDoSoundMasterVolume(int argc, reg_t *argv, reg_t acc) { + acc = make_reg(0, _music->soundGetMasterVolume()); - if (_argc > 1) { // the first parameter is the sound command - int vol = CLIP<int16>(obj.toSint16(), 0, kMaxSciVolume); + if (argc > 0) { + debugC(2, kDebugLevelSound, "kDoSound(masterVolume): %d", argv[0].toSint16()); + int vol = CLIP<int16>(argv[0].toSint16(), 0, kMaxSciVolume); vol = vol * Audio::Mixer::kMaxMixerVolume / kMaxSciVolume; ConfMan.setInt("music_volume", vol); ConfMan.setInt("sfx_volume", vol); g_engine->syncSoundSettings(); } -#endif + return acc; } -void SoundCommandParser::cmdFadeSound(reg_t obj, int16 value) { - if (!obj.segment) - return; +reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) { + reg_t obj = argv[0]; -#ifdef USE_OLD_MUSIC_FUNCTIONS - SongHandle handle = FROBNICATE_HANDLE(obj); - if (_soundVersion != SCI_VERSION_1_LATE) { - /* 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) - writeSelectorValue(_segMan, obj, SELECTOR(state), SOUND_STATUS_STOPPED); - writeSelectorValue(_segMan, obj, SELECTOR(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()) { - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); - _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED); - } else { - // FIXME: Support fade-and-continue. For now, send signal right away. - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); - } - } -#else MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { - warning("cmdFadeSound: Slot not found (%04x:%04x)", PRINT_REG(obj)); - return; + warning("kDoSound(fade): Slot not found (%04x:%04x)", PRINT_REG(obj)); + return acc; } 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 + // If sound is not playing currently, set signal directly + if (musicSlot->status != kSoundPlaying) { + debugC(2, kDebugLevelSound, "kDoSound(fade): fading requested, but sound is currently not playing"); + writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + return acc; + } + + switch (argc) { + case 1: // 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: // SCI01+ - case 6: // SCI1+ (SCI1 late sound scheme), with fade and continue - 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(); + case 4: // SCI01+ + case 5: // SCI1+ (SCI1 late sound scheme), with fade and continue + musicSlot->fadeTo = CLIP<uint16>(argv[1].toUint16(), 0, MUSIC_VOLUME_MAX); + musicSlot->fadeStep = volume > argv[1].toUint16() ? -argv[3].toUint16() : argv[3].toUint16(); + musicSlot->fadeTickerStep = argv[2].toUint16() * 16667 / _music->soundGetTempo(); musicSlot->fadeTicker = 0; - musicSlot->stopAfterFading = (_argc == 6) ? (_argv[5].toUint16() != 0) : false; + musicSlot->stopAfterFading = (argc == 5) ? (argv[4].toUint16() != 0) : false; break; default: - error("cmdFadeSound: unsupported argc %d", _argc); - } - - // If sound is not playing currently, set signal directly - if (musicSlot->status != kSoundPlaying) { - warning("cmdFadeSound: fading requested, but sound is currently not playing"); - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + error("kDoSound(fade): unsupported argc %d", argc); } - debugC(2, kDebugLevelSound, "cmdFadeSound: to %d, step %d, ticker %d", musicSlot->fadeTo, musicSlot->fadeStep, musicSlot->fadeTickerStep); -#endif + debugC(2, kDebugLevelSound, "kDoSound(fade): to %d, step %d, ticker %d", musicSlot->fadeTo, musicSlot->fadeStep, musicSlot->fadeTickerStep); + return acc; } -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 +reg_t SoundCommandParser::kDoSoundGetPolyphony(int argc, reg_t *argv, reg_t acc) { + return make_reg(0, _music->soundGetVoices()); // Get the number of voices } -void SoundCommandParser::cmdUpdateSound(reg_t obj, int16 value) { - if (!obj.segment) - return; +reg_t SoundCommandParser::kDoSoundUpdate(int argc, reg_t *argv, reg_t acc) { + reg_t obj = argv[0]; -#ifdef USE_OLD_MUSIC_FUNCTIONS - SongHandle handle = FROBNICATE_HANDLE(obj); - if (_soundVersion <= SCI_VERSION_0_LATE && obj.segment) { - _state->sfx_song_set_loops(handle, readSelectorValue(_segMan, obj, SELECTOR(loop))); - script_set_priority(_resMan, _segMan, _state, obj, readSelectorValue(_segMan, obj, SELECTOR(pri))); - } -#else MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { - warning("cmdUpdateSound: Slot not found (%04x:%04x)", PRINT_REG(obj)); - return; + warning("kDoSound(update): Slot not found (%04x:%04x)", PRINT_REG(obj)); + return acc; } musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop)); @@ -730,88 +352,18 @@ void SoundCommandParser::cmdUpdateSound(reg_t obj, int16 value) { uint32 objPrio = readSelectorValue(_segMan, obj, SELECTOR(pri)); if (objPrio != musicSlot->priority) _music->soundSetPriority(musicSlot, objPrio); - -#endif + return acc; } -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", - PRINT_REG(obj), signal); - debugC(2, kDebugLevelSound, "abs-signal %04X", signal); - writeSelectorValue(_segMan, obj, SELECTOR(signal), signal); - break; - - case SI_RELATIVE_CUE: - debugC(2, kDebugLevelSound, "--- [CUE] %04x:%04x Relative Cue: %d", - PRINT_REG(obj), signal); - - /* FIXME to match commented-out semantics - * below, with proper storage of dataInc and - * signal in the iterator code. */ - writeSelectorValue(_segMan, obj, SELECTOR(dataInc), signal); - debugC(2, kDebugLevelSound, "rel-signal %04X", signal); - if (_soundVersion == SCI_VERSION_1_EARLY) - writeSelectorValue(_segMan, obj, SELECTOR(signal), signal); - else - writeSelectorValue(_segMan, obj, SELECTOR(signal), signal + 127); - break; - - case SI_FINISHED: - debugC(2, kDebugLevelSound, "--- [FINISHED] %04x:%04x", PRINT_REG(obj)); - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); - break; - - case SI_LOOP: - break; // Doesn't happen - } +reg_t SoundCommandParser::kDoSoundUpdateCues(int argc, reg_t *argv, reg_t acc) { + processUpdateCues(argv[0]); + return acc; +} - //switch (signal) { - //case 0x00: - // if (dataInc != readSelectorValue(segMan, obj, SELECTOR(dataInc))) { - // writeSelectorValue(segMan, obj, SELECTOR(dataInc), dataInc); - // writeSelectorValue(segMan, obj, SELECTOR(signal), dataInc+0x7f); - // } else { - // writeSelectorValue(segMan, obj, SELECTOR(signal), signal); - // } - // break; - //case 0xFF: // May be unnecessary - // s->_sound.sfx_song_set_status(handle, SOUND_STATUS_STOPPED); - // break; - //default : - // if (dataInc != readSelectorValue(segMan, obj, SELECTOR(dataInc))) { - // writeSelectorValue(segMan, obj, SELECTOR(dataInc), dataInc); - // writeSelectorValue(segMan, obj, SELECTOR(signal), dataInc + 0x7f); - // } else { - // writeSelectorValue(segMan, obj, SELECTOR(signal), signal); - // } - // break; - //} - - if (_soundVersion == SCI_VERSION_1_EARLY) { - writeSelectorValue(_segMan, obj, SELECTOR(min), min); - writeSelectorValue(_segMan, obj, SELECTOR(sec), sec); - writeSelectorValue(_segMan, obj, SELECTOR(frame), frame); - } -#else +void SoundCommandParser::processUpdateCues(reg_t obj) { MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { - warning("cmdUpdateCues: Slot not found (%04x:%04x)", PRINT_REG(obj)); + warning("kDoSound(updateCues): Slot not found (%04x:%04x)", PRINT_REG(obj)); return; } @@ -828,7 +380,7 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { musicSlot->sampleLoopCounter = currentLoopCounter; } if ((!_music->soundIsActive(musicSlot)) && (musicSlot->status != kSoundPaused)) { - processStopSound(obj, 0, true); + processStopSound(obj, true); } else { _music->updateAudioStreamTicker(musicSlot); } @@ -841,7 +393,7 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { // Update MIDI slots if (musicSlot->signal == 0) { if (musicSlot->dataInc != readSelectorValue(_segMan, obj, SELECTOR(dataInc))) { - if (_kernel->_selectorCache.dataInc > -1) + if (SELECTOR(dataInc) > -1) writeSelectorValue(_segMan, obj, SELECTOR(dataInc), musicSlot->dataInc); writeSelectorValue(_segMan, obj, SELECTOR(signal), musicSlot->dataInc + 127); } @@ -850,13 +402,15 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { writeSelectorValue(_segMan, obj, SELECTOR(signal), musicSlot->signal); // We need to do this especially because state selector needs to get updated if (musicSlot->signal == SIGNAL_OFFSET) - cmdStopSound(obj, 0); + processStopSound(obj, false); } } else { - // Slot actually has no data (which would mean that a sound-resource w/ unsupported data is used + // Slot actually has no data (which would mean that a sound-resource w/ + // unsupported data is used. // (example lsl5 - sound resource 744 - it's roland exclusive writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); - // If we don't set signal here, at least the switch to the mud wrestling room in lsl5 will not work + // If we don't set signal here, at least the switch to the mud wrestling + // room in lsl5 will not work. } if (musicSlot->fadeCompleted) { @@ -864,10 +418,10 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { // We need signal for sci0 at least in iceman as well (room 14, fireworks) writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); if (_soundVersion <= SCI_VERSION_0_LATE) { - cmdStopSound(obj, 0); + processStopSound(obj, false); } else { if (musicSlot->stopAfterFading) - cmdStopSound(obj, 0); + processStopSound(obj, false); } } @@ -882,48 +436,58 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { writeSelectorValue(_segMan, obj, SELECTOR(sec), musicSlot->ticker % 3600 / 60); writeSelectorValue(_segMan, obj, SELECTOR(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 +reg_t SoundCommandParser::kDoSoundSendMidi(int argc, reg_t *argv, reg_t acc) { + reg_t obj = argv[0]; + byte channel = argv[1].toUint16() & 0xf; + byte midiCmd = argv[2].toUint16() & 0xff; + + uint16 controller = argv[3].toUint16(); + uint16 param = argv[4].toUint16(); + + if (channel) + channel--; // channel is given 1-based, we are using 0-based + + uint32 midiCommand = (channel | midiCmd) | ((uint32)controller << 8) | ((uint32)param << 16); + + MusicEntry *musicSlot = _music->getSlot(obj); + if (!musicSlot) { + // TODO: maybe it's possible to call this with obj == 0:0 and send directly?! + // if so, allow it + //_music->sendMidiCommand(_midiCommand); + warning("kDoSound(sendMidi): Slot not found (%04x:%04x)", PRINT_REG(obj)); + return acc; + } + _music->sendMidiCommand(musicSlot, midiCommand); + return acc; } -void SoundCommandParser::cmdReverb(reg_t obj, int16 value) { -#ifndef USE_OLD_MUSIC_FUNCTIONS - _music->setReverb(obj.toUint16() & 0xF); -#endif +reg_t SoundCommandParser::kDoSoundReverb(int argc, reg_t *argv, reg_t acc) { + _music->setReverb(argv[0].toUint16() & 0xF); + return acc; } -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 +reg_t SoundCommandParser::kDoSoundSetHold(int argc, reg_t *argv, reg_t acc) { + reg_t obj = argv[0]; + MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { - warning("cmdSetSoundHold: Slot not found (%04x:%04x)", PRINT_REG(obj)); - return; + warning("kDoSound(setHold): Slot not found (%04x:%04x)", PRINT_REG(obj)); + return acc; } // Set the special hold marker ID where the song should be looped at. - musicSlot->hold = value; -#endif + musicSlot->hold = argv[1].toSint16(); + return acc; } -void SoundCommandParser::cmdGetAudioCapability(reg_t obj, int16 value) { +reg_t SoundCommandParser::kDoSoundGetAudioCapability(int argc, reg_t *argv, reg_t acc) { // Tests for digital audio support - _acc = make_reg(0, 1); + return make_reg(0, 1); } -void SoundCommandParser::cmdStopAllSounds(reg_t obj, int16 value) { -#ifndef USE_OLD_MUSIC_FUNCTIONS +reg_t SoundCommandParser::kDoSoundStopAll(int argc, reg_t *argv, reg_t acc) { Common::StackLock(_music->_mutex); const MusicList::iterator end = _music->getPlayListEnd(); @@ -931,32 +495,31 @@ void SoundCommandParser::cmdStopAllSounds(reg_t obj, int16 value) { if (_soundVersion <= SCI_VERSION_0_LATE) { writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(state), kSoundStopped); } else { - writeSelectorValue(_segMan, obj, SELECTOR(handle), 0); + writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(handle), 0); writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal), SIGNAL_OFFSET); } (*i)->dataInc = 0; _music->soundStop(*i); } -#endif + return acc; } -void SoundCommandParser::cmdSetSoundVolume(reg_t obj, int16 value) { - if (!obj.segment) - return; +reg_t SoundCommandParser::kDoSoundSetVolume(int argc, reg_t *argv, reg_t acc) { + reg_t obj = argv[0]; + int16 value = argv[1].toSint16(); -#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 + // 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; + return acc; } - debugC(2, kDebugLevelSound, "cmdSetSoundVolume: %d", value); + debugC(2, kDebugLevelSound, "kDoSound(setVolume): %d", value); value = CLIP<int>(value, 0, MUSIC_VOLUME_MAX); @@ -965,20 +528,17 @@ void SoundCommandParser::cmdSetSoundVolume(reg_t obj, int16 value) { _music->soundSetVolume(musicSlot, value); writeSelectorValue(_segMan, obj, SELECTOR(vol), value); } -#endif + return acc; } -void SoundCommandParser::cmdSetSoundPriority(reg_t obj, int16 value) { - if (!obj.segment) - return; +reg_t SoundCommandParser::kDoSoundSetPriority(int argc, reg_t *argv, reg_t acc) { + reg_t obj = argv[0]; + int16 value = argv[1].toSint16(); -#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; + warning("kDoSound(setPriority): Slot not found (%04x:%04x)", PRINT_REG(obj)); + return acc; } if (value == -1) { @@ -987,7 +547,7 @@ void SoundCommandParser::cmdSetSoundPriority(reg_t obj, int16 value) { 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"); + warning("kDoSound(setPriority): Attempt to unset song priority when there is no built-in value"); //pSnd->prio=0;field_15B=0 writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) & 0xFD); @@ -998,32 +558,26 @@ void SoundCommandParser::cmdSetSoundPriority(reg_t obj, int16 value) { writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) | 2); //DoSOund(0xF,hobj,w) } -#endif + return acc; } -void SoundCommandParser::cmdSetSoundLoop(reg_t obj, int16 value) { - if (!obj.segment) - return; +reg_t SoundCommandParser::kDoSoundSetLoop(int argc, reg_t *argv, reg_t acc) { + reg_t obj = argv[0]; + int16 value = argv[1].toSint16(); -#ifdef USE_OLD_MUSIC_FUNCTIONS - if (!readSelector(_segMan, obj, SELECTOR(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 + // 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)); + warning("kDoSound(setLoop): Slot not found (%04x:%04x) and the song was requested to be looped", PRINT_REG(obj)); } else { // Doesn't really matter } - return; + return acc; } if (value == -1) { musicSlot->loop = 0xFFFF; @@ -1032,40 +586,37 @@ void SoundCommandParser::cmdSetSoundLoop(reg_t obj, int16 value) { } writeSelectorValue(_segMan, obj, SELECTOR(loop), musicSlot->loop); -#endif + return acc; } -void SoundCommandParser::cmdSuspendSound(reg_t obj, int16 value) { +reg_t SoundCommandParser::kDoSoundSuspend(int argc, reg_t *argv, reg_t acc) { // TODO - warning("STUB: cmdSuspendSound"); + warning("kDoSound(suspend): STUB"); + return acc; } -#ifndef USE_OLD_MUSIC_FUNCTIONS - void SoundCommandParser::updateSci0Cues() { bool noOnePlaying = true; MusicEntry *pWaitingForPlay = NULL; - _music->_mutex.lock(); - 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 + // this sound, as SCI0 only allows one active song. if ((*i)->isQueued) { pWaitingForPlay = (*i); - // FIXME (?) - in iceman 2 songs are queued when playing the door sound - if we use the first song for resuming - // then it's the wrong one. Both songs have same priority. Maybe the new sound function in sci0 - // is somehow responsible + // FIXME(?): In iceman 2 songs are queued when playing the door + // sound - if we use the first song for resuming then it's the wrong + // one. Both songs have same priority. Maybe the new sound function + // in sci0 is somehow responsible. continue; } if ((*i)->signal == 0 && (*i)->status != kSoundPlaying) continue; - cmdUpdateCues((*i)->soundObj, 0); + processUpdateCues((*i)->soundObj); noOnePlaying = false; } - _music->_mutex.unlock(); if (noOnePlaying && pWaitingForPlay) { // If there is a queued entry, play it now ffs: SciMusic::soundPlay() @@ -1074,90 +625,40 @@ void SoundCommandParser::updateSci0Cues() { } } -#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); - - const MusicList::iterator end = _music->getPlayListEnd(); - for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) { - if ((*i)->resourceId && _resMan->testResource(ResourceId(kResourceTypeSound, (*i)->resourceId))) { - (*i)->soundRes = new SoundResource((*i)->resourceId, _resMan, _soundVersion); - _music->soundInitSnd(*i); - } else { - (*i)->soundRes = 0; - } - if ((*i)->status == kSoundPlaying) { - if (savegame_version < 14) { - (*i)->dataInc = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(dataInc)); - (*i)->signal = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal)); - - if (_soundVersion >= SCI_VERSION_1_LATE) - (*i)->volume = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(vol)); - } - - cmdPlaySound((*i)->soundObj, 0); - } - } - -#endif } void SoundCommandParser::printPlayList(Console *con) { -#ifndef USE_OLD_MUSIC_FUNCTIONS _music->printPlayList(con); -#endif } void SoundCommandParser::printSongInfo(reg_t obj, Console *con) { -#ifndef USE_OLD_MUSIC_FUNCTIONS _music->printSongInfo(obj, con); -#endif } void SoundCommandParser::stopAllSounds() { -#ifndef USE_OLD_MUSIC_FUNCTIONS _music->stopAll(); -#endif } void SoundCommandParser::startNewSound(int number) { -#ifndef USE_OLD_MUSIC_FUNCTIONS Common::StackLock lock(_music->_mutex); // Overwrite the first sound in the playlist MusicEntry *song = *_music->getPlayListStart(); reg_t soundObj = song->soundObj; - cmdDisposeSound(soundObj, 0); + processDisposeSound(soundObj); writeSelectorValue(_segMan, soundObj, SELECTOR(number), number); - cmdInitSound(soundObj, 0); - cmdPlaySound(soundObj, 0); -#endif + processInitSound(soundObj); + processPlaySound(soundObj); } void SoundCommandParser::setMasterVolume(int vol) { -#ifndef USE_OLD_MUSIC_FUNCTIONS _music->soundSetMasterVolume(vol); -#endif } void SoundCommandParser::pauseAll(bool pause) { -#ifndef USE_OLD_MUSIC_FUNCTIONS _music->pauseAll(pause); -#endif } } // End of namespace Sci diff --git a/engines/sci/sound/soundcmd.h b/engines/sci/sound/soundcmd.h index 09cca23450..a8bc1eb280 100644 --- a/engines/sci/sound/soundcmd.h +++ b/engines/sci/sound/soundcmd.h @@ -26,8 +26,6 @@ #ifndef SCI_SOUNDCMD_H #define SCI_SOUNDCMD_H -#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS - #include "common/list.h" #include "sci/engine/state.h" @@ -36,13 +34,13 @@ namespace Sci { class Console; class SciMusic; class SoundCommandParser; -typedef void (SoundCommandParser::*SoundCommand)(reg_t obj, int16 value); +//typedef void (SoundCommandParser::*SoundCommand)(reg_t obj, int16 value); -struct MusicEntryCommand { - MusicEntryCommand(const char *d, SoundCommand c) : sndCmd(c), desc(d) {} - SoundCommand sndCmd; - const char *desc; -}; +//struct MusicEntryCommand { +// MusicEntryCommand(const char *d, SoundCommand c) : sndCmd(c), desc(d) {} +// SoundCommand sndCmd; +// const char *desc; +//}; class SoundCommandParser { public: @@ -53,11 +51,7 @@ public: kMaxSciVolume = 15 }; -#ifdef USE_OLD_MUSIC_FUNCTIONS - void updateSfxState(SfxState *newState) { _state = newState; } -#endif - - reg_t parseCommand(int argc, reg_t *argv, reg_t acc); + //reg_t parseCommand(int argc, reg_t *argv, reg_t acc); // Functions used for game state loading void clearPlayList(); @@ -69,14 +63,14 @@ public: void pauseAll(bool pause); // Debug console functions - void playSound(reg_t obj) { cmdPlaySound(obj, 0); } - void stopSound(reg_t obj) { cmdStopSound(obj, 0); } void startNewSound(int number); void stopAllSounds(); void printPlayList(Console *con); void printSongInfo(reg_t obj, Console *con); -#ifndef USE_OLD_MUSIC_FUNCTIONS + void processPlaySound(reg_t obj); + void processStopSound(reg_t obj, bool sampleFinishedPlaying); + /** * Synchronizes the current state of the music list to the rest of the engine, so that * the changes that the sound thread makes to the music are registered with the engine @@ -85,56 +79,43 @@ public: * by the engine scripts themselves, so the engine itself polls for changes to the music */ void updateSci0Cues(); -#endif + + reg_t kDoSoundInit(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundPlay(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundDummy(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundMute(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundPause(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundResume(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundStop(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundStopAll(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundDispose(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundMasterVolume(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundFade(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundGetPolyphony(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundUpdate(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundUpdateCues(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundSendMidi(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundReverb(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundSetHold(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundGetAudioCapability(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundSetVolume(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundSetPriority(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundSetLoop(int argc, reg_t *argv, reg_t acc); + reg_t kDoSoundSuspend(int argc, reg_t *argv, reg_t acc); private: - typedef Common::Array<MusicEntryCommand *> SoundCommandContainer; - SoundCommandContainer _soundCommands; + //typedef Common::Array<MusicEntryCommand *> SoundCommandContainer; + //SoundCommandContainer _soundCommands; ResourceManager *_resMan; SegManager *_segMan; Kernel *_kernel; -#ifdef USE_OLD_MUSIC_FUNCTIONS - SfxState *_state; - int _midiCmd, _controller, _param; -#else SciMusic *_music; -#endif AudioPlayer *_audio; SciVersion _soundVersion; - int _argc; - reg_t *_argv; // for cmdFadeSound - uint32 _midiCommand; // for cmdSendMidi - reg_t _acc; - int _cmdUpdateCuesIndex; - - void cmdInitSound(reg_t obj, int16 value); - void cmdPlaySound(reg_t obj, int16 value); - void cmdDummy(reg_t obj, int16 value); - void cmdMuteSound(reg_t obj, int16 value); - void cmdPauseSound(reg_t obj, int16 value); - void cmdResumeSound(reg_t obj, int16 value); - void cmdStopSound(reg_t obj, int16 value); - void cmdDisposeSound(reg_t obj, int16 value); - void cmdMasterVolume(reg_t obj, int16 value); - void cmdFadeSound(reg_t obj, int16 value); - void cmdGetPolyphony(reg_t obj, int16 value); - void cmdStopAllSounds(reg_t obj, int16 value); - void cmdUpdateSound(reg_t obj, int16 value); - void cmdUpdateCues(reg_t obj, int16 value); - void cmdSendMidi(reg_t obj, int16 value); - void cmdReverb(reg_t obj, int16 value); - void cmdSetSoundHold(reg_t obj, int16 value); - void cmdGetAudioCapability(reg_t obj, int16 value); - void cmdSetSoundVolume(reg_t obj, int16 value); - void cmdSetSoundPriority(reg_t obj, int16 value); - void cmdSetSoundLoop(reg_t obj, int16 value); - void cmdSuspendSound(reg_t obj, int16 value); - - void processStopSound(reg_t obj, int16 value, bool sampleFinishedPlaying); - -#ifdef USE_OLD_MUSIC_FUNCTIONS - void changeSoundStatus(reg_t obj, int newStatus); -#endif + + void processInitSound(reg_t obj); + void processDisposeSound(reg_t obj); + void processUpdateCues(reg_t obj); }; } // End of namespace Sci |