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