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