Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,375 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/textconsole.h"
#include "common/timer.h"
#include "engines/grim/savegame.h"
#include "engines/grim/debug.h"
#include "engines/grim/imuse/imuse.h"
#include "engines/grim/movie/codecs/vima.h"
#include "audio/audiostream.h"
#include "audio/mixer.h"
#include "audio/decoders/raw.h"
namespace Grim {
Imuse *g_imuse = nullptr;
extern uint16 imuseDestTable[];
extern ImuseTable grimStateMusicTable[];
extern ImuseTable grimSeqMusicTable[];
extern ImuseTable grimDemoStateMusicTable[];
extern ImuseTable grimDemoSeqMusicTable[];
void Imuse::timerHandler(void *refCon) {
Imuse *imuse = (Imuse *)refCon;
imuse->callback();
}
Imuse::Imuse(int fps, bool demo) {
_demo = demo;
_pause = false;
_sound = new ImuseSndMgr(_demo);
assert(_sound);
_callbackFps = fps;
resetState();
for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
_track[l] = new Track;
assert(_track[l]);
_track[l]->clear();
_track[l]->trackId = l;
}
vimaInit(imuseDestTable);
if (_demo) {
_stateMusicTable = grimDemoStateMusicTable;
_seqMusicTable = grimDemoSeqMusicTable;
} else {
_stateMusicTable = grimStateMusicTable;
_seqMusicTable = grimSeqMusicTable;
}
g_system->getTimerManager()->installTimerProc(timerHandler, 1000000 / _callbackFps, this, "imuseCallback");
}
Imuse::~Imuse() {
g_system->getTimerManager()->removeTimerProc(timerHandler);
stopAllSounds();
for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
delete _track[l];
}
delete _sound;
}
void Imuse::resetState() {
_curMusicState = 0;
_curMusicSeq = 0;
memset(_attributes, 0, sizeof(_attributes));
}
void Imuse::restoreState(SaveGame *savedState) {
Common::StackLock lock(_mutex);
savedState->beginSection('IMUS');
_curMusicState = savedState->readLESint32();
_curMusicSeq = savedState->readLESint32();
for (int r = 0; r < 185; r++) {
_attributes[r] = savedState->readLESint32();
}
for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
Track *track = _track[l];
track->clear();
track->trackId = l;
track->pan = savedState->readLESint32();
track->panFadeDest = savedState->readLESint32();
track->panFadeDelay = savedState->readLESint32();
track->panFadeUsed = savedState->readBool();
track->vol = savedState->readLESint32();
track->volFadeDest = savedState->readLESint32();
track->volFadeDelay = savedState->readLESint32();
track->volFadeUsed = savedState->readBool();
savedState->read(track->soundName, 32);
track->used = savedState->readBool();
track->toBeRemoved = savedState->readBool();
track->priority = savedState->readLESint32();
track->regionOffset = savedState->readLESint32();
track->dataOffset = savedState->readLESint32();
track->curRegion = savedState->readLESint32();
track->curHookId = savedState->readLESint32();
track->volGroupId = savedState->readLESint32();
track->feedSize = savedState->readLESint32();
track->mixerFlags = savedState->readLESint32();
if (!track->used)
continue;
if (track->toBeRemoved || track->curRegion == -1) {
track->used = false;
continue;
}
track->soundDesc = _sound->openSound(track->soundName, track->volGroupId);
if (!track->soundDesc) {
warning("Imuse::restoreState: Can't open sound so will not be resumed");
track->used = false;
continue;
}
int channels = _sound->getChannels(track->soundDesc);
int freq = _sound->getFreq(track->soundDesc);
track->mixerFlags = kFlag16Bits;
if (channels == 2)
track->mixerFlags |= kFlagStereo | kFlagReverseStereo;
track->stream = Audio::makeQueuingAudioStream(freq, (track->mixerFlags & kFlagStereo) != 0);
g_system->getMixer()->playStream(track->getType(), &track->handle, track->stream, -1, track->getVol(),
track->getPan(), DisposeAfterUse::YES, false,
(track->mixerFlags & kFlagReverseStereo) != 0);
g_system->getMixer()->pauseHandle(track->handle, true);
}
savedState->endSection();
g_system->getMixer()->pauseAll(false);
}
void Imuse::saveState(SaveGame *savedState) {
Common::StackLock lock(_mutex);
savedState->beginSection('IMUS');
savedState->writeLESint32(_curMusicState);
savedState->writeLESint32(_curMusicSeq);
for (int r = 0; r < 185; r++) {
savedState->writeLESint32(_attributes[r]);
}
for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
Track *track = _track[l];
savedState->writeLESint32(track->pan);
savedState->writeLESint32(track->panFadeDest);
savedState->writeLESint32(track->panFadeDelay);
savedState->writeBool(track->panFadeUsed);
savedState->writeLESint32(track->vol);
savedState->writeLESint32(track->volFadeDest);
savedState->writeLESint32(track->volFadeDelay);
savedState->writeBool(track->volFadeUsed);
savedState->write(track->soundName, 32);
savedState->writeBool(track->used);
savedState->writeBool(track->toBeRemoved);
savedState->writeLESint32(track->priority);
savedState->writeLESint32(track->regionOffset);
savedState->writeLESint32(track->dataOffset);
savedState->writeLESint32(track->curRegion);
savedState->writeLESint32(track->curHookId);
savedState->writeLESint32(track->volGroupId);
savedState->writeLESint32(track->feedSize);
savedState->writeLESint32(track->mixerFlags);
}
savedState->endSection();
}
int32 Imuse::makeMixerFlags(int32 flags) {
int32 mixerFlags = 0;
if (flags & kFlagUnsigned)
mixerFlags |= Audio::FLAG_UNSIGNED;
if (flags & kFlag16Bits)
mixerFlags |= Audio::FLAG_16BITS;
if (flags & kFlagLittleEndian)
mixerFlags |= Audio::FLAG_LITTLE_ENDIAN;
if (flags & kFlagStereo)
mixerFlags |= Audio::FLAG_STEREO;
return mixerFlags;
}
void Imuse::callback() {
Common::StackLock lock(_mutex);
for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
Track *track = _track[l];
if (track->used) {
// Ignore tracks which are about to finish. Also, if it did finish in the meantime,
// mark it as unused.
if (!track->stream) {
if (!track->soundDesc || !g_system->getMixer()->isSoundHandleActive(track->handle))
track->clear();
continue;
}
if (_pause)
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) {
// Fade out complete -> remove this track
flushTrack(track);
continue;
}
}
} 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;
}
}
}
}
if (track->panFadeUsed) {
if (track->panFadeStep < 0) {
if (track->pan > track->panFadeDest) {
track->pan += track->panFadeStep;
if (track->pan < track->panFadeDest) {
track->pan = track->panFadeDest;
track->panFadeUsed = false;
}
}
} else if (track->panFadeStep > 0) {
if (track->pan < track->panFadeDest) {
track->pan += track->panFadeStep;
if (track->pan > track->panFadeDest) {
track->pan = track->panFadeDest;
track->panFadeUsed = false;
}
}
}
}
assert(track->stream);
byte *data = nullptr;
int32 result = 0;
if (track->curRegion == -1) {
switchToNextRegion(track);
if (!track->stream) // Seems we reached the end of the stream
continue;
}
int channels = _sound->getChannels(track->soundDesc);
int32 mixer_size = track->feedSize / _callbackFps;
if (track->stream->endOfData()) {
mixer_size *= 2;
}
if (channels == 1)
mixer_size &= ~1;
if (channels == 2)
mixer_size &= ~3;
if (mixer_size == 0)
continue;
do {
int32 mixerFlags = makeMixerFlags(track->mixerFlags);
result = _sound->getDataFromRegion(track->soundDesc, track->curRegion, &data, track->regionOffset, mixer_size, &mixerFlags);
if (channels == 1) {
result &= ~1;
}
if (channels == 2) {
result &= ~3;
}
if (result > mixer_size)
result = mixer_size;
if (g_system->getMixer()->isReady()) {
track->stream->queueBuffer(data, result, DisposeAfterUse::YES, mixerFlags);
track->regionOffset += result;
} else
free(data);
if (_sound->isEndOfRegion(track->soundDesc, track->curRegion)) {
switchToNextRegion(track);
if (!track->stream)
break;
}
mixer_size -= result;
assert(mixer_size >= 0);
} while (mixer_size);
if (g_system->getMixer()->isReady()) {
g_system->getMixer()->setChannelVolume(track->handle, track->getVol());
g_system->getMixer()->setChannelBalance(track->handle, track->getPan());
}
}
}
}
void Imuse::switchToNextRegion(Track *track) {
assert(track);
if (track->trackId >= MAX_IMUSE_TRACKS) {
Debug::debug(Debug::Sound, "Imuse::switchToNextRegion(): fadeTrack end: soundName:%s", track->soundName);
flushTrack(track);
return;
}
int numRegions = _sound->getNumRegions(track->soundDesc);
if (++track->curRegion == numRegions) {
Debug::debug(Debug::Sound, "Imuse::switchToNextRegion(): end of tracks: soundName:%s", track->soundName);
flushTrack(track);
return;
}
ImuseSndMgr::SoundDesc *soundDesc = track->soundDesc;
int jumpId = _sound->getJumpIdByRegionAndHookId(soundDesc, track->curRegion, track->curHookId);
// It seems 128 is a special value meaning it should not force the 0 hookId,
// otherwise the sound hkwine.imu when glottis drinks the wine in the barrel
// in hk won't stop.
if (jumpId == -1 && track->curHookId != 128)
jumpId = _sound->getJumpIdByRegionAndHookId(soundDesc, track->curRegion, 0);
if (jumpId != -1) {
Debug::debug(Debug::Sound, "Imuse::switchToNextRegion(): JUMP: soundName:%s", track->soundName);
int region = _sound->getRegionIdByJumpId(soundDesc, jumpId);
assert(region != -1);
int sampleHookId = _sound->getJumpHookId(soundDesc, jumpId);
assert(sampleHookId != -1);
int fadeDelay = (60 * _sound->getJumpFade(soundDesc, jumpId)) / 1000;
if (fadeDelay) {
Track *fadeTrack = cloneToFadeOutTrack(track, fadeDelay);
if (fadeTrack) {
fadeTrack->dataOffset = _sound->getRegionOffset(fadeTrack->soundDesc, fadeTrack->curRegion);
fadeTrack->regionOffset = 0;
fadeTrack->curHookId = 0;
}
}
track->curRegion = region;
if (track->curHookId == sampleHookId)
track->curHookId = 0;
else if (track->curHookId == 0x80)
track->curHookId = 0;
}
Debug::debug(Debug::Sound, "Imuse::switchToNextRegion(): REGION %d: soundName:%s", (int)track->curRegion, track->soundName);
track->dataOffset = _sound->getRegionOffset(soundDesc, track->curRegion);
track->regionOffset = 0;
}
} // end of namespace Grim

