passthrought v7+ sound voices by imuse digital
svn-id: r11939
This commit is contained in:
parent
908fbe2ac5
commit
a45e92efed
7 changed files with 249 additions and 203 deletions
|
@ -634,58 +634,6 @@ static const imuse_ft_music_table _ftSeqMusicTable[] = {
|
|||
};
|
||||
#endif
|
||||
|
||||
static byte *readCreativeVocFile(byte *ptr, int32 &size, int &rate) {
|
||||
assert(strncmp((char *)ptr, "Creative Voice File\x1A", 20) == 0);
|
||||
int32 offset = READ_LE_UINT16(ptr + 20);
|
||||
int16 version = READ_LE_UINT16(ptr + 22);
|
||||
int16 code = READ_LE_UINT16(ptr + 24);
|
||||
assert(version == 0x010A || version == 0x0114);
|
||||
assert(code == ~version + 0x1234);
|
||||
bool quit = 0;
|
||||
byte *ret_sound = 0; size = 0;
|
||||
int loops = 0;
|
||||
while (!quit) {
|
||||
int len = READ_LE_UINT32(ptr + offset);
|
||||
offset += 4;
|
||||
code = len & 0xFF;
|
||||
len >>= 8;
|
||||
switch(code) {
|
||||
case 0: quit = 1; break;
|
||||
case 1: {
|
||||
int time_constant = ptr[offset++];
|
||||
int packing = ptr[offset++];
|
||||
len -= 2;
|
||||
rate = getSampleRateFromVOCRate(time_constant);
|
||||
debug(9, "VOC Data Bloc : %d, %d, %d", rate, packing, len);
|
||||
if (packing == 0) {
|
||||
if (size) {
|
||||
ret_sound = (byte *)realloc(ret_sound, size + len);
|
||||
} else {
|
||||
ret_sound = (byte *)malloc(len);
|
||||
}
|
||||
memcpy(ret_sound + size, ptr + offset, len);
|
||||
size += len;
|
||||
} else {
|
||||
warning("VOC file packing %d unsupported", packing);
|
||||
}
|
||||
} break;
|
||||
case 6: // begin of loop
|
||||
loops = len + 1;
|
||||
break;
|
||||
case 7: // end of loop
|
||||
break;
|
||||
default:
|
||||
warning("Invalid code in VOC file : %d", code);
|
||||
quit = 1;
|
||||
break;
|
||||
}
|
||||
// FIXME some FT samples (ex. 362) has bad length, 2 bytes too short
|
||||
offset += len;
|
||||
}
|
||||
debug(9, "VOC Data Size : %d", size);
|
||||
return ret_sound;
|
||||
}
|
||||
|
||||
static uint32 decode12BitsSample(byte *src, byte **dst, uint32 size, bool stereo) {
|
||||
uint32 loop_size = size / 3;
|
||||
uint32 s_size = loop_size * 4;
|
||||
|
@ -722,6 +670,12 @@ IMuseDigital::IMuseDigital(ScummEngine *scumm)
|
|||
: _scumm(scumm) {
|
||||
_pause = false;
|
||||
|
||||
_voiceVocData = NULL;
|
||||
_voiceVocSize = 0;
|
||||
_voiceVocRate = 0;
|
||||
|
||||
_voiceBundleData = NULL;
|
||||
|
||||
_nameBundleMusic = "";
|
||||
_musicBundleBufFinal = NULL;
|
||||
_musicBundleBufOutput = NULL;
|
||||
|
@ -802,18 +756,37 @@ void IMuseDigital::callback() {
|
|||
}
|
||||
}
|
||||
|
||||
void IMuseDigital::setVocVoice(byte *src, int32 size, int rate) {
|
||||
_voiceVocData = src;
|
||||
_voiceVocSize = size;
|
||||
_voiceVocRate = rate;
|
||||
}
|
||||
|
||||
void IMuseDigital::setBundleVoice(byte *src) {
|
||||
_voiceBundleData = src;
|
||||
}
|
||||
|
||||
void IMuseDigital::startSound(int sound) {
|
||||
debug(5, "IMuseDigital::startSound(%d)", sound);
|
||||
int l, r;
|
||||
|
||||
for (l = 0; l < MAX_DIGITAL_CHANNELS; l++) {
|
||||
if (!_channel[l].used && !_channel[l].handle.isActive()) {
|
||||
byte *ptr = _scumm->getResourceAddress(rtSound, sound);
|
||||
byte *s_ptr = ptr;
|
||||
byte *ptr, *s_ptr;
|
||||
if ((sound == 10000) && (_voiceBundleData)) {
|
||||
s_ptr = ptr = _voiceBundleData;
|
||||
} else if ((sound == 10000) && (_voiceVocData)) {
|
||||
//
|
||||
} else if (sound != 10000) {
|
||||
ptr = _scumm->getResourceAddress(rtSound, sound);
|
||||
s_ptr = ptr;
|
||||
if (ptr == NULL) {
|
||||
warning("IMuseDigital::startSound(%d) NULL resource pointer", sound);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
error("IMuseDigital::startSound() unknown condition");
|
||||
}
|
||||
_channel[l].pan = 0;
|
||||
_channel[l].vol = 127 * 1000;
|
||||
_channel[l].volFadeDest = 0;
|
||||
|
@ -832,8 +805,22 @@ void IMuseDigital::startSound(int sound) {
|
|||
int32 size = 0;
|
||||
int t;
|
||||
|
||||
if (READ_UINT32(ptr) == MKID('Crea')) {
|
||||
byte *t_ptr= readCreativeVocFile(ptr, size, _channel[l].freq);
|
||||
if ((sound == 10000) && (_voiceVocData)) {
|
||||
_channel[l].mixerSize = _voiceVocRate * 2;
|
||||
_channel[l].size = _voiceVocSize * 2;
|
||||
_channel[l].bits = 8;
|
||||
_channel[l].channels = 2;
|
||||
_channel[l].mixerFlags = SoundMixer::FLAG_STEREO | SoundMixer::FLAG_REVERSE_STEREO | SoundMixer::FLAG_UNSIGNED;
|
||||
_channel[l].data = (byte *)malloc(_channel[l].size);
|
||||
|
||||
for (t = 0; t < _channel[l].size / 2; t++) {
|
||||
*(_channel[l].data + t * 2 + 0) = *(_voiceVocData + t);
|
||||
*(_channel[l].data + t * 2 + 1) = *(_voiceVocData + t);
|
||||
}
|
||||
|
||||
_voiceVocRate = NULL;
|
||||
} else if (READ_UINT32(ptr) == MKID('Crea')) {
|
||||
byte *t_ptr= readCreativeVoc(ptr, size, _channel[l].freq);
|
||||
_channel[l].mixerSize = _channel[l].freq * 2;
|
||||
_channel[l].size = size * 2;
|
||||
_channel[l].bits = 8;
|
||||
|
@ -897,6 +884,9 @@ void IMuseDigital::startSound(int sound) {
|
|||
_channel[l].jump[_channel[l].numJumps].fadeDelay = READ_BE_UINT32(ptr); ptr += 4;
|
||||
_channel[l].numJumps++;
|
||||
break;
|
||||
case MKID_BE('SYNC'):
|
||||
size = READ_BE_UINT32(ptr); ptr += size + 4;
|
||||
break;
|
||||
case MKID_BE('DATA'):
|
||||
size = READ_BE_UINT32(ptr); ptr += 4;
|
||||
break;
|
||||
|
@ -907,6 +897,16 @@ void IMuseDigital::startSound(int sound) {
|
|||
break;
|
||||
}
|
||||
|
||||
if ((sound == 10000) && (_voiceBundleData)) {
|
||||
if (_scumm->_actorToPrintStrFor != 0xFF && _scumm->_actorToPrintStrFor != 0) {
|
||||
Actor *a = _scumm->derefActor(_scumm->_actorToPrintStrFor, "playBundleSound");
|
||||
_channel[l].freq = (_channel[l].freq * a->talkFrequency) / 256;
|
||||
|
||||
// Adjust to fit the mixer's notion of panning.
|
||||
_channel[l].pan = (a->talkPan != 64) ? 2 * a->talkPan - 127 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 header_size = ptr - s_ptr;
|
||||
|
||||
_channel[l].offsetStop -= header_size;
|
||||
|
@ -938,6 +938,26 @@ void IMuseDigital::startSound(int sound) {
|
|||
_channel[l].mixerSize *= 2;
|
||||
_channel[l].mixerFlags |= SoundMixer::FLAG_16BITS;
|
||||
_channel[l].size = decode12BitsSample(ptr, &_channel[l].data, size, (_channel[l].channels == 2) ? false : true);
|
||||
} else if (_channel[l].bits == 16) {
|
||||
_channel[l].mixerSize *= 2;
|
||||
_channel[l].mixerFlags |= SoundMixer::FLAG_16BITS;
|
||||
|
||||
// FIXME: For some weird reasons, sometimes we get an odd size, even though
|
||||
// the data is supposed to be in 16 bit format... that makes no sense...
|
||||
size &= ~1;
|
||||
|
||||
if (_channel[l].channels == 1) {
|
||||
size *= 2;
|
||||
_channel[l].channels = 2;
|
||||
_channel[l].data = (byte *)malloc(size);
|
||||
for (t = 0; t < size / 4; t++) {
|
||||
*(_channel[l].data + t * 4 + 0) = *(ptr + t * 2 + 0);
|
||||
*(_channel[l].data + t * 4 + 1) = *(ptr + t * 2 + 1);
|
||||
*(_channel[l].data + t * 4 + 2) = *(ptr + t * 2 + 0);
|
||||
*(_channel[l].data + t * 4 + 3) = *(ptr + t * 2 + 1);
|
||||
}
|
||||
}
|
||||
_channel[l].size = size;
|
||||
} else if (_channel[l].bits == 8) {
|
||||
_channel[l].mixerFlags |= SoundMixer::FLAG_UNSIGNED;
|
||||
if (_channel[l].channels == 1) {
|
||||
|
@ -1408,9 +1428,8 @@ void IMuseDigital::bundleMusicHandler() {
|
|||
free(buffer);
|
||||
}
|
||||
|
||||
void IMuseDigital::playBundleSound(const char *sound, PlayingSoundHandle *handle) {
|
||||
void IMuseDigital::playBundleSound(const char *sound) {
|
||||
byte *ptr = 0, *orig_ptr = 0;
|
||||
byte *final;
|
||||
bool result;
|
||||
|
||||
if (!_scumm->_mixer->isReady())
|
||||
|
@ -1439,89 +1458,22 @@ void IMuseDigital::playBundleSound(const char *sound, PlayingSoundHandle *handle
|
|||
if (!result)
|
||||
return;
|
||||
|
||||
int32 rate = 22050, pan = 0, channels = 0, output_size = 0;
|
||||
int32 tag, size = -1, bits = 0;
|
||||
|
||||
if (_scumm->_gameId == GID_CMI) {
|
||||
char name[20];
|
||||
strcpy(name, sound);
|
||||
if (!(_scumm->_features & GF_DEMO)) // CMI demo does not have .IMX for voice but does for music...
|
||||
strcat(name, ".IMX");
|
||||
output_size = _bundle->decompressVoiceSampleByName(name, &ptr);
|
||||
_bundle->decompressVoiceSampleByName(name, &ptr);
|
||||
} else {
|
||||
output_size = _bundle->decompressVoiceSampleByName(sound, &ptr);
|
||||
_bundle->decompressVoiceSampleByName(sound, &ptr);
|
||||
}
|
||||
|
||||
orig_ptr = ptr;
|
||||
if (output_size == 0 || orig_ptr == 0) {
|
||||
goto bail;
|
||||
if (ptr) {
|
||||
stopSound(10000);
|
||||
setBundleVoice(ptr);
|
||||
startSound(10000);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
tag = READ_BE_UINT32(ptr); ptr += 4;
|
||||
if (tag != MKID_BE('iMUS')) {
|
||||
warning("Decompression of bundle sound failed");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
ptr += 12;
|
||||
while (tag != MKID_BE('DATA')) {
|
||||
tag = READ_BE_UINT32(ptr); ptr += 4;
|
||||
switch(tag) {
|
||||
case MKID_BE('FRMT'):
|
||||
ptr += 12;
|
||||
bits = READ_BE_UINT32(ptr); ptr += 4;
|
||||
rate = READ_BE_UINT32(ptr); ptr += 4;
|
||||
channels = READ_BE_UINT32(ptr); ptr += 4;
|
||||
break;
|
||||
case MKID_BE('TEXT'):
|
||||
case MKID_BE('REGN'):
|
||||
case MKID_BE('STOP'):
|
||||
case MKID_BE('JUMP'):
|
||||
case MKID_BE('SYNC'):
|
||||
size = READ_BE_UINT32(ptr); ptr += size + 4;
|
||||
break;
|
||||
case MKID_BE('DATA'):
|
||||
size = READ_BE_UINT32(ptr); ptr += 4;
|
||||
break;
|
||||
default:
|
||||
error("Unknown sound header '%s'", tag2str(tag));
|
||||
}
|
||||
}
|
||||
|
||||
if (size < 0) {
|
||||
warning("Decompression sound failed (no size field)");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
final = (byte *)malloc(size);
|
||||
memcpy(final, ptr, size);
|
||||
|
||||
if (_scumm->_actorToPrintStrFor != 0xFF && _scumm->_actorToPrintStrFor != 0) {
|
||||
Actor *a = _scumm->derefActor(_scumm->_actorToPrintStrFor, "playBundleSound");
|
||||
rate = (rate * a->talkFrequency) / 256;
|
||||
|
||||
// Adjust to fit the mixer's notion of panning.
|
||||
pan = (a->talkPan != 64) ? 2 * a->talkPan - 127 : 0;
|
||||
}
|
||||
|
||||
// Stop any sound currently playing on the given handle
|
||||
if (handle)
|
||||
_scumm->_mixer->stopHandle(*handle);
|
||||
|
||||
assert(channels == 1);
|
||||
if (bits == 8) {
|
||||
_scumm->_mixer->playRaw(handle, final, size, rate, SoundMixer::FLAG_UNSIGNED | SoundMixer::FLAG_AUTOFREE, -1, 255, pan);
|
||||
} else if (bits == 16) {
|
||||
// FIXME: For some weird reasons, sometimes we get an odd size, even though
|
||||
// the data is supposed to be in 16 bit format... that makes no sense...
|
||||
size &= ~1;
|
||||
_scumm->_mixer->playRaw(handle, final, size, rate, SoundMixer::FLAG_16BITS | SoundMixer::FLAG_AUTOFREE, -1, 255, pan);
|
||||
} else {
|
||||
warning("IMuseDigital::playBundleSound() to do more options to playRaw...");
|
||||
}
|
||||
|
||||
bail:
|
||||
free(orig_ptr);
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
||||
|
|
|
@ -128,10 +128,17 @@ private:
|
|||
public:
|
||||
int32 _bundleSongPosInMs;
|
||||
Bundle *_bundle; // FIXME: should be protected but is used by ScummEngine::askForDisk
|
||||
byte *_voiceVocData;
|
||||
int32 _voiceVocSize;
|
||||
int _voiceVocRate;
|
||||
|
||||
byte *_voiceBundleData;
|
||||
|
||||
void pauseBundleMusic(bool state);
|
||||
void stopBundleMusic();
|
||||
void playBundleSound(const char *sound, PlayingSoundHandle *handle);
|
||||
void playBundleSound(const char *sound);
|
||||
void setVocVoice(byte *src, int32 size, int rate);
|
||||
void setBundleVoice(byte *src);
|
||||
|
||||
public:
|
||||
IMuseDigital(ScummEngine *scumm);
|
||||
|
|
|
@ -1078,9 +1078,10 @@ void ScummEngine_v8::o8_actorOps() {
|
|||
// on the current talk channel handle. (If the handle is 0,
|
||||
// setChannelPan() won't do anything.)
|
||||
|
||||
if (_actorToPrintStrFor == a->number)
|
||||
_mixer->setChannelPan(_sound->_talkChannelHandle,
|
||||
(a->talkPan != 64) ? 2 * a->talkPan - 127 : 0);
|
||||
if (_actorToPrintStrFor == a->number) {
|
||||
if (_sound->isSoundRunning(10000))
|
||||
_imuseDigital->parseScriptCmds(12, 0x700, 10000, a->talkPan, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -39,11 +39,6 @@
|
|||
|
||||
namespace Scumm {
|
||||
|
||||
enum {
|
||||
SOUND_HEADER_SIZE = 26,
|
||||
SOUND_HEADER_BIG_SIZE = 26 + 8
|
||||
};
|
||||
|
||||
struct MP3OffsetTable { /* Compressed Sound (.SO3) */
|
||||
int org_offset;
|
||||
int new_offset;
|
||||
|
@ -411,7 +406,11 @@ void Sound::processSfxQueues() {
|
|||
if ((_sfxMode & 2) && _scumm->VAR(_scumm->VAR_TALK_ACTOR)) {
|
||||
act = _scumm->VAR(_scumm->VAR_TALK_ACTOR);
|
||||
|
||||
if (_scumm->_imuseDigital) {
|
||||
finished = !isSoundRunning(10000);
|
||||
} else {
|
||||
finished = !_talkChannelHandle.isActive();
|
||||
}
|
||||
|
||||
if (act != 0 && (uint) act < 0x80 && !_scumm->_string[0].no_talk_anim) {
|
||||
a = _scumm->derefActor(act, "processSfxQueues");
|
||||
|
@ -486,7 +485,6 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, PlayingSoundHandle
|
|||
warning("startTalkSound: dig demo: voc file not found");
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (!_sfxFile->isOpen()) {
|
||||
|
@ -551,7 +549,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, PlayingSoundHandle
|
|||
|
||||
_sfxFile->seek(offset, SEEK_SET);
|
||||
|
||||
assert(num+1 < (int)ARRAYSIZE(_mouthSyncTimes));
|
||||
assert(num + 1 < (int)ARRAYSIZE(_mouthSyncTimes));
|
||||
for (i = 0; i < num; i++)
|
||||
_mouthSyncTimes[i] = _sfxFile->readUint16BE();
|
||||
|
||||
|
@ -566,7 +564,11 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, PlayingSoundHandle
|
|||
|
||||
void Sound::stopTalkSound() {
|
||||
if (_sfxMode & 2) {
|
||||
if (_scumm->_imuseDigital) {
|
||||
_scumm->_imuseDigital->stopSound(10000);
|
||||
} else {
|
||||
_scumm->_mixer->stopHandle(_talkChannelHandle);
|
||||
}
|
||||
_sfxMode &= ~2;
|
||||
}
|
||||
}
|
||||
|
@ -797,10 +799,6 @@ void Sound::pauseSounds(bool pause) {
|
|||
}
|
||||
|
||||
void Sound::startSfxSound(File *file, int file_size, PlayingSoundHandle *handle, int id) {
|
||||
char ident[8];
|
||||
uint size = 0;
|
||||
int rate, comp;
|
||||
byte *data;
|
||||
|
||||
if (_soundsPaused || !_scumm->_mixer->isReady())
|
||||
return;
|
||||
|
@ -818,47 +816,17 @@ void Sound::startSfxSound(File *file, int file_size, PlayingSoundHandle *handle,
|
|||
return;
|
||||
}
|
||||
|
||||
if (file->read(ident, 8) != 8)
|
||||
goto invalid;
|
||||
int32 size;
|
||||
int rate;
|
||||
byte *data = loadVocSample(_sfxFile, size, rate);
|
||||
|
||||
if (!memcmp(ident, "VTLK", 4)) {
|
||||
file->seek(SOUND_HEADER_BIG_SIZE - 8, SEEK_CUR);
|
||||
} else if (!memcmp(ident, "Creative", 8)) {
|
||||
file->seek(SOUND_HEADER_SIZE - 8, SEEK_CUR);
|
||||
if (_scumm->_imuseDigital) {
|
||||
_scumm->_imuseDigital->setVocVoice(data, size, rate);
|
||||
_scumm->_imuseDigital->startSound(10000);
|
||||
free(data);
|
||||
} else {
|
||||
invalid:;
|
||||
warning("startSfxSound: invalid header");
|
||||
return;
|
||||
}
|
||||
|
||||
VocBlockHeader voc_block_hdr;
|
||||
|
||||
file->read(&voc_block_hdr, sizeof(voc_block_hdr));
|
||||
if (voc_block_hdr.blocktype != 1) {
|
||||
warning("startSfxSound: Expecting block_type == 1, got %d", voc_block_hdr.blocktype);
|
||||
return;
|
||||
}
|
||||
|
||||
size = voc_block_hdr.size[0] + (voc_block_hdr.size[1] << 8) + (voc_block_hdr.size[2] << 16) - 2;
|
||||
rate = getSampleRateFromVOCRate(voc_block_hdr.sr);
|
||||
comp = voc_block_hdr.pack;
|
||||
|
||||
if (comp != 0) {
|
||||
warning("startSfxSound: Unsupported compression type %d", comp);
|
||||
return;
|
||||
}
|
||||
|
||||
data = (byte *)malloc(size);
|
||||
if (data == NULL) {
|
||||
error("startSfxSound: out of memory");
|
||||
}
|
||||
|
||||
if (file->read(data, size) != size) {
|
||||
/* no need to free the memory since error will shut down */
|
||||
error("startSfxSound: cannot read %d bytes", size);
|
||||
}
|
||||
|
||||
_scumm->_mixer->playRaw(handle, data, size, rate, SoundMixer::FLAG_AUTOFREE | SoundMixer::FLAG_UNSIGNED, id);
|
||||
}
|
||||
}
|
||||
|
||||
File *Sound::openSfxFile() {
|
||||
|
@ -870,6 +838,15 @@ File *Sound::openSfxFile() {
|
|||
* same directory */
|
||||
offset_table = NULL;
|
||||
|
||||
// for now until better streaming will be, ft voice can't not be compressed
|
||||
if (_scumm->_imuseDigital) {
|
||||
sprintf(buf, "%s.sou", _scumm->getGameName());
|
||||
if (!file->open(buf, _scumm->getGameDataPath())) {
|
||||
file->open("monster.sou", _scumm->getGameDataPath());
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
#ifdef USE_MAD
|
||||
sprintf(buf, "%s.so3", _scumm->getGameName());
|
||||
if (!file->open(buf, _scumm->getGameDataPath())) {
|
||||
|
|
|
@ -163,9 +163,10 @@ void ScummEngine::CHARSET_1() {
|
|||
if (_talkDelay)
|
||||
return;
|
||||
|
||||
if ((_gameId == GID_CMI || _gameId == GID_DIG) && _sound->_talkChannelHandle.isActive()) {
|
||||
if ((_gameId == GID_CMI || _gameId == GID_DIG) && (_imuseDigital)
|
||||
&& _sound->isSoundRunning(10000)) {
|
||||
// Keep the 'speech' flag in _sound->_sfxMode set as long as the
|
||||
// _talkChannelHandle is valid.
|
||||
// sound 10000 is playing.
|
||||
_sound->_sfxMode |= 2;
|
||||
}
|
||||
|
||||
|
@ -900,7 +901,7 @@ const byte *ScummEngine::translateTextAndPlaySpeech(const byte *ptr) {
|
|||
pointer[j] = 0;
|
||||
|
||||
// Play speech
|
||||
_imuseDigital->playBundleSound(pointer, &_sound->_talkChannelHandle);
|
||||
_imuseDigital->playBundleSound(pointer);
|
||||
|
||||
ptr = _transText;
|
||||
}
|
||||
|
|
104
sound/voc.cpp
104
sound/voc.cpp
|
@ -22,9 +22,113 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
#include "common/util.h"
|
||||
#include "common/file.h"
|
||||
#include "sound/voc.h"
|
||||
|
||||
|
||||
byte *readCreativeVoc(byte *ptr, int32 &size, int &rate) {
|
||||
assert(strncmp((char *)ptr, "Creative Voice File\x1A", 20) == 0);
|
||||
int32 offset = READ_LE_UINT16(ptr + 20);
|
||||
int16 version = READ_LE_UINT16(ptr + 22);
|
||||
int16 code = READ_LE_UINT16(ptr + 24);
|
||||
assert(version == 0x010A || version == 0x0114);
|
||||
assert(code == ~version + 0x1234);
|
||||
bool quit = 0;
|
||||
byte *ret_sound = 0; size = 0;
|
||||
int loops = 0;
|
||||
while (!quit) {
|
||||
int len = READ_LE_UINT32(ptr + offset);
|
||||
offset += 4;
|
||||
code = len & 0xFF;
|
||||
len >>= 8;
|
||||
switch(code) {
|
||||
case 0: quit = 1; break;
|
||||
case 1: {
|
||||
int time_constant = ptr[offset++];
|
||||
int packing = ptr[offset++];
|
||||
len -= 2;
|
||||
rate = getSampleRateFromVOCRate(time_constant);
|
||||
debug(9, "VOC Data Bloc : %d, %d, %d", rate, packing, len);
|
||||
if (packing == 0) {
|
||||
if (size) {
|
||||
ret_sound = (byte *)realloc(ret_sound, size + len);
|
||||
} else {
|
||||
ret_sound = (byte *)malloc(len);
|
||||
}
|
||||
memcpy(ret_sound + size, ptr + offset, len);
|
||||
size += len;
|
||||
} else {
|
||||
warning("VOC file packing %d unsupported", packing);
|
||||
}
|
||||
} break;
|
||||
case 6: // begin of loop
|
||||
loops = len + 1;
|
||||
break;
|
||||
case 7: // end of loop
|
||||
break;
|
||||
default:
|
||||
warning("Invalid code in VOC file : %d", code);
|
||||
quit = 1;
|
||||
break;
|
||||
}
|
||||
// FIXME some FT samples (ex. 362) has bad length, 2 bytes too short
|
||||
offset += len;
|
||||
}
|
||||
debug(9, "VOC Data Size : %d", size);
|
||||
return ret_sound;
|
||||
}
|
||||
|
||||
enum {
|
||||
SOUND_HEADER_SIZE = 26,
|
||||
SOUND_HEADER_BIG_SIZE = 26 + 8
|
||||
};
|
||||
|
||||
byte *loadVocSample(File *file, int32 &size, int &rate) {
|
||||
char ident[8];
|
||||
|
||||
if (file->read(ident, 8) != 8)
|
||||
goto invalid;
|
||||
|
||||
if (!memcmp(ident, "VTLK", 4)) {
|
||||
file->seek(SOUND_HEADER_BIG_SIZE - 8, SEEK_CUR);
|
||||
} else if (!memcmp(ident, "Creative", 8)) {
|
||||
file->seek(SOUND_HEADER_SIZE - 8, SEEK_CUR);
|
||||
} else {
|
||||
invalid:;
|
||||
warning("loadVocSample: invalid header");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VocBlockHeader voc_block_hdr;
|
||||
|
||||
file->read(&voc_block_hdr, sizeof(voc_block_hdr));
|
||||
if (voc_block_hdr.blocktype != 1) {
|
||||
warning("loadVocSample: Expecting block_type == 1, got %d", voc_block_hdr.blocktype);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size = voc_block_hdr.size[0] + (voc_block_hdr.size[1] << 8) + (voc_block_hdr.size[2] << 16) - 2;
|
||||
rate = getSampleRateFromVOCRate(voc_block_hdr.sr);
|
||||
int comp = voc_block_hdr.pack;
|
||||
|
||||
if (comp != 0) {
|
||||
warning("loadVocSample: Unsupported compression type %d", comp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
byte *data = (byte *)malloc(size);
|
||||
if (data == NULL) {
|
||||
error("loadVocSample: out of memory");
|
||||
}
|
||||
|
||||
if (file->read(data, size) != size) {
|
||||
/* no need to free the memory since error will shut down */
|
||||
error("startSfxSound: cannot read %d bytes", size);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
int getSampleRateFromVOCRate(int vocSR) {
|
||||
if (vocSR == 0xa5 || vocSR == 0xa6 || vocSR == 0x83) {
|
||||
return 11025;
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include "stdafx.h"
|
||||
#include "common/scummsys.h"
|
||||
|
||||
class File;
|
||||
|
||||
#if !defined(__GNUC__)
|
||||
#pragma START_PACK_STRUCTS
|
||||
#endif
|
||||
|
@ -53,5 +55,7 @@ struct VocBlockHeader {
|
|||
* return the corresponding sample frequency.
|
||||
*/
|
||||
extern int getSampleRateFromVOCRate(int vocSR);
|
||||
extern byte *readCreativeVoc(byte *ptr, int32 &size, int &rate);
|
||||
extern byte *loadVocSample(File *file, int32 &size, int &rate);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue