mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
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:
parent
82519eedaf
commit
0bdb88b1cd
385
content/media/webaudio/blink/DirectConvolver.cpp
Normal file
385
content/media/webaudio/blink/DirectConvolver.cpp
Normal 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)
|
59
content/media/webaudio/blink/DirectConvolver.h
Normal file
59
content/media/webaudio/blink/DirectConvolver.h
Normal 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
|
120
content/media/webaudio/blink/FFTConvolver.cpp
Normal file
120
content/media/webaudio/blink/FFTConvolver.cpp
Normal 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)
|
71
content/media/webaudio/blink/FFTConvolver.h
Normal file
71
content/media/webaudio/blink/FFTConvolver.h
Normal 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
|
243
content/media/webaudio/blink/Reverb.cpp
Normal file
243
content/media/webaudio/blink/Reverb.cpp
Normal 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)
|
67
content/media/webaudio/blink/Reverb.h
Normal file
67
content/media/webaudio/blink/Reverb.h
Normal 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
|
119
content/media/webaudio/blink/ReverbAccumulationBuffer.cpp
Normal file
119
content/media/webaudio/blink/ReverbAccumulationBuffer.cpp
Normal 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)
|
67
content/media/webaudio/blink/ReverbAccumulationBuffer.h
Normal file
67
content/media/webaudio/blink/ReverbAccumulationBuffer.h
Normal 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
|
238
content/media/webaudio/blink/ReverbConvolver.cpp
Normal file
238
content/media/webaudio/blink/ReverbConvolver.cpp
Normal 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)
|
95
content/media/webaudio/blink/ReverbConvolver.h
Normal file
95
content/media/webaudio/blink/ReverbConvolver.h
Normal 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
|
182
content/media/webaudio/blink/ReverbConvolverStage.cpp
Normal file
182
content/media/webaudio/blink/ReverbConvolverStage.cpp
Normal 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)
|
85
content/media/webaudio/blink/ReverbConvolverStage.h
Normal file
85
content/media/webaudio/blink/ReverbConvolverStage.h
Normal 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
|
89
content/media/webaudio/blink/ReverbInputBuffer.cpp
Normal file
89
content/media/webaudio/blink/ReverbInputBuffer.cpp
Normal 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)
|
64
content/media/webaudio/blink/ReverbInputBuffer.h
Normal file
64
content/media/webaudio/blink/ReverbInputBuffer.h
Normal 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
|
Loading…
Reference in New Issue
Block a user