114
engines/grim/imuse/imuse.h Normal file
View File

@@ -0,0 +1,114 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRIM_IMUSE_H
#define GRIM_IMUSE_H
#include "common/mutex.h"
#include "engines/grim/imuse/imuse_track.h"
namespace Grim {
#define MAX_IMUSE_TRACKS 16
#define MAX_IMUSE_FADETRACKS 16
struct ImuseTable;
class SaveGame;
class Imuse {
private:
int _callbackFps;
Track *_track[MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS];
Common::Mutex _mutex;
ImuseSndMgr *_sound;
bool _pause;
bool _demo;
int32 _attributes[185];
int32 _curMusicState;
int32 _curMusicSeq;
const ImuseTable *_stateMusicTable;
const ImuseTable *_seqMusicTable;
int32 makeMixerFlags(int32 flags);
static void timerHandler(void *refConf);
void callback();
void switchToNextRegion(Track *track);
int allocSlot(int priority);
void selectVolumeGroup(const char *soundName, int volGroupId);
void fadeOutMusic(int fadeDelay);
void fadeOutMusicAndStartNew(int fadeDelay, const char *filename, int hookId, int vol, int pan);
Track *cloneToFadeOutTrack(Track *track, int fadeDelay);
void playMusic(const ImuseTable *table, int atribPos, bool sequence);
void flushTrack(Track *track);
public:
Imuse(int fps, bool demo);
~Imuse();
bool startSound(const char *soundName, int volGroupId, int hookId, int volume, int pan, int priority, Track *otherTrack);
bool startVoice(const char *soundName, int volume = 127, int pan = 64);
void startMusic(const char *soundName, int hookId, int volume, int pan);
void startMusicWithOtherPos(const char *soundName, int hookId, int volume, int pan, Track *otherTrack);
void startSfx(const char *soundName, int priority = 127);
void restoreState(SaveGame *savedState);
void saveState(SaveGame *savedState);
void resetState();
Track *findTrack(const char *soundName);
void setPriority(const char *soundName, int priority);
void setVolume(const char *soundName, int volume);
int getVolume(const char *soundName);
void setPan(const char *soundName, int pan); /* pan: 0 .. 127 */
void setFadePan(const char *soundName, int destPan, int duration);
void setFadeVolume(const char *soundName, int destVolume, int duration);
void setHookId(const char *soundName, int hookId);
int getCountPlayedTracks(const char *soundName);
void stopSound(const char *soundName);
void stopAllSounds();
void pause(bool pause);
void setMusicState(int stateId);
int setMusicSequence(int seqId);
void refreshScripts();
void flushTracks();
bool isVoicePlaying();
char *getCurMusicSoundName();
int getCurMusicPan();
int getCurMusicVol();
bool getSoundStatus(const char *soundName);
int32 getPosIn16msTicks(const char *soundName);
};
extern Imuse *g_imuse;
} // end of namespace Grim
#endif

View File

