Line data Source code
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/. */
6 :
7 : #include "AudioBlock.h"
8 : #include "AlignmentUtils.h"
9 :
10 : namespace mozilla {
11 :
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.
16 : *
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.
20 : *
21 : * We guarantee 16 byte alignment of the channel data.
22 : */
23 : class AudioBlockBuffer final : public ThreadSharedObject {
24 : public:
25 :
26 0 : virtual AudioBlockBuffer* AsAudioBlockBuffer() override { return this; };
27 :
28 0 : float* ChannelData(uint32_t aChannel)
29 : {
30 0 : float* base = reinterpret_cast<float*>(((uintptr_t)(this + 1) + 15) & ~0x0F);
31 0 : ASSERT_ALIGNED16(base);
32 0 : return base + aChannel * WEBAUDIO_BLOCK_SIZE;
33 : }
34 :
35 0 : static already_AddRefed<AudioBlockBuffer> Create(uint32_t aChannelCount)
36 : {
37 0 : CheckedInt<size_t> size = WEBAUDIO_BLOCK_SIZE;
38 0 : size *= aChannelCount;
39 0 : size *= sizeof(float);
40 0 : size += sizeof(AudioBlockBuffer);
41 0 : size += 15; //padding for alignment
42 0 : if (!size.isValid()) {
43 0 : MOZ_CRASH();
44 : }
45 :
46 0 : void* m = moz_xmalloc(size.value());
47 0 : RefPtr<AudioBlockBuffer> p = new (m) AudioBlockBuffer();
48 0 : NS_ASSERTION((reinterpret_cast<char*>(p.get() + 1) - reinterpret_cast<char*>(p.get())) % 4 == 0,
49 : "AudioBlockBuffers should be at least 4-byte aligned");
50 0 : return p.forget();
51 : }
52 :
53 : // Graph thread only.
54 0 : void DownstreamRefAdded() { ++mDownstreamRefCount; }
55 0 : void DownstreamRefRemoved() {
56 0 : MOZ_ASSERT(mDownstreamRefCount > 0);
57 0 : --mDownstreamRefCount;
58 0 : }
59 : // Whether this is shared by any owners that are not downstream.
60 : // Called only from owners with a reference that is not a downstream
61 : // reference. Graph thread only.
62 0 : bool HasLastingShares()
63 : {
64 : // mRefCnt is atomic and so reading its value is defined even when
65 : // modifications may happen on other threads. mDownstreamRefCount is
66 : // not modified on any other thread.
67 : //
68 : // If all other references are downstream references (managed on this, the
69 : // graph thread), then other threads are not using this buffer and cannot
70 : // add further references. This method can safely return false. The
71 : // buffer contents can be modified.
72 : //
73 : // If there are other references that are not downstream references, then
74 : // this method will return true. The buffer will be assumed to be still
75 : // in use and so will not be reused.
76 0 : nsrefcnt count = mRefCnt;
77 : // This test is strictly less than because the caller has a reference
78 : // that is not a downstream reference.
79 0 : MOZ_ASSERT(mDownstreamRefCount < count);
80 0 : return count != mDownstreamRefCount + 1;
81 : }
82 :
83 0 : virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
84 : {
85 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
86 : }
87 :
88 : private:
89 0 : AudioBlockBuffer() {}
90 0 : ~AudioBlockBuffer() override { MOZ_ASSERT(mDownstreamRefCount == 0); }
91 :
92 : nsAutoRefCnt mDownstreamRefCount;
93 : };
94 :
95 0 : AudioBlock::~AudioBlock()
96 : {
97 0 : ClearDownstreamMark();
98 0 : }
99 :
100 : void
101 0 : AudioBlock::SetBuffer(ThreadSharedObject* aNewBuffer)
102 : {
103 0 : if (aNewBuffer == mBuffer) {
104 0 : return;
105 : }
106 :
107 0 : ClearDownstreamMark();
108 :
109 0 : mBuffer = aNewBuffer;
110 :
111 0 : if (!aNewBuffer) {
112 0 : return;
113 : }
114 :
115 0 : AudioBlockBuffer* buffer = aNewBuffer->AsAudioBlockBuffer();
116 0 : if (buffer) {
117 0 : buffer->DownstreamRefAdded();
118 0 : mBufferIsDownstreamRef = true;
119 : }
120 : }
121 :
122 : void
123 0 : AudioBlock::ClearDownstreamMark() {
124 0 : if (mBufferIsDownstreamRef) {
125 0 : mBuffer->AsAudioBlockBuffer()->DownstreamRefRemoved();
126 0 : mBufferIsDownstreamRef = false;
127 : }
128 0 : }
129 :
130 : bool
131 0 : AudioBlock::CanWrite() {
132 : // If mBufferIsDownstreamRef is set then the buffer is not ours to use.
133 : // It may be in use by another node which is not downstream.
134 0 : return !mBufferIsDownstreamRef &&
135 0 : !mBuffer->AsAudioBlockBuffer()->HasLastingShares();
136 : }
137 :
138 : void
139 0 : AudioBlock::AllocateChannels(uint32_t aChannelCount)
140 : {
141 0 : MOZ_ASSERT(mDuration == WEBAUDIO_BLOCK_SIZE);
142 :
143 0 : if (mBufferIsDownstreamRef) {
144 : // This is not our buffer to re-use.
145 0 : ClearDownstreamMark();
146 0 : } else if (mBuffer && ChannelCount() == aChannelCount) {
147 0 : AudioBlockBuffer* buffer = mBuffer->AsAudioBlockBuffer();
148 0 : if (buffer && !buffer->HasLastingShares()) {
149 0 : MOZ_ASSERT(mBufferFormat == AUDIO_FORMAT_FLOAT32);
150 : // No need to allocate again.
151 0 : mVolume = 1.0f;
152 0 : return;
153 : }
154 : }
155 :
156 0 : RefPtr<AudioBlockBuffer> buffer = AudioBlockBuffer::Create(aChannelCount);
157 0 : mChannelData.SetLength(aChannelCount);
158 0 : for (uint32_t i = 0; i < aChannelCount; ++i) {
159 0 : mChannelData[i] = buffer->ChannelData(i);
160 : }
161 0 : mBuffer = buffer.forget();
162 0 : mVolume = 1.0f;
163 0 : mBufferFormat = AUDIO_FORMAT_FLOAT32;
164 : }
165 :
166 : } // namespace mozilla
|