Bug 1839170 - Refactor Snap pulling, Add Firefox Snap Core22 and GNOME 42 SDK symbols...
[gecko.git] / dom / media / webaudio / AudioBlock.cpp
blob09bbd7f70a92beae3938a1192127f1867b36023e
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "AudioBlock.h"
8 #include "AlignmentUtils.h"
10 namespace mozilla {
12 /**
13 * Heap-allocated buffer of channels of 128-sample float arrays, with
14 * threadsafe refcounting. Typically you would allocate one of these, fill it
15 * in, and then treat it as immutable while it's shared.
17 * Downstream references are accounted specially so that the creator of the
18 * buffer can reuse and modify its contents next iteration if other references
19 * are all downstream temporary references held by AudioBlock.
21 * We guarantee 16 byte alignment of the channel data.
23 class AudioBlockBuffer final : public ThreadSharedObject {
24 public:
25 virtual AudioBlockBuffer* AsAudioBlockBuffer() override { return this; };
27 uint32_t ChannelsAllocated() const { return mChannelsAllocated; }
28 float* ChannelData(uint32_t aChannel) const {
29 float* base =
30 reinterpret_cast<float*>(((uintptr_t)(this + 1) + 15) & ~0x0F);
31 ASSERT_ALIGNED16(base);
32 return base + aChannel * WEBAUDIO_BLOCK_SIZE;
35 static already_AddRefed<AudioBlockBuffer> Create(uint32_t aChannelCount) {
36 CheckedInt<size_t> size = WEBAUDIO_BLOCK_SIZE;
37 size *= aChannelCount;
38 size *= sizeof(float);
39 size += sizeof(AudioBlockBuffer);
40 size += 15; // padding for alignment
41 if (!size.isValid()) {
42 MOZ_CRASH();
45 void* m = operator new(size.value());
46 RefPtr<AudioBlockBuffer> p = new (m) AudioBlockBuffer(aChannelCount);
47 NS_ASSERTION((reinterpret_cast<char*>(p.get() + 1) -
48 reinterpret_cast<char*>(p.get())) %
49 4 ==
51 "AudioBlockBuffers should be at least 4-byte aligned");
52 return p.forget();
55 // Graph thread only.
56 void DownstreamRefAdded() { ++mDownstreamRefCount; }
57 void DownstreamRefRemoved() {
58 MOZ_ASSERT(mDownstreamRefCount > 0);
59 --mDownstreamRefCount;
61 // Whether this is shared by any owners that are not downstream.
62 // Called only from owners with a reference that is not a downstream
63 // reference. Graph thread only.
64 bool HasLastingShares() const {
65 // mRefCnt is atomic and so reading its value is defined even when
66 // modifications may happen on other threads. mDownstreamRefCount is
67 // not modified on any other thread.
69 // If all other references are downstream references (managed on this, the
70 // graph thread), then other threads are not using this buffer and cannot
71 // add further references. This method can safely return false. The
72 // buffer contents can be modified.
74 // If there are other references that are not downstream references, then
75 // this method will return true. The buffer will be assumed to be still
76 // in use and so will not be reused.
77 nsrefcnt count = mRefCnt;
78 // This test is strictly less than because the caller has a reference
79 // that is not a downstream reference.
80 MOZ_ASSERT(mDownstreamRefCount < count);
81 return count != mDownstreamRefCount + 1;
84 virtual size_t SizeOfIncludingThis(
85 MallocSizeOf aMallocSizeOf) const override {
86 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
89 private:
90 explicit AudioBlockBuffer(uint32_t aChannelsAllocated)
91 : mChannelsAllocated(aChannelsAllocated) {}
92 ~AudioBlockBuffer() override { MOZ_ASSERT(mDownstreamRefCount == 0); }
94 nsAutoRefCnt mDownstreamRefCount;
95 const uint32_t mChannelsAllocated;
98 AudioBlock::~AudioBlock() { ClearDownstreamMark(); }
100 void AudioBlock::SetBuffer(ThreadSharedObject* aNewBuffer) {
101 if (aNewBuffer == mBuffer) {
102 return;
105 ClearDownstreamMark();
107 mBuffer = aNewBuffer;
109 if (!aNewBuffer) {
110 return;
113 AudioBlockBuffer* buffer = aNewBuffer->AsAudioBlockBuffer();
114 if (buffer) {
115 buffer->DownstreamRefAdded();
116 mBufferIsDownstreamRef = true;
120 void AudioBlock::ClearDownstreamMark() {
121 if (mBufferIsDownstreamRef) {
122 mBuffer->AsAudioBlockBuffer()->DownstreamRefRemoved();
123 mBufferIsDownstreamRef = false;
127 bool AudioBlock::CanWrite() {
128 // If mBufferIsDownstreamRef is set then the buffer is not ours to use.
129 // It may be in use by another node which is not downstream.
130 return !mBufferIsDownstreamRef &&
131 !mBuffer->AsAudioBlockBuffer()->HasLastingShares();
134 void AudioBlock::AllocateChannels(uint32_t aChannelCount) {
135 MOZ_ASSERT(mDuration == WEBAUDIO_BLOCK_SIZE);
137 if (mBufferIsDownstreamRef) {
138 // This is not our buffer to re-use.
139 ClearDownstreamMark();
140 } else if (mBuffer) {
141 AudioBlockBuffer* buffer = mBuffer->AsAudioBlockBuffer();
142 if (buffer && !buffer->HasLastingShares() &&
143 buffer->ChannelsAllocated() >= aChannelCount) {
144 MOZ_ASSERT(mBufferFormat == AUDIO_FORMAT_FLOAT32);
145 // No need to allocate again.
146 uint32_t previousChannelCount = ChannelCount();
147 mChannelData.SetLength(aChannelCount);
148 for (uint32_t i = previousChannelCount; i < aChannelCount; ++i) {
149 mChannelData[i] = buffer->ChannelData(i);
151 mVolume = 1.0f;
152 return;
156 RefPtr<AudioBlockBuffer> buffer = AudioBlockBuffer::Create(aChannelCount);
157 mChannelData.SetLength(aChannelCount);
158 for (uint32_t i = 0; i < aChannelCount; ++i) {
159 mChannelData[i] = buffer->ChannelData(i);
161 mBuffer = std::move(buffer);
162 mVolume = 1.0f;
163 mBufferFormat = AUDIO_FORMAT_FLOAT32;
166 } // namespace mozilla