diff --git a/TODO b/TODO index fd626d3848cf..16755f42aacc 100644 --- a/TODO +++ b/TODO @@ -255,9 +255,6 @@ SCUMM on small devices * Fix codec44 for nut fonts * iMUSE Digital: - - Cleanup startSound code - - Make struct sync, jump, regions use dynamic memory usage - - Add pool method for FT voc samples from resource - Fix music code - Recheck 'next region' code - Implement pool method data transfer between imuse and sound mixer diff --git a/dists/msvc7/scumm.vcproj b/dists/msvc7/scumm.vcproj index 4566e52cc8d2..58bb91451842 100644 --- a/dists/msvc7/scumm.vcproj +++ b/dists/msvc7/scumm.vcproj @@ -175,6 +175,9 @@ + + @@ -184,6 +187,9 @@ + + + RelativePath="..\..\scumm\scumm.cpp"> + RelativePath="..\..\scumm\scumm.h"> diff --git a/scumm/imuse_digi/dimuse.cpp b/scumm/imuse_digi/dimuse.cpp index 97628c15982e..3a1700c25b7b 100644 --- a/scumm/imuse_digi/dimuse.cpp +++ b/scumm/imuse_digi/dimuse.cpp @@ -71,18 +71,6 @@ void IMuseDigital::resetState() { _curSeqAtribPos = 0; } -void IMuseDigital::setGroupVoiceVolume(int volume) { - _volVoice = volume; -} - -void IMuseDigital::setGroupMusicVolume(int volume) { - _volMusic = volume; -} - -void IMuseDigital::setGroupSfxVolume(int volume) { - _volSfx = volume; -} - void IMuseDigital::callback() { Common::StackLock lock(_mutex, "IMuseDigital::callback()"); int l = 0; @@ -138,11 +126,11 @@ void IMuseDigital::callback() { int pan = (_track[l].pan != 64) ? 2 * _track[l].pan - 127 : 0; int vol = _track[l].vol / 1000; - if (_track[l].soundGroup == 1) + if (_track[l].volGroupId == 1) vol = (vol * _volVoice) / 128; - if (_track[l].soundGroup == 2) + if (_track[l].volGroupId == 2) vol = (vol * _volSfx) / 128; - if (_track[l].soundGroup == 3) + if (_track[l].volGroupId == 3) vol = (vol * _volMusic) / 128; if (_vm->_mixer->isReady()) { @@ -257,524 +245,4 @@ void IMuseDigital::switchToNextRegion(int track) { _track[track].regionOffset = 0; } -void IMuseDigital::startSound(int soundId, const char *soundName, int soundType, int soundGroup, AudioStream *input, int hookId, int volume, int priority) { - Common::StackLock lock(_mutex, "IMuseDigital::startSound()"); - debug(5, "IMuseDigital::startSound(%d)", soundId); - int l; - int lower_priority = 127; - bool found_free = false; - - for (l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if (!_track[l].used && !_track[l].handle.isActive()) - found_free = true; - } - - if (!found_free) { - warning("IMuseDigital::startSound(): All slots are full"); - for (l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if (_track[l].used && _track[l].handle.isActive() && - (lower_priority > _track[l].priority) && (!_track[l].stream2)) - lower_priority = _track[l].priority; - } - if (lower_priority <= priority) { - int track_id = -1; - for (l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if (_track[l].used && _track[l].handle.isActive() && - (lower_priority == _track[l].priority) && (!_track[l].stream2)) { - track_id = l; - } - } - assert(track_id != -1); - _track[track_id].stream->finish(); - _track[track_id].stream = NULL; - _vm->_mixer->stopHandle(_track[track_id].handle); - _sound->closeSound(_track[track_id].soundHandle); - _track[track_id].used = false; - assert(!_track[track_id].handle.isActive()); - warning("IMuseDigital::startSound(): Removed sound %d from track %d", _track[track_id].soundId, track_id); - } else { - warning("IMuseDigital::startSound(): Priority sound too low"); - return; - } - } - - for (l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if (!_track[l].used && !_track[l].handle.isActive()) { - _track[l].pan = 64; - _track[l].vol = volume * 1000; - _track[l].volFadeDest = 0; - _track[l].volFadeStep = 0; - _track[l].volFadeDelay = 0; - _track[l].volFadeUsed = false; - _track[l].soundId = soundId; - _track[l].started = false; - _track[l].soundGroup = soundGroup; - _track[l].curHookId = hookId; - _track[l].priority = priority; - _track[l].curRegion = -1; - _track[l].dataOffset = 0; - _track[l].regionOffset = 0; - _track[l].trackOffset = 0; - _track[l].mod = 0; - _track[l].toBeRemoved = false; - - int bits = 0, freq = 0, channels = 0, mixerFlags = 0; - - if (input) { - _track[l].iteration = 1; // ? - // Do nothing here, we already have an audio stream - } else { - _track[l].soundHandle = _sound->openSound(soundId, soundName, soundType, soundGroup); - - if (_track[l].soundHandle == NULL) - return; - - bits = _sound->getBits(_track[l].soundHandle); - channels = _sound->getChannels(_track[l].soundHandle); - freq = _sound->getFreq(_track[l].soundHandle); - - if ((soundId == kTalkSoundID) && (soundType == IMUSE_BUNDLE)) { - if (_vm->_actorToPrintStrFor != 0xFF && _vm->_actorToPrintStrFor != 0) { - Actor *a = _vm->derefActor(_vm->_actorToPrintStrFor, "IMuseDigital::startSound"); - freq = (freq * a->talkFrequency) / 256; - _track[l].pan = a->talkPan; - _track[l].vol = a->talkVolume * 1000; - } - } - - assert(bits == 8 || bits == 12 || bits == 16); - assert(channels == 1 || channels == 2); - assert(0 < freq && freq <= 65535); - - // Round the frequency to a multiple of 25. This is done to - // ensure we don't run into data under-/overflows (this is a - // design limitation of the current IMuseDigital code, which - // pushes data 'blindly' into the mixer, instead of providing - // a pull based interface, i.e. a custom AudioInputStream - // subclass). - freq -= (freq % 25); - - _track[l].iteration = _track[l].pullSize = freq * channels; - - if (channels == 2) - mixerFlags = SoundMixer::FLAG_STEREO | SoundMixer::FLAG_REVERSE_STEREO; - - if ((bits == 12) || (bits == 16)) { - mixerFlags |= SoundMixer::FLAG_16BITS; - _track[l].iteration = _track[l].pullSize *= 2; - } else if (bits == 8) { - mixerFlags |= SoundMixer::FLAG_UNSIGNED; - } else - error("IMuseDigital::startSound(): Can't handle %d bit samples", bits); - - _track[l].pullSize /= 25; // We want a "frame rate" of 25 audio blocks per second - } - - if (input) { - _track[l].stream2 = input; - _track[l].stream = NULL; - } else { - _track[l].stream2 = NULL; - _track[l].stream = makeAppendableAudioStream(freq, mixerFlags, 100000); - _vm->_mixer->playInputStream(&_track[l].handle, _track[l].stream, false, _track[l].vol / 1000, _track[l].pan, -1); - } - - _track[l].used = true; - return; - } - } - - warning("it should not happen"); - assert(0); -} - -void IMuseDigital::stopSound(int soundId) { - Common::StackLock lock(_mutex, "IMuseDigital::stopSound()"); - debug(5, "IMuseDigital::stopSound(%d)", soundId); - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if ((_track[l].soundId == soundId) && _track[l].used) { - if (_track[l].stream) { - _track[l].toBeRemoved = true; - } - else if (_track[l].stream2) - _vm->_mixer->stopHandle(_track[l].handle); - } - } -} - -void IMuseDigital::setPriority(int soundId, int priority) { - Common::StackLock lock(_mutex, "IMuseDigital::setPriority()"); - debug(5, "IMuseDigital::setPriority(%d, %d)", soundId, priority); - - assert ((priority >= 0) && (priority <= 127)); - - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if ((_track[l].soundId == soundId) && _track[l].used) { - _track[l].priority = priority; - } - } -} - -void IMuseDigital::setVolume(int soundId, int volume) { - Common::StackLock lock(_mutex, "IMuseDigital::setVolume()"); - debug(5, "IMuseDigital::setVolume(%d, %d)", soundId, volume); - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if ((_track[l].soundId == soundId) && _track[l].used) { - _track[l].vol = volume * 1000; - } - } -} - -void IMuseDigital::setPan(int soundId, int pan) { - Common::StackLock lock(_mutex, "IMuseDigital::setPan()"); - debug(5, "IMuseDigital::setPan(%d, %d)", soundId, pan); - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if ((_track[l].soundId == soundId) && _track[l].used) { - _track[l].pan = pan; - } - } -} - -void IMuseDigital::selectGroupVolume(int soundId, int groupId) { - Common::StackLock lock(_mutex, "IMuseDigital::setGroupVolume()"); - debug(5, "IMuseDigital::setGroupVolume(%d, %d)", soundId, groupId); - assert((groupId >= 1) && (groupId <= 3)); - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if ((_track[l].soundId == soundId) && _track[l].used) { - _track[l].soundGroup = groupId; - } - } -} - -void IMuseDigital::setFade(int soundId, int destVolume, int delay60HzTicks) { - Common::StackLock lock(_mutex, "IMuseDigital::setFade()"); - debug(5, "IMuseDigital::setFade(%d, %d, %d)", soundId, destVolume, delay60HzTicks); - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if ((_track[l].soundId == soundId) && _track[l].used) { - _track[l].volFadeDelay = delay60HzTicks; - _track[l].volFadeDest = destVolume * 1000; - _track[l].volFadeStep = (_track[l].volFadeDest - _track[l].vol) * 60 * 40 / (1000 * delay60HzTicks); - _track[l].volFadeUsed = true; - } - } -} - -void IMuseDigital::refreshScripts() { - bool found = false; - { - Common::StackLock lock(_mutex, "IMuseDigital::refreshScripts()"); - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if ((_track[l].used) && (_track[l].soundGroup == IMUSE_MUSIC) && (!_track[l].volFadeUsed)) { - found = true; - } - } - } - - if ((!found) && (_curMusicSeq != 0)) { - parseScriptCmds(0x1001, 0, 0, 0, 0, 0, 0, 0); - } -} - -void IMuseDigital::stopAllSounds(bool waitForStop) { - debug(5, "IMuseDigital::stopAllSounds"); - { - Common::StackLock lock(_mutex, "IMuseDigital::stopAllSounds()"); - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if (_track[l].used) { - if (_track[l].stream) { - _track[l].toBeRemoved = true; - } else if (_track[l].stream2) - _vm->_mixer->stopHandle(_track[l].handle); - } - } - } - - if (waitForStop) { - bool used; - do { - used = false; - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if (_track[l].used) - used = true; - } - g_system->delay_msecs(10); - } while (used); - } -} - -void IMuseDigital::fadeOutMusic(int fadeDelay) { - Common::StackLock lock(_mutex, "IMuseDigital::fadeOutMusic()"); - debug(5, "IMuseDigital::fadeOutMusic"); - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if ((_track[l].used) && (_track[l].soundGroup == IMUSE_MUSIC) && (!_track[l].volFadeUsed)) { - _track[l].volFadeDelay = fadeDelay; - _track[l].volFadeDest = 0; - _track[l].volFadeStep = (_track[l].volFadeDest - _track[l].vol) * 60 * 40 / (1000 * fadeDelay); - _track[l].volFadeUsed = true; - } - } -} - -void IMuseDigital::pause(bool p) { - Common::StackLock lock(_mutex, "IMuseDigital::pause()"); - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if (_track[l].used) { - _vm->_mixer->pauseHandle(_track[l].handle, p); - } - } - _pause = p; -} - -void IMuseDigital::parseScriptCmds(int a, int b, int c, int d, int e, int f, int g, int h) { - int cmd = a; - int soundId = b; - int sub_cmd = c; - - if (!cmd) - return; - - switch (cmd) { - case 10: // ImuseStopAllSounds - stopAllSounds(); - break; - case 12: // ImuseSetParam - switch (sub_cmd) { - case 0x400: // select group volume - selectGroupVolume(soundId, d); - break; - case 0x500: // set priority - setPriority(soundId, d); - break; - case 0x600: // set volume - setVolume(soundId, d); - break; - case 0x700: // set pan - setPan(soundId, d); - break; - default: - warning("IMuseDigital::doCommand SetParam DEFAULT command %d", sub_cmd); - break; - } - break; - case 14: // ImuseFadeParam - switch (sub_cmd) { - case 0x600: // set volume fading - if ((d != 0) && (e == 0)) - setVolume(soundId, d); - else if ((d == 0) && (e == 0)) - stopSound(soundId); - else - setFade(soundId, d, e); - break; - default: - warning("IMuseDigital::doCommand FadeParam DEFAULT sub command %d", sub_cmd); - break; - } - break; - case 25: // ImuseStartStream - debug(5, "ImuseStartStream (%d, %d, %d)", soundId, c, d); - break; - case 26: // ImuseSwitchStream - debug(5, "ImuseSwitchStream (%d, %d, %d, %d, %d)", soundId, c, d, e, f); - break; - case 0x1000: // ImuseSetState - debug(5, "ImuseSetState (%d)", b); - if ((_vm->_gameId == GID_DIG) && (_vm->_features & GF_DEMO)) { - if (b == 1) { - fadeOutMusic(200); - startMusic(1, 127); - } else { - if (getSoundStatus(2) == 0) { - fadeOutMusic(200); - startMusic(2, 127); - } - } - } else if ((_vm->_gameId == GID_CMI) && (_vm->_features & GF_DEMO)) { - fadeOutMusic(120); - if (b == 2) { - startMusic("in1.imx", 1100, 0, 127); - } else if (b == 4) { - startMusic("in2.imx", 1120, 0, 127); - } else if (b == 8) { - startMusic("out1.imx", 1140, 0, 127); - } else if (b == 9) { - startMusic("out2.imx", 1150, 0, 127); - } else if (b == 16) { - startMusic("gun.imx", 1210, 0, 127); - } else { - warning("imuse digital: set state unknown for cmi demo: %d, room: %d", b, _vm->_currentRoom); - } - } else if (_vm->_gameId == GID_DIG) { - setDigMusicState(b); - } else if (_vm->_gameId == GID_CMI) { - setComiMusicState(b); - } else if (_vm->_gameId == GID_FT) { - setFtMusicState(b); - } - break; - case 0x1001: // ImuseSetSequence - debug(5, "ImuseSetSequence (%d)", b); - if (_vm->_gameId == GID_DIG) { - setDigMusicSequence(b); - } else if (_vm->_gameId == GID_CMI) { - setComiMusicSequence(b); - } else if (_vm->_gameId == GID_FT) { - setFtMusicSequence(b); - } - break; - case 0x1002: // ImuseSetCuePoint - debug(5, "ImuseSetCuePoint (%d)", b); - if (_vm->_gameId == GID_FT) { - setFtMusicCuePoint(b); - } - break; - case 0x1003: // ImuseSetAttribute - debug(5, "ImuseSetAttribute (%d, %d)", b, c); - assert((_vm->_gameId == GID_DIG) || (_vm->_gameId == GID_FT)); - if (_vm->_gameId == GID_DIG) { - _attributes[b] = c; - } - break; - case 0x2000: // ImuseSetGroupSfxVolume - debug(5, "ImuseSetGroupSFXVolume (%d)", b); -// setGroupSfxVolume(b); - break; - case 0x2001: // ImuseSetGroupVoiceVolume - debug(5, "ImuseSetGroupVoiceVolume (%d)", b); -// setGroupVoiceVolume(b); - break; - case 0x2002: // ImuseSetGroupMusicVolume - debug(5, "ImuseSetGroupMusicVolume (%d)", b); -// setGroupMusicVolume(b); - break; - default: - warning("IMuseDigital::doCommand DEFAULT command %d", cmd); - } -} - -int IMuseDigital::getSoundStatus(int sound) const { - Common::StackLock lock(_mutex, "IMuseDigital::getSoundStatus()"); - debug(5, "IMuseDigital::getSoundStatus(%d)", sound); - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if ((_track[l].soundId == sound) && _track[l].used) { - return 1; - } - } - - return 0; -} - -void IMuseDigital::getLipSync(int soundId, int syncId, int32 msPos, int32 &width, int32 &height) { - int32 sync_size; - byte *sync_ptr; - - msPos /= 16; - if (msPos < 65536) { - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if ((_track[l].soundId == soundId) && _track[l].used) { - _sound->getSyncSizeAndPtrById(_track[l].soundHandle, syncId, sync_size, &sync_ptr); - if ((sync_size != 0) && (sync_ptr != NULL)) { - sync_size /= 4; - while (sync_size--) { - if (READ_BE_UINT16(sync_ptr) >= msPos) - break; - sync_ptr += 4; - } - if (sync_size < 0) - sync_ptr -= 4; - else - if (READ_BE_UINT16(sync_ptr) > msPos) - sync_ptr -= 4; - - width = sync_ptr[2]; - height = sync_ptr[3]; - return; - } - } - } - } -} - -int32 IMuseDigital::getPosInMs(int soundId) { - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if ((_track[l].soundId == soundId) && (_track[l].used)) { - int32 pos = (5 * (_track[l].dataOffset + _track[l].regionOffset)) / (_track[l].iteration / 200); - return pos; - } - } - - return 0; -} - -int32 IMuseDigital::getCurMusicPosInMs() { - Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicPosInMs()"); - int soundId = -1; - - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if ((_track[l].used) && (_track[l].soundGroup == IMUSE_MUSIC) && (!_track[l].volFadeUsed)) { - soundId = _track[l].soundId; - } - } - - int32 msPos = getPosInMs(soundId); - debug(5, "IMuseDigital::getCurMusicPosInMs(%d) = %d", soundId, msPos); - return msPos; -} - -int32 IMuseDigital::getCurVoiceLipSyncWidth() { - Common::StackLock lock(_mutex, "IMuseDigital::getCutVoiceLipSyncWidth()"); - int32 msPos = getPosInMs(kTalkSoundID) + 50; - int32 width = 0, height = 0; - - debug(5, "IMuseDigital::getCurVoiceLipSyncWidth(%d)", kTalkSoundID); - getLipSync(kTalkSoundID, 0, msPos, width, height); - return width; -} - -int32 IMuseDigital::getCurVoiceLipSyncHeight() { - Common::StackLock lock(_mutex, "IMuseDigital::getCurVoiceLipSyncHeight()"); - int32 msPos = getPosInMs(kTalkSoundID) + 50; - int32 width = 0, height = 0; - - debug(5, "IMuseDigital::getCurVoiceLipSyncHeight(%d)", kTalkSoundID); - getLipSync(kTalkSoundID, 0, msPos, width, height); - return height; -} - -int32 IMuseDigital::getCurMusicLipSyncWidth(int syncId) { - Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicLipSyncWidth()"); - int soundId = -1; - - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if ((_track[l].used) && (_track[l].soundGroup == IMUSE_MUSIC) && (!_track[l].volFadeUsed)) { - soundId = _track[l].soundId; - } - } - - int32 msPos = getPosInMs(soundId) + 50; - int32 width = 0, height = 0; - - debug(5, "IMuseDigital::getCurVoiceLipSyncWidth(%d, %d)", soundId, msPos); - getLipSync(soundId, syncId, msPos, width, height); - return width; -} - -int32 IMuseDigital::getCurMusicLipSyncHeight(int syncId) { - Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicLipSyncHeight()"); - int soundId = -1; - - for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { - if ((_track[l].used) && (_track[l].soundGroup == IMUSE_MUSIC) && (!_track[l].volFadeUsed)) { - soundId = _track[l].soundId; - } - } - - int32 msPos = getPosInMs(soundId) + 50; - int32 width = 0, height = 0; - - debug(5, "IMuseDigital::getCurVoiceLipSyncHeight(%d, %d)", soundId, msPos); - getLipSync(soundId, syncId, msPos, width, height); - return height; -} - } // End of namespace Scumm diff --git a/scumm/imuse_digi/dimuse.h b/scumm/imuse_digi/dimuse.h index 995ed5f85626..8320104c95fc 100644 --- a/scumm/imuse_digi/dimuse.h +++ b/scumm/imuse_digi/dimuse.h @@ -59,7 +59,7 @@ class IMuseDigital : public MusicEngine { int32 dataOffset; int curRegion; int curHookId; - int soundGroup; + int volGroupId; int iteration; int mod; int32 pullSize; @@ -93,7 +93,8 @@ class IMuseDigital : public MusicEngine { static void timer_handler(void *refConf); void callback(); void switchToNextRegion(int track); - void startSound(int soundId, const char *soundName, int soundType, int soundGroup, AudioStream *input, int hookId, int volume, int priority); + void allocSlot(int priority); + void startSound(int soundId, const char *soundName, int soundType, int volGroupId, AudioStream *input, int hookId, int volume, int priority); int32 getPosInMs(int soundId); void getLipSync(int soundId, int syncId, int32 msPos, int32 &width, int32 &height); @@ -118,24 +119,19 @@ class IMuseDigital : public MusicEngine { IMuseDigital(ScummEngine *scumm); virtual ~IMuseDigital(); - void startVoice(int soundId, AudioStream *input) - { debug(5, "startVoiceStream(%d)", soundId); startSound(soundId, NULL, 0, IMUSE_VOICE, input, 0, 127, 127); } - void startVoice(int soundId, const char *soundName) - { debug(5, "startVoiceBundle(%s)", soundName); startSound(soundId, soundName, IMUSE_BUNDLE, IMUSE_VOICE, NULL, 0, 127, 127); } - void startMusic(int soundId, int volume) - { debug(5, "startMusicResource(%d)", soundId); startSound(soundId, NULL, IMUSE_RESOURCE, IMUSE_MUSIC, NULL, 0, volume, 126); } - void startMusic(const char *soundName, int soundId, int hookId, int volume) - { debug(5, "startMusicBundle(%s)", soundName); startSound(soundId, soundName, IMUSE_BUNDLE, IMUSE_MUSIC, NULL, hookId, volume, 126); } - void startSfx(int soundId, int priority) - { debug(5, "startSfx(%d)", soundId); startSound(soundId, NULL, IMUSE_RESOURCE, IMUSE_SFX, NULL, 0, 127, priority); } + void startVoice(int soundId, AudioStream *input); + void startVoice(int soundId, const char *soundName); + void startMusic(int soundId, int volume); + void startMusic(const char *soundName, int soundId, int hookId, int volume); + void startSfx(int soundId, int priority); void startSound(int soundId) { error("MusicEngine::startSound() Should be never called"); } void resetState(); - void setGroupVoiceVolume(int volume); - void setGroupSfxVolume(int volume); - void setGroupMusicVolume(int volume); + void setGroupVoiceVolume(int volume) { _volVoice = volume; } + void setGroupSfxVolume(int volume) { _volSfx = volume; } + void setGroupMusicVolume(int volume) { _volMusic = volume; } int getGroupVoiceVolume() { return _volVoice; } int getGroupSfxVolume() { return _volSfx; } int getGroupMusicVolume() { return _volMusic; } @@ -144,7 +140,7 @@ class IMuseDigital : public MusicEngine { void setVolume(int soundId, int volume); void setPan(int soundId, int pan); void setFade(int soundId, int destVolume, int delay60HzTicks); - void selectGroupVolume(int soundId, int groupId); + void selectVolumeGroup(int soundId, int volGroupId); void setMasterVolume(int vol) {} void stopSound(int soundId); void stopAllSounds() { stopAllSounds(false); } diff --git a/scumm/imuse_digi/dimuse_music.cpp b/scumm/imuse_digi/dimuse_music.cpp index 11d23a174080..345a2746196b 100644 --- a/scumm/imuse_digi/dimuse_music.cpp +++ b/scumm/imuse_digi/dimuse_music.cpp @@ -30,6 +30,22 @@ namespace Scumm { #define COMI_STATE_OFFSET 3 #define COMI_SEQ_OFFSET (COMI_STATE_OFFSET + 94) +void IMuseDigital::refreshScripts() { + bool found = false; + { + Common::StackLock lock(_mutex, "IMuseDigital::refreshScripts()"); + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if ((_track[l].used) && (_track[l].volGroupId == IMUSE_VOLGRP_MUSIC) && (!_track[l].volFadeUsed)) { + found = true; + } + } + } + + if ((!found) && (_curMusicSeq != 0)) { + parseScriptCmds(0x1001, 0, 0, 0, 0, 0, 0, 0); + } +} + void IMuseDigital::setDigMusicState(int stateId) { int l, num = -1; diff --git a/scumm/imuse_digi/dimuse_script.cpp b/scumm/imuse_digi/dimuse_script.cpp new file mode 100644 index 000000000000..6d44b55796d2 --- /dev/null +++ b/scumm/imuse_digi/dimuse_script.cpp @@ -0,0 +1,363 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2004 The ScummVM project + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#include "stdafx.h" +#include "common/timer.h" + +#include "scumm/actor.h" +#include "scumm/scumm.h" +#include "scumm/sound.h" +#include "scumm/imuse_digi/dimuse.h" +#include "scumm/imuse_digi/dimuse_bndmgr.h" + +#include "sound/audiostream.h" +#include "sound/mixer.h" + +namespace Scumm { + +void IMuseDigital::parseScriptCmds(int a, int b, int c, int d, int e, int f, int g, int h) { + int cmd = a; + int soundId = b; + int sub_cmd = c; + + if (!cmd) + return; + + switch (cmd) { + case 10: // ImuseStopAllSounds + stopAllSounds(); + break; + case 12: // ImuseSetParam + switch (sub_cmd) { + case 0x400: // select group volume + selectVolumeGroup(soundId, d); + break; + case 0x500: // set priority + setPriority(soundId, d); + break; + case 0x600: // set volume + setVolume(soundId, d); + break; + case 0x700: // set pan + setPan(soundId, d); + break; + default: + warning("IMuseDigital::doCommand SetParam DEFAULT command %d", sub_cmd); + break; + } + break; + case 14: // ImuseFadeParam + switch (sub_cmd) { + case 0x600: // set volume fading + if ((d != 0) && (e == 0)) + setVolume(soundId, d); + else if ((d == 0) && (e == 0)) + stopSound(soundId); + else + setFade(soundId, d, e); + break; + default: + warning("IMuseDigital::doCommand FadeParam DEFAULT sub command %d", sub_cmd); + break; + } + break; + case 25: // ImuseStartStream + debug(5, "ImuseStartStream (%d, %d, %d)", soundId, c, d); + break; + case 26: // ImuseSwitchStream + debug(5, "ImuseSwitchStream (%d, %d, %d, %d, %d)", soundId, c, d, e, f); + break; + case 0x1000: // ImuseSetState + debug(5, "ImuseSetState (%d)", b); + if ((_vm->_gameId == GID_DIG) && (_vm->_features & GF_DEMO)) { + if (b == 1) { + fadeOutMusic(200); + startMusic(1, 127); + } else { + if (getSoundStatus(2) == 0) { + fadeOutMusic(200); + startMusic(2, 127); + } + } + } else if ((_vm->_gameId == GID_CMI) && (_vm->_features & GF_DEMO)) { + fadeOutMusic(120); + if (b == 2) { + startMusic("in1.imx", 1100, 0, 127); + } else if (b == 4) { + startMusic("in2.imx", 1120, 0, 127); + } else if (b == 8) { + startMusic("out1.imx", 1140, 0, 127); + } else if (b == 9) { + startMusic("out2.imx", 1150, 0, 127); + } else if (b == 16) { + startMusic("gun.imx", 1210, 0, 127); + } else { + warning("imuse digital: set state unknown for cmi demo: %d, room: %d", b, _vm->_currentRoom); + } + } else if (_vm->_gameId == GID_DIG) { + setDigMusicState(b); + } else if (_vm->_gameId == GID_CMI) { + setComiMusicState(b); + } else if (_vm->_gameId == GID_FT) { + setFtMusicState(b); + } + break; + case 0x1001: // ImuseSetSequence + debug(5, "ImuseSetSequence (%d)", b); + if (_vm->_gameId == GID_DIG) { + setDigMusicSequence(b); + } else if (_vm->_gameId == GID_CMI) { + setComiMusicSequence(b); + } else if (_vm->_gameId == GID_FT) { + setFtMusicSequence(b); + } + break; + case 0x1002: // ImuseSetCuePoint + debug(5, "ImuseSetCuePoint (%d)", b); + if (_vm->_gameId == GID_FT) { + setFtMusicCuePoint(b); + } + break; + case 0x1003: // ImuseSetAttribute + debug(5, "ImuseSetAttribute (%d, %d)", b, c); + assert((_vm->_gameId == GID_DIG) || (_vm->_gameId == GID_FT)); + if (_vm->_gameId == GID_DIG) { + _attributes[b] = c; + } + break; + case 0x2000: // ImuseSetGroupSfxVolume + debug(5, "ImuseSetGroupSFXVolume (%d)", b); +// setGroupSfxVolume(b); + break; + case 0x2001: // ImuseSetGroupVoiceVolume + debug(5, "ImuseSetGroupVoiceVolume (%d)", b); +// setGroupVoiceVolume(b); + break; + case 0x2002: // ImuseSetGroupMusicVolume + debug(5, "ImuseSetGroupMusicVolume (%d)", b); +// setGroupMusicVolume(b); + break; + default: + warning("IMuseDigital::doCommand DEFAULT command %d", cmd); + } +} + +void IMuseDigital::startVoice(int soundId, AudioStream *input) { + debug(5, "startVoiceStream(%d)", soundId); + startSound(soundId, NULL, 0, IMUSE_VOLGRP_VOICE, input, 0, 127, 127); +} + +void IMuseDigital::startVoice(int soundId, const char *soundName) { + debug(5, "startVoiceBundle(%s)", soundName); + startSound(soundId, soundName, IMUSE_BUNDLE, IMUSE_VOLGRP_VOICE, NULL, 0, 127, 127); +} + +void IMuseDigital::startMusic(int soundId, int volume) { + debug(5, "startMusicResource(%d)", soundId); + startSound(soundId, NULL, IMUSE_RESOURCE, IMUSE_VOLGRP_MUSIC, NULL, 0, volume, 126); +} + +void IMuseDigital::startMusic(const char *soundName, int soundId, int hookId, int volume) { + debug(5, "startMusicBundle(%s)", soundName); + startSound(soundId, soundName, IMUSE_BUNDLE, IMUSE_VOLGRP_MUSIC, NULL, hookId, volume, 126); +} + +void IMuseDigital::startSfx(int soundId, int priority) { + debug(5, "startSfx(%d)", soundId); + startSound(soundId, NULL, IMUSE_RESOURCE, IMUSE_VOLGRP_SFX, NULL, 0, 127, priority); +} + +void IMuseDigital::getLipSync(int soundId, int syncId, int32 msPos, int32 &width, int32 &height) { + int32 sync_size; + byte *sync_ptr; + + msPos /= 16; + if (msPos < 65536) { + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if ((_track[l].soundId == soundId) && _track[l].used) { + _sound->getSyncSizeAndPtrById(_track[l].soundHandle, syncId, sync_size, &sync_ptr); + if ((sync_size != 0) && (sync_ptr != NULL)) { + sync_size /= 4; + while (sync_size--) { + if (READ_BE_UINT16(sync_ptr) >= msPos) + break; + sync_ptr += 4; + } + if (sync_size < 0) + sync_ptr -= 4; + else + if (READ_BE_UINT16(sync_ptr) > msPos) + sync_ptr -= 4; + + width = sync_ptr[2]; + height = sync_ptr[3]; + return; + } + } + } + } +} + +int32 IMuseDigital::getPosInMs(int soundId) { + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if ((_track[l].soundId == soundId) && (_track[l].used)) { + int32 pos = (5 * (_track[l].dataOffset + _track[l].regionOffset)) / (_track[l].iteration / 200); + return pos; + } + } + + return 0; +} + +int IMuseDigital::getSoundStatus(int sound) const { + Common::StackLock lock(_mutex, "IMuseDigital::getSoundStatus()"); + debug(5, "IMuseDigital::getSoundStatus(%d)", sound); + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if ((_track[l].soundId == sound) && _track[l].used) { + return 1; + } + } + + return 0; +} + +void IMuseDigital::stopSound(int soundId) { + Common::StackLock lock(_mutex, "IMuseDigital::stopSound()"); + debug(5, "IMuseDigital::stopSound(%d)", soundId); + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if ((_track[l].soundId == soundId) && _track[l].used) { + if (_track[l].stream) { + _track[l].toBeRemoved = true; + } + else if (_track[l].stream2) + _vm->_mixer->stopHandle(_track[l].handle); + } + } +} + +int32 IMuseDigital::getCurMusicPosInMs() { + Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicPosInMs()"); + int soundId = -1; + + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if ((_track[l].used) && (_track[l].volGroupId == IMUSE_VOLGRP_MUSIC) && (!_track[l].volFadeUsed)) { + soundId = _track[l].soundId; + } + } + + int32 msPos = getPosInMs(soundId); + debug(5, "IMuseDigital::getCurMusicPosInMs(%d) = %d", soundId, msPos); + return msPos; +} + +int32 IMuseDigital::getCurVoiceLipSyncWidth() { + Common::StackLock lock(_mutex, "IMuseDigital::getCutVoiceLipSyncWidth()"); + int32 msPos = getPosInMs(kTalkSoundID) + 50; + int32 width = 0, height = 0; + + debug(5, "IMuseDigital::getCurVoiceLipSyncWidth(%d)", kTalkSoundID); + getLipSync(kTalkSoundID, 0, msPos, width, height); + return width; +} + +int32 IMuseDigital::getCurVoiceLipSyncHeight() { + Common::StackLock lock(_mutex, "IMuseDigital::getCurVoiceLipSyncHeight()"); + int32 msPos = getPosInMs(kTalkSoundID) + 50; + int32 width = 0, height = 0; + + debug(5, "IMuseDigital::getCurVoiceLipSyncHeight(%d)", kTalkSoundID); + getLipSync(kTalkSoundID, 0, msPos, width, height); + return height; +} + +int32 IMuseDigital::getCurMusicLipSyncWidth(int syncId) { + Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicLipSyncWidth()"); + int soundId = -1; + + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if ((_track[l].used) && (_track[l].volGroupId == IMUSE_VOLGRP_MUSIC) && (!_track[l].volFadeUsed)) { + soundId = _track[l].soundId; + } + } + + int32 msPos = getPosInMs(soundId) + 50; + int32 width = 0, height = 0; + + debug(5, "IMuseDigital::getCurVoiceLipSyncWidth(%d, %d)", soundId, msPos); + getLipSync(soundId, syncId, msPos, width, height); + return width; +} + +int32 IMuseDigital::getCurMusicLipSyncHeight(int syncId) { + Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicLipSyncHeight()"); + int soundId = -1; + + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if ((_track[l].used) && (_track[l].volGroupId == IMUSE_VOLGRP_MUSIC) && (!_track[l].volFadeUsed)) { + soundId = _track[l].soundId; + } + } + + int32 msPos = getPosInMs(soundId) + 50; + int32 width = 0, height = 0; + + debug(5, "IMuseDigital::getCurVoiceLipSyncHeight(%d, %d)", soundId, msPos); + getLipSync(soundId, syncId, msPos, width, height); + return height; +} + +void IMuseDigital::stopAllSounds(bool waitForStop) { + debug(5, "IMuseDigital::stopAllSounds"); + { + Common::StackLock lock(_mutex, "IMuseDigital::stopAllSounds()"); + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if (_track[l].used) { + if (_track[l].stream) { + _track[l].toBeRemoved = true; + } else if (_track[l].stream2) + _vm->_mixer->stopHandle(_track[l].handle); + } + } + } + + if (waitForStop) { + bool used; + do { + used = false; + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if (_track[l].used) + used = true; + } + g_system->delay_msecs(10); + } while (used); + } +} + +void IMuseDigital::pause(bool p) { + Common::StackLock lock(_mutex, "IMuseDigital::pause()"); + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if (_track[l].used) { + _vm->_mixer->pauseHandle(_track[l].handle, p); + } + } + _pause = p; +} + +} // End of namespace Scumm diff --git a/scumm/imuse_digi/dimuse_sndmgr.cpp b/scumm/imuse_digi/dimuse_sndmgr.cpp index 9d6201a75518..afdc36d3f01b 100644 --- a/scumm/imuse_digi/dimuse_sndmgr.cpp +++ b/scumm/imuse_digi/dimuse_sndmgr.cpp @@ -47,38 +47,111 @@ ImuseDigiSndMgr::~ImuseDigiSndMgr() { #endif } +void ImuseDigiSndMgr::countElements(byte *ptr, int &numRegions, int &numJumps, int &numSyncs) { + uint32 tag; + int32 size = 0; + + do { + tag = READ_BE_UINT32(ptr); ptr += 4; + switch(tag) { + case MKID_BE('TEXT'): + case MKID_BE('STOP'): + case MKID_BE('FRMT'): + case MKID_BE('DATA'): + size = READ_BE_UINT32(ptr); ptr += size + 4; + break; + case MKID_BE('REGN'): + numRegions++; + size = READ_BE_UINT32(ptr); ptr += size + 4; + break; + case MKID_BE('JUMP'): + numJumps++; + size = READ_BE_UINT32(ptr); ptr += size + 4; + break; + case MKID_BE('SYNC'): + numSyncs++; + size = READ_BE_UINT32(ptr); ptr += size + 4; + break; + default: + error("ImuseDigiSndMgr::countElements() Unknown sfx header '%s'", tag2str(tag)); + } + } while (tag != MKID_BE('DATA')); +} + void ImuseDigiSndMgr::prepareSound(byte *ptr, int slot) { if (READ_UINT32(ptr) == MKID('Crea')) { - int size = 0, rate = 0, loops = 0, begin_loop = 0, end_loop = 0; - _sounds[slot].resPtr = readVOCFromMemory(ptr, size, rate, loops, begin_loop, end_loop); - _sounds[slot].freeResPtr = true; + bool quit = false; + + int32 offset = READ_LE_UINT16(ptr + 20); + int16 version = READ_LE_UINT16(ptr + 22); + int16 code = READ_LE_UINT16(ptr + 24); + assert(version == 0x010A || version == 0x0114); + assert(code == ~version + 0x1234); + + _sounds[slot].region = (_region *)malloc(sizeof(_region) * 4); + _sounds[slot].jump = (_jump *)malloc(sizeof(_jump)); + _sounds[slot].resPtr = ptr; _sounds[slot].bits = 8; - _sounds[slot].freq = rate; _sounds[slot].channels = 1; - _sounds[slot].region[0].offset = 0; - _sounds[slot].region[0].length = size; - _sounds[slot].numRegions++; - if (loops != 0) { - if (begin_loop == 0) { - _sounds[slot].region[1].offset = end_loop; + + while (!quit) { + int len = READ_LE_UINT32(ptr + offset); + offset += 4; + code = len & 0xFF; + len >>= 8; + switch(code) { + case 0: + quit = true; + break; + case 1: + { + int time_constant = ptr[offset]; + offset += 2; + len -= 2; + _sounds[slot].freq = getSampleRateFromVOCRate(time_constant); + _sounds[slot].region[_sounds[slot].numRegions].offset = offset; + _sounds[slot].region[_sounds[slot].numRegions].length = len; + _sounds[slot].numRegions++; + } + break; + case 6: // begin of loop + _sounds[slot].jump[0].dest = offset + 8; + _sounds[slot].jump[0].hookId = 0; + _sounds[slot].jump[0].fadeDelay = 0; + break; + case 7: // end of loop + _sounds[slot].jump[0].offset = offset - 4; + _sounds[slot].numJumps++; + _sounds[slot].region[_sounds[slot].numRegions].offset = offset - 4; + _sounds[slot].region[_sounds[slot].numRegions].length = 0; _sounds[slot].numRegions++; - } else { - _sounds[slot].region[0].length = begin_loop; - _sounds[slot].region[1].offset = begin_loop; - _sounds[slot].region[1].length = end_loop - begin_loop; - _sounds[slot].region[2].offset = end_loop; - _sounds[slot].numRegions += 2; + break; + default: + error("Invalid code in VOC file : %d", code); + quit = true; + break; } - _sounds[slot].jump[0].dest = begin_loop; - _sounds[slot].jump[0].offset = end_loop; - _sounds[slot].numJumps++; + // FIXME some FT samples (ex. 362) has bad length, 2 bytes too short + offset += len; } } else if (READ_UINT32(ptr) == MKID('iMUS')) { uint32 tag; int32 size = 0; byte *s_ptr = ptr; - ptr += 16; + + int curIndexRegion = 0; + int curIndexJump = 0; + int curIndexSync = 0; + + _sounds[slot].numRegions = 0; + _sounds[slot].numJumps = 0; + _sounds[slot].numSyncs = 0; + countElements(ptr, _sounds[slot].numRegions, _sounds[slot].numJumps, _sounds[slot].numSyncs); + _sounds[slot].region = (_region *)malloc(sizeof(_region) * _sounds[slot].numRegions); + _sounds[slot].jump = (_jump *)malloc(sizeof(_jump) * _sounds[slot].numJumps); + _sounds[slot].sync = (_sync *)malloc(sizeof(_sync) * _sounds[slot].numSyncs); + do { tag = READ_BE_UINT32(ptr); ptr += 4; switch(tag) { @@ -87,57 +160,35 @@ void ImuseDigiSndMgr::prepareSound(byte *ptr, int slot) { _sounds[slot].bits = READ_BE_UINT32(ptr); ptr += 4; _sounds[slot].freq = READ_BE_UINT32(ptr); ptr += 4; _sounds[slot].channels = READ_BE_UINT32(ptr); ptr += 4; - break; + break; case MKID_BE('TEXT'): + case MKID_BE('STOP'): size = READ_BE_UINT32(ptr); ptr += size + 4; break; case MKID_BE('REGN'): - size = READ_BE_UINT32(ptr); ptr += 4; - if (_sounds[slot].numRegions >= MAX_IMUSE_REGIONS) { - warning("ImuseDigiSndMgr::prepareSound(%d/%s) Not enough space for Region", _sounds[slot].soundId, _sounds[slot].name); - ptr += 8; - break; - } - _sounds[slot].region[_sounds[slot].numRegions].offset = READ_BE_UINT32(ptr); ptr += 4; - _sounds[slot].region[_sounds[slot].numRegions].length = READ_BE_UINT32(ptr); ptr += 4; -// if (_sounds[slot].soundId == 2311) -// printf("REGN: offset: %d, length: %d\n", _sounds[slot].region[_sounds[slot].numRegions].offset, _sounds[slot].region[_sounds[slot].numRegions].length); - _sounds[slot].numRegions++; - break; - case MKID_BE('STOP'): ptr += 4; - _sounds[slot].offsetStop = READ_BE_UINT32(ptr); ptr += 4; + _sounds[slot].region[curIndexRegion].offset = READ_BE_UINT32(ptr); ptr += 4; + _sounds[slot].region[curIndexRegion].length = READ_BE_UINT32(ptr); ptr += 4; + curIndexRegion++; break; case MKID_BE('JUMP'): - size = READ_BE_UINT32(ptr); ptr += 4; - if (_sounds[slot].numJumps >= MAX_IMUSE_JUMPS) { - warning("ImuseDigiSndMgr::prepareSound(%d/%s) Not enough space for Jump", _sounds[slot].soundId, _sounds[slot].name); - ptr += size; - break; - } - _sounds[slot].jump[_sounds[slot].numJumps].offset = READ_BE_UINT32(ptr); ptr += 4; - _sounds[slot].jump[_sounds[slot].numJumps].dest = READ_BE_UINT32(ptr); ptr += 4; - _sounds[slot].jump[_sounds[slot].numJumps].hookId = READ_BE_UINT32(ptr); ptr += 4; - _sounds[slot].jump[_sounds[slot].numJumps].fadeDelay = READ_BE_UINT32(ptr); ptr += 4; -// if (_sounds[slot].soundId == 2311) -// printf("JUMP: offset: %d, dest: %d, hookId: %d\n", _sounds[slot].jump[_sounds[slot].numJumps].offset, _sounds[slot].jump[_sounds[slot].numJumps].dest, _sounds[slot].jump[_sounds[slot].numJumps].hookId); - _sounds[slot].numJumps++; + ptr += 4; + _sounds[slot].jump[curIndexJump].offset = READ_BE_UINT32(ptr); ptr += 4; + _sounds[slot].jump[curIndexJump].dest = READ_BE_UINT32(ptr); ptr += 4; + _sounds[slot].jump[curIndexJump].hookId = READ_BE_UINT32(ptr); ptr += 4; + _sounds[slot].jump[curIndexJump].fadeDelay = READ_BE_UINT32(ptr); ptr += 4; + curIndexJump++; break; case MKID_BE('SYNC'): size = READ_BE_UINT32(ptr); ptr += 4; - if (_sounds[slot].numSyncs >= MAX_IMUSE_SYNCS) { - warning("ImuseDigiSndMgr::prepareSound(%d/%s) Not enough space for Sync", _sounds[slot].soundId, _sounds[slot].name); - ptr += size; - break; - } - _sounds[slot].sync[_sounds[slot].numSyncs].size = size; - _sounds[slot].sync[_sounds[slot].numSyncs].ptr = (byte *)malloc(size); - memcpy(_sounds[slot].sync[_sounds[slot].numSyncs].ptr, ptr, size); - _sounds[slot].numSyncs++; + _sounds[slot].sync[curIndexSync].size = size; + _sounds[slot].sync[curIndexSync].ptr = (byte *)malloc(size); + memcpy(_sounds[slot].sync[curIndexSync].ptr, ptr, size); + curIndexSync++; ptr += size; break; case MKID_BE('DATA'): - size = READ_BE_UINT32(ptr); ptr += 4; + ptr += 4; break; default: error("ImuseDigiSndMgr::prepareSound(%d/%s) Unknown sfx header '%s'", _sounds[slot].soundId, _sounds[slot].name, tag2str(tag)); @@ -163,24 +214,24 @@ int ImuseDigiSndMgr::allocSlot() { bool ImuseDigiSndMgr::openMusicBundle(int slot) { bool result = false; - _sounds[slot]._bundle = new BundleMgr(_cacheBundleDir); + _sounds[slot].bundle = new BundleMgr(_cacheBundleDir); if (_vm->_gameId == GID_CMI) { if (_vm->_features & GF_DEMO) { - result = _sounds[slot]._bundle->openFile("music.bun", _vm->getGameDataPath()); + result = _sounds[slot].bundle->openFile("music.bun", _vm->getGameDataPath()); } else { char musicfile[20]; sprintf(musicfile, "musdisk%d.bun", _vm->VAR(_vm->VAR_CURRENTDISK)); if (_disk != _vm->VAR(_vm->VAR_CURRENTDISK)) - _sounds[slot]._bundle->closeFile(); + _sounds[slot].bundle->closeFile(); - result = _sounds[slot]._bundle->openFile(musicfile, _vm->getGameDataPath()); + result = _sounds[slot].bundle->openFile(musicfile, _vm->getGameDataPath()); if (result == false) - result = _sounds[slot]._bundle->openFile("music.bun", _vm->getGameDataPath()); + result = _sounds[slot].bundle->openFile("music.bun", _vm->getGameDataPath()); _disk = (byte)_vm->VAR(_vm->VAR_CURRENTDISK); } } else if (_vm->_gameId == GID_DIG) - result = _sounds[slot]._bundle->openFile("digmusic.bun", _vm->getGameDataPath()); + result = _sounds[slot].bundle->openFile("digmusic.bun", _vm->getGameDataPath()); else error("ImuseDigiSndMgr::openMusicBundle() Don't know which bundle file to load"); @@ -190,31 +241,31 @@ bool ImuseDigiSndMgr::openMusicBundle(int slot) { bool ImuseDigiSndMgr::openVoiceBundle(int slot) { bool result = false; - _sounds[slot]._bundle = new BundleMgr(_cacheBundleDir); + _sounds[slot].bundle = new BundleMgr(_cacheBundleDir); if (_vm->_gameId == GID_CMI) { if (_vm->_features & GF_DEMO) { - result = _sounds[slot]._bundle->openFile("voice.bun", _vm->getGameDataPath()); + result = _sounds[slot].bundle->openFile("voice.bun", _vm->getGameDataPath()); } else { char voxfile[20]; sprintf(voxfile, "voxdisk%d.bun", _vm->VAR(_vm->VAR_CURRENTDISK)); if (_disk != _vm->VAR(_vm->VAR_CURRENTDISK)) - _sounds[slot]._bundle->closeFile(); + _sounds[slot].bundle->closeFile(); - result = _sounds[slot]._bundle->openFile(voxfile, _vm->getGameDataPath()); + result = _sounds[slot].bundle->openFile(voxfile, _vm->getGameDataPath()); if (result == false) - result = _sounds[slot]._bundle->openFile("voice.bun", _vm->getGameDataPath()); + result = _sounds[slot].bundle->openFile("voice.bun", _vm->getGameDataPath()); _disk = (byte)_vm->VAR(_vm->VAR_CURRENTDISK); } } else if (_vm->_gameId == GID_DIG) - result = _sounds[slot]._bundle->openFile("digvoice.bun", _vm->getGameDataPath()); + result = _sounds[slot].bundle->openFile("digvoice.bun", _vm->getGameDataPath()); else error("ImuseDigiSndMgr::openVoiceBundle() Don't know which bundle file to load"); return result; } -ImuseDigiSndMgr::soundStruct *ImuseDigiSndMgr::openSound(int32 soundId, const char *soundName, int soundType, int soundGroup) { +ImuseDigiSndMgr::soundStruct *ImuseDigiSndMgr::openSound(int32 soundId, const char *soundName, int soundType, int volGroupId) { assert(soundId >= 0); assert(soundType); @@ -237,13 +288,13 @@ ImuseDigiSndMgr::soundStruct *ImuseDigiSndMgr::openSound(int32 soundId, const ch result = true; } else if (soundType == IMUSE_BUNDLE) { bool header_outside = ((_vm->_gameId == GID_CMI) && !(_vm->_features & GF_DEMO)); - if (soundGroup == IMUSE_VOICE) + if (volGroupId == IMUSE_VOLGRP_VOICE) result = openVoiceBundle(slot); - else if (soundGroup == IMUSE_MUSIC) + else if (volGroupId == IMUSE_VOLGRP_MUSIC) result = openMusicBundle(slot); - else + else error("ImuseDigiSndMgr::openSound() Don't know how load sound: %d", soundId); - _sounds[slot]._bundle->decompressSampleByIndex(soundId, 0, 0x2000, &ptr, 0, header_outside); + _sounds[slot].bundle->decompressSampleByIndex(soundId, 0, 0x2000, &ptr, 0, header_outside); _sounds[slot].name[0] = 0; _sounds[slot].soundId = soundId; } else { @@ -252,13 +303,13 @@ ImuseDigiSndMgr::soundStruct *ImuseDigiSndMgr::openSound(int32 soundId, const ch } else { if (soundType == IMUSE_BUNDLE) { bool header_outside = ((_vm->_gameId == GID_CMI) && !(_vm->_features & GF_DEMO)); - if (soundGroup == IMUSE_VOICE) + if (volGroupId == IMUSE_VOLGRP_VOICE) result = openVoiceBundle(slot); - else if (soundGroup == IMUSE_MUSIC) + else if (volGroupId == IMUSE_VOLGRP_MUSIC) result = openMusicBundle(slot); else error("ImuseDigiSndMgr::openSound() Don't know how load sound: %d", soundId); - _sounds[slot]._bundle->decompressSampleByName(soundName, 0, 0x2000, &ptr, header_outside); + _sounds[slot].bundle->decompressSampleByName(soundName, 0, 0x2000, &ptr, header_outside); strcpy(_sounds[slot].name, soundName); _sounds[slot].soundId = soundId; } else { @@ -283,13 +334,17 @@ void ImuseDigiSndMgr::closeSound(soundStruct *soundHandle) { for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) { if (&_sounds[l] == soundHandle) { - if (_sounds[l].freeResPtr) - free(_sounds[l].resPtr); - if (_sounds[l]._bundle) - delete _sounds[l]._bundle; + if (_sounds[l].bundle) + delete _sounds[l].bundle; for (int r = 0; r < _sounds[l].numSyncs; r++) if (_sounds[l].sync[r].ptr) free(_sounds[l].sync[r].ptr); + if (_sounds[l].region) + free(_sounds[l].region); + if (_sounds[l].jump) + free(_sounds[l].jump); + if (_sounds[l].sync) + free(_sounds[l].sync); memset(&_sounds[l], 0, sizeof(soundStruct)); } } @@ -408,8 +463,8 @@ int32 ImuseDigiSndMgr::getDataFromRegion(soundStruct *soundHandle, int region, b int header_size = soundHandle->offsetData; bool header_outside = ((_vm->_gameId == GID_CMI) && !(_vm->_features & GF_DEMO)); - if (soundHandle->_bundle) { - size = soundHandle->_bundle->decompressSampleByCurIndex(start + offset, size, buf, header_size, header_outside); + if (soundHandle->bundle) { + size = soundHandle->bundle->decompressSampleByCurIndex(start + offset, size, buf, header_size, header_outside); } else if (soundHandle->resPtr) { *buf = (byte *)malloc(size); memcpy(*buf, soundHandle->resPtr + start + offset + header_size, size); diff --git a/scumm/imuse_digi/dimuse_sndmgr.h b/scumm/imuse_digi/dimuse_sndmgr.h index c0d1b2878347..c6055e5e0a41 100644 --- a/scumm/imuse_digi/dimuse_sndmgr.h +++ b/scumm/imuse_digi/dimuse_sndmgr.h @@ -34,17 +34,14 @@ class BundleMgr; class ImuseDigiSndMgr { public: -#define MAX_IMUSE_SOUNDS 16 -#define MAX_IMUSE_JUMPS 80 -#define MAX_IMUSE_REGIONS 85 -#define MAX_IMUSE_SYNCS 4 +#define MAX_IMUSE_SOUNDS 10 #define IMUSE_RESOURCE 1 #define IMUSE_BUNDLE 2 -#define IMUSE_VOICE 1 -#define IMUSE_SFX 2 -#define IMUSE_MUSIC 3 +#define IMUSE_VOLGRP_VOICE 1 +#define IMUSE_VOLGRP_SFX 2 +#define IMUSE_VOLGRP_MUSIC 3 private: struct _region { @@ -55,25 +52,27 @@ class ImuseDigiSndMgr { struct _jump { int32 offset; // jump offset position int32 dest; // jump to dest position - byte hookId; // id of hook - int16 fadeDelay; // fade delay in ms + byte hookId; // id of hook + int16 fadeDelay; // fade delay in ms }; struct _sync { - int32 size; // size of sync - byte *ptr; // pointer to sync + int32 size; // size of sync + byte *ptr; // pointer to sync }; public: struct soundStruct { - uint16 freq; // frequency + uint16 freq; // frequency byte channels; // stereo or mono byte bits; // 8, 12, 16 - int8 numJumps; // number of Jumps - int8 numRegions; // number of Regions - int8 numSyncs; // number of Syncs - int32 offsetStop; // end offset in source data + int numJumps; // number of Jumps + int numRegions; // number of Regions + int numSyncs; // number of Syncs + _region *region; + _jump *jump; + _sync *sync; bool endFlag; bool inUse; byte *allData; @@ -81,11 +80,7 @@ class ImuseDigiSndMgr { byte *resPtr; char name[15]; int16 soundId; - bool freeResPtr; - BundleMgr *_bundle; - _region region[MAX_IMUSE_REGIONS]; - _jump jump[MAX_IMUSE_JUMPS]; - _sync sync[MAX_IMUSE_SYNCS]; + BundleMgr *bundle; }; private: @@ -103,12 +98,14 @@ class ImuseDigiSndMgr { bool openMusicBundle(int slot); bool openVoiceBundle(int slot); + void countElements(byte *ptr, int &numRegions, int &numJumps, int &numSyncs); + public: ImuseDigiSndMgr(ScummEngine *scumm); ~ImuseDigiSndMgr(); - - soundStruct * openSound(int32 soundId, const char *soundName, int soundType, int soundGroup); + + soundStruct * openSound(int32 soundId, const char *soundName, int soundType, int volGroupId); void closeSound(soundStruct *soundHandle); int getFreq(soundStruct *soundHandle); diff --git a/scumm/imuse_digi/dimuse_track.cpp b/scumm/imuse_digi/dimuse_track.cpp new file mode 100644 index 000000000000..36d3618af1ff --- /dev/null +++ b/scumm/imuse_digi/dimuse_track.cpp @@ -0,0 +1,242 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2004 The ScummVM project + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#include "stdafx.h" +#include "common/timer.h" + +#include "scumm/actor.h" +#include "scumm/scumm.h" +#include "scumm/sound.h" +#include "scumm/imuse_digi/dimuse.h" +#include "scumm/imuse_digi/dimuse_bndmgr.h" + +#include "sound/audiostream.h" +#include "sound/mixer.h" + +namespace Scumm { + +void IMuseDigital::allocSlot(int priority) { + int l; + int lower_priority = 127; + bool found_free = false; + + for (l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if (!_track[l].used && !_track[l].handle.isActive()) + found_free = true; + } + + if (!found_free) { + warning("IMuseDigital::startSound(): All slots are full"); + for (l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if (_track[l].used && _track[l].handle.isActive() && + (lower_priority > _track[l].priority) && (!_track[l].stream2)) + lower_priority = _track[l].priority; + } + if (lower_priority <= priority) { + int track_id = -1; + for (l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if (_track[l].used && _track[l].handle.isActive() && + (lower_priority == _track[l].priority) && (!_track[l].stream2)) { + track_id = l; + } + } + assert(track_id != -1); + _track[track_id].stream->finish(); + _track[track_id].stream = NULL; + _vm->_mixer->stopHandle(_track[track_id].handle); + _sound->closeSound(_track[track_id].soundHandle); + _track[track_id].used = false; + assert(!_track[track_id].handle.isActive()); + warning("IMuseDigital::startSound(): Removed sound %d from track %d", _track[track_id].soundId, track_id); + } else { + warning("IMuseDigital::startSound(): Priority sound too low"); + return; + } + } +} + +void IMuseDigital::startSound(int soundId, const char *soundName, int soundType, int volGroupId, AudioStream *input, int hookId, int volume, int priority) { + Common::StackLock lock(_mutex, "IMuseDigital::startSound()"); + debug(5, "IMuseDigital::startSound(%d)", soundId); + int l; + + allocSlot(priority); + + for (l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if (!_track[l].used && !_track[l].handle.isActive()) { + _track[l].pan = 64; + _track[l].vol = volume * 1000; + _track[l].volFadeDest = 0; + _track[l].volFadeStep = 0; + _track[l].volFadeDelay = 0; + _track[l].volFadeUsed = false; + _track[l].soundId = soundId; + _track[l].started = false; + _track[l].volGroupId = volGroupId; + _track[l].curHookId = hookId; + _track[l].priority = priority; + _track[l].curRegion = -1; + _track[l].dataOffset = 0; + _track[l].regionOffset = 0; + _track[l].trackOffset = 0; + _track[l].mod = 0; + _track[l].toBeRemoved = false; + + int bits = 0, freq = 0, channels = 0, mixerFlags = 0; + + if (input) { + _track[l].iteration = 1; // ? + // Do nothing here, we already have an audio stream + } else { + _track[l].soundHandle = _sound->openSound(soundId, soundName, soundType, volGroupId); + + if (_track[l].soundHandle == NULL) + return; + + bits = _sound->getBits(_track[l].soundHandle); + channels = _sound->getChannels(_track[l].soundHandle); + freq = _sound->getFreq(_track[l].soundHandle); + + if ((soundId == kTalkSoundID) && (soundType == IMUSE_BUNDLE)) { + if (_vm->_actorToPrintStrFor != 0xFF && _vm->_actorToPrintStrFor != 0) { + Actor *a = _vm->derefActor(_vm->_actorToPrintStrFor, "IMuseDigital::startSound"); + freq = (freq * a->talkFrequency) / 256; + _track[l].pan = a->talkPan; + _track[l].vol = a->talkVolume * 1000; + } + } + + assert(bits == 8 || bits == 12 || bits == 16); + assert(channels == 1 || channels == 2); + assert(0 < freq && freq <= 65535); + + // Round the frequency to a multiple of 25. This is done to + // ensure we don't run into data under-/overflows (this is a + // design limitation of the current IMuseDigital code, which + // pushes data 'blindly' into the mixer, instead of providing + // a pull based interface, i.e. a custom AudioInputStream + // subclass). + freq -= (freq % 25); + + _track[l].iteration = _track[l].pullSize = freq * channels; + + if (channels == 2) + mixerFlags = SoundMixer::FLAG_STEREO | SoundMixer::FLAG_REVERSE_STEREO; + + if ((bits == 12) || (bits == 16)) { + mixerFlags |= SoundMixer::FLAG_16BITS; + _track[l].iteration = _track[l].pullSize *= 2; + } else if (bits == 8) { + mixerFlags |= SoundMixer::FLAG_UNSIGNED; + } else + error("IMuseDigital::startSound(): Can't handle %d bit samples", bits); + + _track[l].pullSize /= 25; // We want a "frame rate" of 25 audio blocks per second + } + + if (input) { + _track[l].stream2 = input; + _track[l].stream = NULL; + } else { + _track[l].stream2 = NULL; + _track[l].stream = makeAppendableAudioStream(freq, mixerFlags, 100000); + _vm->_mixer->playInputStream(&_track[l].handle, _track[l].stream, false, _track[l].vol / 1000, _track[l].pan, -1); + } + + _track[l].used = true; + return; + } + } + + warning("it should not happen"); + assert(0); +} + +void IMuseDigital::setPriority(int soundId, int priority) { + Common::StackLock lock(_mutex, "IMuseDigital::setPriority()"); + debug(5, "IMuseDigital::setPriority(%d, %d)", soundId, priority); + + assert ((priority >= 0) && (priority <= 127)); + + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if ((_track[l].soundId == soundId) && _track[l].used) { + _track[l].priority = priority; + } + } +} + +void IMuseDigital::setVolume(int soundId, int volume) { + Common::StackLock lock(_mutex, "IMuseDigital::setVolume()"); + debug(5, "IMuseDigital::setVolume(%d, %d)", soundId, volume); + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if ((_track[l].soundId == soundId) && _track[l].used) { + _track[l].vol = volume * 1000; + } + } +} + +void IMuseDigital::setPan(int soundId, int pan) { + Common::StackLock lock(_mutex, "IMuseDigital::setPan()"); + debug(5, "IMuseDigital::setPan(%d, %d)", soundId, pan); + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if ((_track[l].soundId == soundId) && _track[l].used) { + _track[l].pan = pan; + } + } +} + +void IMuseDigital::selectVolumeGroup(int soundId, int volGroupId) { + Common::StackLock lock(_mutex, "IMuseDigital::setGroupVolume()"); + debug(5, "IMuseDigital::setGroupVolume(%d, %d)", soundId, volGroupId); + assert((volGroupId >= 1) && (volGroupId <= 3)); + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if ((_track[l].soundId == soundId) && _track[l].used) { + _track[l].volGroupId = volGroupId; + } + } +} + +void IMuseDigital::setFade(int soundId, int destVolume, int delay60HzTicks) { + Common::StackLock lock(_mutex, "IMuseDigital::setFade()"); + debug(5, "IMuseDigital::setFade(%d, %d, %d)", soundId, destVolume, delay60HzTicks); + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if ((_track[l].soundId == soundId) && _track[l].used) { + _track[l].volFadeDelay = delay60HzTicks; + _track[l].volFadeDest = destVolume * 1000; + _track[l].volFadeStep = (_track[l].volFadeDest - _track[l].vol) * 60 * 40 / (1000 * delay60HzTicks); + _track[l].volFadeUsed = true; + } + } +} + +void IMuseDigital::fadeOutMusic(int fadeDelay) { + Common::StackLock lock(_mutex, "IMuseDigital::fadeOutMusic()"); + debug(5, "IMuseDigital::fadeOutMusic"); + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if ((_track[l].used) && (_track[l].volGroupId == IMUSE_VOLGRP_MUSIC) && (!_track[l].volFadeUsed)) { + _track[l].volFadeDelay = fadeDelay; + _track[l].volFadeDest = 0; + _track[l].volFadeStep = (_track[l].volFadeDest - _track[l].vol) * 60 * 40 / (1000 * fadeDelay); + _track[l].volFadeUsed = true; + } + } +} + +} // End of namespace Scumm diff --git a/scumm/module.mk b/scumm/module.mk index 220adc6327c7..01a6de5db854 100644 --- a/scumm/module.mk +++ b/scumm/module.mk @@ -50,6 +50,8 @@ MODULE_OBJS := \ scumm/imuse_digi/dimuse_codecs.o \ scumm/imuse_digi/dimuse_music.o \ scumm/imuse_digi/dimuse_sndmgr.o \ + scumm/imuse_digi/dimuse_script.o \ + scumm/imuse_digi/dimuse_track.o \ scumm/imuse_digi/dimuse_tables.o \ scumm/insane/insane.o \ scumm/insane/insane_ben.o \