From 8c8abca6f80fc63b5c11fa43319cdf56b4845660 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 28 Feb 2007 14:48:26 +0000 Subject: Changed the AppendableAudioStream code to use a queue of buffers, instead of a fixed size wrap-around memory buffer (this reduces memory usage in some cases by 500-700k, while actually being more flexible) svn-id: r25909 --- engines/kyra/vqa.cpp | 33 +++++---- engines/scumm/imuse_digi/dimuse.cpp | 9 +-- engines/scumm/imuse_digi/dimuse_sndmgr.cpp | 4 +- engines/scumm/imuse_digi/dimuse_track.cpp | 6 +- engines/scumm/smush/smush_mixer.cpp | 10 ++- engines/scumm/smush/smush_player.cpp | 8 +- engines/sword1/sound.cpp | 6 +- sound/audiostream.cpp | 114 +++++++++++++++-------------- sound/audiostream.h | 32 +++++++- 9 files changed, 127 insertions(+), 95 deletions(-) diff --git a/engines/kyra/vqa.cpp b/engines/kyra/vqa.cpp index 3660bcf217..603b67221b 100644 --- a/engines/kyra/vqa.cpp +++ b/engines/kyra/vqa.cpp @@ -268,8 +268,8 @@ bool VQAMovie::open(const char *filename) { _numPartialCodeBooks = 0; if (_header.flags & 1) { - // A 2-second buffer ought to be enough - _stream = Audio::makeAppendableAudioStream(_header.freq, Audio::Mixer::FLAG_UNSIGNED, 2 * _header.freq * _header.channels); + // TODO/FIXME: Shouldn't we set FLAG_STEREO if _header.channels == 2 (wonders Fingolfin) + _stream = Audio::makeAppendableAudioStream(_header.freq, Audio::Mixer::FLAG_UNSIGNED); } else { _stream = NULL; } @@ -399,9 +399,10 @@ void VQAMovie::displayFrame(uint frameNum) { switch (tag) { case MKID_BE('SND0'): // Uncompressed sound foundSound = true; - inbuf = (byte *)allocBuffer(0, size); + inbuf = new byte[size]; _file.read(inbuf, size); - _stream->append(inbuf, size); + assert(_stream); + _stream->queueBuffer(inbuf, size); break; case MKID_BE('SND1'): // Compressed sound, almost like AUD @@ -409,15 +410,18 @@ void VQAMovie::displayFrame(uint frameNum) { outsize = _file.readUint16LE(); insize = _file.readUint16LE(); - inbuf = (byte *)allocBuffer(0, insize); + inbuf = new byte[insize]; _file.read(inbuf, insize); if (insize == outsize) { - _stream->append(inbuf, insize); + assert(_stream); + _stream->queueBuffer(inbuf, insize); } else { - outbuf = (byte *)allocBuffer(1, outsize); + outbuf = new byte[outsize]; decodeSND1(inbuf, insize, outbuf, outsize); - _stream->append(outbuf, outsize); + assert(_stream); + _stream->queueBuffer(outbuf, outsize); + delete[] inbuf; } break; @@ -589,24 +593,25 @@ void VQAMovie::play() { switch (tag) { case MKID_BE('SND0'): // Uncompressed sound - inbuf = (byte *)allocBuffer(0, size); + inbuf = new byte[size]; _file.read(inbuf, size); - _stream->append(inbuf, size); + _stream->queueBuffer(inbuf, size); break; case MKID_BE('SND1'): // Compressed sound outsize = _file.readUint16LE(); insize = _file.readUint16LE(); - inbuf = (byte *)allocBuffer(0, insize); + inbuf = new byte[insize]; _file.read(inbuf, insize); if (insize == outsize) { - _stream->append(inbuf, insize); + _stream->queueBuffer(inbuf, insize); } else { - outbuf = (byte *)allocBuffer(1, outsize); + outbuf = new byte[outsize]; decodeSND1(inbuf, insize, outbuf, outsize); - _stream->append(outbuf, outsize); + _stream->queueBuffer(outbuf, outsize); + delete[] inbuf; } break; diff --git a/engines/scumm/imuse_digi/dimuse.cpp b/engines/scumm/imuse_digi/dimuse.cpp index 7e70268b12..c249dd57a9 100644 --- a/engines/scumm/imuse_digi/dimuse.cpp +++ b/engines/scumm/imuse_digi/dimuse.cpp @@ -188,9 +188,8 @@ void IMuseDigital::saveOrLoad(Serializer *ser) { track->mixerFlags |= Audio::Mixer::FLAG_LITTLE_ENDIAN; #endif - int32 streamBufferSize = track->iteration; track->stream2 = NULL; - track->stream = Audio::makeAppendableAudioStream(freq, track->mixerFlags, streamBufferSize); + track->stream = Audio::makeAppendableAudioStream(freq, track->mixerFlags); const int pan = (track->pan != 64) ? 2 * track->pan - 127 : 0; const int vol = track->vol / 1000; @@ -324,10 +323,10 @@ void IMuseDigital::callback() { if (_mixer->isReady()) { _mixer->setChannelVolume(track->handle, vol); _mixer->setChannelBalance(track->handle, pan); - track->stream->append(data, result); + track->stream->queueBuffer(data, result); track->regionOffset += result; - } - free(data); + } else + delete[] data; if (_sound->isEndOfRegion(track->soundHandle, track->curRegion)) { switchToNextRegion(track); diff --git a/engines/scumm/imuse_digi/dimuse_sndmgr.cpp b/engines/scumm/imuse_digi/dimuse_sndmgr.cpp index 2b88f37b17..5c7fa5b613 100644 --- a/engines/scumm/imuse_digi/dimuse_sndmgr.cpp +++ b/engines/scumm/imuse_digi/dimuse_sndmgr.cpp @@ -583,11 +583,11 @@ int32 ImuseDigiSndMgr::getDataFromRegion(soundStruct *soundHandle, int region, b if ((soundHandle->bundle) && (!soundHandle->compressed)) { size = soundHandle->bundle->decompressSampleByCurIndex(start + offset, size, buf, header_size, header_outside); } else if (soundHandle->resPtr) { - *buf = (byte *)malloc(size); + *buf = new byte[size]; assert(*buf); memcpy(*buf, soundHandle->resPtr + start + offset + header_size, size); } else if ((soundHandle->bundle) && (soundHandle->compressed)) { - *buf = (byte *)malloc(size); + *buf = new byte[size]; assert(*buf); char fileName[24]; sprintf(fileName, "%s_reg%03d", soundHandle->name, region); diff --git a/engines/scumm/imuse_digi/dimuse_track.cpp b/engines/scumm/imuse_digi/dimuse_track.cpp index 37ae22f47f..6acb2a0318 100644 --- a/engines/scumm/imuse_digi/dimuse_track.cpp +++ b/engines/scumm/imuse_digi/dimuse_track.cpp @@ -176,9 +176,8 @@ void IMuseDigital::startSound(int soundId, const char *soundName, int soundType, type = Audio::Mixer::kMusicSoundType; // setup 1 second stream wrapped buffer - int32 streamBufferSize = track->iteration; track->stream2 = NULL; - track->stream = Audio::makeAppendableAudioStream(freq, track->mixerFlags, streamBufferSize); + track->stream = Audio::makeAppendableAudioStream(freq, track->mixerFlags); _mixer->playInputStream(type, &track->handle, track->stream, -1, vol, pan, false); track->started = true; } @@ -356,8 +355,7 @@ IMuseDigital::Track *IMuseDigital::cloneToFadeOutTrack(Track *track, int fadeDel type = Audio::Mixer::kMusicSoundType; // setup 1 second stream wrapped buffer - int32 streamBufferSize = fadeTrack->iteration; - fadeTrack->stream = Audio::makeAppendableAudioStream(_sound->getFreq(fadeTrack->soundHandle), fadeTrack->mixerFlags, streamBufferSize); + fadeTrack->stream = Audio::makeAppendableAudioStream(_sound->getFreq(fadeTrack->soundHandle), fadeTrack->mixerFlags); _mixer->playInputStream(type, &fadeTrack->handle, fadeTrack->stream, -1, fadeTrack->vol / 1000, fadeTrack->pan, false); fadeTrack->started = true; fadeTrack->used = true; diff --git a/engines/scumm/smush/smush_mixer.cpp b/engines/scumm/smush/smush_mixer.cpp index 9e6631a365..e29a921224 100644 --- a/engines/scumm/smush/smush_mixer.cpp +++ b/engines/scumm/smush/smush_mixer.cpp @@ -105,6 +105,7 @@ bool SmushMixer::handleFrame() { _channels[i].chan->getParameters(stereo, is_16bit, vol, pan); + // Grab the audio data from the channel int32 size = _channels[i].chan->getAvailableSoundDataSize(); byte *data = _channels[i].chan->getSoundData(); @@ -116,15 +117,16 @@ bool SmushMixer::handleFrame() { } if (_mixer->isReady()) { + // Stream the data if (!_channels[i].stream) { - _channels[i].stream = Audio::makeAppendableAudioStream(_channels[i].chan->getRate(), flags, 500000); + _channels[i].stream = Audio::makeAppendableAudioStream(_channels[i].chan->getRate(), flags); _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_channels[i].handle, _channels[i].stream); } _mixer->setChannelVolume(_channels[i].handle, vol); _mixer->setChannelBalance(_channels[i].handle, pan); - _channels[i].stream->append(data, size); - } - delete[] data; + _channels[i].stream->queueBuffer(data, size); // The stream will free the buffer for us + } else + delete[] data; } } } diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp index d4b3352be4..3310415f30 100644 --- a/engines/scumm/smush/smush_player.cpp +++ b/engines/scumm/smush/smush_player.cpp @@ -462,7 +462,7 @@ void SmushPlayer::handleIACT(Chunk &b) { c->checkParameters(index, nbframes, size, track_flags, unknown); c->appendData(b, bsize); } else { - byte output_data[4096]; + // TODO: Move this code into another SmushChannel subclass? byte *src = (byte *)malloc(bsize); b.read(src, bsize); byte *d_src = src; @@ -477,6 +477,8 @@ void SmushPlayer::handleIACT(Chunk &b) { _IACTpos += bsize; bsize = 0; } else { + byte *output_data = new byte[4096]; + memcpy(_IACToutput + _IACTpos, d_src, len); byte *dst = output_data; byte *d_src2 = _IACToutput; @@ -507,10 +509,10 @@ void SmushPlayer::handleIACT(Chunk &b) { } while (--count); if (!_IACTstream) { - _IACTstream = Audio::makeAppendableAudioStream(22050, Audio::Mixer::FLAG_STEREO | Audio::Mixer::FLAG_16BITS, 900000); + _IACTstream = Audio::makeAppendableAudioStream(22050, Audio::Mixer::FLAG_STEREO | Audio::Mixer::FLAG_16BITS); _vm->_mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_IACTchannel, _IACTstream); } - _IACTstream->append(output_data, 0x1000); + _IACTstream->queueBuffer(output_data, 0x1000); bsize -= len; d_src += len; diff --git a/engines/sword1/sound.cpp b/engines/sword1/sound.cpp index fd0a37003e..6c255cb4ce 100644 --- a/engines/sword1/sound.cpp +++ b/engines/sword1/sound.cpp @@ -382,10 +382,8 @@ void Sound::initCowSystem(void) { } void Sound::closeCowSystem(void) { - if (_cowFile.isOpen()) - _cowFile.close(); - if (_cowHeader) - free(_cowHeader); + _cowFile.close(); + free(_cowHeader); _cowHeader = NULL; _currentCowFile = 0; } diff --git a/sound/audiostream.cpp b/sound/audiostream.cpp index d3c0e2e745..54b0221199 100644 --- a/sound/audiostream.cpp +++ b/sound/audiostream.cpp @@ -23,6 +23,7 @@ #include "common/stdafx.h" #include "common/endian.h" #include "common/file.h" +#include "common/list.h" #include "common/util.h" #include "sound/audiostream.h" @@ -128,7 +129,6 @@ protected: const int _rate; const byte *_origPtr; - inline bool eosIntern() const { return _ptr >= _end; }; public: LinearMemoryStream(int rate, const byte *ptr, uint len, uint loopOffset, uint loopLen, bool autoFreeMemory) : _ptr(ptr), _end(ptr+len), _loopPtr(0), _loopEnd(0), _rate(rate) { @@ -154,7 +154,7 @@ public: int readBuffer(int16 *buffer, const int numSamples); bool isStereo() const { return stereo; } - bool endOfData() const { return eosIntern(); } + bool endOfData() const { return _ptr >= _end; } int getRate() const { return _rate; } }; @@ -162,7 +162,7 @@ public: template int LinearMemoryStream::readBuffer(int16 *buffer, const int numSamples) { int samples = 0; - while (samples < numSamples && !eosIntern()) { + while (samples < numSamples && _ptr < _end) { const int len = MIN(numSamples, samples + (int)(_end - _ptr) / (is16Bit ? 2 : 1)); while (samples < len) { *buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _ptr, isLE); @@ -170,7 +170,7 @@ int LinearMemoryStream::readBuffer(int16 *buf samples++; } // Loop, if looping was specified - if (_loopPtr && eosIntern()) { + if (_loopPtr && _ptr >= _end) { _ptr = _loopPtr; _end = _loopEnd; } @@ -228,6 +228,10 @@ AudioStream *makeLinearInputStream(int rate, byte flags, const byte *ptr, uint32 #pragma mark --- Appendable audio stream --- #pragma mark - +struct Buffer { + byte *start; + byte *end; +}; /** * Wrapped memory stream. @@ -235,18 +239,22 @@ AudioStream *makeLinearInputStream(int rate, byte flags, const byte *ptr, uint32 template class AppendableMemoryStream : public AppendableAudioStream { protected: + + // A mutex to avoid access problems (causing e.g. corruption of + // the linked list) in thread aware environments. Common::Mutex _mutex; - byte *_bufferStart; - byte *_bufferEnd; - byte *_pos; - byte *_end; + // List of all queueud buffers + Common::List _bufferQueue; + + // Position in the front buffer, if any bool _finalized; const int _rate; + byte *_pos; - inline bool eosIntern() const { return _end == _pos; }; + inline bool eosIntern() const { return _bufferQueue.empty(); }; public: - AppendableMemoryStream(int rate, uint bufferSize); + AppendableMemoryStream(int rate); ~AppendableMemoryStream(); int readBuffer(int16 *buffer, const int numSamples); @@ -256,30 +264,22 @@ public: int getRate() const { return _rate; } - void append(const byte *data, uint32 len); + void queueBuffer(byte *data, uint32 size); void finish() { _finalized = true; } }; template -AppendableMemoryStream::AppendableMemoryStream(int rate, uint bufferSize) - : _finalized(false), _rate(rate) { - - // Verify the buffer size is sane - if (is16Bit && stereo) - assert((bufferSize & 3) == 0); - else if (is16Bit || stereo) - assert((bufferSize & 1) == 0); - - _bufferStart = (byte *)malloc(bufferSize); - assert(_bufferStart != NULL); +AppendableMemoryStream::AppendableMemoryStream(int rate) + : _finalized(false), _rate(rate), _pos(0) { - _pos = _end = _bufferStart; - _bufferEnd = _bufferStart + bufferSize; } template AppendableMemoryStream::~AppendableMemoryStream() { - free(_bufferStart); + // Clear the queue + Common::List::iterator iter; + for (iter = _bufferQueue.begin(); iter != _bufferQueue.end(); ++iter) + delete[] iter->start; } template @@ -288,12 +288,19 @@ int AppendableMemoryStream::readBuffer(int16 int samples = 0; while (samples < numSamples && !eosIntern()) { - // Wrap around? - if (_pos >= _bufferEnd) - _pos = _pos - (_bufferEnd - _bufferStart); + Buffer buf = *_bufferQueue.begin(); + if (_pos == 0) + _pos = buf.start; + + assert(buf.start <= _pos && _pos <= buf.end); + const int samplesLeftInCurBuffer = buf.end - _pos; + if (samplesLeftInCurBuffer == 0) { + _bufferQueue.erase(_bufferQueue.begin()); + _pos = 0; + continue; + } - const byte *endMarker = (_pos > _end) ? _bufferEnd : _end; - const int len = MIN(numSamples, samples + (int)(endMarker - _pos) / (is16Bit ? 2 : 1)); + const int len = MIN(numSamples, samples + samplesLeftInCurBuffer / (is16Bit ? 2 : 1)); while (samples < len) { *buffer++ = READ_ENDIAN_SAMPLE(is16Bit, isUnsigned, _pos, isLE); _pos += (is16Bit ? 2 : 1); @@ -305,50 +312,45 @@ int AppendableMemoryStream::readBuffer(int16 } template -void AppendableMemoryStream::append(const byte *data, uint32 len) { +void AppendableMemoryStream::queueBuffer(byte *data, uint32 size) { Common::StackLock lock(_mutex); // Verify the buffer size is sane if (is16Bit && stereo) - assert((len & 3) == 0); + assert((size & 3) == 0); else if (is16Bit || stereo) - assert((len & 1) == 0); + assert((size & 1) == 0); // Verify that the stream has not yet been finalized (by a call to finish()) assert(!_finalized); - if (_end + len > _bufferEnd) { - // Wrap-around case - uint32 size_to_end_of_buffer = _bufferEnd - _end; - len -= size_to_end_of_buffer; - if ((_end < _pos) || (_bufferStart + len >= _pos)) { - debug(2, "AppendableMemoryStream: buffer overflow (A)"); - return; - } - memcpy(_end, data, size_to_end_of_buffer); - memcpy(_bufferStart, data + size_to_end_of_buffer, len); - _end = _bufferStart + len; - } else { - if ((_end < _pos) && (_end + len >= _pos)) { - debug(2, "AppendableMemoryStream: buffer overflow (B)"); - return; - } - memcpy(_end, data, len); - _end += len; - } + // Queue the buffer + Buffer buf = {data, data+size}; + _bufferQueue.push_back(buf); + + +#if 0 + // Output some stats + uint totalSize = 0; + Common::List::iterator iter; + for (iter = _bufferQueue.begin(); iter != _bufferQueue.end(); ++iter) + totalSize += iter->end - iter->start; + printf("AppendableMemoryStream::queueBuffer: added a %d byte buf, a total of %d bytes are queued\n", + size, totalSize); +#endif } #define MAKE_WRAPPED(STEREO, UNSIGNED) \ if (is16Bit) { \ if (isLE) \ - return new AppendableMemoryStream(rate, len); \ + return new AppendableMemoryStream(rate); \ else \ - return new AppendableMemoryStream(rate, len); \ + return new AppendableMemoryStream(rate); \ } else \ - return new AppendableMemoryStream(rate, len) + return new AppendableMemoryStream(rate) -AppendableAudioStream *makeAppendableAudioStream(int rate, byte _flags, uint32 len) { +AppendableAudioStream *makeAppendableAudioStream(int rate, byte _flags) { const bool isStereo = (_flags & Audio::Mixer::FLAG_STEREO) != 0; const bool is16Bit = (_flags & Audio::Mixer::FLAG_16BITS) != 0; const bool isUnsigned = (_flags & Audio::Mixer::FLAG_UNSIGNED) != 0; diff --git a/sound/audiostream.h b/sound/audiostream.h index b76f2f9cd2..499fb782ab 100644 --- a/sound/audiostream.h +++ b/sound/audiostream.h @@ -31,7 +31,8 @@ namespace Audio { /** - * Generic input stream for the resampling code. + * Generic audio input stream. Subclasses of this are used to feed arbitrary + * sampled audio data into ScummVM's audio mixer. */ class AudioStream { public: @@ -89,6 +90,13 @@ public: static AudioStream* openStreamFile(const char *filename); }; +/** + * Factory function for a raw linear AudioStream, which will simply treat all data + * in the buffer described by ptr and len as raw sample data in the specified + * format. It will then simply pass this data directly to the mixer, after converting + * it to the sample format used by the mixer (i.e. 16 bit signed native endian). + * Optionally supports (infinite) looping of a portion of the data. + */ AudioStream *makeLinearInputStream(int rate, byte flags, const byte *ptr, uint32 len, uint loopOffset, uint loopLen); /** @@ -97,11 +105,29 @@ AudioStream *makeLinearInputStream(int rate, byte flags, const byte *ptr, uint32 */ class AppendableAudioStream : public Audio::AudioStream { public: - virtual void append(const byte *data, uint32 len) = 0; + + /** + * Queue another audio data buffer for playback. The stream + * will playback all queued buffers, in the order they were + * queued. After all data contained in them has been played, + * the buffer will be delete[]'d (so make sure to allocate them + * with new[], not with malloc). + */ + virtual void queueBuffer(byte *data, uint32 size) = 0; + + /** + * Mark the stream as finished, that is, signal that no further data + * will be appended to it. Only after this has been done can the + * AppendableAudioStream ever 'end' ( + */ virtual void finish() = 0; }; -AppendableAudioStream *makeAppendableAudioStream(int rate, byte _flags, uint32 len); +/** + * Factory function for an AppendableAudioStream. The rate and flags + * parameters are analog to those used in makeLinearInputStream. + */ +AppendableAudioStream *makeAppendableAudioStream(int rate, byte flags); } // End of namespace Audio -- cgit v1.2.3