From 13ee099090f45a1e9383d71d3481247f669d984c Mon Sep 17 00:00:00 2001 From: Martin Kiewitz Date: Thu, 29 Apr 2010 15:54:59 +0000 Subject: SCI: audio compression support svn-id: r48856 --- engines/sci/resource.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++--- engines/sci/resource.h | 7 ++++ engines/sci/sound/audio.cpp | 82 +++++++++++++++++++++++++++++------------- 3 files changed, 146 insertions(+), 29 deletions(-) diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index eee7ba3a47..18d19456e4 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -57,6 +57,8 @@ struct ResourceSource { const Common::FSNode *resourceFile; int volume_number; ResourceSource *associated_map; + uint32 audioCompressionType; + int32 *audioCompressionOffsetMapping; }; ////////////////////////////////////////////////////////////////////// @@ -176,6 +178,10 @@ void Resource::writeToStream(Common::WriteStream *stream) const { stream->write(data, size); } +uint32 Resource::getAudioCompressionType() { + return _source->audioCompressionType; +} + //-- resMan helper functions -- // Resource source list management @@ -217,6 +223,10 @@ ResourceSource *ResourceManager::addSource(ResourceSource *map, ResSourceType ty newsrc->resourceFile = 0; newsrc->volume_number = number; newsrc->associated_map = map; + newsrc->audioCompressionType = 0; + newsrc->audioCompressionOffsetMapping = NULL; + if (type == kSourceAudioVolume) + checkIfAudioVolumeIsCompressed(newsrc); _sources.push_back(newsrc); return newsrc; @@ -231,6 +241,10 @@ ResourceSource *ResourceManager::addSource(ResourceSource *map, ResSourceType ty newsrc->resourceFile = resFile; newsrc->volume_number = number; newsrc->associated_map = map; + newsrc->audioCompressionType = 0; + newsrc->audioCompressionOffsetMapping = NULL; + if (type == kSourceAudioVolume) + checkIfAudioVolumeIsCompressed(newsrc); _sources.push_back(newsrc); return newsrc; @@ -260,6 +274,32 @@ ResourceSource *ResourceManager::getVolume(ResourceSource *map, int volume_nr) { // Resource manager constructors and operations +void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) { + Common::File *file = getVolumeFile(source->location_name.c_str()); + if (!file) { + warning("Failed to open %s", source->location_name.c_str()); + return; + } + file->seek(0, SEEK_SET); + uint32 compressionType = file->readUint32BE(); + switch (compressionType) { + case MKID_BE('MP3 '): + case MKID_BE('OGG '): + case MKID_BE('FLAC'): + // Detected a compressed audio volume + source->audioCompressionType = compressionType; + // Now read the whole offset mapping table for later usage + uint32 recordCount = file->readUint32LE(); + if (!recordCount) + error("compressed audio volume doesn't contain any entries!"); + source->audioCompressionOffsetMapping = new int32[(recordCount + 1) * 4 * 2]; + file->read(&source->audioCompressionOffsetMapping, recordCount * 4 * 2); + // Put ending zero + source->audioCompressionOffsetMapping[recordCount * 2] = 0; + source->audioCompressionOffsetMapping[recordCount * 2 + 1] = file->size(); + } +} + bool ResourceManager::loadPatch(Resource *res, Common::File &file) { // We assume that the resource type matches res->type file.seek(res->_fileOffset + 2, SEEK_SET); @@ -408,17 +448,55 @@ void ResourceManager::loadResource(Resource *res) { res->unalloc(); return; } - file->seek(res->_fileOffset, SEEK_SET); - if (res->_source->source_type == kSourceWave && loadFromWaveFile(res, *file)) + switch(res->_source->source_type) { + case kSourceWave: + file->seek(res->_fileOffset, SEEK_SET); + loadFromWaveFile(res, *file); return; - if (res->_source->source_type == kSourceAudioVolume) { + case kSourceAudioVolume: + if (res->_source->audioCompressionType) { + // this file is compressed, so lookup our offset in the offset-translation table and get the new offset + // also calculate the compressed size by using the next offset + int32 *mappingTable = res->_source->audioCompressionOffsetMapping; + int32 compressedOffset = 0; + + do { + if (*mappingTable == res->_fileOffset) { + mappingTable++; + compressedOffset = *mappingTable; + // Go to next compressed offset and use that to calculate size of compressed sample + mappingTable += 2; + res->size = *mappingTable - compressedOffset; + break; + } + mappingTable += 2; + } while (*mappingTable); + + if (!compressedOffset) + error("could not translate offset to compressed offset in audio volume"); + file->seek(compressedOffset, SEEK_SET); + + switch (res->_id.type) { + case kResourceTypeAudio: + case kResourceTypeAudio36: + // Directly read the stream, compressed audio wont have resource type id and header size for SCI1.1 + loadFromAudioVolumeSCI1(res, *file); + return; + } + } else { + // original file, directly seek to given offset and get SCI1/SCI1.1 audio resource + file->seek(res->_fileOffset, SEEK_SET); + } if (getSciVersion() < SCI_VERSION_1_1) loadFromAudioVolumeSCI1(res, *file); else loadFromAudioVolumeSCI11(res, *file); - } else { + return; + + default: + file->seek(res->_fileOffset, SEEK_SET); int error = decompress(res, file); if (error) { warning("Error %d occured while reading %s from resource file: %s", diff --git a/engines/sci/resource.h b/engines/sci/resource.h index eb1b844071..b9825011e5 100644 --- a/engines/sci/resource.h +++ b/engines/sci/resource.h @@ -185,6 +185,7 @@ public: uint32 _headerSize; void writeToStream(Common::WriteStream *stream) const; + uint32 getAudioCompressionType(); protected: int32 _fileOffset; /**< Offset in file */ @@ -332,6 +333,12 @@ protected: */ ResourceSource *addInternalMap(const char *name, int resNr); + /** + * Checks, if an audio volume got compressed by our tool. If that's the case, it will set audioCompressionType + * and read in the offset translation table for later usage. + */ + void checkIfAudioVolumeIsCompressed(ResourceSource *source); + /** * Scans newly registered resource sources for resources, earliest addition first. * @param detected_version: Pointer to the detected version number, diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp index 935427c51a..96a3f1c577 100644 --- a/engines/sci/sound/audio.cpp +++ b/engines/sci/sound/audio.cpp @@ -36,6 +36,9 @@ #include "sound/audiocd.h" #include "sound/decoders/raw.h" #include "sound/decoders/wave.h" +#include "sound/decoders/flac.h" +#include "sound/decoders/mp3.h" +#include "sound/decoders/vorbis.h" namespace Sci { @@ -224,36 +227,65 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32 } byte audioFlags; - - if (audioRes->_headerSize > 0) { - // SCI1.1 - Common::MemoryReadStream headerStream(audioRes->_header, audioRes->_headerSize, DisposeAfterUse::NO); - - if (readSOLHeader(&headerStream, audioRes->_headerSize, size, _audioRate, audioFlags)) { - Common::MemoryReadStream dataStream(audioRes->data, audioRes->size, DisposeAfterUse::NO); - data = readSOLAudio(&dataStream, size, audioFlags, flags); + uint32 audioCompressionType = audioRes->getAudioCompressionType(); + + if (audioCompressionType) { + // Compressed audio made by our tool + Common::MemoryReadStream *compressedStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::YES); + + switch (audioCompressionType) { + case MKID_BE('MP3 '): +#ifdef USE_MAD + audioStream = Audio::makeMP3Stream(compressedStream, DisposeAfterUse::YES); +#endif + break; + case MKID_BE('OGG '): +#ifdef USE_VORBIS + audioStream = Audio::makeVorbisStream(compressedStream, DisposeAfterUse::YES); +#endif + break; + case MKID_BE('FLAC'): +#ifdef USE_FLAC + audioStream = Audio::makeFLACStream(compressedStream, DisposeAfterUse::YES); +#endif + break; } + + // Hopefully FLAC/OGG/MP3 are always 16-bit, otherwise we will get inaccuracies during sampleLen calculation + // TODO: Check if this is true, otherwise implement support for getting 8-bit/16-bit from stream in common + flags = Audio::FLAG_16BITS; } else { - // SCI1 or WAVE file - if (audioRes->size > 4) { - if (memcmp(audioRes->data, "RIFF", 4) == 0) { - // WAVE detected - Common::MemoryReadStream *waveStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO); - audioStream = Audio::makeWAVStream(waveStream, DisposeAfterUse::YES); + // Original source file + if (audioRes->_headerSize > 0) { + // SCI1.1 + Common::MemoryReadStream headerStream(audioRes->_header, audioRes->_headerSize, DisposeAfterUse::NO); + + if (readSOLHeader(&headerStream, audioRes->_headerSize, size, _audioRate, audioFlags)) { + Common::MemoryReadStream dataStream(audioRes->data, audioRes->size, DisposeAfterUse::NO); + data = readSOLAudio(&dataStream, size, audioFlags, flags); + } + } else { + // SCI1 or WAVE file + if (audioRes->size > 4) { + if (memcmp(audioRes->data, "RIFF", 4) == 0) { + // WAVE detected + Common::MemoryReadStream *waveStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO); + audioStream = Audio::makeWAVStream(waveStream, DisposeAfterUse::YES); + } + } + if (!audioStream) { + // SCI1 raw audio + size = audioRes->size; + data = (byte *)malloc(size); + assert(data); + memcpy(data, audioRes->data, size); + flags = Audio::FLAG_UNSIGNED; } } - if (!audioStream) { - // SCI1 raw audio - size = audioRes->size; - data = (byte *)malloc(size); - assert(data); - memcpy(data, audioRes->data, size); - flags = Audio::FLAG_UNSIGNED; - } - } - if (data) - audioStream = Audio::makeRawStream(data, size, _audioRate, flags); + if (data) + audioStream = Audio::makeRawStream(data, size, _audioRate, flags); + } if (audioStream) { *sampleLen = (flags & Audio::FLAG_16BITS ? size >> 1 : size) * 60 / _audioRate; -- cgit v1.2.3