Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/webaudio/blink/PeriodicWave.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2012 Google Inc. All rights reserved.
3
 *
4
 * Redistribution and use in source and binary forms, with or without
5
 * modification, are permitted provided that the following conditions
6
 * are met:
7
 *
8
 * 1.  Redistributions of source code must retain the above copyright
9
 *     notice, this list of conditions and the following disclaimer.
10
 * 2.  Redistributions in binary form must reproduce the above copyright
11
 *     notice, this list of conditions and the following disclaimer in the
12
 *     documentation and/or other materials provided with the distribution.
13
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14
 *     its contributors may be used to endorse or promote products derived
15
 *     from this software without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
28
29
#include "PeriodicWave.h"
30
#include <algorithm>
31
#include <cmath>
32
#include <limits>
33
#include "mozilla/FFTBlock.h"
34
35
const unsigned MinPeriodicWaveSize = 4096; // This must be a power of two.
36
const unsigned MaxPeriodicWaveSize = 8192; // This must be a power of two.
37
const float CentsPerRange = 1200 / 3; // 1/3 Octave.
38
39
using namespace mozilla;
40
using mozilla::dom::OscillatorType;
41
42
namespace WebCore {
43
44
already_AddRefed<PeriodicWave>
45
PeriodicWave::create(float sampleRate,
46
                     const float* real,
47
                     const float* imag,
48
                     size_t numberOfComponents,
49
                     bool disableNormalization)
50
0
{
51
0
    bool isGood = real && imag && numberOfComponents > 0;
52
0
    MOZ_ASSERT(isGood);
53
0
    if (isGood) {
54
0
        RefPtr<PeriodicWave> periodicWave =
55
0
            new PeriodicWave(sampleRate, numberOfComponents,
56
0
                             disableNormalization);
57
0
58
0
        // Limit the number of components used to those for frequencies below the
59
0
        // Nyquist of the fixed length inverse FFT.
60
0
        size_t halfSize = periodicWave->m_periodicWaveSize / 2;
61
0
        numberOfComponents = std::min(numberOfComponents, halfSize);
62
0
        periodicWave->m_numberOfComponents = numberOfComponents;
63
0
        periodicWave->m_realComponents = new AudioFloatArray(numberOfComponents);
64
0
        periodicWave->m_imagComponents = new AudioFloatArray(numberOfComponents);
65
0
        memcpy(periodicWave->m_realComponents->Elements(), real,
66
0
               numberOfComponents * sizeof(float));
67
0
        memcpy(periodicWave->m_imagComponents->Elements(), imag,
68
0
               numberOfComponents * sizeof(float));
69
0
70
0
        return periodicWave.forget();
71
0
    }
72
0
    return nullptr;
73
0
}
74
75
already_AddRefed<PeriodicWave>
76
PeriodicWave::createSine(float sampleRate)
77
0
{
78
0
    RefPtr<PeriodicWave> periodicWave =
79
0
        new PeriodicWave(sampleRate, MinPeriodicWaveSize, false);
80
0
    periodicWave->generateBasicWaveform(OscillatorType::Sine);
81
0
    return periodicWave.forget();
82
0
}
83
84
already_AddRefed<PeriodicWave>
85
PeriodicWave::createSquare(float sampleRate)
86
0
{
87
0
    RefPtr<PeriodicWave> periodicWave =
88
0
        new PeriodicWave(sampleRate, MinPeriodicWaveSize, false);
89
0
    periodicWave->generateBasicWaveform(OscillatorType::Square);
90
0
    return periodicWave.forget();
91
0
}
92
93
already_AddRefed<PeriodicWave>
94
PeriodicWave::createSawtooth(float sampleRate)
95
0
{
96
0
    RefPtr<PeriodicWave> periodicWave =
97
0
        new PeriodicWave(sampleRate, MinPeriodicWaveSize, false);
98
0
    periodicWave->generateBasicWaveform(OscillatorType::Sawtooth);
99
0
    return periodicWave.forget();
100
0
}
101
102
already_AddRefed<PeriodicWave>
103
PeriodicWave::createTriangle(float sampleRate)
104
0
{
105
0
    RefPtr<PeriodicWave> periodicWave =
106
0
        new PeriodicWave(sampleRate, MinPeriodicWaveSize, false);
107
0
    periodicWave->generateBasicWaveform(OscillatorType::Triangle);
108
0
    return periodicWave.forget();
109
0
}
110
111
PeriodicWave::PeriodicWave(float sampleRate, size_t numberOfComponents, bool disableNormalization)
112
    : m_sampleRate(sampleRate)
113
    , m_centsPerRange(CentsPerRange)
114
    , m_maxPartialsInBandLimitedTable(0)
115
    , m_normalizationScale(1.0f)
116
    , m_disableNormalization(disableNormalization)
117
0
{
118
0
    float nyquist = 0.5 * m_sampleRate;
119
0
120
0
    if (numberOfComponents <= MinPeriodicWaveSize) {
121
0
        m_periodicWaveSize = MinPeriodicWaveSize;
122
0
    } else {
123
0
        unsigned npow2 = powf(2.0f, floorf(logf(numberOfComponents - 1.0)/logf(2.0f) + 1.0f));
124
0
        m_periodicWaveSize = std::min(MaxPeriodicWaveSize, npow2);
125
0
    }
126
0
127
0
    m_numberOfRanges = (unsigned)(3.0f*logf(m_periodicWaveSize)/logf(2.0f));
128
0
    m_bandLimitedTables.SetLength(m_numberOfRanges);
129
0
    m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
130
0
    m_rateScale = m_periodicWaveSize / m_sampleRate;
131
0
}
132
133
size_t PeriodicWave::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
134
0
{
135
0
    size_t amount = aMallocSizeOf(this);
136
0
137
0
    amount += m_bandLimitedTables.ShallowSizeOfExcludingThis(aMallocSizeOf);
138
0
    for (size_t i = 0; i < m_bandLimitedTables.Length(); i++) {
139
0
        if (m_bandLimitedTables[i]) {
140
0
            amount += m_bandLimitedTables[i]->ShallowSizeOfIncludingThis(aMallocSizeOf);
141
0
        }
142
0
    }
143
0
144
0
    return amount;
145
0
}
146
147
void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor)
148
0
{
149
0
150
0
    // Negative frequencies are allowed, in which case we alias
151
0
    // to the positive frequency.
152
0
    fundamentalFrequency = fabsf(fundamentalFrequency);
153
0
154
0
    // We only need to rebuild to the tables if the new fundamental
155
0
    // frequency is low enough to allow for more partials below the
156
0
    // Nyquist frequency.
157
0
    unsigned numberOfPartials = numberOfPartialsForRange(0);
158
0
    float nyquist = 0.5 * m_sampleRate;
159
0
    if (fundamentalFrequency != 0.0) {
160
0
        numberOfPartials = std::min(numberOfPartials, (unsigned)(nyquist / fundamentalFrequency));
161
0
    }
162
0
    if (numberOfPartials > m_maxPartialsInBandLimitedTable) {
163
0
        for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) {
164
0
            m_bandLimitedTables[rangeIndex] = 0;
165
0
        }
166
0
167
0
        // We need to create the first table to determine the normalization
168
0
        // constant.
169
0
        createBandLimitedTables(fundamentalFrequency, 0);
170
0
        m_maxPartialsInBandLimitedTable = numberOfPartials;
171
0
    }
