Ticket #9066: streaming_voc_patch.diff
File streaming_voc_patch.diff, 15.6 KB (added by , 15 years ago) |
---|
-
sound/audiostream.cpp
173 173 } 174 174 175 175 176 176 177 #pragma mark - 178 #pragma mark --- LinearDiskStream --- 179 #pragma mark - 180 181 182 183 /** 184 * LinearDiskStream. This can stream linear (PCM) audio from disk. The 185 * function takes an pointer to an array of LinearDiskStreamAudioBlock which defines the 186 * start position and length of each block of uncompressed audio in the stream. 187 */ 188 template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE> 189 class LinearDiskStream : public AudioStream { 190 191 // Allow backends to override buffer size 192 #ifdef CUSTOM_AUDIO_BUFFER_SIZE 193 static const int32 BUFFER_SIZE = CUSTOM_AUDIO_BUFFER_SIZE; 194 #else 195 static const int32 BUFFER_SIZE = 16384; 196 #endif 197 198 protected: 199 byte* _buffer; ///< Streaming buffer 200 const byte *_ptr; ///< Pointer to current position in stream buffer 201 const int _rate; ///< Sample rate of stream 202 203 int32 _playtime; ///< Calculated total play time 204 Common::SeekableReadStream* _stream; ///< Stream to read data from 205 int32 _filePos; ///< Current position in stream 206 int32 _diskLeft; ///< Samples left in stream in current block not yet read to buffer 207 int32 _bufferLeft; ///< Samples left in buffer in current block 208 bool _ownsStream; ///< If true, delete stream object when LinearDiskStream is destructed 209 210 LinearDiskStreamAudioBlock* _audioBlock; ///< Audio block list 211 int _audioBlockCount; ///< Number of blocks in _audioBlock 212 int _currentBlock; ///< Current audio block number 213 214 int _beginLoop; ///< Loop parameter, currently not implemented 215 int _endLoop; ///< Loop parameter, currently not implemented 216 217 218 public: 219 LinearDiskStream(int rate, uint beginLoop, uint endLoop, bool takeOwnership, Common::SeekableReadStream* stream, LinearDiskStreamAudioBlock* block, uint numBlocks) 220 : _rate(rate), _stream(stream), _beginLoop(beginLoop), _endLoop(endLoop), _ownsStream(takeOwnership), 221 _audioBlockCount(numBlocks) { 222 223 // Allocate streaming buffer 224 if (is16Bit) { 225 _buffer = (byte *) malloc(BUFFER_SIZE * sizeof(int16)); 226 } else { 227 _buffer = (byte *) malloc(BUFFER_SIZE * sizeof(byte)); 228 } 229 _ptr = _buffer; 230 _bufferLeft = 0; 231 232 // Copy audio block data to our buffer 233 int blockDataSize = numBlocks * sizeof(LinearDiskStreamAudioBlock); 234 _audioBlock = (LinearDiskStreamAudioBlock *) malloc(blockDataSize); 235 memcpy(_audioBlock, block, blockDataSize); 236 237 // Set current buffer state, playing first block 238 _currentBlock = 0; 239 _filePos = _audioBlock[_currentBlock].pos; 240 _diskLeft = _audioBlock[_currentBlock].len; 241 242 // Add up length of all blocks in order to caluclate total play time 243 int len = 0; 244 for (int r = 0; r < _audioBlockCount; r++) { 245 len += _audioBlock[r].len; 246 } 247 _playtime = calculatePlayTime(rate, len / (is16Bit ? 2 : 1) / (stereo ? 2 : 1)); 248 } 249 250 251 virtual ~LinearDiskStream() { 252 if (_ownsStream) { 253 delete _stream; 254 } 255 256 free(_buffer); 257 } 258 int readBuffer(int16 *buffer, const int numSamples); 259 260 bool isStereo() const { return stereo; } 261 bool endOfData() const { return (_currentBlock == _audioBlockCount - 1) && (_diskLeft == 0) && (_bufferLeft == 0); } 262 263 int getRate() const { return _rate; } 264 int32 getTotalPlayTime() const { return _playtime; } 265 }; 266 267 template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE> 268 int LinearDiskStream<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buffer, const int numSamples) { 269 int oldPos = _stream->pos(); 270 bool restoreFilePosition = false; 271 272 int samples = numSamples; 273 274 while (samples > 0 && ((_diskLeft > 0 || _bufferLeft > 0) || (_currentBlock != _audioBlockCount - 1)) ) { 275 276 // Output samples in the buffer to the output 277 int len = MIN(samples, _bufferLeft); 278 samples -= len; 279 _bufferLeft -= len; 280 281 while (len > 0) { 282 *buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _ptr, isLE); 283 _ptr += (is16Bit ? 2 : 1); 284 len--; 285 } 286 287 // Have we now finished this block? If so, read the next block 288 if ((_bufferLeft == 0) && (_diskLeft == 0) && (_currentBlock != _audioBlockCount - 1)) { 289 // Next block 290 _currentBlock++; 291 292 _filePos = _audioBlock[_currentBlock].pos; 293 _diskLeft = _audioBlock[_currentBlock].len; 294 } 295 296 297 // Now read more data from disk if there is more to be read 298 if ((_bufferLeft == 0) && (_diskLeft > 0)) { 299 int32 readAmount = MIN(_diskLeft, BUFFER_SIZE); 300 301 _stream->seek(_filePos, SEEK_SET); 302 _stream->read(_buffer, readAmount * (is16Bit? 2: 1)); 303 304 // Amount of data in buffer is now the amount read in, and 305 // the amount left to read on disk is decreased by the same amount 306 _bufferLeft = readAmount; 307 _diskLeft -= readAmount; 308 _ptr = (byte*) _buffer; 309 _filePos += readAmount * (is16Bit? 2: 1); 310 311 // Set this flag now we've used the file, it restores it's 312 // original position. 313 restoreFilePosition = true; 314 } 315 316 317 } 318 319 // In case calling code relies on the position of this stream staying 320 // constant, I restore the location if I've changed it. This is probably 321 // not necessary. 322 if (restoreFilePosition) { 323 _stream->seek(oldPos, SEEK_SET); 324 } 325 326 return numSamples-samples; 327 } 328 329 330 331 #pragma mark - 177 332 #pragma mark --- Input stream factory --- 178 333 #pragma mark - 179 334 … … 202 357 const bool isLE = (flags & Audio::Mixer::FLAG_LITTLE_ENDIAN) != 0; 203 358 const bool autoFree = (flags & Audio::Mixer::FLAG_AUTOFREE) != 0; 204 359 360 205 361 uint loopOffset = 0, loopLen = 0; 206 362 if (flags & Audio::Mixer::FLAG_LOOP) { 207 363 if (loopEnd == 0) … … 236 392 } 237 393 238 394 395 396 397 398 #define MAKE_LINEAR_DISK(STEREO, UNSIGNED) \ 399 if (is16Bit) { \ 400 if (isLE) \ 401 return new LinearDiskStream<STEREO, true, UNSIGNED, true>(rate, loopStart, loopEnd, takeOwnership, &stream, block, numBlocks); \ 402 else \ 403 return new LinearDiskStream<STEREO, true, UNSIGNED, false>(rate, loopStart, loopEnd, takeOwnership, &stream, block, numBlocks); \ 404 } else \ 405 return new LinearDiskStream<STEREO, false, UNSIGNED, false>(rate, loopStart, loopEnd, takeOwnership, &stream, block, numBlocks) 406 407 408 AudioStream *makeLinearDiskStream(Common::SeekableReadStream& stream, LinearDiskStreamAudioBlock* block, int numBlocks, int rate, byte flags, bool takeOwnership, uint loopStart, uint loopEnd) { 409 const bool isStereo = (flags & Audio::Mixer::FLAG_STEREO) != 0; 410 const bool is16Bit = (flags & Audio::Mixer::FLAG_16BITS) != 0; 411 const bool isUnsigned = (flags & Audio::Mixer::FLAG_UNSIGNED) != 0; 412 const bool isLE = (flags & Audio::Mixer::FLAG_LITTLE_ENDIAN) != 0; 413 414 415 if (isStereo) { 416 if (isUnsigned) { 417 MAKE_LINEAR_DISK(true, true); 418 } else { 419 MAKE_LINEAR_DISK(true, false); 420 } 421 } else { 422 if (isUnsigned) { 423 MAKE_LINEAR_DISK(false, true); 424 } else { 425 MAKE_LINEAR_DISK(false, false); 426 } 427 } 428 } 429 430 431 432 239 433 #pragma mark - 240 434 #pragma mark --- Appendable audio stream --- 241 435 #pragma mark - … … 306 500 template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE> 307 501 int AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buffer, const int numSamples) { 308 502 Common::StackLock lock(_mutex); 309 310 503 int samples = numSamples; 311 504 while (samples > 0 && !eosIntern()) { 312 505 Buffer buf = *_bufferQueue.begin(); -
sound/voc.cpp
166 166 return loadVOCFromStream(stream, size, rate, loops, begin_loop, end_loop); 167 167 } 168 168 169 AudioStream *makeVOCStream(Common::ReadStream &stream, byte flags, uint loopStart, uint loopEnd) { 169 170 #ifdef STREAM_AUDIO_FROM_DISK 171 172 int parseVOCFormat(Common::SeekableReadStream& stream, LinearDiskStreamAudioBlock* block, int &rate, int &loops, int &begin_loop, int &end_loop) { 173 VocFileHeader fileHeader; 174 int currentBlock = 0; 175 int size = 0; 176 177 if (stream.read(&fileHeader, 8) != 8) 178 goto invalid; 179 180 if (!memcmp(&fileHeader, "VTLK", 4)) { 181 if (stream.read(&fileHeader, sizeof(VocFileHeader)) != sizeof(VocFileHeader)) 182 goto invalid; 183 } else if (!memcmp(&fileHeader, "Creative", 8)) { 184 if (stream.read(((byte *)&fileHeader) + 8, sizeof(VocFileHeader) - 8) != sizeof(VocFileHeader) - 8) 185 goto invalid; 186 } else { 187 invalid:; 188 warning("loadVOCFromStream: Invalid header"); 189 return 0; 190 } 191 192 if (memcmp(fileHeader.desc, "Creative Voice File", 19) != 0) 193 error("loadVOCFromStream: Invalid header"); 194 if (fileHeader.desc[19] != 0x1A) 195 debug(3, "loadVOCFromStream: Partially invalid header"); 196 197 int32 offset = FROM_LE_16(fileHeader.datablock_offset); 198 int16 version = FROM_LE_16(fileHeader.version); 199 int16 code = FROM_LE_16(fileHeader.id); 200 assert(offset == sizeof(VocFileHeader)); 201 // 0x100 is an invalid VOC version used by German version of DOTT (Disk) and 202 // French version of Simon the Sorcerer 2 (CD) 203 assert(version == 0x010A || version == 0x0114 || version == 0x0100); 204 assert(code == ~version + 0x1234); 205 206 int len; 207 byte *ret_sound = 0; 208 size = 0; 209 begin_loop = 0; 210 end_loop = 0; 211 212 while ((code = stream.readByte())) { 213 len = stream.readByte(); 214 len |= stream.readByte() << 8; 215 len |= stream.readByte() << 16; 216 217 switch (code) { 218 case 1: 219 case 9: { 220 int packing; 221 if (code == 1) { 222 int time_constant = stream.readByte(); 223 packing = stream.readByte(); 224 len -= 2; 225 rate = getSampleRateFromVOCRate(time_constant); 226 } else { 227 rate = stream.readUint32LE(); 228 int bits = stream.readByte(); 229 int channels = stream.readByte(); 230 if (bits != 8 || channels != 1) { 231 warning("Unsupported VOC file format (%d bits per sample, %d channels)", bits, channels); 232 break; 233 } 234 packing = stream.readUint16LE(); 235 stream.readUint32LE(); 236 len -= 12; 237 } 238 debug(9, "VOC Data Block: %d, %d, %d", rate, packing, len); 239 if (packing == 0) { 240 241 // Found a data block - so add it to the block list 242 block[currentBlock].pos = stream.pos(); 243 block[currentBlock].len = len; 244 currentBlock++; 245 246 stream.seek(len, SEEK_CUR); 247 248 size += len; 249 begin_loop = size; 250 end_loop = size; 251 } else { 252 warning("VOC file packing %d unsupported", packing); 253 } 254 } break; 255 case 3: // silence 256 // occur with a few Igor sounds, voc file starts with a silence block with a 257 // frequency different from the data block. Just ignore fow now (implementing 258 // it wouldn't make a big difference anyway...) 259 assert(len == 3); 260 stream.readUint16LE(); 261 stream.readByte(); 262 break; 263 case 6: // begin of loop 264 assert(len == 2); 265 loops = stream.readUint16LE(); 266 break; 267 case 7: // end of loop 268 assert(len == 0); 269 break; 270 case 8: // "Extended" 271 // This occures in the LoL Intro demo. This block can usually be used to create stereo 272 // sound, but the LoL intro has only an empty block, thus this dummy implementation will 273 // work. 274 assert(len == 4); 275 stream.readUint16LE(); 276 stream.readByte(); 277 stream.readByte(); 278 break; 279 default: 280 warning("Unhandled code in VOC file : %d", code); 281 return 0; 282 } 283 } 284 debug(4, "VOC Data Size : %d", size); 285 return currentBlock; 286 } 287 288 AudioStream *makeVOCDiskStream(Common::SeekableReadStream& stream, byte flags, bool takeOwnership) { 289 const bool isUnsigned = (flags & Audio::Mixer::FLAG_UNSIGNED) != 0; 290 const bool isLE = (flags & Audio::Mixer::FLAG_LITTLE_ENDIAN) != 0; 291 const int MAX_AUDIO_BLOCKS = 256; 292 293 LinearDiskStreamAudioBlock* block = new LinearDiskStreamAudioBlock[MAX_AUDIO_BLOCKS]; 294 int rate, loops, begin_loop, end_loop; 295 296 int numBlocks = parseVOCFormat(stream, block, rate, loops, begin_loop, end_loop); 297 298 return makeLinearDiskStream(stream, block, numBlocks, rate, flags, takeOwnership, begin_loop, end_loop); 299 } 300 301 #endif 302 303 304 AudioStream *makeVOCStream(Common::SeekableReadStream &stream, byte flags, uint loopStart, uint loopEnd, bool takeOwnershipOfStream) { 170 305 int size, rate; 171 306 307 #ifdef STREAM_AUDIO_FROM_DISK 308 309 return makeVOCDiskStream(stream, flags, takeOwnershipOfStream); 310 311 #else 172 312 byte *data = loadVOCFromStream(stream, size, rate); 313 173 314 if (!data) 174 315 return 0; 175 316 176 317 return makeLinearInputStream(data, size, rate, flags | Audio::Mixer::FLAG_AUTOFREE, loopStart, loopEnd); 318 #endif 177 319 } 178 320 179 321 -
sound/audiostream.h
28 28 29 29 #include "common/util.h" 30 30 #include "common/scummsys.h" 31 #include "common/stream.h" 31 32 32 33 33 namespace Audio { 34 34 35 35 /** … … 109 109 virtual int32 getTotalPlayTime() const { return kUnknownPlayTime; } 110 110 }; 111 111 112 112 113 /** 113 114 * Factory function for a raw linear AudioStream, which will simply treat all data 114 115 * in the buffer described by ptr and len as raw sample data in the specified … … 118 119 */ 119 120 AudioStream *makeLinearInputStream(const byte *ptr, uint32 len, int rate, byte flags, uint loopStart, uint loopEnd); 120 121 122 123 /** Struct used to define the audio data to be played by a LinearDiskStream */ 124 125 struct LinearDiskStreamAudioBlock { 126 int32 pos; ///< Position in stream of the block 127 int32 len; ///< Length of the block (in samples) 128 }; 129 130 131 /** Factory function for a Linear Disk Stream. This can stream linear (PCM) audio from disk. The 132 * function takes an pointer to an array of LinearDiskStreamAudioBlock which defines the 133 * start position and length of each block of uncompressed audio in the stream. 134 */ 135 136 AudioStream *makeLinearDiskStream(Common::SeekableReadStream& stream, LinearDiskStreamAudioBlock* block, int 137 numBlocks, int rate, byte flags, bool takeOwnership, uint loopStart, uint loopEnd); 138 121 139 /** 122 140 * An audio stream to which additional data can be appended on-the-fly. 123 141 * Used by SMUSH, iMuseDigital, the Kyrandia 3 VQA player, etc. -
sound/voc.h
41 41 #include "common/scummsys.h" 42 42 43 43 namespace Common { class ReadStream; } 44 namespace Common { class SeekableReadStream; } 44 45 45 46 namespace Audio { 46 47 … … 78 79 extern int getSampleRateFromVOCRate(int vocSR); 79 80 80 81 /** 81 * Try to load a VOC from the given s eekable stream. Returns a pointer to memory82 * Try to load a VOC from the given stream. Returns a pointer to memory 82 83 * containing the PCM sample data (allocated with malloc). It is the callers 83 84 * responsibility to dellocate that data again later on! Currently this 84 85 * function only supports uncompressed raw PCM data. … … 92 93 * 93 94 * This function uses loadVOCFromStream() internally. 94 95 */ 95 AudioStream *makeVOCStream(Common:: ReadStream &stream, byte flags = 0, uint loopStart = 0, uint loopEnd = 0);96 AudioStream *makeVOCStream(Common::SeekableReadStream &stream, byte flags = 0, uint loopStart = 0, uint loopEnd = 0, bool takeOwnershipOfStream = false); 96 97 97 98 } // End of namespace Audio 98 99 -
engines/kyra/sound.cpp
235 235 // A simple wrapper to create VOC streams the way like creating MP3, OGG/Vorbis and FLAC streams. 236 236 // Possible TODO: Think of making this complete and moving it to sound/voc.cpp ? 237 237 Audio::AudioStream *makeVOCStream(Common::SeekableReadStream *stream, bool disposeAfterUse, uint32 startTime, uint32 duration, uint numLoops) { 238 239 #ifdef STREAM_AUDIO_FROM_DISK 240 Audio::AudioStream *as = Audio::makeVOCStream(*stream, Audio::Mixer::FLAG_UNSIGNED, 0, 0, disposeAfterUse); 241 #else 238 242 Audio::AudioStream *as = Audio::makeVOCStream(*stream, Audio::Mixer::FLAG_UNSIGNED); 239 243 240 244 if (disposeAfterUse) 241 245 delete stream; 246 #endif 242 247 243 248 return as; 244 249 }