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
This commit is contained in:
Max Horn 2007-02-28 14:48:26 +00:00
parent dea688d0f5
commit 8c8abca6f8
9 changed files with 127 additions and 95 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
int LinearMemoryStream<stereo, is16Bit, isUnsigned, isLE>::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<stereo, is16Bit, isUnsigned, isLE>::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<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
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<Buffer> _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<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::AppendableMemoryStream(int rate, uint bufferSize)
: _finalized(false), _rate(rate) {
AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::AppendableMemoryStream(int rate)
: _finalized(false), _rate(rate), _pos(0) {
// 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);
_pos = _end = _bufferStart;
_bufferEnd = _bufferStart + bufferSize;
}
template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::~AppendableMemoryStream() {
free(_bufferStart);
// Clear the queue
Common::List<Buffer>::iterator iter;
for (iter = _bufferQueue.begin(); iter != _bufferQueue.end(); ++iter)
delete[] iter->start;
}
template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
@ -288,12 +288,19 @@ int AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::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;
const byte *endMarker = (_pos > _end) ? _bufferEnd : _end;
const int len = MIN(numSamples, samples + (int)(endMarker - _pos) / (is16Bit ? 2 : 1));
assert(buf.start <= _pos && _pos <= buf.end);
const int samplesLeftInCurBuffer = buf.end - _pos;
if (samplesLeftInCurBuffer == 0) {
_bufferQueue.erase(_bufferQueue.begin());
_pos = 0;
continue;
}
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<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16
}
template<bool stereo, bool is16Bit, bool isUnsigned, bool isLE>
void AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::append(const byte *data, uint32 len) {
void AppendableMemoryStream<stereo, is16Bit, isUnsigned, isLE>::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<Buffer>::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<STEREO, true, UNSIGNED, true>(rate, len); \
return new AppendableMemoryStream<STEREO, true, UNSIGNED, true>(rate); \
else \
return new AppendableMemoryStream<STEREO, true, UNSIGNED, false>(rate, len); \
return new AppendableMemoryStream<STEREO, true, UNSIGNED, false>(rate); \
} else \
return new AppendableMemoryStream<STEREO, false, UNSIGNED, false>(rate, len)
return new AppendableMemoryStream<STEREO, false, UNSIGNED, false>(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;

View File

@ -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