@@ -0,0 +1,172 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "engines/grim/resource.h"
#include "engines/grim/imuse/imuse_mcmp_mgr.h"
#include "engines/grim/movie/codecs/vima.h"
namespace Grim {
uint16 imuseDestTable[5786];
McmpMgr::McmpMgr() {
_compTable = nullptr;
_numCompItems = 0;
_curSample = -1;
_compInput = nullptr;
_outputSize = 0;
_file = nullptr;
_lastBlock = -1;
}
McmpMgr::~McmpMgr() {
delete[] _compTable;
delete[] _compInput;
}
bool McmpMgr::openSound(const char *filename, Common::SeekableReadStream *data, int &offsetData) {
_file = data;
uint32 tag = _file->readUint32BE();
if (tag != 'MCMP') {
error("McmpMgr::openSound() Expected MCMP tag");
return false;
}
_numCompItems = _file->readSint16BE();
assert(_numCompItems > 0);
int32 offset = _file->pos() + (_numCompItems * 9) + 2;
_numCompItems--;
_compTable = new CompTable[_numCompItems];
_file->seek(5, SEEK_CUR);
int32 headerSize = _compTable[0].decompSize = _file->readSint32BE();
int32 maxSize = headerSize;
offset += headerSize;
int i;
for (i = 0; i < _numCompItems; i++) {
_compTable[i].codec = _file->readByte();
_compTable[i].decompSize = _file->readSint32BE();
_compTable[i].compSize = _file->readSint32BE();
_compTable[i].offset = offset;
offset += _compTable[i].compSize;
if (_compTable[i].compSize > maxSize)
maxSize = _compTable[i].compSize;
}
int16 sizeCodecs = _file->readSint16BE();
for (i = 0; i < _numCompItems; i++) {
_compTable[i].offset += sizeCodecs;
}
_file->seek(sizeCodecs, SEEK_CUR);
_uncompressedSingleBlock = true;
if (_numCompItems == 0)
_uncompressedSingleBlock = false;
if (_numCompItems >= 1 && _compTable[0].codec != 0)
_uncompressedSingleBlock = false;
for (i = 1; i < _numCompItems; i++) {
if (_compTable[i].codec || _compTable[i].decompSize || _compTable[i].compSize) {
_uncompressedSingleBlock = false;
break;
}
}
// hack: two more bytes at the end of input buffer
if (!_uncompressedSingleBlock)
_compInput = new byte[maxSize + 2];
offsetData = headerSize;
return true;
}
int32 McmpMgr::decompressSample(int32 offset, int32 size, byte **comp_final) {
int32 i, final_size, output_size;
int skip, first_block, last_block;
if (!_file) {
error("McmpMgr::decompressSampleByName() File is not open!");
return 0;
}
if (_uncompressedSingleBlock) {
*comp_final = static_cast<byte *>(malloc(size));
_file->seek(_compTable[0].offset + offset, SEEK_SET);
return _file->read(*comp_final, size);
}
first_block = offset / 0x2000;
last_block = (offset + size - 1) / 0x2000;
skip = offset % 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 = static_cast<byte *>(malloc(blocks_final_size));
final_size = 0;
for (i = first_block; i <= last_block; i++) {
if (_lastBlock != i) {
// hack: two more zero bytes at the end of input buffer
_compInput[_compTable[i].compSize] = 0;
_compInput[_compTable[i].compSize + 1] = 0;
_file->seek(_compTable[i].offset, SEEK_SET);
if (_compTable[i].codec == 0 && _compTable[i].decompSize == _compTable[i].compSize) {
_file->read(_compOutput, _compTable[i].compSize);
} else {
_file->read(_compInput, _compTable[i].compSize);
decompressVima(_compInput, (int16 *)_compOutput, _compTable[i].decompSize, imuseDestTable, false);
}
_outputSize = _compTable[i].decompSize;
if (_outputSize > 0x2000) {
error("McmpMgr::decompressSample() _outputSize: %d", _outputSize);
}
_lastBlock = i;
}
output_size = _outputSize - 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;
}
} // end of namespace Grim

View File

@@ -0,0 +1,58 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRIM_MCMP_MGR_H
#define GRIM_MCMP_MGR_H
namespace Grim {
class McmpMgr {
private:
struct CompTable {
byte codec;
int32 decompSize;
int32 compSize;
int32 offset;
};
CompTable *_compTable;
int16 _numCompItems;
int _curSample;
Common::SeekableReadStream *_file;
byte _compOutput[0x2000];
byte *_compInput;
int _outputSize;
int _lastBlock;
bool _uncompressedSingleBlock;
public:
McmpMgr();
~McmpMgr();
bool openSound(const char *filename, Common::SeekableReadStream *data, int &offsetData);
int32 decompressSample(int32 offset, int32 size, byte **comp_final);
};
} // end of namespace Grim
#endif

View File

@@ -0,0 +1,167 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "engines/grim/debug.h"
#include "engines/grim/imuse/imuse.h"
#include "engines/grim/imuse/imuse_tables.h"
namespace Grim {
void Imuse::setMusicState(int stateId) {
int l, num = -1;
if (stateId == 0)
stateId = 1000;
for (l = 0; _stateMusicTable[l].soundId != -1; l++) {
if (_stateMusicTable[l].soundId == stateId) {
num = l;
break;
}
}
assert(num != -1);
Debug::debug(Debug::Sound, "Imuse::setMusicState(): SoundId %d, filename: %s", _stateMusicTable[l].soundId, _stateMusicTable[l].filename);
if (_curMusicState == num)
return;
if (!_curMusicSeq) {
playMusic(&_stateMusicTable[num], num, false);
}
_curMusicState = num;
}
int Imuse::setMusicSequence(int seqId) {
int l, num = -1;
if (seqId == -1)
return _seqMusicTable[_curMusicSeq].soundId;
if (seqId == 0)
seqId = 2000;
for (l = 0; _seqMusicTable[l].soundId != -1; l++) {
if (_seqMusicTable[l].soundId == seqId) {
num = l;
break;
}
}
assert(num != -1);
Debug::debug(Debug::Sound, "Imuse::setMusicSequence(): SoundId %d, filename: %s", _seqMusicTable[l].soundId, _seqMusicTable[l].filename);
if (_curMusicSeq == num)
return _seqMusicTable[_curMusicSeq].soundId;
if (num) {
playMusic(&_seqMusicTable[num], 0, true);
} else {
playMusic(&_stateMusicTable[_curMusicState], _curMusicState, true);
num = 0;
}
_curMusicSeq = num;
return _seqMusicTable[_curMusicSeq].soundId;
}
void Imuse::playMusic(const ImuseTable *table, int atribPos, bool sequence) {
int hookId = 0;
if (atribPos) {
if (table->atribPos)
atribPos = table->atribPos;
hookId = _attributes[atribPos];
if (table->hookId) {
if (hookId && table->hookId > 1) {
_attributes[atribPos] = 2;
} else {
_attributes[atribPos] = hookId + 1;
if (table->hookId < hookId + 1)
_attributes[atribPos] = 1;
}
}
}
if (hookId == 0)
hookId = 100;
if (table->opcode == 0) {
fadeOutMusic(120);
return;
}
if (table->opcode == 2 || table->opcode == 3) {
if (table->filename[0] == 0) {
fadeOutMusic(60);
return;
}
char *soundName = getCurMusicSoundName();
int pan;
if (table->pan == 0)
pan = 64;
else
pan = table->pan;
if (!soundName) {
startMusic(table->filename, hookId, 0, pan);
setVolume(table->filename, 0);
setFadeVolume(table->filename, table->volume, table->fadeOut60TicksDelay);
return;
}
int old_pan = getCurMusicPan();
int old_vol = getCurMusicVol();
if (old_pan == -1)
old_pan = 64;
if (old_vol == -1)
old_vol = 127;
if (table->opcode == 2) {
fadeOutMusic(table->fadeOut60TicksDelay);
startMusic(table->filename, hookId, table->volume, pan);
setVolume(table->filename, 0);
setFadeVolume(table->filename, table->volume, table->fadeOut60TicksDelay);
setFadePan(table->filename, pan, table->fadeOut60TicksDelay);
return;
}
if (strcmp(soundName, table->filename) == 0) {
setFadeVolume(soundName, table->volume, table->fadeOut60TicksDelay);
setFadePan(soundName, pan, table->fadeOut60TicksDelay);
return;
}
if (!sequence && table->atribPos && table->atribPos == _stateMusicTable[_curMusicState].atribPos) {
fadeOutMusicAndStartNew(table->fadeOut60TicksDelay, table->filename, hookId, old_vol, old_pan);
setVolume(table->filename, 0);
setFadeVolume(table->filename, table->volume, table->fadeOut60TicksDelay);
setFadePan(table->filename, pan, table->fadeOut60TicksDelay);
} else {
fadeOutMusic(table->fadeOut60TicksDelay);
startMusic(table->filename, hookId, table->volume, pan);
setVolume(table->filename, 0);
setFadeVolume(table->filename, table->volume, table->fadeOut60TicksDelay);
}
}
}
} // end of namespace Grim

View File

@@ -0,0 +1,178 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/textconsole.h"
#include "engines/grim/imuse/imuse.h"
#include "engines/grim/debug.h"
namespace Grim {
void Imuse::flushTrack(Track *track) {
track->toBeRemoved = true;
if (track->stream) {
// Finalize the appendable stream, then remove our reference to it.
// Note that there might still be some data left in the buffers of the
// appendable stream. We play it nice and wait till all of it
// played. The audio mixer will take care of it afterwards (and dispose it).
track->stream->finish();
track->stream = nullptr;
if (track->soundDesc) {
_sound->closeSound(track->soundDesc);
track->soundDesc = nullptr;
}
}
if (!g_system->getMixer()->isSoundHandleActive(track->handle)) {
track->clear();
}
}
void Imuse::flushTracks() {
Common::StackLock lock(_mutex);
for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
Track *track = _track[l];
if (track->used && track->toBeRemoved && !g_system->getMixer()->isSoundHandleActive(track->handle)) {
track->clear();
}
}
}
void Imuse::refreshScripts() {
Common::StackLock lock(_mutex);
bool found = false;
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l];
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
found = true;
}
}
if (!found && _curMusicState) {
setMusicSequence(0);
}
}
bool Imuse::startVoice(const char *soundName, int volume, int pan) {
Debug::debug(Debug::Sound, "Imuse::startVoice(): SoundName %s, vol:%d, pan:%d", soundName, volume, pan);
return startSound(soundName, IMUSE_VOLGRP_VOICE, 0, volume, pan, 127, nullptr);
}
void Imuse::startMusic(const char *soundName, int hookId, int volume, int pan) {
Debug::debug(Debug::Sound, "Imuse::startMusic(): SoundName %s, hookId:%d, vol:%d, pan:%d", soundName, hookId, volume, pan);
startSound(soundName, IMUSE_VOLGRP_MUSIC, hookId, volume, pan, 126, nullptr);
}
void Imuse::startMusicWithOtherPos(const char *soundName, int hookId, int volume, int pan, Track *otherTrack) {
Debug::debug(Debug::Sound, "Imuse::startMusicWithOtherPos(): SoundName %s, hookId:%d, vol:%d, pan:%d", soundName, hookId, volume, pan);
startSound(soundName, IMUSE_VOLGRP_MUSIC, hookId, volume, pan, 126, otherTrack);
}
void Imuse::startSfx(const char *soundName, int priority) {
Debug::debug(Debug::Sound, "Imuse::startSfx(): SoundName %s, priority:%d", soundName, priority);
startSound(soundName, IMUSE_VOLGRP_SFX, 0, 127, 0, priority, nullptr);
}
int32 Imuse::getPosIn16msTicks(const char *soundName) {
Common::StackLock lock(_mutex);
Track *getTrack = nullptr;
getTrack = findTrack(soundName);
// Warn the user if the track was not found
if (getTrack == nullptr) {
Debug::warning(Debug::Sound, "Sound '%s' could not be found to get ticks", soundName);
return false;
}
int32 pos = (62.5 / 60.0) * (5 * (getTrack->dataOffset + getTrack->regionOffset)) / (getTrack->feedSize / 12); // 16ms is 62.5 Hz
return pos;
}
bool Imuse::isVoicePlaying() {
Common::StackLock lock(_mutex);
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l];
if (track->used && track->volGroupId == IMUSE_VOLGRP_VOICE) {
if (g_system->getMixer()->isSoundHandleActive(track->handle))
return true;
}
}
return false;
}
bool Imuse::getSoundStatus(const char *soundName) {
Common::StackLock lock(_mutex);
Track *track = nullptr;
// If there's no name then don't try to get the status!
if (strlen(soundName) == 0)
return false;
track = findTrack(soundName);
// Warn the user if the track was not found
if (track == nullptr || !g_system->getMixer()->isSoundHandleActive(track->handle)) {
// This debug warning should be "light" since this function gets called
// on occasion to see if a sound has stopped yet
Debug::debug(Debug::Sound, "Sound '%s' could not be found to get status, assume inactive.", soundName);
return false;
}
return true;
}
void Imuse::stopSound(const char *soundName) {
Common::StackLock lock(_mutex);
Debug::debug(Debug::Sound, "Imuse::stopSound(): SoundName %s", soundName);
Track *removeTrack = nullptr;
removeTrack = findTrack(soundName);
// Warn the user if the track was not found
if (removeTrack == nullptr) {
Debug::warning(Debug::Sound, "Sound track '%s' could not be found to stop", soundName);
return;
}
flushTrack(removeTrack);
}
void Imuse::stopAllSounds() {
Common::StackLock lock(_mutex);
Debug::debug(Debug::Sound, "Imuse::stopAllSounds()");
for (int l = 0; l < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; l++) {
Track *track = _track[l];
if (track->used) {
g_system->getMixer()->stopHandle(track->handle);
if (track->soundDesc) {
_sound->closeSound(track->soundDesc);
}
track->clear();
}
}
}
void Imuse::pause(bool p) {
_pause = p;
}
} // end of namespace Grim

View File

