LCOV - code coverage report
Current view: top level - dom/media/webaudio - DelayBuffer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 116 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 12 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13