172
0
173
0
    // Calculate the pitch range.
174
0
    float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5;
175
0
    float centsAboveLowestFrequency = logf(ratio)/logf(2.0f) * 1200;
176
0
177
0
    // Add one to round-up to the next range just in time to truncate
178
0
    // partials before aliasing occurs.
179
0
    float pitchRange = 1 + centsAboveLowestFrequency / m_centsPerRange;
180
0
181
0
    pitchRange = std::max(pitchRange, 0.0f);
182
0
    pitchRange = std::min(pitchRange, static_cast<float>(m_numberOfRanges - 1));
183
0
184
0
    // The words "lower" and "higher" refer to the table data having
185
0
    // the lower and higher numbers of partials. It's a little confusing
186
0
    // since the range index gets larger the more partials we cull out.
187
0
    // So the lower table data will have a larger range index.
188
0
    unsigned rangeIndex1 = static_cast<unsigned>(pitchRange);
189
0
    unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1;
190
0
191
0
    if (!m_bandLimitedTables[rangeIndex1].get())
192
0
        createBandLimitedTables(fundamentalFrequency, rangeIndex1);
193
0
194
0
    if (!m_bandLimitedTables[rangeIndex2].get())
195
0
        createBandLimitedTables(fundamentalFrequency, rangeIndex2);
