/* 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "common/debug.h" #include "common/endian.h" #include "common/util.h" #include "common/stream.h" #include "common/textconsole.h" #include "common/list.h" #include "audio/audiostream.h" #include "audio/decoders/raw.h" #include "audio/decoders/voc.h" namespace Audio { namespace { bool checkVOCHeader(Common::ReadStream &stream) { VocFileHeader fileHeader; if (stream.read(&fileHeader, 8) != 8) return false; if (!memcmp(&fileHeader, "VTLK", 4)) { if (stream.read(&fileHeader, sizeof(VocFileHeader)) != sizeof(VocFileHeader)) return false; } else if (!memcmp(&fileHeader, "Creative", 8)) { if (stream.read(((byte *)&fileHeader) + 8, sizeof(VocFileHeader) - 8) != sizeof(VocFileHeader) - 8) return false; } else { return false; } if (memcmp(fileHeader.desc, "Creative Voice File", 19) != 0) return false; //if (fileHeader.desc[19] != 0x1A) // debug(3, "checkVOCHeader: Partially invalid header"); int32 offset = FROM_LE_16(fileHeader.datablock_offset); int16 version = FROM_LE_16(fileHeader.version); int16 code = FROM_LE_16(fileHeader.id); if (offset != sizeof(VocFileHeader)) return false; // 0x100 is an invalid VOC version used by German version of DOTT (Disk) and // French version of Simon the Sorcerer 2 (CD) if (version != 0x010A && version != 0x0114 && version != 0x0100) return false; if (code != ~version + 0x1234) return false; return true; } class VocStream : public SeekableAudioStream { public: VocStream(Common::SeekableReadStream *stream, bool isUnsigned, DisposeAfterUse::Flag disposeAfterUse); ~VocStream(); virtual int readBuffer(int16 *buffer, const int numSamples); virtual bool isStereo() const { return false; } virtual int getRate() const { return _rate; } virtual bool endOfData() const { return (_curBlock == _blocks.end()) && (_blockLeft == 0); } virtual bool seek(const Timestamp &where); virtual Timestamp getLength() const { return _length; } private: void preProcess(); Common::SeekableReadStream *const _stream; const DisposeAfterUse::Flag _disposeAfterUse; const bool _isUnsigned; int _rate; Timestamp _length; struct Block { uint8 code; uint32 length; union { struct { uint32 offset; int rate; int samples; } sampleBlock; struct { int count; } loopBlock; }; }; typedef Common::List BlockList; BlockList _blocks; BlockList::const_iterator _curBlock; uint32 _blockLeft; /** * Advance one block in the stream in case * the current one is empty. */ void updateBlockIfNeeded(); // Do some internal buffering for systems with really slow slow disk i/o enum { /** * How many samples we can buffer at once. * * TODO: Check whether this size suffices * for systems with slow disk I/O. */ kSampleBufferLength = 2048 }; byte _buffer[kSampleBufferLength]; /** * Fill the temporary sample buffer used in readBuffer. * * @param maxSamples Maximum samples to read. * @return actual count of samples read. */ int fillBuffer(int maxSamples); }; VocStream::VocStream(Common::SeekableReadStream *stream, bool isUnsigned, DisposeAfterUse::Flag disposeAfterUse) : _stream(stream), _disposeAfterUse(disposeAfterUse), _isUnsigned(isUnsigned), _rate(0), _length(), _blocks(), _curBlock(_blocks.end()), _blockLeft(0), _buffer() { preProcess(); } VocStream::~VocStream() { if (_disposeAfterUse == DisposeAfterUse::YES) delete _stream; } int VocStream::readBuffer(int16 *buffer, const int numSamples) { int samplesLeft = numSamples; while (samplesLeft > 0) { // Try to read up to "samplesLeft" samples. int len = fillBuffer(samplesLeft); // In case we were not able to read any samples // we will stop reading here. if (!len) break; // Adjust the samples left to read. samplesLeft -= len; // Copy the data to the caller's buffer. const byte *src = _buffer; while (len-- > 0) *buffer++ = (*src++ << 8) ^ (_isUnsigned ? 0x8000 : 0); } return numSamples - samplesLeft; } void VocStream::updateBlockIfNeeded() { // Have we now finished this block? If so, read the next block if (_blockLeft == 0 && _curBlock != _blocks.end()) { // Find the next sample block while (true) { // Next block ++_curBlock; // Check whether we reached the end of the stream // yet. if (_curBlock == _blocks.end()) return; // Skip all none sample blocks for now if (_curBlock->code != 1 && _curBlock->code != 9) continue; _stream->seek(_curBlock->sampleBlock.offset, SEEK_SET); // In case of an error we will stop // stream playback. if (_stream->err()) { _blockLeft = 0; _curBlock = _blocks.end(); } else { _blockLeft = _curBlock->sampleBlock.samples; } return; } } } int VocStream::fillBuffer(int maxSamples) { int bufferedSamples = 0; byte *dst = _buffer; // We can only read up to "kSampleBufferLength" samples // so we take this into consideration, when trying to // read up to maxSamples. maxSamples = MIN(kSampleBufferLength, maxSamples); // We will only read up to maxSamples while (maxSamples > 0 && !endOfData()) { // Calculate how many samples we can safely read // from the current block. const int len = MIN(maxSamples, _blockLeft); // Try to read all the sample data and update the // destination pointer. const int bytesRead = _stream->read(dst, len); dst += bytesRead; // Calculate how many samples we actually read. const int samplesRead = bytesRead; // Update all status variables bufferedSamples += samplesRead; maxSamples -= samplesRead; _blockLeft -= samplesRead; // In case of an error we will stop // stream playback. if (_stream->err()) { _blockLeft = 0; _curBlock = _blocks.end(); break; } // Advance to the next block in case the current // one is already finished. updateBlockIfNeeded(); } return bufferedSamples; } bool VocStream::seek(const Timestamp &where) { // Invalidate stream _blockLeft = 0; _curBlock = _blocks.end(); if (where > _length) return false; // Search for the block containing the requested sample const uint32 seekSample = convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames(); uint32 curSample = 0; for (_curBlock = _blocks.begin(); _curBlock != _blocks.end(); ++_curBlock) { // Skip all none sample blocks for now if (_curBlock->code != 1 && _curBlock->code != 9) continue; uint32 nextBlockSample = curSample + _curBlock->sampleBlock.samples; if (nextBlockSample > seekSample) break; curSample = nextBlockSample; } if (_curBlock == _blocks.end()) { return ((seekSample - curSample) == 0); } else { const uint32 offset = seekSample - curSample; _stream->seek(_curBlock->sampleBlock.offset + offset, SEEK_SET); // In case of an error we will stop // stream playback. if (_stream->err()) { _blockLeft = 0; _curBlock = _blocks.end(); } else { _blockLeft = _curBlock->sampleBlock.samples - offset; } return true; } } void VocStream::preProcess() { Block block; // Scan through the file and collect all blocks while (true) { block.code = _stream->readByte(); block.length = 0; // If we hit EOS here we found the end of the VOC file. // According to http://wiki.multimedia.cx/index.php?title=Creative_Voice // there is no need for an "Terminator" block to be present. // In case we hit a "Terminator" block we also break here. if (_stream->eos() || block.code == 0) break; // We will allow invalid block numbers as terminators. This is needed, // since some games ship broken VOC files. The following occasions are // known: // - 128 is used as terminator in Simon 1 Amiga CD32 // - Full Throttle contains a VOC file with an incorrect block length // resulting in a sample (127) to be read as block code. if (block.code > 9) { warning("VocStream::preProcess: Caught %d as terminator", block.code); break; } block.length = _stream->readByte(); block.length |= _stream->readByte() << 8; block.length |= _stream->readByte() << 16; // Premature end of stream => error! if (_stream->eos() || _stream->err()) { warning("VocStream::preProcess: Reading failed"); return; } uint32 skip = 0; switch (block.code) { // Sound data case 1: // Sound data (New format) case 9: if (block.code == 1) { if (block.length < 2) { warning("Invalid sound data block length %d in VOC file", block.length); return; } // Read header data int freqDiv = _stream->readByte(); // Prevent division through 0 if (freqDiv == 256) { warning("Invalid frequency divisor 256 in VOC file"); return; } block.sampleBlock.rate = getSampleRateFromVOCRate(freqDiv); int codec = _stream->readByte(); // We only support 8bit PCM if (codec != 0) { warning("Unhandled codec %d in VOC file", codec); return; } block.sampleBlock.samples = skip = block.length - 2; block.sampleBlock.offset = _stream->pos(); // Check the last block if there is any if (_blocks.size() > 0) { BlockList::iterator lastBlock = _blocks.end(); --lastBlock; // When we have found a block 8 as predecessor // we need to use its settings if (lastBlock->code == 8) { block.sampleBlock.rate = lastBlock->sampleBlock.rate; // Remove the block since we don't need it anymore _blocks.erase(lastBlock); } } } else { if (block.length < 12) { warning("Invalid sound data (wew format) block length %d in VOC file", block.length); return; } block.sampleBlock.rate = _stream->readUint32LE(); int bitsPerSample = _stream->readByte(); // We only support 8bit PCM if (bitsPerSample != 8) { warning("Unhandled bits per sample %d in VOC file", bitsPerSample); return; } int channels = _stream->readByte(); // We only support mono if (channels != 1) { warning("Unhandled channel count %d in VOC file", channels); return; } int codec = _stream->readUint16LE(); // We only support 8bit PCM if (codec != 0) { warning("Unhandled codec %d in VOC file", codec); return; } /*uint32 reserved = */_stream->readUint32LE(); block.sampleBlock.offset = _stream->pos(); block.sampleBlock.samples = skip = block.length - 12; } // Check whether we found a new highest rate if (_rate < block.sampleBlock.rate) _rate = block.sampleBlock.rate; break; // Silence case 3: { if (block.length != 3) { warning("Invalid silence block length %d in VOC file", block.length); return; } block.sampleBlock.offset = 0; block.sampleBlock.samples = _stream->readUint16LE() + 1; int freqDiv = _stream->readByte(); // Prevent division through 0 if (freqDiv == 256) { warning("Invalid frequency divisor 256 in VOC file"); return; } block.sampleBlock.rate = getSampleRateFromVOCRate(freqDiv); } break; // Repeat start case 6: if (block.length != 2) { warning("Invalid repeat start block length %d in VOC file", block.length); return; } block.loopBlock.count = _stream->readUint16LE() + 1; break; // Repeat end case 7: break; // Extra info case 8: { if (block.length != 4) return; int freqDiv = _stream->readUint16LE(); // Prevent division through 0 if (freqDiv == 65536) { warning("Invalid frequency divisor 65536 in VOC file"); return; } int codec = _stream->readByte(); // We only support RAW 8bit PCM. if (codec != 0) { warning("Unhandled codec %d in VOC file", codec); return; } int channels = _stream->readByte() + 1; // We only support mono sound right now if (channels != 1) { warning("Unhandled channel count %d in VOC file", channels); return; } block.sampleBlock.offset = 0; block.sampleBlock.samples = 0; block.sampleBlock.rate = 256000000L / (65536L - freqDiv); } break; default: warning("Unhandled code %d in VOC file (len %d)", block.code, block.length); // Skip the whole block and try to use the next one. skip = block.length; } // Premature end of stream => error! if (_stream->eos() || _stream->err()) { warning("VocStream::preProcess: Reading failed"); return; } // Skip the rest of the block if (skip) _stream->skip(skip); _blocks.push_back(block); } // Since we determined the sample rate we need for playback now, we will // initialize the play length. _length = Timestamp(0, _rate); // Calculate the total play time and do some more sanity checks for (BlockList::const_iterator i = _blocks.begin(), end = _blocks.end(); i != end; ++i) { // Check whether we found a block 8 which survived, this is not // allowed to happen! if (i->code == 8) { warning("VOC file contains unused block 8"); return; } // For now only use blocks with actual samples if (i->code != 1 && i->code != 9) continue; // Check the sample rate if (i->sampleBlock.rate != _rate) { warning("VOC file contains chunks with different sample rates (%d != %d)", _rate, i->sampleBlock.rate); return; } _length = _length.addFrames(i->sampleBlock.samples); } // Set the current block to the first block in the stream rewind(); } } // End of anonymous namespace int getSampleRateFromVOCRate(int vocSR) { if (vocSR == 0xa5 || vocSR == 0xa6) { return 11025; } else if (vocSR == 0xd2 || vocSR == 0xd3) { return 22050; } else { int sr = 1000000L / (256L - vocSR); // inexact sampling rates occur e.g. in the kitchen in Monkey Island, // very easy to reach right from the start of the game. //warning("inexact sample rate used: %i (0x%x)", sr, vocSR); return sr; } } SeekableAudioStream *makeVOCStream(Common::SeekableReadStream *stream, byte flags, DisposeAfterUse::Flag disposeAfterUse) { if (!checkVOCHeader(*stream)) { if (disposeAfterUse == DisposeAfterUse::YES) delete stream; return 0; } SeekableAudioStream *audioStream = new VocStream(stream, (flags & Audio::FLAG_UNSIGNED) != 0, disposeAfterUse); if (audioStream->endOfData()) { delete audioStream; return 0; } else { return audioStream; } } } // End of namespace Audio