Coverage Report

Created: 2018-09-25 14:53

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