Created
June 7, 2013 15:32
-
-
Save ehsan/5730140 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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