Skip to content

Instantly share code, notes, and snippets.

@ehsan
Created June 7, 2013 15:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ehsan/5730140 to your computer and use it in GitHub Desktop.
Save ehsan/5730140 to your computer and use it in GitHub Desktop.
diff --git a/content/media/AudioNodeEngine.cpp b/content/media/AudioNodeEngine.cpp
index 6d4f3f3..c7c0901 100644
--- a/content/media/AudioNodeEngine.cpp
+++ b/content/media/AudioNodeEngine.cpp
@@ -39,23 +39,31 @@ WriteZeroesToAudioBlock(AudioChunk* aChunk, uint32_t aStart, uint32_t aLength)
}
}
-void
-AudioBlockAddChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE],
- float aScale,
- float aOutput[WEBAUDIO_BLOCK_SIZE])
+void AudioBufferAddWithScale(const float* aInput,
+ float aScale,
+ float* aOutput,
+ uint32_t aSize)
{
if (aScale == 1.0f) {
- for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
+ for (uint32_t i = 0; i < aSize; ++i) {
aOutput[i] += aInput[i];
}
} else {
- for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
+ 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])
+{
+ AudioBufferAddWithScale(aInput, aScale, aOutput, WEBAUDIO_BLOCK_SIZE);
+}
+
+void
AudioBlockCopyChannelWithScale(const float* aInput,
float aScale,
float* aOutput)
@@ -70,6 +78,24 @@ AudioBlockCopyChannelWithScale(const float* aInput,
}
void
+BufferComplexMultiply(const float* aInput,
+ const float* aScale,
+ float* aOutput,
+ uint32_t aSize)
+{
+ for (uint32_t i = 0; i < aSize; i += 2) {
+ float real1 = aInput[i];
+ float imag1 = aInput[i + 1];
+ float real2 = aScale[i];
+ float imag2 = aScale[i + 1];
+ float realResult = real1 * real2 - imag1 * imag2;
+ float imagResult = real1 * imag2 + imag1 * real2;
+ aOutput[i] = realResult;
+ aOutput[i + 1] = imagResult;
+ }
+}
+
+void
AudioBlockCopyChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE],
const float aScale[WEBAUDIO_BLOCK_SIZE],
float aOutput[WEBAUDIO_BLOCK_SIZE])
@@ -84,10 +110,19 @@ AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE],
uint32_t aChannelCount,
float aScale)
{
+ AudioBlockInPlaceScale(aBlock, aChannelCount, aScale, WEBAUDIO_BLOCK_SIZE);
+}
+
+void
+AudioBlockInPlaceScale(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;
}
}
@@ -123,4 +158,16 @@ AudioBlockPanStereoToStereo(const float aInputL[WEBAUDIO_BLOCK_SIZE],
}
}
}
+
+float
+AudioBlockSumOfSquares(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 f9cd8e8..54cb2e5 100644
--- a/content/media/AudioNodeEngine.h
+++ b/content/media/AudioNodeEngine.h
@@ -90,6 +90,14 @@ void WriteZeroesToAudioBlock(AudioChunk* aChunk, uint32_t aStart, uint32_t aLeng
/**
* 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.
+ */
void AudioBlockAddChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE],
float aScale,
float aOutput[WEBAUDIO_BLOCK_SIZE]);
@@ -111,6 +119,14 @@ void AudioBlockCopyChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE],
float aOutput[WEBAUDIO_BLOCK_SIZE]);
/**
+ * Vectory copy-scaled operation on arbitrary sized buffers.
+ */
+void BufferComplexMultiply(const float* aInput,
+ const float* aScale,
+ float* aOutput,
+ uint32_t aSize);
+
+/**
* In place gain. aScale == 1.0f should be optimized.
*/
void AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE],
@@ -118,6 +134,14 @@ void AudioBlockInPlaceScale(float aBlock[WEBAUDIO_BLOCK_SIZE],
float aScale);
/**
+ * In place gain. aScale == 1.0f should be optimized.
+ */
+void AudioBlockInPlaceScale(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
* different gain value.
* This algorithm is specified in the WebAudio spec.
@@ -140,6 +164,12 @@ AudioBlockPanStereoToStereo(const float aInputL[WEBAUDIO_BLOCK_SIZE],
float aOutputR[WEBAUDIO_BLOCK_SIZE]);
/**
+ * Return the sum of squares of all of the samples in the input.
+ */
+float
+AudioBlockSumOfSquares(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 afc881c..f2292e6 100644
--- a/content/media/webaudio/AnalyserNode.cpp
+++ b/content/media/webaudio/AnalyserNode.cpp
@@ -10,7 +10,6 @@
#include "AudioNodeStream.h"
#include "mozilla/Mutex.h"
#include "mozilla/PodOperations.h"
-#include "kiss_fft/kiss_fftr.h"
namespace mozilla {
namespace dom {
@@ -80,7 +79,7 @@ AnalyserNode::AnalyserNode(AudioContext* aContext)
1,
ChannelCountMode::Explicit,
ChannelInterpretation::Speakers)
- , mFFTSize(2048)
+ , mAnalysisFrame(2048)
, mMinDecibels(-100.)
, mMaxDecibels(-30.)
, mSmoothingTimeConstant(.8)
@@ -107,8 +106,8 @@ AnalyserNode::SetFftSize(uint32_t aValue, ErrorResult& aRv)
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
- if (mFFTSize != aValue) {
- mFFTSize = aValue;
+ if (FftSize() != aValue) {
+ mAnalysisFrame.SetFFTSize(aValue);
AllocateBuffer();
}
}
@@ -204,28 +203,25 @@ AnalyserNode::FFTAnalysis()
if (mWriteIndex == 0) {
inputBuffer = mBuffer.Elements();
} else {
- inputBuffer = static_cast<float*>(moz_malloc(mFFTSize * sizeof(float)));
+ inputBuffer = static_cast<float*>(moz_malloc(FftSize() * sizeof(float)));
if (!inputBuffer) {
return false;
}
- memcpy(inputBuffer, mBuffer.Elements() + mWriteIndex, sizeof(float) * (mFFTSize - mWriteIndex));
- memcpy(inputBuffer + mFFTSize - mWriteIndex, mBuffer.Elements(), sizeof(float) * mWriteIndex);
+ memcpy(inputBuffer, mBuffer.Elements() + mWriteIndex, sizeof(float) * (FftSize() - mWriteIndex));
+ memcpy(inputBuffer + FftSize() - mWriteIndex, mBuffer.Elements(), sizeof(float) * mWriteIndex);
allocated = true;
}
- nsAutoArrayPtr<kiss_fft_cpx> outputBuffer(new kiss_fft_cpx[FrequencyBinCount() + 1]);
- ApplyBlackmanWindow(inputBuffer, mFFTSize);
+ ApplyBlackmanWindow(inputBuffer, FftSize());
- kiss_fftr_cfg fft = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr);
- kiss_fftr(fft, inputBuffer, outputBuffer);
- free(fft);
+ mAnalysisFrame.PerformFFT(inputBuffer);
// Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FFT scaling factor).
- const double magnitudeScale = 1.0 / mFFTSize;
+ const double magnitudeScale = 1.0 / FftSize();
for (uint32_t i = 0; i < mOutputBuffer.Length(); ++i) {
- double scalarMagnitude = sqrt(outputBuffer[i].r * outputBuffer[i].r +
- outputBuffer[i].i * outputBuffer[i].i) *
+ double scalarMagnitude = sqrt(mAnalysisFrame.RealData(i) * mAnalysisFrame.RealData(i) +
+ mAnalysisFrame.ImagData(i) * mAnalysisFrame.ImagData(i)) *
magnitudeScale;
mOutputBuffer[i] = mSmoothingTimeConstant * mOutputBuffer[i] +
(1.0 - mSmoothingTimeConstant) * scalarMagnitude;
@@ -256,10 +252,10 @@ bool
AnalyserNode::AllocateBuffer()
{
bool result = true;
- if (mBuffer.Length() != mFFTSize) {
- result = mBuffer.SetLength(mFFTSize);
+ if (mBuffer.Length() != FftSize()) {
+ result = mBuffer.SetLength(FftSize());
if (result) {
- memset(mBuffer.Elements(), 0, sizeof(float) * mFFTSize);
+ memset(mBuffer.Elements(), 0, sizeof(float) * FftSize());
mWriteIndex = 0;
result = mOutputBuffer.SetLength(FrequencyBinCount());
diff --git a/content/media/webaudio/AnalyserNode.h b/content/media/webaudio/AnalyserNode.h
index b5be69f..b1880f4a 100644
--- a/content/media/webaudio/AnalyserNode.h
+++ b/content/media/webaudio/AnalyserNode.h
@@ -8,6 +8,7 @@
#define AnalyserNode_h_
#include "AudioNode.h"
+#include "FFTFrame.h"
namespace mozilla {
namespace dom {
@@ -29,7 +30,7 @@ public:
void GetByteTimeDomainData(Uint8Array& aArray);
uint32_t FftSize() const
{
- return mFFTSize;
+ return mAnalysisFrame.FFTSize();
}
void SetFftSize(uint32_t aValue, ErrorResult& aRv);
uint32_t FrequencyBinCount() const
@@ -60,7 +61,7 @@ private:
void ApplyBlackmanWindow(float* aBuffer, uint32_t aSize);
private:
- uint32_t mFFTSize;
+ FFTFrame mAnalysisFrame;
double mMinDecibels;
double mMaxDecibels;
double mSmoothingTimeConstant;
diff --git a/content/media/webaudio/AudioContext.cpp b/content/media/webaudio/AudioContext.cpp
index eacef00..5a49401 100644
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -26,6 +26,8 @@
#include "ChannelSplitterNode.h"
#include "WaveShaperNode.h"
#include "WaveTable.h"
+#include "OscillatorNode.h"
+#include "ConvolverNode.h"
#include "nsNetUtil.h"
namespace mozilla {
@@ -263,6 +265,13 @@ AudioContext::CreatePanner()
return pannerNode.forget();
}
+already_AddRefed<ConvolverNode>
+AudioContext::CreateConvolver()
+{
+ nsRefPtr<ConvolverNode> convolverNode = new ConvolverNode(this);
+ return convolverNode.forget();
+}
+
already_AddRefed<ChannelSplitterNode>
AudioContext::CreateChannelSplitter(uint32_t aNumberOfOutputs, ErrorResult& aRv)
{
@@ -307,6 +316,14 @@ AudioContext::CreateBiquadFilter()
return filterNode.forget();
}
+already_AddRefed<OscillatorNode>
+AudioContext::CreateOscillator()
+{
+ nsRefPtr<OscillatorNode> oscillatorNode =
+ new OscillatorNode(this);
+ return oscillatorNode.forget();
+}
+
already_AddRefed<WaveTable>
AudioContext::CreateWaveTable(const Float32Array& aRealData,
const Float32Array& aImagData,
diff --git a/content/media/webaudio/AudioContext.h b/content/media/webaudio/AudioContext.h
index 7369ddd..c037cce 100644
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -46,11 +46,13 @@ class AudioListener;
class BiquadFilterNode;
class ChannelMergerNode;
class ChannelSplitterNode;
+class ConvolverNode;
class DelayNode;
class DynamicsCompressorNode;
class GainNode;
class GlobalObject;
class OfflineRenderSuccessCallback;
+class OscillatorNode;
class PannerNode;
class ScriptProcessorNode;
class WaveShaperNode;
@@ -167,6 +169,9 @@ public:
already_AddRefed<PannerNode>
CreatePanner();
+ already_AddRefed<ConvolverNode>
+ CreateConvolver();
+
already_AddRefed<ChannelSplitterNode>
CreateChannelSplitter(uint32_t aNumberOfOutputs, ErrorResult& aRv);
@@ -179,6 +184,9 @@ public:
already_AddRefed<BiquadFilterNode>
CreateBiquadFilter();
+ already_AddRefed<OscillatorNode>
+ CreateOscillator();
+
already_AddRefed<WaveTable>
CreateWaveTable(const Float32Array& aRealData, const Float32Array& aImagData,
ErrorResult& aRv);
diff --git a/content/media/webaudio/ConvolverNode.cpp b/content/media/webaudio/ConvolverNode.cpp
new file mode 100644
index 0000000..614aa2a
--- /dev/null
+++ b/content/media/webaudio/ConvolverNode.cpp
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ConvolverNode.h"
+#include "mozilla/dom/ConvolverNodeBinding.h"
+#include "AudioNodeEngine.h"
+#include "AudioNodeStream.h"
+#include "blink/Reverb.h"
+
+#include <cmath>
+#include "nsMathUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(ConvolverNode, AudioNode, mBuffer)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ConvolverNode)
+NS_INTERFACE_MAP_END_INHERITING(AudioNode)
+
+NS_IMPL_ADDREF_INHERITED(ConvolverNode, AudioNode)
+NS_IMPL_RELEASE_INHERITED(ConvolverNode, AudioNode)
+
+class ConvolverNodeEngine : public AudioNodeEngine
+{
+public:
+ ConvolverNodeEngine(AudioNode* aNode, bool aNormalize)
+ : AudioNodeEngine(aNode)
+ , mBufferLength(0)
+ , mSampleRate(0.0f)
+ , mUseBackgroundThreads(!aNode->Context()->IsOffline())
+ , mNormalize(aNormalize)
+ , mSeenInput(false)
+ {
+ }
+
+ enum Parameters {
+ BUFFER_LENGTH,
+ SAMPLE_RATE,
+ NORMALIZE
+ };
+ virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam) MOZ_OVERRIDE
+ {
+ switch (aIndex) {
+ case BUFFER_LENGTH:
+ // BUFFER_LENGTH is the first parameter that we set when setting a new buffer,
+ // so we should be careful to invalidate the rest of our state here.
+ mBuffer = nullptr;
+ mSampleRate = 0.0f;
+ mBufferLength = aParam;
+ break;
+ case SAMPLE_RATE:
+ mSampleRate = aParam;
+ break;
+ case NORMALIZE:
+ mNormalize = !!aParam;
+ break;
+ default:
+ NS_ERROR("Bad ConvolverNodeEngine Int32Parameter");
+ }
+ }
+ virtual void SetDoubleParameter(uint32_t aIndex, double aParam) MOZ_OVERRIDE
+ {
+ switch (aIndex) {
+ case SAMPLE_RATE:
+ mSampleRate = aParam;
+ AdjustReverb();
+ break;
+ default:
+ NS_ERROR("Bad ConvolverNodeEngine DoubleParameter");
+ }
+ }
+ virtual void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer)
+ {
+ mBuffer = aBuffer;
+ AdjustReverb();
+ }
+
+ void AdjustReverb()
+ {
+ // Note about empirical tuning (this is copied from Blink)
+ // The maximum FFT size affects reverb performance and accuracy.
+ // If the reverb is single-threaded and processes entirely in the real-time audio thread,
+ // it's important not to make this too high. In this case 8192 is a good value.
+ // But, the Reverb object is multi-threaded, so we want this as high as possible without losing too much accuracy.
+ // Very large FFTs will have worse phase errors. Given these constraints 32768 is a good compromise.
+ const size_t MaxFFTSize = 32768;
+
+ if (!mBuffer || !mBufferLength || !mSampleRate) {
+ mReverb = nullptr;
+ mSeenInput = false;
+ return;
+ }
+
+ mReverb = new WebCore::Reverb(mBuffer, mBufferLength,
+ WEBAUDIO_BLOCK_SIZE,
+ MaxFFTSize, 2, mUseBackgroundThreads,
+ mNormalize, mSampleRate);
+ }
+
+ virtual void ProduceAudioBlock(AudioNodeStream* aStream,
+ const AudioChunk& aInput,
+ AudioChunk* aOutput,
+ bool* aFinished)
+ {
+ if (!mSeenInput && aInput.IsNull()) {
+ aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
+ return;
+ }
+ if (!mReverb) {
+ *aOutput = aInput;
+ return;
+ }
+
+ mSeenInput = true;
+ uint32_t numChannels = 2;
+ AudioChunk input = aInput;
+ if (aInput.IsNull()) {
+ AllocateAudioBlock(1, &input);
+ } else {
+ numChannels = aInput.mChannelData.Length();
+ }
+ AllocateAudioBlock(numChannels, aOutput);
+
+ mReverb->process(&input, aOutput, WEBAUDIO_BLOCK_SIZE);
+ }
+
+private:
+ nsRefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
+ nsAutoPtr<WebCore::Reverb> mReverb;
+ int32_t mBufferLength;
+ float mSampleRate;
+ bool mUseBackgroundThreads;
+ bool mNormalize;
+ bool mSeenInput;
+};
+
+ConvolverNode::ConvolverNode(AudioContext* aContext)
+ : AudioNode(aContext,
+ 2,
+ ChannelCountMode::Clamped_max,
+ ChannelInterpretation::Speakers)
+ , mNormalize(true)
+{
+ ConvolverNodeEngine* engine = new ConvolverNodeEngine(this, mNormalize);
+ mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
+}
+
+JSObject*
+ConvolverNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+ return ConvolverNodeBinding::Wrap(aCx, aScope, this);
+}
+
+void
+ConvolverNode::SetBuffer(JSContext* aCx, AudioBuffer* aBuffer, ErrorResult& aRv)
+{
+ switch (aBuffer->NumberOfChannels()) {
+ case 1:
+ case 2:
+ case 4:
+ // Supported number of channels
+ break;
+ default:
+ aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+ return;
+ }
+
+ mBuffer = aBuffer;
+
+ // Send the buffer to the stream
+ AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
+ MOZ_ASSERT(ns, "Why don't we have a stream here?");
+ if (mBuffer) {
+ SendInt32ParameterToStream(ConvolverNodeEngine::BUFFER_LENGTH,
+ mBuffer->Length());
+ SendDoubleParameterToStream(ConvolverNodeEngine::SAMPLE_RATE,
+ mBuffer->SampleRate());
+ nsRefPtr<ThreadSharedFloatArrayBufferList> data =
+ mBuffer->GetThreadSharedChannelsForRate(aCx);
+ ns->SetBuffer(data.forget());
+ } else {
+ ns->SetBuffer(nullptr);
+ }
+}
+
+void
+ConvolverNode::SetNormalize(bool aNormalize)
+{
+ mNormalize = aNormalize;
+ SendInt32ParameterToStream(ConvolverNodeEngine::NORMALIZE, aNormalize);
+}
+
+}
+}
+
diff --git a/content/media/webaudio/ConvolverNode.h b/content/media/webaudio/ConvolverNode.h
new file mode 100644
index 0000000..b79f271
--- /dev/null
+++ b/content/media/webaudio/ConvolverNode.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ConvolverNode_h_
+#define ConvolverNode_h_
+
+#include "AudioNode.h"
+#include "AudioBuffer.h"
+
+namespace mozilla {
+namespace dom {
+
+class ConvolverNode : public AudioNode
+{
+public:
+ explicit ConvolverNode(AudioContext* aContext);
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ConvolverNode, AudioNode);
+
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+ AudioBuffer* GetBuffer(JSContext* aCx) const
+ {
+ return mBuffer;
+ }
+
+ void SetBuffer(JSContext* aCx, AudioBuffer* aBufferi, ErrorResult& aRv);
+
+ bool Normalize() const
+ {
+ return mNormalize;
+ }
+
+ void SetNormalize(bool aNormal);
+
+private:
+ nsRefPtr<AudioBuffer> mBuffer;
+ bool mNormalize;
+};
+
+
+} //end namespace dom
+} //end namespace mozilla
+
+#endif
+
diff --git a/content/media/webaudio/FFTFrame.h b/content/media/webaudio/FFTFrame.h
new file mode 100644
index 0000000..71a7e060
--- /dev/null
+++ b/content/media/webaudio/FFTFrame.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef FFTFrame_h_
+#define FFTFrame_h_
+
+#include "nsTArray.h"
+#include "AudioNodeEngine.h"
+#include "kiss_fft/kiss_fftr.h"
+
+namespace mozilla {
+
+// This class defines an FFT frame, loosely modeled after Blink's FFTFrame
+// class to make sharing code with Blink easy.
+// Currently it's implemented on top of KissFFT on all platforms.
+class FFTFrame {
+public:
+ explicit FFTFrame(uint32_t aFFTSize)
+ : mFFTSize(aFFTSize)
+ {
+ mOutputBuffer.SetLength(aFFTSize / 2 + 1);
+ }
+
+ void PerformFFT(const float* aData)
+ {
+ kiss_fftr_cfg fft = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr);
+ kiss_fftr(fft, aData, mOutputBuffer.Elements());
+ free(fft);
+ }
+ void PerformInverseFFT(float* aData) const
+ {
+ kiss_fftr_cfg fft = kiss_fftr_alloc(mFFTSize, 1, nullptr, nullptr);
+ kiss_fftri(fft, mOutputBuffer.Elements(), aData);
+ free(fft);
+ for (uint32_t i = 0; i < mFFTSize; ++i) {
+ aData[i] /= mFFTSize;
+ }
+ }
+ void Multiply(const FFTFrame& aFrame)
+ {
+ BufferComplexMultiply(reinterpret_cast<const float*>(mOutputBuffer.Elements()),
+ reinterpret_cast<const float*>(aFrame.mOutputBuffer.Elements()),
+ reinterpret_cast<float*>(mOutputBuffer.Elements()),
+ mFFTSize / 2 + 1);
+ }
+
+ void PerformPaddedFFT(const float* aData, size_t dataSize)
+ {
+ nsTArray<float> 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;
+ mOutputBuffer.SetLength(aSize / 2 + 1);
+ }
+
+ uint32_t FFTSize() const
+ {
+ return mFFTSize;
+ }
+ float RealData(uint32_t aIndex) const
+ {
+ return mOutputBuffer[aIndex].r;
+ }
+ float ImagData(uint32_t aIndex) const
+ {
+ return mOutputBuffer[aIndex].i;
+ }
+
+private:
+ nsTArray<kiss_fft_cpx> mOutputBuffer;
+ uint32_t mFFTSize;
+};
+
+}
+
+#endif
+
diff --git a/content/media/webaudio/OscillatorNode.cpp b/content/media/webaudio/OscillatorNode.cpp
new file mode 100644
index 0000000..c12be4b
--- /dev/null
+++ b/content/media/webaudio/OscillatorNode.cpp
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OscillatorNode.h"
+#include "AudioNodeEngine.h"
+#include "AudioNodeStream.h"
+#include "AudioDestinationNode.h"
+#include "WebAudioUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED_3(OscillatorNode, AudioNode,
+ mWaveTable, mFrequency, mDetune)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OscillatorNode)
+NS_INTERFACE_MAP_END_INHERITING(AudioNode)
+
+NS_IMPL_ADDREF_INHERITED(OscillatorNode, AudioNode)
+NS_IMPL_RELEASE_INHERITED(OscillatorNode, AudioNode)
+
+class OscillatorNodeEngine : public AudioNodeEngine
+{
+public:
+ OscillatorNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
+ : AudioNodeEngine(aNode)
+ , mSource(nullptr)
+ , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
+ , mStart(0)
+ , mStop(TRACK_TICKS_MAX)
+ // Keep the default value in sync with the default value in OscillatorNode::OscillatorNode.
+ , mFrequency(440.f)
+ , mDetune(0.f)
+ , mType(OscillatorType::Sine)
+ {
+ }
+
+ void SetSourceStream(AudioNodeStream* aSource)
+ {
+ mSource = aSource;
+ }
+
+ enum Parameters {
+ FREQUENCY,
+ DETUNE,
+ TYPE,
+ WAVETABLE,
+ START,
+ STOP,
+ };
+ void SetTimelineParameter(uint32_t aIndex,
+ const AudioParamTimeline& aValue,
+ TrackRate aSampleRate) MOZ_OVERRIDE
+ {
+ switch (aIndex) {
+ case FREQUENCY:
+ MOZ_ASSERT(mSource && mDestination);
+ mFrequency = aValue;
+ WebAudioUtils::ConvertAudioParamToTicks(mFrequency, mSource, mDestination);
+ break;
+ case DETUNE:
+ MOZ_ASSERT(mSource && mDestination);
+ mDetune = aValue;
+ WebAudioUtils::ConvertAudioParamToTicks(mDetune, mSource, mDestination);
+ break;
+ default:
+ NS_ERROR("Bad OscillatorNodeEngine TimelineParameter");
+ }
+ }
+ virtual void SetStreamTimeParameter(uint32_t aIndex, TrackTicks aParam)
+ {
+ switch (aIndex) {
+ case START: mStart = aParam; break;
+ case STOP: mStop = aParam; break;
+ default:
+ NS_ERROR("Bad OscillatorNodeEngine StreamTimeParameter");
+ }
+ }
+ virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam)
+ {
+ switch (aIndex) {
+ case TYPE: mType = static_cast<OscillatorType>(aParam); break;
+ default:
+ NS_ERROR("Bad OscillatorNodeEngine Int32Parameter");
+ }
+ }
+
+ virtual void ProduceAudioBlock(AudioNodeStream* aStream,
+ const AudioChunk& aInput,
+ AudioChunk* aOutput,
+ bool* aFinished) MOZ_OVERRIDE
+ {
+ MOZ_ASSERT(mSource == aStream, "Invalid source stream");
+
+ // TODO: Synthesize a waveform here.
+ *aOutput = aInput;
+ }
+
+ AudioNodeStream* mSource;
+ AudioNodeStream* mDestination;
+ TrackTicks mStart;
+ TrackTicks mStop;
+ AudioParamTimeline mFrequency;
+ AudioParamTimeline mDetune;
+ OscillatorType mType;
+};
+
+OscillatorNode::OscillatorNode(AudioContext* aContext)
+ : AudioNode(aContext,
+ 2,
+ ChannelCountMode::Max,
+ ChannelInterpretation::Speakers)
+ , mType(OscillatorType::Sine)
+ , mFrequency(new AudioParam(this, SendFrequencyToStream, 440.0f))
+ , mDetune(new AudioParam(this, SendDetuneToStream, 0.0f))
+ , mStartCalled(false)
+ , mStopped(false)
+{
+ OscillatorNodeEngine* engine = new OscillatorNodeEngine(this, aContext->Destination());
+ mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
+ engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
+}
+
+JSObject*
+OscillatorNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+ return OscillatorNodeBinding::Wrap(aCx, aScope, this);
+}
+
+void
+OscillatorNode::SendFrequencyToStream(AudioNode* aNode)
+{
+ OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
+ SendTimelineParameterToStream(This, OscillatorNodeEngine::FREQUENCY, *This->mFrequency);
+}
+
+void
+OscillatorNode::SendDetuneToStream(AudioNode* aNode)
+{
+ OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
+ SendTimelineParameterToStream(This, OscillatorNodeEngine::DETUNE, *This->mDetune);
+}
+
+void
+OscillatorNode::SendTypeToStream()
+{
+ SendInt32ParameterToStream(OscillatorNodeEngine::TYPE, static_cast<int32_t>(mType));
+ if (mType == OscillatorType::Custom) {
+ // TODO: Send the custom wave table somehow
+ }
+}
+
+void
+OscillatorNode::Start(double aWhen, ErrorResult& aRv)
+{
+ if (!WebAudioUtils::IsTimeValid(aWhen)) {
+ aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return;
+ }
+
+ if (mStartCalled) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+ mStartCalled = true;
+
+ AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
+ if (!ns) {
+ // Nothing to play, or we're already dead for some reason
+ return;
+ }
+
+ // TODO: Perhaps we need to do more here.
+ ns->SetStreamTimeParameter(OscillatorNodeEngine::START,
+ Context()->DestinationStream(),
+ aWhen);
+
+ MOZ_ASSERT(!mPlayingRef, "We can only accept a successful start() call once");
+ mPlayingRef.Take(this);
+}
+
+void
+OscillatorNode::Stop(double aWhen, ErrorResult& aRv)
+{
+ if (!WebAudioUtils::IsTimeValid(aWhen)) {
+ aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return;
+ }
+
+ if (!mStartCalled) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ mPlayingRef.Drop(this);
+
+ AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
+ if (!ns || !Context()) {
+ // We've already stopped and had our stream shut down
+ return;
+ }
+
+ // TODO: Perhaps we need to do more here.
+ ns->SetStreamTimeParameter(OscillatorNodeEngine::STOP,
+ Context()->DestinationStream(),
+ std::max(0.0, aWhen));
+}
+
+void
+OscillatorNode::NotifyMainThreadStateChanged()
+{
+ if (mStream->IsFinished()) {
+ class EndedEventDispatcher : public nsRunnable
+ {
+ public:
+ explicit EndedEventDispatcher(OscillatorNode* aNode)
+ : mNode(aNode) {}
+ NS_IMETHODIMP Run()
+ {
+ // If it's not safe to run scripts right now, schedule this to run later
+ if (!nsContentUtils::IsSafeToRunScript()) {
+ nsContentUtils::AddScriptRunner(this);
+ return NS_OK;
+ }
+
+ mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
+ return NS_OK;
+ }
+ private:
+ nsRefPtr<OscillatorNode> mNode;
+ };
+ if (!mStopped) {
+ // Only dispatch the ended event once
+ NS_DispatchToMainThread(new EndedEventDispatcher(this));
+ mStopped = true;
+ }
+
+ // Drop the playing reference
+ // Warning: The below line might delete this.
+ mPlayingRef.Drop(this);
+ }
+}
+
+}
+}
+
diff --git a/content/media/webaudio/OscillatorNode.h b/content/media/webaudio/OscillatorNode.h
new file mode 100644
index 0000000..a247fd7
--- /dev/null
+++ b/content/media/webaudio/OscillatorNode.h
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef OscillatorNode_h_
+#define OscillatorNode_h_
+
+#include "AudioNode.h"
+#include "AudioParam.h"
+#include "WaveTable.h"
+#include "mozilla/dom/OscillatorNodeBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+class AudioContext;
+
+class OscillatorNode : public AudioNode,
+ public MainThreadMediaStreamListener
+{
+public:
+ explicit OscillatorNode(AudioContext* aContext);
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OscillatorNode, AudioNode)
+
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+ virtual void DestroyMediaStream() MOZ_OVERRIDE
+ {
+ if (mStream) {
+ mStream->RemoveMainThreadListener(this);
+ }
+ AudioNode::DestroyMediaStream();
+ }
+ virtual uint16_t NumberOfInputs() const MOZ_FINAL MOZ_OVERRIDE
+ {
+ return 0;
+ }
+
+ OscillatorType Type() const
+ {
+ return mType;
+ }
+ void SetType(OscillatorType aType, ErrorResult& aRv)
+ {
+ // Handle the alternate enum values
+ switch (aType) {
+ case OscillatorType::_0: aType = OscillatorType::Sine; break;
+ case OscillatorType::_1: aType = OscillatorType::Square; break;
+ case OscillatorType::_2: aType = OscillatorType::Sawtooth; break;
+ case OscillatorType::_3: aType = OscillatorType::Triangle; break;
+ case OscillatorType::_4: aType = OscillatorType::Custom; break;
+ default:
+ // Shut up the compiler warning
+ break;
+ }
+
+ if (aType == OscillatorType::Custom) {
+ aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return;
+ }
+ mType = aType;
+ SendTypeToStream();
+ }
+
+ AudioParam* Frequency() const
+ {
+ return mFrequency;
+ }
+ AudioParam* Detune() const
+ {
+ return mDetune;
+ }
+
+ void Start(double aWhen, ErrorResult& aRv);
+ void NoteOn(double aWhen, ErrorResult& aRv)
+ {
+ Start(aWhen, aRv);
+ }
+ void Stop(double aWhen, ErrorResult& aRv);
+ void NoteOff(double aWhen, ErrorResult& aRv)
+ {
+ Stop(aWhen, aRv);
+ }
+ void SetWaveTable(WaveTable& aWaveTable)
+ {
+ mWaveTable = &aWaveTable;
+ mType = OscillatorType::Custom;
+ SendTypeToStream();
+ }
+
+ IMPL_EVENT_HANDLER(ended)
+
+ virtual void NotifyMainThreadStateChanged() MOZ_OVERRIDE;
+
+private:
+ static void SendFrequencyToStream(AudioNode* aNode);
+ static void SendDetuneToStream(AudioNode* aNode);
+ void SendTypeToStream();
+
+private:
+ OscillatorType mType;
+ nsRefPtr<WaveTable> mWaveTable;
+ nsRefPtr<AudioParam> mFrequency;
+ nsRefPtr<AudioParam> mDetune;
+ SelfReference<OscillatorNode> mPlayingRef;
+ bool mStartCalled;
+ bool mStopped;
+};
+
+}
+}
+
+#endif
+
diff --git a/content/media/webaudio/blink/DirectConvolver.cpp b/content/media/webaudio/blink/DirectConvolver.cpp
new file mode 100644
index 0000000..43461ad
--- /dev/null
+++ b/content/media/webaudio/blink/DirectConvolver.cpp
@@ -0,0 +1,347 @@
+/*
+ * 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 "DirectConvolver.h"
+#include "mozilla/PodOperations.h"
+
+using namespace mozilla;
+
+namespace WebCore {
+
+DirectConvolver::DirectConvolver(size_t inputBlockSize)
+ : m_inputBlockSize(inputBlockSize)
+{
+ m_buffer.SetLength(inputBlockSize * 2);
+ PodZero(m_buffer.Elements(), inputBlockSize * 2);
+}
+
+void DirectConvolver::process(const nsTArray<float>* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess)
+{
+ MOZ_ASSERT(framesToProcess == m_inputBlockSize);
+ if (framesToProcess != m_inputBlockSize)
+ return;
+
+ // Only support kernelSize <= m_inputBlockSize
+ size_t kernelSize = convolutionKernel->Length();
+ MOZ_ASSERT(kernelSize <= m_inputBlockSize);
+ if (kernelSize > m_inputBlockSize)
+ return;
+
+ const float* kernelP = convolutionKernel->Elements();
+
+ // Sanity check
+ bool isCopyGood = kernelP && sourceP && destP && m_buffer.Elements();
+ MOZ_ASSERT(isCopyGood);
+ if (!isCopyGood)
+ return;
+
+ float* inputP = m_buffer.Elements() + m_inputBlockSize;
+
+ // Copy samples to 2nd half of input buffer.
+ memcpy(inputP, sourceP, sizeof(float) * framesToProcess);
+
+ // 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;
+ }
+
+ // Copy 2nd half of input buffer to 1st half.
+ memcpy(m_buffer.Elements(), inputP, sizeof(float) * framesToProcess);
+}
+
+void DirectConvolver::reset()
+{
+ PodZero(m_buffer.Elements(), m_buffer.Length());
+}
+
+} // namespace WebCore
diff --git a/content/media/webaudio/blink/DirectConvolver.h b/content/media/webaudio/blink/DirectConvolver.h
new file mode 100644
index 0000000..0a89cd5
--- /dev/null
+++ b/content/media/webaudio/blink/DirectConvolver.h
@@ -0,0 +1,52 @@
+/*
+ * 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 "nsTArray.h"
+
+namespace WebCore {
+
+class DirectConvolver {
+public:
+ DirectConvolver(size_t inputBlockSize);
+
+ void process(const nsTArray<float>* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess);
+
+ void reset();
+
+private:
+ size_t m_inputBlockSize;
+
+ nsTArray<float> m_buffer;
+};
+
+} // namespace WebCore
+
+#endif // DirectConvolver_h
diff --git a/content/media/webaudio/blink/FFTConvolver.cpp b/content/media/webaudio/blink/FFTConvolver.cpp
new file mode 100644
index 0000000..6181516
--- /dev/null
+++ b/content/media/webaudio/blink/FFTConvolver.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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 "FFTConvolver.h"
+#include "mozilla/PodOperations.h"
+
+using namespace mozilla;
+
+namespace WebCore {
+
+FFTConvolver::FFTConvolver(size_t fftSize)
+ : m_frame(fftSize)
+ , m_readWriteIndex(0)
+{
+ 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)
+{
+ 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);
+ MOZ_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.Elements();
+
+ // Sanity check
+ 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.Elements();
+
+ // Sanity check
+ bool isCopyGood2 = destP && outputP && m_readWriteIndex + divisionSize <= m_outputBuffer.Length();
+ MOZ_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.PerformFFT(m_inputBuffer.Elements());
+ m_frame.Multiply(*fftKernel);
+ m_frame.PerformInverseFFT(m_outputBuffer.Elements());
+
+ // Overlap-add 1st half from previous time
+ AudioBufferAddWithScale(m_lastOverlapBuffer.Elements(), 1.0f,
+ m_outputBuffer.Elements(), halfSize);
+
+ // Finally, save 2nd half of result
+ bool isCopyGood3 = m_outputBuffer.Length() == 2 * halfSize && m_lastOverlapBuffer.Length() == halfSize;
+ MOZ_ASSERT(isCopyGood3);
+ if (!isCopyGood3)
+ return;
+
+ memcpy(m_lastOverlapBuffer.Elements(), m_outputBuffer.Elements() + halfSize, sizeof(float) * halfSize);
+
+ // Reset index back to start for next time
+ m_readWriteIndex = 0;
+ }
+ }
+}
+
+void FFTConvolver::reset()
+{
+ PodZero(m_lastOverlapBuffer.Elements(), m_lastOverlapBuffer.Length());
+ m_readWriteIndex = 0;
+}
+
+} // namespace WebCore
diff --git a/content/media/webaudio/blink/FFTConvolver.h b/content/media/webaudio/blink/FFTConvolver.h
new file mode 100644
index 0000000..1ddd40c
--- /dev/null
+++ b/content/media/webaudio/blink/FFTConvolver.h
@@ -0,0 +1,74 @@
+/*
+ * 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 "nsTArray.h"
+#include "mozilla/FFTFrame.h"
+
+namespace WebCore {
+
+typedef nsTArray<float> AudioFloatArray;
+using mozilla::FFTFrame;
+
+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
diff --git a/content/media/webaudio/blink/Reverb.cpp b/content/media/webaudio/blink/Reverb.cpp
new file mode 100644
index 0000000..deccbbd
--- /dev/null
+++ b/content/media/webaudio/blink/Reverb.cpp
@@ -0,0 +1,236 @@
+/*
+ * 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 "Reverb.h"
+
+#include <math.h>
+#include "ReverbConvolver.h"
+
+using namespace mozilla;
+
+namespace WebCore {
+
+// 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(ThreadSharedFloatArrayBufferList* response, size_t aLength, float sampleRate)
+{
+ // Normalize by RMS power
+ size_t numberOfChannels = response->GetChannels();
+
+ float power = 0;
+
+ for (size_t i = 0; i < numberOfChannels; ++i) {
+ float channelPower = AudioBlockSumOfSquares(static_cast<const float*>(response->GetData(i)), aLength);
+ power += channelPower;
+ }
+
+ power = sqrt(power / (numberOfChannels * aLength));
+
+ // 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 (sampleRate)
+ scale *= GainCalibrationSampleRate / sampleRate;
+
+ // True-stereo compensation
+ if (response->GetChannels() == 4)
+ scale *= 0.5f;
+
+ return scale;
+}
+
+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, impulseResponseBufferLength, sampleRate);
+
+ if (scale) {
+ for (uint32_t i = 0; i < impulseResponse->GetChannels(); ++i) {
+ AudioBlockInPlaceScale(const_cast<float*>(impulseResponse->GetData(i)),
+ 1, scale, impulseResponseBufferLength);
+ }
+ }
+ }
+
+ 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) {
+ for (uint32_t i = 0; i < impulseResponse->GetChannels(); ++i) {
+ AudioBlockInPlaceScale(const_cast<float*>(impulseResponse->GetData(i)),
+ 1, 1 / scale, impulseResponseBufferLength);
+ }
+ }
+}
+
+void Reverb::initialize(ThreadSharedFloatArrayBufferList* impulseResponseBuffer, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads)
+{
+ m_impulseResponseLength = impulseResponseBufferLength;
+
+ // The reverb can handle a mono impulse response and still do stereo processing
+ size_t numResponseChannels = impulseResponseBuffer->GetChannels();
+ m_convolvers.SetCapacity(numberOfChannels);
+
+ int convolverRenderPhase = 0;
+ for (size_t i = 0; i < numResponseChannels; ++i) {
+ const float* channel = impulseResponseBuffer->GetData(i);
+ size_t length = impulseResponseBufferLength;
+
+ nsAutoPtr<ReverbConvolver> 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)
+ AllocateAudioBlock(2, &m_tempBuffer);
+}
+
+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->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->mChannelData.Length() > 2) {
+ destinationBus->SetNull(destinationBus->mDuration);
+ return;
+ }
+
+ float* destinationChannelL = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[0]));
+ const float* sourceBusL = static_cast<const float*>(sourceBus->mChannelData[0]);
+
+ // Handle input -> output matrixing...
+ 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 float* sourceBusR = static_cast<const float*>(sourceBus->mChannelData[1]);
+ float* destinationChannelR = static_cast<float*>(const_cast<void*>(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) {
+ float* destinationChannel = static_cast<float*>(const_cast<void*>(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(sourceBusL, sourceBus->mDuration, destinationChannelL, destinationBus->mDuration, framesToProcess);
+
+ // simply copy L -> R
+ float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
+ bool isCopySafe = destinationChannelL && destinationChannelR && size_t(destinationBus->mDuration) >= framesToProcess && size_t(destinationBus->mDuration) >= framesToProcess;
+ MOZ_ASSERT(isCopySafe);
+ if (!isCopySafe)
+ return;
+ PodCopy(destinationChannelR, destinationChannelL, framesToProcess);
+ } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 1) {
+ // 1 -> 1 -> 1
+ 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 float* sourceBusR = static_cast<const float*>(sourceBus->mChannelData[1]);
+ float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
+
+ float* tempChannelL = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[0]));
+ float* tempChannelR = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1]));
+
+ // Process left virtual source
+ 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(sourceBusR, sourceBus->mDuration, tempChannelL, m_tempBuffer.mDuration, framesToProcess);
+ m_convolvers[3]->process(sourceBusR, sourceBus->mDuration, tempChannelR, m_tempBuffer.mDuration, framesToProcess);
+
+ 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.
+ float* destinationChannelR = static_cast<float*>(const_cast<void*>(destinationBus->mChannelData[1]));
+
+ float* tempChannelL = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[0]));
+ float* tempChannelR = static_cast<float*>(const_cast<void*>(m_tempBuffer.mChannelData[1]));
+
+ // Process left virtual source
+ 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(sourceBusL, sourceBus->mDuration, tempChannelL, m_tempBuffer.mDuration, framesToProcess);
+ m_convolvers[3]->process(sourceBusL, sourceBus->mDuration, tempChannelR, m_tempBuffer.mDuration, framesToProcess);
+
+ 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->SetNull(destinationBus->mDuration);
+ }
+}
+
+void Reverb::reset()
+{
+ 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[0]->latencyFrames() : 0;
+}
+
+} // namespace WebCore
diff --git a/content/media/webaudio/blink/Reverb.h b/content/media/webaudio/blink/Reverb.h
new file mode 100644
index 0000000..8979474
--- /dev/null
+++ b/content/media/webaudio/blink/Reverb.h
@@ -0,0 +1,70 @@
+/*
+ * 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 "ReverbConvolver.h"
+#include "nsAutoPtr.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+class ThreadSharedFloatArrayBufferList;
+}
+
+namespace WebCore {
+
+// 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(mozilla::ThreadSharedFloatArrayBufferList* impulseResponseBuffer, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate);
+
+ 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(mozilla::ThreadSharedFloatArrayBufferList* impulseResponseBuffer, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads);
+
+ size_t m_impulseResponseLength;
+
+ nsTArray<nsAutoPtr<ReverbConvolver> > m_convolvers;
+
+ // For "True" stereo processing
+ mozilla::AudioChunk m_tempBuffer;
+};
+
+} // namespace WebCore
+
+#endif // Reverb_h
diff --git a/content/media/webaudio/blink/ReverbAccumulationBuffer.cpp b/content/media/webaudio/blink/ReverbAccumulationBuffer.cpp
new file mode 100644
index 0000000..6b92e99
--- /dev/null
+++ b/content/media/webaudio/blink/ReverbAccumulationBuffer.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 "ReverbAccumulationBuffer.h"
+#include "AudioNodeEngine.h"
+#include "mozilla/PodOperations.h"
+#include <algorithm>
+
+using namespace mozilla;
+
+namespace WebCore {
+
+ReverbAccumulationBuffer::ReverbAccumulationBuffer(size_t length)
+ : 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.Length();
+ bool isCopySafe = m_readIndex <= bufferLength && numberOfFrames <= bufferLength;
+
+ MOZ_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.Elements();
+ 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.Length();
+}
+
+int ReverbAccumulationBuffer::accumulate(float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames)
+{
+ size_t bufferLength = m_buffer.Length();
+
+ 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.Elements();
+
+ bool isSafe = writeIndex <= bufferLength && numberOfFrames1 + writeIndex <= bufferLength && numberOfFrames2 <= bufferLength;
+ MOZ_ASSERT(isSafe);
+ if (!isSafe)
+ return 0;
+
+ AudioBufferAddWithScale(source, 1.0f, destination + writeIndex, numberOfFrames1);
+
+ // Handle wrap-around if necessary
+ if (numberOfFrames2 > 0) {
+ AudioBufferAddWithScale(source + numberOfFrames1, 1.0f, destination, numberOfFrames2);
+ }
+
+ return writeIndex;
+}
+
+void ReverbAccumulationBuffer::reset()
+{
+ PodZero(m_buffer.Elements(), m_buffer.Length());
+ m_readIndex = 0;
+ m_readTimeFrame = 0;
+}
+
+} // namespace WebCore
diff --git a/content/media/webaudio/blink/ReverbAccumulationBuffer.h b/content/media/webaudio/blink/ReverbAccumulationBuffer.h
new file mode 100644
index 0000000..4fe3367
--- /dev/null
+++ b/content/media/webaudio/blink/ReverbAccumulationBuffer.h
@@ -0,0 +1,69 @@
+/*
+ * 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 "nsTArray.h"
+
+namespace WebCore {
+
+typedef nsTArray<float> 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.
+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
diff --git a/content/media/webaudio/blink/ReverbConvolver.cpp b/content/media/webaudio/blink/ReverbConvolver.cpp
new file mode 100644
index 0000000..e9b8e3d
--- /dev/null
+++ b/content/media/webaudio/blink/ReverbConvolver.cpp
@@ -0,0 +1,250 @@
+/*
+ * 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 "ReverbConvolver.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla;
+
+namespace WebCore {
+
+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;
+
+class ReverbRunnable : public nsRunnable {
+public:
+ ReverbRunnable(ReverbConvolver* aConvolver)
+ : mConvolver(aConvolver)
+ {}
+
+ NS_IMETHODIMP Run() MOZ_OVERRIDE
+ {
+ mConvolver->backgroundThreadEntry();
+ return NS_OK;
+ }
+
+private:
+ ReverbConvolver* mConvolver;
+};
+
+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_useBackgroundThreads(useBackgroundThreads)
+ , m_wantsToExit(false)
+ , m_moreInputBuffered(false)
+ , m_backgroundThreadMonitor("ConvolverMonitor")
+{
+ // 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 = impulseResponseData;
+ size_t totalResponseLength = impulseResponseLength;
+
+ // 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;
+
+ nsAutoPtr<ReverbConvolverStage> stage(new ReverbConvolverStage(response, totalResponseLength, reverbTotalLatency, stageOffset, stageSize, fftSize, renderPhase, renderSliceSize, &m_accumulationBuffer, useDirectConvolver));
+
+ bool isBackgroundStage = false;
+
+ if (this->useBackgroundThreads() && stageOffset > RealtimeFrameLimit) {
+ m_backgroundStages.AppendElement(stage.forget());
+ isBackgroundStage = true;
+ } else
+ m_stages.AppendElement(stage.forget());
+
+ 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.Length() > 0) {
+ NS_NewNamedThread("ConvolverWorker",
+ getter_AddRefs(m_backgroundThread),
+ new ReverbRunnable(this));
+ }
+}
+
+ReverbConvolver::~ReverbConvolver()
+{
+ // Wait for background thread to stop
+ if (useBackgroundThreads() && m_backgroundThread) {
+ m_wantsToExit = true;
+
+ // Wake up thread so it can return
+ {
+ MonitorAutoLock locker(m_backgroundThreadMonitor);
+ m_moreInputBuffered = true;
+ locker.Notify();
+ }
+
+ m_backgroundThread->Shutdown();
+ }
+}
+
+void ReverbConvolver::backgroundThreadEntry()
+{
+ while (!m_wantsToExit) {
+ // Wait for realtime thread to give us more input
+ m_moreInputBuffered = false;
+ {
+ MonitorAutoLock locker(m_backgroundThreadMonitor);
+ while (!m_moreInputBuffered && !m_wantsToExit)
+ locker.Wait();
+ }
+
+ // If there are any pending events, it means that XPCOM wants to shut us down.
+ bool pendingEvents = false;
+ m_backgroundThread->HasPendingEvents(&pendingEvents);
+ if (pendingEvents) {
+ break;
+ }
+
+ // 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.Length(); ++i)
+ m_backgroundStages[i]->processInBackground(this, SliceSize);
+ }
+ }
+}
+
+void ReverbConvolver::process(const float* sourceChannelData, size_t sourceChannelLength,
+ float* destinationChannelData, size_t destinationChannelLength,
+ size_t framesToProcess)
+{
+ bool isSafe = sourceChannelData && destinationChannelData && sourceChannelLength >= framesToProcess && destinationChannelLength >= framesToProcess;
+ MOZ_ASSERT(isSafe);
+ if (!isSafe)
+ return;
+
+ const float* source = sourceChannelData;
+ float* destination = destinationChannelData;
+ bool isDataSafe = source && destination;
+ MOZ_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.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...
+ MonitorAutoLock locker(m_backgroundThreadMonitor);
+ m_moreInputBuffered = true;
+ locker.Notify();
+}
+
+void ReverbConvolver::reset()
+{
+ for (size_t i = 0; i < m_stages.Length(); ++i)
+ m_stages[i]->reset();
+
+ for (size_t i = 0; i < m_backgroundStages.Length(); ++i)
+ m_backgroundStages[i]->reset();
+
+ m_accumulationBuffer.reset();
+ m_inputBuffer.reset();
+}
+
+size_t ReverbConvolver::latencyFrames() const
+{
+ return 0;
+}
+
+} // namespace WebCore
diff --git a/content/media/webaudio/blink/ReverbConvolver.h b/content/media/webaudio/blink/ReverbConvolver.h
new file mode 100644
index 0000000..f676f1e
--- /dev/null
+++ b/content/media/webaudio/blink/ReverbConvolver.h
@@ -0,0 +1,96 @@
+/*
+ * 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 "DirectConvolver.h"
+#include "FFTConvolver.h"
+#include "ReverbAccumulationBuffer.h"
+#include "ReverbConvolverStage.h"
+#include "ReverbInputBuffer.h"
+#include "nsAutoPtr.h"
+#include "nsTArray.h"
+#include "nsCOMPtr.h"
+#include "nsIThread.h"
+#include "mozilla/Monitor.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(const float* impulseResponseData, size_t impulseResponseLength, size_t renderSliceSize, size_t maxFFTSize, size_t convolverRenderPhase, bool useBackgroundThreads);
+ ~ReverbConvolver();
+
+ 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; }
+
+ ReverbInputBuffer* inputBuffer() { return &m_inputBuffer; }
+
+ bool useBackgroundThreads() const { return m_useBackgroundThreads; }
+ void backgroundThreadEntry();
+
+ size_t latencyFrames() const;
+private:
+ nsTArray<nsAutoPtr<ReverbConvolverStage> > m_stages;
+ nsTArray<nsAutoPtr<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;
+ nsCOMPtr<nsIThread> m_backgroundThread;
+ bool m_wantsToExit;
+ bool m_moreInputBuffered;
+ mozilla::Monitor m_backgroundThreadMonitor;
+};
+
+} // namespace WebCore
+
+#endif // ReverbConvolver_h
diff --git a/content/media/webaudio/blink/ReverbConvolverStage.cpp b/content/media/webaudio/blink/ReverbConvolverStage.cpp
new file mode 100644
index 0000000..905cc95
--- /dev/null
+++ b/content/media/webaudio/blink/ReverbConvolverStage.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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 "ReverbConvolverStage.h"
+
+#include "ReverbAccumulationBuffer.h"
+#include "ReverbConvolver.h"
+#include "ReverbInputBuffer.h"
+#include "mozilla/PodOperations.h"
+
+using namespace mozilla;
+
+namespace WebCore {
+
+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)
+{
+ MOZ_ASSERT(impulseResponse);
+ MOZ_ASSERT(accumulationBuffer);
+
+ if (!m_directMode) {
+ m_fftKernel = new FFTFrame(fftSize);
+ m_fftKernel->PerformPaddedFFT(impulseResponse + stageOffset, stageLength);
+ m_fftConvolver = new FFTConvolver(fftSize);
+ } else {
+ m_directKernel.SetLength(fftSize / 2);
+ PodCopy(m_directKernel.Elements(), impulseResponse + stageOffset, fftSize / 2);
+ m_directConvolver = new DirectConvolver(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;
+
+ // But, the FFT convolution itself incurs fftSize / 2 latency, so subtract this out...
+ size_t halfSize = fftSize / 2;
+ if (!m_directMode) {
+ MOZ_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.SetLength(delayBufferSize);
+ PodZero(m_preDelayBuffer.Elements(), m_preDelayBuffer.Length());
+}
+
+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)
+{
+ MOZ_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.Length();
+ MOZ_ASSERT(isPreDelaySafe);
+ if (!isPreDelaySafe)
+ return;
+
+ isTemporaryBufferSafe = framesToProcess <= m_temporaryBuffer.Length();
+
+ preDelayedDestination = m_preDelayBuffer.Elements() + m_preReadWriteIndex;
+ preDelayedSource = preDelayedDestination;
+ temporaryBuffer = m_temporaryBuffer.Elements();
+ } else {
+ // Zero delay
+ preDelayedDestination = 0;
+ preDelayedSource = source;
+ temporaryBuffer = m_preDelayBuffer.Elements();
+
+ isTemporaryBufferSafe = framesToProcess <= m_preDelayBuffer.Length();
+ }
+
+ MOZ_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, preDelayedSource, temporaryBuffer, framesToProcess);
+ else
+ m_directConvolver->process(&m_directKernel, 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;
+
+ MOZ_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();
+ PodZero(m_preDelayBuffer.Elements(), m_preDelayBuffer.Length());
+ m_accumulationReadIndex = 0;
+ m_inputReadIndex = 0;
+ m_framesProcessed = 0;
+}
+
+} // namespace WebCore
diff --git a/content/media/webaudio/blink/ReverbConvolverStage.h b/content/media/webaudio/blink/ReverbConvolverStage.h
new file mode 100644
index 0000000..4df74b1
--- /dev/null
+++ b/content/media/webaudio/blink/ReverbConvolverStage.h
@@ -0,0 +1,86 @@
+/*
+ * 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 "nsTArray.h"
+#include "mozilla/FFTFrame.h"
+
+namespace WebCore {
+
+using mozilla::FFTFrame;
+
+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:
+ nsAutoPtr<FFTFrame> m_fftKernel;
+ nsAutoPtr<FFTConvolver> m_fftConvolver;
+
+ nsTArray<float> 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;
+
+ nsTArray<float> m_temporaryBuffer;
+
+ bool m_directMode;
+ nsTArray<float> m_directKernel;
+ nsAutoPtr<DirectConvolver> m_directConvolver;
+};
+
+} // namespace WebCore
+
+#endif // ReverbConvolverStage_h
diff --git a/content/media/webaudio/blink/ReverbInputBuffer.cpp b/content/media/webaudio/blink/ReverbInputBuffer.cpp
new file mode 100644
index 0000000..8221f81
--- /dev/null
+++ b/content/media/webaudio/blink/ReverbInputBuffer.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 "ReverbInputBuffer.h"
+#include "mozilla/PodOperations.h"
+
+using namespace mozilla;
+
+namespace WebCore {
+
+ReverbInputBuffer::ReverbInputBuffer(size_t length)
+ : 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.Length();
+ bool isCopySafe = m_writeIndex + numberOfFrames <= bufferLength;
+ MOZ_ASSERT(isCopySafe);
+ if (!isCopySafe)
+ return;
+
+ memcpy(m_buffer.Elements() + m_writeIndex, sourceP, sizeof(float) * numberOfFrames);
+
+ m_writeIndex += numberOfFrames;
+ MOZ_ASSERT(m_writeIndex <= bufferLength);
+
+ if (m_writeIndex >= bufferLength)
+ m_writeIndex = 0;
+}
+
+float* ReverbInputBuffer::directReadFrom(int* readIndex, size_t numberOfFrames)
+{
+ size_t bufferLength = m_buffer.Length();
+ bool isPointerGood = readIndex && *readIndex >= 0 && *readIndex + numberOfFrames <= bufferLength;
+ 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.Elements();
+ }
+
+ float* sourceP = m_buffer.Elements();
+ float* p = sourceP + *readIndex;
+
+ // Update readIndex
+ *readIndex = (*readIndex + numberOfFrames) % bufferLength;
+
+ return p;
+}
+
+void ReverbInputBuffer::reset()
+{
+ PodZero(m_buffer.Elements(), m_buffer.Length());
+ m_writeIndex = 0;
+}
+
+} // namespace WebCore
diff --git a/content/media/webaudio/blink/ReverbInputBuffer.h b/content/media/webaudio/blink/ReverbInputBuffer.h
new file mode 100644
index 0000000..22f2bf9
--- /dev/null
+++ b/content/media/webaudio/blink/ReverbInputBuffer.h
@@ -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 "nsTArray.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:
+ nsTArray<float> m_buffer;
+ size_t m_writeIndex;
+};
+
+} // namespace WebCore
+
+#endif // ReverbInputBuffer_h
diff --git a/content/media/webaudio/blink/moz.build b/content/media/webaudio/blink/moz.build
index 569be17..39b2de1 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 d8b2c50b..f505c61 100644
--- a/content/media/webaudio/moz.build
+++ b/content/media/webaudio/moz.build
@@ -17,6 +17,10 @@ EXPORTS += [
'WebAudioUtils.h',
]
+EXPORTS.mozilla += [
+ 'FFTFrame.h',
+]
+
EXPORTS.mozilla.dom += [
'AnalyserNode.h',
'AudioBuffer.h',
@@ -30,11 +34,13 @@ EXPORTS.mozilla.dom += [
'BiquadFilterNode.h',
'ChannelMergerNode.h',
'ChannelSplitterNode.h',
+ 'ConvolverNode.h',
'DelayNode.h',
'DynamicsCompressorNode.h',
'EnableWebAudioCheck.h',
'GainNode.h',
'OfflineAudioCompletionEvent.h',
+ 'OscillatorNode.h',
'PannerNode.h',
'ScriptProcessorNode.h',
'WaveShaperNode.h',
@@ -54,12 +60,14 @@ CPP_SOURCES += [
'BiquadFilterNode.cpp',
'ChannelMergerNode.cpp',
'ChannelSplitterNode.cpp',
+ 'ConvolverNode.cpp',
'DelayNode.cpp',
'DynamicsCompressorNode.cpp',
'EnableWebAudioCheck.cpp',
'GainNode.cpp',
'MediaBufferDecoder.cpp',
'OfflineAudioCompletionEvent.cpp',
+ 'OscillatorNode.cpp',
'PannerNode.cpp',
'ScriptProcessorNode.cpp',
'ThreeDPoint.cpp',
diff --git a/content/media/webaudio/test/Makefile.in b/content/media/webaudio/test/Makefile.in
index a18b048..badaeb9 100644
--- a/content/media/webaudio/test/Makefile.in
+++ b/content/media/webaudio/test/Makefile.in
@@ -66,6 +66,7 @@ MOCHITEST_FILES := \
test_OfflineAudioContext.html \
test_offlineDestinationChannelCountLess.html \
test_offlineDestinationChannelCountMore.html \
+ test_oscillatorNode.html \
test_pannerNode.html \
test_scriptProcessorNode.html \
test_scriptProcessorNodeChannelCount.html \
diff --git a/content/media/webaudio/test/test_oscillatorNode.html b/content/media/webaudio/test/test_oscillatorNode.html
new file mode 100644
index 0000000..d9a86c8
--- /dev/null
+++ b/content/media/webaudio/test/test_oscillatorNode.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the OscillatorNode interface</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="webaudio.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+
+ var context = new AudioContext();
+ var osc = context.createOscillator();
+
+ is(osc.channelCount, 2, "Oscillator node has 2 input channels by default");
+ is(osc.channelCountMode, "max", "Correct channelCountMode for the Oscillator node");
+ is(osc.channelInterpretation, "speakers", "Correct channelCountInterpretation for the Oscillator node");
+ is(osc.type, "sine", "Correct default type");
+ expectException(function() {
+ osc.type = "custom";
+ }, DOMException.NOT_SUPPORTED_ERR);
+ expectException(function() {
+ osc.type = osc.CUSTOM;
+ }, DOMException.NOT_SUPPORTED_ERR);
+ is(osc.type, "sine", "Cannot set the type to custom");
+ is(osc.frequency.value, 440, "Correct default frequency value");
+ is(osc.detune.value, 0, "Correct default detine value");
+
+ // Make sure that we can set all of the valid type values
+ var types = [
+ "sine",
+ "square",
+ "sawtooth",
+ "triangle",
+ ];
+ for (var i = 0; i < types.length; ++i) {
+ osc.type = osc[types[i].toUpperCase()];
+ is(osc.type, types[i], "Correct alternname type enum value");
+ osc.type = types[i];
+ }
+
+ SpecialPowers.clearUserPref("media.webaudio.enabled");
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf
index cbd46e8..494e5ab 100644
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -206,6 +206,11 @@ DOMInterfaces = {
'nativeType': 'nsDOMCompositionEvent',
},
+'ConvolverNode': {
+ 'implicitJSContext': [ 'buffer' ],
+ 'resultNotAddRefed': [ 'buffer' ],
+},
+
'Coordinates': {
'headerFile': 'nsGeoPosition.h'
},
@@ -715,6 +720,10 @@ DOMInterfaces = {
'nativeType': 'nsDOMOfflineResourceList',
},
+'OscillatorNode': {
+ 'resultNotAddRefed': [ 'frequency', 'detune' ],
+},
+
'PaintRequest': {
'nativeType': 'nsPaintRequest',
},
diff --git a/dom/webidl/AudioContext.webidl b/dom/webidl/AudioContext.webidl
index 7d45abf..71f3cb8 100644
--- a/dom/webidl/AudioContext.webidl
+++ b/dom/webidl/AudioContext.webidl
@@ -52,6 +52,8 @@ interface AudioContext : EventTarget {
WaveShaperNode createWaveShaper();
[Creator]
PannerNode createPanner();
+ [Creator]
+ ConvolverNode createConvolver();
[Creator, Throws]
ChannelSplitterNode createChannelSplitter(optional unsigned long numberOfOutputs = 6);
@@ -61,6 +63,8 @@ interface AudioContext : EventTarget {
[Creator]
DynamicsCompressorNode createDynamicsCompressor();
+ [Creator]
+ OscillatorNode createOscillator();
[Creator, Throws]
WaveTable createWaveTable(Float32Array real, Float32Array imag);
diff --git a/dom/webidl/ConvolverNode.webidl b/dom/webidl/ConvolverNode.webidl
new file mode 100644
index 0000000..482b048
--- /dev/null
+++ b/dom/webidl/ConvolverNode.webidl
@@ -0,0 +1,21 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+[PrefControlled]
+interface ConvolverNode : AudioNode {
+
+ [SetterThrows]
+ attribute AudioBuffer? buffer;
+ attribute boolean normalize;
+
+};
+
diff --git a/dom/webidl/OscillatorNode.webidl b/dom/webidl/OscillatorNode.webidl
new file mode 100644
index 0000000..0116f83
--- /dev/null
+++ b/dom/webidl/OscillatorNode.webidl
@@ -0,0 +1,63 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+enum OscillatorType {
+ // Hack: Use numbers to support alternate enum values
+ "0", "1", "2", "3", "4",
+
+ "sine",
+ "square",
+ "sawtooth",
+ "triangle",
+ "custom"
+};
+
+[PrefControlled]
+interface OscillatorNode : AudioNode {
+
+ [SetterThrows]
+ attribute OscillatorType type;
+
+ readonly attribute AudioParam frequency; // in Hertz
+ readonly attribute AudioParam detune; // in Cents
+
+ [Throws]
+ void start(double when);
+ [Throws]
+ void stop(double when);
+ void setWaveTable(WaveTable waveTable);
+
+ [SetterThrows]
+ attribute EventHandler onended;
+
+};
+
+/*
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#AlternateNames
+ */
+partial interface OscillatorNode {
+ // Same as start()
+ [Throws]
+ void noteOn(double when);
+
+ // Same as stop()
+ [Throws]
+ void noteOff(double when);
+
+ const unsigned short SINE = 0;
+ const unsigned short SQUARE = 1;
+ const unsigned short SAWTOOTH = 2;
+ const unsigned short TRIANGLE = 3;
+ const unsigned short CUSTOM = 4;
+};
+
diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk
index d381d46..1072754 100644
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -43,6 +43,7 @@ webidl_files = \
CommandEvent.webidl \
Comment.webidl \
CompositionEvent.webidl \
+ ConvolverNode.webidl \
Coordinates.webidl \
CSS.webidl \
CSSPrimitiveValue.webidl \
@@ -192,6 +193,7 @@ webidl_files = \
OfflineAudioCompletionEvent.webidl \
OfflineAudioContext.webidl \
OfflineResourceList.webidl \
+ OscillatorNode.webidl \
PaintRequest.webidl \
PaintRequestList.webidl \
PannerNode.webidl \
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment