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"
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
{
25 virtual AudioBlockBuffer
* AsAudioBlockBuffer() override
{ return this; };
27 uint32_t ChannelsAllocated() const { return mChannelsAllocated
; }
28 float* ChannelData(uint32_t aChannel
) const {
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()) {
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())) %
51 "AudioBlockBuffers should be at least 4-byte aligned");
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
);
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
) {
105 ClearDownstreamMark();
107 mBuffer
= aNewBuffer
;
113 AudioBlockBuffer
* buffer
= aNewBuffer
->AsAudioBlockBuffer();
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
);
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
);
163 mBufferFormat
= AUDIO_FORMAT_FLOAT32
;
166 } // namespace mozilla