@@ -0,0 +1,370 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "audio/decoders/raw.h"
#include "common/endian.h"
#include "common/stream.h"
#include "engines/grim/resource.h"
#include "engines/grim/imuse/imuse_sndmgr.h"
#include "engines/grim/imuse/imuse_mcmp_mgr.h"
namespace Grim {
ImuseSndMgr::ImuseSndMgr(bool demo) {
_demo = demo;
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
memset(&_sounds[l], 0, sizeof(SoundDesc));
}
}
ImuseSndMgr::~ImuseSndMgr() {
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
closeSound(&_sounds[l]);
}
}
void ImuseSndMgr::countElements(SoundDesc *sound) {
uint32 tag;
int32 size = 0;
uint32 pos = sound->inStream->pos();
do {
tag = sound->inStream->readUint32BE();
switch(tag) {
case MKTAG('T','E','X','T'):
case MKTAG('S','T','O','P'):
case MKTAG('F','R','M','T'):
size = sound->inStream->readUint32BE();
sound->inStream->seek(size, SEEK_CUR);
break;
case MKTAG('R','E','G','N'):
sound->numRegions++;
size = sound->inStream->readUint32BE();
sound->inStream->seek(size, SEEK_CUR);
break;
case MKTAG('J','U','M','P'):
sound->numJumps++;
size = sound->inStream->readUint32BE();
sound->inStream->seek(size, SEEK_CUR);
break;
case MKTAG('D','A','T','A'):
break;
default:
error("ImuseSndMgr::countElements() Unknown MAP tag '%s'", Common::tag2string(tag).c_str());
}
} while (tag != MKTAG('D','A','T','A'));
sound->inStream->seek(pos, SEEK_SET);
}
void ImuseSndMgr::parseSoundHeader(SoundDesc *sound, int &headerSize) {
Common::SeekableReadStream *data = sound->inStream;
uint32 tag = data->readUint32BE();
if (tag == MKTAG('R','I','F','F')) {
sound->region = new Region[1];
sound->jump = new Jump[1];
sound->numJumps = 0;
sound->numRegions = 1;
sound->region[0].offset = 0;
data->seek(18, SEEK_CUR);
sound->channels = data->readByte();
data->readByte();
sound->freq = data->readUint32LE();
data->seek(6, SEEK_CUR);
sound->bits = data->readByte();
data->seek(5, SEEK_CUR);
sound->region[0].length = data->readUint32LE();
headerSize = 44;
} else if (tag == MKTAG('i','M','U','S')) {
int32 size = 0;
int32 headerStart = data->pos();
data->seek(12, SEEK_CUR);
int curIndexRegion = 0;
int curIndexJump = 0;
sound->numRegions = 0;
sound->numJumps = 0;
countElements(sound);
sound->region = new Region [sound->numRegions];
sound->jump = new Jump [sound->numJumps];
do {
tag = data->readUint32BE();
switch(tag) {
case MKTAG('F','R','M','T'):
data->seek(12, SEEK_CUR);
sound->bits = data->readUint32BE();
sound->freq = data->readUint32BE();
sound->channels = data->readUint32BE();
break;
case MKTAG('T','E','X','T'):
case MKTAG('S','T','O','P'):
size = data->readUint32BE();
data->seek(size, SEEK_CUR);
break;
case MKTAG('R','E','G','N'):
data->seek(4, SEEK_CUR);
sound->region[curIndexRegion].offset = data->readUint32BE();
sound->region[curIndexRegion].length = data->readUint32BE();
curIndexRegion++;
break;
case MKTAG('J','U','M','P'):
data->seek(4, SEEK_CUR);
sound->jump[curIndexJump].offset = data->readUint32BE();
sound->jump[curIndexJump].dest = data->readUint32BE();
sound->jump[curIndexJump].hookId = data->readUint32BE();
sound->jump[curIndexJump].fadeDelay = data->readUint32BE();
curIndexJump++;
break;
case MKTAG('D','A','T','A'):
data->seek(4, SEEK_CUR);
break;
default:
error("ImuseSndMgr::prepareSound(%s) Unknown MAP tag '%s'", sound->name, Common::tag2string(tag).c_str());
}
} while (tag != MKTAG('D','A','T','A'));
headerSize = data->pos() - headerStart;
int i;
for (i = 0; i < sound->numRegions; i++) {
sound->region[i].offset -= headerSize;
}
for (i = 0; i < sound->numJumps; i++) {
sound->jump[i].offset -= headerSize;
sound->jump[i].dest -= headerSize;
}
} else {
error("ImuseSndMgr::prepareSound() Unknown sound format");
}
}
ImuseSndMgr::SoundDesc *ImuseSndMgr::allocSlot() {
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
if (!_sounds[l].inUse) {
_sounds[l].inUse = true;
return &_sounds[l];
}
}
return nullptr;
}
ImuseSndMgr::SoundDesc *ImuseSndMgr::openSound(const char *soundName, int volGroupId) {
Common::String s = soundName;
s.toLowercase();
soundName = s.c_str();
const char *extension = soundName + strlen(soundName) - 3;
int headerSize = 0;
SoundDesc *sound = allocSlot();
if (!sound) {
error("ImuseSndMgr::openSound() Can't alloc free sound slot");
}
Common::strcpy_s(sound->name, soundName);
sound->volGroupId = volGroupId;
sound->inStream = nullptr;
sound->inStream = g_resourceloader->openNewStreamFile(soundName);
if (!sound->inStream) {
closeSound(sound);
return nullptr;
}
if (!_demo && scumm_stricmp(extension, "imu") == 0) {
parseSoundHeader(sound, headerSize);
sound->mcmpData = false;
sound->headerSize = headerSize;
} else if (scumm_stricmp(extension, "wav") == 0 || scumm_stricmp(extension, "imc") == 0 ||
(_demo && scumm_stricmp(extension, "imu") == 0)) {
sound->mcmpMgr = new McmpMgr();
if (!sound->mcmpMgr->openSound(soundName, sound->inStream, headerSize)) {
closeSound(sound);
return nullptr;
}
parseSoundHeader(sound, headerSize);
sound->mcmpData = true;
} else {
error("ImuseSndMgr::openSound() Unrecognized extension for sound file %s", soundName);
}
return sound;
}
void ImuseSndMgr::closeSound(SoundDesc *sound) {
assert(checkForProperHandle(sound));
if (sound->mcmpMgr) {
delete sound->mcmpMgr;
sound->mcmpMgr = nullptr;
}
if (sound->region) {
delete[] sound->region;
sound->region = nullptr;
}
if (sound->jump) {
delete[] sound->jump;
sound->jump = nullptr;
}
if (sound->inStream) {
delete sound->inStream;
sound->inStream = nullptr;
}
memset(sound, 0, sizeof(SoundDesc));
}
ImuseSndMgr::SoundDesc *ImuseSndMgr::cloneSound(SoundDesc *sound) {
assert(checkForProperHandle(sound));
return openSound(sound->name, sound->volGroupId);
}
bool ImuseSndMgr::checkForProperHandle(SoundDesc *sound) {
if (!sound)
return false;
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
if (sound == &_sounds[l])
return true;
}
return false;
}
int ImuseSndMgr::getFreq(SoundDesc *sound) {
assert(checkForProperHandle(sound));
return sound->freq;
}
int ImuseSndMgr::getBits(SoundDesc *sound) {
assert(checkForProperHandle(sound));
return sound->bits;
}
int ImuseSndMgr::getChannels(SoundDesc *sound) {
assert(checkForProperHandle(sound));
return sound->channels;
}
bool ImuseSndMgr::isEndOfRegion(SoundDesc *sound, int region) {
assert(checkForProperHandle(sound));
assert(region >= 0 && region < sound->numRegions);
return sound->endFlag;
}
int ImuseSndMgr::getNumRegions(SoundDesc *sound) {
assert(checkForProperHandle(sound));
return sound->numRegions;
}
int ImuseSndMgr::getNumJumps(SoundDesc *sound) {
assert(checkForProperHandle(sound));
return sound->numJumps;
}
int ImuseSndMgr::getRegionOffset(SoundDesc *sound, int region) {
assert(checkForProperHandle(sound));
assert(region >= 0 && region < sound->numRegions);
return sound->region[region].offset;
}
int ImuseSndMgr::getRegionLength(SoundDesc *sound, int region) {
assert(checkForProperHandle(sound));
assert(region >= 0 && region < sound->numRegions);
return sound->region[region].length;
}
int ImuseSndMgr::getJumpIdByRegionAndHookId(SoundDesc *sound, int region, int hookId) {
assert(checkForProperHandle(sound));
assert(region >= 0 && region < sound->numRegions);
int32 offset = sound->region[region].offset;
for (int l = 0; l < sound->numJumps; l++) {
if (offset == sound->jump[l].offset) {
if (sound->jump[l].hookId == hookId)
return l;
}
}
return -1;
}
int ImuseSndMgr::getRegionIdByJumpId(SoundDesc *sound, int jumpId) {
assert(checkForProperHandle(sound));
assert(jumpId >= 0 && jumpId < sound->numJumps);
int32 dest = sound->jump[jumpId].dest;
for (int l = 0; l < sound->numRegions; l++) {
if (dest == sound->region[l].offset) {
return l;
}
}
return -1;
}
int ImuseSndMgr::getJumpHookId(SoundDesc *sound, int number) {
assert(checkForProperHandle(sound));
assert(number >= 0 && number < sound->numJumps);
return sound->jump[number].hookId;
}
int ImuseSndMgr::getJumpFade(SoundDesc *sound, int number) {
assert(checkForProperHandle(sound));
assert(number >= 0 && number < sound->numJumps);
return sound->jump[number].fadeDelay;
}
int32 ImuseSndMgr::getDataFromRegion(SoundDesc *sound, int region, byte **buf, int32 offset, int32 size, int32 *flags) {
assert(checkForProperHandle(sound));
assert(buf && offset >= 0 && size >= 0);
assert(region >= 0 && region < sound->numRegions);
int32 region_offset = sound->region[region].offset;
int32 region_length = sound->region[region].length;
if (offset + size > region_length) {
size = region_length - offset;
sound->endFlag = true;
} else {
sound->endFlag = false;
}
if (sound->mcmpData) {
size = sound->mcmpMgr->decompressSample(region_offset + offset, size, buf);
*flags |= Audio::FLAG_LITTLE_ENDIAN;
} else {
*buf = static_cast<byte *>(malloc(size));
sound->inStream->seek(region_offset + offset + sound->headerSize, SEEK_SET);
sound->inStream->read(*buf, size);
*flags &= ~Audio::FLAG_LITTLE_ENDIAN;
}
return size;
}
} // end of namespace Grim

View File

@@ -0,0 +1,118 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRIM_IMUSE_SNDMGR_H
#define GRIM_IMUSE_SNDMGR_H
#include "audio/mixer.h"
#include "audio/audiostream.h"
namespace Grim {
class McmpMgr;
class ImuseSndMgr {
public:
// MAX_IMUSE_SOUNDS needs to be hardcoded, ask aquadran
#define MAX_IMUSE_SOUNDS 16
// The numbering below fixes talking to Domino in his office
// and it also allows Manny to get the info for Mercedes
// Colomar, without this the game hangs at these points!
#define IMUSE_VOLGRP_BGND 0
#define IMUSE_VOLGRP_SFX 1
#define IMUSE_VOLGRP_VOICE 2
#define IMUSE_VOLGRP_MUSIC 3
#define IMUSE_VOLGRP_ACTION 4
private:
struct Region {
int32 offset; // offset of region
int32 length; // length 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
};
public:
struct SoundDesc {
uint16 freq; // frequency
byte channels; // stereo or mono
byte bits; // 8, 12, 16
int numJumps; // number of Jumps
int numRegions; // number of Regions
Region *region;
Jump *jump;
bool endFlag;
bool inUse;
char name[32];
McmpMgr *mcmpMgr;
int type;
int volGroupId;
bool mcmpData;
uint32 headerSize;
Common::SeekableReadStream *inStream;
};
private:
SoundDesc _sounds[MAX_IMUSE_SOUNDS];
bool _demo;
bool checkForProperHandle(SoundDesc *soundDesc);
SoundDesc *allocSlot();
void parseSoundHeader(SoundDesc *sound, int &headerSize);
void countElements(SoundDesc *sound);
public:
ImuseSndMgr(bool demo);
~ImuseSndMgr();
SoundDesc *openSound(const char *soundName, int volGroupId);
void closeSound(SoundDesc *sound);
SoundDesc *cloneSound(SoundDesc *sound);
int getFreq(SoundDesc *sound);
int getBits(SoundDesc *sound);
int getChannels(SoundDesc *sound);
bool isEndOfRegion(SoundDesc *sound, int region);
int getNumRegions(SoundDesc *sound);
int getNumJumps(SoundDesc *sound);
int getRegionOffset(SoundDesc *sound, int region);
int getRegionLength(SoundDesc *sound, int region);
int getJumpIdByRegionAndHookId(SoundDesc *sound, int region, int hookId);
int getRegionIdByJumpId(SoundDesc *sound, int jumpId);
int getJumpHookId(SoundDesc *sound, int number);
int getJumpFade(SoundDesc *sound, int number);
int32 getDataFromRegion(SoundDesc *sound, int region, byte **buf, int32 offset, int32 size, int32 *flags);
};
} // end of namespace Grim
#endif

