diff --git a/src/core/audio/Buffer.cpp b/src/core/audio/Buffer.cpp index 8a7025813..ad35392e1 100644 --- a/src/core/audio/Buffer.cpp +++ b/src/core/audio/Buffer.cpp @@ -61,7 +61,7 @@ Buffer::Buffer(void) } Buffer::~Buffer() { - delete this->buffer; + delete[] this->buffer; } BufferPtr Buffer::Create() { @@ -106,8 +106,8 @@ void Buffer::CopyFormat(BufferPtr fromBuffer) { void Buffer::ResizeBuffer() { if (this->sampleSize > this->internalBufferSize) { if (this->buffer) { - delete this->buffer; - this->buffer = NULL; + delete[] this->buffer; + this->buffer = nullptr; } this->buffer = new float[this->sampleSize]; @@ -132,7 +132,7 @@ void Buffer::Copy(float* buffer, long samples) { if (samples > this->internalBufferSize) { float *newBuffer = new float[samples]; CopyFloat(newBuffer, buffer, samples); - delete this->buffer; + delete[] this->buffer; this->buffer = newBuffer; this->internalBufferSize = samples; } @@ -143,45 +143,36 @@ void Buffer::Copy(float* buffer, long samples) { this->sampleSize = samples; } +void Buffer::Append(float* src, long samples) { + /* number of floats (not bytes) in buffer */ + long newBufferSize = (this->Samples() + samples); + + if (newBufferSize > this->internalBufferSize) { /* resize, then copy, if too small */ + float *newBuffer = new float[newBufferSize]; + + CopyFloat(newBuffer, this->buffer, this->sampleSize); + float *dst = &newBuffer[this->sampleSize]; + CopyFloat(dst, src, samples); + + delete[] this->buffer; + + this->buffer = newBuffer; + this->internalBufferSize = newBufferSize; + } + else { /* append, no resize required */ + float *dst = &this->buffer[this->sampleSize]; + CopyFloat(dst, src, samples); + this->internalBufferSize += samples; + } + + this->sampleSize = newBufferSize; +} + bool Buffer::Append(BufferPtr appendBuffer) { if (this->SampleRate() == appendBuffer->SampleRate() && this->Channels() == appendBuffer->Channels()) { - /* number of floats (not bytes) in buffer */ - long newBufferSize = (this->Samples() + appendBuffer->Samples()); - - if (newBufferSize > this->internalBufferSize) { /* resize, then copy, if too small */ - float *newBuffer = new float[newBufferSize]; - - CopyFloat(newBuffer, this->buffer, this->sampleSize); - - float *dst = &newBuffer[this->sampleSize]; - float *src = appendBuffer->BufferPointer(); - long count = appendBuffer->Samples(); - - CopyFloat(dst, src, count); - - delete this->buffer; - -#if DEBUG > 0 - std::cerr << "resized with realloc old: " << this->internalBufferSize << " new: " << newBufferSize << "\n"; -#endif - - this->buffer = newBuffer; - this->internalBufferSize = newBufferSize; - } - else { /* append, no resize required */ - float *dst = &this->buffer[this->sampleSize]; - float *src = appendBuffer->BufferPointer(); - long count = appendBuffer->Samples(); - CopyFloat(dst, src, count); - -#if DEBUG > 0 - std::cerr << "appended " << count << " floats to existing buffer, logical bytes=" << newBufferSize << "\n"; -#endif - } - - this->sampleSize = newBufferSize; + this->Append(appendBuffer->BufferPointer(), appendBuffer->Samples()); return true; } diff --git a/src/core/audio/Buffer.h b/src/core/audio/Buffer.h index c1e2dd1cf..4dec8c856 100644 --- a/src/core/audio/Buffer.h +++ b/src/core/audio/Buffer.h @@ -66,6 +66,7 @@ namespace musik { namespace core { namespace audio { void SetPosition(double position); void Copy(float* buffer, long samples); + void Append(float* buffer, long samples); bool Append(BufferPtr appendBuffer); void CopyFormat(BufferPtr fromBuffer); diff --git a/src/core/audio/FixedSizeStream.cpp b/src/core/audio/FixedSizeStream.cpp index cfad02c16..a1bc404ea 100644 --- a/src/core/audio/FixedSizeStream.cpp +++ b/src/core/audio/FixedSizeStream.cpp @@ -45,6 +45,16 @@ using musik::core::PluginFactory; static std::string TAG = "FixedSizeStream"; +#define SET_OFFSET(target, offset) \ + target->SetPosition( \ + ((double) this->decoderSamplePosition + offset) / \ + ((double) target->Channels()) / \ + ((double) this->decoderSampleRate)); + +#define COPY_BUFFER(target, current, count, offset) \ + target->Copy(¤t->BufferPointer()[offset], count); \ + SET_OFFSET(target, offset) \ + FixedSizeStream::FixedSizeStream(int samplesPerChannel, int bufferCount, unsigned int options) : options(options) , samplesPerChannel(samplesPerChannel) @@ -52,7 +62,7 @@ FixedSizeStream::FixedSizeStream(int samplesPerChannel, int bufferCount, unsigne , decoderSampleRate(0) , decoderChannels(0) , decoderSamplePosition(0) -, currentBuffer(Buffer::Create()) +, decoderBuffer(Buffer::Create()) , dspBuffer(Buffer::Create()) { if ((this->options & NoDSP) == 0) { @@ -60,6 +70,10 @@ FixedSizeStream::FixedSizeStream(int samplesPerChannel, int bufferCount, unsigne this->dsps = PluginFactory::Instance().QueryInterface("GetDSP"); } + for (int i = 0; i < bufferCount; i++) { + this->recycledBuffers.push_back(Buffer::Create()); + } + this->LoadDecoderPlugins(); } @@ -78,6 +92,10 @@ double FixedSizeStream::SetPosition(double requestedSeconds) { this->decoderSamplePosition = (uint64)(actualSeconds * rate) * this->decoderChannels; + + this->recycledBuffers.splice( + this->recycledBuffers.begin(), + this->filledBuffers); } return actualSeconds; @@ -137,12 +155,12 @@ void FixedSizeStream::OnBufferProcessedByPlayer(BufferPtr buffer) { this->RecycleBuffer(buffer); } -BufferPtr FixedSizeStream::GetNextBufferFromDecoder() { - BufferPtr buffer = this->currentBuffer; +bool FixedSizeStream::GetNextBufferFromDecoder() { + BufferPtr buffer = this->decoderBuffer; /* get a spare buffer, then ask the decoder for some data */ if (!this->decoder->GetBuffer(buffer.get())) { - return BufferPtr(); + return false; } /* remember the sample rate so we can calculate the current time-position */ @@ -160,7 +178,7 @@ BufferPtr FixedSizeStream::GetNextBufferFromDecoder() { ((double) buffer->Channels()) / ((double) this->decoderSampleRate)); - return buffer; + return true; } inline BufferPtr FixedSizeStream::GetEmptyBuffer() { @@ -172,53 +190,67 @@ inline BufferPtr FixedSizeStream::GetEmptyBuffer() { } else { target = Buffer::Create(); - target->CopyFormat(this->currentBuffer); + target->CopyFormat(this->decoderBuffer); } return target; } -#define COPY_BUFFER(target, current, count, offset) \ - target->Copy(¤t->BufferPointer()[offset], count); \ - target->SetPosition( \ - ((double) this->decoderSamplePosition + offset) / \ - ((double) current->Channels()) / \ - ((double) this->decoderSampleRate)); \ - this->filledBuffers.push_back(target); - BufferPtr FixedSizeStream::GetNextProcessedOutputBuffer() { BufferPtr currentBuffer; - + /* ensure we have at least BUFFER_COUNT buffers, and that at least half of them are filled with data! */ - while (this->filledBuffers.size() < (this->bufferCount / 2) || - this->filledBuffers.size() + this->recycledBuffers.size() < this->bufferCount) - { + while (this->filledBuffers.size() < (this->bufferCount / 2)) { /* ask the decoder for the next buffer */ - currentBuffer = this->GetNextBufferFromDecoder(); - - if (!currentBuffer) { - break; /* important... bust out of the loop when we're done! */ + if (!GetNextBufferFromDecoder()) { + break; } - /* break the buffer into 512 sample per channel buffers. this will - help us ensure visualizer data is uniform. note that the last buffer - may not be exactly 512 -- that should be fine, generally. */ - BufferPtr target; + currentBuffer = this->decoderBuffer; int floatsPerBuffer = this->samplesPerChannel * currentBuffer->Channels(); - int buffers = currentBuffer->Samples() / floatsPerBuffer; + + BufferPtr target; int offset = 0; - for (int i = 0; i < buffers; i++) { + /* if we have a partial / remainder buffer hanging out from the last time + through, let's fill it up with the head of the new buffer. */ + if (remainder) { + long desired = floatsPerBuffer - remainder->Samples(); + long actual = std::min(currentBuffer->Samples(), desired); + + remainder->Append(currentBuffer->BufferPointer(), actual); + SET_OFFSET(remainder, 0); + + if (remainder->Samples() == floatsPerBuffer) { + /* normal case: we were able to fill it; add it to the list of + filled buffers and continue to fill some more... */ + this->filledBuffers.push_back(remainder); + offset += actual; + remainder.reset(); + } + else { + continue; /* already consumed all of the decoder buffer. go back + to the top of the loop to get some more data. */ + } + } + + /* now that the remainder is taken care of, break the rest of the data + into uniform chunks */ + + int buffersToFill = (currentBuffer->Samples() - offset) / floatsPerBuffer; + + for (int i = 0; i < buffersToFill; i++) { target = this->GetEmptyBuffer(); COPY_BUFFER(target, currentBuffer, floatsPerBuffer, offset); + this->filledBuffers.push_back(target); offset += floatsPerBuffer; } - if (offset < this->currentBuffer->Samples()) { - target = this->GetEmptyBuffer(); - COPY_BUFFER(target, currentBuffer, this->currentBuffer->Samples() - offset, offset); + if (offset < currentBuffer->Samples()) { + remainder = this->GetEmptyBuffer(); + COPY_BUFFER(remainder, currentBuffer, currentBuffer->Samples() - offset, offset); } } @@ -241,6 +273,13 @@ BufferPtr FixedSizeStream::GetNextProcessedOutputBuffer() { return currentBuffer; } + /* final remainder */ + if (remainder) { + BufferPtr result = remainder; + remainder.reset(); + return result; + } + return BufferPtr(); } diff --git a/src/core/audio/FixedSizeStream.h b/src/core/audio/FixedSizeStream.h index 574550cc1..df2769083 100644 --- a/src/core/audio/FixedSizeStream.h +++ b/src/core/audio/FixedSizeStream.h @@ -71,7 +71,7 @@ namespace musik { namespace core { namespace audio { private: void RecycleBuffer(BufferPtr oldBuffer); - BufferPtr GetNextBufferFromDecoder(); + bool GetNextBufferFromDecoder(); BufferPtr GetEmptyBuffer(); void LoadDecoderPlugins(); @@ -90,9 +90,11 @@ namespace musik { namespace core { namespace audio { BufferList recycledBuffers; BufferList filledBuffers; - BufferPtr currentBuffer; + BufferPtr decoderBuffer; BufferPtr dspBuffer; + BufferPtr remainder; + unsigned int options; int samplesPerChannel; int bufferCount; diff --git a/src/core/audio/Player.cpp b/src/core/audio/Player.cpp index 8d2c2ef43..36abe79cd 100644 --- a/src/core/audio/Player.cpp +++ b/src/core/audio/Player.cpp @@ -139,7 +139,7 @@ int Player::State() { void Player::ThreadLoop() { /* create and open the stream */ - this->stream = DynamicStream::Create(); + this->stream = FixedSizeStream::Create(); BufferPtr buffer;