summaryrefslogtreecommitdiffstats
path: root/dom/media/webaudio/AudioBlock.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webaudio/AudioBlock.cpp')
-rw-r--r--dom/media/webaudio/AudioBlock.cpp166
1 files changed, 166 insertions, 0 deletions
diff --git a/dom/media/webaudio/AudioBlock.cpp b/dom/media/webaudio/AudioBlock.cpp
new file mode 100644
index 0000000000..09bbd7f70a
--- /dev/null
+++ b/dom/media/webaudio/AudioBlock.cpp
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "AudioBlock.h"
+#include "AlignmentUtils.h"
+
+namespace mozilla {
+
+/**
+ * Heap-allocated buffer of channels of 128-sample float arrays, with
+ * threadsafe refcounting. Typically you would allocate one of these, fill it
+ * in, and then treat it as immutable while it's shared.
+ *
+ * Downstream references are accounted specially so that the creator of the
+ * buffer can reuse and modify its contents next iteration if other references
+ * are all downstream temporary references held by AudioBlock.
+ *
+ * We guarantee 16 byte alignment of the channel data.
+ */
+class AudioBlockBuffer final : public ThreadSharedObject {
+ public:
+ virtual AudioBlockBuffer* AsAudioBlockBuffer() override { return this; };
+
+ uint32_t ChannelsAllocated() const { return mChannelsAllocated; }
+ float* ChannelData(uint32_t aChannel) const {
+ float* base =
+ reinterpret_cast<float*>(((uintptr_t)(this + 1) + 15) & ~0x0F);
+ ASSERT_ALIGNED16(base);
+ return base + aChannel * WEBAUDIO_BLOCK_SIZE;
+ }
+
+ static already_AddRefed<AudioBlockBuffer> Create(uint32_t aChannelCount) {
+ CheckedInt<size_t> size = WEBAUDIO_BLOCK_SIZE;
+ size *= aChannelCount;
+ size *= sizeof(float);
+ size += sizeof(AudioBlockBuffer);
+ size += 15; // padding for alignment
+ if (!size.isValid()) {
+ MOZ_CRASH();
+ }
+
+ void* m = operator new(size.value());
+ RefPtr<AudioBlockBuffer> p = new (m) AudioBlockBuffer(aChannelCount);
+ NS_ASSERTION((reinterpret_cast<char*>(p.get() + 1) -
+ reinterpret_cast<char*>(p.get())) %
+ 4 ==
+ 0,
+ "AudioBlockBuffers should be at least 4-byte aligned");
+ return p.forget();
+ }
+
+ // Graph thread only.
+ void DownstreamRefAdded() { ++mDownstreamRefCount; }
+ void DownstreamRefRemoved() {
+ MOZ_ASSERT(mDownstreamRefCount > 0);
+ --mDownstreamRefCount;
+ }
+ // Whether this is shared by any owners that are not downstream.
+ // Called only from owners with a reference that is not a downstream
+ // reference. Graph thread only.
+ bool HasLastingShares() const {
+ // mRefCnt is atomic and so reading its value is defined even when
+ // modifications may happen on other threads. mDownstreamRefCount is
+ // not modified on any other thread.
+ //
+ // If all other references are downstream references (managed on this, the
+ // graph thread), then other threads are not using this buffer and cannot
+ // add further references. This method can safely return false. The
+ // buffer contents can be modified.
+ //
+ // If there are other references that are not downstream references, then
+ // this method will return true. The buffer will be assumed to be still
+ // in use and so will not be reused.
+ nsrefcnt count = mRefCnt;
+ // This test is strictly less than because the caller has a reference
+ // that is not a downstream reference.
+ MOZ_ASSERT(mDownstreamRefCount < count);
+ return count != mDownstreamRefCount + 1;
+ }
+
+ virtual size_t SizeOfIncludingThis(
+ MallocSizeOf aMallocSizeOf) const override {
+ return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ private:
+ explicit AudioBlockBuffer(uint32_t aChannelsAllocated)
+ : mChannelsAllocated(aChannelsAllocated) {}
+ ~AudioBlockBuffer() override { MOZ_ASSERT(mDownstreamRefCount == 0); }
+
+ nsAutoRefCnt mDownstreamRefCount;
+ const uint32_t mChannelsAllocated;
+};
+
+AudioBlock::~AudioBlock() { ClearDownstreamMark(); }
+
+void AudioBlock::SetBuffer(ThreadSharedObject* aNewBuffer) {
+ if (aNewBuffer == mBuffer) {
+ return;
+ }
+
+ ClearDownstreamMark();
+
+ mBuffer = aNewBuffer;
+
+ if (!aNewBuffer) {
+ return;
+ }
+
+ AudioBlockBuffer* buffer = aNewBuffer->AsAudioBlockBuffer();
+ if (buffer) {
+ buffer->DownstreamRefAdded();
+ mBufferIsDownstreamRef = true;
+ }
+}
+
+void AudioBlock::ClearDownstreamMark() {
+ if (mBufferIsDownstreamRef) {
+ mBuffer->AsAudioBlockBuffer()->DownstreamRefRemoved();
+ mBufferIsDownstreamRef = false;
+ }
+}
+
+bool AudioBlock::CanWrite() {
+ // If mBufferIsDownstreamRef is set then the buffer is not ours to use.
+ // It may be in use by another node which is not downstream.
+ return !mBufferIsDownstreamRef &&
+ !mBuffer->AsAudioBlockBuffer()->HasLastingShares();
+}
+
+void AudioBlock::AllocateChannels(uint32_t aChannelCount) {
+ MOZ_ASSERT(mDuration == WEBAUDIO_BLOCK_SIZE);
+
+ if (mBufferIsDownstreamRef) {
+ // This is not our buffer to re-use.
+ ClearDownstreamMark();
+ } else if (mBuffer) {
+ AudioBlockBuffer* buffer = mBuffer->AsAudioBlockBuffer();
+ if (buffer && !buffer->HasLastingShares() &&
+ buffer->ChannelsAllocated() >= aChannelCount) {
+ MOZ_ASSERT(mBufferFormat == AUDIO_FORMAT_FLOAT32);
+ // No need to allocate again.
+ uint32_t previousChannelCount = ChannelCount();
+ mChannelData.SetLength(aChannelCount);
+ for (uint32_t i = previousChannelCount; i < aChannelCount; ++i) {
+ mChannelData[i] = buffer->ChannelData(i);
+ }
+ mVolume = 1.0f;
+ return;
+ }
+ }
+
+ RefPtr<AudioBlockBuffer> buffer = AudioBlockBuffer::Create(aChannelCount);
+ mChannelData.SetLength(aChannelCount);
+ for (uint32_t i = 0; i < aChannelCount; ++i) {
+ mChannelData[i] = buffer->ChannelData(i);
+ }
+ mBuffer = std::move(buffer);
+ mVolume = 1.0f;
+ mBufferFormat = AUDIO_FORMAT_FLOAT32;
+}
+
+} // namespace mozilla