Initial commit
This commit is contained in:
375
engines/grim/imuse/imuse.cpp
Normal file
375
engines/grim/imuse/imuse.cpp
Normal 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
114
engines/grim/imuse/imuse.h
Normal 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
|
||||
172
engines/grim/imuse/imuse_mcmp_mgr.cpp
Normal file
172
engines/grim/imuse/imuse_mcmp_mgr.cpp
Normal 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
|
||||
58
engines/grim/imuse/imuse_mcmp_mgr.h
Normal file
58
engines/grim/imuse/imuse_mcmp_mgr.h
Normal 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
|
||||
167
engines/grim/imuse/imuse_music.cpp
Normal file
167
engines/grim/imuse/imuse_music.cpp
Normal 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
|
||||
178
engines/grim/imuse/imuse_script.cpp
Normal file
178
engines/grim/imuse/imuse_script.cpp
Normal 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
|
||||
370
engines/grim/imuse/imuse_sndmgr.cpp
Normal file
370
engines/grim/imuse/imuse_sndmgr.cpp
Normal 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
|
||||
118
engines/grim/imuse/imuse_sndmgr.h
Normal file
118
engines/grim/imuse/imuse_sndmgr.h
Normal 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
|
||||
290
engines/grim/imuse/imuse_tables.cpp
Normal file
290
engines/grim/imuse/imuse_tables.cpp
Normal 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
|
||||
40
engines/grim/imuse/imuse_tables.h
Normal file
40
engines/grim/imuse/imuse_tables.h
Normal 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
|
||||
411
engines/grim/imuse/imuse_track.cpp
Normal file
411
engines/grim/imuse/imuse_track.cpp
Normal 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
|
||||
125
engines/grim/imuse/imuse_track.h
Normal file
125
engines/grim/imuse/imuse_track.h
Normal 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
|
||||
Reference in New Issue
Block a user