Bug 815643 - Part 3: Import the Convolution processing implementation from Blink; r=roc

The original code was copied from Blink SVN revision 150518.

--HG--
extra : rebase_source : 1fbc3cc7f7325bdd8806d7ef30a5c882b505debf
This commit is contained in:
Ehsan Akhgari 2013-06-10 16:08:38 -04:00
parent 82519eedaf
commit 0bdb88b1cd
14 changed files with 1884 additions and 0 deletions

View File

@ -0,0 +1,385 @@
/*
* Copyright (C) 2012 Intel Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#if ENABLE(WEB_AUDIO)
#include "core/platform/audio/DirectConvolver.h"
#if OS(DARWIN)
#include <Accelerate/Accelerate.h>
#endif
#include "core/platform/audio/VectorMath.h"
namespace WebCore {
using namespace VectorMath;
DirectConvolver::DirectConvolver(size_t inputBlockSize)
: m_inputBlockSize(inputBlockSize)
#if USE(WEBAUDIO_IPP)
, m_overlayBuffer(inputBlockSize)
#endif // USE(WEBAUDIO_IPP)
, m_buffer(inputBlockSize * 2)
{
}
void DirectConvolver::process(AudioFloatArray* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess)
{
ASSERT(framesToProcess == m_inputBlockSize);
if (framesToProcess != m_inputBlockSize)
return;
// Only support kernelSize <= m_inputBlockSize
size_t kernelSize = convolutionKernel->size();
ASSERT(kernelSize <= m_inputBlockSize);
if (kernelSize > m_inputBlockSize)
return;
float* kernelP = convolutionKernel->data();
// Sanity check
bool isCopyGood = kernelP && sourceP && destP && m_buffer.data();
ASSERT(isCopyGood);
if (!isCopyGood)
return;
#if USE(WEBAUDIO_IPP)
float* outputBuffer = m_buffer.data();
float* overlayBuffer = m_overlayBuffer.data();
bool isCopyGood2 = overlayBuffer && m_overlayBuffer.size() >= kernelSize && m_buffer.size() == m_inputBlockSize * 2;
ASSERT(isCopyGood2);
if (!isCopyGood2)
return;
ippsConv_32f(static_cast<const Ipp32f*>(sourceP), framesToProcess, static_cast<Ipp32f*>(kernelP), kernelSize, static_cast<Ipp32f*>(outputBuffer));
vadd(outputBuffer, 1, overlayBuffer, 1, destP, 1, framesToProcess);
memcpy(overlayBuffer, outputBuffer + m_inputBlockSize, sizeof(float) * kernelSize);
#else
float* inputP = m_buffer.data() + m_inputBlockSize;
// Copy samples to 2nd half of input buffer.
memcpy(inputP, sourceP, sizeof(float) * framesToProcess);
#if OS(DARWIN)
#if defined(__ppc__) || defined(__i386__)
conv(inputP - kernelSize + 1, 1, kernelP + kernelSize - 1, -1, destP, 1, framesToProcess, kernelSize);
#else
vDSP_conv(inputP - kernelSize + 1, 1, kernelP + kernelSize - 1, -1, destP, 1, framesToProcess, kernelSize);
#endif // defined(__ppc__) || defined(__i386__)
#else
// FIXME: The macro can be further optimized to avoid pipeline stalls. One possibility is to maintain 4 separate sums and change the macro to CONVOLVE_FOUR_SAMPLES.
#define CONVOLVE_ONE_SAMPLE \
sum += inputP[i - j] * kernelP[j]; \
j++;
size_t i = 0;
while (i < framesToProcess) {
size_t j = 0;
float sum = 0;
// FIXME: SSE optimization may be applied here.
if (kernelSize == 32) {
CONVOLVE_ONE_SAMPLE // 1
CONVOLVE_ONE_SAMPLE // 2
CONVOLVE_ONE_SAMPLE // 3
CONVOLVE_ONE_SAMPLE // 4
CONVOLVE_ONE_SAMPLE // 5
CONVOLVE_ONE_SAMPLE // 6
CONVOLVE_ONE_SAMPLE // 7
CONVOLVE_ONE_SAMPLE // 8
CONVOLVE_ONE_SAMPLE // 9
CONVOLVE_ONE_SAMPLE // 10
CONVOLVE_ONE_SAMPLE // 11
CONVOLVE_ONE_SAMPLE // 12
CONVOLVE_ONE_SAMPLE // 13
CONVOLVE_ONE_SAMPLE // 14
CONVOLVE_ONE_SAMPLE // 15
CONVOLVE_ONE_SAMPLE // 16
CONVOLVE_ONE_SAMPLE // 17
CONVOLVE_ONE_SAMPLE // 18
CONVOLVE_ONE_SAMPLE // 19
CONVOLVE_ONE_SAMPLE // 20
CONVOLVE_ONE_SAMPLE // 21
CONVOLVE_ONE_SAMPLE // 22
CONVOLVE_ONE_SAMPLE // 23
CONVOLVE_ONE_SAMPLE // 24
CONVOLVE_ONE_SAMPLE // 25
CONVOLVE_ONE_SAMPLE // 26
CONVOLVE_ONE_SAMPLE // 27
CONVOLVE_ONE_SAMPLE // 28
CONVOLVE_ONE_SAMPLE // 29
CONVOLVE_ONE_SAMPLE // 30
CONVOLVE_ONE_SAMPLE // 31
CONVOLVE_ONE_SAMPLE // 32
} else if (kernelSize == 64) {
CONVOLVE_ONE_SAMPLE // 1
CONVOLVE_ONE_SAMPLE // 2
CONVOLVE_ONE_SAMPLE // 3
CONVOLVE_ONE_SAMPLE // 4
CONVOLVE_ONE_SAMPLE // 5
CONVOLVE_ONE_SAMPLE // 6
CONVOLVE_ONE_SAMPLE // 7
CONVOLVE_ONE_SAMPLE // 8
CONVOLVE_ONE_SAMPLE // 9
CONVOLVE_ONE_SAMPLE // 10
CONVOLVE_ONE_SAMPLE // 11
CONVOLVE_ONE_SAMPLE // 12
CONVOLVE_ONE_SAMPLE // 13
CONVOLVE_ONE_SAMPLE // 14
CONVOLVE_ONE_SAMPLE // 15
CONVOLVE_ONE_SAMPLE // 16
CONVOLVE_ONE_SAMPLE // 17
CONVOLVE_ONE_SAMPLE // 18
CONVOLVE_ONE_SAMPLE // 19
CONVOLVE_ONE_SAMPLE // 20
CONVOLVE_ONE_SAMPLE // 21
CONVOLVE_ONE_SAMPLE // 22
CONVOLVE_ONE_SAMPLE // 23
CONVOLVE_ONE_SAMPLE // 24
CONVOLVE_ONE_SAMPLE // 25
CONVOLVE_ONE_SAMPLE // 26
CONVOLVE_ONE_SAMPLE // 27
CONVOLVE_ONE_SAMPLE // 28
CONVOLVE_ONE_SAMPLE // 29
CONVOLVE_ONE_SAMPLE // 30
CONVOLVE_ONE_SAMPLE // 31
CONVOLVE_ONE_SAMPLE // 32
CONVOLVE_ONE_SAMPLE // 33
CONVOLVE_ONE_SAMPLE // 34
CONVOLVE_ONE_SAMPLE // 35
CONVOLVE_ONE_SAMPLE // 36
CONVOLVE_ONE_SAMPLE // 37
CONVOLVE_ONE_SAMPLE // 38
CONVOLVE_ONE_SAMPLE // 39
CONVOLVE_ONE_SAMPLE // 40
CONVOLVE_ONE_SAMPLE // 41
CONVOLVE_ONE_SAMPLE // 42
CONVOLVE_ONE_SAMPLE // 43
CONVOLVE_ONE_SAMPLE // 44
CONVOLVE_ONE_SAMPLE // 45
CONVOLVE_ONE_SAMPLE // 46
CONVOLVE_ONE_SAMPLE // 47
CONVOLVE_ONE_SAMPLE // 48
CONVOLVE_ONE_SAMPLE // 49
CONVOLVE_ONE_SAMPLE // 50
CONVOLVE_ONE_SAMPLE // 51
CONVOLVE_ONE_SAMPLE // 52
CONVOLVE_ONE_SAMPLE // 53
CONVOLVE_ONE_SAMPLE // 54
CONVOLVE_ONE_SAMPLE // 55
CONVOLVE_ONE_SAMPLE // 56
CONVOLVE_ONE_SAMPLE // 57
CONVOLVE_ONE_SAMPLE // 58
CONVOLVE_ONE_SAMPLE // 59
CONVOLVE_ONE_SAMPLE // 60
CONVOLVE_ONE_SAMPLE // 61
CONVOLVE_ONE_SAMPLE // 62
CONVOLVE_ONE_SAMPLE // 63
CONVOLVE_ONE_SAMPLE // 64
} else if (kernelSize == 128) {
CONVOLVE_ONE_SAMPLE // 1
CONVOLVE_ONE_SAMPLE // 2
CONVOLVE_ONE_SAMPLE // 3
CONVOLVE_ONE_SAMPLE // 4
CONVOLVE_ONE_SAMPLE // 5
CONVOLVE_ONE_SAMPLE // 6
CONVOLVE_ONE_SAMPLE // 7
CONVOLVE_ONE_SAMPLE // 8
CONVOLVE_ONE_SAMPLE // 9
CONVOLVE_ONE_SAMPLE // 10
CONVOLVE_ONE_SAMPLE // 11
CONVOLVE_ONE_SAMPLE // 12
CONVOLVE_ONE_SAMPLE // 13
CONVOLVE_ONE_SAMPLE // 14
CONVOLVE_ONE_SAMPLE // 15
CONVOLVE_ONE_SAMPLE // 16
CONVOLVE_ONE_SAMPLE // 17
CONVOLVE_ONE_SAMPLE // 18
CONVOLVE_ONE_SAMPLE // 19
CONVOLVE_ONE_SAMPLE // 20
CONVOLVE_ONE_SAMPLE // 21
CONVOLVE_ONE_SAMPLE // 22
CONVOLVE_ONE_SAMPLE // 23
CONVOLVE_ONE_SAMPLE // 24
CONVOLVE_ONE_SAMPLE // 25
CONVOLVE_ONE_SAMPLE // 26
CONVOLVE_ONE_SAMPLE // 27
CONVOLVE_ONE_SAMPLE // 28
CONVOLVE_ONE_SAMPLE // 29
CONVOLVE_ONE_SAMPLE // 30
CONVOLVE_ONE_SAMPLE // 31
CONVOLVE_ONE_SAMPLE // 32
CONVOLVE_ONE_SAMPLE // 33
CONVOLVE_ONE_SAMPLE // 34
CONVOLVE_ONE_SAMPLE // 35
CONVOLVE_ONE_SAMPLE // 36
CONVOLVE_ONE_SAMPLE // 37
CONVOLVE_ONE_SAMPLE // 38
CONVOLVE_ONE_SAMPLE // 39
CONVOLVE_ONE_SAMPLE // 40
CONVOLVE_ONE_SAMPLE // 41
CONVOLVE_ONE_SAMPLE // 42
CONVOLVE_ONE_SAMPLE // 43
CONVOLVE_ONE_SAMPLE // 44
CONVOLVE_ONE_SAMPLE // 45
CONVOLVE_ONE_SAMPLE // 46
CONVOLVE_ONE_SAMPLE // 47
CONVOLVE_ONE_SAMPLE // 48
CONVOLVE_ONE_SAMPLE // 49
CONVOLVE_ONE_SAMPLE // 50
CONVOLVE_ONE_SAMPLE // 51
CONVOLVE_ONE_SAMPLE // 52
CONVOLVE_ONE_SAMPLE // 53
CONVOLVE_ONE_SAMPLE // 54
CONVOLVE_ONE_SAMPLE // 55
CONVOLVE_ONE_SAMPLE // 56
CONVOLVE_ONE_SAMPLE // 57
CONVOLVE_ONE_SAMPLE // 58
CONVOLVE_ONE_SAMPLE // 59
CONVOLVE_ONE_SAMPLE // 60
CONVOLVE_ONE_SAMPLE // 61
CONVOLVE_ONE_SAMPLE // 62
CONVOLVE_ONE_SAMPLE // 63
CONVOLVE_ONE_SAMPLE // 64
CONVOLVE_ONE_SAMPLE // 65
CONVOLVE_ONE_SAMPLE // 66
CONVOLVE_ONE_SAMPLE // 67
CONVOLVE_ONE_SAMPLE // 68
CONVOLVE_ONE_SAMPLE // 69
CONVOLVE_ONE_SAMPLE // 70
CONVOLVE_ONE_SAMPLE // 71
CONVOLVE_ONE_SAMPLE // 72
CONVOLVE_ONE_SAMPLE // 73
CONVOLVE_ONE_SAMPLE // 74
CONVOLVE_ONE_SAMPLE // 75
CONVOLVE_ONE_SAMPLE // 76
CONVOLVE_ONE_SAMPLE // 77
CONVOLVE_ONE_SAMPLE // 78
CONVOLVE_ONE_SAMPLE // 79
CONVOLVE_ONE_SAMPLE // 80
CONVOLVE_ONE_SAMPLE // 81
CONVOLVE_ONE_SAMPLE // 82
CONVOLVE_ONE_SAMPLE // 83
CONVOLVE_ONE_SAMPLE // 84
CONVOLVE_ONE_SAMPLE // 85
CONVOLVE_ONE_SAMPLE // 86
CONVOLVE_ONE_SAMPLE // 87
CONVOLVE_ONE_SAMPLE // 88
CONVOLVE_ONE_SAMPLE // 89
CONVOLVE_ONE_SAMPLE // 90
CONVOLVE_ONE_SAMPLE // 91
CONVOLVE_ONE_SAMPLE // 92
CONVOLVE_ONE_SAMPLE // 93
CONVOLVE_ONE_SAMPLE // 94
CONVOLVE_ONE_SAMPLE // 95
CONVOLVE_ONE_SAMPLE // 96
CONVOLVE_ONE_SAMPLE // 97
CONVOLVE_ONE_SAMPLE // 98
CONVOLVE_ONE_SAMPLE // 99
CONVOLVE_ONE_SAMPLE // 100
CONVOLVE_ONE_SAMPLE // 101
CONVOLVE_ONE_SAMPLE // 102
CONVOLVE_ONE_SAMPLE // 103
CONVOLVE_ONE_SAMPLE // 104
CONVOLVE_ONE_SAMPLE // 105
CONVOLVE_ONE_SAMPLE // 106
CONVOLVE_ONE_SAMPLE // 107
CONVOLVE_ONE_SAMPLE // 108
CONVOLVE_ONE_SAMPLE // 109
CONVOLVE_ONE_SAMPLE // 110
CONVOLVE_ONE_SAMPLE // 111
CONVOLVE_ONE_SAMPLE // 112
CONVOLVE_ONE_SAMPLE // 113
CONVOLVE_ONE_SAMPLE // 114
CONVOLVE_ONE_SAMPLE // 115
CONVOLVE_ONE_SAMPLE // 116
CONVOLVE_ONE_SAMPLE // 117
CONVOLVE_ONE_SAMPLE // 118
CONVOLVE_ONE_SAMPLE // 119
CONVOLVE_ONE_SAMPLE // 120
CONVOLVE_ONE_SAMPLE // 121
CONVOLVE_ONE_SAMPLE // 122
CONVOLVE_ONE_SAMPLE // 123
CONVOLVE_ONE_SAMPLE // 124
CONVOLVE_ONE_SAMPLE // 125
CONVOLVE_ONE_SAMPLE // 126
CONVOLVE_ONE_SAMPLE // 127
CONVOLVE_ONE_SAMPLE // 128
} else {
while (j < kernelSize) {
// Non-optimized using actual while loop.
CONVOLVE_ONE_SAMPLE
}
}
destP[i++] = sum;
}
#endif // OS(DARWIN)
// Copy 2nd half of input buffer to 1st half.
memcpy(m_buffer.data(), inputP, sizeof(float) * framesToProcess);
#endif
}
void DirectConvolver::reset()
{
m_buffer.zero();
#if USE(WEBAUDIO_IPP)
m_overlayBuffer.zero();
#endif // USE(WEBAUDIO_IPP)
}
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO)

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2012 Intel Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef DirectConvolver_h
#define DirectConvolver_h
#include "core/platform/audio/AudioArray.h"
#if USE(WEBAUDIO_IPP)
#include <ipps.h>
#endif // USE(WEBAUDIO_IPP)
namespace WebCore {
class DirectConvolver {
public:
DirectConvolver(size_t inputBlockSize);
void process(AudioFloatArray* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess);
void reset();
private:
size_t m_inputBlockSize;
#if USE(WEBAUDIO_IPP)
AudioFloatArray m_overlayBuffer;
#endif // USE(WEBAUDIO_IPP)
AudioFloatArray m_buffer;
};
} // namespace WebCore
#endif // DirectConvolver_h

View File

@ -0,0 +1,120 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#if ENABLE(WEB_AUDIO)
#include "core/platform/audio/FFTConvolver.h"
#include "core/platform/audio/VectorMath.h"
namespace WebCore {
using namespace VectorMath;
FFTConvolver::FFTConvolver(size_t fftSize)
: m_frame(fftSize)
, m_readWriteIndex(0)
, m_inputBuffer(fftSize) // 2nd half of buffer is always zeroed
, m_outputBuffer(fftSize)
, m_lastOverlapBuffer(fftSize / 2)
{
}
void FFTConvolver::process(FFTFrame* fftKernel, const float* sourceP, float* destP, size_t framesToProcess)
{
size_t halfSize = fftSize() / 2;
// framesToProcess must be an exact multiple of halfSize,
// or halfSize is a multiple of framesToProcess when halfSize > framesToProcess.
bool isGood = !(halfSize % framesToProcess && framesToProcess % halfSize);
ASSERT(isGood);
if (!isGood)
return;
size_t numberOfDivisions = halfSize <= framesToProcess ? (framesToProcess / halfSize) : 1;
size_t divisionSize = numberOfDivisions == 1 ? framesToProcess : halfSize;
for (size_t i = 0; i < numberOfDivisions; ++i, sourceP += divisionSize, destP += divisionSize) {
// Copy samples to input buffer (note contraint above!)
float* inputP = m_inputBuffer.data();
// Sanity check
bool isCopyGood1 = sourceP && inputP && m_readWriteIndex + divisionSize <= m_inputBuffer.size();
ASSERT(isCopyGood1);
if (!isCopyGood1)
return;
memcpy(inputP + m_readWriteIndex, sourceP, sizeof(float) * divisionSize);
// Copy samples from output buffer
float* outputP = m_outputBuffer.data();
// Sanity check
bool isCopyGood2 = destP && outputP && m_readWriteIndex + divisionSize <= m_outputBuffer.size();
ASSERT(isCopyGood2);
if (!isCopyGood2)
return;
memcpy(destP, outputP + m_readWriteIndex, sizeof(float) * divisionSize);
m_readWriteIndex += divisionSize;
// Check if it's time to perform the next FFT
if (m_readWriteIndex == halfSize) {
// The input buffer is now filled (get frequency-domain version)
m_frame.doFFT(m_inputBuffer.data());
m_frame.multiply(*fftKernel);
m_frame.doInverseFFT(m_outputBuffer.data());
// Overlap-add 1st half from previous time
vadd(m_outputBuffer.data(), 1, m_lastOverlapBuffer.data(), 1, m_outputBuffer.data(), 1, halfSize);
// Finally, save 2nd half of result
bool isCopyGood3 = m_outputBuffer.size() == 2 * halfSize && m_lastOverlapBuffer.size() == halfSize;
ASSERT(isCopyGood3);
if (!isCopyGood3)
return;
memcpy(m_lastOverlapBuffer.data(), m_outputBuffer.data() + halfSize, sizeof(float) * halfSize);
// Reset index back to start for next time
m_readWriteIndex = 0;
}
}
}
void FFTConvolver::reset()
{
m_lastOverlapBuffer.zero();
m_readWriteIndex = 0;
}
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO)

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FFTConvolver_h
#define FFTConvolver_h
#include "core/platform/audio/AudioArray.h"
#include "core/platform/audio/FFTFrame.h"
namespace WebCore {
class FFTConvolver {
public:
// fftSize must be a power of two
FFTConvolver(size_t fftSize);
// For now, with multiple calls to Process(), framesToProcess MUST add up EXACTLY to fftSize / 2
//
// FIXME: Later, we can do more sophisticated buffering to relax this requirement...
//
// The input to output latency is equal to fftSize / 2
//
// Processing in-place is allowed...
void process(FFTFrame* fftKernel, const float* sourceP, float* destP, size_t framesToProcess);
void reset();
size_t fftSize() const { return m_frame.fftSize(); }
private:
FFTFrame m_frame;
// Buffer input until we get fftSize / 2 samples then do an FFT
size_t m_readWriteIndex;
AudioFloatArray m_inputBuffer;
// Stores output which we read a little at a time
AudioFloatArray m_outputBuffer;
// Saves the 2nd half of the FFT buffer, so we can do an overlap-add with the 1st half of the next one
AudioFloatArray m_lastOverlapBuffer;
};
} // namespace WebCore
#endif // FFTConvolver_h

View File

@ -0,0 +1,243 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#if ENABLE(WEB_AUDIO)
#include "core/platform/audio/Reverb.h"
#include <math.h>
#include "core/platform/audio/AudioBus.h"
#include "core/platform/audio/AudioFileReader.h"
#include "core/platform/audio/ReverbConvolver.h"
#include "core/platform/audio/VectorMath.h"
#include <wtf/MathExtras.h>
#include <wtf/OwnPtr.h>
#include <wtf/PassOwnPtr.h>
#if OS(DARWIN)
using namespace std;
#endif
namespace WebCore {
using namespace VectorMath;
// Empirical gain calibration tested across many impulse responses to ensure perceived volume is same as dry (unprocessed) signal
const float GainCalibration = -58;
const float GainCalibrationSampleRate = 44100;
// A minimum power value to when normalizing a silent (or very quiet) impulse response
const float MinPower = 0.000125f;
static float calculateNormalizationScale(AudioBus* response)
{
// Normalize by RMS power
size_t numberOfChannels = response->numberOfChannels();
size_t length = response->length();
float power = 0;
for (size_t i = 0; i < numberOfChannels; ++i) {
float channelPower = 0;
vsvesq(response->channel(i)->data(), 1, &channelPower, length);
power += channelPower;
}
power = sqrt(power / (numberOfChannels * length));
// Protect against accidental overload
if (std::isinf(power) || std::isnan(power) || power < MinPower)
power = MinPower;
float scale = 1 / power;
scale *= powf(10, GainCalibration * 0.05f); // calibrate to make perceived volume same as unprocessed
// Scale depends on sample-rate.
if (response->sampleRate())
scale *= GainCalibrationSampleRate / response->sampleRate();
// True-stereo compensation
if (response->numberOfChannels() == 4)
scale *= 0.5f;
return scale;
}
Reverb::Reverb(AudioBus* impulseResponse, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize)
{
float scale = 1;
if (normalize) {
scale = calculateNormalizationScale(impulseResponse);
if (scale)
impulseResponse->scale(scale);
}
initialize(impulseResponse, renderSliceSize, maxFFTSize, numberOfChannels, useBackgroundThreads);
// Undo scaling since this shouldn't be a destructive operation on impulseResponse.
// FIXME: What about roundoff? Perhaps consider making a temporary scaled copy
// instead of scaling and unscaling in place.
if (normalize && scale)
impulseResponse->scale(1 / scale);
}
void Reverb::initialize(AudioBus* impulseResponseBuffer, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads)
{
m_impulseResponseLength = impulseResponseBuffer->length();
// The reverb can handle a mono impulse response and still do stereo processing
size_t numResponseChannels = impulseResponseBuffer->numberOfChannels();
m_convolvers.reserveCapacity(numberOfChannels);
int convolverRenderPhase = 0;
for (size_t i = 0; i < numResponseChannels; ++i) {
AudioChannel* channel = impulseResponseBuffer->channel(i);
OwnPtr<ReverbConvolver> convolver = adoptPtr(new ReverbConvolver(channel, renderSliceSize, maxFFTSize, convolverRenderPhase, useBackgroundThreads));
m_convolvers.append(convolver.release());
convolverRenderPhase += renderSliceSize;
}
// For "True" stereo processing we allocate a temporary buffer to avoid repeatedly allocating it in the process() method.
// It can be bad to allocate memory in a real-time thread.
if (numResponseChannels == 4)
m_tempBuffer = AudioBus::create(2, MaxFrameSize);
}
void Reverb::process(const AudioBus* sourceBus, AudioBus* destinationBus, size_t framesToProcess)
{
// Do a fairly comprehensive sanity check.
// If these conditions are satisfied, all of the source and destination pointers will be valid for the various matrixing cases.
bool isSafeToProcess = sourceBus && destinationBus && sourceBus->numberOfChannels() > 0 && destinationBus->numberOfChannels() > 0
&& framesToProcess <= MaxFrameSize && framesToProcess <= sourceBus->length() && framesToProcess <= destinationBus->length();
ASSERT(isSafeToProcess);
if (!isSafeToProcess)
return;
// For now only handle mono or stereo output
if (destinationBus->numberOfChannels() > 2) {
destinationBus->zero();
return;
}
AudioChannel* destinationChannelL = destinationBus->channel(0);
const AudioChannel* sourceChannelL = sourceBus->channel(0);
// Handle input -> output matrixing...
size_t numInputChannels = sourceBus->numberOfChannels();
size_t numOutputChannels = destinationBus->numberOfChannels();
size_t numReverbChannels = m_convolvers.size();
if (numInputChannels == 2 && numReverbChannels == 2 && numOutputChannels == 2) {
// 2 -> 2 -> 2
const AudioChannel* sourceChannelR = sourceBus->channel(1);
AudioChannel* destinationChannelR = destinationBus->channel(1);
m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess);
m_convolvers[1]->process(sourceChannelR, destinationChannelR, framesToProcess);
} else if (numInputChannels == 1 && numOutputChannels == 2 && numReverbChannels == 2) {
// 1 -> 2 -> 2
for (int i = 0; i < 2; ++i) {
AudioChannel* destinationChannel = destinationBus->channel(i);
m_convolvers[i]->process(sourceChannelL, destinationChannel, framesToProcess);
}
} else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 2) {
// 1 -> 1 -> 2
m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess);
// simply copy L -> R
AudioChannel* destinationChannelR = destinationBus->channel(1);
bool isCopySafe = destinationChannelL->data() && destinationChannelR->data() && destinationChannelL->length() >= framesToProcess && destinationChannelR->length() >= framesToProcess;
ASSERT(isCopySafe);
if (!isCopySafe)
return;
memcpy(destinationChannelR->mutableData(), destinationChannelL->data(), sizeof(float) * framesToProcess);
} else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 1) {
// 1 -> 1 -> 1
m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess);
} else if (numInputChannels == 2 && numReverbChannels == 4 && numOutputChannels == 2) {
// 2 -> 4 -> 2 ("True" stereo)
const AudioChannel* sourceChannelR = sourceBus->channel(1);
AudioChannel* destinationChannelR = destinationBus->channel(1);
AudioChannel* tempChannelL = m_tempBuffer->channel(0);
AudioChannel* tempChannelR = m_tempBuffer->channel(1);
// Process left virtual source
m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess);
m_convolvers[1]->process(sourceChannelL, destinationChannelR, framesToProcess);
// Process right virtual source
m_convolvers[2]->process(sourceChannelR, tempChannelL, framesToProcess);
m_convolvers[3]->process(sourceChannelR, tempChannelR, framesToProcess);
destinationBus->sumFrom(*m_tempBuffer);
} else if (numInputChannels == 1 && numReverbChannels == 4 && numOutputChannels == 2) {
// 1 -> 4 -> 2 (Processing mono with "True" stereo impulse response)
// This is an inefficient use of a four-channel impulse response, but we should handle the case.
AudioChannel* destinationChannelR = destinationBus->channel(1);
AudioChannel* tempChannelL = m_tempBuffer->channel(0);
AudioChannel* tempChannelR = m_tempBuffer->channel(1);
// Process left virtual source
m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess);
m_convolvers[1]->process(sourceChannelL, destinationChannelR, framesToProcess);
// Process right virtual source
m_convolvers[2]->process(sourceChannelL, tempChannelL, framesToProcess);
m_convolvers[3]->process(sourceChannelL, tempChannelR, framesToProcess);
destinationBus->sumFrom(*m_tempBuffer);
} else {
// Handle gracefully any unexpected / unsupported matrixing
// FIXME: add code for 5.1 support...
destinationBus->zero();
}
}
void Reverb::reset()
{
for (size_t i = 0; i < m_convolvers.size(); ++i)
m_convolvers[i]->reset();
}
size_t Reverb::latencyFrames() const
{
return !m_convolvers.isEmpty() ? m_convolvers.first()->latencyFrames() : 0;
}
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO)

View File

@ -0,0 +1,67 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef Reverb_h
#define Reverb_h
#include "core/platform/audio/ReverbConvolver.h"
#include <wtf/Vector.h>
namespace WebCore {
class AudioBus;
// Multi-channel convolution reverb with channel matrixing - one or more ReverbConvolver objects are used internally.
class Reverb {
public:
enum { MaxFrameSize = 256 };
// renderSliceSize is a rendering hint, so the FFTs can be optimized to not all occur at the same time (very bad when rendering on a real-time thread).
Reverb(AudioBus* impulseResponseBuffer, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize);
void process(const AudioBus* sourceBus, AudioBus* destinationBus, size_t framesToProcess);
void reset();
size_t impulseResponseLength() const { return m_impulseResponseLength; }
size_t latencyFrames() const;
private:
void initialize(AudioBus* impulseResponseBuffer, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads);
size_t m_impulseResponseLength;
Vector<OwnPtr<ReverbConvolver> > m_convolvers;
// For "True" stereo processing
RefPtr<AudioBus> m_tempBuffer;
};
} // namespace WebCore
#endif // Reverb_h

View File

@ -0,0 +1,119 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#if ENABLE(WEB_AUDIO)
#include "core/platform/audio/ReverbAccumulationBuffer.h"
#include "core/platform/audio/VectorMath.h"
namespace WebCore {
using namespace VectorMath;
ReverbAccumulationBuffer::ReverbAccumulationBuffer(size_t length)
: m_buffer(length)
, m_readIndex(0)
, m_readTimeFrame(0)
{
}
void ReverbAccumulationBuffer::readAndClear(float* destination, size_t numberOfFrames)
{
size_t bufferLength = m_buffer.size();
bool isCopySafe = m_readIndex <= bufferLength && numberOfFrames <= bufferLength;
ASSERT(isCopySafe);
if (!isCopySafe)
return;
size_t framesAvailable = bufferLength - m_readIndex;
size_t numberOfFrames1 = std::min(numberOfFrames, framesAvailable);
size_t numberOfFrames2 = numberOfFrames - numberOfFrames1;
float* source = m_buffer.data();
memcpy(destination, source + m_readIndex, sizeof(float) * numberOfFrames1);
memset(source + m_readIndex, 0, sizeof(float) * numberOfFrames1);
// Handle wrap-around if necessary
if (numberOfFrames2 > 0) {
memcpy(destination + numberOfFrames1, source, sizeof(float) * numberOfFrames2);
memset(source, 0, sizeof(float) * numberOfFrames2);
}
m_readIndex = (m_readIndex + numberOfFrames) % bufferLength;
m_readTimeFrame += numberOfFrames;
}
void ReverbAccumulationBuffer::updateReadIndex(int* readIndex, size_t numberOfFrames) const
{
// Update caller's readIndex
*readIndex = (*readIndex + numberOfFrames) % m_buffer.size();
}
int ReverbAccumulationBuffer::accumulate(float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames)
{
size_t bufferLength = m_buffer.size();
size_t writeIndex = (*readIndex + delayFrames) % bufferLength;
// Update caller's readIndex
*readIndex = (*readIndex + numberOfFrames) % bufferLength;
size_t framesAvailable = bufferLength - writeIndex;
size_t numberOfFrames1 = std::min(numberOfFrames, framesAvailable);
size_t numberOfFrames2 = numberOfFrames - numberOfFrames1;
float* destination = m_buffer.data();
bool isSafe = writeIndex <= bufferLength && numberOfFrames1 + writeIndex <= bufferLength && numberOfFrames2 <= bufferLength;
ASSERT(isSafe);
if (!isSafe)
return 0;
vadd(source, 1, destination + writeIndex, 1, destination + writeIndex, 1, numberOfFrames1);
// Handle wrap-around if necessary
if (numberOfFrames2 > 0)
vadd(source + numberOfFrames1, 1, destination, 1, destination, 1, numberOfFrames2);
return writeIndex;
}
void ReverbAccumulationBuffer::reset()
{
m_buffer.zero();
m_readIndex = 0;
m_readTimeFrame = 0;
}
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO)

View File

@ -0,0 +1,67 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ReverbAccumulationBuffer_h
#define ReverbAccumulationBuffer_h
#include "core/platform/audio/AudioArray.h"
namespace WebCore {
// ReverbAccumulationBuffer is a circular delay buffer with one client reading from it and multiple clients
// writing/accumulating to it at different delay offsets from the read position. The read operation will zero the memory
// just read from the buffer, so it will be ready for accumulation the next time around.
class ReverbAccumulationBuffer {
public:
ReverbAccumulationBuffer(size_t length);
// This will read from, then clear-out numberOfFrames
void readAndClear(float* destination, size_t numberOfFrames);
// Each ReverbConvolverStage will accumulate its output at the appropriate delay from the read position.
// We need to pass in and update readIndex here, since each ReverbConvolverStage may be running in
// a different thread than the realtime thread calling ReadAndClear() and maintaining m_readIndex
// Returns the writeIndex where the accumulation took place
int accumulate(float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames);
size_t readIndex() const { return m_readIndex; }
void updateReadIndex(int* readIndex, size_t numberOfFrames) const;
size_t readTimeFrame() const { return m_readTimeFrame; }
void reset();
private:
AudioFloatArray m_buffer;
size_t m_readIndex;
size_t m_readTimeFrame; // for debugging (frame on continuous timeline)
};
} // namespace WebCore
#endif // ReverbAccumulationBuffer_h

View File

@ -0,0 +1,238 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#if ENABLE(WEB_AUDIO)
#include "core/platform/audio/ReverbConvolver.h"
#include "core/platform/audio/AudioBus.h"
#include "core/platform/audio/VectorMath.h"
namespace WebCore {
using namespace VectorMath;
const int InputBufferSize = 8 * 16384;
// We only process the leading portion of the impulse response in the real-time thread. We don't exceed this length.
// It turns out then, that the background thread has about 278msec of scheduling slop.
// Empirically, this has been found to be a good compromise between giving enough time for scheduling slop,
// while still minimizing the amount of processing done in the primary (high-priority) thread.
// This was found to be a good value on Mac OS X, and may work well on other platforms as well, assuming
// the very rough scheduling latencies are similar on these time-scales. Of course, this code may need to be
// tuned for individual platforms if this assumption is found to be incorrect.
const size_t RealtimeFrameLimit = 8192 + 4096; // ~278msec @ 44.1KHz
const size_t MinFFTSize = 128;
const size_t MaxRealtimeFFTSize = 2048;
static void backgroundThreadEntry(void* threadData)
{
ReverbConvolver* reverbConvolver = static_cast<ReverbConvolver*>(threadData);
reverbConvolver->backgroundThreadEntry();
}
ReverbConvolver::ReverbConvolver(AudioChannel* impulseResponse, size_t renderSliceSize, size_t maxFFTSize, size_t convolverRenderPhase, bool useBackgroundThreads)
: m_impulseResponseLength(impulseResponse->length())
, m_accumulationBuffer(impulseResponse->length() + renderSliceSize)
, m_inputBuffer(InputBufferSize)
, m_minFFTSize(MinFFTSize) // First stage will have this size - successive stages will double in size each time
, m_maxFFTSize(maxFFTSize) // until we hit m_maxFFTSize
, m_useBackgroundThreads(useBackgroundThreads)
, m_backgroundThread(0)
, m_wantsToExit(false)
, m_moreInputBuffered(false)
{
// If we are using background threads then don't exceed this FFT size for the
// stages which run in the real-time thread. This avoids having only one or two
// large stages (size 16384 or so) at the end which take a lot of time every several
// processing slices. This way we amortize the cost over more processing slices.
m_maxRealtimeFFTSize = MaxRealtimeFFTSize;
// For the moment, a good way to know if we have real-time constraint is to check if we're using background threads.
// Otherwise, assume we're being run from a command-line tool.
bool hasRealtimeConstraint = useBackgroundThreads;
const float* response = impulseResponse->data();
size_t totalResponseLength = impulseResponse->length();
// The total latency is zero because the direct-convolution is used in the leading portion.
size_t reverbTotalLatency = 0;
size_t stageOffset = 0;
int i = 0;
size_t fftSize = m_minFFTSize;
while (stageOffset < totalResponseLength) {
size_t stageSize = fftSize / 2;
// For the last stage, it's possible that stageOffset is such that we're straddling the end
// of the impulse response buffer (if we use stageSize), so reduce the last stage's length...
if (stageSize + stageOffset > totalResponseLength)
stageSize = totalResponseLength - stageOffset;
// This "staggers" the time when each FFT happens so they don't all happen at the same time
int renderPhase = convolverRenderPhase + i * renderSliceSize;
bool useDirectConvolver = !stageOffset;
OwnPtr<ReverbConvolverStage> stage = adoptPtr(new ReverbConvolverStage(response, totalResponseLength, reverbTotalLatency, stageOffset, stageSize, fftSize, renderPhase, renderSliceSize, &m_accumulationBuffer, useDirectConvolver));
bool isBackgroundStage = false;
if (this->useBackgroundThreads() && stageOffset > RealtimeFrameLimit) {
m_backgroundStages.append(stage.release());
isBackgroundStage = true;
} else
m_stages.append(stage.release());
stageOffset += stageSize;
++i;
if (!useDirectConvolver) {
// Figure out next FFT size
fftSize *= 2;
}
if (hasRealtimeConstraint && !isBackgroundStage && fftSize > m_maxRealtimeFFTSize)
fftSize = m_maxRealtimeFFTSize;
if (fftSize > m_maxFFTSize)
fftSize = m_maxFFTSize;
}
// Start up background thread
// FIXME: would be better to up the thread priority here. It doesn't need to be real-time, but higher than the default...
if (this->useBackgroundThreads() && m_backgroundStages.size() > 0)
m_backgroundThread = createThread(WebCore::backgroundThreadEntry, this, "convolution background thread");
}
ReverbConvolver::~ReverbConvolver()
{
// Wait for background thread to stop
if (useBackgroundThreads() && m_backgroundThread) {
m_wantsToExit = true;
// Wake up thread so it can return
{
MutexLocker locker(m_backgroundThreadLock);
m_moreInputBuffered = true;
m_backgroundThreadCondition.signal();
}
waitForThreadCompletion(m_backgroundThread);
}
}
void ReverbConvolver::backgroundThreadEntry()
{
while (!m_wantsToExit) {
// Wait for realtime thread to give us more input
m_moreInputBuffered = false;
{
MutexLocker locker(m_backgroundThreadLock);
while (!m_moreInputBuffered && !m_wantsToExit)
m_backgroundThreadCondition.wait(m_backgroundThreadLock);
}
// Process all of the stages until their read indices reach the input buffer's write index
int writeIndex = m_inputBuffer.writeIndex();
// Even though it doesn't seem like every stage needs to maintain its own version of readIndex
// we do this in case we want to run in more than one background thread.
int readIndex;
while ((readIndex = m_backgroundStages[0]->inputReadIndex()) != writeIndex) { // FIXME: do better to detect buffer overrun...
// The ReverbConvolverStages need to process in amounts which evenly divide half the FFT size
const int SliceSize = MinFFTSize / 2;
// Accumulate contributions from each stage
for (size_t i = 0; i < m_backgroundStages.size(); ++i)
m_backgroundStages[i]->processInBackground(this, SliceSize);
}
}
}
void ReverbConvolver::process(const AudioChannel* sourceChannel, AudioChannel* destinationChannel, size_t framesToProcess)
{
bool isSafe = sourceChannel && destinationChannel && sourceChannel->length() >= framesToProcess && destinationChannel->length() >= framesToProcess;
ASSERT(isSafe);
if (!isSafe)
return;
const float* source = sourceChannel->data();
float* destination = destinationChannel->mutableData();
bool isDataSafe = source && destination;
ASSERT(isDataSafe);
if (!isDataSafe)
return;
// Feed input buffer (read by all threads)
m_inputBuffer.write(source, framesToProcess);
// Accumulate contributions from each stage
for (size_t i = 0; i < m_stages.size(); ++i)
m_stages[i]->process(source, framesToProcess);
// Finally read from accumulation buffer
m_accumulationBuffer.readAndClear(destination, framesToProcess);
// Now that we've buffered more input, wake up our background thread.
// Not using a MutexLocker looks strange, but we use a tryLock() instead because this is run on the real-time
// thread where it is a disaster for the lock to be contended (causes audio glitching). It's OK if we fail to
// signal from time to time, since we'll get to it the next time we're called. We're called repeatedly
// and frequently (around every 3ms). The background thread is processing well into the future and has a considerable amount of
// leeway here...
if (m_backgroundThreadLock.tryLock()) {
m_moreInputBuffered = true;
m_backgroundThreadCondition.signal();
m_backgroundThreadLock.unlock();
}
}
void ReverbConvolver::reset()
{
for (size_t i = 0; i < m_stages.size(); ++i)
m_stages[i]->reset();
for (size_t i = 0; i < m_backgroundStages.size(); ++i)
m_backgroundStages[i]->reset();
m_accumulationBuffer.reset();
m_inputBuffer.reset();
}
size_t ReverbConvolver::latencyFrames() const
{
return 0;
}
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO)

View File

@ -0,0 +1,95 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ReverbConvolver_h
#define ReverbConvolver_h
#include "core/platform/audio/AudioArray.h"
#include "core/platform/audio/DirectConvolver.h"
#include "core/platform/audio/FFTConvolver.h"
#include "core/platform/audio/ReverbAccumulationBuffer.h"
#include "core/platform/audio/ReverbConvolverStage.h"
#include "core/platform/audio/ReverbInputBuffer.h"
#include <wtf/OwnPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/Threading.h>
#include <wtf/Vector.h>
namespace WebCore {
class AudioChannel;
class ReverbConvolver {
public:
// maxFFTSize can be adjusted (from say 2048 to 32768) depending on how much precision is necessary.
// For certain tweaky de-convolving applications the phase errors add up quickly and lead to non-sensical results with
// larger FFT sizes and single-precision floats. In these cases 2048 is a good size.
// If not doing multi-threaded convolution, then should not go > 8192.
ReverbConvolver(AudioChannel* impulseResponse, size_t renderSliceSize, size_t maxFFTSize, size_t convolverRenderPhase, bool useBackgroundThreads);
~ReverbConvolver();
void process(const AudioChannel* sourceChannel, AudioChannel* destinationChannel, size_t framesToProcess);
void reset();
size_t impulseResponseLength() const { return m_impulseResponseLength; }
ReverbInputBuffer* inputBuffer() { return &m_inputBuffer; }
bool useBackgroundThreads() const { return m_useBackgroundThreads; }
void backgroundThreadEntry();
size_t latencyFrames() const;
private:
Vector<OwnPtr<ReverbConvolverStage> > m_stages;
Vector<OwnPtr<ReverbConvolverStage> > m_backgroundStages;
size_t m_impulseResponseLength;
ReverbAccumulationBuffer m_accumulationBuffer;
// One or more background threads read from this input buffer which is fed from the realtime thread.
ReverbInputBuffer m_inputBuffer;
// First stage will be of size m_minFFTSize. Each next stage will be twice as big until we hit m_maxFFTSize.
size_t m_minFFTSize;
size_t m_maxFFTSize;
// But don't exceed this size in the real-time thread (if we're doing background processing).
size_t m_maxRealtimeFFTSize;
// Background thread and synchronization
bool m_useBackgroundThreads;
ThreadIdentifier m_backgroundThread;
bool m_wantsToExit;
bool m_moreInputBuffered;
mutable Mutex m_backgroundThreadLock;
mutable ThreadCondition m_backgroundThreadCondition;
};
} // namespace WebCore
#endif // ReverbConvolver_h

View File

@ -0,0 +1,182 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#if ENABLE(WEB_AUDIO)
#include "core/platform/audio/ReverbConvolverStage.h"
#include "core/platform/audio/ReverbAccumulationBuffer.h"
#include "core/platform/audio/ReverbConvolver.h"
#include "core/platform/audio/ReverbInputBuffer.h"
#include "core/platform/audio/VectorMath.h"
#include <wtf/OwnPtr.h>
#include <wtf/PassOwnPtr.h>
namespace WebCore {
using namespace VectorMath;
ReverbConvolverStage::ReverbConvolverStage(const float* impulseResponse, size_t, size_t reverbTotalLatency, size_t stageOffset, size_t stageLength,
size_t fftSize, size_t renderPhase, size_t renderSliceSize, ReverbAccumulationBuffer* accumulationBuffer, bool directMode)
: m_accumulationBuffer(accumulationBuffer)
, m_accumulationReadIndex(0)
, m_inputReadIndex(0)
, m_directMode(directMode)
{
ASSERT(impulseResponse);
ASSERT(accumulationBuffer);
if (!m_directMode) {
m_fftKernel = adoptPtr(new FFTFrame(fftSize));
m_fftKernel->doPaddedFFT(impulseResponse + stageOffset, stageLength);
m_fftConvolver = adoptPtr(new FFTConvolver(fftSize));
} else {
m_directKernel = adoptPtr(new AudioFloatArray(fftSize / 2));
m_directKernel->copyToRange(impulseResponse + stageOffset, 0, fftSize / 2);
m_directConvolver = adoptPtr(new DirectConvolver(renderSliceSize));
}
m_temporaryBuffer.allocate(renderSliceSize);
// The convolution stage at offset stageOffset needs to have a corresponding delay to cancel out the offset.
size_t totalDelay = stageOffset + reverbTotalLatency;
// But, the FFT convolution itself incurs fftSize / 2 latency, so subtract this out...
size_t halfSize = fftSize / 2;
if (!m_directMode) {
ASSERT(totalDelay >= halfSize);
if (totalDelay >= halfSize)
totalDelay -= halfSize;
}
// We divide up the total delay, into pre and post delay sections so that we can schedule at exactly the moment when the FFT will happen.
// This is coordinated with the other stages, so they don't all do their FFTs at the same time...
int maxPreDelayLength = std::min(halfSize, totalDelay);
m_preDelayLength = totalDelay > 0 ? renderPhase % maxPreDelayLength : 0;
if (m_preDelayLength > totalDelay)
m_preDelayLength = 0;
m_postDelayLength = totalDelay - m_preDelayLength;
m_preReadWriteIndex = 0;
m_framesProcessed = 0; // total frames processed so far
size_t delayBufferSize = m_preDelayLength < fftSize ? fftSize : m_preDelayLength;
delayBufferSize = delayBufferSize < renderSliceSize ? renderSliceSize : delayBufferSize;
m_preDelayBuffer.allocate(delayBufferSize);
}
void ReverbConvolverStage::processInBackground(ReverbConvolver* convolver, size_t framesToProcess)
{
ReverbInputBuffer* inputBuffer = convolver->inputBuffer();
float* source = inputBuffer->directReadFrom(&m_inputReadIndex, framesToProcess);
process(source, framesToProcess);
}
void ReverbConvolverStage::process(const float* source, size_t framesToProcess)
{
ASSERT(source);
if (!source)
return;
// Deal with pre-delay stream : note special handling of zero delay.
const float* preDelayedSource;
float* preDelayedDestination;
float* temporaryBuffer;
bool isTemporaryBufferSafe = false;
if (m_preDelayLength > 0) {
// Handles both the read case (call to process() ) and the write case (memcpy() )
bool isPreDelaySafe = m_preReadWriteIndex + framesToProcess <= m_preDelayBuffer.size();
ASSERT(isPreDelaySafe);
if (!isPreDelaySafe)
return;
isTemporaryBufferSafe = framesToProcess <= m_temporaryBuffer.size();
preDelayedDestination = m_preDelayBuffer.data() + m_preReadWriteIndex;
preDelayedSource = preDelayedDestination;
temporaryBuffer = m_temporaryBuffer.data();
} else {
// Zero delay
preDelayedDestination = 0;
preDelayedSource = source;
temporaryBuffer = m_preDelayBuffer.data();
isTemporaryBufferSafe = framesToProcess <= m_preDelayBuffer.size();
}
ASSERT(isTemporaryBufferSafe);
if (!isTemporaryBufferSafe)
return;
if (m_framesProcessed < m_preDelayLength) {
// For the first m_preDelayLength frames don't process the convolver, instead simply buffer in the pre-delay.
// But while buffering the pre-delay, we still need to update our index.
m_accumulationBuffer->updateReadIndex(&m_accumulationReadIndex, framesToProcess);
} else {
// Now, run the convolution (into the delay buffer).
// An expensive FFT will happen every fftSize / 2 frames.
// We process in-place here...
if (!m_directMode)
m_fftConvolver->process(m_fftKernel.get(), preDelayedSource, temporaryBuffer, framesToProcess);
else
m_directConvolver->process(m_directKernel.get(), preDelayedSource, temporaryBuffer, framesToProcess);
// Now accumulate into reverb's accumulation buffer.
m_accumulationBuffer->accumulate(temporaryBuffer, framesToProcess, &m_accumulationReadIndex, m_postDelayLength);
}
// Finally copy input to pre-delay.
if (m_preDelayLength > 0) {
memcpy(preDelayedDestination, source, sizeof(float) * framesToProcess);
m_preReadWriteIndex += framesToProcess;
ASSERT(m_preReadWriteIndex <= m_preDelayLength);
if (m_preReadWriteIndex >= m_preDelayLength)
m_preReadWriteIndex = 0;
}
m_framesProcessed += framesToProcess;
}
void ReverbConvolverStage::reset()
{
if (!m_directMode)
m_fftConvolver->reset();
else
m_directConvolver->reset();
m_preDelayBuffer.zero();
m_accumulationReadIndex = 0;
m_inputReadIndex = 0;
m_framesProcessed = 0;
}
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO)

View File

@ -0,0 +1,85 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ReverbConvolverStage_h
#define ReverbConvolverStage_h
#include "core/platform/audio/AudioArray.h"
#include "core/platform/audio/FFTFrame.h"
#include <wtf/OwnPtr.h>
namespace WebCore {
class ReverbAccumulationBuffer;
class ReverbConvolver;
class FFTConvolver;
class DirectConvolver;
// A ReverbConvolverStage represents the convolution associated with a sub-section of a large impulse response.
// It incorporates a delay line to account for the offset of the sub-section within the larger impulse response.
class ReverbConvolverStage {
public:
// renderPhase is useful to know so that we can manipulate the pre versus post delay so that stages will perform
// their heavy work (FFT processing) on different slices to balance the load in a real-time thread.
ReverbConvolverStage(const float* impulseResponse, size_t responseLength, size_t reverbTotalLatency, size_t stageOffset, size_t stageLength, size_t fftSize, size_t renderPhase, size_t renderSliceSize, ReverbAccumulationBuffer*, bool directMode = false);
// WARNING: framesToProcess must be such that it evenly divides the delay buffer size (stage_offset).
void process(const float* source, size_t framesToProcess);
void processInBackground(ReverbConvolver* convolver, size_t framesToProcess);
void reset();
// Useful for background processing
int inputReadIndex() const { return m_inputReadIndex; }
private:
OwnPtr<FFTFrame> m_fftKernel;
OwnPtr<FFTConvolver> m_fftConvolver;
AudioFloatArray m_preDelayBuffer;
ReverbAccumulationBuffer* m_accumulationBuffer;
int m_accumulationReadIndex;
int m_inputReadIndex;
size_t m_preDelayLength;
size_t m_postDelayLength;
size_t m_preReadWriteIndex;
size_t m_framesProcessed;
AudioFloatArray m_temporaryBuffer;
bool m_directMode;
OwnPtr<AudioFloatArray> m_directKernel;
OwnPtr<DirectConvolver> m_directConvolver;
};
} // namespace WebCore
#endif // ReverbConvolverStage_h

View File

@ -0,0 +1,89 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#if ENABLE(WEB_AUDIO)
#include "core/platform/audio/ReverbInputBuffer.h"
namespace WebCore {
ReverbInputBuffer::ReverbInputBuffer(size_t length)
: m_buffer(length)
, m_writeIndex(0)
{
}
void ReverbInputBuffer::write(const float* sourceP, size_t numberOfFrames)
{
size_t bufferLength = m_buffer.size();
bool isCopySafe = m_writeIndex + numberOfFrames <= bufferLength;
ASSERT(isCopySafe);
if (!isCopySafe)
return;
memcpy(m_buffer.data() + m_writeIndex, sourceP, sizeof(float) * numberOfFrames);
m_writeIndex += numberOfFrames;
ASSERT(m_writeIndex <= bufferLength);
if (m_writeIndex >= bufferLength)
m_writeIndex = 0;
}
float* ReverbInputBuffer::directReadFrom(int* readIndex, size_t numberOfFrames)
{
size_t bufferLength = m_buffer.size();
bool isPointerGood = readIndex && *readIndex >= 0 && *readIndex + numberOfFrames <= bufferLength;
ASSERT(isPointerGood);
if (!isPointerGood) {
// Should never happen in practice but return pointer to start of buffer (avoid crash)
if (readIndex)
*readIndex = 0;
return m_buffer.data();
}
float* sourceP = m_buffer.data();
float* p = sourceP + *readIndex;
// Update readIndex
*readIndex = (*readIndex + numberOfFrames) % bufferLength;
return p;
}
void ReverbInputBuffer::reset()
{
m_buffer.zero();
m_writeIndex = 0;
}
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO)

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ReverbInputBuffer_h
#define ReverbInputBuffer_h
#include "core/platform/audio/AudioArray.h"
namespace WebCore {
// ReverbInputBuffer is used to buffer input samples for deferred processing by the background threads.
class ReverbInputBuffer {
public:
ReverbInputBuffer(size_t length);
// The realtime audio thread keeps writing samples here.
// The assumption is that the buffer's length is evenly divisible by numberOfFrames (for nearly all cases this will be fine).
// FIXME: remove numberOfFrames restriction...
void write(const float* sourceP, size_t numberOfFrames);
// Background threads can call this to check if there's anything to read...
size_t writeIndex() const { return m_writeIndex; }
// The individual background threads read here (and hope that they can keep up with the buffer writing).
// readIndex is updated with the next readIndex to read from...
// The assumption is that the buffer's length is evenly divisible by numberOfFrames.
// FIXME: remove numberOfFrames restriction...
float* directReadFrom(int* readIndex, size_t numberOfFrames);
void reset();
private:
AudioFloatArray m_buffer;
size_t m_writeIndex;
};
} // namespace WebCore
#endif // ReverbInputBuffer_h