196
0
197
0
    lowerWaveData = m_bandLimitedTables[rangeIndex2]->Elements();
198
0
    higherWaveData = m_bandLimitedTables[rangeIndex1]->Elements();
199
0
200
0
    // Ranges from 0 -> 1 to interpolate between lower -> higher.
201
0
    tableInterpolationFactor = rangeIndex2 - pitchRange;
202
0
}
203
204
unsigned PeriodicWave::maxNumberOfPartials() const
205
0
{
206
0
    return m_periodicWaveSize / 2;
207
0
}
208
209
unsigned PeriodicWave::numberOfPartialsForRange(unsigned rangeIndex) const
210
0
{
211
0
    // Number of cents below nyquist where we cull partials.
212
0
    float centsToCull = rangeIndex * m_centsPerRange;
213
0
214
0
    // A value from 0 -> 1 representing what fraction of the partials to keep.
215
0
    float cullingScale = pow(2, -centsToCull / 1200);
216
0
217
0
    // The very top range will have all the partials culled.
218
0
    unsigned numberOfPartials = cullingScale * maxNumberOfPartials();
219
0
220
0
    return numberOfPartials;
221
0
}
222
223
// Convert into time-domain wave buffers.
224
// One table is created for each range for non-aliasing playback
225
// at different playback rates. Thus, higher ranges have more
226
// high-frequency partials culled out.
227
void PeriodicWave::createBandLimitedTables(float fundamentalFrequency,
228
                                           unsigned rangeIndex)
229
0
{
230
0
    unsigned fftSize = m_periodicWaveSize;
231
0
    unsigned i;
232
0
233
0
    const float *realData = m_realComponents->Elements();
234
0
    const float *imagData = m_imagComponents->Elements();
235
0
236
0
    // This FFTBlock is used to cull partials (represented by frequency bins).
237
0
    FFTBlock frame(fftSize);
238
0
239
0
    // Find the starting bin where we should start culling the aliasing
240
0
    // partials for this pitch range.  We need to clear out the highest
241
0
    // frequencies to band-limit the waveform.
242
0
    unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex);
243
0
    // Also limit to the number of components that are provided.
244
0
    numberOfPartials = std::min(numberOfPartials, m_numberOfComponents - 1);
245
0
246
0
    // Limit number of partials to those below Nyquist frequency
247
0
    float nyquist = 0.5 * m_sampleRate;
248
0
    if (fundamentalFrequency != 0.0) {
249
0
        numberOfPartials = std::min(numberOfPartials,
250
0
                                    (unsigned)(nyquist / fundamentalFrequency));
251
0
    }
252
0
253
0
    // Copy from loaded frequency data and generate complex conjugate
254
0
    // because of the way the inverse FFT is defined.
255
0
    // The coefficients of higher partials remain zero, as initialized in
256
0
    // the FFTBlock constructor.
257
0
    for (i = 0; i < numberOfPartials + 1; ++i) {
258
0
        frame.RealData(i) = realData[i];
259
0
        frame.ImagData(i) = -imagData[i];
260
0
    }
261
0
262
0
    // Clear any DC-offset.
263
0
    frame.RealData(0) = 0;
264
0
    // Clear value which has no effect.
265
0
    frame.ImagData(0) = 0;
266
0
267
0
    // Create the band-limited table.
268
0
    AlignedAudioFloatArray* table = new AlignedAudioFloatArray(m_periodicWaveSize);
