scummvm/engines/grim/imuse/imuse_sndmgr.cpp
Vladimir Serbinenko 7d8aba66cf GRIM: Change audio encoding to little-endian
We create it from 8-bit compression so on most platforms it just results in
2 useless byte swaps
2023-03-06 16:00:46 +01:00

371 lines
9.9 KiB
C++

/* 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