diff --git a/content/media/AudioNodeEngine.cpp b/content/media/AudioNodeEngine.cpp index 04d8717e5f18..23f21a3b73ee 100644 --- a/content/media/AudioNodeEngine.cpp +++ b/content/media/AudioNodeEngine.cpp @@ -39,20 +39,28 @@ WriteZeroesToAudioBlock(AudioChunk* aChunk, uint32_t aStart, uint32_t aLength) } } +void AudioBufferAddWithScale(const float* aInput, + float aScale, + float* aOutput, + uint32_t aSize) +{ + if (aScale == 1.0f) { + for (uint32_t i = 0; i < aSize; ++i) { + aOutput[i] += aInput[i]; + } + } else { + for (uint32_t i = 0; i < aSize; ++i) { + aOutput[i] += aInput[i]*aScale; + } + } +} + void AudioBlockAddChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE], float aScale, float aOutput[WEBAUDIO_BLOCK_SIZE]) { - if (aScale == 1.0f) { - for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) { - aOutput[i] += aInput[i]; - } - } else { - for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) { - aOutput[i] += aInput[i]*aScale; - } - } + AudioBufferAddWithScale(aInput, aScale, aOutput, WEBAUDIO_BLOCK_SIZE); } void @@ -98,14 +106,23 @@ AudioBlockCopyChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE], } void -AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE], - uint32_t aChannelCount, - float aScale) +AudioBufferInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE], + uint32_t aChannelCount, + float aScale) +{ + AudioBufferInPlaceScale(aBlock, aChannelCount, aScale, WEBAUDIO_BLOCK_SIZE); +} + +void +AudioBufferInPlaceScale(float* aBlock, + uint32_t aChannelCount, + float aScale, + uint32_t aSize) { if (aScale == 1.0f) { return; } - for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE * aChannelCount; ++i) { + for (uint32_t i = 0; i < aSize * aChannelCount; ++i) { *aBlock++ *= aScale; } } @@ -141,4 +158,16 @@ AudioBlockPanStereoToStereo(const float aInputL[WEBAUDIO_BLOCK_SIZE], } } } + +float +AudioBufferSumOfSquares(const float* aInput, uint32_t aLength) +{ + float sum = 0.0f; + while (aLength--) { + sum += *aInput * *aInput; + ++aInput; + } + return sum; +} + } diff --git a/content/media/AudioNodeEngine.h b/content/media/AudioNodeEngine.h index 92bd2c0e6aff..25f5c4f29239 100644 --- a/content/media/AudioNodeEngine.h +++ b/content/media/AudioNodeEngine.h @@ -87,6 +87,14 @@ void AllocateAudioBlock(uint32_t aChannelCount, AudioChunk* aChunk); */ void WriteZeroesToAudioBlock(AudioChunk* aChunk, uint32_t aStart, uint32_t aLength); +/** + * Pointwise multiply-add operation. aScale == 1.0f should be optimized. + */ +void AudioBufferAddWithScale(const float* aInput, + float aScale, + float* aOutput, + uint32_t aSize); + /** * Pointwise multiply-add operation. aScale == 1.0f should be optimized. */ @@ -121,9 +129,17 @@ void BufferComplexMultiply(const float* aInput, /** * In place gain. aScale == 1.0f should be optimized. */ -void AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE], - uint32_t aChannelCount, - float aScale); +void AudioBufferInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE], + uint32_t aChannelCount, + float aScale); + +/** + * In place gain. aScale == 1.0f should be optimized. + */ +void AudioBufferInPlaceScale(float* aBlock, + uint32_t aChannelCount, + float aScale, + uint32_t aSize); /** * Upmix a mono input to a stereo output, scaling the two output channels by two @@ -147,6 +163,12 @@ AudioBlockPanStereoToStereo(const float aInputL[WEBAUDIO_BLOCK_SIZE], float aOutputL[WEBAUDIO_BLOCK_SIZE], float aOutputR[WEBAUDIO_BLOCK_SIZE]); +/** + * Return the sum of squares of all of the samples in the input. + */ +float +AudioBufferSumOfSquares(const float* aInput, uint32_t aLength); + /** * All methods of this class and its subclasses are called on the * MediaStreamGraph thread. diff --git a/content/media/webaudio/AnalyserNode.cpp b/content/media/webaudio/AnalyserNode.cpp index 44cb544ab7a8..d5fbe9cd0fdd 100644 --- a/content/media/webaudio/AnalyserNode.cpp +++ b/content/media/webaudio/AnalyserNode.cpp @@ -288,8 +288,8 @@ AnalyserNode::AppendChunk(const AudioChunk& aChunk) mBuffer.Elements() + mWriteIndex); } if (channelCount > 1) { - AudioBlockInPlaceScale(mBuffer.Elements() + mWriteIndex, 1, - 1.0f / aChunk.mChannelData.Length()); + AudioBufferInPlaceScale(mBuffer.Elements() + mWriteIndex, 1, + 1.0f / aChunk.mChannelData.Length()); } mWriteIndex += chunkDuration; MOZ_ASSERT(mWriteIndex <= bufferSize); diff --git a/content/media/webaudio/FFTBlock.h b/content/media/webaudio/FFTBlock.h index f3eba43c4d75..4b77f777c2fc 100644 --- a/content/media/webaudio/FFTBlock.h +++ b/content/media/webaudio/FFTBlock.h @@ -48,6 +48,16 @@ public: mFFTSize / 2 + 1); } + void PerformPaddedFFT(const float* aData, size_t dataSize) + { + MOZ_ASSERT(dataSize <= FFTSize()); + nsTArray paddedData; + paddedData.SetLength(FFTSize()); + PodCopy(paddedData.Elements(), aData, dataSize); + PodZero(paddedData.Elements() + dataSize, mFFTSize - dataSize); + PerformFFT(paddedData.Elements()); + } + void SetFFTSize(uint32_t aSize) { mFFTSize = aSize; @@ -55,7 +65,7 @@ public: PodZero(mOutputBuffer.Elements(), aSize / 2 + 1); } - float FFTSize() const + uint32_t FFTSize() const { return mFFTSize; } diff --git a/content/media/webaudio/Makefile.in b/content/media/webaudio/Makefile.in index ba100bd39c8f..42d5976341e7 100644 --- a/content/media/webaudio/Makefile.in +++ b/content/media/webaudio/Makefile.in @@ -18,3 +18,4 @@ endif # !_MSC_VER FORCE_STATIC_LIB := 1 include $(topsrcdir)/config/rules.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk diff --git a/content/media/webaudio/PannerNode.cpp b/content/media/webaudio/PannerNode.cpp index 8100bd5e2502..89036112024a 100644 --- a/content/media/webaudio/PannerNode.cpp +++ b/content/media/webaudio/PannerNode.cpp @@ -332,7 +332,7 @@ PannerNodeEngine::DistanceAndConeGain(AudioChunk* aChunk, float aGain) float* samples = static_cast(const_cast(*aChunk->mChannelData.Elements())); uint32_t channelCount = aChunk->mChannelData.Length(); - AudioBlockInPlaceScale(samples, channelCount, aGain); + AudioBufferInPlaceScale(samples, channelCount, aGain); } // This algorithm is specicied in the webaudio spec. diff --git a/content/media/webaudio/blink/DirectConvolver.cpp b/content/media/webaudio/blink/DirectConvolver.cpp index 43a3ab3d47ef..43461ad37e17 100644 --- a/content/media/webaudio/blink/DirectConvolver.cpp +++ b/content/media/webaudio/blink/DirectConvolver.cpp @@ -26,76 +26,45 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" +#include "DirectConvolver.h" +#include "mozilla/PodOperations.h" -#if ENABLE(WEB_AUDIO) - -#include "core/platform/audio/DirectConvolver.h" - -#if OS(DARWIN) -#include -#endif - -#include "core/platform/audio/VectorMath.h" +using namespace mozilla; 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) { + m_buffer.SetLength(inputBlockSize * 2); + PodZero(m_buffer.Elements(), inputBlockSize * 2); } -void DirectConvolver::process(AudioFloatArray* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess) +void DirectConvolver::process(const nsTArray* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess) { - ASSERT(framesToProcess == m_inputBlockSize); + MOZ_ASSERT(framesToProcess == m_inputBlockSize); if (framesToProcess != m_inputBlockSize) return; // Only support kernelSize <= m_inputBlockSize - size_t kernelSize = convolutionKernel->size(); - ASSERT(kernelSize <= m_inputBlockSize); + size_t kernelSize = convolutionKernel->Length(); + MOZ_ASSERT(kernelSize <= m_inputBlockSize); if (kernelSize > m_inputBlockSize) return; - float* kernelP = convolutionKernel->data(); + const float* kernelP = convolutionKernel->Elements(); // Sanity check - bool isCopyGood = kernelP && sourceP && destP && m_buffer.data(); - ASSERT(isCopyGood); + bool isCopyGood = kernelP && sourceP && destP && m_buffer.Elements(); + MOZ_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(sourceP), framesToProcess, static_cast(kernelP), kernelSize, static_cast(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; + float* inputP = m_buffer.Elements() + 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]; \ @@ -365,21 +334,14 @@ void DirectConvolver::process(AudioFloatArray* convolutionKernel, const float* s } destP[i++] = sum; } -#endif // OS(DARWIN) // Copy 2nd half of input buffer to 1st half. - memcpy(m_buffer.data(), inputP, sizeof(float) * framesToProcess); -#endif + memcpy(m_buffer.Elements(), inputP, sizeof(float) * framesToProcess); } void DirectConvolver::reset() { - m_buffer.zero(); -#if USE(WEBAUDIO_IPP) - m_overlayBuffer.zero(); -#endif // USE(WEBAUDIO_IPP) + PodZero(m_buffer.Elements(), m_buffer.Length()); } } // namespace WebCore - -#endif // ENABLE(WEB_AUDIO) diff --git a/content/media/webaudio/blink/DirectConvolver.h b/content/media/webaudio/blink/DirectConvolver.h index 0528d7cb6ce6..0a89cd59f8d2 100644 --- a/content/media/webaudio/blink/DirectConvolver.h +++ b/content/media/webaudio/blink/DirectConvolver.h @@ -29,11 +29,7 @@ #ifndef DirectConvolver_h #define DirectConvolver_h -#include "core/platform/audio/AudioArray.h" - -#if USE(WEBAUDIO_IPP) -#include -#endif // USE(WEBAUDIO_IPP) +#include "nsTArray.h" namespace WebCore { @@ -41,17 +37,14 @@ class DirectConvolver { public: DirectConvolver(size_t inputBlockSize); - void process(AudioFloatArray* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess); + void process(const nsTArray* 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; + nsTArray m_buffer; }; } // namespace WebCore diff --git a/content/media/webaudio/blink/FFTConvolver.cpp b/content/media/webaudio/blink/FFTConvolver.cpp index 4566ec169335..b4c39c862c27 100644 --- a/content/media/webaudio/blink/FFTConvolver.cpp +++ b/content/media/webaudio/blink/FFTConvolver.cpp @@ -26,35 +26,33 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" +#include "FFTConvolver.h" +#include "mozilla/PodOperations.h" -#if ENABLE(WEB_AUDIO) - -#include "core/platform/audio/FFTConvolver.h" - -#include "core/platform/audio/VectorMath.h" +using namespace mozilla; 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) { + m_inputBuffer.SetLength(fftSize); + PodZero(m_inputBuffer.Elements(), fftSize); + m_outputBuffer.SetLength(fftSize); + PodZero(m_outputBuffer.Elements(), fftSize); + m_lastOverlapBuffer.SetLength(fftSize / 2); + PodZero(m_lastOverlapBuffer.Elements(), fftSize / 2); } -void FFTConvolver::process(FFTFrame* fftKernel, const float* sourceP, float* destP, size_t framesToProcess) +void FFTConvolver::process(FFTBlock* 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); + MOZ_ASSERT(isGood); if (!isGood) return; @@ -63,22 +61,22 @@ void FFTConvolver::process(FFTFrame* fftKernel, const float* sourceP, float* des 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(); + float* inputP = m_inputBuffer.Elements(); // Sanity check - bool isCopyGood1 = sourceP && inputP && m_readWriteIndex + divisionSize <= m_inputBuffer.size(); - ASSERT(isCopyGood1); + bool isCopyGood1 = sourceP && inputP && m_readWriteIndex + divisionSize <= m_inputBuffer.Length(); + MOZ_ASSERT(isCopyGood1); if (!isCopyGood1) return; memcpy(inputP + m_readWriteIndex, sourceP, sizeof(float) * divisionSize); // Copy samples from output buffer - float* outputP = m_outputBuffer.data(); + float* outputP = m_outputBuffer.Elements(); // Sanity check - bool isCopyGood2 = destP && outputP && m_readWriteIndex + divisionSize <= m_outputBuffer.size(); - ASSERT(isCopyGood2); + bool isCopyGood2 = destP && outputP && m_readWriteIndex + divisionSize <= m_outputBuffer.Length(); + MOZ_ASSERT(isCopyGood2); if (!isCopyGood2) return; @@ -88,20 +86,21 @@ void FFTConvolver::process(FFTFrame* fftKernel, const float* sourceP, float* des // 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()); + m_frame.PerformFFT(m_inputBuffer.Elements()); + m_frame.Multiply(*fftKernel); + m_frame.PerformInverseFFT(m_outputBuffer.Elements()); // Overlap-add 1st half from previous time - vadd(m_outputBuffer.data(), 1, m_lastOverlapBuffer.data(), 1, m_outputBuffer.data(), 1, halfSize); + AudioBufferAddWithScale(m_lastOverlapBuffer.Elements(), 1.0f, + m_outputBuffer.Elements(), halfSize); // Finally, save 2nd half of result - bool isCopyGood3 = m_outputBuffer.size() == 2 * halfSize && m_lastOverlapBuffer.size() == halfSize; - ASSERT(isCopyGood3); + bool isCopyGood3 = m_outputBuffer.Length() == 2 * halfSize && m_lastOverlapBuffer.Length() == halfSize; + MOZ_ASSERT(isCopyGood3); if (!isCopyGood3) return; - memcpy(m_lastOverlapBuffer.data(), m_outputBuffer.data() + halfSize, sizeof(float) * halfSize); + memcpy(m_lastOverlapBuffer.Elements(), m_outputBuffer.Elements() + halfSize, sizeof(float) * halfSize); // Reset index back to start for next time m_readWriteIndex = 0; @@ -111,10 +110,8 @@ void FFTConvolver::process(FFTFrame* fftKernel, const float* sourceP, float* des void FFTConvolver::reset() { - m_lastOverlapBuffer.zero(); + PodZero(m_lastOverlapBuffer.Elements(), m_lastOverlapBuffer.Length()); m_readWriteIndex = 0; } } // namespace WebCore - -#endif // ENABLE(WEB_AUDIO) diff --git a/content/media/webaudio/blink/FFTConvolver.h b/content/media/webaudio/blink/FFTConvolver.h index 73f6e58a4696..dea9a7c76367 100644 --- a/content/media/webaudio/blink/FFTConvolver.h +++ b/content/media/webaudio/blink/FFTConvolver.h @@ -29,11 +29,14 @@ #ifndef FFTConvolver_h #define FFTConvolver_h -#include "core/platform/audio/AudioArray.h" -#include "core/platform/audio/FFTFrame.h" +#include "nsTArray.h" +#include "mozilla/FFTBlock.h" namespace WebCore { +typedef nsTArray AudioFloatArray; +using mozilla::FFTBlock; + class FFTConvolver { public: // fftSize must be a power of two @@ -46,14 +49,14 @@ public: // 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 process(FFTBlock* fftKernel, const float* sourceP, float* destP, size_t framesToProcess); void reset(); - size_t fftSize() const { return m_frame.fftSize(); } + size_t fftSize() const { return m_frame.FFTSize(); } private: - FFTFrame m_frame; + FFTBlock m_frame; // Buffer input until we get fftSize / 2 samples then do an FFT size_t m_readWriteIndex; diff --git a/content/media/webaudio/blink/Makefile.in b/content/media/webaudio/blink/Makefile.in index 8a062b45249d..29d314bc71c3 100644 --- a/content/media/webaudio/blink/Makefile.in +++ b/content/media/webaudio/blink/Makefile.in @@ -15,3 +15,4 @@ LIBXUL_LIBRARY := 1 FORCE_STATIC_LIB := 1 include $(topsrcdir)/config/rules.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk diff --git a/content/media/webaudio/blink/Reverb.cpp b/content/media/webaudio/blink/Reverb.cpp index 0a934ad7c0e0..c689558a3275 100644 --- a/content/media/webaudio/blink/Reverb.cpp +++ b/content/media/webaudio/blink/Reverb.cpp @@ -26,54 +26,39 @@ * 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 "Reverb.h" #include -#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 -#include -#include +#include "ReverbConvolver.h" +#include "mozilla/FloatingPoint.h" -#if OS(DARWIN) -using namespace std; -#endif +using namespace mozilla; 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) + +static float calculateNormalizationScale(ThreadSharedFloatArrayBufferList* response, size_t aLength, float sampleRate) { // Normalize by RMS power - size_t numberOfChannels = response->numberOfChannels(); - size_t length = response->length(); + size_t numberOfChannels = response->GetChannels(); float power = 0; for (size_t i = 0; i < numberOfChannels; ++i) { - float channelPower = 0; - vsvesq(response->channel(i)->data(), 1, &channelPower, length); + float channelPower = AudioBufferSumOfSquares(static_cast(response->GetData(i)), aLength); power += channelPower; } - power = sqrt(power / (numberOfChannels * length)); + power = sqrt(power / (numberOfChannels * aLength)); // Protect against accidental overload - if (std::isinf(power) || std::isnan(power) || power < MinPower) + if (!IsFinite(power) || IsNaN(power) || power < MinPower) power = MinPower; float scale = 1 / power; @@ -81,163 +66,174 @@ static float calculateNormalizationScale(AudioBus* response) 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(); + if (sampleRate) + scale *= GainCalibrationSampleRate / sampleRate; // True-stereo compensation - if (response->numberOfChannels() == 4) + if (response->GetChannels() == 4) scale *= 0.5f; return scale; } -Reverb::Reverb(AudioBus* impulseResponse, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize) +Reverb::Reverb(ThreadSharedFloatArrayBufferList* impulseResponse, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate) { float scale = 1; if (normalize) { - scale = calculateNormalizationScale(impulseResponse); + scale = calculateNormalizationScale(impulseResponse, impulseResponseBufferLength, sampleRate); - if (scale) - impulseResponse->scale(scale); + if (scale) { + for (uint32_t i = 0; i < impulseResponse->GetChannels(); ++i) { + AudioBufferInPlaceScale(const_cast(impulseResponse->GetData(i)), + 1, scale, impulseResponseBufferLength); + } + } } - initialize(impulseResponse, renderSliceSize, maxFFTSize, numberOfChannels, useBackgroundThreads); + initialize(impulseResponse, impulseResponseBufferLength, 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); + if (normalize && scale) { + for (uint32_t i = 0; i < impulseResponse->GetChannels(); ++i) { + AudioBufferInPlaceScale(const_cast(impulseResponse->GetData(i)), + 1, 1 / scale, impulseResponseBufferLength); + } + } } -void Reverb::initialize(AudioBus* impulseResponseBuffer, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads) +void Reverb::initialize(ThreadSharedFloatArrayBufferList* impulseResponseBuffer, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads) { - m_impulseResponseLength = impulseResponseBuffer->length(); + m_impulseResponseLength = impulseResponseBufferLength; // The reverb can handle a mono impulse response and still do stereo processing - size_t numResponseChannels = impulseResponseBuffer->numberOfChannels(); - m_convolvers.reserveCapacity(numberOfChannels); + size_t numResponseChannels = impulseResponseBuffer->GetChannels(); + m_convolvers.SetCapacity(numberOfChannels); int convolverRenderPhase = 0; for (size_t i = 0; i < numResponseChannels; ++i) { - AudioChannel* channel = impulseResponseBuffer->channel(i); + const float* channel = impulseResponseBuffer->GetData(i); + size_t length = impulseResponseBufferLength; - OwnPtr convolver = adoptPtr(new ReverbConvolver(channel, renderSliceSize, maxFFTSize, convolverRenderPhase, useBackgroundThreads)); - m_convolvers.append(convolver.release()); + nsAutoPtr convolver(new ReverbConvolver(channel, length, renderSliceSize, maxFFTSize, convolverRenderPhase, useBackgroundThreads)); + m_convolvers.AppendElement(convolver.forget()); 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); + if (numResponseChannels == 4) { + AllocateAudioBlock(2, &m_tempBuffer); + WriteZeroesToAudioBlock(&m_tempBuffer, 0, WEBAUDIO_BLOCK_SIZE); + } } -void Reverb::process(const AudioBus* sourceBus, AudioBus* destinationBus, size_t framesToProcess) +void Reverb::process(const AudioChunk* sourceBus, AudioChunk* 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); + bool isSafeToProcess = sourceBus && destinationBus && sourceBus->mChannelData.Length() > 0 && destinationBus->mChannelData.Length() > 0 + && framesToProcess <= MaxFrameSize && framesToProcess <= size_t(sourceBus->mDuration) && framesToProcess <= size_t(destinationBus->mDuration); + + MOZ_ASSERT(isSafeToProcess); if (!isSafeToProcess) return; // For now only handle mono or stereo output - if (destinationBus->numberOfChannels() > 2) { - destinationBus->zero(); + if (destinationBus->mChannelData.Length() > 2) { + destinationBus->SetNull(destinationBus->mDuration); return; } - AudioChannel* destinationChannelL = destinationBus->channel(0); - const AudioChannel* sourceChannelL = sourceBus->channel(0); + float* destinationChannelL = static_cast(const_cast(destinationBus->mChannelData[0])); + const float* sourceBusL = static_cast(sourceBus->mChannelData[0]); // Handle input -> output matrixing... - size_t numInputChannels = sourceBus->numberOfChannels(); - size_t numOutputChannels = destinationBus->numberOfChannels(); - size_t numReverbChannels = m_convolvers.size(); + size_t numInputChannels = sourceBus->mChannelData.Length(); + size_t numOutputChannels = destinationBus->mChannelData.Length(); + size_t numReverbChannels = m_convolvers.Length(); 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); + const float* sourceBusR = static_cast(sourceBus->mChannelData[1]); + float* destinationChannelR = static_cast(const_cast(destinationBus->mChannelData[1])); + m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess); + m_convolvers[1]->process(sourceBusR, sourceBus->mDuration, destinationChannelR, destinationBus->mDuration, 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); + float* destinationChannel = static_cast(const_cast(destinationBus->mChannelData[i])); + m_convolvers[i]->process(sourceBusL, sourceBus->mDuration, destinationChannel, destinationBus->mDuration, framesToProcess); } } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 2) { // 1 -> 1 -> 2 - m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess); + m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess); // simply copy L -> R - AudioChannel* destinationChannelR = destinationBus->channel(1); - bool isCopySafe = destinationChannelL->data() && destinationChannelR->data() && destinationChannelL->length() >= framesToProcess && destinationChannelR->length() >= framesToProcess; - ASSERT(isCopySafe); + float* destinationChannelR = static_cast(const_cast(destinationBus->mChannelData[1])); + bool isCopySafe = destinationChannelL && destinationChannelR && size_t(destinationBus->mDuration) >= framesToProcess && size_t(destinationBus->mDuration) >= framesToProcess; + MOZ_ASSERT(isCopySafe); if (!isCopySafe) return; - memcpy(destinationChannelR->mutableData(), destinationChannelL->data(), sizeof(float) * framesToProcess); + PodCopy(destinationChannelR, destinationChannelL, framesToProcess); } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 1) { // 1 -> 1 -> 1 - m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess); + m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, 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); + const float* sourceBusR = static_cast(sourceBus->mChannelData[1]); + float* destinationChannelR = static_cast(const_cast(destinationBus->mChannelData[1])); - AudioChannel* tempChannelL = m_tempBuffer->channel(0); - AudioChannel* tempChannelR = m_tempBuffer->channel(1); + float* tempChannelL = static_cast(const_cast(m_tempBuffer.mChannelData[0])); + float* tempChannelR = static_cast(const_cast(m_tempBuffer.mChannelData[1])); // Process left virtual source - m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess); - m_convolvers[1]->process(sourceChannelL, destinationChannelR, framesToProcess); + m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess); + m_convolvers[1]->process(sourceBusL, sourceBus->mDuration, destinationChannelR, destinationBus->mDuration, framesToProcess); // Process right virtual source - m_convolvers[2]->process(sourceChannelR, tempChannelL, framesToProcess); - m_convolvers[3]->process(sourceChannelR, tempChannelR, framesToProcess); + m_convolvers[2]->process(sourceBusR, sourceBus->mDuration, tempChannelL, m_tempBuffer.mDuration, framesToProcess); + m_convolvers[3]->process(sourceBusR, sourceBus->mDuration, tempChannelR, m_tempBuffer.mDuration, framesToProcess); - destinationBus->sumFrom(*m_tempBuffer); + AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->mDuration); + AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->mDuration); } 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); + float* destinationChannelR = static_cast(const_cast(destinationBus->mChannelData[1])); - AudioChannel* tempChannelL = m_tempBuffer->channel(0); - AudioChannel* tempChannelR = m_tempBuffer->channel(1); + float* tempChannelL = static_cast(const_cast(m_tempBuffer.mChannelData[0])); + float* tempChannelR = static_cast(const_cast(m_tempBuffer.mChannelData[1])); // Process left virtual source - m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess); - m_convolvers[1]->process(sourceChannelL, destinationChannelR, framesToProcess); + m_convolvers[0]->process(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess); + m_convolvers[1]->process(sourceBusL, sourceBus->mDuration, destinationChannelR, destinationBus->mDuration, framesToProcess); // Process right virtual source - m_convolvers[2]->process(sourceChannelL, tempChannelL, framesToProcess); - m_convolvers[3]->process(sourceChannelL, tempChannelR, framesToProcess); + m_convolvers[2]->process(sourceBusL, sourceBus->mDuration, tempChannelL, m_tempBuffer.mDuration, framesToProcess); + m_convolvers[3]->process(sourceBusL, sourceBus->mDuration, tempChannelR, m_tempBuffer.mDuration, framesToProcess); - destinationBus->sumFrom(*m_tempBuffer); + AudioBufferAddWithScale(tempChannelL, 1.0f, destinationChannelL, sourceBus->mDuration); + AudioBufferAddWithScale(tempChannelR, 1.0f, destinationChannelR, sourceBus->mDuration); } else { // Handle gracefully any unexpected / unsupported matrixing // FIXME: add code for 5.1 support... - destinationBus->zero(); + destinationBus->SetNull(destinationBus->mDuration); } } void Reverb::reset() { - for (size_t i = 0; i < m_convolvers.size(); ++i) + for (size_t i = 0; i < m_convolvers.Length(); ++i) m_convolvers[i]->reset(); } size_t Reverb::latencyFrames() const { - return !m_convolvers.isEmpty() ? m_convolvers.first()->latencyFrames() : 0; + return !m_convolvers.IsEmpty() ? m_convolvers[0]->latencyFrames() : 0; } } // namespace WebCore - -#endif // ENABLE(WEB_AUDIO) diff --git a/content/media/webaudio/blink/Reverb.h b/content/media/webaudio/blink/Reverb.h index 37c9095a9f68..89794748742a 100644 --- a/content/media/webaudio/blink/Reverb.h +++ b/content/media/webaudio/blink/Reverb.h @@ -29,13 +29,16 @@ #ifndef Reverb_h #define Reverb_h -#include "core/platform/audio/ReverbConvolver.h" -#include +#include "ReverbConvolver.h" +#include "nsAutoPtr.h" +#include "nsTArray.h" + +namespace mozilla { +class ThreadSharedFloatArrayBufferList; +} namespace WebCore { -class AudioBus; - // Multi-channel convolution reverb with channel matrixing - one or more ReverbConvolver objects are used internally. class Reverb { @@ -43,23 +46,23 @@ 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); + Reverb(mozilla::ThreadSharedFloatArrayBufferList* impulseResponseBuffer, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate); - void process(const AudioBus* sourceBus, AudioBus* destinationBus, size_t framesToProcess); + void process(const mozilla::AudioChunk* sourceBus, mozilla::AudioChunk* 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); + void initialize(mozilla::ThreadSharedFloatArrayBufferList* impulseResponseBuffer, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads); size_t m_impulseResponseLength; - Vector > m_convolvers; + nsTArray > m_convolvers; // For "True" stereo processing - RefPtr m_tempBuffer; + mozilla::AudioChunk m_tempBuffer; }; } // namespace WebCore diff --git a/content/media/webaudio/blink/ReverbAccumulationBuffer.cpp b/content/media/webaudio/blink/ReverbAccumulationBuffer.cpp index f1d79caa7ce7..6b92e9983e9a 100644 --- a/content/media/webaudio/blink/ReverbAccumulationBuffer.cpp +++ b/content/media/webaudio/blink/ReverbAccumulationBuffer.cpp @@ -26,31 +26,29 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" +#include "ReverbAccumulationBuffer.h" +#include "AudioNodeEngine.h" +#include "mozilla/PodOperations.h" +#include -#if ENABLE(WEB_AUDIO) - -#include "core/platform/audio/ReverbAccumulationBuffer.h" - -#include "core/platform/audio/VectorMath.h" +using namespace mozilla; namespace WebCore { -using namespace VectorMath; - ReverbAccumulationBuffer::ReverbAccumulationBuffer(size_t length) - : m_buffer(length) - , m_readIndex(0) + : m_readIndex(0) , m_readTimeFrame(0) { + m_buffer.SetLength(length); + PodZero(m_buffer.Elements(), length); } void ReverbAccumulationBuffer::readAndClear(float* destination, size_t numberOfFrames) { - size_t bufferLength = m_buffer.size(); + size_t bufferLength = m_buffer.Length(); bool isCopySafe = m_readIndex <= bufferLength && numberOfFrames <= bufferLength; - - ASSERT(isCopySafe); + + MOZ_ASSERT(isCopySafe); if (!isCopySafe) return; @@ -58,7 +56,7 @@ void ReverbAccumulationBuffer::readAndClear(float* destination, size_t numberOfF size_t numberOfFrames1 = std::min(numberOfFrames, framesAvailable); size_t numberOfFrames2 = numberOfFrames - numberOfFrames1; - float* source = m_buffer.data(); + float* source = m_buffer.Elements(); memcpy(destination, source + m_readIndex, sizeof(float) * numberOfFrames1); memset(source + m_readIndex, 0, sizeof(float) * numberOfFrames1); @@ -75,13 +73,13 @@ void ReverbAccumulationBuffer::readAndClear(float* destination, size_t numberOfF void ReverbAccumulationBuffer::updateReadIndex(int* readIndex, size_t numberOfFrames) const { // Update caller's readIndex - *readIndex = (*readIndex + numberOfFrames) % m_buffer.size(); + *readIndex = (*readIndex + numberOfFrames) % m_buffer.Length(); } int ReverbAccumulationBuffer::accumulate(float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames) { - size_t bufferLength = m_buffer.size(); - + size_t bufferLength = m_buffer.Length(); + size_t writeIndex = (*readIndex + delayFrames) % bufferLength; // Update caller's readIndex @@ -91,29 +89,28 @@ int ReverbAccumulationBuffer::accumulate(float* source, size_t numberOfFrames, i size_t numberOfFrames1 = std::min(numberOfFrames, framesAvailable); size_t numberOfFrames2 = numberOfFrames - numberOfFrames1; - float* destination = m_buffer.data(); + float* destination = m_buffer.Elements(); bool isSafe = writeIndex <= bufferLength && numberOfFrames1 + writeIndex <= bufferLength && numberOfFrames2 <= bufferLength; - ASSERT(isSafe); + MOZ_ASSERT(isSafe); if (!isSafe) return 0; - vadd(source, 1, destination + writeIndex, 1, destination + writeIndex, 1, numberOfFrames1); + AudioBufferAddWithScale(source, 1.0f, destination + writeIndex, numberOfFrames1); // Handle wrap-around if necessary - if (numberOfFrames2 > 0) - vadd(source + numberOfFrames1, 1, destination, 1, destination, 1, numberOfFrames2); + if (numberOfFrames2 > 0) { + AudioBufferAddWithScale(source + numberOfFrames1, 1.0f, destination, numberOfFrames2); + } return writeIndex; } void ReverbAccumulationBuffer::reset() { - m_buffer.zero(); + PodZero(m_buffer.Elements(), m_buffer.Length()); m_readIndex = 0; m_readTimeFrame = 0; } } // namespace WebCore - -#endif // ENABLE(WEB_AUDIO) diff --git a/content/media/webaudio/blink/ReverbAccumulationBuffer.h b/content/media/webaudio/blink/ReverbAccumulationBuffer.h index fe3f3a58bf9a..4fe336737a53 100644 --- a/content/media/webaudio/blink/ReverbAccumulationBuffer.h +++ b/content/media/webaudio/blink/ReverbAccumulationBuffer.h @@ -29,10 +29,12 @@ #ifndef ReverbAccumulationBuffer_h #define ReverbAccumulationBuffer_h -#include "core/platform/audio/AudioArray.h" +#include "nsTArray.h" namespace WebCore { +typedef nsTArray AudioFloatArray; + // 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. diff --git a/content/media/webaudio/blink/ReverbConvolver.cpp b/content/media/webaudio/blink/ReverbConvolver.cpp index dc935b85465b..4397365be87f 100644 --- a/content/media/webaudio/blink/ReverbConvolver.cpp +++ b/content/media/webaudio/blink/ReverbConvolver.cpp @@ -26,19 +26,19 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" +#include "ReverbConvolver.h" -#if ENABLE(WEB_AUDIO) +using namespace mozilla; -#include "core/platform/audio/ReverbConvolver.h" - -#include "core/platform/audio/AudioBus.h" -#include "core/platform/audio/VectorMath.h" +template<> +struct RunnableMethodTraits +{ + static void RetainCallee(WebCore::ReverbConvolver* obj) {} + static void ReleaseCallee(WebCore::ReverbConvolver* obj) {} +}; 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. @@ -53,20 +53,15 @@ 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(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) +ReverbConvolver::ReverbConvolver(const float* impulseResponseData, size_t impulseResponseLength, size_t renderSliceSize, size_t maxFFTSize, size_t convolverRenderPhase, bool useBackgroundThreads) + : m_impulseResponseLength(impulseResponseLength) + , m_accumulationBuffer(impulseResponseLength + 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_backgroundThread("ConvolverWorker") + , m_backgroundThreadMonitor("ConvolverMonitor") , m_useBackgroundThreads(useBackgroundThreads) - , m_backgroundThread(0) , m_wantsToExit(false) , m_moreInputBuffered(false) { @@ -80,8 +75,8 @@ ReverbConvolver::ReverbConvolver(AudioChannel* impulseResponse, size_t renderSli // Otherwise, assume we're being run from a command-line tool. bool hasRealtimeConstraint = useBackgroundThreads; - const float* response = impulseResponse->data(); - size_t totalResponseLength = impulseResponse->length(); + const float* response = impulseResponseData; + size_t totalResponseLength = impulseResponseLength; // The total latency is zero because the direct-convolution is used in the leading portion. size_t reverbTotalLatency = 0; @@ -102,15 +97,15 @@ ReverbConvolver::ReverbConvolver(AudioChannel* impulseResponse, size_t renderSli bool useDirectConvolver = !stageOffset; - OwnPtr stage = adoptPtr(new ReverbConvolverStage(response, totalResponseLength, reverbTotalLatency, stageOffset, stageSize, fftSize, renderPhase, renderSliceSize, &m_accumulationBuffer, useDirectConvolver)); + nsAutoPtr stage(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()); + m_backgroundStages.AppendElement(stage.forget()); isBackgroundStage = true; } else - m_stages.append(stage.release()); + m_stages.AppendElement(stage.forget()); stageOffset += stageSize; ++i; @@ -128,24 +123,27 @@ ReverbConvolver::ReverbConvolver(AudioChannel* impulseResponse, size_t renderSli // 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"); + if (this->useBackgroundThreads() && m_backgroundStages.Length() > 0) { + m_backgroundThread.Start(); + CancelableTask* task = NewRunnableMethod(this, &ReverbConvolver::backgroundThreadEntry); + m_backgroundThread.message_loop()->PostTask(FROM_HERE, task); + } } ReverbConvolver::~ReverbConvolver() { // Wait for background thread to stop - if (useBackgroundThreads() && m_backgroundThread) { + if (useBackgroundThreads() && m_backgroundThread.IsRunning()) { m_wantsToExit = true; // Wake up thread so it can return { - MutexLocker locker(m_backgroundThreadLock); + MonitorAutoLock locker(m_backgroundThreadMonitor); m_moreInputBuffered = true; - m_backgroundThreadCondition.signal(); + locker.Notify(); } - waitForThreadCompletion(m_backgroundThread); + m_backgroundThread.Stop(); } } @@ -153,11 +151,11 @@ void ReverbConvolver::backgroundThreadEntry() { while (!m_wantsToExit) { // Wait for realtime thread to give us more input - m_moreInputBuffered = false; + m_moreInputBuffered = false; { - MutexLocker locker(m_backgroundThreadLock); + MonitorAutoLock locker(m_backgroundThreadMonitor); while (!m_moreInputBuffered && !m_wantsToExit) - m_backgroundThreadCondition.wait(m_backgroundThreadLock); + locker.Wait(); } // Process all of the stages until their read indices reach the input buffer's write index @@ -172,23 +170,25 @@ void ReverbConvolver::backgroundThreadEntry() const int SliceSize = MinFFTSize / 2; // Accumulate contributions from each stage - for (size_t i = 0; i < m_backgroundStages.size(); ++i) + for (size_t i = 0; i < m_backgroundStages.Length(); ++i) m_backgroundStages[i]->processInBackground(this, SliceSize); } } } -void ReverbConvolver::process(const AudioChannel* sourceChannel, AudioChannel* destinationChannel, size_t framesToProcess) +void ReverbConvolver::process(const float* sourceChannelData, size_t sourceChannelLength, + float* destinationChannelData, size_t destinationChannelLength, + size_t framesToProcess) { - bool isSafe = sourceChannel && destinationChannel && sourceChannel->length() >= framesToProcess && destinationChannel->length() >= framesToProcess; - ASSERT(isSafe); + bool isSafe = sourceChannelData && destinationChannelData && sourceChannelLength >= framesToProcess && destinationChannelLength >= framesToProcess; + MOZ_ASSERT(isSafe); if (!isSafe) return; - - const float* source = sourceChannel->data(); - float* destination = destinationChannel->mutableData(); + + const float* source = sourceChannelData; + float* destination = destinationChannelData; bool isDataSafe = source && destination; - ASSERT(isDataSafe); + MOZ_ASSERT(isDataSafe); if (!isDataSafe) return; @@ -196,32 +196,30 @@ void ReverbConvolver::process(const AudioChannel* sourceChannel, AudioChannel* d m_inputBuffer.write(source, framesToProcess); // Accumulate contributions from each stage - for (size_t i = 0; i < m_stages.size(); ++i) + for (size_t i = 0; i < m_stages.Length(); ++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(); - } + MonitorAutoLock locker(m_backgroundThreadMonitor); + m_moreInputBuffered = true; + locker.Notify(); } void ReverbConvolver::reset() { - for (size_t i = 0; i < m_stages.size(); ++i) + for (size_t i = 0; i < m_stages.Length(); ++i) m_stages[i]->reset(); - for (size_t i = 0; i < m_backgroundStages.size(); ++i) + for (size_t i = 0; i < m_backgroundStages.Length(); ++i) m_backgroundStages[i]->reset(); m_accumulationBuffer.reset(); @@ -234,5 +232,3 @@ size_t ReverbConvolver::latencyFrames() const } } // namespace WebCore - -#endif // ENABLE(WEB_AUDIO) diff --git a/content/media/webaudio/blink/ReverbConvolver.h b/content/media/webaudio/blink/ReverbConvolver.h index 76b306f475f7..e937afb3e1c2 100644 --- a/content/media/webaudio/blink/ReverbConvolver.h +++ b/content/media/webaudio/blink/ReverbConvolver.h @@ -29,16 +29,19 @@ #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 -#include -#include -#include +#include "DirectConvolver.h" +#include "FFTConvolver.h" +#include "ReverbAccumulationBuffer.h" +#include "ReverbConvolverStage.h" +#include "ReverbInputBuffer.h" +#include "nsAutoPtr.h" +#include "nsTArray.h" +#include "nsCOMPtr.h" +#include "mozilla/Monitor.h" +#ifdef LOG +#undef LOG +#endif +#include "base/thread.h" namespace WebCore { @@ -50,10 +53,12 @@ public: // 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(const float* impulseResponseData, size_t impulseResponseLength, size_t renderSliceSize, size_t maxFFTSize, size_t convolverRenderPhase, bool useBackgroundThreads); ~ReverbConvolver(); - void process(const AudioChannel* sourceChannel, AudioChannel* destinationChannel, size_t framesToProcess); + void process(const float* sourceChannelData, size_t sourceChannelLength, + float* destinationChannelData, size_t destinationChannelLength, + size_t framesToProcess); void reset(); size_t impulseResponseLength() const { return m_impulseResponseLength; } @@ -65,8 +70,8 @@ public: size_t latencyFrames() const; private: - Vector > m_stages; - Vector > m_backgroundStages; + nsTArray > m_stages; + nsTArray > m_backgroundStages; size_t m_impulseResponseLength; ReverbAccumulationBuffer m_accumulationBuffer; @@ -82,12 +87,11 @@ private: size_t m_maxRealtimeFFTSize; // Background thread and synchronization + base::Thread m_backgroundThread; + mozilla::Monitor m_backgroundThreadMonitor; bool m_useBackgroundThreads; - ThreadIdentifier m_backgroundThread; bool m_wantsToExit; bool m_moreInputBuffered; - mutable Mutex m_backgroundThreadLock; - mutable ThreadCondition m_backgroundThreadCondition; }; } // namespace WebCore diff --git a/content/media/webaudio/blink/ReverbConvolverStage.cpp b/content/media/webaudio/blink/ReverbConvolverStage.cpp index 2e65d6f8b466..25660b83302e 100644 --- a/content/media/webaudio/blink/ReverbConvolverStage.cpp +++ b/content/media/webaudio/blink/ReverbConvolverStage.cpp @@ -26,23 +26,17 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" +#include "ReverbConvolverStage.h" -#if ENABLE(WEB_AUDIO) +#include "ReverbAccumulationBuffer.h" +#include "ReverbConvolver.h" +#include "ReverbInputBuffer.h" +#include "mozilla/PodOperations.h" -#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 -#include +using namespace mozilla; 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) @@ -50,19 +44,20 @@ ReverbConvolverStage::ReverbConvolverStage(const float* impulseResponse, size_t, , m_inputReadIndex(0) , m_directMode(directMode) { - ASSERT(impulseResponse); - ASSERT(accumulationBuffer); + MOZ_ASSERT(impulseResponse); + MOZ_ASSERT(accumulationBuffer); if (!m_directMode) { - m_fftKernel = adoptPtr(new FFTFrame(fftSize)); - m_fftKernel->doPaddedFFT(impulseResponse + stageOffset, stageLength); - m_fftConvolver = adoptPtr(new FFTConvolver(fftSize)); + m_fftKernel = new FFTBlock(fftSize); + m_fftKernel->PerformPaddedFFT(impulseResponse + stageOffset, stageLength); + m_fftConvolver = 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_directKernel.SetLength(fftSize / 2); + PodCopy(m_directKernel.Elements(), impulseResponse + stageOffset, fftSize / 2); + m_directConvolver = new DirectConvolver(renderSliceSize); } - m_temporaryBuffer.allocate(renderSliceSize); + m_temporaryBuffer.SetLength(renderSliceSize); + PodZero(m_temporaryBuffer.Elements(), m_temporaryBuffer.Length()); // The convolution stage at offset stageOffset needs to have a corresponding delay to cancel out the offset. size_t totalDelay = stageOffset + reverbTotalLatency; @@ -70,7 +65,7 @@ ReverbConvolverStage::ReverbConvolverStage(const float* impulseResponse, size_t, // But, the FFT convolution itself incurs fftSize / 2 latency, so subtract this out... size_t halfSize = fftSize / 2; if (!m_directMode) { - ASSERT(totalDelay >= halfSize); + MOZ_ASSERT(totalDelay >= halfSize); if (totalDelay >= halfSize) totalDelay -= halfSize; } @@ -88,7 +83,8 @@ ReverbConvolverStage::ReverbConvolverStage(const float* impulseResponse, size_t, size_t delayBufferSize = m_preDelayLength < fftSize ? fftSize : m_preDelayLength; delayBufferSize = delayBufferSize < renderSliceSize ? renderSliceSize : delayBufferSize; - m_preDelayBuffer.allocate(delayBufferSize); + m_preDelayBuffer.SetLength(delayBufferSize); + PodZero(m_preDelayBuffer.Elements(), m_preDelayBuffer.Length()); } void ReverbConvolverStage::processInBackground(ReverbConvolver* convolver, size_t framesToProcess) @@ -100,7 +96,7 @@ void ReverbConvolverStage::processInBackground(ReverbConvolver* convolver, size_ void ReverbConvolverStage::process(const float* source, size_t framesToProcess) { - ASSERT(source); + MOZ_ASSERT(source); if (!source) return; @@ -112,26 +108,26 @@ void ReverbConvolverStage::process(const float* source, size_t framesToProcess) 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); + bool isPreDelaySafe = m_preReadWriteIndex + framesToProcess <= m_preDelayBuffer.Length(); + MOZ_ASSERT(isPreDelaySafe); if (!isPreDelaySafe) return; - isTemporaryBufferSafe = framesToProcess <= m_temporaryBuffer.size(); + isTemporaryBufferSafe = framesToProcess <= m_temporaryBuffer.Length(); - preDelayedDestination = m_preDelayBuffer.data() + m_preReadWriteIndex; + preDelayedDestination = m_preDelayBuffer.Elements() + m_preReadWriteIndex; preDelayedSource = preDelayedDestination; - temporaryBuffer = m_temporaryBuffer.data(); + temporaryBuffer = m_temporaryBuffer.Elements(); } else { // Zero delay preDelayedDestination = 0; preDelayedSource = source; - temporaryBuffer = m_preDelayBuffer.data(); + temporaryBuffer = m_preDelayBuffer.Elements(); - isTemporaryBufferSafe = framesToProcess <= m_preDelayBuffer.size(); + isTemporaryBufferSafe = framesToProcess <= m_preDelayBuffer.Length(); } - ASSERT(isTemporaryBufferSafe); + MOZ_ASSERT(isTemporaryBufferSafe); if (!isTemporaryBufferSafe) return; @@ -144,9 +140,9 @@ void ReverbConvolverStage::process(const float* source, size_t framesToProcess) // 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); + m_fftConvolver->process(m_fftKernel, preDelayedSource, temporaryBuffer, framesToProcess); else - m_directConvolver->process(m_directKernel.get(), preDelayedSource, temporaryBuffer, framesToProcess); + m_directConvolver->process(&m_directKernel, preDelayedSource, temporaryBuffer, framesToProcess); // Now accumulate into reverb's accumulation buffer. m_accumulationBuffer->accumulate(temporaryBuffer, framesToProcess, &m_accumulationReadIndex, m_postDelayLength); @@ -157,7 +153,7 @@ void ReverbConvolverStage::process(const float* source, size_t framesToProcess) memcpy(preDelayedDestination, source, sizeof(float) * framesToProcess); m_preReadWriteIndex += framesToProcess; - ASSERT(m_preReadWriteIndex <= m_preDelayLength); + MOZ_ASSERT(m_preReadWriteIndex <= m_preDelayLength); if (m_preReadWriteIndex >= m_preDelayLength) m_preReadWriteIndex = 0; } @@ -171,12 +167,10 @@ void ReverbConvolverStage::reset() m_fftConvolver->reset(); else m_directConvolver->reset(); - m_preDelayBuffer.zero(); + PodZero(m_preDelayBuffer.Elements(), m_preDelayBuffer.Length()); m_accumulationReadIndex = 0; m_inputReadIndex = 0; m_framesProcessed = 0; } } // namespace WebCore - -#endif // ENABLE(WEB_AUDIO) diff --git a/content/media/webaudio/blink/ReverbConvolverStage.h b/content/media/webaudio/blink/ReverbConvolverStage.h index f61ac1f3bece..51f9d2d84667 100644 --- a/content/media/webaudio/blink/ReverbConvolverStage.h +++ b/content/media/webaudio/blink/ReverbConvolverStage.h @@ -29,17 +29,18 @@ #ifndef ReverbConvolverStage_h #define ReverbConvolverStage_h -#include "core/platform/audio/AudioArray.h" -#include "core/platform/audio/FFTFrame.h" -#include +#include "nsTArray.h" +#include "mozilla/FFTBlock.h" namespace WebCore { +using mozilla::FFTBlock; + 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 { @@ -59,10 +60,10 @@ public: int inputReadIndex() const { return m_inputReadIndex; } private: - OwnPtr m_fftKernel; - OwnPtr m_fftConvolver; + nsAutoPtr m_fftKernel; + nsAutoPtr m_fftConvolver; - AudioFloatArray m_preDelayBuffer; + nsTArray m_preDelayBuffer; ReverbAccumulationBuffer* m_accumulationBuffer; int m_accumulationReadIndex; @@ -73,11 +74,11 @@ private: size_t m_preReadWriteIndex; size_t m_framesProcessed; - AudioFloatArray m_temporaryBuffer; + nsTArray m_temporaryBuffer; bool m_directMode; - OwnPtr m_directKernel; - OwnPtr m_directConvolver; + nsTArray m_directKernel; + nsAutoPtr m_directConvolver; }; } // namespace WebCore diff --git a/content/media/webaudio/blink/ReverbInputBuffer.cpp b/content/media/webaudio/blink/ReverbInputBuffer.cpp index 1e14de4325fd..8221f8151d68 100644 --- a/content/media/webaudio/blink/ReverbInputBuffer.cpp +++ b/content/media/webaudio/blink/ReverbInputBuffer.cpp @@ -26,32 +26,32 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" +#include "ReverbInputBuffer.h" +#include "mozilla/PodOperations.h" -#if ENABLE(WEB_AUDIO) - -#include "core/platform/audio/ReverbInputBuffer.h" +using namespace mozilla; namespace WebCore { ReverbInputBuffer::ReverbInputBuffer(size_t length) - : m_buffer(length) - , m_writeIndex(0) + : m_writeIndex(0) { + m_buffer.SetLength(length); + PodZero(m_buffer.Elements(), length); } void ReverbInputBuffer::write(const float* sourceP, size_t numberOfFrames) { - size_t bufferLength = m_buffer.size(); + size_t bufferLength = m_buffer.Length(); bool isCopySafe = m_writeIndex + numberOfFrames <= bufferLength; - ASSERT(isCopySafe); + MOZ_ASSERT(isCopySafe); if (!isCopySafe) return; - - memcpy(m_buffer.data() + m_writeIndex, sourceP, sizeof(float) * numberOfFrames); + + memcpy(m_buffer.Elements() + m_writeIndex, sourceP, sizeof(float) * numberOfFrames); m_writeIndex += numberOfFrames; - ASSERT(m_writeIndex <= bufferLength); + MOZ_ASSERT(m_writeIndex <= bufferLength); if (m_writeIndex >= bufferLength) m_writeIndex = 0; @@ -59,17 +59,17 @@ void ReverbInputBuffer::write(const float* sourceP, size_t numberOfFrames) float* ReverbInputBuffer::directReadFrom(int* readIndex, size_t numberOfFrames) { - size_t bufferLength = m_buffer.size(); + size_t bufferLength = m_buffer.Length(); bool isPointerGood = readIndex && *readIndex >= 0 && *readIndex + numberOfFrames <= bufferLength; - ASSERT(isPointerGood); + MOZ_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(); + return m_buffer.Elements(); } - - float* sourceP = m_buffer.data(); + + float* sourceP = m_buffer.Elements(); float* p = sourceP + *readIndex; // Update readIndex @@ -80,10 +80,8 @@ float* ReverbInputBuffer::directReadFrom(int* readIndex, size_t numberOfFrames) void ReverbInputBuffer::reset() { - m_buffer.zero(); + PodZero(m_buffer.Elements(), m_buffer.Length()); m_writeIndex = 0; } } // namespace WebCore - -#endif // ENABLE(WEB_AUDIO) diff --git a/content/media/webaudio/blink/ReverbInputBuffer.h b/content/media/webaudio/blink/ReverbInputBuffer.h index ea1233ffbc57..22f2bf941b7b 100644 --- a/content/media/webaudio/blink/ReverbInputBuffer.h +++ b/content/media/webaudio/blink/ReverbInputBuffer.h @@ -29,7 +29,7 @@ #ifndef ReverbInputBuffer_h #define ReverbInputBuffer_h -#include "core/platform/audio/AudioArray.h" +#include "nsTArray.h" namespace WebCore { @@ -55,7 +55,7 @@ public: void reset(); private: - AudioFloatArray m_buffer; + nsTArray m_buffer; size_t m_writeIndex; }; diff --git a/content/media/webaudio/blink/moz.build b/content/media/webaudio/blink/moz.build index 569be179c350..39b2de127193 100644 --- a/content/media/webaudio/blink/moz.build +++ b/content/media/webaudio/blink/moz.build @@ -8,8 +8,15 @@ MODULE = 'content' CPP_SOURCES += [ 'Biquad.cpp', + 'DirectConvolver.cpp', 'DynamicsCompressor.cpp', 'DynamicsCompressorKernel.cpp', + 'FFTConvolver.cpp', + 'Reverb.cpp', + 'ReverbAccumulationBuffer.cpp', + 'ReverbConvolver.cpp', + 'ReverbConvolverStage.cpp', + 'ReverbInputBuffer.cpp', 'ZeroPole.cpp', ] diff --git a/content/media/webaudio/moz.build b/content/media/webaudio/moz.build index 299773bd9aa6..fe66e25cd01d 100644 --- a/content/media/webaudio/moz.build +++ b/content/media/webaudio/moz.build @@ -17,6 +17,10 @@ EXPORTS += [ 'WebAudioUtils.h', ] +EXPORTS.mozilla += [ + 'FFTBlock.h', +] + EXPORTS.mozilla.dom += [ 'AnalyserNode.h', 'AudioBuffer.h',