content/media/webaudio/DelayBuffer.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "DelayBuffer.h"
michael@0 8
michael@0 9 #include "mozilla/PodOperations.h"
michael@0 10 #include "AudioChannelFormat.h"
michael@0 11 #include "AudioNodeEngine.h"
michael@0 12
michael@0 13 namespace mozilla {
michael@0 14
michael@0 15 size_t
michael@0 16 DelayBuffer::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 17 {
michael@0 18 size_t amount = 0;
michael@0 19 amount += mChunks.SizeOfExcludingThis(aMallocSizeOf);
michael@0 20 for (size_t i = 0; i < mChunks.Length(); i++) {
michael@0 21 amount += mChunks[i].SizeOfExcludingThis(aMallocSizeOf, false);
michael@0 22 }
michael@0 23
michael@0 24 amount += mUpmixChannels.SizeOfExcludingThis(aMallocSizeOf);
michael@0 25 return amount;
michael@0 26 }
michael@0 27
michael@0 28 void
michael@0 29 DelayBuffer::Write(const AudioChunk& aInputChunk)
michael@0 30 {
michael@0 31 // We must have a reference to the buffer if there are channels
michael@0 32 MOZ_ASSERT(aInputChunk.IsNull() == !aInputChunk.mChannelData.Length());
michael@0 33 #ifdef DEBUG
michael@0 34 MOZ_ASSERT(!mHaveWrittenBlock);
michael@0 35 mHaveWrittenBlock = true;
michael@0 36 #endif
michael@0 37
michael@0 38 if (!EnsureBuffer()) {
michael@0 39 return;
michael@0 40 }
michael@0 41
michael@0 42 if (mCurrentChunk == mLastReadChunk) {
michael@0 43 mLastReadChunk = -1; // invalidate cache
michael@0 44 }
michael@0 45 mChunks[mCurrentChunk] = aInputChunk;
michael@0 46 }
michael@0 47
michael@0 48 void
michael@0 49 DelayBuffer::Read(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
michael@0 50 AudioChunk* aOutputChunk,
michael@0 51 ChannelInterpretation aChannelInterpretation)
michael@0 52 {
michael@0 53 int chunkCount = mChunks.Length();
michael@0 54 if (!chunkCount) {
michael@0 55 aOutputChunk->SetNull(WEBAUDIO_BLOCK_SIZE);
michael@0 56 return;
michael@0 57 }
michael@0 58
michael@0 59 // Find the maximum number of contributing channels to determine the output
michael@0 60 // channel count that retains all signal information. Buffered blocks will
michael@0 61 // be upmixed if necessary.
michael@0 62 //
michael@0 63 // First find the range of "delay" offsets backwards from the current
michael@0 64 // position. Note that these may be negative for frames that are after the
michael@0 65 // current position (including i).
michael@0 66 double minDelay = aPerFrameDelays[0];
michael@0 67 double maxDelay = minDelay;
michael@0 68 for (unsigned i = 1; i < WEBAUDIO_BLOCK_SIZE; ++i) {
michael@0 69 minDelay = std::min(minDelay, aPerFrameDelays[i] - i);
michael@0 70 maxDelay = std::max(maxDelay, aPerFrameDelays[i] - i);
michael@0 71 }
michael@0 72
michael@0 73 // Now find the chunks touched by this range and check their channel counts.
michael@0 74 int oldestChunk = ChunkForDelay(int(maxDelay) + 1);
michael@0 75 int youngestChunk = ChunkForDelay(minDelay);
michael@0 76
michael@0 77 uint32_t channelCount = 0;
michael@0 78 for (int i = oldestChunk; true; i = (i + 1) % chunkCount) {
michael@0 79 channelCount = GetAudioChannelsSuperset(channelCount,
michael@0 80 mChunks[i].ChannelCount());
michael@0 81 if (i == youngestChunk) {
michael@0 82 break;
michael@0 83 }
michael@0 84 }
michael@0 85
michael@0 86 if (channelCount) {
michael@0 87 AllocateAudioBlock(channelCount, aOutputChunk);
michael@0 88 ReadChannels(aPerFrameDelays, aOutputChunk,
michael@0 89 0, channelCount, aChannelInterpretation);
michael@0 90 } else {
michael@0 91 aOutputChunk->SetNull(WEBAUDIO_BLOCK_SIZE);
michael@0 92 }
michael@0 93
michael@0 94 // Remember currentDelayFrames for the next ProcessBlock call
michael@0 95 mCurrentDelay = aPerFrameDelays[WEBAUDIO_BLOCK_SIZE - 1];
michael@0 96 }
michael@0 97
michael@0 98 void
michael@0 99 DelayBuffer::ReadChannel(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
michael@0 100 const AudioChunk* aOutputChunk, uint32_t aChannel,
michael@0 101 ChannelInterpretation aChannelInterpretation)
michael@0 102 {
michael@0 103 if (!mChunks.Length()) {
michael@0 104 float* outputChannel = static_cast<float*>
michael@0 105 (const_cast<void*>(aOutputChunk->mChannelData[aChannel]));
michael@0 106 PodZero(outputChannel, WEBAUDIO_BLOCK_SIZE);
michael@0 107 return;
michael@0 108 }
michael@0 109
michael@0 110 ReadChannels(aPerFrameDelays, aOutputChunk,
michael@0 111 aChannel, 1, aChannelInterpretation);
michael@0 112 }
michael@0 113
michael@0 114 void
michael@0 115 DelayBuffer::ReadChannels(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
michael@0 116 const AudioChunk* aOutputChunk,
michael@0 117 uint32_t aFirstChannel, uint32_t aNumChannelsToRead,
michael@0 118 ChannelInterpretation aChannelInterpretation)
michael@0 119 {
michael@0 120 uint32_t totalChannelCount = aOutputChunk->mChannelData.Length();
michael@0 121 uint32_t readChannelsEnd = aFirstChannel + aNumChannelsToRead;
michael@0 122 MOZ_ASSERT(readChannelsEnd <= totalChannelCount);
michael@0 123
michael@0 124 if (mUpmixChannels.Length() != totalChannelCount) {
michael@0 125 mLastReadChunk = -1; // invalidate cache
michael@0 126 }
michael@0 127
michael@0 128 float* const* outputChannels = reinterpret_cast<float* const*>
michael@0 129 (const_cast<void* const*>(aOutputChunk->mChannelData.Elements()));
michael@0 130 for (uint32_t channel = aFirstChannel;
michael@0 131 channel < readChannelsEnd; ++channel) {
michael@0 132 PodZero(outputChannels[channel], WEBAUDIO_BLOCK_SIZE);
michael@0 133 }
michael@0 134
michael@0 135 for (unsigned i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
michael@0 136 double currentDelay = aPerFrameDelays[i];
michael@0 137 MOZ_ASSERT(currentDelay >= 0.0);
michael@0 138 MOZ_ASSERT(currentDelay <= (mChunks.Length() - 1) * WEBAUDIO_BLOCK_SIZE);
michael@0 139
michael@0 140 // Interpolate two input frames in case the read position does not match
michael@0 141 // an integer index.
michael@0 142 // Use the larger delay, for the older frame, first, as this is more
michael@0 143 // likely to use the cached upmixed channel arrays.
michael@0 144 int floorDelay = int(currentDelay);
michael@0 145 double interpolationFactor = currentDelay - floorDelay;
michael@0 146 int positions[2];
michael@0 147 positions[1] = PositionForDelay(floorDelay) + i;
michael@0 148 positions[0] = positions[1] - 1;
michael@0 149
michael@0 150 for (unsigned tick = 0; tick < ArrayLength(positions); ++tick) {
michael@0 151 int readChunk = ChunkForPosition(positions[tick]);
michael@0 152 // mVolume is not set on default initialized chunks so handle null
michael@0 153 // chunks specially.
michael@0 154 if (!mChunks[readChunk].IsNull()) {
michael@0 155 int readOffset = OffsetForPosition(positions[tick]);
michael@0 156 UpdateUpmixChannels(readChunk, totalChannelCount,
michael@0 157 aChannelInterpretation);
michael@0 158 double multiplier = interpolationFactor * mChunks[readChunk].mVolume;
michael@0 159 for (uint32_t channel = aFirstChannel;
michael@0 160 channel < readChannelsEnd; ++channel) {
michael@0 161 outputChannels[channel][i] += multiplier *
michael@0 162 static_cast<const float*>(mUpmixChannels[channel])[readOffset];
michael@0 163 }
michael@0 164 }
michael@0 165
michael@0 166 interpolationFactor = 1.0 - interpolationFactor;
michael@0 167 }
michael@0 168 }
michael@0 169 }
michael@0 170
michael@0 171 void
michael@0 172 DelayBuffer::Read(double aDelayTicks, AudioChunk* aOutputChunk,
michael@0 173 ChannelInterpretation aChannelInterpretation)
michael@0 174 {
michael@0 175 const bool firstTime = mCurrentDelay < 0.0;
michael@0 176 double currentDelay = firstTime ? aDelayTicks : mCurrentDelay;
michael@0 177
michael@0 178 double computedDelay[WEBAUDIO_BLOCK_SIZE];
michael@0 179
michael@0 180 for (unsigned i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
michael@0 181 // If the value has changed, smoothly approach it
michael@0 182 currentDelay += (aDelayTicks - currentDelay) * mSmoothingRate;
michael@0 183 computedDelay[i] = currentDelay;
michael@0 184 }
michael@0 185
michael@0 186 Read(computedDelay, aOutputChunk, aChannelInterpretation);
michael@0 187 }
michael@0 188
michael@0 189 bool
michael@0 190 DelayBuffer::EnsureBuffer()
michael@0 191 {
michael@0 192 if (mChunks.Length() == 0) {
michael@0 193 // The length of the buffer is at least one block greater than the maximum
michael@0 194 // delay so that writing an input block does not overwrite the block that
michael@0 195 // would subsequently be read at maximum delay. Also round up to the next
michael@0 196 // block size, so that no block of writes will need to wrap.
michael@0 197 const int chunkCount = (mMaxDelayTicks + 2 * WEBAUDIO_BLOCK_SIZE - 1) >>
michael@0 198 WEBAUDIO_BLOCK_SIZE_BITS;
michael@0 199 if (!mChunks.SetLength(chunkCount)) {
michael@0 200 return false;
michael@0 201 }
michael@0 202
michael@0 203 mLastReadChunk = -1;
michael@0 204 }
michael@0 205 return true;
michael@0 206 }
michael@0 207
michael@0 208 int
michael@0 209 DelayBuffer::PositionForDelay(int aDelay) {
michael@0 210 // Adding mChunks.Length() keeps integers positive for defined and
michael@0 211 // appropriate bitshift, remainder, and bitwise operations.
michael@0 212 return ((mCurrentChunk + mChunks.Length()) * WEBAUDIO_BLOCK_SIZE) - aDelay;
michael@0 213 }
michael@0 214
michael@0 215 int
michael@0 216 DelayBuffer::ChunkForPosition(int aPosition)
michael@0 217 {
michael@0 218 MOZ_ASSERT(aPosition >= 0);
michael@0 219 return (aPosition >> WEBAUDIO_BLOCK_SIZE_BITS) % mChunks.Length();
michael@0 220 }
michael@0 221
michael@0 222 int
michael@0 223 DelayBuffer::OffsetForPosition(int aPosition)
michael@0 224 {
michael@0 225 MOZ_ASSERT(aPosition >= 0);
michael@0 226 return aPosition & (WEBAUDIO_BLOCK_SIZE - 1);
michael@0 227 }
michael@0 228
michael@0 229 int
michael@0 230 DelayBuffer::ChunkForDelay(int aDelay)
michael@0 231 {
michael@0 232 return ChunkForPosition(PositionForDelay(aDelay));
michael@0 233 }
michael@0 234
michael@0 235 void
michael@0 236 DelayBuffer::UpdateUpmixChannels(int aNewReadChunk, uint32_t aChannelCount,
michael@0 237 ChannelInterpretation aChannelInterpretation)
michael@0 238 {
michael@0 239 if (aNewReadChunk == mLastReadChunk) {
michael@0 240 MOZ_ASSERT(mUpmixChannels.Length() == aChannelCount);
michael@0 241 return;
michael@0 242 }
michael@0 243
michael@0 244 static const float silenceChannel[WEBAUDIO_BLOCK_SIZE] = {};
michael@0 245
michael@0 246 NS_WARN_IF_FALSE(mHaveWrittenBlock || aNewReadChunk != mCurrentChunk,
michael@0 247 "Smoothing is making feedback delay too small.");
michael@0 248
michael@0 249 mLastReadChunk = aNewReadChunk;
michael@0 250 // Missing assignment operator is bug 976927
michael@0 251 mUpmixChannels.ReplaceElementsAt(0, mUpmixChannels.Length(),
michael@0 252 mChunks[aNewReadChunk].mChannelData);
michael@0 253 MOZ_ASSERT(mUpmixChannels.Length() <= aChannelCount);
michael@0 254 if (mUpmixChannels.Length() < aChannelCount) {
michael@0 255 if (aChannelInterpretation == ChannelInterpretation::Speakers) {
michael@0 256 AudioChannelsUpMix(&mUpmixChannels, aChannelCount, silenceChannel);
michael@0 257 MOZ_ASSERT(mUpmixChannels.Length() == aChannelCount,
michael@0 258 "We called GetAudioChannelsSuperset to avoid this");
michael@0 259 } else {
michael@0 260 // Fill up the remaining channels with zeros
michael@0 261 for (uint32_t channel = mUpmixChannels.Length();
michael@0 262 channel < aChannelCount; ++channel) {
michael@0 263 mUpmixChannels.AppendElement(silenceChannel);
michael@0 264 }
michael@0 265 }
michael@0 266 }
michael@0 267 }
michael@0 268
michael@0 269 } // mozilla

mercurial