2006-04-02 14:20:45 +00:00
|
|
|
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
2008-06-13 14:57:47 +00:00
|
|
|
*
|
|
|
|
* Residual is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the AUTHORS
|
|
|
|
* file distributed with this source distribution.
|
2006-04-02 14:20:45 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
* This library 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
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*
|
|
|
|
* $URL$
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
*/
|
2004-12-26 13:43:43 +00:00
|
|
|
|
2008-01-26 11:47:23 +00:00
|
|
|
#include "common/sys.h"
|
2008-06-12 12:08:15 +00:00
|
|
|
#include "common/endian.h"
|
2008-07-30 09:54:22 +00:00
|
|
|
#include "common/util.h"
|
2008-01-26 11:47:23 +00:00
|
|
|
#include "common/timer.h"
|
2008-07-30 09:54:22 +00:00
|
|
|
#include "common/str.h"
|
2008-01-26 11:47:23 +00:00
|
|
|
|
|
|
|
#include "engine/resource.h"
|
2004-12-26 13:43:43 +00:00
|
|
|
|
2005-01-01 12:27:57 +00:00
|
|
|
#include "mixer/mixer.h"
|
|
|
|
#include "mixer/audiostream.h"
|
2004-12-26 13:43:43 +00:00
|
|
|
|
2008-01-26 11:47:23 +00:00
|
|
|
#include "engine/imuse/imuse_sndmgr.h"
|
|
|
|
#include "engine/imuse/imuse_mcmp_mgr.h"
|
2004-12-26 13:43:43 +00:00
|
|
|
|
2004-12-29 06:32:07 +00:00
|
|
|
ImuseSndMgr::ImuseSndMgr() {
|
2004-12-26 13:43:43 +00:00
|
|
|
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
|
2008-03-10 23:06:07 +00:00
|
|
|
memset(&_sounds[l], 0, sizeof(SoundDesc));
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ImuseSndMgr::~ImuseSndMgr() {
|
|
|
|
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
|
|
|
|
closeSound(&_sounds[l]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImuseSndMgr::countElements(byte *ptr, int &numRegions, int &numJumps) {
|
|
|
|
uint32 tag;
|
|
|
|
int32 size = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
tag = READ_BE_UINT32(ptr); ptr += 4;
|
|
|
|
switch(tag) {
|
|
|
|
case MKID_BE('TEXT'):
|
|
|
|
case MKID_BE('STOP'):
|
|
|
|
case MKID_BE('FRMT'):
|
|
|
|
case MKID_BE('DATA'):
|
|
|
|
size = READ_BE_UINT32(ptr); ptr += size + 4;
|
|
|
|
break;
|
|
|
|
case MKID_BE('REGN'):
|
|
|
|
numRegions++;
|
|
|
|
size = READ_BE_UINT32(ptr); ptr += size + 4;
|
|
|
|
break;
|
|
|
|
case MKID_BE('JUMP'):
|
|
|
|
numJumps++;
|
|
|
|
size = READ_BE_UINT32(ptr); ptr += size + 4;
|
|
|
|
break;
|
|
|
|
default:
|
2008-08-17 07:08:50 +00:00
|
|
|
error("ImuseSndMgr::countElements() Unknown MAP tag '%s'", Common::tag2string(tag).c_str());
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
} while (tag != MKID_BE('DATA'));
|
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
void ImuseSndMgr::parseSoundHeader(byte *ptr, SoundDesc *sound, int &headerSize) {
|
2008-06-12 12:08:15 +00:00
|
|
|
if (READ_BE_UINT32(ptr) == MKID_BE('RIFF')) {
|
2008-03-10 23:06:07 +00:00
|
|
|
sound->region = new Region[1];
|
|
|
|
sound->jump = new Jump[0];
|
2004-12-28 18:25:14 +00:00
|
|
|
sound->numJumps = 0;
|
|
|
|
sound->numRegions = 1;
|
|
|
|
sound->region[0].offset = 0;
|
|
|
|
sound->region[0].length = READ_LE_UINT32(ptr + 40);
|
|
|
|
sound->bits = *(ptr + 34);
|
|
|
|
sound->freq = READ_LE_UINT32(ptr + 24);
|
|
|
|
sound->channels = *(ptr + 22);
|
|
|
|
headerSize = 44;
|
2008-06-12 12:08:15 +00:00
|
|
|
} else if (READ_BE_UINT32(ptr) == MKID_BE('iMUS')) {
|
2004-12-26 13:43:43 +00:00
|
|
|
uint32 tag;
|
|
|
|
int32 size = 0;
|
|
|
|
byte *s_ptr = ptr;
|
|
|
|
ptr += 16;
|
|
|
|
|
|
|
|
int curIndexRegion = 0;
|
|
|
|
int curIndexJump = 0;
|
|
|
|
|
|
|
|
sound->numRegions = 0;
|
|
|
|
sound->numJumps = 0;
|
2004-12-29 06:32:07 +00:00
|
|
|
countElements(ptr, sound->numRegions, sound->numJumps);
|
2008-03-10 23:06:07 +00:00
|
|
|
sound->region = new Region [sound->numRegions];
|
|
|
|
sound->jump = new Jump [sound->numJumps];
|
2004-12-26 13:43:43 +00:00
|
|
|
|
|
|
|
do {
|
|
|
|
tag = READ_BE_UINT32(ptr); ptr += 4;
|
|
|
|
switch(tag) {
|
|
|
|
case MKID_BE('FRMT'):
|
|
|
|
ptr += 12;
|
|
|
|
sound->bits = READ_BE_UINT32(ptr); ptr += 4;
|
|
|
|
sound->freq = READ_BE_UINT32(ptr); ptr += 4;
|
|
|
|
sound->channels = READ_BE_UINT32(ptr); ptr += 4;
|
|
|
|
break;
|
|
|
|
case MKID_BE('TEXT'):
|
|
|
|
case MKID_BE('STOP'):
|
|
|
|
size = READ_BE_UINT32(ptr); ptr += size + 4;
|
|
|
|
break;
|
|
|
|
case MKID_BE('REGN'):
|
|
|
|
ptr += 4;
|
|
|
|
sound->region[curIndexRegion].offset = READ_BE_UINT32(ptr); ptr += 4;
|
|
|
|
sound->region[curIndexRegion].length = READ_BE_UINT32(ptr); ptr += 4;
|
|
|
|
curIndexRegion++;
|
|
|
|
break;
|
|
|
|
case MKID_BE('JUMP'):
|
|
|
|
ptr += 4;
|
|
|
|
sound->jump[curIndexJump].offset = READ_BE_UINT32(ptr); ptr += 4;
|
|
|
|
sound->jump[curIndexJump].dest = READ_BE_UINT32(ptr); ptr += 4;
|
|
|
|
sound->jump[curIndexJump].hookId = READ_BE_UINT32(ptr); ptr += 4;
|
|
|
|
sound->jump[curIndexJump].fadeDelay = READ_BE_UINT32(ptr); ptr += 4;
|
|
|
|
curIndexJump++;
|
|
|
|
break;
|
|
|
|
case MKID_BE('DATA'):
|
|
|
|
ptr += 4;
|
|
|
|
break;
|
|
|
|
default:
|
2008-08-17 07:08:50 +00:00
|
|
|
error("ImuseSndMgr::prepareSound(%s) Unknown MAP tag '%s'", sound->name, Common::tag2string(tag).c_str());
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
} while (tag != MKID_BE('DATA'));
|
2004-12-28 18:25:14 +00:00
|
|
|
headerSize = ptr - s_ptr;
|
|
|
|
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;
|
2004-12-31 18:17:25 +00:00
|
|
|
sound->jump[i].dest -= headerSize;
|
2004-12-28 18:25:14 +00:00
|
|
|
}
|
2004-12-26 13:43:43 +00:00
|
|
|
} else {
|
2004-12-28 18:25:14 +00:00
|
|
|
error("ImuseSndMgr::prepareSound() Unknown sound format");
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
ImuseSndMgr::SoundDesc *ImuseSndMgr::allocSlot() {
|
2004-12-26 13:43:43 +00:00
|
|
|
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
|
|
|
|
if (!_sounds[l].inUse) {
|
|
|
|
_sounds[l].inUse = true;
|
|
|
|
return &_sounds[l];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
ImuseSndMgr::SoundDesc *ImuseSndMgr::openSound(const char *soundName, int volGroupId) {
|
2004-12-28 08:05:18 +00:00
|
|
|
const char *extension = soundName + std::strlen(soundName) - 3;
|
|
|
|
byte *ptr = NULL;
|
2004-12-28 18:25:14 +00:00
|
|
|
int headerSize = 0;
|
2004-12-26 13:43:43 +00:00
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
SoundDesc *sound = allocSlot();
|
2004-12-26 13:43:43 +00:00
|
|
|
if (!sound) {
|
2004-12-28 18:25:14 +00:00
|
|
|
error("ImuseSndMgr::openSound() Can't alloc free sound slot");
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
2004-12-28 18:25:14 +00:00
|
|
|
strcpy(sound->name, soundName);
|
|
|
|
sound->volGroupId = volGroupId;
|
|
|
|
|
2004-12-28 08:05:18 +00:00
|
|
|
if (strcasecmp(extension, "imu") == 0) {
|
2005-01-01 21:07:52 +00:00
|
|
|
sound->blockRes = g_resourceloader->getFileBlock(soundName);
|
2008-09-10 11:16:57 +00:00
|
|
|
if (sound->blockRes) {
|
2005-01-01 21:07:52 +00:00
|
|
|
ptr = (byte *)sound->blockRes->data();
|
2004-12-28 18:25:14 +00:00
|
|
|
parseSoundHeader(ptr, sound, headerSize);
|
2004-12-28 08:05:18 +00:00
|
|
|
sound->mcmpData = false;
|
2004-12-28 18:25:14 +00:00
|
|
|
sound->resPtr = ptr + headerSize;
|
2004-12-28 08:05:18 +00:00
|
|
|
} else {
|
|
|
|
closeSound(sound);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else if (strcasecmp(extension, "wav") == 0 || strcasecmp(extension, "imc") == 0) {
|
2004-12-28 18:25:14 +00:00
|
|
|
sound->mcmpMgr = new McmpMgr();
|
2004-12-29 06:32:07 +00:00
|
|
|
if (!sound->mcmpMgr->openSound(soundName, &ptr, headerSize)) {
|
2004-12-28 08:05:18 +00:00
|
|
|
closeSound(sound);
|
|
|
|
return NULL;
|
|
|
|
}
|
2004-12-28 18:25:14 +00:00
|
|
|
parseSoundHeader(ptr, sound, headerSize);
|
2004-12-28 08:05:18 +00:00
|
|
|
sound->mcmpData = true;
|
2004-12-26 13:43:43 +00:00
|
|
|
} else {
|
2004-12-28 18:25:14 +00:00
|
|
|
error("ImuseSndMgr::openSound() Unrecognized extension for sound file %s", soundName);
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return sound;
|
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
void ImuseSndMgr::closeSound(SoundDesc *sound) {
|
|
|
|
assert(checkForProperHandle(sound));
|
2004-12-26 13:43:43 +00:00
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
if (sound->mcmpMgr) {
|
|
|
|
delete sound->mcmpMgr;
|
|
|
|
sound->mcmpMgr = NULL;
|
2005-01-01 11:18:34 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
if (sound->blockRes) {
|
|
|
|
delete sound->blockRes;
|
|
|
|
sound->blockRes = NULL;
|
2005-01-01 21:07:52 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
if (sound->region) {
|
|
|
|
delete[] sound->region;
|
|
|
|
sound->region = NULL;
|
2005-01-01 11:18:34 +00:00
|
|
|
}
|
2004-12-26 13:43:43 +00:00
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
if (sound->jump) {
|
|
|
|
delete[] sound->jump;
|
|
|
|
sound->jump = NULL;
|
2005-01-01 11:18:34 +00:00
|
|
|
}
|
2004-12-28 18:25:14 +00:00
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
memset(sound, 0, sizeof(SoundDesc));
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
ImuseSndMgr::SoundDesc *ImuseSndMgr::cloneSound(SoundDesc *sound) {
|
|
|
|
assert(checkForProperHandle(sound));
|
2004-12-26 13:43:43 +00:00
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
return openSound(sound->name, sound->volGroupId);
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
bool ImuseSndMgr::checkForProperHandle(SoundDesc *sound) {
|
|
|
|
if (!sound)
|
2004-12-26 13:43:43 +00:00
|
|
|
return false;
|
2004-12-28 18:25:14 +00:00
|
|
|
|
2004-12-26 13:43:43 +00:00
|
|
|
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
|
2008-03-10 23:06:07 +00:00
|
|
|
if (sound == &_sounds[l])
|
2004-12-26 13:43:43 +00:00
|
|
|
return true;
|
|
|
|
}
|
2004-12-28 18:25:14 +00:00
|
|
|
|
2004-12-26 13:43:43 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
int ImuseSndMgr::getFreq(SoundDesc *sound) {
|
|
|
|
assert(checkForProperHandle(sound));
|
|
|
|
return sound->freq;
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
int ImuseSndMgr::getBits(SoundDesc *sound) {
|
|
|
|
assert(checkForProperHandle(sound));
|
|
|
|
return sound->bits;
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
int ImuseSndMgr::getChannels(SoundDesc *sound) {
|
|
|
|
assert(checkForProperHandle(sound));
|
|
|
|
return sound->channels;
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
bool ImuseSndMgr::isEndOfRegion(SoundDesc *sound, int region) {
|
|
|
|
assert(checkForProperHandle(sound));
|
|
|
|
assert(region >= 0 && region < sound->numRegions);
|
|
|
|
return sound->endFlag;
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
int ImuseSndMgr::getNumRegions(SoundDesc *sound) {
|
|
|
|
assert(checkForProperHandle(sound));
|
|
|
|
return sound->numRegions;
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
int ImuseSndMgr::getNumJumps(SoundDesc *sound) {
|
|
|
|
assert(checkForProperHandle(sound));
|
|
|
|
return sound->numJumps;
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
int ImuseSndMgr::getRegionOffset(SoundDesc *sound, int region) {
|
|
|
|
assert(checkForProperHandle(sound));
|
|
|
|
assert(region >= 0 && region < sound->numRegions);
|
|
|
|
return sound->region[region].offset;
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
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)
|
2004-12-26 13:43:43 +00:00
|
|
|
return l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
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) {
|
2004-12-26 13:43:43 +00:00
|
|
|
return l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
int ImuseSndMgr::getJumpHookId(SoundDesc *sound, int number) {
|
|
|
|
assert(checkForProperHandle(sound));
|
|
|
|
assert(number >= 0 && number < sound->numJumps);
|
|
|
|
return sound->jump[number].hookId;
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
int ImuseSndMgr::getJumpFade(SoundDesc *sound, int number) {
|
|
|
|
assert(checkForProperHandle(sound));
|
|
|
|
assert(number >= 0 && number < sound->numJumps);
|
|
|
|
return sound->jump[number].fadeDelay;
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
int32 ImuseSndMgr::getDataFromRegion(SoundDesc *sound, int region, byte **buf, int32 offset, int32 size) {
|
|
|
|
assert(checkForProperHandle(sound));
|
2004-12-26 13:43:43 +00:00
|
|
|
assert(buf && offset >= 0 && size >= 0);
|
2008-03-10 23:06:07 +00:00
|
|
|
assert(region >= 0 && region < sound->numRegions);
|
2004-12-26 13:43:43 +00:00
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
int32 region_offset = sound->region[region].offset;
|
|
|
|
int32 region_length = sound->region[region].length;
|
2004-12-26 13:43:43 +00:00
|
|
|
|
2004-12-28 08:05:18 +00:00
|
|
|
if (offset + size > region_length) {
|
2004-12-26 13:43:43 +00:00
|
|
|
size = region_length - offset;
|
2008-03-10 23:06:07 +00:00
|
|
|
sound->endFlag = true;
|
2004-12-26 13:43:43 +00:00
|
|
|
} else {
|
2008-03-10 23:06:07 +00:00
|
|
|
sound->endFlag = false;
|
2004-12-26 13:43:43 +00:00
|
|
|
}
|
|
|
|
|
2008-03-10 23:06:07 +00:00
|
|
|
if (sound->mcmpData) {
|
|
|
|
size = sound->mcmpMgr->decompressSample(region_offset + offset, size, buf);
|
2004-12-28 08:05:18 +00:00
|
|
|
} else {
|
2008-03-10 23:06:07 +00:00
|
|
|
*buf = new byte[size];
|
|
|
|
memcpy(*buf, sound->resPtr + region_offset + offset, size);
|
2004-12-28 08:05:18 +00:00
|
|
|
}
|
2004-12-26 13:43:43 +00:00
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|