View File

@@ -0,0 +1,290 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/scummsys.h"
#include "engines/grim/imuse/imuse_tables.h"
namespace Grim {
ImuseTable grimStateMusicTable[] = {
{0, 1000, 0, 0, 60, 127, 0, ""},
{0, 1999, 0, 0, 60, 127, 0, ""},
{3, 1001, 0, 0, 60, 127, 0, "1001 - Manny's Office.IMC" },
{3, 1002, 3, 8, 60, 20, 50, "1002 - Mr. Frustration.IMC"},
{3, 1003, 3, 8, 60, 80, 12, "1002 - Mr. Frustration.IMC"},
{3, 1004, 3, 8, 60, 90, 110, "1002 - Mr. Frustration.IMC"},
{3, 1005, 3, 8, 60, 60, 110, "1002 - Mr. Frustration.IMC"},
{3, 1006, 3, 8, 60, 80, 64, "1002 - Mr. Frustration.IMC"},
{3, 1007, 3, 8, 60, 60, 60, "1002 - Mr. Frustration.IMC"},
{3, 1008, 0, 0, 60, 127, 0, "1008 - Domino's Office.IMC"},
{3, 1009, 0, 0, 60, 127, 0, "1009 - Copal's Office.IMC"},
{3, 1010, 0, 0, 60, 127, 0, "1010 - Ledge.IMC"},
{3, 1011, 0, 0, 60, 127, 0, "1011 - Roof.IMC"},
{3, 1020, 0, 0, 60, 127, 0, "1020 - Tube Room.IMC"},
{3, 1021, 0, 0, 60, 127, 0, "1021 - Brennis.IMC"},
{3, 1022, 0, 0, 60, 127, 0, "1022 - Lobby.IMC"},
{3, 1023, 0, 0, 60, 127, 0, "1023 - Packing Room.IMC"},
{3, 1030, 0, 0, 60, 127, 0, "1030 - Garage.IMC"},
{3, 1031, 0, 0, 60, 127, 0, "1031 - Glottis' Shop.IMC"},
{3, 1032, 0, 0, 60, 127, 0, "1030 - Garage.IMC"},
{3, 1040, 21, 4, 30, 60, 0, "1040 - Festival Wet.IMC"},
{3, 1041, 21, 4, 12, 127, 0, "1041 - Festival Dry.IMC"},
{3, 1042, 21, 4, 12, 60, 0, "1041 - Festival Dry.IMC"},
{3, 1043, 21, 4, 12, 46, 0, "1041 - Festival Dry.IMC"},
{3, 1044, 21, 4, 12, 87, 0, "1040 - Festival Wet.IMC"},
{3, 1050, 0, 0, 60, 127, 0, "1050 - Headquarters.IMC"},
{2, 1060, 0, 0, 20, 80, 0, "1060 - Real World.IMC"},
{2, 1070, 0, 0, 60, 127, 0, "1070 - Stump Room.IMC"},
{2, 1071, 0, 3, 60, 127, 0, "1071 - Signpost Room.IMC"},
{2, 1072, 0, 0, 60, 127, 0, "1072 - Navigation.IMC"},
{2, 1073, 0, 3, 60, 127, 0, "1071 - Signpost Room.IMC"},
{2, 1074, 0, 0, 60, 127, 0, "1074 - Bone Wagon.IMC"},
{2, 1075, 0, 0, 60, 127, 0, "1075 - Spider's Eye.IMC"},
{2, 1076, 0, 0, 60, 127, 0, "1076 - Spider Room.IMC"},
{2, 1077, 0, 0, 60, 127, 0, "1077 - Tree Pump Amb.IMC"},
{2, 1078, 0, 0, 60, 127, 0, "1078 - Tree Pump.IMC"},
{2, 1079, 0, 3, 60, 127, 0, "1071 - Signpost Room.IMC"},
{2, 1080, 0, 0, 60, 127, 0, "1080 - Beaver Room Lobby.IMC"},
{2, 1081, 0, 0, 60, 127, 0, "1081 - Beaver Dam.IMC"},
{2, 1082, 0, 0, 60, 127, 0, "1083 - Beaver Room.IMC"},
{2, 1083, 0, 0, 60, 127, 0, "1083 - Beaver Room.IMC"},
{2, 1084, 0, 0, 60, 80, 0, "1084 - Foggy Cactus.IMC"},
{2, 1085, 0, 0, 60, 105, 0, "1085 - Rubamat Exterior.IMC"},
{2, 1086, 0, 4, 60, 80, 30, "1087 - Blue Hector.IMC"},
{2, 1100, 0, 0, 60, 127, 0, "1109 - Cafe Exterior.IMC"},
{3, 1101, 45, 5, 24, 60, 0, "1101 - Cafe Office.IMC"},
{3, 1102, 45, 0, 24, 127, 0, "1102 - Cafe Intercom.IMC"},
{3, 1103, 49, 4, 24, 60, 100, "1103 - Coat Check.IMC"},
{2, 1104, 0, 0, 20, 127, 0, "1104 - Lupe.IMC"},
{3, 1105, 49, 4, 24, 127, 75, "1106 - Glottis Noodle.IMC"},
{3, 1106, 49, 4, 24, 127, 75, "1106 - Glottis Noodle.IMC"},
{3, 1190, 49, 4, 24, 80, 45, "1106 - Glottis Noodle.IMC"},
{3, 1191, 49, 4, 24, 127, 127, "1106 - Glottis Noodle.IMC"},
{3, 1107, 45, 5, 24, 127, 0, "1101 - Cafe Office.IMC"},
{3, 1108, 45, 6, 24, 127, 0, "1108 - Casino Interior.IMC"},
{2, 1109, 0, 0, 24, 127, 0, "1109 - Cafe Exterior.IMC"},
{2, 1110, 0, 0, 24, 105, 0, "1110 - Cafe Ledge.IMC"},
{3, 1111, 0, 0, 24, 60, 0, "1108 - Casino Interior.IMC"},
{2, 1112, 0, 0, 24, 127, 68, "1112 - Rusty Sans Vox.IMC"},
{2, 1120, 0, 0, 60, 127, 0, "1120 - Elevator Station.IMC"},
{3, 1121, 0, 0, 24, 127, 0, "1122 - Blue Exterior.IMC"},
{3, 1122, 0, 0, 24, 127, 0, "1122 - Blue Exterior.IMC"},
{2, 1123, 0, 4, 41, 127, 0, "1123 - Blue Casket Ins.IMC"},
{2, 1124, 0, 0, 60, 127, 0, "1124 - Blue Casket Amb.IMC"},
{2, 1125, 0, 4, 60, 80, 75, "1125 - Smooth Hector.IMC"},
{3, 1126, 0, 0, 24, 127, 0, "1122 - Blue Exterior.IMC"},
{3, 1127, 66, 0, 60, 127, 0, "1127 - Limbo Dock.IMC"},
{2, 1128, 0, 0, 24, 127, 0, "1128 - Limbo Talk.IMC"},
{3, 1129, 66, 0, 60, 127, 0, "1129 - Limbo Poem.IMC"},
{2, 1130, 0, 0, 24, 127, 0, "1130 - Dry Dock.IMC"},
{2, 1131, 0, 0, 24, 127, 0, "1131 - Dry Dock Strike.IMC"},
{2, 1132, 0, 0, 60, 127, 0, "1132 - Lighthouse Ext.IMC"},
{2, 1133, 0, 0, 60, 127, 0, "1133 - Lola's Last.IMC"},
{3, 1140, 0, 0, 60, 127, 0, "1140 - Police Station.IMC"},
{2, 1141, 0, 0, 60, 105, 0, "1141 - Police Interior.IMC"},
{2, 1142, 0, 0, 60, 105, 0, "1141 - Police Interior.IMC"},
{2, 1143, 0, 0, 60, 105, 0, "1143 - Morgue.IMC"},
{3, 1144, 0, 0, 60, 105, 0, "1140 - Police Station.IMC"},
{2, 1145, 0, 0, 60, 127, 0, "1145 - Bridge Blimp.IMC"},
{2, 1146, 0, 0, 60, 80, 0, "1146 - LOL Security Ext.IMC"},
{2, 1147, 0, 0, 60, 127, 0, "1147 - LOL Security Int.IMC"},
{2, 1148, 0, 0, 60, 127, 0, "1148 - Carla's Life.IMC"},
{2, 1149, 0, 0, 24, 127, 0, "1149 - Bomb.IMC"},
{3, 1150, 83, 0, 60, 105, 0, "1152 - Track Stairs.IMC"},
{3, 1151, 83, 0, 60, 105, 0, "1152 - Track Stairs.IMC"},
{3, 1152, 83, 0, 60, 105, 0, "1152 - Track Stairs.IMC"},
{2, 1153, 0, 0, 24, 127, 0, "1153 - Track Base.IMC"},
{2, 1154, 0, 0, 60, 127, 0, "1154 - Kitty Hall.IMC"},
{2, 1155, 0, 0, 60, 127, 0, "1155 - Sanspoof.IMC"},
{2, 1156, 0, 0, 60, 127, 0, "1156 - Kitty Stables.IMC"},
{2, 1160, 0, 0, 60, 127, 0, "1160 - High Roller Hall.IMC"},
{3, 1161, 0, 0, 24, 127, 0, "1161 - High Roller Lnge.IMC"},
{2, 1162, 0, 0, 60, 127, 0, "1162 - Glottis Gambling.IMC"},
{3, 1163, 0, 0, 24, 127, 0, "1163 - Max's Office.IMC"},
{3, 1164, 94, 4, 24, 127, 80, "1125 - Hector Steps Out.IMC"},
{3, 1165, 94, 4, 24, 20, 64, "1125 - Hector Steps Out.IMC"},
{3, 1166, 94, 4, 24, 127, 10, "1125 - Hector Steps Out.IMC"},
{3, 1167, 0, 0, 60, 127, 0, "1167 - Dillopede Elev.IMC"},
{3, 1168, 0, 0, 60, 127, 0, "1167 - Dillopede Elev.IMC"},
{3, 1169, 0, 0, 60, 127, 0, "1167 - Dillopede Elev.IMC"},
{3, 1170, 0, 0, 24, 105, 0, "1170 - Extendo Bridge.IMC"},
{3, 1171, 0, 0, 24, 105, 0, "1170 - Extendo Bridge.IMC"},
{3, 1172, 0, 0, 24, 105, 0, "1170 - Extendo Bridge.IMC"},
{2, 1173, 0, 0, 24, 127, 0, "1173 - Scrimshaw Int.IMC"},
{2, 1174, 0, 0, 24, 127, 0, "1174 - Scrim Sleep.IMC"},
{2, 1180, 0, 0, 60, 72, 0, "1180 - Note to Manny.IMC"},
{2, 1181, 0, 0, 60, 127, 0, "1155 - Sanspoof.IMC"},
{2, 1201, 0, 0, 60, 127, 0, "1201 - Lola Zapata.IMC"},
{2, 1202, 0, 0, 60, 127, 0, "1202 - Inside the Lola.IMC"},
{2, 1203, 0, 0, 60, 127, 0, "1203 - Engine Room.IMC"},
{2, 1204, 0, 0, 60, 127, 0, "1204 - Porthole.IMC"},
{2, 1205, 0, 0, 60, 127, 0, "1204 - Porthole.IMC"},
{2, 1210, 0, 0, 60, 127, 0, "1210 - Sunken Lola.IMC"},
{2, 1211, 0, 0, 60, 127, 0, "1211 - Pearl Crater Sub.IMC"},
{3, 1220, 0, 0, 60, 127, 0, "1220 - Miner's Room.IMC"},
{3, 1221, 0, 0, 60, 127, 0, "1221 - Miner's Room.IMC"},
{2, 1222, 0, 0, 60, 127, 0, "1222 - Exterior Airlock.IMC"},
{2, 1223, 0, 4, 60, 127, 0, "1223 - Factory Hub.IMC"},
{2, 1224, 0, 0, 60, 127, 0, "1224 - Foreman's Office.IMC"},
{2, 1230, 0, 0, 60, 127, 0, "1230 - Vault Door.IMC"},
{2, 1231, 0, 0, 60, 127, 0, "1231 - Outer Vault.IMC"},
{2, 1232, 0, 0, 60, 127, 0, "1232 - Inner Vault.IMC"},
{2, 1233, 0, 0, 60, 127, 0, "1233 - Ashtray Room.IMC"},
{2, 1234, 0, 0, 60, 127, 0, "1234 - Ashtray Scary.IMC"},
{2, 1235, 0, 0, 60, 127, 0, "1235 - Ashtray Pensive.IMC"},
{2, 1236, 0, 0, 60, 127, 0, "1236 - Domino's Room.IMC"},
{3, 1240, 0, 4, 60, 127, 0, "1240 - Conveyor Under.IMC"},
{3, 1241, 0, 0, 60, 127, 0, "1240 - Conveyor Under.IMC"},
{2, 1242, 0, 0, 60, 127, 0, "1241 - Crane Intro.IMC"},
{3, 1243, 0, 0, 60, 127, 0, "1243 - Anchor Room.IMC"},
{2, 1244, 0, 0, 60, 127, 0, "1244 - Glottis Hanging.IMC"},
{2, 1245, 0, 0, 60, 127, 0, "1245 - End of the World.IMC"},
{2, 1246, 0, 0, 60, 127, 0, "1246 - End World Later.IMC"},
{2, 1247, 0, 0, 60, 127, 0, "1241 - Crane Intro.IMC"},
{3, 1250, 0, 0, 60, 127, 0, "1250 - Upper Beach.IMC"},
{3, 1251, 0, 0, 60, 127, 0, "1250 - Upper Beach.IMC"},
{3, 1252, 0, 0, 60, 127, 0, "1252 - Lower Beach Boat.IMC"},
{2, 1253, 0, 0, 60, 127, 0, "1253 - Lamancha Sub.IMC"},
{2, 1254, 0, 0, 60, 127, 0, "1254 - Crane Later.IMC"},
{3, 1301, 0, 0, 60, 127, 0, "1301 - Temple Gate.IMC"},
{3, 1302, 0, 0, 60, 127, 0, "1301 - Temple Gate.IMC"},
{2, 1303, 0, 0, 60, 105, 0, "1303 - Truck Depot.IMC"},
{3, 1304, 143, 0, 60, 127, 0, "1304 - Mayan Train Sta.IMC"},
{2, 1305, 0, 0, 60, 127, 0, "1305 - Mayan Workshop.IMC"},
{3, 1306, 143, 0, 60, 127, 0, "1306 - Mayan Train Pad.IMC"},
{2, 1307, 0, 4, 60, 40, 64, "1307 - Mechanic's Kitch.IMC"},
{3, 1310, 0, 0, 60, 127, 0, "1310 - Jello Bomb.IMC"},
{3, 1311, 0, 0, 60, 127, 0, "1310 - Jello Bomb.IMC"},
{3, 1312, 150, 4, 12, 20, 64, "1125 - Smooth Hector.IMC"},
{3, 1313, 150, 4, 12, 50, 32, "1125 - Smooth Hector.IMC"},
{3, 1314, 150, 4, 12, 105, 75, "1125 - Smooth Hector.IMC"},
{3, 1315, 0, 0, 60, 127, 0, "1122 - Blue Exterior.IMC"},
{3, 1316, 0, 0, 60, 127, 0, "1122 - Blue Exterior.IMC"},
{3, 1317, 0, 0, 60, 127, 0, "1122 - Blue Exterior.IMC"},
{3, 1318, 0, 0, 60, 127, 0, "1332 - Hector's Foyer.IMC"},
{2, 1319, 0, 0, 60, 127, 0, "1319 - Florist Video.IMC"},
{2, 1320, 0, 0, 60, 127, 0, "1320 - New LSA HQ.IMC"},
{3, 1321, 0, 0, 60, 127, 0, "1321 - LSA Sewer.IMC"},
{3, 1322, 0, 0, 60, 127, 0, "1321 - LSA Sewer.IMC"},
{3, 1323, 0, 2, 60, 127, 0, "1323 - Sewer Maze.IMC"},
{3, 1324, 0, 0, 60, 127, 0, "1324 - Albinozod.IMC"},
{3, 1325, 162, 0, 60, 127, 0, "1325 - Florist Shop.IMC"},
{3, 1326, 162, 0, 60, 127, 0, "1326 - Florist Shop Int.IMC"},
{2, 1327, 0, 0, 60, 127, 0, "1327 - Florist OK.IMC"},
{3, 1328, 0, 2, 60, 127, 0, "1323 - Sewer Maze.IMC"},
{2, 1329, 0, 4, 60, 127, 0, "1329 - Theater Backstag.IMC"},
{2, 1330, 0, 0, 60, 127, 0, "1330 - Lemans Lobby.IMC"},
{2, 1331, 0, 0, 60, 60, 0, "1330 - Lemans Lobby.IMC"},
{3, 1332, 0, 0, 60, 127, 0, "1332 - Hector's Foyer.IMC"},
{2, 1333, 0, 0, 60, 127, 0, "1333 - Brennis Talk.IMC"},
{3, 1334, 0, 0, 60, 127, 0, "1334 - Albino Trap.IMC"},
{2, 1340, 0, 0, 60, 127, 0, "1342 - Neon Ledge.IMC"},
{2, 1350, 0, 0, 60, 127, 0, "1350 - Meadow Flowers.IMC"},
{2, 1351, 0, 0, 60, 127, 0, "1351 - Meadow.IMC"},
{2, 1352, 0, 0, 60, 127, 0, "1352 - Car Head.IMC"},
{2, 1353, 0, 0, 60, 127, 0, "1353 - Greenhouse Appr.IMC"},
{2, 1354, 0, 0, 60, 127, 0, "1354 - Game Ending.IMC"},
{2, 1355, 0, 0, 60, 127, 0, "1355 - Shootout.IMC"},
{2, 1400, 0, 0, 1, 105, 0, "1400 - Start Credits.IMC"},
{2, 1401, 0, 0, 60, 127, 0, "1401 - Smooth Hector.IMC"},
{0, 1500, 0, 0, 60, 127, 0, ""},
{0, -1, 0, 0, 0, 0, 0, ""}
};
ImuseTable grimSeqMusicTable[] = {
{0, 2000, 0, 0, 20, 127, 0, ""},
{2, 2001, 0, 0, 20, 127, 0, "2001 - Climb Rope.IMC"},
{2, 2010, 0, 0, 20, 127, 0, "2010 - Glottis OK.IMC"},
{2, 2020, 0, 0, 20, 127, 0, "2020 - Reap Bruno.IMC"},
{2, 2030, 0, 0, 20, 127, 0, "2030 - Ledgepeckers.IMC"},
{2, 2050, 0, 0, 20, 105, 0, "2050 - Glottis Heart.IMC"},
{2, 2055, 0, 0, 20, 127, 0, "2055 - Slingshot Bone.IMC"},
{2, 2060, 0, 0, 20, 127, 0, "2060 - Glott Tree Fall.IMC"},
{2, 2070, 0, 0, 20, 127, 0, "2070 - Beaver Fly.IMC"},
{2, 2071, 0, 0, 20, 127, 0, "2071 - Beaver Sink.IMC"},
{2, 2080, 0, 0, 20, 127, 0, "2080 - Meet Velasco.IMC"},
{2, 2140, 0, 0, 20, 127, 75, "2140 - Ooo Bonewagon.IMC"},
{2, 2141, 0, 0, 20, 127, 75, "2141 - Ooo Meche.IMC"},
{2, 2155, 0, 0, 20, 127, 0, "2155 - Find Detector.IMC"},
{2, 2156, 0, 0, 20, 127, 0, "2156 - Glott Drink Wine.IMC"},
{2, 2157, 0, 0, 20, 127, 0, "2157 - Glott No Wine.IMC"},
{2, 2161, 0, 0, 20, 127, 0, "2161 - Raoul Appears.IMC"},
{2, 2162, 0, 0, 20, 127, 0, "2162 - Raoul KO.IMC"},
{2, 2163, 0, 0, 20, 127, 0, "2163 - Raoul Dissed.IMC"},
{2, 2165, 0, 0, 20, 127, 0, "2165 - Fake Tix.IMC"},
{2, 2180, 0, 0, 20, 127, 0, "2180 - Befriend Commies.IMC"},
{2, 2186, 0, 0, 20, 127, 0, "2186 - Nick Punchout.IMC"},
{2, 2200, 0, 0, 20, 127, 0, "2200 - Year 3 Iris.IMC"},
{2, 2210, 0, 0, 41, 127, 0, "2210 - Hit Men.IMC"},
{2, 2230, 0, 0, 20, 127, 0, "2230 - Open Vault.IMC"},
{2, 2235, 0, 0, 20, 127, 0, "2235 - Dead Tix.IMC"},
{2, 2240, 0, 0, 30, 127, 0, "2240 - Sprinkler.IMC"},
{2, 2250, 0, 0, 20, 127, 0, "2250 - Crane Track.IMC"},
{2, 2255, 0, 0, 20, 127, 0, "2255 - Crane Fall.IMC"},
{2, 2300, 0, 0, 20, 127, 0, "2300 - Yr 4 Iris.IMC"},
{2, 2301, 0, 0, 20, 127, 0, "2301 - Pop Bruno Casket.IMC"},
{2, 2310, 0, 0, 20, 127, 0, "2310 - Rocket Idea.IMC"},
{2, 2320, 0, 0, 20, 127, 0, "2320 - Jello Suspense.IMC"},
{2, 2325, 0, 0, 20, 127, 0, "2325 - Lumbago Lemo.IMC"},
{2, 2327, 0, 0, 20, 127, 0, "2327 - Breath Mint.IMC"},
{2, 2330, 0, 0, 20, 127, 0, "2330 - Pigeon Fly.IMC"},
{2, 2340, 0, 0, 20, 127, 0, "2340 - Coffee On Boys.IMC"},
{2, 2350, 0, 0, 20, 127, 0, "2350 - Sprout Aha.IMC"},
{2, 2360, 0, 0, 20, 127, 0, "2360 - Chowchilla Bye.IMC"},
{2, 2370, 0, 0, 20, 127, 0, "2370 - Salvador Death.IMC"},
{2, 2399, 0, 0, 12, 127, 0, "2399 - End Credits.IMC"},
{0, -1, 0, 0, 0, 0, 0, ""}
};
ImuseTable grimDemoStateMusicTable[] = {
{0, 0, 0, 0, 60, 127, 0, ""},
{0, 1000, 0, 0, 60, 127, 0, ""},
{3, 1001, 0, 0, 60, 127, 0, "MO - Manny's Office.IMC"},
{3, 1002, 0, 0, 60, 127, 0, "MO - Manny's Office.IMC"},
{3, 1003, 0, 0, 60, 127, 0, "MO - Manny's Office.IMC"},
{3, 1004, 0, 0, 60, 127, 0, "DO - Domino's Office.IMC"},
{3, 1005, 0, 0, 60, 127, 0, "CO - Copal's Office.IMC"},
{3, 1010, 0, 0, 60, 127, 0, "LE - Ledge.IMC"},
{3, 1011, 0, 0, 60, 127, 0, "RF - Roof.IMC"},
{3, 1020, 0, 0, 60, 127, 0, "TU - Tube Room.IMC"},
{3, 1021, 0, 0, 60, 127, 0, "LO - Lobby.IMC"},
{3, 1022, 0, 0, 60, 127, 0, "PK - Packing Room.IMC"},
{3, 1030, 0, 0, 60, 127, 0, "PK - Packing Room.IMC"},
{3, 1031, 0, 0, 60, 127, 0, "PK - Packing Room.IMC"},
{3, 1040, 1, 0, 5, 60, 0, "Festival Wet.IMC"},
{3, 1041, 1, 0, 60, 127, 0, "Festival Dry.IMC"},
{3, 1042, 1, 0, 5, 50, 0, "Festival Dry.IMC"},
{3, 1043, 1, 0, 5, 64, 0, "Festival Dry.IMC"},
{3, 1044, 1, 0, 5, 75, 0, "Festival Wet.IMC"},
{3, 1050, 0, 0, 60, 127, 0, "HQ - Headquarters.IMC"},
{3, 1060, 0, 0, 60, 127, 0, ""},
{0, 5000, 0, 0, 60, 127, 0, ""},
{0, -1, 0, 0, 0, 0, 0, ""}
};
ImuseTable grimDemoSeqMusicTable[] = {
{0, 2000, 0, 0, 60, 127, 0, ""},
{3, 2100, 0, 0, 60, 127, 0, "Rope Climb.IMC"},
{0, -1, 0, 0, 0, 0, 0, ""}
};
} // end of namespace Grim

