aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaweł Kołodziejski2003-12-26 12:15:23 +0000
committerPaweł Kołodziejski2003-12-26 12:15:23 +0000
commita45e92efed3715b3bb0b1191b718207b191ab1ae (patch)
tree79e58723288257ce0e3e68a434b86f17110330d3
parent908fbe2ac563f41b039da0392baa60207331d64d (diff)
downloadscummvm-rg350-a45e92efed3715b3bb0b1191b718207b191ab1ae.tar.gz
scummvm-rg350-a45e92efed3715b3bb0b1191b718207b191ab1ae.tar.bz2
scummvm-rg350-a45e92efed3715b3bb0b1191b718207b191ab1ae.zip
passthrought v7+ sound voices by imuse digital
svn-id: r11939
-rw-r--r--scumm/imuse_digi.cpp224
-rw-r--r--scumm/imuse_digi.h11
-rw-r--r--scumm/script_v8.cpp7
-rw-r--r--scumm/sound.cpp95
-rw-r--r--scumm/string.cpp7
-rw-r--r--sound/voc.cpp104
-rw-r--r--sound/voc.h4
7 files changed, 249 insertions, 203 deletions
diff --git a/scumm/imuse_digi.cpp b/scumm/imuse_digi.cpp
index 8db1fb3dd4..53641531a4 100644
--- a/scumm/imuse_digi.cpp
+++ b/scumm/imuse_digi.cpp
@@ -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,17 +756,36 @@ 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;
- if (ptr == NULL) {
- warning("IMuseDigital::startSound(%d) NULL resource pointer", sound);
- return;
+ 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;
@@ -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())
@@ -1427,7 +1446,7 @@ void IMuseDigital::playBundleSound(const char *sound, PlayingSoundHandle *handle
result = _bundle->openVoiceFile(voxfile, _scumm->getGameDataPath());
- if (result == false)
+ if (result == false)
result = _bundle->openVoiceFile("voice.bun", _scumm->getGameDataPath());
_voiceDisk = (byte)_scumm->VAR(_scumm->VAR_CURRENTDISK);
}
@@ -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);
- }
-
- orig_ptr = ptr;
- if (output_size == 0 || orig_ptr == 0) {
- goto bail;
- }
-
- tag = READ_BE_UINT32(ptr); ptr += 4;
- if (tag != MKID_BE('iMUS')) {
- warning("Decompression of bundle sound failed");
- goto bail;
+ _bundle->decompressVoiceSampleByName(sound, &ptr);
}
- 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;
+ if (ptr) {
+ stopSound(10000);
+ setBundleVoice(ptr);
+ startSound(10000);
+ free(ptr);
}
-
- 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
diff --git a/scumm/imuse_digi.h b/scumm/imuse_digi.h
index 913f1e72e7..eb7ade49d2 100644
--- a/scumm/imuse_digi.h
+++ b/scumm/imuse_digi.h
@@ -128,11 +128,18 @@ 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);
~IMuseDigital();
diff --git a/scumm/script_v8.cpp b/scumm/script_v8.cpp
index 6d4afc2bff..b4675ce533 100644
--- a/scumm/script_v8.cpp
+++ b/scumm/script_v8.cpp
@@ -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:
diff --git a/scumm/sound.cpp b/scumm/sound.cpp
index 0590a2696f..5523635069 100644
--- a/scumm/sound.cpp
+++ b/scumm/sound.cpp
@@ -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);
- finished = !_talkChannelHandle.isActive();
+ 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,14 +485,13 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, PlayingSoundHandle
warning("startTalkSound: dig demo: voc file not found");
return;
}
-
} else {
if (!_sfxFile->isOpen()) {
warning("startTalkSound: SFX file is not open");
return;
}
-
+
// FIXME hack until more is known
// the size of the data after the sample isn't known
// 64 is just a guess
@@ -506,7 +504,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, PlayingSoundHandle
_scumm->_mixer->playRaw(handle, sound, b - 64, 11025, SoundMixer::FLAG_UNSIGNED | SoundMixer::FLAG_AUTOFREE);
return;
}
-
+
// Some games frequently assume that starting one sound effect will
// automatically stop any other that may be playing at that time. So
// that is what we do here, but we make an exception for speech.
@@ -515,24 +513,24 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, PlayingSoundHandle
//
// HACK: Checking for script 99 in Sam & Max is to keep Conroy's song
// from being interrupted.
-
+
if (mode == 1 && (_scumm->_gameId == GID_TENTACLE
|| (_scumm->_gameId == GID_SAMNMAX && !_scumm->isScriptRunning(99)))) {
id = 777777;
_scumm->_mixer->stopID(id);
}
-
+
if (b > 8) {
num = (b - 8) >> 1;
}
-
+
if (offset_table != NULL) {
MP3OffsetTable *result = NULL, key;
key.org_offset = offset;
result = (MP3OffsetTable *)bsearch(&key, offset_table, num_sound_effects,
sizeof(MP3OffsetTable), compareMP3OffsetTable);
-
+
if (result == NULL) {
warning("startTalkSound: did not find sound at offset %d !", offset);
return;
@@ -548,10 +546,10 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, PlayingSoundHandle
offset += 8;
size = -1;
}
-
+
_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) {
- _scumm->_mixer->stopHandle(_talkChannelHandle);
+ 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");
+ _scumm->_mixer->playRaw(handle, data, size, rate, SoundMixer::FLAG_AUTOFREE | SoundMixer::FLAG_UNSIGNED, id);
}
-
- 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())) {
diff --git a/scumm/string.cpp b/scumm/string.cpp
index 69d5ed5e6c..1a15a48255 100644
--- a/scumm/string.cpp
+++ b/scumm/string.cpp
@@ -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;
}
diff --git a/sound/voc.cpp b/sound/voc.cpp
index 46dc6f6abc..7951d72633 100644
--- a/sound/voc.cpp
+++ b/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;
diff --git a/sound/voc.h b/sound/voc.h
index 5768be2c19..f29e804b4e 100644
--- a/sound/voc.h
+++ b/sound/voc.h
@@ -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