269
0
    m_bandLimitedTables[rangeIndex] = table;
270
0
271
0
    // Apply an inverse FFT to generate the time-domain table data.
272
0
    float* data = m_bandLimitedTables[rangeIndex]->Elements();
273
0
    frame.GetInverseWithoutScaling(data);
274
0
275
0
    // For the first range (which has the highest power), calculate
276
0
    // its peak value then compute normalization scale.
277
0
    if (m_disableNormalization) {
278
0
      // See Bug 1424906, results need to be scaled by 0.5 even
279
0
      // when normalization is disabled.
280
0
      m_normalizationScale = 0.5;
281
0
    } else if (!rangeIndex) {
282
0
        float maxValue;
283
0
        maxValue = AudioBufferPeakValue(data, m_periodicWaveSize);
284
0
285
0
        if (maxValue)
286
0
            m_normalizationScale = 1.0f / maxValue;
287
0
    }
288
0
289
0
    // Apply normalization scale.
290
0
    AudioBufferInPlaceScale(data, m_normalizationScale, m_periodicWaveSize);
291
0
}
292
293
void PeriodicWave::generateBasicWaveform(OscillatorType shape)
294
0
{
295
0
    const float piFloat = float(M_PI);
296
0
    unsigned fftSize = periodicWaveSize();
297
0
    unsigned halfSize = fftSize / 2;
298
0
299
0
    m_numberOfComponents = halfSize;
300
0
    m_realComponents = new AudioFloatArray(halfSize);
301
0
    m_imagComponents = new AudioFloatArray(halfSize);
302
0
    float* realP = m_realComponents->Elements();
303
0
    float* imagP = m_imagComponents->Elements();
304
0
305
0
    // Clear DC and imag value which is ignored.
306
0
    realP[0] = 0;
307
0
    imagP[0] = 0;
308
0
309
0
    for (unsigned n = 1; n < halfSize; ++n) {
310
0
        float omega = 2 * piFloat * n;
311
0
        float invOmega = 1 / omega;
312
0
313
0
        // Fourier coefficients according to standard definition.
314
0
        float a; // Coefficient for cos().
315
0
        float b; // Coefficient for sin().
316
0
317
0
        // Calculate Fourier coefficients depending on the shape.
318
0
        // Note that the overall scaling (magnitude) of the waveforms
319
0
        // is normalized in createBandLimitedTables().
320
0
        switch (shape) {
321
0
        case OscillatorType::Sine:
322
0
            // Standard sine wave function.
323
0
            a = 0;
324
0
            b = (n == 1) ? 1 : 0;
325
0
            break;
326
0
        case OscillatorType::Square:
327
0
            // Square-shaped waveform with the first half its maximum value
328
0
            // and the second half its minimum value.
329
0
            a = 0;
330
0
            b = invOmega * ((n & 1) ? 2 : 0);
331
0
            break;
332
0
        case OscillatorType::Sawtooth:
333
0
            // Sawtooth-shaped waveform with the first half ramping from
334
0
            // zero to maximum and the second half from minimum to zero.
335
0
            a = 0;
336
0
            b = -invOmega * cos(0.5 * omega);
337
0
            break;
338
0
        case OscillatorType::Triangle:
339
0
            // Triangle-shaped waveform going from its maximum value to
340
0
            // its minimum value then back to the maximum value.
341
0
            a = 0;
342
0
            if (n & 1) {
343
0
              b = 2 * (2 / (n * piFloat) * 2 / (n * piFloat)) * ((((n - 1) >> 1) & 1) ? -1 : 1);
344
0
            } else {
345
0
              b = 0;
346
0
            }
347
0
            break;
348
0
        default:
349
0
            MOZ_ASSERT_UNREACHABLE("invalid oscillator type");
350
0
            a = 0;
351
0
            b = 0;
352
0
            break;
353
0
        }
354
0
355
0
        realP[n] = a;
356
0
        imagP[n] = b;
357
0
    }
358
0
}
359
360
} // namespace WebCore