View File

@@ -0,0 +1,40 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRIM_IMUSE_TABLES_H
#define GRIM_IMUSE_TABLES_H
namespace Grim {
struct ImuseTable {
byte opcode;
int16 soundId;
byte atribPos;
byte hookId;
int16 fadeOut60TicksDelay;
byte volume;
byte pan;
char filename[32];
};
} // end of namespace Grim
#endif

View File

@@ -0,0 +1,411 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/textconsole.h"
#include "engines/grim/debug.h"
#include "engines/grim/imuse/imuse.h"
namespace Grim {
int Imuse::allocSlot(int priority) {
int l, lowest_priority = 127;
int trackId = -1;
// allocSlot called by startSound so no locking is necessary
for (l = 0; l < MAX_IMUSE_TRACKS; l++) {
if (!_track[l]->used) {
trackId = l;
break;
}
}
if (trackId == -1) {
Debug::warning(Debug::Sound, "Imuse::startSound(): All slots are full");
for (l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l];
if (track->used && !track->toBeRemoved &&
(lowest_priority > track->priority)) {
lowest_priority = track->priority;
trackId = l;
}
}
if (lowest_priority <= priority) {
assert(trackId != -1);
Track *track = _track[trackId];
// Stop the track immediately
g_system->getMixer()->stopHandle(track->handle);
if (track->soundDesc) {
_sound->closeSound(track->soundDesc);
}
// Mark it as unused
track->clear();
} else {
return -1;
}
}
return trackId;
}
bool Imuse::startSound(const char *soundName, int volGroupId, int hookId, int volume, int pan, int priority, Track *otherTrack) {
Common::StackLock lock(_mutex);
Track *track = nullptr;
int i;
// If the track is fading out bring it back to the normal running tracks
for (i = MAX_IMUSE_TRACKS; i < MAX_IMUSE_TRACKS + MAX_IMUSE_FADETRACKS; i++) {
if (!scumm_stricmp(_track[i]->soundName, soundName) && !_track[i]->toBeRemoved) {
Track *fadeTrack = _track[i];
track = _track[i - MAX_IMUSE_TRACKS];
if (track->used) {
flushTrack(track);
g_system->getMixer()->stopHandle(track->handle);
}
// Clone the settings of the given track
memcpy(track, fadeTrack, sizeof(Track));
track->trackId = i - MAX_IMUSE_TRACKS;
// Reset the track
fadeTrack->clear();
// Mark as used for now so the track won't be reused again this frame
track->used = true;
return true;
}
}
// If the track is already playing then there is absolutely no
// reason to start it again, the existing track should be modified
// instead of starting a new copy of the track
for (i = 0; i < MAX_IMUSE_TRACKS; i++) {
// Filenames are case insensitive, see findTrack
if (!scumm_stricmp(_track[i]->soundName, soundName)) {
Debug::debug(Debug::Sound, "Imuse::startSound(): Track '%s' already playing.", soundName);
return true;
}
}
// Priority Level 127 appears to mean "load but don't play", so
// within our paradigm this is a much lower priority than everything
// else we're doing
if (priority == 127)
priority = -1;
int l = allocSlot(priority);
if (l == -1) {
Debug::warning(Debug::Sound, "Imuse::startSound() Can't start sound - no free slots");
return false;
}
track = _track[l];
// Reset the track
track->clear();
track->pan = pan * 1000;
track->vol = volume * 1000;
track->volGroupId = volGroupId;
track->curHookId = hookId;
track->priority = priority;
track->curRegion = -1;
track->trackId = l;
Common::strcpy_s(track->soundName, soundName);
track->soundDesc = _sound->openSound(soundName, volGroupId);
if (!track->soundDesc)
return false;
const int bits = _sound->getBits(track->soundDesc);
const int channels = _sound->getChannels(track->soundDesc);
const int freq = _sound->getFreq(track->soundDesc);
assert(bits == 8 || bits == 12 || bits == 16);
assert(channels == 1 || channels == 2);
assert(0 < freq && freq <= 65535);
(void)bits;
track->feedSize = freq * channels * 2;
track->mixerFlags = kFlag16Bits;
if (channels == 2)
track->mixerFlags |= kFlagStereo | kFlagReverseStereo;
if (otherTrack && otherTrack->used && !otherTrack->toBeRemoved) {
track->curRegion = otherTrack->curRegion;
track->dataOffset = otherTrack->dataOffset;
track->regionOffset = otherTrack->regionOffset;
}
track->stream = Audio::makeQueuingAudioStream(freq, track->mixerFlags & kFlagStereo);
g_system->getMixer()->playStream(track->getType(), &track->handle, track->stream, -1,
track->getVol(), track->getPan(), DisposeAfterUse::YES,
false, (track->mixerFlags & kFlagReverseStereo) != 0);
track->used = true;
return true;
}
Track *Imuse::findTrack(const char *soundName) {
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l];
// Since the audio (at least for Eva's keystrokes) can be referenced
// two ways: keyboard.IMU and keyboard.imu, make a case insensitive
// search for the track to make sure we can find it
if (track->used && !track->toBeRemoved
&& strlen(track->soundName) != 0 && scumm_stricmp(track->soundName, soundName) == 0) {
return track;
}
}
return nullptr;
}
void Imuse::setPriority(const char *soundName, int priority) {
Common::StackLock lock(_mutex);
Track *changeTrack = nullptr;
assert ((priority >= 0) && (priority <= 127));
changeTrack = findTrack(soundName);
// Check to make sure we found the track
if (changeTrack == nullptr) {
Debug::warning(Debug::Sound, "Unable to find track '%s' to change priority", soundName);
return;
}
changeTrack->priority = priority;
}
void Imuse::setVolume(const char *soundName, int volume) {
Common::StackLock lock(_mutex);
Track *changeTrack;
changeTrack = findTrack(soundName);
if (changeTrack == nullptr) {
Debug::warning(Debug::Sound, "Unable to find track '%s' to change volume", soundName);
return;
}
changeTrack->vol = volume * 1000;
}
void Imuse::setPan(const char *soundName, int pan) {
Common::StackLock lock(_mutex);
Track *changeTrack;
changeTrack = findTrack(soundName);
if (changeTrack == nullptr) {
Debug::warning(Debug::Sound, "Unable to find track '%s' to change pan", soundName);
return;
}
changeTrack->pan = pan * 1000;
}
int Imuse::getVolume(const char *soundName) {
Common::StackLock lock(_mutex);
Track *getTrack;
getTrack = findTrack(soundName);
if (getTrack == nullptr) {
Debug::warning(Debug::Sound, "Unable to find track '%s' to get volume", soundName);
return 0;
}
return getTrack->vol / 1000;
}
void Imuse::setHookId(const char *soundName, int hookId) {
Common::StackLock lock(_mutex);
Track *changeTrack;
changeTrack = findTrack(soundName);
if (changeTrack == nullptr) {
Debug::warning(Debug::Sound, "Unable to find track '%s' to change hook id", soundName);
return;
}
changeTrack->curHookId = hookId;
}
int Imuse::getCountPlayedTracks(const char *soundName) {
Common::StackLock lock(_mutex);
int count = 0;
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l];
if (track->used && !track->toBeRemoved && (scumm_stricmp(track->soundName, soundName) == 0)) {
count++;
}
}
return count;
}
void Imuse::selectVolumeGroup(const char *soundName, int volGroupId) {
Common::StackLock lock(_mutex);
Track *changeTrack;
assert((volGroupId >= 1) && (volGroupId <= 4));
if (volGroupId == 4)
volGroupId = 3;
changeTrack = findTrack(soundName);
if (changeTrack == nullptr) {
Debug::warning(Debug::Sound, "Unable to find track '%s' to change volume group id", soundName);
return;
}
changeTrack->volGroupId = volGroupId;
}
void Imuse::setFadeVolume(const char *soundName, int destVolume, int duration) {
Common::StackLock lock(_mutex);
Track *changeTrack;
changeTrack = findTrack(soundName);
if (changeTrack == nullptr) {
Debug::warning(Debug::Sound, "Unable to find track '%s' to change fade volume", soundName);
return;
}
changeTrack->volFadeDelay = duration;
changeTrack->volFadeDest = destVolume * 1000;
changeTrack->volFadeStep = (changeTrack->volFadeDest - changeTrack->vol) * 60 * (1000 / _callbackFps) / (1000 * duration);
changeTrack->volFadeUsed = true;
}
void Imuse::setFadePan(const char *soundName, int destPan, int duration) {
Common::StackLock lock(_mutex);
Track *changeTrack;
changeTrack = findTrack(soundName);
if (changeTrack == nullptr) {
Debug::warning(Debug::Sound, "Unable to find track '%s' to change fade pan", soundName);
return;
}
changeTrack->panFadeDelay = duration;
changeTrack->panFadeDest = destPan * 1000;
changeTrack->panFadeStep = (changeTrack->panFadeDest - changeTrack->pan) * 60 * (1000 / _callbackFps) / (1000 * duration);
changeTrack->panFadeUsed = true;
}
char *Imuse::getCurMusicSoundName() {
Common::StackLock lock(_mutex);
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l];
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
return track->soundName;
}
}
return nullptr;
}
int Imuse::getCurMusicPan() {
Common::StackLock lock(_mutex);
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l];
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
return track->pan / 1000;
}
}
return 0;
}
int Imuse::getCurMusicVol() {
Common::StackLock lock(_mutex);
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l];
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
return track->vol / 1000;
}
}
return 0;
}
void Imuse::fadeOutMusic(int duration) {
Common::StackLock lock(_mutex);
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l];
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
cloneToFadeOutTrack(track, duration);
flushTrack(track);
return;
}
}
}
void Imuse::fadeOutMusicAndStartNew(int fadeDelay, const char *filename, int hookId, int vol, int pan) {
Common::StackLock lock(_mutex);
for (int l = 0; l < MAX_IMUSE_TRACKS; l++) {
Track *track = _track[l];
if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
startMusicWithOtherPos(filename, 0, vol, pan, track);
cloneToFadeOutTrack(track, fadeDelay);
flushTrack(track);
break;
}
}
}
Track *Imuse::cloneToFadeOutTrack(Track *track, int fadeDelay) {
assert(track);
Track *fadeTrack;
if (track->toBeRemoved) {
error("cloneToFadeOutTrack: Tried to clone a track to be removed, please bug report");
return nullptr;
}
assert(track->trackId < MAX_IMUSE_TRACKS);
fadeTrack = _track[track->trackId + MAX_IMUSE_TRACKS];
if (fadeTrack->used) {
flushTrack(fadeTrack);
g_system->getMixer()->stopHandle(fadeTrack->handle);
}
// Clone the settings of the given track
memcpy(fadeTrack, track, sizeof(Track));
fadeTrack->trackId = track->trackId + MAX_IMUSE_TRACKS;
// Clone the sound.
// leaving bug number for now #3005
ImuseSndMgr::SoundDesc *soundDesc = _sound->cloneSound(track->soundDesc);
assert(soundDesc);
track->soundDesc = soundDesc;
// Set the volume fading parameters to indicate a fade out
fadeTrack->volFadeDelay = fadeDelay;
fadeTrack->volFadeDest = 0;
fadeTrack->volFadeStep = (fadeTrack->volFadeDest - fadeTrack->vol) * 60 * (1000 / _callbackFps) / (1000 * fadeDelay);
fadeTrack->volFadeUsed = true;
// Create an appendable output buffer
fadeTrack->stream = Audio::makeQueuingAudioStream(_sound->getFreq(fadeTrack->soundDesc), track->mixerFlags & kFlagStereo);
g_system->getMixer()->playStream(track->getType(), &fadeTrack->handle, fadeTrack->stream, -1, fadeTrack->getVol(),
fadeTrack->getPan(), DisposeAfterUse::YES, false,
(track->mixerFlags & kFlagReverseStereo) != 0);
fadeTrack->used = true;
return fadeTrack;
}
} // end of namespace Grim

