/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "AudioRingBuffer.h" #include "MediaData.h" #include "mozilla/Assertions.h" #include "mozilla/Maybe.h" #include "mozilla/PodOperations.h" namespace mozilla { /** * RingBuffer is used to preallocate a buffer of a specific size in bytes and * then to use it for writing and reading values without any re-allocation or * memory moving. Please note that the total byte size of the buffer modulo the * size of the chosen type must be zero. The RingBuffer has been created with * audio sample values types in mind which are integer or float. However, it * can be used with any trivial type. It is _not_ thread-safe! The constructor * can be called on any thread but the reads and write must happen on the same * thread, which can be different than the construction thread. */ template class RingBuffer final { public: explicit RingBuffer(AlignedByteBuffer&& aMemoryBuffer) : mStorage(ConvertToSpan(aMemoryBuffer)), mMemoryBuffer(std::move(aMemoryBuffer)) { MOZ_ASSERT(std::is_trivial::value); MOZ_ASSERT(!mStorage.IsEmpty()); } /** * Write `aSamples` number of zeros in the buffer. */ uint32_t WriteSilence(uint32_t aSamples) { MOZ_ASSERT(aSamples); return Write(Span(), aSamples); } /** * Copy `aBuffer` to the RingBuffer. */ uint32_t Write(const Span& aBuffer) { MOZ_ASSERT(!aBuffer.IsEmpty()); return Write(aBuffer, aBuffer.Length()); } private: /** * Copy `aSamples` number of elements from `aBuffer` to the RingBuffer. If * `aBuffer` is empty append `aSamples` of zeros. */ uint32_t Write(const Span& aBuffer, uint32_t aSamples) { MOZ_ASSERT(aSamples > 0 && aBuffer.Length() <= static_cast(aSamples)); if (IsFull()) { return 0; } uint32_t toWrite = std::min(AvailableWrite(), aSamples); uint32_t part1 = std::min(Capacity() - mWriteIndex, toWrite); uint32_t part2 = toWrite - part1; Span part1Buffer = mStorage.Subspan(mWriteIndex, part1); Span part2Buffer = mStorage.To(part2); if (!aBuffer.IsEmpty()) { Span fromPart1 = aBuffer.To(part1); Span fromPart2 = aBuffer.Subspan(part1, part2); CopySpan(part1Buffer, fromPart1); CopySpan(part2Buffer, fromPart2); } else { // The aBuffer is empty, append zeros. PodZero(part1Buffer.Elements(), part1Buffer.Length()); PodZero(part2Buffer.Elements(), part2Buffer.Length()); } mWriteIndex = NextIndex(mWriteIndex, toWrite); return toWrite; } public: /** * Copy `aSamples` number of elements from `aBuffer` to the RingBuffer. The * `aBuffer` does not change. */ uint32_t Write(const RingBuffer& aBuffer, uint32_t aSamples) { MOZ_ASSERT(aSamples); if (IsFull()) { return 0; } uint32_t toWriteThis = std::min(AvailableWrite(), aSamples); uint32_t toReadThat = std::min(aBuffer.AvailableRead(), toWriteThis); uint32_t part1 = std::min(aBuffer.Capacity() - aBuffer.mReadIndex, toReadThat); uint32_t part2 = toReadThat - part1; Span part1Buffer = aBuffer.mStorage.Subspan(aBuffer.mReadIndex, part1); DebugOnly ret = Write(part1Buffer); MOZ_ASSERT(ret == part1); if (part2) { Span part2Buffer = aBuffer.mStorage.To(part2); ret = Write(part2Buffer); MOZ_ASSERT(ret == part2); } return toReadThat; } /** * Copy `aBuffer.Length()` number of elements from RingBuffer to `aBuffer`. */ uint32_t Read(const Span& aBuffer) { MOZ_ASSERT(!aBuffer.IsEmpty()); MOZ_ASSERT(aBuffer.size() <= std::numeric_limits::max()); if (IsEmpty()) { return 0; } uint32_t toRead = std::min(AvailableRead(), aBuffer.Length()); uint32_t part1 = std::min(Capacity() - mReadIndex, toRead); uint32_t part2 = toRead - part1; Span part1Buffer = mStorage.Subspan(mReadIndex, part1); Span part2Buffer = mStorage.To(part2); Span toPart1 = aBuffer.To(part1); Span toPart2 = aBuffer.Subspan(part1, part2); CopySpan(toPart1, part1Buffer); CopySpan(toPart2, part2Buffer); mReadIndex = NextIndex(mReadIndex, toRead); return toRead; } /** * Provide `aCallable` that will be called with the internal linear read * buffers and the number of samples available for reading. The `aCallable` * will be called at most 2 times. The `aCallable` must return the number of * samples that have been actually read. If that number is smaller than the * available number of samples, provided in the argument, the `aCallable` will * not be called again. The RingBuffer's available read samples will be * decreased by the number returned from the `aCallable`. * * The important aspects of this method are that first, it makes it possible * to avoid extra copies to an intermediates buffer, and second, each buffer * provided to `aCallable is a linear piece of memory which can be used * directly to a resampler for example. * * In general, the problem with ring buffers is that they cannot provide one * linear chunk of memory so extra copies, to a linear buffer, are often * needed. This method bridge that gap by breaking the ring buffer's * internal read memory into linear pieces and making it available through * the `aCallable`. In the body of the `aCallable` those buffers can be used * directly without any copy or intermediate steps. */ uint32_t ReadNoCopy( std::function&)>&& aCallable) { if (IsEmpty()) { return 0; } uint32_t part1 = std::min(Capacity() - mReadIndex, AvailableRead()); uint32_t part2 = AvailableRead() - part1; Span part1Buffer = mStorage.Subspan(mReadIndex, part1); uint32_t toRead = aCallable(part1Buffer); MOZ_ASSERT(toRead <= part1); if (toRead == part1 && part2) { Span part2Buffer = mStorage.To(part2); toRead += aCallable(part2Buffer); MOZ_ASSERT(toRead <= part1 + part2); } mReadIndex = NextIndex(mReadIndex, toRead); return toRead; } /** * Remove the next `aSamples` number of samples from the ring buffer. */ uint32_t Discard(uint32_t aSamples) { MOZ_ASSERT(aSamples); if (IsEmpty()) { return 0; } uint32_t toDiscard = std::min(AvailableRead(), aSamples); mReadIndex = NextIndex(mReadIndex, toDiscard); return toDiscard; } /** * Empty the ring buffer. */ uint32_t Clear() { if (IsEmpty()) { return 0; } uint32_t toDiscard = AvailableRead(); mReadIndex = NextIndex(mReadIndex, toDiscard); return toDiscard; } /** * Returns true if the full capacity of the ring buffer is being used. When * full any attempt to write more samples to the ring buffer will fail. */ bool IsFull() const { return (mWriteIndex + 1) % Capacity() == mReadIndex; } /** * Returns true if the ring buffer is empty. When empty any attempt to read * more samples from the ring buffer will fail. */ bool IsEmpty() const { return mWriteIndex == mReadIndex; } /** * The number of samples available for writing. */ uint32_t AvailableWrite() const { /* We subtract one element here to always keep at least one sample * free in the buffer, to distinguish between full and empty array. */ uint32_t rv = mReadIndex - mWriteIndex - 1; if (mWriteIndex >= mReadIndex) { rv += Capacity(); } return rv; } /** * The number of samples available for reading. */ uint32_t AvailableRead() const { if (mWriteIndex >= mReadIndex) { return mWriteIndex - mReadIndex; } return mWriteIndex + Capacity() - mReadIndex; } private: uint32_t NextIndex(uint32_t aIndex, uint32_t aStep) const { MOZ_ASSERT(aStep < Capacity()); MOZ_ASSERT(aIndex < Capacity()); return (aIndex + aStep) % Capacity(); } uint32_t Capacity() const { return mStorage.Length(); } Span ConvertToSpan(const AlignedByteBuffer& aOther) const { MOZ_ASSERT(aOther.Length() >= sizeof(T)); return Span(reinterpret_cast(aOther.Data()), aOther.Length() / sizeof(T)); } void CopySpan(Span& aTo, const Span& aFrom) { MOZ_ASSERT(aTo.Length() == aFrom.Length()); std::copy(aFrom.cbegin(), aFrom.cend(), aTo.begin()); } private: uint32_t mReadIndex = 0; uint32_t mWriteIndex = 0; /* Points to the mMemoryBuffer. */ const Span mStorage; /* The actual allocated memory set from outside. It is set in the ctor and it * is not used again. It is here to control the lifetime of the memory. The * memory is accessed through the mStorage. The idea is that the memory used * from the RingBuffer can be pre-allocated. */ const AlignedByteBuffer mMemoryBuffer; }; /** AudioRingBuffer **/ /* The private members of AudioRingBuffer. */ class AudioRingBuffer::AudioRingBufferPrivate { public: AudioSampleFormat mSampleFormat = AUDIO_FORMAT_SILENCE; Maybe> mFloatRingBuffer; Maybe> mIntRingBuffer; Maybe mBackingBuffer; }; AudioRingBuffer::AudioRingBuffer(uint32_t aSizeInBytes) : mPtr(MakeUnique()) { MOZ_ASSERT(aSizeInBytes > 0); mPtr->mBackingBuffer.emplace(aSizeInBytes); MOZ_ASSERT(mPtr->mBackingBuffer); } AudioRingBuffer::~AudioRingBuffer() = default; void AudioRingBuffer::SetSampleFormat(AudioSampleFormat aFormat) { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_SILENCE); MOZ_ASSERT(aFormat == AUDIO_FORMAT_S16 || aFormat == AUDIO_FORMAT_FLOAT32); MOZ_ASSERT(!mPtr->mIntRingBuffer); MOZ_ASSERT(!mPtr->mFloatRingBuffer); MOZ_ASSERT(mPtr->mBackingBuffer); mPtr->mSampleFormat = aFormat; if (mPtr->mSampleFormat == AUDIO_FORMAT_S16) { mPtr->mIntRingBuffer.emplace(mPtr->mBackingBuffer.extract()); MOZ_ASSERT(!mPtr->mBackingBuffer); return; } mPtr->mFloatRingBuffer.emplace(mPtr->mBackingBuffer.extract()); MOZ_ASSERT(!mPtr->mBackingBuffer); } uint32_t AudioRingBuffer::Write(const Span& aBuffer) { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_FLOAT32); MOZ_ASSERT(!mPtr->mIntRingBuffer); MOZ_ASSERT(!mPtr->mBackingBuffer); return mPtr->mFloatRingBuffer->Write(aBuffer); } uint32_t AudioRingBuffer::Write(const Span& aBuffer) { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_S16); MOZ_ASSERT(!mPtr->mFloatRingBuffer); MOZ_ASSERT(!mPtr->mBackingBuffer); return mPtr->mIntRingBuffer->Write(aBuffer); } uint32_t AudioRingBuffer::Write(const AudioRingBuffer& aBuffer, uint32_t aSamples) { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_S16 || mPtr->mSampleFormat == AUDIO_FORMAT_FLOAT32); MOZ_ASSERT(!mPtr->mBackingBuffer); if (mPtr->mSampleFormat == AUDIO_FORMAT_S16) { MOZ_ASSERT(!mPtr->mFloatRingBuffer); return mPtr->mIntRingBuffer->Write(aBuffer.mPtr->mIntRingBuffer.ref(), aSamples); } MOZ_ASSERT(!mPtr->mIntRingBuffer); return mPtr->mFloatRingBuffer->Write(aBuffer.mPtr->mFloatRingBuffer.ref(), aSamples); } uint32_t AudioRingBuffer::WriteSilence(uint32_t aSamples) { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_S16 || mPtr->mSampleFormat == AUDIO_FORMAT_FLOAT32); MOZ_ASSERT(!mPtr->mBackingBuffer); if (mPtr->mSampleFormat == AUDIO_FORMAT_S16) { MOZ_ASSERT(!mPtr->mFloatRingBuffer); return mPtr->mIntRingBuffer->WriteSilence(aSamples); } MOZ_ASSERT(!mPtr->mIntRingBuffer); return mPtr->mFloatRingBuffer->WriteSilence(aSamples); } uint32_t AudioRingBuffer::Read(const Span& aBuffer) { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_FLOAT32); MOZ_ASSERT(!mPtr->mIntRingBuffer); MOZ_ASSERT(!mPtr->mBackingBuffer); return mPtr->mFloatRingBuffer->Read(aBuffer); } uint32_t AudioRingBuffer::Read(const Span& aBuffer) { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_S16); MOZ_ASSERT(!mPtr->mFloatRingBuffer); MOZ_ASSERT(!mPtr->mBackingBuffer); return mPtr->mIntRingBuffer->Read(aBuffer); } uint32_t AudioRingBuffer::ReadNoCopy( std::function&)>&& aCallable) { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_FLOAT32); MOZ_ASSERT(!mPtr->mIntRingBuffer); MOZ_ASSERT(!mPtr->mBackingBuffer); return mPtr->mFloatRingBuffer->ReadNoCopy(std::move(aCallable)); } uint32_t AudioRingBuffer::ReadNoCopy( std::function&)>&& aCallable) { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_S16); MOZ_ASSERT(!mPtr->mFloatRingBuffer); MOZ_ASSERT(!mPtr->mBackingBuffer); return mPtr->mIntRingBuffer->ReadNoCopy(std::move(aCallable)); } uint32_t AudioRingBuffer::Discard(uint32_t aSamples) { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_S16 || mPtr->mSampleFormat == AUDIO_FORMAT_FLOAT32); MOZ_ASSERT(!mPtr->mBackingBuffer); if (mPtr->mSampleFormat == AUDIO_FORMAT_S16) { MOZ_ASSERT(!mPtr->mFloatRingBuffer); return mPtr->mIntRingBuffer->Discard(aSamples); } MOZ_ASSERT(!mPtr->mIntRingBuffer); return mPtr->mFloatRingBuffer->Discard(aSamples); } uint32_t AudioRingBuffer::Clear() { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_S16 || mPtr->mSampleFormat == AUDIO_FORMAT_FLOAT32); MOZ_ASSERT(!mPtr->mBackingBuffer); if (mPtr->mSampleFormat == AUDIO_FORMAT_S16) { MOZ_ASSERT(!mPtr->mFloatRingBuffer); MOZ_ASSERT(mPtr->mIntRingBuffer); return mPtr->mIntRingBuffer->Clear(); } MOZ_ASSERT(!mPtr->mIntRingBuffer); MOZ_ASSERT(mPtr->mFloatRingBuffer); return mPtr->mFloatRingBuffer->Clear(); } bool AudioRingBuffer::IsFull() const { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_S16 || mPtr->mSampleFormat == AUDIO_FORMAT_FLOAT32); MOZ_ASSERT(!mPtr->mBackingBuffer); if (mPtr->mSampleFormat == AUDIO_FORMAT_S16) { MOZ_ASSERT(!mPtr->mFloatRingBuffer); return mPtr->mIntRingBuffer->IsFull(); } MOZ_ASSERT(!mPtr->mIntRingBuffer); return mPtr->mFloatRingBuffer->IsFull(); } bool AudioRingBuffer::IsEmpty() const { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_S16 || mPtr->mSampleFormat == AUDIO_FORMAT_FLOAT32); MOZ_ASSERT(!mPtr->mBackingBuffer); if (mPtr->mSampleFormat == AUDIO_FORMAT_S16) { MOZ_ASSERT(!mPtr->mFloatRingBuffer); return mPtr->mIntRingBuffer->IsEmpty(); } MOZ_ASSERT(!mPtr->mIntRingBuffer); return mPtr->mFloatRingBuffer->IsEmpty(); } uint32_t AudioRingBuffer::AvailableWrite() const { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_S16 || mPtr->mSampleFormat == AUDIO_FORMAT_FLOAT32); MOZ_ASSERT(!mPtr->mBackingBuffer); if (mPtr->mSampleFormat == AUDIO_FORMAT_S16) { MOZ_ASSERT(!mPtr->mFloatRingBuffer); return mPtr->mIntRingBuffer->AvailableWrite(); } MOZ_ASSERT(!mPtr->mIntRingBuffer); return mPtr->mFloatRingBuffer->AvailableWrite(); } uint32_t AudioRingBuffer::AvailableRead() const { MOZ_ASSERT(mPtr->mSampleFormat == AUDIO_FORMAT_S16 || mPtr->mSampleFormat == AUDIO_FORMAT_FLOAT32); MOZ_ASSERT(!mPtr->mBackingBuffer); if (mPtr->mSampleFormat == AUDIO_FORMAT_S16) { MOZ_ASSERT(!mPtr->mFloatRingBuffer); return mPtr->mIntRingBuffer->AvailableRead(); } MOZ_ASSERT(!mPtr->mIntRingBuffer); return mPtr->mFloatRingBuffer->AvailableRead(); } } // namespace mozilla