diff options
author | Max Horn | 2006-02-11 22:45:04 +0000 |
---|---|---|
committer | Max Horn | 2006-02-11 22:45:04 +0000 |
commit | 26ee630756ebdd7c96bccede0881a8c8b98e8f2b (patch) | |
tree | 26e378d5cf990a2b81c2c96e9e683a7f333b62e8 /engines/scumm/imuse_digi | |
parent | 2a9a0d4211b1ea5723f1409d91cb95de8984429e (diff) | |
download | scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.tar.gz scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.tar.bz2 scummvm-rg350-26ee630756ebdd7c96bccede0881a8c8b98e8f2b.zip |
Moved engines to the new engines/ directory
svn-id: r20582
Diffstat (limited to 'engines/scumm/imuse_digi')
-rw-r--r-- | engines/scumm/imuse_digi/dimuse.cpp | 412 | ||||
-rw-r--r-- | engines/scumm/imuse_digi/dimuse.h | 239 | ||||
-rw-r--r-- | engines/scumm/imuse_digi/dimuse_bndmgr.cpp | 344 | ||||
-rw-r--r-- | engines/scumm/imuse_digi/dimuse_bndmgr.h | 115 | ||||
-rw-r--r-- | engines/scumm/imuse_digi/dimuse_codecs.cpp | 655 | ||||
-rw-r--r-- | engines/scumm/imuse_digi/dimuse_music.cpp | 444 | ||||
-rw-r--r-- | engines/scumm/imuse_digi/dimuse_script.cpp | 409 | ||||
-rw-r--r-- | engines/scumm/imuse_digi/dimuse_sndmgr.cpp | 625 | ||||
-rw-r--r-- | engines/scumm/imuse_digi/dimuse_sndmgr.h | 139 | ||||
-rw-r--r-- | engines/scumm/imuse_digi/dimuse_tables.cpp | 881 | ||||
-rw-r--r-- | engines/scumm/imuse_digi/dimuse_track.cpp | 368 |
11 files changed, 4631 insertions, 0 deletions
diff --git a/engines/scumm/imuse_digi/dimuse.cpp b/engines/scumm/imuse_digi/dimuse.cpp new file mode 100644 index 0000000000..c057cc8d85 --- /dev/null +++ b/engines/scumm/imuse_digi/dimuse.cpp @@ -0,0 +1,412 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/stdafx.h" +#include "common/system.h" +#include "common/timer.h" + +#include "scumm/actor.h" +#include "scumm/saveload.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 { + +IMuseDigital::Track::Track() + : soundId(-1), used(false), stream(NULL), stream2(NULL) { +} + +void IMuseDigital::timer_handler(void *refCon) { + IMuseDigital *imuseDigital = (IMuseDigital *)refCon; + imuseDigital->callback(); +} + +IMuseDigital::IMuseDigital(ScummEngine *scumm, int fps) + : _vm(scumm) { + _pause = false; + _sound = new ImuseDigiSndMgr(_vm); + _callbackFps = fps; + resetState(); + for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) { + _track[l] = new Track; + _track[l]->trackId = l; + _track[l]->used = false; + } + _vm->_timer->installTimerProc(timer_handler, 1000000 / _callbackFps, this); + + _audioNames = NULL; + _numAudioNames = 0; +} + +IMuseDigital::~IMuseDigital() { + stopAllSounds(); + _vm->_timer->removeTimerProc(timer_handler); + for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) { + delete _track[l]; + } + delete _sound; + free(_audioNames); +} + +void IMuseDigital::resetState() { + _curMusicState = 0; + _curMusicSeq = 0; + _curMusicCue = 0; + memset(_attributes, 0, sizeof(_attributes)); + _nextSeqToPlay = 0; +} + +void IMuseDigital::saveOrLoad(Serializer *ser) { + Common::StackLock lock(_mutex, "IMuseDigital::saveOrLoad()"); + + const SaveLoadEntry mainEntries[] = { + MK_OBSOLETE(IMuseDigital, _volVoice, sleInt32, VER(31), VER(42)), + MK_OBSOLETE(IMuseDigital, _volSfx, sleInt32, VER(31), VER(42)), + MK_OBSOLETE(IMuseDigital, _volMusic, sleInt32, VER(31), VER(42)), + MKLINE(IMuseDigital, _curMusicState, sleInt32, VER(31)), + MKLINE(IMuseDigital, _curMusicSeq, sleInt32, VER(31)), + MKLINE(IMuseDigital, _curMusicCue, sleInt32, VER(31)), + MKLINE(IMuseDigital, _nextSeqToPlay, sleInt32, VER(31)), + MKARRAY(IMuseDigital, _attributes[0], sleInt32, 188, VER(31)), + MKEND() + }; + + const SaveLoadEntry trackEntries[] = { + MKLINE(Track, pan, sleInt8, VER(31)), + MKLINE(Track, vol, sleInt32, VER(31)), + MKLINE(Track, volFadeDest, sleInt32, VER(31)), + MKLINE(Track, volFadeStep, sleInt32, VER(31)), + MKLINE(Track, volFadeDelay, sleInt32, VER(31)), + MKLINE(Track, volFadeUsed, sleByte, VER(31)), + MKLINE(Track, soundId, sleInt32, VER(31)), + MKARRAY(Track, soundName[0], sleByte, 15, VER(31)), + MKLINE(Track, used, sleByte, VER(31)), + MKLINE(Track, toBeRemoved, sleByte, VER(31)), + MKLINE(Track, souStream, sleByte, VER(31)), + MKLINE(Track, started, sleByte, VER(31)), + MKLINE(Track, priority, sleInt32, VER(31)), + MKLINE(Track, regionOffset, sleInt32, VER(31)), + MK_OBSOLETE(Track, trackOffset, sleInt32, VER(31), VER(31)), + MKLINE(Track, dataOffset, sleInt32, VER(31)), + MKLINE(Track, curRegion, sleInt32, VER(31)), + MKLINE(Track, curHookId, sleInt32, VER(31)), + MKLINE(Track, volGroupId, sleInt32, VER(31)), + MKLINE(Track, soundType, sleInt32, VER(31)), + MKLINE(Track, iteration, sleInt32, VER(31)), + MKLINE(Track, mod, sleInt32, VER(31)), + MKLINE(Track, mixerFlags, sleInt32, VER(31)), + MK_OBSOLETE(Track, mixerVol, sleInt32, VER(31), VER(42)), + MK_OBSOLETE(Track, mixerPan, sleInt32, VER(31), VER(42)), + MKLINE(Track, compressed, sleByte, VER(45)), + MKEND() + }; + + ser->saveLoadEntries(this, mainEntries); + + for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) { + Track *track = _track[l]; + if (!ser->isSaving()) { + track->compressed = false; + } + ser->saveLoadEntries(track, trackEntries); + if (!ser->isSaving()) { + if (!track->used) + continue; + track->readyToRemove = false; + if ((track->toBeRemoved) || (track->souStream) || (track->curRegion == -1)) { + track->stream2 = NULL; + track->stream = NULL; + track->used = false; + continue; + } + + track->soundHandle = _sound->openSound(track->soundId, + track->soundName, track->soundType, + track->volGroupId, -1); + if (!track->soundHandle) { + warning("IMuseDigital::saveOrLoad: Can't open sound so will not be resumed, propably on diffrent CD"); + track->stream2 = NULL; + track->stream = NULL; + track->used = false; + continue; + } + + if (track->compressed) { + track->regionOffset = 0; + } + track->compressed = _sound->isCompressed(track->soundHandle); + if (track->compressed) { + track->regionOffset = 0; + } + track->dataOffset = _sound->getRegionOffset(track->soundHandle, track->curRegion); + int bits = _sound->getBits(track->soundHandle); + int channels = _sound->getChannels(track->soundHandle); + int freq = _sound->getFreq(track->soundHandle); + track->iteration = freq * channels; + track->mixerFlags = 0; + if (channels == 2) + track->mixerFlags = Audio::Mixer::FLAG_STEREO | Audio::Mixer::FLAG_REVERSE_STEREO; + + if ((bits == 12) || (bits == 16)) { + track->mixerFlags |= Audio::Mixer::FLAG_16BITS; + track->iteration *= 2; + } else if (bits == 8) { + track->mixerFlags |= Audio::Mixer::FLAG_UNSIGNED; + } else + error("IMuseDigital::saveOrLoad(): Can't handle %d bit samples", bits); + +#ifdef SCUMM_LITTLE_ENDIAN + if (track->compressed) + track->mixerFlags |= Audio::Mixer::FLAG_LITTLE_ENDIAN; +#endif + + int32 streamBufferSize = track->iteration; + track->stream2 = NULL; + track->stream = makeAppendableAudioStream(freq, track->mixerFlags, streamBufferSize); + + const int pan = (track->pan != 64) ? 2 * track->pan - 127 : 0; + const int vol = track->vol / 1000; + Audio::Mixer::SoundType type = Audio::Mixer::kPlainSoundType; + + if (track->volGroupId == 1) + type = Audio::Mixer::kSpeechSoundType; + if (track->volGroupId == 2) + type = Audio::Mixer::kSFXSoundType; + if (track->volGroupId == 3) + type = Audio::Mixer::kMusicSoundType; + + _vm->_mixer->playInputStream(type, &track->handle, track->stream, -1, vol, pan, false); + } + } +} + +void IMuseDigital::callback() { + Common::StackLock lock(_mutex, "IMuseDigital::callback()"); + + for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) { + Track *track = _track[l]; + if (track->used && !track->readyToRemove) { + if (track->toBeRemoved) { + track->readyToRemove = true; + continue; + } + + if (_pause || !_vm) + return; + + if (track->volFadeUsed) { + if (track->volFadeStep < 0) { + if (track->vol > track->volFadeDest) { + track->vol += track->volFadeStep; + if (track->vol < track->volFadeDest) { + track->vol = track->volFadeDest; + track->volFadeUsed = false; + } + if (track->vol == 0) { + track->toBeRemoved = true; + } + } + } else if (track->volFadeStep > 0) { + if (track->vol < track->volFadeDest) { + track->vol += track->volFadeStep; + if (track->vol > track->volFadeDest) { + track->vol = track->volFadeDest; + track->volFadeUsed = false; + } + } + } + debug(5, "Fade: sound(%d), Vol(%d)", track->soundId, track->vol / 1000); + } + + const int pan = (track->pan != 64) ? 2 * track->pan - 127 : 0; + const int vol = track->vol / 1000; + Audio::Mixer::SoundType type = Audio::Mixer::kPlainSoundType; + + if (track->volGroupId == 1) + type = Audio::Mixer::kSpeechSoundType; + if (track->volGroupId == 2) + type = Audio::Mixer::kSFXSoundType; + if (track->volGroupId == 3) + type = Audio::Mixer::kMusicSoundType; + + if (track->stream) { + byte *data = NULL; + int32 result = 0; + + if (track->curRegion == -1) { + switchToNextRegion(track); + if (track->toBeRemoved) + continue; + } + + int bits = _sound->getBits(track->soundHandle); + int channels = _sound->getChannels(track->soundHandle); + + int32 mixer_size = track->iteration / _callbackFps; + + if (track->stream->endOfData()) { + mixer_size *= 2; + } + + if ((bits == 12) || (bits == 16)) { + if (channels == 1) + mixer_size &= ~1; + if (channels == 2) + mixer_size &= ~3; + } else { + if (channels == 2) + mixer_size &= ~1; + } + + if (mixer_size == 0) + continue; + + do { + if (bits == 12) { + byte *ptr = NULL; + + mixer_size += track->mod; + int mixer_size_12 = (mixer_size * 3) / 4; + int length = (mixer_size_12 / 3) * 4; + track->mod = mixer_size - length; + + int32 offset = (track->regionOffset * 3) / 4; + int result2 = _sound->getDataFromRegion(track->soundHandle, track->curRegion, &ptr, offset, mixer_size_12); + result = BundleCodecs::decode12BitsSample(ptr, &data, result2); + + free(ptr); + } else if (bits == 16) { + result = _sound->getDataFromRegion(track->soundHandle, track->curRegion, &data, track->regionOffset, mixer_size); + if (channels == 1) { + result &= ~1; + } + if (channels == 2) { + result &= ~3; + } + } else if (bits == 8) { + result = _sound->getDataFromRegion(track->soundHandle, track->curRegion, &data, track->regionOffset, mixer_size); + if (channels == 2) { + result &= ~1; + } + } + + if (result > mixer_size) + result = mixer_size; + + if (_vm->_mixer->isReady()) { + _vm->_mixer->setChannelVolume(track->handle, vol); + _vm->_mixer->setChannelBalance(track->handle, pan); + track->stream->append(data, result); + track->regionOffset += result; + } + free(data); + + if (_sound->isEndOfRegion(track->soundHandle, track->curRegion)) { + switchToNextRegion(track); + if (track->toBeRemoved) + break; + } + mixer_size -= result; + assert(mixer_size >= 0); + } while (mixer_size != 0); + } else if (track->stream2) { + if (_vm->_mixer->isReady()) { + if (!track->started) { + track->started = true; + _vm->_mixer->playInputStream(type, &track->handle, track->stream2, -1, vol, pan, false); + } else { + _vm->_mixer->setChannelVolume(track->handle, vol); + _vm->_mixer->setChannelBalance(track->handle, pan); + } + } + } + } + } +} + +void IMuseDigital::switchToNextRegion(Track *track) { + assert(track); + debug(5, "switchToNextRegion(track:%d)", track->trackId); + + if (track->trackId >= MAX_DIGITAL_TRACKS) { + track->toBeRemoved = true; + debug(5, "exit (fadetrack can't go next region) switchToNextRegion(trackId:%d)", track->trackId); + return; + } + + int num_regions = _sound->getNumRegions(track->soundHandle); + + if (++track->curRegion == num_regions) { + track->toBeRemoved = true; + debug(5, "exit (end of regions) switchToNextRegion(track:%d)", track->trackId); + return; + } + + ImuseDigiSndMgr::soundStruct *soundHandle = track->soundHandle; + int jumpId = _sound->getJumpIdByRegionAndHookId(soundHandle, track->curRegion, track->curHookId); + if (jumpId == -1) + jumpId = _sound->getJumpIdByRegionAndHookId(soundHandle, track->curRegion, 0); + if (jumpId != -1) { + int region = _sound->getRegionIdByJumpId(soundHandle, jumpId); + assert(region != -1); + int sampleHookId = _sound->getJumpHookId(soundHandle, jumpId); + assert(sampleHookId != -1); + int fadeDelay = (60 * _sound->getJumpFade(soundHandle, jumpId)) / 1000; + if (sampleHookId != 0) { + if (track->curHookId == sampleHookId) { + if (fadeDelay != 0) { + Track *fadeTrack = cloneToFadeOutTrack(track, fadeDelay); + if (fadeTrack) { + fadeTrack->dataOffset = _sound->getRegionOffset(fadeTrack->soundHandle, fadeTrack->curRegion); + fadeTrack->regionOffset = 0; + debug(5, "switchToNextRegion-sound(%d) select region %d, curHookId: %d", fadeTrack->soundId, fadeTrack->curRegion, fadeTrack->curHookId); + fadeTrack->curHookId = 0; + } + } + track->curRegion = region; + debug(5, "switchToNextRegion-sound(%d) jump to region %d, curHookId: %d", track->soundId, track->curRegion, track->curHookId); + track->curHookId = 0; + } + } else { + if (fadeDelay != 0) { + Track *fadeTrack = cloneToFadeOutTrack(track, fadeDelay); + if (fadeTrack) { + fadeTrack->dataOffset = _sound->getRegionOffset(fadeTrack->soundHandle, fadeTrack->curRegion); + fadeTrack->regionOffset = 0; + debug(5, "switchToNextRegion-sound(%d) select region %d, curHookId: %d", fadeTrack->soundId, fadeTrack->curRegion, fadeTrack->curHookId); + } + } + track->curRegion = region; + debug(5, "switchToNextRegion-sound(%d) jump to region %d, curHookId: %d", track->soundId, track->curRegion, track->curHookId); + } + } + + debug(5, "switchToNextRegion-sound(%d) select region %d, curHookId: %d", track->soundId, track->curRegion, track->curHookId); + track->dataOffset = _sound->getRegionOffset(soundHandle, track->curRegion); + track->regionOffset = 0; +} + +} // End of namespace Scumm diff --git a/engines/scumm/imuse_digi/dimuse.h b/engines/scumm/imuse_digi/dimuse.h new file mode 100644 index 0000000000..fed2e48457 --- /dev/null +++ b/engines/scumm/imuse_digi/dimuse.h @@ -0,0 +1,239 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#if !defined(IMUSE_DIGI_H) && !defined(DISABLE_SCUMM_7_8) +#define IMUSE_DIGI_H + +#include "common/scummsys.h" +#include "common/util.h" + +#include "scumm/imuse_digi/dimuse.h" +#include "scumm/imuse_digi/dimuse_bndmgr.h" +#include "scumm/imuse_digi/dimuse_sndmgr.h" +#include "scumm/music.h" +#include "scumm/sound.h" + +#include "sound/mixer.h" +#include "sound/audiostream.h" + +namespace Scumm { + +#define MAX_DIGITAL_TRACKS 8 +#define MAX_DIGITAL_FADETRACKS 8 + +struct imuseDigTable; +struct imuseComiTable; +class Serializer; + +class IMuseDigital : public MusicEngine { +private: + + int _callbackFps; + + struct Track { + int trackId; + + int8 pan; // pan + int32 vol; // volume + int32 volFadeDest; // + int32 volFadeStep; // + int32 volFadeDelay; // + bool volFadeUsed; // + + int32 soundId; + char soundName[15]; + bool used; + bool toBeRemoved; + bool readyToRemove; + bool started; + bool souStream; + bool compressed; + int32 priority; + int32 regionOffset; + int32 dataOffset; + int32 curRegion; + int32 curHookId; + int32 volGroupId; + int32 soundType; + int32 iteration; + int32 mod; + int32 mixerFlags; + + ImuseDigiSndMgr::soundStruct *soundHandle; + Audio::SoundHandle handle; + AppendableAudioStream *stream; + AudioStream *stream2; + + Track(); + }; + + Track *_track[MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS]; + + Common::Mutex _mutex; + ScummEngine *_vm; + ImuseDigiSndMgr *_sound; + + char *_audioNames; + int32 _numAudioNames; + + bool _pause; + + int32 _attributes[188]; + int32 _nextSeqToPlay; + int32 _curMusicState; + int32 _curMusicSeq; + int32 _curMusicCue; + + static void timer_handler(void *refConf); + void callback(); + void switchToNextRegion(Track *track); + int allocSlot(int priority); + void startSound(int soundId, const char *soundName, int soundType, int volGroupId, AudioStream *input, int hookId, int volume, int priority); + void selectVolumeGroup(int soundId, int volGroupId); + + int32 getPosInMs(int soundId); + void getLipSync(int soundId, int syncId, int32 msPos, int32 &width, int32 &height); + + int getSoundIdByName(const char *soundName); + void fadeOutMusic(int fadeDelay); + Track *cloneToFadeOutTrack(Track *track, int fadeDelay); + + void setFtMusicState(int stateId); + void setFtMusicSequence(int seqId); + void setFtMusicCuePoint(int cueId); + void playFtMusic(const char *songName, int opcode, int volume); + + void setComiMusicState(int stateId); + void setComiMusicSequence(int seqId); + void playComiMusic(const char *songName, const imuseComiTable *table, int atribPos, bool sequence); + + void setDigMusicState(int stateId); + void setDigMusicSequence(int seqId); + void playDigMusic(const char *songName, const imuseDigTable *table, int atribPos, bool sequence); + +public: + IMuseDigital(ScummEngine *scumm, int fps); + virtual ~IMuseDigital(); + + void setAudioNames(int32 num, char *names); + + 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 sound) + { error("MusicEngine::startSound() Should be never called"); } + + void saveOrLoad(Serializer *ser); + void resetState(); + + void setPriority(int soundId, int priority); + void setVolume(int soundId, int volume); + void setPan(int soundId, int pan); + void setFade(int soundId, int destVolume, int delay60HzTicks); + int getCurMusicSoundId(); + char *getCurMusicSoundName(); + void setHookId(int soundId, int hookId); + void setMusicVolume(int vol) {} + void stopSound(int sound); + void stopAllSounds(); + void pause(bool pause); + void parseScriptCmds(int cmd, int soundId, int sub_cmd, int d, int e, int f, int g, int h); + void refreshScripts(); + void flushTracks(); + int getSoundStatus(int sound) const; + int32 getCurMusicPosInMs(); + int32 getCurVoiceLipSyncWidth(); + int32 getCurVoiceLipSyncHeight(); + int32 getCurMusicLipSyncWidth(int syncId); + int32 getCurMusicLipSyncHeight(int syncId); +}; + +struct imuseRoomMap { + int8 roomId; + byte stateIndex1; + byte offset; + byte stateIndex2; + byte atribPos; + byte stateIndex3; +}; + +struct imuseDigTable { + byte opcode; + int16 soundId; + char name[20]; + byte atribPos; + byte hookId; + char filename[13]; +}; + +struct imuseComiTable { + byte opcode; + int16 soundId; + char name[20]; + byte atribPos; + byte hookId; + int16 fadeOut60TicksDelay; + char filename[13]; +}; + + +struct imuseFtNames { + char name[20]; +}; + +struct imuseFtStateTable { + char audioName[9]; + byte opcode; + byte volume; + char name[21]; +}; + +struct imuseFtSeqTable { + char audioName[9]; + byte opcode; + byte volume; +}; + +#ifdef PALMOS_68K +extern const imuseRoomMap *_digStateMusicMap; +extern const imuseDigTable *_digStateMusicTable; +extern const imuseDigTable *_digSeqMusicTable; +extern const imuseComiTable *_comiStateMusicTable; +extern const imuseComiTable *_comiSeqMusicTable; +extern const imuseFtStateTable *_ftStateMusicTable; +extern const imuseFtSeqTable *_ftSeqMusicTable; +extern const imuseFtNames *_ftSeqNames; +#else +extern const imuseRoomMap _digStateMusicMap[]; +extern const imuseDigTable _digStateMusicTable[]; +extern const imuseDigTable _digSeqMusicTable[]; +extern const imuseComiTable _comiStateMusicTable[]; +extern const imuseComiTable _comiSeqMusicTable[]; +extern const imuseFtStateTable _ftStateMusicTable[]; +extern const imuseFtSeqTable _ftSeqMusicTable[]; +extern const imuseFtNames _ftSeqNames[]; +#endif + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/imuse_digi/dimuse_bndmgr.cpp b/engines/scumm/imuse_digi/dimuse_bndmgr.cpp new file mode 100644 index 0000000000..d36d733ef2 --- /dev/null +++ b/engines/scumm/imuse_digi/dimuse_bndmgr.cpp @@ -0,0 +1,344 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "scumm/scumm.h" +#include "scumm/util.h" +#include "scumm/imuse_digi/dimuse_bndmgr.h" + +namespace Scumm { + +BundleDirCache::BundleDirCache() { + for (int fileId = 0; fileId < ARRAYSIZE(_budleDirCache); fileId++) { + _budleDirCache[fileId].bundleTable = NULL; + _budleDirCache[fileId].fileName[0] = 0; + _budleDirCache[fileId].numFiles = 0; + _budleDirCache[fileId].compressedBun = false; + _budleDirCache[fileId].indexTable = NULL; + } +} + +BundleDirCache::~BundleDirCache() { + for (int fileId = 0; fileId < ARRAYSIZE(_budleDirCache); fileId++) { + free(_budleDirCache[fileId].bundleTable); + free(_budleDirCache[fileId].indexTable); + } +} + +BundleDirCache::AudioTable *BundleDirCache::getTable(int slot) { + return _budleDirCache[slot].bundleTable; +} + +int32 BundleDirCache::getNumFiles(int slot) { + return _budleDirCache[slot].numFiles; +} + +BundleDirCache::IndexNode *BundleDirCache::getIndexTable(int slot) { + return _budleDirCache[slot].indexTable; +} + +bool BundleDirCache::isCompressed(int slot) { + return _budleDirCache[slot].compressedBun; +} + +int BundleDirCache::matchFile(const char *filename) { + int32 tag, offset; + bool found = false; + int freeSlot = -1; + int fileId; + + for (fileId = 0; fileId < ARRAYSIZE(_budleDirCache); fileId++) { + if ((_budleDirCache[fileId].bundleTable == NULL) && (freeSlot == -1)) { + freeSlot = fileId; + } + if (scumm_stricmp(filename, _budleDirCache[fileId].fileName) == 0) { + found = true; + break; + } + } + + if (!found) { + ScummFile file; + + if (g_scumm->openFile(file, filename) == false) { + error("BundleDirCache::matchFile() Can't open bundle file: %s", filename); + return false; + } + + if (freeSlot == -1) + error("BundleDirCache::matchFileFile() Can't find free slot for file bundle dir cache"); + + tag = file.readUint32BE(); + if (tag == 'LB23') + _budleDirCache[freeSlot].compressedBun = true; + offset = file.readUint32BE(); + + strcpy(_budleDirCache[freeSlot].fileName, filename); + _budleDirCache[freeSlot].numFiles = file.readUint32BE(); + _budleDirCache[freeSlot].bundleTable = (AudioTable *) malloc(_budleDirCache[freeSlot].numFiles * sizeof(AudioTable)); + + file.seek(offset, SEEK_SET); + + _budleDirCache[freeSlot].indexTable = + (IndexNode *)calloc(_budleDirCache[freeSlot].numFiles, sizeof(IndexNode)); + + for (int32 i = 0; i < _budleDirCache[freeSlot].numFiles; i++) { + char name[24], c; + int32 z = 0; + int32 z2; + + if (tag == 'LB23') { + file.read(_budleDirCache[freeSlot].bundleTable[i].filename, 24); + } else { + for (z2 = 0; z2 < 8; z2++) + if ((c = file.readByte()) != 0) + name[z++] = c; + name[z++] = '.'; + for (z2 = 0; z2 < 4; z2++) + if ((c = file.readByte()) != 0) + name[z++] = c; + + name[z] = '\0'; + strcpy(_budleDirCache[freeSlot].bundleTable[i].filename, name); + } + _budleDirCache[freeSlot].bundleTable[i].offset = file.readUint32BE(); + _budleDirCache[freeSlot].bundleTable[i].size = file.readUint32BE(); + strcpy(_budleDirCache[freeSlot].indexTable[i].filename, + _budleDirCache[freeSlot].bundleTable[i].filename); + _budleDirCache[freeSlot].indexTable[i].index = i; + } + qsort(_budleDirCache[freeSlot].indexTable, _budleDirCache[freeSlot].numFiles, + sizeof(IndexNode), (int (*)(const void*, const void*))scumm_stricmp); + return freeSlot; + } else { + return fileId; + } +} + +BundleMgr::BundleMgr(BundleDirCache *cache) { + _cache = cache; + _bundleTable = NULL; + _compTable = NULL; + _numFiles = 0; + _numCompItems = 0; + _curSample = -1; + _fileBundleId = -1; + _compInput = NULL; +} + +BundleMgr::~BundleMgr() { + close(); +} + +Common::File *BundleMgr::getFile(const char *filename, int32 &offset, int32 &size) { + BundleDirCache::IndexNode target; + strcpy(target.filename, filename); + BundleDirCache::IndexNode *found = (BundleDirCache::IndexNode *)bsearch(&target, _indexTable, _numFiles, + sizeof(BundleDirCache::IndexNode), (int (*)(const void*, const void*))scumm_stricmp); + if (found) { + _file.seek(_bundleTable[found->index].offset, SEEK_SET); + offset = _bundleTable[found->index].offset; + size = _bundleTable[found->index].size; + return &_file; + } + + return NULL; +} + +bool BundleMgr::open(const char *filename, bool &compressed, bool errorFlag) { + if (_file.isOpen()) + return true; + + if (g_scumm->openFile(_file, filename) == false) { + if (errorFlag) { + error("BundleMgr::open() Can't open bundle file: %s", filename); + } else { + warning("BundleMgr::open() Can't open bundle file: %s", filename); + } + return false; + } + + int slot = _cache->matchFile(filename); + assert(slot != -1); + compressed = _cache->isCompressed(slot); + _numFiles = _cache->getNumFiles(slot); + assert(_numFiles); + _bundleTable = _cache->getTable(slot); + _indexTable = _cache->getIndexTable(slot); + assert(_bundleTable); + _compTableLoaded = false; + _outputSize = 0; + _lastBlock = -1; + + return true; +} + +void BundleMgr::close() { + if (_file.isOpen()) { + _file.close(); + _bundleTable = NULL; + _numFiles = 0; + _numCompItems = 0; + _compTableLoaded = false; + _lastBlock = -1; + _outputSize = 0; + _curSample = -1; + free(_compTable); + _compTable = NULL; + free(_compInput); + _compInput = NULL; + } +} + +bool BundleMgr::loadCompTable(int32 index) { + _file.seek(_bundleTable[index].offset, SEEK_SET); + uint32 tag = _file.readUint32BE(); + _numCompItems = _file.readUint32BE(); + assert(_numCompItems > 0); + _file.seek(8, SEEK_CUR); + + if (tag != MKID_BE('COMP')) { + error("BundleMgr::loadCompTable() Compressed sound %d invalid (%s)", index, tag2str(tag)); + return false; + } + + _compTable = (CompTable *)malloc(sizeof(CompTable) * _numCompItems); + int32 maxSize = 0; + for (int i = 0; i < _numCompItems; i++) { + _compTable[i].offset = _file.readUint32BE(); + _compTable[i].size = _file.readUint32BE(); + _compTable[i].codec = _file.readUint32BE(); + _file.seek(4, SEEK_CUR); + if (_compTable[i].size > maxSize) + maxSize = _compTable[i].size; + } + // CMI hack: one more byte at the end of input buffer + _compInput = (byte *)malloc(maxSize + 1); + + return true; +} + +int32 BundleMgr::decompressSampleByCurIndex(int32 offset, int32 size, byte **comp_final, int header_size, bool header_outside) { + return decompressSampleByIndex(_curSample, offset, size, comp_final, header_size, header_outside); +} + +int32 BundleMgr::decompressSampleByIndex(int32 index, int32 offset, int32 size, byte **comp_final, int header_size, bool header_outside) { + int32 i, final_size, output_size; + int skip, first_block, last_block; + + assert(0 <= index && index < _numFiles); + + if (_file.isOpen() == false) { + error("BundleMgr::decompressSampleByIndex() File is not open!"); + return 0; + } + + if (_curSample == -1) + _curSample = index; + + assert(_curSample == index); + + if (!_compTableLoaded) { + _compTableLoaded = loadCompTable(index); + if (!_compTableLoaded) + return 0; + } + + first_block = (offset + header_size) / 0x2000; + last_block = (offset + header_size + size - 1) / 0x2000; + + // Clip last_block by the total number of blocks (= "comp items") + if ((last_block >= _numCompItems) && (_numCompItems > 0)) + last_block = _numCompItems - 1; + + int32 blocks_final_size = 0x2000 * (1 + last_block - first_block); + *comp_final = (byte *)malloc(blocks_final_size); + final_size = 0; + + skip = (offset + header_size) % 0x2000; + + for (i = first_block; i <= last_block; i++) { + if (_lastBlock != i) { + // CMI hack: one more zero byte at the end of input buffer + _compInput[_compTable[i].size] = 0; + _file.seek(_bundleTable[index].offset + _compTable[i].offset, SEEK_SET); + _file.read(_compInput, _compTable[i].size); + _outputSize = BundleCodecs::decompressCodec(_compTable[i].codec, _compInput, _compOutput, _compTable[i].size); + if (_outputSize > 0x2000) { + error("_outputSize: %d", _outputSize); + } + _lastBlock = i; + } + + output_size = _outputSize; + + if (header_outside) { + output_size -= skip; + } else { + if ((header_size != 0) && (skip >= header_size)) + output_size -= skip; + } + + if ((output_size + skip) > 0x2000) // workaround + output_size -= (output_size + skip) - 0x2000; + + if (output_size > size) + output_size = size; + + assert(final_size + output_size <= blocks_final_size); + + memcpy(*comp_final + final_size, _compOutput + skip, output_size); + final_size += output_size; + + size -= output_size; + assert(size >= 0); + if (size == 0) + break; + + skip = 0; + } + + return final_size; +} + +int32 BundleMgr::decompressSampleByName(const char *name, int32 offset, int32 size, byte **comp_final, bool header_outside) { + int32 final_size = 0; + + if (!_file.isOpen()) { + error("BundleMgr::decompressSampleByName() File is not open!"); + return 0; + } + + BundleDirCache::IndexNode target; + strcpy(target.filename, name); + BundleDirCache::IndexNode *found = (BundleDirCache::IndexNode *)bsearch(&target, _indexTable, _numFiles, + sizeof(BundleDirCache::IndexNode), (int (*)(const void*, const void*))scumm_stricmp); + if (found) { + final_size = decompressSampleByIndex(found->index, offset, size, comp_final, 0, header_outside); + return final_size; + } + + debug(2, "BundleMgr::decompressSampleByName() Failed finding voice %s", name); + return final_size; +} + +} // End of namespace Scumm diff --git a/engines/scumm/imuse_digi/dimuse_bndmgr.h b/engines/scumm/imuse_digi/dimuse_bndmgr.h new file mode 100644 index 0000000000..36d4f5683e --- /dev/null +++ b/engines/scumm/imuse_digi/dimuse_bndmgr.h @@ -0,0 +1,115 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef BUNDLE_MGR_H +#define BUNDLE_MGR_H + +#include "common/scummsys.h" +#include "common/file.h" +#include "scumm/util.h" + +namespace Scumm { + +class BundleDirCache { +public: + struct AudioTable { + char filename[24]; + int32 offset; + int32 size; + }; + + struct IndexNode { + char filename[24]; + int32 index; + }; + +private: + + struct FileDirCache { + char fileName[20]; + AudioTable *bundleTable; + int32 numFiles; + bool compressedBun; + IndexNode *indexTable; + } _budleDirCache[4]; + +public: + BundleDirCache(); + ~BundleDirCache(); + + int matchFile(const char *filename); + AudioTable *getTable(int slot); + IndexNode *getIndexTable(int slot); + int32 getNumFiles(int slot); + bool isCompressed(int slot); +}; + +class BundleMgr { + +private: + + struct CompTable { + int32 offset; + int32 size; + int32 codec; + }; + + BundleDirCache *_cache; + BundleDirCache::AudioTable *_bundleTable; + BundleDirCache::IndexNode *_indexTable; + CompTable *_compTable; + int _numFiles; + int _numCompItems; + int _curSample; + ScummFile _file; + bool _compTableLoaded; + int _fileBundleId; + byte _compOutput[0x2000]; + byte *_compInput; + int _outputSize; + int _lastBlock; + + bool loadCompTable(int32 index); + +public: + + BundleMgr(BundleDirCache *_cache); + ~BundleMgr(); + + bool open(const char *filename, bool &compressed, bool errorFlag=false); + void close(); + Common::File *getFile(const char *filename, int32 &offset, int32 &size); + int32 decompressSampleByName(const char *name, int32 offset, int32 size, byte **comp_final, bool header_outside); + int32 decompressSampleByIndex(int32 index, int32 offset, int32 size, byte **comp_final, int header_size, bool header_outside); + int32 decompressSampleByCurIndex(int32 offset, int32 size, byte **comp_final, int header_size, bool header_outside); +}; + +namespace BundleCodecs { + +uint32 decode12BitsSample(const byte *src, byte **dst, uint32 size); +void initializeImcTables(); +int32 decompressCodec(int32 codec, byte *comp_input, byte *comp_output, int32 input_size); + +} // End of namespace BundleCodecs + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/imuse_digi/dimuse_codecs.cpp b/engines/scumm/imuse_digi/dimuse_codecs.cpp new file mode 100644 index 0000000000..dccae928b0 --- /dev/null +++ b/engines/scumm/imuse_digi/dimuse_codecs.cpp @@ -0,0 +1,655 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "common/util.h" + +namespace Scumm { + +namespace BundleCodecs { + +uint32 decode12BitsSample(const byte *src, byte **dst, uint32 size) { + uint32 loop_size = size / 3; + uint32 s_size = loop_size * 4; + byte *ptr = *dst = (byte *)malloc(s_size); + + uint32 tmp; + while (loop_size--) { + byte v1 = *src++; + byte v2 = *src++; + byte v3 = *src++; + tmp = ((((v2 & 0x0f) << 8) | v1) << 4) - 0x8000; + WRITE_BE_UINT16(ptr, tmp); ptr += 2; + tmp = ((((v2 & 0xf0) << 4) | v3) << 4) - 0x8000; + WRITE_BE_UINT16(ptr, tmp); ptr += 2; + } + return s_size; +} + +/* + * The "IMC" codec below (see cases 13 & 15 in decompressCodec) is actually a + * variant of the IMA codec, see also + * <http://www.multimedia.cx/simpleaudio.html> + * + * It is somewhat different, though: the standard ADPCM codecs use a fixed + * size for their data packets (4 bits), while the codec implemented here + * varies the size of each "packet" between 2 and 7 bits. + */ + +static byte _imcTableEntryBitCount[89]; + +#ifdef PALMOS_68K +static const int16 *imcTable; +#else +static const int16 imcTable[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493,10442,11487,12635,13899, + 15289,16818,18500,20350,22385,24623,27086,29794, + 32767 +}; +#endif + +static const byte imxOtherTable[6][64] = { + { + 0xFF, + 4 + }, + + { + 0xFF, 0xFF, + 2, 8 + }, + + { + 0xFF, 0xFF, 0xFF, 0xFF, + 1, 2, 4, 6 + }, + + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 1, 2, 4, 6, 8, 12, 16, 32 + }, + + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 1, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 32 + }, + + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32 + } +}; + +void initializeImcTables() { + int pos; + + for (pos = 0; pos < ARRAYSIZE(imcTable); ++pos) { + byte put = 0; + int32 tableValue = ((imcTable[pos] * 4) / 7) / 2; + while (tableValue != 0) { + tableValue /= 2; + put++; + } + if (put < 2) { + put = 2; + } + if (put > 7) { + put = 7; + } + _imcTableEntryBitCount[pos] = put; + } +} + +#define NextBit \ + do { \ + bit = mask & 1; \ + mask >>= 1; \ + if (!--bitsleft) { \ + mask = READ_LE_UINT16(srcptr); \ + srcptr += 2; \ + bitsleft = 16; \ + } \ + } while (0) + +static int32 compDecode(byte *src, byte *dst) { + byte *result, *srcptr = src, *dstptr = dst; + int data, size, bit, bitsleft = 16, mask = READ_LE_UINT16(srcptr); + srcptr += 2; + + for (;;) { + NextBit; + if (bit) { + *dstptr++ = *srcptr++; + } else { + NextBit; + if (!bit) { + NextBit; + size = bit << 1; + NextBit; + size = (size | bit) + 3; + data = *srcptr++ | 0xffffff00; + } else { + data = *srcptr++; + size = *srcptr++; + + data |= 0xfffff000 + ((size & 0xf0) << 4); + size = (size & 0x0f) + 3; + + if (size == 3) + if (((*srcptr++) + 1) == 1) + return dstptr - dst; + } + result = dstptr + data; + while (size--) + *dstptr++ = *result++; + } + } +} +#undef NextBit + +int32 decompressCodec(int32 codec, byte *comp_input, byte *comp_output, int32 input_size) { + int32 output_size, channels; + int32 offset1, offset2, offset3, length, k, c, s, j, r, t, z; + byte *src, *t_table, *p, *ptr; + byte t_tmp1, t_tmp2; + + switch (codec) { + case 0: + memcpy(comp_output, comp_input, input_size); + output_size = input_size; + break; + + case 1: + output_size = compDecode(comp_input, comp_output); + break; + + case 2: + output_size = compDecode(comp_input, comp_output); + p = comp_output; + for (z = 1; z < output_size; z++) + p[z] += p[z - 1]; + break; + + case 3: + output_size = compDecode(comp_input, comp_output); + p = comp_output; + for (z = 2; z < output_size; z++) + p[z] += p[z - 1]; + for (z = 1; z < output_size; z++) + p[z] += p[z - 1]; + break; + + case 4: + output_size = compDecode(comp_input, comp_output); + p = comp_output; + for (z = 2; z < output_size; z++) + p[z] += p[z - 1]; + for (z = 1; z < output_size; z++) + p[z] += p[z - 1]; + + t_table = (byte *)malloc(output_size); + + src = comp_output; + length = (output_size << 3) / 12; + k = 0; + if (length > 0) { + c = -12; + s = 0; + j = 0; + do { + ptr = src + length + (k >> 1); + t_tmp2 = src[j]; + if (k & 1) { + r = c >> 3; + t_table[r + 2] = ((t_tmp2 & 0x0f) << 4) | (ptr[1] >> 4); + t_table[r + 1] = (t_tmp2 & 0xf0) | (t_table[r + 1]); + } else { + r = s >> 3; + t_table[r + 0] = ((t_tmp2 & 0x0f) << 4) | (ptr[0] & 0x0f); + t_table[r + 1] = t_tmp2 >> 4; + } + s += 12; + c += 12; + k++; + j++; + } while (k < length); + } + offset1 = ((length - 1) * 3) >> 1; + t_table[offset1 + 1] = (t_table[offset1 + 1]) | (src[length - 1] & 0xf0); + memcpy(src, t_table, output_size); + free(t_table); + break; + + case 5: + output_size = compDecode(comp_input, comp_output); + p = comp_output; + for (z = 2; z < output_size; z++) + p[z] += p[z - 1]; + for (z = 1; z < output_size; z++) + p[z] += p[z - 1]; + + t_table = (byte *)malloc(output_size); + + src = comp_output; + length = (output_size << 3) / 12; + k = 1; + c = 0; + s = 12; + t_table[0] = src[length] >> 4; + t = length + k; + j = 1; + if (t > k) { + do { + t_tmp1 = *(src + length + (k >> 1)); + t_tmp2 = src[j - 1]; + if (k & 1) { + r = c >> 3; + t_table[r + 0] = (t_tmp2 & 0xf0) | t_table[r]; + t_table[r + 1] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 & 0x0f); + } else { + r = s >> 3; + t_table[r + 0] = t_tmp2 >> 4; + t_table[r - 1] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 >> 4); + } + s += 12; + c += 12; + k++; + j++; + } while (k < t); + } + memcpy(src, t_table, output_size); + free(t_table); + break; + + case 6: + output_size = compDecode(comp_input, comp_output); + p = comp_output; + for (z = 2; z < output_size; z++) + p[z] += p[z - 1]; + for (z = 1; z < output_size; z++) + p[z] += p[z - 1]; + + t_table = (byte *)malloc(output_size); + + src = comp_output; + length = (output_size << 3) / 12; + k = 0; + c = 0; + j = 0; + s = -12; + t_table[0] = src[output_size - 1]; + t_table[output_size - 1] = src[length - 1]; + t = length - 1; + if (t > 0) { + do { + t_tmp1 = *(src + length + (k >> 1)); + t_tmp2 = src[j]; + if (k & 1) { + r = s >> 3; + t_table[r + 2] = (t_tmp2 & 0xf0) | t_table[r + 2]; + t_table[r + 3] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 >> 4); + } else { + r = c >> 3; + t_table[r + 2] = t_tmp2 >> 4; + t_table[r + 1] = ((t_tmp2 & 0x0f) << 4) | (t_tmp1 & 0x0f); + } + s += 12; + c += 12; + k++; + j++; + } while (k < t); + } + memcpy(src, t_table, output_size); + free(t_table); + break; + + case 10: + output_size = compDecode(comp_input, comp_output); + p = comp_output; + for (z = 2; z < output_size; z++) + p[z] += p[z - 1]; + for (z = 1; z < output_size; z++) + p[z] += p[z - 1]; + + t_table = (byte *)malloc(output_size); + memcpy(t_table, p, output_size); + + offset1 = output_size / 3; + offset2 = offset1 << 1; + offset3 = offset2; + src = comp_output; + + while (offset1--) { + offset2 -= 2; + offset3--; + t_table[offset2 + 0] = src[offset1]; + t_table[offset2 + 1] = src[offset3]; + } + + src = comp_output; + length = (output_size << 3) / 12; + k = 0; + if (length > 0) { + c = -12; + s = 0; + do { + j = length + (k >> 1); + t_tmp1 = t_table[k]; + if (k & 1) { + r = c >> 3; + t_tmp2 = t_table[j + 1]; + src[r + 2] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 >> 4); + src[r + 1] = (src[r + 1]) | (t_tmp1 & 0xf0); + } else { + r = s >> 3; + t_tmp2 = t_table[j]; + src[r + 0] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 & 0x0f); + src[r + 1] = t_tmp1 >> 4; + } + s += 12; + c += 12; + k++; + } while (k < length); + } + offset1 = ((length - 1) * 3) >> 1; + src[offset1 + 1] = (t_table[length] & 0xf0) | src[offset1 + 1]; + free(t_table); + break; + + case 11: + output_size = compDecode(comp_input, comp_output); + p = comp_output; + for (z = 2; z < output_size; z++) + p[z] += p[z - 1]; + for (z = 1; z < output_size; z++) + p[z] += p[z - 1]; + + t_table = (byte *)malloc(output_size); + memcpy(t_table, p, output_size); + + offset1 = output_size / 3; + offset2 = offset1 << 1; + offset3 = offset2; + src = comp_output; + + while (offset1--) { + offset2 -= 2; + offset3--; + t_table[offset2 + 0] = src[offset1]; + t_table[offset2 + 1] = src[offset3]; + } + + src = comp_output; + length = (output_size << 3) / 12; + k = 1; + c = 0; + s = 12; + t_tmp1 = t_table[length] >> 4; + src[0] = t_tmp1; + t = length + k; + if (t > k) { + do { + j = length + (k >> 1); + t_tmp1 = t_table[k - 1]; + t_tmp2 = t_table[j]; + if (k & 1) { + r = c >> 3; + src[r + 0] = (src[r]) | (t_tmp1 & 0xf0); + src[r + 1] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 & 0x0f); + } else { + r = s >> 3; + src[r + 0] = t_tmp1 >> 4; + src[r - 1] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 >> 4); + } + s += 12; + c += 12; + k++; + } while (k < t); + } + free(t_table); + break; + + case 12: + output_size = compDecode(comp_input, comp_output); + p = comp_output; + for (z = 2; z < output_size; z++) + p[z] += p[z - 1]; + for (z = 1; z < output_size; z++) + p[z] += p[z - 1]; + + t_table = (byte *)malloc(output_size); + memcpy(t_table, p, output_size); + + offset1 = output_size / 3; + offset2 = offset1 << 1; + offset3 = offset2; + src = comp_output; + + while (offset1--) { + offset2 -= 2; + offset3--; + t_table[offset2 + 0] = src[offset1]; + t_table[offset2 + 1] = src[offset3]; + } + + src = comp_output; + length = (output_size << 3) / 12; + k = 0; + c = 0; + s = -12; + src[0] = t_table[output_size - 1]; + src[output_size - 1] = t_table[length - 1]; + t = length - 1; + if (t > 0) { + do { + j = length + (k >> 1); + t_tmp1 = t_table[k]; + t_tmp2 = t_table[j]; + if (k & 1) { + r = s >> 3; + src[r + 2] = (src[r + 2]) | (t_tmp1 & 0xf0); + src[r + 3] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 >> 4); + } else { + r = c >> 3; + src[r + 2] = t_tmp1 >> 4; + src[r + 1] = ((t_tmp1 & 0x0f) << 4) | (t_tmp2 & 0x0f); + } + s += 12; + c += 12; + k++; + } while (k < t); + } + free(t_table); + break; + + case 13: + case 15: + if (codec == 13) { + channels = 1; + } else { + channels = 2; + } + + { + // Decoder for the the IMA ADPCM variants used in COMI. + // Contrary to regular IMA ADPCM, this codec uses a variable + // bitsize for the encoded data. + + const int MAX_CHANNELS = 2; + int32 outputSamplesLeft; + int32 destPos; + int16 firstWord; + byte initialTablePos[MAX_CHANNELS] = {0, 0}; + int32 initialimcTableEntry[MAX_CHANNELS] = {7, 7}; + int32 initialOutputWord[MAX_CHANNELS] = {0, 0}; + int32 totalBitOffset, curTablePos, outputWord; + byte *dst; + int i; + + // We only support mono and stereo + assert(channels == 1 || channels == 2); + + src = comp_input; + dst = comp_output; + output_size = 0x2000; + outputSamplesLeft = 0x1000; + + // Every data packet contains 0x2000 bytes of audio data + // when extracted. In order to encode bigger data sets, + // one has to split the data into multiple blocks. + // + // Every block starts with a 2 byte word. If that word is + // non-zero, it indicates the size of a block of raw audio + // data (not encoded) following it. That data we simply copy + // to the output buffer and the proceed by decoding the + // remaining data. + // + // If on the other hand the word is zero, then what follows + // are 7*channels bytes containing seed data for the decoder. + firstWord = READ_BE_UINT16(src); + src += 2; + if (firstWord != 0) { + // Copy raw data + memcpy(dst, src, firstWord); + dst += firstWord; + src += firstWord; + assert((firstWord & 1) == 0); + outputSamplesLeft -= firstWord / 2; + } else { + // Read the seed values for the decoder. + for (i = 0; i < channels; i++) { + initialTablePos[i] = *src; + src += 1; + initialimcTableEntry[i] = READ_BE_UINT32(src); + src += 4; + initialOutputWord[i] = READ_BE_UINT32(src); + src += 4; + } + } + + totalBitOffset = 0; + // The channels are encoded separately. + for (int chan = 0; chan < channels; chan++) { + // Read initial state (this makes it possible for the data stream + // to be split & spread across multiple data chunks. + curTablePos = initialTablePos[chan]; + //imcTableEntry = initialimcTableEntry[chan]; + outputWord = initialOutputWord[chan]; + + // We need to interleave the channels in the output; we achieve + // that by using a variables dest offset: + destPos = chan * 2; + + const int bound = (channels == 1) + ? outputSamplesLeft + : ((chan == 0) + ? (outputSamplesLeft+1) / 2 + : outputSamplesLeft / 2); + for (i = 0; i < bound; ++i) { + // Determine the size (in bits) of the next data packet + const int32 curTableEntryBitCount = _imcTableEntryBitCount[curTablePos]; + assert(2 <= curTableEntryBitCount && curTableEntryBitCount <= 7); + + // Read the next data packet + const byte *readPos = src + (totalBitOffset >> 3); + const uint16 readWord = (uint16)(READ_BE_UINT16(readPos) << (totalBitOffset & 7)); + const byte packet = (byte)(readWord >> (16 - curTableEntryBitCount)); + + // Advance read position to the next data packet + totalBitOffset += curTableEntryBitCount; + + // Decode the data packet into a delta value for the output signal. + const byte signBitMask = (1 << (curTableEntryBitCount - 1)); + const byte dataBitMask = (signBitMask - 1); + const byte data = (packet & dataBitMask); + + int32 delta = imcTable[curTablePos] * (2 * data + 1) >> (curTableEntryBitCount - 1); + + // The topmost bit in the data packet tells is a sign bit + if ((packet & signBitMask) != 0) { + delta = -delta; + } + + // Accumulate the delta onto the output data + outputWord += delta; + + // Clip outputWord to 16 bit signed, and write it into the destination stream + if (outputWord > 0x7fff) + outputWord = 0x7fff; + if (outputWord < -0x8000) + outputWord = -0x8000; + WRITE_BE_UINT16(dst + destPos, outputWord); + destPos += channels << 1; + + // Adjust the curTablePos + curTablePos += (int8)imxOtherTable[curTableEntryBitCount - 2][data]; + if (curTablePos < 0) + curTablePos = 0; + else if (curTablePos >= ARRAYSIZE(imcTable)) + curTablePos = ARRAYSIZE(imcTable) - 1; + } + } + } + break; + + default: + error("BundleCodecs::decompressCodec() Unknown codec %d!", (int)codec); + output_size = 0; + break; + } + + return output_size; +} + +} // End of namespace BundleCodecs + +} // End of namespace Scumm + +#ifdef PALMOS_68K +#include "scumm_globals.h" + +_GINIT(DimuseCodecs) +_GSETPTR(Scumm::BundleCodecs::imcTable, GBVARS_IMCTABLE_INDEX, int16, GBVARS_SCUMM) +_GEND + +_GRELEASE(DimuseCodecs) +_GRELEASEPTR(GBVARS_IMCTABLE_INDEX, GBVARS_SCUMM) +_GEND + +#endif diff --git a/engines/scumm/imuse_digi/dimuse_music.cpp b/engines/scumm/imuse_digi/dimuse_music.cpp new file mode 100644 index 0000000000..4c0cedc57c --- /dev/null +++ b/engines/scumm/imuse_digi/dimuse_music.cpp @@ -0,0 +1,444 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "scumm/scumm.h" +#include "scumm/imuse_digi/dimuse.h" + +namespace Scumm { + +#define DIG_STATE_OFFSET 11 +#define DIG_SEQ_OFFSET (DIG_STATE_OFFSET + 65) +#define COMI_STATE_OFFSET 3 + +void IMuseDigital::setDigMusicState(int stateId) { + int l, num = -1; + + for (l = 0; _digStateMusicTable[l].soundId != -1; l++) { + if ((_digStateMusicTable[l].soundId == stateId)) { + debug(5, "Set music state: %s, %s", _digStateMusicTable[l].name, _digStateMusicTable[l].filename); + num = l; + break; + } + } + + if (num == -1) { + for (l = 0; _digStateMusicMap[l].roomId != -1; l++) { + if ((_digStateMusicMap[l].roomId == stateId)) { + break; + } + } + num = l; + + int offset = _attributes[_digStateMusicMap[num].offset]; + if (offset == 0) { + if (_attributes[_digStateMusicMap[num].atribPos] != 0) { + num = _digStateMusicMap[num].stateIndex3; + } else { + num = _digStateMusicMap[num].stateIndex1; + } + } else { + int stateIndex2 = _digStateMusicMap[num].stateIndex2; + if (stateIndex2 == 0) { + num = _digStateMusicMap[num].stateIndex1 + offset; + } else { + num = stateIndex2; + } + } + } + + debug(5, "Set music state: %s, %s", _digStateMusicTable[num].name, _digStateMusicTable[num].filename); + + if (_curMusicState == num) + return; + + if (_curMusicSeq == 0) { + if (num == 0) + playDigMusic(NULL, &_digStateMusicTable[0], num, false); + else + playDigMusic(_digStateMusicTable[num].name, &_digStateMusicTable[num], num, false); + } + + _curMusicState = num; +} + +void IMuseDigital::setDigMusicSequence(int seqId) { + int l, num = -1; + + if (seqId == 0) + seqId = 2000; + + for (l = 0; _digSeqMusicTable[l].soundId != -1; l++) { + if ((_digSeqMusicTable[l].soundId == seqId)) { + debug(5, "Set music sequence: %s, %s", _digSeqMusicTable[l].name, _digSeqMusicTable[l].filename); + num = l; + break; + } + } + + if (num == -1) + return; + + if (_curMusicSeq == num) + return; + + if (num != 0) { + if (_curMusicSeq == 0) { + playDigMusic(_digSeqMusicTable[num].name, &_digSeqMusicTable[num], 0, true); + _nextSeqToPlay = 0; + _attributes[DIG_SEQ_OFFSET + num] = 1; + } else { + if ((_digSeqMusicTable[_curMusicSeq].opcode == 4) || (_digSeqMusicTable[_curMusicSeq].opcode == 6)) { + _nextSeqToPlay = num; + return; + } else { + playDigMusic(_digSeqMusicTable[num].name, &_digSeqMusicTable[num], 0, true); + _nextSeqToPlay = 0; + _attributes[DIG_SEQ_OFFSET + num] = 1; + } + } + } else { + if (_nextSeqToPlay != 0) { + playDigMusic(_digSeqMusicTable[_nextSeqToPlay].name, &_digSeqMusicTable[_nextSeqToPlay], 0, true); + _attributes[DIG_SEQ_OFFSET + _nextSeqToPlay] = 1; + num = _nextSeqToPlay; + _nextSeqToPlay = 0; + } else { + if (_curMusicState != 0) { + playDigMusic(_digStateMusicTable[_curMusicState].name, &_digStateMusicTable[_curMusicState], _curMusicState, true); + } else + playDigMusic(NULL, &_digStateMusicTable[0], _curMusicState, true); + num = 0; + } + } + + _curMusicSeq = num; +} + +void IMuseDigital::playDigMusic(const char *songName, const imuseDigTable *table, int atribPos, bool sequence) { + int hookId = 0; + + if (songName != NULL) { + if ((_attributes[DIG_SEQ_OFFSET + 38] != 0) && (_attributes[DIG_SEQ_OFFSET + 41] == _attributes[DIG_SEQ_OFFSET + 38])) { + if ((atribPos == 43) || (atribPos == 44)) + hookId = 3; + } + + if ((_attributes[DIG_SEQ_OFFSET + 46] != 0) && (_attributes[DIG_SEQ_OFFSET + 48] == 0)) { + if ((atribPos == 38) || (atribPos == 39)) + hookId = 3; + } + + if ((_attributes[DIG_SEQ_OFFSET + 53] != 0)) { + if ((atribPos == 50) || (atribPos == 51)) + hookId = 3; + } + + if ((atribPos != 0) && (hookId == 0)) { + if (table->atribPos != 0) + atribPos = table->atribPos; + hookId = _attributes[DIG_STATE_OFFSET + atribPos]; + if (table->hookId != 0) { + if ((hookId != 0) && (table->hookId > 1)) { + _attributes[DIG_STATE_OFFSET + atribPos] = 2; + } else { + _attributes[DIG_STATE_OFFSET + atribPos] = hookId + 1; + if (table->hookId < hookId + 1) + _attributes[DIG_STATE_OFFSET + atribPos] = 1; + } + } + } + } + + fadeOutMusic(120); + + switch(table->opcode) { + case 0: + case 5: + case 6: + break; + case 3: + case 4: + if (table->filename[0] == 0) { + return; + } + if ((!sequence) && (table->atribPos != 0) && + (table->atribPos == _digStateMusicTable[_curMusicState].atribPos)) { + startMusic(table->filename, table->soundId, 0, 127); + return; + } + startMusic(table->filename, table->soundId, hookId, 127); + break; + } +} + +void IMuseDigital::setComiMusicState(int stateId) { + int l, num = -1; + + // This happens at the beginning of Part II, but should apparently not + // do anything since the correct music is already playing. A left-over + // of some kind? + + if (stateId == 4) + return; + + if (stateId == 0) + stateId = 1000; + + for (l = 0; _comiStateMusicTable[l].soundId != -1; l++) { + if ((_comiStateMusicTable[l].soundId == stateId)) { + debug(5, "Set music state: %s, %s", _comiStateMusicTable[l].name, _comiStateMusicTable[l].filename); + num = l; + break; + } + } + assert(num != -1); + + if (_curMusicState == num) + return; + + if (_curMusicSeq == 0) { + if (num == 0) + playComiMusic(NULL, &_comiStateMusicTable[0], num, false); + else + playComiMusic(_comiStateMusicTable[num].name, &_comiStateMusicTable[num], num, false); + } + + _curMusicState = num; +} + +void IMuseDigital::setComiMusicSequence(int seqId) { + int l, num = -1; + + if (seqId == 0) + seqId = 2000; + + for (l = 0; _comiSeqMusicTable[l].soundId != -1; l++) { + if ((_comiSeqMusicTable[l].soundId == seqId)) { + debug(5, "Set music sequence: %s, %s", _comiSeqMusicTable[l].name, _comiSeqMusicTable[l].filename); + num = l; + break; + } + } + assert(num != -1); + + if (_curMusicSeq == num) + return; + + if (num != 0) { + if (_curMusicSeq == 0) { + playComiMusic(_comiSeqMusicTable[num].name, &_comiSeqMusicTable[num], 0, true); + _nextSeqToPlay = 0; + } else { + if ((_comiSeqMusicTable[_curMusicSeq].opcode == 4) || (_comiSeqMusicTable[_curMusicSeq].opcode == 6)) { + _nextSeqToPlay = num; + return; + } else { + playComiMusic(_comiSeqMusicTable[num].name, &_comiSeqMusicTable[num], 0, true); + _nextSeqToPlay = 0; + } + } + } else { + if (_nextSeqToPlay != 0) { + playComiMusic(_comiSeqMusicTable[_nextSeqToPlay].name, &_comiSeqMusicTable[_nextSeqToPlay], 0, true); + num = _nextSeqToPlay; + _nextSeqToPlay = 0; + } else { + if (_curMusicState != 0) { + playComiMusic(_comiStateMusicTable[_curMusicState].name, &_comiStateMusicTable[_curMusicState], _curMusicState, true); + } else + playComiMusic(NULL, &_comiStateMusicTable[0], _curMusicState, true); + num = 0; + } + } + + _curMusicSeq = num; +} + +void IMuseDigital::playComiMusic(const char *songName, const imuseComiTable *table, int atribPos, bool sequence) { + int hookId = 0; + + if ((songName != NULL) && (atribPos != 0)) { + if (table->atribPos != 0) + atribPos = table->atribPos; + hookId = _attributes[COMI_STATE_OFFSET + atribPos]; + if (table->hookId != 0) { + if ((hookId != 0) && (table->hookId > 1)) { + _attributes[COMI_STATE_OFFSET + atribPos] = 2; + } else { + _attributes[COMI_STATE_OFFSET + atribPos] = hookId + 1; + if (table->hookId < hookId + 1) + _attributes[COMI_STATE_OFFSET + atribPos] = 1; + } + } + } + + switch(table->opcode) { + case 0: + case 8: + case 9: + fadeOutMusic(120); + break; + case 1: + if (table->filename[0] == 0) { + fadeOutMusic(120); + return; + } + fadeOutMusic(120); + startMusic(table->filename, table->soundId, 0, 1); + setFade(table->soundId, 127, 120); + break; + case 2: + if (table->filename[0] == 0) { + fadeOutMusic(60); + return; + } + fadeOutMusic(table->fadeOut60TicksDelay); + startMusic(table->filename, table->soundId, table->hookId, 127); + break; + case 3: + case 4: + case 12: + if (table->filename[0] == 0) { + fadeOutMusic(60); + return; + } + fadeOutMusic(table->fadeOut60TicksDelay); + if ((!sequence) && (table->atribPos != 0) && + (table->atribPos == _comiStateMusicTable[_curMusicState].atribPos)) { + startMusic(table->filename, table->soundId, 0, 127); + return; + } + if (table->opcode == 12) { + startMusic(table->filename, table->soundId, table->hookId, 127); + } else { + startMusic(table->filename, table->soundId, hookId, 127); + } + break; + } +} + +void IMuseDigital::setFtMusicState(int stateId) { + if (stateId > 48) + return; + + debug(5, "State music: %s, %s", _ftStateMusicTable[stateId].name, _ftStateMusicTable[stateId].audioName); + + if (_curMusicState == stateId) + return; + + if (_curMusicSeq == 0) { + if (stateId == 0) + playFtMusic(NULL, 0, 0); + else + playFtMusic(_ftStateMusicTable[stateId].audioName, _ftStateMusicTable[stateId].opcode, _ftStateMusicTable[stateId].volume); + } + + _curMusicState = stateId; +} + +void IMuseDigital::setFtMusicSequence(int seqId) { + if (seqId > 52) + return; + + debug(5, "Sequence music: %s", _ftSeqNames[seqId].name); + + if (_curMusicSeq == seqId) + return; + + if (seqId == 0) { + if (_curMusicState == 0) + playFtMusic(NULL, 0, 0); + else { + playFtMusic(_ftStateMusicTable[_curMusicState].audioName, _ftStateMusicTable[_curMusicState].opcode, _ftStateMusicTable[_curMusicState].volume); + } + } else { + int seq = (seqId - 1) * 4; + playFtMusic(_ftSeqMusicTable[seq].audioName, _ftSeqMusicTable[seq].opcode, _ftSeqMusicTable[seq].volume); + } + + _curMusicSeq = seqId; + _curMusicCue = 0; +} + +void IMuseDigital::setFtMusicCuePoint(int cueId) { + if (cueId > 3) + return; + + debug(5, "Cue point sequence: %d", cueId); + + if (_curMusicSeq == 0) + return; + + if (_curMusicCue == cueId) + return; + + if (cueId == 0) + playFtMusic(NULL, 0, 0); + else { + int seq = ((_curMusicSeq - 1) * 4) + cueId; + playFtMusic(_ftSeqMusicTable[seq].audioName, _ftSeqMusicTable[seq].opcode, _ftSeqMusicTable[seq].volume); + } + + _curMusicCue = cueId; +} + +void IMuseDigital::setAudioNames(int32 num, char *names) { + free(_audioNames); + _numAudioNames = num; + _audioNames = names; +} + +int IMuseDigital::getSoundIdByName(const char *soundName) { + if (soundName && soundName[0] != 0) { + for (int r = 0; r < _numAudioNames; r++) { + if (strcmp(soundName, &_audioNames[r * 9]) == 0) { + return r; + } + } + } + + return -1; +} + +void IMuseDigital::playFtMusic(const char *songName, int opcode, int volume) { + fadeOutMusic(200); + + switch(opcode) { + case 0: + case 4: + break; + case 1: + case 2: + case 3: + { + int soundId = getSoundIdByName(songName); + if (soundId != -1) { + startMusic(soundId, volume); + } + } + break; + } +} + + +} // End of namespace Scumm diff --git a/engines/scumm/imuse_digi/dimuse_script.cpp b/engines/scumm/imuse_digi/dimuse_script.cpp new file mode 100644 index 0000000000..83deb91e54 --- /dev/null +++ b/engines/scumm/imuse_digi/dimuse_script.cpp @@ -0,0 +1,409 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/stdafx.h" +#include "common/system.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 cmd, int b, int c, int d, int e, int f, int g, int h) { + 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: + error("IMuseDigital::doCommand DEFAULT command %d", cmd); + } +} + +void IMuseDigital::flushTracks() { + Common::StackLock lock(_mutex, "IMuseDigital::flushTracks()"); + debug(5, "flushTracks()"); + for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) { + Track *track = _track[l]; + if (track->used && (track->readyToRemove || + (_vm->_insaneRunning && track->toBeRemoved))) { // INSANE hack for sync timer mode + if (track->stream) { + if (!track->stream->endOfStream()) { + track->stream->finish(); + } + if (track->stream->endOfStream() + || _vm->_mixer->isPaused() // hack for paused Mixer + || _vm->_insaneRunning) { // INSANE hack for sync timer mode + _vm->_mixer->stopHandle(track->handle); + delete track->stream; + track->stream = NULL; + _sound->closeSound(track->soundHandle); + track->soundHandle = NULL; + track->used = false; + } + } else if (track->stream2) { + _vm->_mixer->stopHandle(track->handle); + delete track->stream2; + track->stream2 = NULL; + track->used = false; + } + } + } +} + +void IMuseDigital::refreshScripts() { + Common::StackLock lock(_mutex, "IMuseDigital::refreshScripts()"); + debug(5, "refreshScripts()"); + bool found = false; + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + Track *track = _track[l]; + if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { + found = true; + } + } + + if (!found && (_curMusicSeq != 0)) { + debug(5, "refreshScripts() Start Sequence"); + parseScriptCmds(0x1001, 0, 0, 0, 0, 0, 0, 0); + } +} + +void IMuseDigital::startVoice(int soundId, AudioStream *input) { + debug(5, "startVoiceStream(%d)", soundId); + startSound(soundId, "", 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, "", 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, "", 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) { + Common::StackLock lock(_mutex, "IMuseDigital::getLipSync()"); + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + Track *track = _track[l]; + if ((track->soundId == soundId) && track->used && !track->toBeRemoved) { + _sound->getSyncSizeAndPtrById(track->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) { + Common::StackLock lock(_mutex, "IMuseDigital::getPosInMs()"); + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + Track *track = _track[l]; + if ((track->soundId == soundId) && track->used && !track->toBeRemoved) { + int32 pos = (5 * (track->dataOffset + track->regionOffset)) / (track->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++) { + Track *track = _track[l]; + if (track->soundId == sound) { + if ((track->stream2 && _vm->_mixer->isSoundHandleActive(track->handle)) || + (track->stream && track->used && !track->readyToRemove)) { + 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++) { + Track *track = _track[l]; + if ((track->soundId == soundId) && track->used && !track->toBeRemoved) { + track->toBeRemoved = true; + } + } +} + +int32 IMuseDigital::getCurMusicPosInMs() { + Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicPosInMs()"); + int soundId = -1; + + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + Track *track = _track[l]; + if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { + soundId = track->soundId; + } + } + + int32 msPos = getPosInMs(soundId); + debug(5, "IMuseDigital::getCurMusicPosInMs(%d) = %d", soundId, msPos); + return msPos; +} + +int32 IMuseDigital::getCurVoiceLipSyncWidth() { + Common::StackLock lock(_mutex, "IMuseDigital::getCurVoiceLipSyncWidth()"); + 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++) { + Track *track = _track[l]; + if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { + soundId = track->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++) { + Track *track = _track[l]; + if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { + soundId = track->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() { + debug(5, "IMuseDigital::stopAllSounds"); + + for (;;) { + bool foundNotRemoved = false; + for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) { + Track *track = _track[l]; + if (track->used) { + track->toBeRemoved = true; + foundNotRemoved = true; + } + } + if (!foundNotRemoved) + break; + flushTracks(); + _vm->_system->delayMillis(50); +#if defined(_WIN32_WCE) || defined (PALMOS_MODE) || defined(__SYMBIAN32__) + _vm->parseEvents(); // timers are events, we need to consume them +#endif + } +} + +void IMuseDigital::pause(bool p) { + _pause = p; +} + +} // End of namespace Scumm diff --git a/engines/scumm/imuse_digi/dimuse_sndmgr.cpp b/engines/scumm/imuse_digi/dimuse_sndmgr.cpp new file mode 100644 index 0000000000..7d3d06f16e --- /dev/null +++ b/engines/scumm/imuse_digi/dimuse_sndmgr.cpp @@ -0,0 +1,625 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "common/util.h" + +#include "sound/voc.h" +#include "sound/vorbis.h" +#include "sound/mp3.h" + +#include "scumm/scumm.h" +#include "scumm/util.h" +#include "scumm/imuse_digi/dimuse.h" +#include "scumm/imuse_digi/dimuse_sndmgr.h" +#include "scumm/imuse_digi/dimuse_bndmgr.h" + +namespace Scumm { + +ImuseDigiSndMgr::ImuseDigiSndMgr(ScummEngine *scumm) { + for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) { + memset(&_sounds[l], 0, sizeof(soundStruct)); + } + _vm = scumm; + _disk = 0; + _cacheBundleDir = new BundleDirCache(); + BundleCodecs::initializeImcTables(); +} + +ImuseDigiSndMgr::~ImuseDigiSndMgr() { + for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) { + closeSound(&_sounds[l]); + } + + delete _cacheBundleDir; +} + +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::prepareSoundFromRMAP(Common::File *file, soundStruct *sound, int32 offset, int32 size) { + int l; + + file->seek(offset, SEEK_SET); + uint32 tag = file->readUint32BE(); + assert(tag == 'RMAP'); + int32 version = file->readUint32BE(); + if (version != 2) { + error("ImuseDigiSndMgr::prepareSoundFromRMAP: Wrong version number, expected 2, but it's: %d.", version); + } + sound->bits = file->readUint32BE(); + sound->freq = file->readUint32BE(); + sound->channels = file->readUint32BE(); + sound->numRegions = file->readUint32BE(); + sound->numJumps = file->readUint32BE(); + sound->numSyncs = file->readUint32BE(); + sound->region = (_region *)malloc(sizeof(_region) * sound->numRegions); + sound->jump = (_jump *)malloc(sizeof(_jump) * sound->numJumps); + sound->sync = (_sync *)malloc(sizeof(_sync) * sound->numSyncs); + for (l = 0; l < sound->numRegions; l++) { + sound->region[l].offset = file->readUint32BE(); + sound->region[l].length = file->readUint32BE(); + } + for (l = 0; l < sound->numJumps; l++) { + sound->jump[l].offset = file->readUint32BE(); + sound->jump[l].dest = file->readUint32BE(); + sound->jump[l].hookId = file->readUint32BE(); + sound->jump[l].fadeDelay = file->readUint32BE(); + } + for (l = 0; l < sound->numSyncs; l++) { + sound->sync[l].size = file->readUint32BE(); + sound->sync[l].ptr = (byte *)malloc(sound->sync[l].size); + file->read(sound->sync[l].ptr, sound->sync[l].size); + } +} + +void ImuseDigiSndMgr::prepareSound(byte *ptr, soundStruct *sound) { + if (READ_UINT32(ptr) == MKID('Crea')) { + bool quit = false; + int len; + + int32 offset = READ_LE_UINT16(ptr + 20); + int16 code = READ_LE_UINT16(ptr + 24); + + sound->region = (_region *)malloc(sizeof(_region) * 70); + sound->jump = (_jump *)malloc(sizeof(_jump)); + sound->resPtr = ptr; + sound->bits = 8; + sound->channels = 1; + + while (!quit) { + len = READ_LE_UINT32(ptr + offset); + code = len & 0xFF; + if ((code != 0) && (code != 1) && (code != 6) && (code != 7)) { + // try again with 2 bytes forward (workaround for some FT sounds (ex.362, 363) + offset += 2; + len = READ_LE_UINT32(ptr + offset); + code = len & 0xFF; + if ((code != 0) && (code != 1) && (code != 6) && (code != 7)) { + error("Invalid code in VOC file : %d", code); + } + } + offset += 4; + len >>= 8; + switch(code) { + case 0: + quit = true; + break; + case 1: + { + int time_constant = ptr[offset]; + offset += 2; + len -= 2; + sound->freq = getSampleRateFromVOCRate(time_constant); + sound->region[sound->numRegions].offset = offset; + sound->region[sound->numRegions].length = len; + sound->numRegions++; + } + break; + case 6: // begin of loop + sound->jump[0].dest = offset + 8; + sound->jump[0].hookId = 0; + sound->jump[0].fadeDelay = 0; + break; + case 7: // end of loop + sound->jump[0].offset = offset - 4; + sound->numJumps++; + sound->region[sound->numRegions].offset = offset - 4; + sound->region[sound->numRegions].length = 0; + sound->numRegions++; + break; + default: + error("Invalid code in VOC file : %d", code); + quit = true; + break; + } + 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; + + sound->numRegions = 0; + sound->numJumps = 0; + sound->numSyncs = 0; + countElements(ptr, sound->numRegions, sound->numJumps, sound->numSyncs); + sound->region = (_region *)malloc(sizeof(_region) * sound->numRegions); + sound->jump = (_jump *)malloc(sizeof(_jump) * sound->numJumps); + sound->sync = (_sync *)malloc(sizeof(_sync) * sound->numSyncs); + + do { + tag = READ_BE_UINT32(ptr); ptr += 4; + switch(tag) { + case MKID_BE('FRMT'): + ptr += 12; + sound->bits = READ_BE_UINT32(ptr); ptr += 4; + sound->freq = READ_BE_UINT32(ptr); ptr += 4; + sound->channels = READ_BE_UINT32(ptr); ptr += 4; + break; + case MKID_BE('TEXT'): + case MKID_BE('STOP'): + size = READ_BE_UINT32(ptr); ptr += size + 4; + break; + case MKID_BE('REGN'): + ptr += 4; + sound->region[curIndexRegion].offset = READ_BE_UINT32(ptr); ptr += 4; + sound->region[curIndexRegion].length = READ_BE_UINT32(ptr); ptr += 4; + curIndexRegion++; + break; + case MKID_BE('JUMP'): + ptr += 4; + sound->jump[curIndexJump].offset = READ_BE_UINT32(ptr); ptr += 4; + sound->jump[curIndexJump].dest = READ_BE_UINT32(ptr); ptr += 4; + sound->jump[curIndexJump].hookId = READ_BE_UINT32(ptr); ptr += 4; + sound->jump[curIndexJump].fadeDelay = READ_BE_UINT32(ptr); ptr += 4; + curIndexJump++; + break; + case MKID_BE('SYNC'): + size = READ_BE_UINT32(ptr); ptr += 4; + sound->sync[curIndexSync].size = size; + sound->sync[curIndexSync].ptr = (byte *)malloc(size); + memcpy(sound->sync[curIndexSync].ptr, ptr, size); + curIndexSync++; + ptr += size; + break; + case MKID_BE('DATA'): + ptr += 4; + break; + default: + error("ImuseDigiSndMgr::prepareSound(%d/%s) Unknown sfx header '%s'", sound->soundId, sound->name, tag2str(tag)); + } + } while (tag != MKID_BE('DATA')); + sound->offsetData = ptr - s_ptr; + } else { + error("ImuseDigiSndMgr::prepareSound(): Unknown sound format"); + } +} + +ImuseDigiSndMgr::soundStruct *ImuseDigiSndMgr::allocSlot() { + for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) { + if (!_sounds[l].inUse) { + _sounds[l].inUse = true; + return &_sounds[l]; + } + } + + return NULL; +} + +bool ImuseDigiSndMgr::openMusicBundle(soundStruct *sound, int disk) { + bool result = false; + + sound->bundle = new BundleMgr(_cacheBundleDir); + if (_vm->_gameId == GID_CMI) { + if (_vm->_features & GF_DEMO) { + result = sound->bundle->open("music.bun", sound->compressed); + } else { + char musicfile[20]; + if (disk == -1) + disk = _vm->VAR(_vm->VAR_CURRENTDISK); + sprintf(musicfile, "musdisk%d.bun", disk); +// if (_disk != _vm->VAR(_vm->VAR_CURRENTDISK)) { +// _vm->_imuseDigital->parseScriptCmds(0x1000, 0, 0, 0, 0, 0, 0, 0); +// _vm->_imuseDigital->parseScriptCmds(0x2000, 0, 0, 0, 0, 0, 0, 0); +// _vm->_imuseDigital->stopAllSounds(); +// sound->bundle->closeFile(); +// } + + result = sound->bundle->open(musicfile, sound->compressed, true); + + // FIXME: Shouldn't we only set _disk if result == true? + _disk = (byte)_vm->VAR(_vm->VAR_CURRENTDISK); + } + } else if (_vm->_gameId == GID_DIG) + result = sound->bundle->open("digmusic.bun", sound->compressed, true); + else + error("ImuseDigiSndMgr::openMusicBundle() Don't know which bundle file to load"); + + _vm->VAR(_vm->VAR_MUSIC_BUNDLE_LOADED) = result ? 1 : 0; + + return result; +} + +bool ImuseDigiSndMgr::openVoiceBundle(soundStruct *sound, int disk) { + bool result = false; + + sound->bundle = new BundleMgr(_cacheBundleDir); + if (_vm->_gameId == GID_CMI) { + if (_vm->_features & GF_DEMO) { + result = sound->bundle->open("voice.bun", sound->compressed); + } else { + char voxfile[20]; + if (disk == -1) + disk = _vm->VAR(_vm->VAR_CURRENTDISK); + sprintf(voxfile, "voxdisk%d.bun", disk); +// if (_disk != _vm->VAR(_vm->VAR_CURRENTDISK)) { +// _vm->_imuseDigital->parseScriptCmds(0x1000, 0, 0, 0, 0, 0, 0, 0); +// _vm->_imuseDigital->parseScriptCmds(0x2000, 0, 0, 0, 0, 0, 0, 0); +// _vm->_imuseDigital->stopAllSounds(); +// sound->bundle->closeFile(); +// } + + result = sound->bundle->open(voxfile, sound->compressed); + + // FIXME: Shouldn't we only set _disk if result == true? + _disk = (byte)_vm->VAR(_vm->VAR_CURRENTDISK); + } + } else if (_vm->_gameId == GID_DIG) + result = sound->bundle->open("digvoice.bun", sound->compressed); + else + error("ImuseDigiSndMgr::openVoiceBundle() Don't know which bundle file to load"); + + _vm->VAR(_vm->VAR_VOICE_BUNDLE_LOADED) = result ? 1 : 0; + + return result; +} + +ImuseDigiSndMgr::soundStruct *ImuseDigiSndMgr::openSound(int32 soundId, const char *soundName, int soundType, int volGroupId, int disk) { + assert(soundId >= 0); + assert(soundType); + + soundStruct *sound = allocSlot(); + if (!sound) { + error("ImuseDigiSndMgr::openSound() can't alloc free sound slot"); + } + + const bool header_outside = ((_vm->_gameId == GID_CMI) && !(_vm->_features & GF_DEMO)); + bool result = false; + byte *ptr = NULL; + + switch (soundType) { + case IMUSE_RESOURCE: + assert(soundName[0] == 0); // Paranoia check + + _vm->ensureResourceLoaded(rtSound, soundId); + _vm->res.lock(rtSound, soundId); + ptr = _vm->getResourceAddress(rtSound, soundId); + if (ptr == NULL) { + closeSound(sound); + return NULL; + } + sound->resPtr = ptr; + break; + case IMUSE_BUNDLE: + if (volGroupId == IMUSE_VOLGRP_VOICE) + result = openVoiceBundle(sound, disk); + else if (volGroupId == IMUSE_VOLGRP_MUSIC) + result = openMusicBundle(sound, disk); + else + error("ImuseDigiSndMgr::openSound() Don't know how load sound: %d", soundId); + if (!result) { + closeSound(sound); + return NULL; + } + if (sound->compressed) { + char fileName[24]; + int32 offset = 0, size = 0; + sprintf(fileName, "%s.map", soundName); + Common::File *rmapFile = sound->bundle->getFile(fileName, offset, size); + if (!rmapFile) { + closeSound(sound); + return NULL; + } + prepareSoundFromRMAP(rmapFile, sound, offset, size); + strcpy(sound->name, soundName); + sound->soundId = soundId; + sound->type = soundType; + sound->volGroupId = volGroupId; + sound->disk = _disk; + return sound; + } else if (soundName[0] == 0) { + if (sound->bundle->decompressSampleByIndex(soundId, 0, 0x2000, &ptr, 0, header_outside) == 0 || ptr == NULL) { + closeSound(sound); + return NULL; + } + } else { + if (sound->bundle->decompressSampleByName(soundName, 0, 0x2000, &ptr, header_outside) == 0 || ptr == NULL) { + closeSound(sound); + return NULL; + } + } + sound->resPtr = 0; + break; + default: + error("ImuseDigiSndMgr::openSound() Unknown soundType %d (trying to load sound %d)", soundType, soundId); + } + + strcpy(sound->name, soundName); + sound->soundId = soundId; + sound->type = soundType; + sound->volGroupId = volGroupId; + sound->disk = _disk; + prepareSound(ptr, sound); + if ((soundType == IMUSE_BUNDLE) && !sound->compressed) { + free(ptr); + } + return sound; +} + +void ImuseDigiSndMgr::closeSound(soundStruct *soundHandle) { + assert(checkForProperHandle(soundHandle)); + + if (soundHandle->resPtr) { + bool found = false; + for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) { + if ((_sounds[l].soundId == soundHandle->soundId) && (&_sounds[l] != soundHandle)) + found = true; + } + if (!found) + _vm->res.unlock(rtSound, soundHandle->soundId); + } + + if (soundHandle->compressedStream) + delete soundHandle->compressedStream; + + delete soundHandle->bundle; + + for (int r = 0; r < soundHandle->numSyncs; r++) + free(soundHandle->sync[r].ptr); + free(soundHandle->region); + free(soundHandle->jump); + free(soundHandle->sync); + memset(soundHandle, 0, sizeof(soundStruct)); +} + +ImuseDigiSndMgr::soundStruct *ImuseDigiSndMgr::cloneSound(soundStruct *soundHandle) { + assert(checkForProperHandle(soundHandle)); + + return openSound(soundHandle->soundId, soundHandle->name, soundHandle->type, soundHandle->volGroupId, soundHandle->disk); +} + +bool ImuseDigiSndMgr::checkForProperHandle(soundStruct *soundHandle) { + if (!soundHandle) + return false; + for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) { + if (soundHandle == &_sounds[l]) + return true; + } + return false; +} + +bool ImuseDigiSndMgr::isCompressed(soundStruct *soundHandle) { + assert(checkForProperHandle(soundHandle)); + return soundHandle->compressed; +} + +int ImuseDigiSndMgr::getFreq(soundStruct *soundHandle) { + assert(checkForProperHandle(soundHandle)); + return soundHandle->freq; +} + +int ImuseDigiSndMgr::getBits(soundStruct *soundHandle) { + assert(checkForProperHandle(soundHandle)); + return soundHandle->bits; +} + +int ImuseDigiSndMgr::getChannels(soundStruct *soundHandle) { + assert(checkForProperHandle(soundHandle)); + return soundHandle->channels; +} + +bool ImuseDigiSndMgr::isEndOfRegion(soundStruct *soundHandle, int region) { + assert(checkForProperHandle(soundHandle)); + assert(region >= 0 && region < soundHandle->numRegions); + return soundHandle->endFlag; +} + +int ImuseDigiSndMgr::getNumRegions(soundStruct *soundHandle) { + assert(checkForProperHandle(soundHandle)); + return soundHandle->numRegions; +} + +int ImuseDigiSndMgr::getNumJumps(soundStruct *soundHandle) { + assert(checkForProperHandle(soundHandle)); + return soundHandle->numJumps; +} + +int ImuseDigiSndMgr::getRegionOffset(soundStruct *soundHandle, int region) { + debug(5, "getRegionOffset() region:%d", region); + assert(checkForProperHandle(soundHandle)); + assert(region >= 0 && region < soundHandle->numRegions); + return soundHandle->region[region].offset; +} + +int ImuseDigiSndMgr::getJumpIdByRegionAndHookId(soundStruct *soundHandle, int region, int hookId) { + debug(5, "getJumpIdByRegionAndHookId() region:%d, hookId:%d", region, hookId); + assert(checkForProperHandle(soundHandle)); + assert(region >= 0 && region < soundHandle->numRegions); + int32 offset = soundHandle->region[region].offset; + for (int l = 0; l < soundHandle->numJumps; l++) { + if (offset == soundHandle->jump[l].offset) { + if (soundHandle->jump[l].hookId == hookId) + return l; + } + } + + return -1; +} + +void ImuseDigiSndMgr::getSyncSizeAndPtrById(soundStruct *soundHandle, int number, int32 &sync_size, byte **sync_ptr) { + assert(checkForProperHandle(soundHandle)); + assert(number >= 0); + if (number < soundHandle->numSyncs) { + sync_size = soundHandle->sync[number].size; + *sync_ptr = soundHandle->sync[number].ptr; + } else { + sync_size = 0; + *sync_ptr = NULL; + } +} + +int ImuseDigiSndMgr::getRegionIdByJumpId(soundStruct *soundHandle, int jumpId) { + debug(5, "getRegionIdByJumpId() jumpId:%d", jumpId); + assert(checkForProperHandle(soundHandle)); + assert(jumpId >= 0 && jumpId < soundHandle->numJumps); + int32 dest = soundHandle->jump[jumpId].dest; + for (int l = 0; l < soundHandle->numRegions; l++) { + if (dest == soundHandle->region[l].offset) { + return l; + } + } + + return -1; +} + +int ImuseDigiSndMgr::getJumpHookId(soundStruct *soundHandle, int number) { + debug(5, "getJumpHookId() number:%d", number); + assert(checkForProperHandle(soundHandle)); + assert(number >= 0 && number < soundHandle->numJumps); + return soundHandle->jump[number].hookId; +} + +int ImuseDigiSndMgr::getJumpFade(soundStruct *soundHandle, int number) { + debug(5, "getJumpFade() number:%d", number); + assert(checkForProperHandle(soundHandle)); + assert(number >= 0 && number < soundHandle->numJumps); + return soundHandle->jump[number].fadeDelay; +} + +int32 ImuseDigiSndMgr::getDataFromRegion(soundStruct *soundHandle, int region, byte **buf, int32 offset, int32 size) { + debug(5, "getDataFromRegion() region:%d, offset:%d, size:%d, numRegions:%d", region, offset, size, soundHandle->numRegions); + assert(checkForProperHandle(soundHandle)); + assert(buf && offset >= 0 && size >= 0); + assert(region >= 0 && region < soundHandle->numRegions); + + int32 region_offset = soundHandle->region[region].offset; + int32 region_length = soundHandle->region[region].length; + int32 offset_data = soundHandle->offsetData; + int32 start = region_offset - offset_data; + + if (offset + size + offset_data > region_length) { + size = region_length - offset; + soundHandle->endFlag = true; + } else { + soundHandle->endFlag = false; + } + + int header_size = soundHandle->offsetData; + bool header_outside = ((_vm->_gameId == GID_CMI) && !(_vm->_features & GF_DEMO)); + if ((soundHandle->bundle) && (!soundHandle->compressed)) { + 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); + } else if ((soundHandle->bundle) && (soundHandle->compressed)) { + *buf = (byte *)malloc(size); + char fileName[24]; + sprintf(fileName, "%s_reg%03d", soundHandle->name, region); + if (scumm_stricmp(fileName, soundHandle->lastFileName) != 0) { + int32 offs = 0, len = 0; + Common::File *cmpFile; + bool oggMode = false; + sprintf(fileName, "%s_reg%03d.mp3", soundHandle->name, region); + cmpFile = soundHandle->bundle->getFile(fileName, offs, len); +#ifndef USE_MAD + if (len) + error("Mad library compiled support needed!"); +#endif + if (!len) { + sprintf(fileName, "%s_reg%03d.ogg", soundHandle->name, region); + cmpFile = soundHandle->bundle->getFile(fileName, offs, len); +#ifndef USE_VORBIS + if (len) + error("Vorbis library compiled support needed!"); +#endif + assert(len); + oggMode = true; + } + if (!soundHandle->compressedStream) { +#ifdef USE_VORBIS + if (oggMode) + soundHandle->compressedStream = makeVorbisStream(cmpFile, len); +#endif +#ifdef USE_MAD + if (!oggMode) + soundHandle->compressedStream = makeMP3Stream(cmpFile, len); +#endif + assert(soundHandle->compressedStream); + } + strcpy(soundHandle->lastFileName, fileName); + } + size = soundHandle->compressedStream->readBuffer((int16 *)*buf, size / 2) * 2; + if (soundHandle->compressedStream->endOfData()) { + delete soundHandle->compressedStream; + soundHandle->compressedStream = NULL; + soundHandle->lastFileName[0] = 0; + } + } + + return size; +} + +} // End of namespace Scumm diff --git a/engines/scumm/imuse_digi/dimuse_sndmgr.h b/engines/scumm/imuse_digi/dimuse_sndmgr.h new file mode 100644 index 0000000000..5844fa0c1b --- /dev/null +++ b/engines/scumm/imuse_digi/dimuse_sndmgr.h @@ -0,0 +1,139 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef IMUSE_DIGI_SNDMGR_H +#define IMUSE_DIGI_SNDMGR_H + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "sound/audiostream.h" +#include "scumm/imuse_digi/dimuse_bndmgr.h" + +namespace Scumm { + +class ScummEngine; +class BundleMgr; + +class ImuseDigiSndMgr { +public: + +#define MAX_IMUSE_SOUNDS 16 + +#define IMUSE_RESOURCE 1 +#define IMUSE_BUNDLE 2 + +#define IMUSE_VOLGRP_VOICE 1 +#define IMUSE_VOLGRP_SFX 2 +#define IMUSE_VOLGRP_MUSIC 3 + +private: + struct _region { + int32 offset; // offset of region + int32 length; // lenght of region + }; + + struct _jump { + int32 offset; // jump offset position + int32 dest; // jump to dest position + byte hookId; // id of hook + int16 fadeDelay; // fade delay in ms + }; + + struct _sync { + int32 size; // size of sync + byte *ptr; // pointer to sync + }; + +public: + + struct soundStruct { + uint16 freq; // frequency + byte channels; // stereo or mono + byte bits; // 8, 12, 16 + 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; + int32 offsetData; + byte *resPtr; + char name[15]; + int16 soundId; + BundleMgr *bundle; + int type; + int volGroupId; + int disk; + AudioStream *compressedStream; + bool compressed; + char lastFileName[24]; + }; + +private: + + soundStruct _sounds[MAX_IMUSE_SOUNDS]; + + bool checkForProperHandle(soundStruct *soundHandle); + soundStruct *allocSlot(); + void prepareSound(byte *ptr, soundStruct *sound); + void prepareSoundFromRMAP(Common::File *file, soundStruct *sound, int32 offset, int32 size); + + ScummEngine *_vm; + byte _disk; + BundleDirCache *_cacheBundleDir; + + bool openMusicBundle(soundStruct *sound, int disk); + bool openVoiceBundle(soundStruct *sound, int disk); + + 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 volGroupId, int disk); + void closeSound(soundStruct *soundHandle); + soundStruct *cloneSound(soundStruct *soundHandle); + + bool isCompressed(soundStruct *soundHandle); + int getFreq(soundStruct *soundHandle); + int getBits(soundStruct *soundHandle); + int getChannels(soundStruct *soundHandle); + bool isEndOfRegion(soundStruct *soundHandle, int region); + int getNumRegions(soundStruct *soundHandle); + int getNumJumps(soundStruct *soundHandle); + int getRegionOffset(soundStruct *soundHandle, int region); + int getJumpIdByRegionAndHookId(soundStruct *soundHandle, int region, int hookId); + int getRegionIdByJumpId(soundStruct *soundHandle, int jumpId); + int getJumpHookId(soundStruct *soundHandle, int number); + int getJumpFade(soundStruct *soundHandle, int number); + void getSyncSizeAndPtrById(soundStruct *soundHandle, int number, int32 &sync_size, byte **sync_ptr); + + int32 getDataFromRegion(soundStruct *soundHandle, int region, byte **buf, int32 offset, int32 size); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/imuse_digi/dimuse_tables.cpp b/engines/scumm/imuse_digi/dimuse_tables.cpp new file mode 100644 index 0000000000..f4fd25a160 --- /dev/null +++ b/engines/scumm/imuse_digi/dimuse_tables.cpp @@ -0,0 +1,881 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/stdafx.h" +#include "scumm/imuse_digi/dimuse.h" + +namespace Scumm { + +#ifdef PALMOS_68K +const imuseRoomMap *_digStateMusicMap; +const imuseDigTable *_digStateMusicTable; +const imuseDigTable *_digSeqMusicTable; +const imuseComiTable *_comiStateMusicTable; +const imuseComiTable *_comiSeqMusicTable; +const imuseFtStateTable *_ftStateMusicTable; +const imuseFtSeqTable *_ftSeqMusicTable; +const imuseFtNames *_ftSeqNames; +#else +const imuseRoomMap _digStateMusicMap[] = { + {0, 0, 0, 0, 0, 0 }, + {1, 0, 0, 0, 0, 0 }, + {2, 2, 0, 0, 0, 0 }, + {4, 3, 0, 0, 0, 0 }, + {5, 3, 0, 0, 0, 0 }, + {6, 3, 0, 0, 0, 0 }, + {7, 3, 0, 0, 0, 0 }, + {8, 4, 0, 0, 0, 0 }, + {9, 5, 0, 0, 0, 0 }, + {10, 4, 0, 0, 0, 0 }, + {12, 5, 0, 0, 0, 0 }, + {14, 5, 0, 0, 0, 0 }, + {15, 6, 29, 7, 0, 0 }, + {16, 8, 0, 0, 0, 0 }, + {17, 1, 0, 0, 0, 0 }, + {18, 9, 0, 0, 0, 0 }, + {19, 9, 0, 0, 0, 0 }, + {20, 6, 0, 0, 0, 0 }, + {21, 6, 0, 0, 0, 0 }, + {22, 44, 0, 0, 0, 0 }, + {23, 10, 7, 0, 0, 0 }, + {24, 26, 0, 0, 0, 0 }, + {25, 17, 0, 0, 0, 0 }, + {26, 17, 0, 0, 0, 0 }, + {27, 18, 0, 0, 0, 0 }, + {28, 1, 0, 0, 0, 0 }, + {29, 20, 0, 0, 0, 0 }, + {30, 22, 0, 0, 0, 0 }, + {31, 23, 0, 0, 0, 0 }, + {32, 22, 0, 0, 0, 0 }, + {33, 26, 0, 0, 0, 0 }, + {34, 24, 0, 0, 0, 0 }, + {35, 1, 0, 0, 0, 0 }, + {36, 1, 0, 0, 0, 0 }, + {37, 42, 0, 0, 0, 0 }, + {38, 43, 0, 0, 0, 0 }, + {39, 44, 0, 0, 0, 0 }, + {40, 1, 0, 0, 0, 0 }, + {41, 43, 0, 0, 0, 0 }, + {42, 44, 0, 0, 0, 0 }, + {43, 43, 0, 0, 0, 0 }, + {44, 45, 117,45, 114,46}, + {47, 1, 0, 0, 0, 0 }, + {48, 43, 0, 0, 0, 0 }, + {49, 44, 0, 0, 0, 0 }, + {51, 1, 0, 0, 0, 0 }, + {53, 28, 0, 0, 0, 0 }, + {54, 28, 0, 0, 0, 0 }, + {55, 29, 0, 0, 0, 0 }, + {56, 29, 0, 0, 0, 0 }, + {57, 29, 0, 0, 0, 0 }, + {58, 31, 0, 0, 0, 0 }, + {59, 1, 0, 0, 0, 0 }, + {60, 37, 0, 0, 0, 0 }, + {61, 39, 0, 0, 0, 0 }, + {62, 38, 0, 0, 0, 0 }, + {63, 39, 0, 0, 0, 0 }, + {64, 39, 0, 0, 0, 0 }, + {65, 40, 0, 0, 0, 0 }, + {67, 40, 0, 0, 0, 0 }, + {68, 39, 0, 0, 0, 0 }, + {69, 1, 0, 0, 0, 0 }, + {70, 49, 0, 0, 0, 0 }, + {73, 50, 0, 0, 0, 0 }, + {75, 51, 0, 0, 0, 0 }, + {76, 1, 0, 0, 0, 0 }, + {77, 52, 7, 0, 0, 0 }, + {78, 63, 0, 0, 0, 0 }, + {79, 1, 0, 0, 0, 0 }, + {82, 21, 0, 0, 0, 0 }, + {85, 1, 0, 0, 0, 0 }, + {86, 0, 0, 0, 0, 0 }, + {89, 33, 6, 35, 5, 34}, + {90, 16, 0, 0, 0, 0 }, + {91, 57, 0, 0, 0, 0 }, + {88, 32, 0, 0, 0, 0 }, + {92, 25, 0, 0, 0, 0 }, + {93, 0, 0, 0, 0, 0 }, + {95, 19, 0, 0, 0, 0 }, + {80, 41, 0, 0, 0, 0 }, + {81, 48, 0, 0, 0, 0 }, + {83, 27, 0, 0, 0, 0 }, + {94, 36, 0, 0, 0, 0 }, + {40, 1, 0, 0, 0, 0 }, + {96, 13, 0, 0, 0, 0 }, + {97, 14, 0, 0, 0, 0 }, + {98, 11, 0, 0, 0, 0 }, + {99, 15, 0, 0, 0, 0 }, + {100, 17, 0, 0, 0, 0 }, + {101, 38, 0, 0, 0, 0 }, + {103, 0, 0, 0, 0, 0 }, + {104, 0, 0, 0, 0, 0 }, + {11, 44, 0, 0, 0, 0 }, + {3, 47, 0, 0, 0, 0 }, + {105, 30, 128,29, 0, 0 }, + {106, 0, 0, 0, 0, 0 }, + {107, 1, 0, 0, 0, 0 }, + {108, 1, 0, 0, 0, 0 }, + {47, 1, 0, 0, 0, 0 }, + {50, 1, 0, 0, 0, 0 }, + {52, 0, 0, 0, 0, 0 }, + {71, 1, 0, 0, 0, 0 }, + {13, 1, 0, 0, 0, 0 }, + {72, 1, 0, 0, 0, 0 }, + {46, 33, 6, 35, 5, 34}, + {74, 1, 0, 0, 0, 0 }, + {84, 1, 0, 0, 0, 0 }, + {66, 1, 0, 0, 0, 0 }, + {102, 1, 0, 0, 0, 0 }, + {109, 1, 0, 0, 0, 0 }, + {110, 2, 0, 0, 0, 0 }, + {45, 1, 0, 0, 0, 0 }, + {87, 1, 0, 0, 0, 0 }, + {111, 1, 0, 0, 0, 0 }, + {-1, 1, 0, 0, 0, 0 } +}; + +const imuseDigTable _digStateMusicTable[] = { + {0, 1000, "STATE_NULL", 0, 0, ""}, /* 00 */ + {0, 1001, "stateNoChange", 0, 0, ""}, /* 01 */ + {3, 1100, "stateAstShip", 2, 0, "ASTERO~1.IMU"}, /* 02 */ + {3, 1120, "stateAstClose", 2, 0, "ASTERO~2.IMU"}, /* 03 */ + {3, 1140, "stateAstInside", 0, 0, "ASTERO~3.IMU"}, /* 04 */ + {3, 1150, "stateAstCore", 0, 2, "ASTERO~4.IMU"}, /* 05 */ + {3, 1200, "stateCanyonClose", 0, 1, "CANYON~1.IMU"}, /* 06 */ + {3, 1205, "stateCanyonClose_m", 0, 0, "CANYON~2.IMU"}, /* 07 */ + {3, 1210, "stateCanyonOver", 0, 1, "CANYON~3.IMU"}, /* 08 */ + {3, 1220, "stateCanyonWreck", 0, 1, "CANYON~4.IMU"}, /* 09 */ + {3, 1300, "stateNexusCanyon", 10, 0, "NEXUS(~1.IMU"}, /* 10 */ + {3, 1310, "stateNexusPlan", 10, 0, "NEXUS(~1.IMU"}, /* 11 */ + {3, 1320, "stateNexusRamp", 10, 0, "NEXUS(~2.IMU"}, /* 12 */ + {3, 1330, "stateNexusMuseum", 10, 0, "NEXUS(~3.IMU"}, /* 13 */ + {3, 1340, "stateNexusMap", 10, 0, "NEXUS(~4.IMU"}, /* 14 */ + {3, 1350, "stateNexusTomb", 10, 0, "NE3706~5.IMU"}, /* 15 */ + {3, 1360, "stateNexusCath", 10, 0, "NE3305~5.IMU"}, /* 16 */ + {3, 1370, "stateNexusAirlock", 0, 0, "NE2D3A~5.IMU"}, /* 17 */ + {3, 1380, "stateNexusPowerOff", 0, 1, "NE8522~5.IMU"}, /* 18 */ + {3, 1400, "stateMuseumTramNear", 0, 1, "TRAM(M~1.IMU"}, /* 19 */ + {3, 1410, "stateMuseumTramFar", 0, 0, "TRAM(M~2.IMU"}, /* 20 */ + {3, 1420, "stateMuseumLockup", 0, 0, "MUSEUM~1.IMU"}, /* 21 */ + {3, 1433, "stateMuseumPool", 22, 1, "MUSEUM~2.IMU"}, /* 22 */ + {3, 1436, "stateMuseumSpire", 22, 2, "MUSEUM~3.IMU"}, /* 23 */ + {3, 1440, "stateMuseumMuseum", 22, 2, "MUSEUM~4.IMU"}, /* 24 */ + {3, 1450, "stateMuseumLibrary", 0, 0, "MUB575~5.IMU"}, /* 25 */ + {3, 1460, "stateMuseumCavern", 0, 0, "MUF9BE~5.IMU"}, /* 26 */ + {3, 1500, "stateTombTramNear", 0, 1, "TRAM(T~1.IMU"}, /* 27 */ + {3, 1510, "stateTombBase", 28, 2, "TOMB(A~1.IMU"}, /* 28 */ + {3, 1520, "stateTombSpire", 28, 2, "TOMB(A~2.IMU"}, /* 29 */ + {3, 1530, "stateTombCave", 28, 2, "TOMB(A~3.IMU"}, /* 30 */ + {3, 1540, "stateTombCrypt", 31, 1, "TOMB(C~1.IMU"}, /* 31 */ + {3, 1550, "stateTombGuards", 31, 1, "TOMB(C~2.IMU"}, /* 32 */ + {3, 1560, "stateTombInner", 0, 1, "TOMB(I~1.IMU"}, /* 33 */ + {3, 1570, "stateTombCreator1", 0, 0, "TOMB(C~3.IMU"}, /* 34 */ + {3, 1580, "stateTombCreator2", 0, 0, "TOMB(C~4.IMU"}, /* 35 */ + {3, 1600, "statePlanTramNear", 0, 1, "TRAM(P~1.IMU"}, /* 36 */ + {3, 1610, "statePlanTramFar", 0, 0, "TRAM(P~2.IMU"}, /* 37 */ + {3, 1620, "statePlanBase", 38, 2, "PLAN(A~1.IMU"}, /* 38 */ + {3, 1630, "statePlanSpire", 38, 2, "PLAN(A~2.IMU"}, /* 39 */ + {3, 1650, "statePlanDome", 0, 0, "PLAN(D~1.IMU"}, /* 40 */ + {3, 1700, "stateMapTramNear", 0, 1, "TRAM(M~3.IMU"}, /* 41 */ + {3, 1710, "stateMapTramFar", 0, 0, "TRAM(M~4.IMU"}, /* 42 */ + {3, 1720, "stateMapCanyon", 43, 2, "MAP(AM~1.IMU"}, /* 43 */ + {3, 1730, "stateMapExposed", 43, 2, "MAP(AM~2.IMU"}, /* 44 */ + {3, 1750, "stateMapNestEmpty", 43, 2, "MAP(AM~4.IMU"}, /* 45 */ + {3, 1760, "stateMapNestMonster", 0, 0, "MAP(MO~1.IMU"}, /* 46 */ + {3, 1770, "stateMapKlein", 0, 0, "MAP(KL~1.IMU"}, /* 47 */ + {3, 1800, "stateCathTramNear", 0, 1, "TRAM(C~1.IMU"}, /* 48 */ + {3, 1810, "stateCathTramFar", 0, 0, "TRAM(C~2.IMU"}, /* 49 */ + {3, 1820, "stateCathLab", 50, 1, "CATH(A~1.IMU"}, /* 50 */ + {3, 1830, "stateCathOutside", 50, 1, "CATH(A~2.IMU"}, /* 51 */ + {3, 1900, "stateWorldMuseum", 52, 0, "WORLD(~1.IMU"}, /* 52 */ + {3, 1901, "stateWorldPlan", 52, 0, "WORLD(~2.IMU"}, /* 53 */ + {3, 1902, "stateWorldTomb", 52, 0, "WORLD(~3.IMU"}, /* 54 */ + {3, 1903, "stateWorldMap", 52, 0, "WORLD(~4.IMU"}, /* 55 */ + {3, 1904, "stateWorldCath", 52, 0, "WO3227~5.IMU"}, /* 56 */ + {3, 1910, "stateEye1", 0, 0, "EYE1~1.IMU"}, /* 57 */ + {3, 1911, "stateEye2", 0, 0, "EYE2~1.IMU"}, /* 58 */ + {3, 1912, "stateEye3", 0, 0, "EYE3~1.IMU"}, /* 59 */ + {3, 1913, "stateEye4", 0, 0, "EYE4~1.IMU"}, /* 60 */ + {3, 1914, "stateEye5", 0, 0, "EYE5~1.IMU"}, /* 61 */ + {3, 1915, "stateEye6", 0, 0, "EYE6~1.IMU"}, /* 62 */ + {3, 1916, "stateEye7", 0, 0, "EYE7~1.IMU"}, /* 63 */ + {0, -1, "", 0, 0, ""} +}; + +const imuseDigTable _digSeqMusicTable[] = { + {0, 2000, "SEQ_NULL", 0, 0, ""}, + {0, 2005, "seqLogo", 0, 0, ""}, + {0, 2010, "seqIntro", 0, 0, ""}, + {6, 2020, "seqExplosion1b", 0, 0, ""}, + {3, 2030, "seqAstTunnel1a", 0, 0, "SEQ(AS~1.IMU"}, + {6, 2031, "seqAstTunnel2b", 0, 0, ""}, + {4, 2032, "seqAstTunnel3a", 0, 0, "SEQ(AS~2.IMU"}, + {5, 2040, "seqToPlanet1b", 0, 0, ""}, + {4, 2045, "seqArgBegin", 0, 0, "SEQ(AR~1.IMU"}, + {4, 2046, "seqArgEnd", 0, 0, "SEQ(AR~2.IMU"}, + {4, 2050, "seqWreckGhost", 0, 0, "SEQ(GH~1.IMU"}, + {4, 2060, "seqCanyonGhost", 0, 0, "SEQ(GH~2.IMU"}, + {0, 2070, "seqBrinkFall", 0, 0, ""}, + {4, 2080, "seqPanUpCanyon", 0, 0, "SEQ(PA~1.IMU"}, + {6, 2091, "seqAirlockTunnel1b", 0, 0, ""}, + {6, 2100, "seqTramToMu", 0, 0, ""}, + {6, 2101, "seqTramFromMu", 0, 0, ""}, + {6, 2102, "seqTramToTomb", 0, 0, ""}, + {6, 2103, "seqTramFromTomb", 0, 0, ""}, + {6, 2104, "seqTramToPlan", 0, 0, ""}, + {6, 2105, "seqTramFromPlan", 0, 0, ""}, + {6, 2106, "seqTramToMap", 0, 0, ""}, + {6, 2107, "seqTramFromMap", 0, 0, ""}, + {6, 2108, "seqTramToCath", 0, 0, ""}, + {6, 2109, "seqTramFromCath", 0, 0, ""}, + {0, 2110, "seqMuseumGhost", 0, 0, ""}, + {0, 2120, "seqSerpentAppears", 0, 0, ""}, + {0, 2130, "seqSerpentEats", 0, 0, ""}, + {6, 2140, "seqBrinkRes1b", 0, 0, ""}, + {4, 2141, "seqBrinkRes2a", 0, 0, "SEQ(BR~1.IMU"}, + {3, 2150, "seqLockupEntry", 0, 0, "SEQ(BR~1.IMU"}, + {0, 2160, "seqSerpentExplodes", 0, 0, ""}, + {4, 2170, "seqSwimUnderwater", 0, 0, "SEQ(DE~1.IMU"}, + {4, 2175, "seqWavesPlunge", 0, 0, "SEQ(PL~1.IMU"}, + {0, 2180, "seqCryptOpens", 0, 0, ""}, + {0, 2190, "seqGuardsFight", 0, 0, ""}, + {3, 2200, "seqCreatorRes1.1a", 0, 0, "SEQ(CR~1.IMU"}, + {6, 2201, "seqCreatorRes1.2b", 0, 0, ""}, + {6, 2210, "seqMaggieCapture1b", 0, 0, ""}, + {3, 2220, "seqStealCrystals", 0, 0, "SEQ(BR~1.IMU"}, + {0, 2230, "seqGetByMonster", 0, 0, ""}, + {6, 2240, "seqKillMonster1b", 0, 0, ""}, + {3, 2250, "seqCreatorRes2.1a", 0, 0, "SEQ(CR~2.IMU"}, + {6, 2251, "seqCreatorRes2.2b", 0, 0, ""}, + {4, 2252, "seqCreatorRes2.3a", 0, 0, "SEQ(CR~3.IMU"}, + {0, 2260, "seqMaggieInsists", 0, 0, ""}, + {0, 2270, "seqBrinkHelpCall", 0, 0, ""}, + {3, 2280, "seqBrinkCrevice1a", 0, 0, "SEQ(BR~2.IMU"}, + {3, 2281, "seqBrinkCrevice2a", 0, 0, "SEQ(BR~3.IMU"}, + {6, 2290, "seqCathAccess1b", 0, 0, ""}, + {4, 2291, "seqCathAccess2a", 0, 0, "SEQ(CA~1.IMU"}, + {3, 2300, "seqBrinkAtGenerator", 0, 0, "SEQ(BR~1.IMU"}, + {6, 2320, "seqFightBrink1b", 0, 0, ""}, + {6, 2340, "seqMaggieDies1b", 0, 0, ""}, + {6, 2346, "seqMaggieRes1b", 0, 0, ""}, + {4, 2347, "seqMaggieRes2a", 0, 0, "SEQ(MA~1.IMU"}, + {0, 2350, "seqCreatureFalls", 0, 0, ""}, + {5, 2360, "seqFinale1b", 0, 0, ""}, + {3, 2370, "seqFinale2a", 0, 0, "SEQ(FI~1.IMU"}, + {6, 2380, "seqFinale3b1", 0, 0, ""}, + {6, 2390, "seqFinale3b2", 0, 0, ""}, + {3, 2400, "seqFinale4a", 0, 0, "SEQ(FI~2.IMU"}, + {3, 2410, "seqFinale5a", 0, 0, "SEQ(FI~3.IMU"}, + {3, 2420, "seqFinale6a", 0, 0, "SEQ(FI~4.IMU"}, + {3, 2430, "seqFinale7a", 0, 0, "SE3D2B~5.IMU"}, + {6, 2440, "seqFinale8b", 0, 0, ""}, + {4, 2450, "seqFinale9a", 0, 0, "SE313B~5.IMU"}, + {0, -1, "", 0, 0, ""} +}; + +const imuseComiTable _comiStateMusicTable[] = { + {0, 1000, "STATE_NULL", 0, 0, 0, ""}, /* 00 */ + {0, 1001, "stateNoChange", 0, 0, 0, ""}, /* 01 */ + {3, 1098, "stateCredits1", 0, 0, 60, "1098-C~1.IMX"}, /* 02 */ + {3, 1099, "stateMenu", 0, 0, 60, "1099-M~1.IMX"}, /* 03 */ + {3, 1100, "stateHold1", 4, 0, 60, "1100-H~1.IMX"}, /* 04 */ + {3, 1101, "stateWaterline1", 4, 0, 60, "1101-W~1.IMX"}, /* 05 */ + {3, 1102, "stateHold2", 6, 1, 60, "1102-H~1.IMX"}, /* 06 */ + {3, 1103, "stateWaterline2", 6, 0, 60, "1103-W~1.IMX"}, /* 07 */ + {3, 1104, "stateCannon", 0, 0, 60, "1104-C~1.IMX"}, /* 08 */ + {3, 1105, "stateTreasure", 0, 0, 60, "1105-T~1.IMX"}, /* 09 */ + {3, 1200, "stateFortBase", 10, 1, 60, "1200-F~1.IMX"}, /* 10 */ + {3, 1201, "statePreFort", 10, 1, 60, "1201-P~1.IMX"}, /* 11 */ + {3, 1202, "statePreVooOut", 12, 0, 60, "1202-P~1.IMX"}, /* 12 */ + {3, 1203, "statePreVooIn", 12, 0, 60, "1203-P~1.IMX"}, /* 13 */ + {3, 1204, "statePreVooLady", 12, 0, 60, "1204-P~1.IMX"}, /* 14 */ + {3, 1205, "stateVoodooOut", 0, 0, 60, "1205-V~1.IMX"}, /* 15 */ + {3, 1210, "stateVoodooIn", 0, 0, 60, "1210-V~1.IMX"}, /* 16 */ + {12,1212, "stateVoodooInAlt", 0, 1, 42, "1210-V~1.IMX"}, /* 17 */ + {3, 1215, "stateVoodooLady", 0, 0, 60, "1215-V~1.IMX"}, /* 18 */ + {3, 1219, "statePrePlundermap", 0, 0, 60, "1219-P~1.IMX"}, /* 19 */ + {3, 1220, "statePlundermap", 0, 0, 60, "1220-P~1.IMX"}, /* 20 */ + {3, 1222, "statePreCabana", 0, 0, 60, "1222-P~1.IMX"}, /* 21 */ + {3, 1223, "stateCabana", 0, 0, 60, "1223-C~1.IMX"}, /* 22 */ + {3, 1224, "statePostCabana", 23, 0, 60, "1224-P~1.IMX"}, /* 23 */ + {3, 1225, "stateBeachClub", 23, 0, 60, "1225-B~1.IMX"}, /* 24 */ + {3, 1230, "stateCliff", 0, 0, 60, "1230-C~1.IMX"}, /* 25 */ + {3, 1232, "stateBelly", 0, 0, 48, "1232-B~1.IMX"}, /* 26 */ + {3, 1235, "stateQuicksand", 0, 0, 60, "1235-Q~1.IMX"}, /* 27 */ + {3, 1240, "stateDangerBeach", 0, 0, 48, "1240-D~1.IMX"}, /* 28 */ + {12,1241, "stateDangerBeachAlt",0, 2, 48, "1240-D~1.IMX"}, /* 29 */ + {3, 1245, "stateRowBoat", 0, 0, 60, "1245-R~1.IMX"}, /* 30 */ + {3, 1247, "stateAlongside", 0, 0, 48, "1247-A~1.IMX"}, /* 31 */ + {12,1248, "stateAlongsideAlt", 0, 1, 48, "1247-A~1.IMX"}, /* 32 */ + {3, 1250, "stateChimpBoat", 0, 0, 30, "1250-C~1.IMX"}, /* 33 */ + {3, 1255, "stateMrFossey", 0, 0, 48, "1255-M~1.IMX"}, /* 34 */ + {3, 1259, "statePreTown", 0, 0, 60, "1259-P~1.IMX"}, /* 35 */ + {3, 1260, "stateTown", 0, 0, 60, "1260-T~1.IMX"}, /* 36 */ + {3, 1264, "statePreMeadow", 0, 0, 60, "1264-P~1.IMX"}, /* 37 */ + {3, 1265, "stateMeadow", 0, 0, 60, "1265-M~1.IMX"}, /* 38 */ + {3, 1266, "stateMeadowAmb", 0, 0, 60, "1266-M~1.IMX"}, /* 39 */ + {3, 1270, "stateWardrobePre", 40, 0, 60, "1270-W~1.IMX"}, /* 40 */ + {3, 1272, "statePreShow", 40, 0, 60, "1272-P~1.IMX"}, /* 41 */ + {3, 1274, "stateWardrobeShow", 42, 0, 60, "1274-W~1.IMX"}, /* 42 */ + {3, 1276, "stateShow", 42, 0, 60, "1276-S~1.IMX"}, /* 43 */ + {3, 1277, "stateWardrobeJug", 44, 0, 60, "1277-W~1.IMX"}, /* 44 */ + {3, 1278, "stateJuggling", 44, 0, 60, "1278-J~1.IMX"}, /* 45 */ + {3, 1279, "statePostShow", 0, 0, 60, "1279-P~1.IMX"}, /* 46 */ + {3, 1280, "stateChickenShop", 0, 0, 60, "1280-C~1.IMX"}, /* 47 */ + {3, 1285, "stateBarberShop", 48, 0, 60, "1285-B~1.IMX"}, /* 48 */ + {3, 1286, "stateVanHelgen", 48, 0, 60, "1286-V~1.IMX"}, /* 49 */ + {3, 1287, "stateBill", 48, 0, 60, "1287-B~1.IMX"}, /* 50 */ + {3, 1288, "stateHaggis", 48, 0, 60, "1288-H~1.IMX"}, /* 51 */ + {3, 1289, "stateRottingham", 48, 0, 60, "1289-R~1.IMX"}, /* 52 */ + {3, 1305, "stateDeck", 0, 0, 60, "1305-D~1.IMX"}, /* 53 */ + {3, 1310, "stateCombatMap", 0, 0, 60, "1310-C~1.IMX"}, /* 54 */ + {3, 1320, "stateShipCombat", 0, 0, 60, "1320-S~1.IMX"}, /* 55 */ + {3, 1325, "stateSwordfight", 0, 0, 60, "1325-S~1.IMX"}, /* 56 */ + {3, 1327, "stateSwordRott", 0, 0, 60, "1327-S~1.IMX"}, /* 57 */ + {3, 1330, "stateTownEdge", 0, 0, 60, "1330-T~1.IMX"}, /* 58 */ + {3, 1335, "stateSwordLose", 0, 0, 60, "1335-S~1.IMX"}, /* 59 */ + {3, 1340, "stateSwordWin", 0, 0, 60, "1340-S~1.IMX"}, /* 60 */ + {3, 1345, "stateGetMap", 0, 0, 60, "1345-G~1.IMX"}, /* 61 */ + {3, 1400, "stateWreckBeach", 0, 0, 60, "1400-W~1.IMX"}, /* 62 */ + {3, 1405, "stateBloodMap", 63, 0, 60, "1405-B~1.IMX"}, /* 63 */ + {3, 1410, "stateClearing", 0, 0, 60, "1410-C~1.IMX"}, /* 64 */ + {3, 1415, "stateLighthouse", 63, 0, 60, "1415-L~1.IMX"}, /* 65 */ + {3, 1420, "stateVillage", 66, 0, 60, "1420-V~1.IMX"}, /* 66 */ + {3, 1423, "stateVolcano", 66, 0, 60, "1423-V~1.IMX"}, /* 67 */ + {3, 1425, "stateAltar", 66, 0, 60, "1425-A~1.IMX"}, /* 68 */ + {3, 1430, "stateHotelOut", 0, 0, 60, "1430-H~1.IMX"}, /* 69 */ + {3, 1435, "stateHotelBar", 70, 0, 60, "1435-H~1.IMX"}, /* 70 */ + {3, 1440, "stateHotelIn", 70, 0, 60, "1440-H~1.IMX"}, /* 71 */ + {3, 1445, "stateTarotLady", 70, 0, 60, "1445-T~1.IMX"}, /* 72 */ + {3, 1447, "stateGoodsoup", 70, 0, 60, "1447-G~1.IMX"}, /* 73 */ + {3, 1448, "stateGuestRoom", 0, 0, 60, "1448-G~1.IMX"}, /* 74 */ + {3, 1450, "stateWindmill", 63, 0, 60, "1450-W~1.IMX"}, /* 75 */ + {3, 1455, "stateCemetary", 0, 0, 60, "1455-C~1.IMX"}, /* 76 */ + {3, 1460, "stateCrypt", 77, 0, 60, "1460-C~1.IMX"}, /* 77 */ + {3, 1463, "stateGraveDigger", 77, 0, 60, "1463-G~1.IMX"}, /* 78 */ + {3, 1465, "stateMonkey1", 0, 0, 60, "1465-M~1.IMX"}, /* 79 */ + {3, 1475, "stateStanDark", 0, 0, 60, "1475-S~1.IMX"}, /* 80 */ + {3, 1477, "stateStanLight", 0, 0, 60, "1477-S~1.IMX"}, /* 81 */ + {3, 1480, "stateEggBeach", 63, 0, 60, "1480-E~1.IMX"}, /* 82 */ + {3, 1485, "stateSkullIsland", 0, 0, 60, "1485-S~1.IMX"}, /* 83 */ + {3, 1490, "stateSmugglersCave", 0, 0, 60, "1490-S~1.IMX"}, /* 84 */ + {3, 1500, "stateLeChuckTalk", 0, 0, 60, "1500-L~1.IMX"}, /* 85 */ + {3, 1505, "stateCarnival", 0, 0, 60, "1505-C~1.IMX"}, /* 86 */ + {3, 1511, "stateHang", 87, 0, 60, "1511-H~1.IMX"}, /* 87 */ + {3, 1512, "stateRum", 87, 0, 60, "1512-RUM.IMX"}, /* 88 */ + {3, 1513, "stateTorture", 87, 0, 60, "1513-T~1.IMX"}, /* 89 */ + {3, 1514, "stateSnow", 87, 0, 60, "1514-S~1.IMX"}, /* 90 */ + {3, 1515, "stateCredits", 0, 0, 60, "1515-C~1.IMX"}, /* 91 */ + {3, 1520, "stateCarnAmb", 0, 0, 60, "1520-C~1.IMX"}, /* 92 */ + {0, -1, "", 0, 0, 0, ""} +}; + +const imuseComiTable _comiSeqMusicTable[] = { + {0, 2000, "SEQ_NULL", 0, 0, 0, ""}, + {0, 2100, "seqINTRO", 0, 0, 0, ""}, + {3, 2105, "seqInterlude1", 0, 0, 60, "2105-I~1.IMX"}, + {8, 2110, "seqLastBoat", 0, 1, 0, ""}, + {0, 2115, "seqSINK_SHIP", 0, 0, 0, ""}, + {0, 2120, "seqCURSED_RING", 0, 0, 60, ""}, + {3, 2200, "seqInterlude2", 0, 0, 60, "2200-I~1.IMX"}, + {3, 2210, "seqKidnapped", 0, 0, 60, "2210-K~1.IMX"}, + {8, 2220, "seqSnakeVomits", 0, 1, 0, ""}, + {8, 2222, "seqPopBalloon", 0, 1, 0, ""}, + {3, 2225, "seqDropBalls", 0, 0, 60, "2225-D~1.IMX"}, + {4, 2232, "seqArriveBarber", 0, 0, 60, "2232-A~1.IMX"}, + {3, 2233, "seqAtonal", 0, 0, 60, "2233-A~1.IMX"}, + {3, 2235, "seqShaveHead1", 0, 0, 60, "2235-S~1.IMX"}, + {2, 2236, "seqShaveHead2", 0, 2, 60, "2235-S~1.IMX"}, + {3, 2245, "seqCaberLose", 0, 0, 60, "2245-C~1.IMX"}, + {3, 2250, "seqCaberWin", 0, 0, 60, "2250-C~1.IMX"}, + {3, 2255, "seqDuel1", 0, 0, 60, "2255-D~1.IMX"}, + {2, 2256, "seqDuel2", 0, 2, 60, "2255-D~1.IMX"}, + {2, 2257, "seqDuel3", 0, 3, 60, "2255-D~1.IMX"}, + {3, 2260, "seqBlowUpTree1", 0, 0, 60, "2260-B~1.IMX"}, + {2, 2261, "seqBlowUpTree2", 0, 2, 60, "2260-B~1.IMX"}, + {3, 2275, "seqMonkeys", 0, 0, 60, "2275-M~1.IMX"}, + {9, 2277, "seqAttack", 0, 1, 0, ""}, + {3, 2285, "seqSharks", 0, 0, 60, "2285-S~1.IMX"}, + {3, 2287, "seqTowelWalk", 0, 0, 60, "2287-T~1.IMX"}, + {0, 2293, "seqNICE_BOOTS", 0, 0, 0, ""}, + {0, 2295, "seqBIG_BONED", 0, 0, 0, ""}, + {3, 2300, "seqToBlood", 0, 0, 60, "2300-T~1.IMX"}, + {3, 2301, "seqInterlude3", 0, 0, 60, "2301-I~1.IMX"}, + {3, 2302, "seqRott1", 0, 0, 60, "2302-R~1.IMX"}, + {2, 2304, "seqRott2", 0, 2, 60, "2302-R~1.IMX"}, + {2, 2305, "seqRott2b", 0,21, 60, "2302-R~1.IMX"}, + {2, 2306, "seqRott3", 0, 3, 60, "2302-R~1.IMX"}, + {2, 2308, "seqRott4", 0, 4, 60, "2302-R~1.IMX"}, + {2, 2309, "seqRott5", 0, 5, 60, "2302-R~1.IMX"}, + {3, 2311, "seqVerse1", 0, 0, 60, "2311-S~1.IMX"}, + {2, 2312, "seqVerse2", 0, 2, 60, "2311-S~1.IMX"}, + {2, 2313, "seqVerse3", 0, 3, 60, "2311-S~1.IMX"}, + {2, 2314, "seqVerse4", 0, 4, 60, "2311-S~1.IMX"}, + {2, 2315, "seqVerse5", 0, 5, 60, "2311-S~1.IMX"}, + {2, 2316, "seqVerse6", 0, 6, 60, "2311-S~1.IMX"}, + {2, 2317, "seqVerse7", 0, 7, 60, "2311-S~1.IMX"}, + {2, 2318, "seqVerse8", 0, 8, 60, "2311-S~1.IMX"}, + {2, 2319, "seqSongEnd", 0, 9, 60, "2311-S~1.IMX"}, + {2, 2336, "seqRiposteLose", 0, 0, 60, "2336-R~1.IMX"}, + {2, 2337, "seqRiposteWin", 0, 0, 60, "2337-R~1.IMX"}, + {2, 2338, "seqInsultLose", 0, 0, 60, "2338-I~1.IMX"}, + {2, 2339, "seqInsultWin", 0, 0, 60, "2339-I~1.IMX"}, + {3, 2340, "seqSwordLose", 0, 0, 60, "1335-S~1.IMX"}, + {3, 2345, "seqSwordWin", 0, 0, 60, "1340-S~1.IMX"}, + {3, 2347, "seqGetMap", 0, 0, 60, "1345-G~1.IMX"}, + {3, 2400, "seqInterlude4", 0, 0, 60, "2400-I~1.IMX"}, + {0, 2405, "seqSHIPWRECK", 0, 0, 0, ""}, + {3, 2408, "seqFakeCredits", 0, 0, 60, "2408-F~1.IMX"}, + {3, 2410, "seqPassOut", 0, 0, 60, "2410-P~1.IMX"}, + {3, 2414, "seqGhostTalk", 0, 0, 60, "2414-G~1.IMX"}, + {2, 2415, "seqGhostWedding", 0, 1, 60, "2414-G~1.IMX"}, + {3, 2420, "seqEruption", 0, 0, 60, "2420-E~1.IMX"}, + {3, 2425, "seqSacrifice", 0, 0, 60, "2425-S~1.IMX"}, + {2, 2426, "seqSacrificeEnd", 0, 1, 60, "2425-S~1.IMX"}, + {3, 2430, "seqScareDigger", 0, 0, 60, "2430-S~1.IMX"}, + {3, 2445, "seqSkullArrive", 0, 0, 60, "2445-S~1.IMX"}, + {3, 2450, "seqFloat", 0, 0, 60, "2450-C~1.IMX"}, + {2, 2451, "seqFall", 0, 1, 60, "2450-C~1.IMX"}, + {2, 2452, "seqUmbrella", 0, 0, 60, "2450-C~1.IMX"}, + {3, 2460, "seqFight", 0, 0, 60, "2460-F~1.IMX"}, + {0, 2465, "seqLAVE_RIDE", 0, 0, 0, ""}, + {0, 2470, "seqMORE_SLAW", 0, 0, 0, ""}, + {0, 2475, "seqLIFT_CURSE", 0, 0, 0, ""}, + {3, 2500, "seqInterlude5", 0, 0, 60, "2500-I~1.IMX"}, + {3, 2502, "seqExitSkycar", 0, 0, 60, "2502-E~1.IMX"}, + {3, 2504, "seqGrow1", 0, 0, 60, "2504-G~1.IMX"}, + {2, 2505, "seqGrow2", 0, 1, 60, "2504-G~1.IMX"}, + {3, 2508, "seqInterlude6", 0, 0, 60, "2508-I~1.IMX"}, + {0, 2515, "seqFINALE", 0, 0, 0, ""}, + {3, 2520, "seqOut", 0, 0, 60, "2520-OUT.IMX"}, + {3, 2530, "seqZap1a", 0, 0, 60, "2530-Z~1.IMX"}, + {2, 2531, "seqZap1b", 0, 1, 60, "2530-Z~1.IMX"}, + {2, 2532, "seqZap1c", 0, 2, 60, "2530-Z~1.IMX"}, + {2, 2540, "seqZap2a", 0, 0, 60, "2540-Z~1.IMX"}, + {2, 2541, "seqZap2b", 0, 1, 60, "2540-Z~1.IMX"}, + {2, 2542, "seqZap2c", 0, 2, 60, "2540-Z~1.IMX"}, + {3, 2550, "seqZap3a", 0, 0, 60, "2550-Z~1.IMX"}, + {2, 2551, "seqZap3b", 0, 1, 60, "2550-Z~1.IMX"}, + {2, 2552, "seqZap3c", 0, 2, 60, "2550-Z~1.IMX"}, + {3, 2560, "seqZap4a", 0, 0, 60, "2560-Z~1.IMX"}, + {2, 2561, "seqZap4b", 0, 1, 60, "2560-Z~1.IMX"}, + {2, 2562, "seqZap4c", 0, 2, 60, "2560-Z~1.IMX"}, + {0, -1, "", 0, 0, 0, ""} +}; + +const imuseFtStateTable _ftStateMusicTable[] = { + {"", 0, 0, "STATE_NULL" }, + {"", 4, 127, "stateKstandOutside" }, + {"kinside", 2, 127, "stateKstandInside" }, + {"moshop", 3, 64, "stateMoesInside" }, + {"melcut", 2, 127, "stateMoesOutside" }, + {"mellover", 2, 127, "stateMellonAbove" }, + {"radloop", 3, 28, "stateTrailerOutside" }, + {"radloop", 3, 58, "stateTrailerInside" }, + {"radloop", 3, 127, "stateTodShop" }, + {"junkgate", 2, 127, "stateJunkGate" }, + {"junkover", 3, 127, "stateJunkAbove" }, + {"gastower", 2, 127, "stateGasTower" }, + {"", 4, 0, "stateTowerAlarm" }, + {"melcut", 2, 127, "stateCopsOnGround" }, + {"melcut", 2, 127, "stateCopsAround" }, + {"melcut", 2, 127, "stateMoesRuins" }, + {"melcut", 2, 127, "stateKstandNight" }, + {"trukblu2", 2, 127, "stateTruckerTalk" }, + {"stretch", 2, 127, "stateMumblyPeg" }, + {"kstand", 2, 100, "stateRanchOutside" }, + {"kinside", 2, 127, "stateRanchInside" }, + {"desert", 2, 127, "stateWreckedTruck" }, + {"opening", 2, 100, "stateGorgeVista" }, + {"caveopen", 2, 127, "stateCaveOpen" }, + {"cavecut1", 2, 127, "stateCaveOuter" }, + {"cavecut1", 1, 127, "stateCaveMiddle" }, + {"cave", 2, 127, "stateCaveInner" }, + {"corville", 2, 127, "stateCorvilleFront" }, + {"mines", 2, 127, "stateMineField" }, + {"bunyman3", 2, 127, "stateBunnyStore" }, + {"stretch", 2, 127, "stateStretchBen" }, + {"saveme", 2, 127, "stateBenPleas" }, + {"", 4, 0, "stateBenConvinces" }, + {"derby", 3, 127, "stateDemoDerby" }, + {"fire", 3, 127, "stateLightMyFire" }, + {"derby", 3, 127, "stateDerbyChase" }, + {"carparts", 2, 127, "stateVultureCarParts"}, + {"cavecut1", 2, 127, "stateVulturesInside" }, + {"mines", 2, 127, "stateFactoryRear" }, + {"croffice", 2, 127, "stateCorleyOffice" }, + {"melcut", 2, 127, "stateCorleyHall" }, + {"", 4, 0, "stateProjRoom" }, + {"", 4, 0, "stateMMRoom" }, + {"bumper", 2, 127, "stateBenOnBumper" }, + {"benump", 2, 127, "stateBenOnBack" }, + {"plane", 2, 127, "stateInCargoPlane" }, + {"saveme", 2, 127, "statePlaneControls" }, + {"", 4, 0, "stateCliffHanger1" }, + {"", 4, 0, "stateCliffHanger2" }, +}; + +const imuseFtNames _ftSeqNames[] = { + {"SEQ_NULL" }, + {"seqLogo" }, + {"seqOpenFlick" }, + {"seqBartender" }, + {"seqBenWakes" }, + {"seqPhotoScram" }, + {"seqClimbChain" }, + {"seqDogChase" }, + {"seqDogSquish" }, + {"seqDogHoist" }, + {"seqCopsArrive" }, + {"seqCopsLand" }, + {"seqCopsLeave" }, + {"seqCopterFlyby" }, + {"seqCopterCrash" }, + {"seqMoGetsParts" }, + {"seqMoFixesBike" }, + {"seqFirstGoodbye" }, + {"seqCopRoadblock" }, + {"seqDivertCops" }, + {"seqMurder" }, + {"seqCorleyDies" }, + {"seqTooLateAtMoes" }, + {"seqPicture" }, + {"seqNewsReel" }, + {"seqCopsInspect" }, + {"seqHijack" }, + {"seqNestolusAtRanch" }, + {"seqRipLimo" }, + {"seqGorgeTurn" }, + {"seqCavefishTalk" }, + {"seqArriveCorville" }, + {"seqSingleBunny" }, + {"seqBunnyArmy" }, + {"seqArriveAtMines" }, + {"seqArriveAtVultures"}, + {"seqMakePlan" }, + {"seqShowPlan" }, + {"seqDerbyStart" }, + {"seqLightBales" }, + {"seqNestolusBBQ" }, + {"seqCallSecurity" }, + {"seqFilmFail" }, + {"seqFilmBurn" }, + {"seqRipSpeech" }, + {"seqExposeRip" }, + {"seqRipEscape" }, + {"seqRareMoment" }, + {"seqFanBunnies" }, + {"seqRipDead" }, + {"seqFuneral" }, + {"seqCredits" } +}; + +const imuseFtSeqTable _ftSeqMusicTable[] = { + {"", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"opening", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"barbeat", 2, 127}, + {"barwarn", 2, 127}, + {"", 0, 0 }, + {"", 0, 0, }, + + {"benwakes", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"barwarn", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"swatben", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"dogattak", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"", 4, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"", 4, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"cops2", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"cops2", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"cops2", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"bunymrch", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"", 4, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"", 0, 0 }, + {"melcut", 2, 127}, + {"tada", 2, 127}, + {"", 0, 0 }, + + {"", 4, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"trucker", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"cops2", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"barwarn", 2, 127}, + {"murder", 2, 127}, + {"murder2", 2, 127}, + {"", 0, 0 }, + + {"corldie", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"barwarn", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"picture", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"ripintro", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"trucker", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"hosed", 2, 127}, + + {"ripdead", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"nesranch", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"scolding", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"desert", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"cavecut1", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"caveamb", 2, 80 }, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"castle", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"bunymrch", 2, 105}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"valkyrs", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"melcut", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"veltures", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"sorry", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"makeplan", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"castle", 2, 127}, + {"derby", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + + {"fire", 3, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"saveme", 3, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"scolding", 2, 127}, + + {"cops2", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"sorry", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"sorry", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"caveamb", 2, 85 }, + {"tada", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + + {"expose", 2, 127}, + {"", 4, 0 }, + {"", 0, 0 }, + {"mocoup", 2, 127}, + + {"ripscram", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"valkyrs", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"ripdead", 2, 127}, + {"", 0, 0 }, + {"", 0, 0 }, + {"", 0, 0 }, + + {"funeral", 2, 127}, + {"", 2, 127}, + {"moshop", 3, 64 }, + {"", 0, 0 }, + + {"bornbad", 2, 127}, + {"hammvox", 2, 127}, + {"legavox", 2, 127}, + {"chances", 2, 90 }, +}; +#endif + +} // End of namespace Scumm + +#ifdef PALMOS_68K +#include "scumm_globals.h" + +_GINIT(DimuseTables) +_GSETPTR(Scumm::_digStateMusicMap, GBVARS_DIGSTATEMUSICMAP_INDEX, Scumm::imuseRoomMap , GBVARS_SCUMM) +_GSETPTR(Scumm::_digStateMusicTable, GBVARS_DIGSTATEMUSICTABLE_INDEX, Scumm::imuseDigTable , GBVARS_SCUMM) +_GSETPTR(Scumm::_digSeqMusicTable, GBVARS_DIGSEQMUSICTABLE_INDEX, Scumm::imuseDigTable , GBVARS_SCUMM) +_GSETPTR(Scumm::_comiStateMusicTable, GBVARS_COMISTATEMUSICTABLE_INDEX, Scumm::imuseComiTable , GBVARS_SCUMM) +_GSETPTR(Scumm::_comiSeqMusicTable, GBVARS_COMISEQMUSICTABLE_INDEX, Scumm::imuseComiTable , GBVARS_SCUMM) +_GSETPTR(Scumm::_ftStateMusicTable, GBVARS_FTSTATEMUSICTABLE_INDEX, Scumm::imuseFtStateTable, GBVARS_SCUMM) +_GSETPTR(Scumm::_ftSeqMusicTable, GBVARS_FTSEQMUSICTABLE_INDEX, Scumm::imuseFtSeqTable , GBVARS_SCUMM) +_GSETPTR(Scumm::_ftSeqNames, GBVARS_FTSEQNAMES_INDEX, Scumm::imuseFtNames , GBVARS_SCUMM) +_GEND + +_GRELEASE(DimuseTables) +_GRELEASEPTR(GBVARS_DIGSTATEMUSICMAP_INDEX , GBVARS_SCUMM) +_GRELEASEPTR(GBVARS_DIGSTATEMUSICTABLE_INDEX , GBVARS_SCUMM) +_GRELEASEPTR(GBVARS_DIGSEQMUSICTABLE_INDEX , GBVARS_SCUMM) +_GRELEASEPTR(GBVARS_COMISTATEMUSICTABLE_INDEX , GBVARS_SCUMM) +_GRELEASEPTR(GBVARS_COMISEQMUSICTABLE_INDEX , GBVARS_SCUMM) +_GRELEASEPTR(GBVARS_FTSTATEMUSICTABLE_INDEX , GBVARS_SCUMM) +_GRELEASEPTR(GBVARS_FTSEQMUSICTABLE_INDEX , GBVARS_SCUMM) +_GRELEASEPTR(GBVARS_FTSEQNAMES_INDEX , GBVARS_SCUMM) +_GEND + +#endif diff --git a/engines/scumm/imuse_digi/dimuse_track.cpp b/engines/scumm/imuse_digi/dimuse_track.cpp new file mode 100644 index 0000000000..d1bd5b8923 --- /dev/null +++ b/engines/scumm/imuse_digi/dimuse_track.cpp @@ -0,0 +1,368 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001-2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "common/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 { + +int IMuseDigital::allocSlot(int priority) { + int l, lowest_priority = 127; + int trackId = -1; + + for (l = 0; l < MAX_DIGITAL_TRACKS; l++) { + if (!_track[l]->used) { + trackId = l; + break; + } + } + + if (trackId == -1) { + debug(5, "IMuseDigital::startSound(): All slots are full"); + for (l = 0; l < MAX_DIGITAL_TRACKS; l++) { + Track *track = _track[l]; + if (track->used && !track->toBeRemoved && + (lowest_priority > track->priority) && !track->stream2) { + lowest_priority = track->priority; + trackId = l; + } + } + if (lowest_priority <= priority) { + assert(trackId != -1); + _track[trackId]->toBeRemoved = true; + debug(5, "IMuseDigital::startSound(): Removed sound %d from track %d", _track[trackId]->soundId, trackId); + } else { + debug(5, "IMuseDigital::startSound(): Priority sound too low"); + return -1; + } + } + + return trackId; +} + +void IMuseDigital::startSound(int soundId, const char *soundName, int soundType, int volGroupId, AudioStream *input, int hookId, int volume, int priority) { + debug(5, "IMuseDigital::startSound(%d)", soundId); + + int l = allocSlot(priority); + if (l == -1) { + warning("IMuseDigital::startSound() Can't start sound - no free slots"); + return; + } + + Track *track = _track[l]; + while (track->used) { + // The designated track is not yet available. So, we call flushTracks() + // to get it processed (and thus made ready for us). Since the actual + // processing is done by another thread, we also call parseEvents to + // give it some time (and to avoid busy waiting/looping). + flushTracks(); +#ifndef __PLAYSTATION2__ + _vm->parseEvents(); +#endif + } + + track->pan = 64; + track->vol = volume * 1000; + track->volFadeDest = 0; + track->volFadeStep = 0; + track->volFadeDelay = 0; + track->volFadeUsed = false; + track->soundId = soundId; + track->started = false; + track->volGroupId = volGroupId; + track->curHookId = hookId; + track->priority = priority; + track->curRegion = -1; + track->dataOffset = 0; + track->regionOffset = 0; + track->mod = 0; + track->mixerFlags = 0; + track->toBeRemoved = false; + track->readyToRemove = false; + track->soundType = soundType; + + int bits = 0, freq = 0, channels = 0; + + if (input) { + track->iteration = 0; + track->souStream = true; + track->soundName[0] = 0; + } else { + track->souStream = false; + strcpy(track->soundName, soundName); + track->soundHandle = _sound->openSound(soundId, soundName, soundType, volGroupId, -1); + + if (track->soundHandle == NULL) + return; + + track->compressed = _sound->isCompressed(track->soundHandle); + + bits = _sound->getBits(track->soundHandle); + channels = _sound->getChannels(track->soundHandle); + freq = _sound->getFreq(track->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->pan = a->_talkPan; + track->vol = a->_talkVolume * 1000; + } + } + + assert(bits == 8 || bits == 12 || bits == 16); + assert(channels == 1 || channels == 2); + assert(0 < freq && freq <= 65535); + + track->iteration = freq * channels; + if (channels == 2) + track->mixerFlags = Audio::Mixer::FLAG_STEREO | Audio::Mixer::FLAG_REVERSE_STEREO; + + if ((bits == 12) || (bits == 16)) { + track->mixerFlags |= Audio::Mixer::FLAG_16BITS; + track->iteration *= 2; + } else if (bits == 8) { + track->mixerFlags |= Audio::Mixer::FLAG_UNSIGNED; + } else + error("IMuseDigital::startSound(): Can't handle %d bit samples", bits); + +#ifdef SCUMM_LITTLE_ENDIAN + if (track->compressed) + track->mixerFlags |= Audio::Mixer::FLAG_LITTLE_ENDIAN; +#endif + } + + if (input) { + track->stream2 = input; + track->stream = NULL; + track->started = false; + } else { + const int pan = (track->pan != 64) ? 2 * track->pan - 127 : 0; + const int vol = track->vol / 1000; + Audio::Mixer::SoundType type = Audio::Mixer::kPlainSoundType; + + if (track->volGroupId == 1) + type = Audio::Mixer::kSpeechSoundType; + if (track->volGroupId == 2) + type = Audio::Mixer::kSFXSoundType; + if (track->volGroupId == 3) + type = Audio::Mixer::kMusicSoundType; + + // setup 1 second stream wrapped buffer + int32 streamBufferSize = track->iteration; + track->stream2 = NULL; + track->stream = makeAppendableAudioStream(freq, track->mixerFlags, streamBufferSize); + _vm->_mixer->playInputStream(type, &track->handle, track->stream, -1, vol, pan, false); + track->started = true; + } + + track->used = true; +} + +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++) { + Track *track = _track[l]; + if ((track->soundId == soundId) && track->used && !track->toBeRemoved) { + track->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++) { + Track *track = _track[l]; + if ((track->soundId == soundId) && track->used && !track->toBeRemoved) { + track->vol = volume * 1000; + } + } +} + +void IMuseDigital::setHookId(int soundId, int hookId) { + Common::StackLock lock(_mutex, "IMuseDigital::setHookId()"); + + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + Track *track = _track[l]; + if ((track->soundId == soundId) && track->used && !track->toBeRemoved) { + track->curHookId = hookId; + } + } +} + +int IMuseDigital::getCurMusicSoundId() { + Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicSoundId()"); + int soundId = -1; + + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + Track *track = _track[l]; + if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { + soundId = track->soundId; + } + } + + return soundId; +} + +char *IMuseDigital::getCurMusicSoundName() { + Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicSoundName()"); + char *soundName = NULL; + + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + Track *track = _track[l]; + if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { + soundName = track->soundName; + } + } + + return soundName; +} + +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++) { + Track *track = _track[l]; + if ((track->soundId == soundId) && track->used && !track->toBeRemoved) { + track->pan = pan; + } + } +} + +void IMuseDigital::selectVolumeGroup(int soundId, int volGroupId) { + Common::StackLock lock(_mutex, "IMuseDigital::selectVolumeGroup()"); + debug(5, "IMuseDigital::setGroupVolume(%d, %d)", soundId, volGroupId); + assert((volGroupId >= 1) && (volGroupId <= 4)); + + if (volGroupId == 4) + volGroupId = 3; + + for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) { + Track *track = _track[l]; + if ((track->soundId == soundId) && track->used && !track->toBeRemoved) { + track->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++) { + Track *track = _track[l]; + if ((track->soundId == soundId) && track->used && !track->toBeRemoved) { + track->volFadeDelay = delay60HzTicks; + track->volFadeDest = destVolume * 1000; + track->volFadeStep = (track->volFadeDest - track->vol) * 60 * (1000 / _callbackFps) / (1000 * delay60HzTicks); + track->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++) { + Track *track = _track[l]; + if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) { + cloneToFadeOutTrack(track, fadeDelay); + track->toBeRemoved = true; + } + } +} + +IMuseDigital::Track *IMuseDigital::cloneToFadeOutTrack(Track *track, int fadeDelay) { + Common::StackLock lock(_mutex, "IMuseDigital::cloneToFadeOutTrack()"); + assert(track); + Track *fadeTrack = 0; + + debug(5, "IMuseDigital::cloneToFadeOutTrack(%d, %d)", track->trackId, fadeDelay); + + if (_track[track->trackId + MAX_DIGITAL_TRACKS]->used) { + warning("IMuseDigital::cloneToFadeOutTrack: Not free fade track"); + return NULL; + } + + fadeTrack = _track[track->trackId + MAX_DIGITAL_TRACKS]; + fadeTrack->pan = track->pan; + fadeTrack->vol = track->vol; + fadeTrack->volGroupId = track->volGroupId; + fadeTrack->priority = track->priority; + fadeTrack->soundId = track->soundId; + fadeTrack->dataOffset = track->dataOffset; + fadeTrack->regionOffset = track->regionOffset; + fadeTrack->curRegion = track->curRegion; + fadeTrack->curHookId = track->curHookId; + fadeTrack->iteration = track->iteration; + fadeTrack->mixerFlags = track->mixerFlags; + fadeTrack->mod = track->mod; + fadeTrack->toBeRemoved = track->toBeRemoved; + fadeTrack->readyToRemove = track->readyToRemove; + fadeTrack->souStream = track->souStream; + fadeTrack->started = track->started; + fadeTrack->stream2 = track->stream2; + strcpy(fadeTrack->soundName, track->soundName); + fadeTrack->soundType = track->soundType; + fadeTrack->soundHandle = _sound->cloneSound(track->soundHandle); + assert(fadeTrack->soundHandle); + + fadeTrack->volFadeDelay = fadeDelay; + fadeTrack->volFadeDest = 0; + fadeTrack->volFadeStep = (fadeTrack->volFadeDest - fadeTrack->vol) * 60 * (1000 / _callbackFps) / (1000 * fadeDelay); + fadeTrack->volFadeUsed = true; + + Audio::Mixer::SoundType type = Audio::Mixer::kPlainSoundType; + + if (fadeTrack->volGroupId == 1) + type = Audio::Mixer::kSpeechSoundType; + if (fadeTrack->volGroupId == 2) + type = Audio::Mixer::kSFXSoundType; + if (fadeTrack->volGroupId == 3) + type = Audio::Mixer::kMusicSoundType; + + // setup 1 second stream wrapped buffer + int32 streamBufferSize = fadeTrack->iteration; + fadeTrack->stream = makeAppendableAudioStream(_sound->getFreq(fadeTrack->soundHandle), fadeTrack->mixerFlags, streamBufferSize); + _vm->_mixer->playInputStream(type, &fadeTrack->handle, fadeTrack->stream, -1, fadeTrack->vol / 1000, fadeTrack->pan, false); + fadeTrack->started = true; + fadeTrack->used = true; + + return fadeTrack; +} + +} // End of namespace Scumm |