View File

@@ -0,0 +1,125 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRIM_IMUSE_TRACK_H
#define GRIM_IMUSE_TRACK_H
#include "engines/grim/imuse/imuse_sndmgr.h"
namespace Grim {
enum {
kFlagUnsigned = 1 << 0,
kFlag16Bits = 1 << 1,
kFlagLittleEndian = 1 << 2,
kFlagStereo = 1 << 3,
kFlagReverseStereo = 1 << 4
};
struct Track {
int trackId;
int32 pan;
int32 panFadeDest;
int32 panFadeStep;
int32 panFadeDelay;
bool panFadeUsed;
int32 vol;
int32 volFadeDest;
int32 volFadeStep;
int32 volFadeDelay;
bool volFadeUsed;
char soundName[32];
bool used;
bool toBeRemoved;
int32 priority;
int32 regionOffset;
int32 dataOffset;
int32 curRegion;
int32 curHookId;
int32 volGroupId;
int32 feedSize;
int32 mixerFlags;
ImuseSndMgr::SoundDesc *soundDesc;
Audio::SoundHandle handle;
Audio::QueuingAudioStream *stream;
Track() {
clear();
}
void clear() {
trackId = 0;
pan = 0;
panFadeDest = 0;
panFadeStep = 0;
panFadeDelay = 0;
panFadeUsed = 0;
vol = 0;
volFadeDest = 0;
volFadeStep = 0;
volFadeDelay = 0;
volFadeUsed = 0;
for (uint i = 0; i < ARRAYSIZE(soundName); i++) {
soundName[i] = 0;
}
used = false;
toBeRemoved = false;
priority = 0;
regionOffset = 0;
dataOffset = 0;
curRegion = 0;
curHookId = 0;
volGroupId = 0;
feedSize = 0;
mixerFlags = 0;
soundDesc = nullptr;
// handle not cleared. FIXME: Clear by resetting _val to default (0xFFFFFFFF not 0)?
stream = nullptr;
}
/* getPan() returns -127 ... 127 */
int getPan() const { return (pan != 64000) ? 2 * (pan / 1000) - 127 : 0; }
int getVol() const { return vol / 1000; }
Audio::Mixer::SoundType getType() const {
Audio::Mixer::SoundType type = Audio::Mixer::kPlainSoundType;
if (volGroupId == IMUSE_VOLGRP_VOICE)
type = Audio::Mixer::kSpeechSoundType;
else if (volGroupId == IMUSE_VOLGRP_SFX)
type = Audio::Mixer::kSFXSoundType;
else if (volGroupId == IMUSE_VOLGRP_MUSIC)
type = Audio::Mixer::kMusicSoundType;
else if (volGroupId == IMUSE_VOLGRP_BGND)
type = Audio::Mixer::kPlainSoundType;
else if (volGroupId == IMUSE_VOLGRP_ACTION)
type = Audio::Mixer::kPlainSoundType;
return type;
}
};
} // end of namespace Grim
#endif