mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 815643 - Part 5: Implement ConvolverNode's processing based on the Blink implementation; r=roc
--HG-- extra : rebase_source : 4c1a8dbaa4ab2d4fcc67c0a5226a6937e0656404
This commit is contained in:
parent
5c7839318c
commit
af95a1d60d
@ -8,6 +8,7 @@
|
||||
#include "mozilla/dom/ConvolverNodeBinding.h"
|
||||
#include "AudioNodeEngine.h"
|
||||
#include "AudioNodeStream.h"
|
||||
#include "blink/Reverb.h"
|
||||
|
||||
#include <cmath>
|
||||
#include "nsMathUtils.h"
|
||||
@ -28,16 +29,32 @@ 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;
|
||||
@ -45,9 +62,43 @@ public:
|
||||
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,
|
||||
@ -55,12 +106,46 @@ public:
|
||||
AudioChunk* aOutput,
|
||||
bool* aFinished)
|
||||
{
|
||||
*aOutput = aInput;
|
||||
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);
|
||||
WriteZeroesToAudioBlock(&input, 0, WEBAUDIO_BLOCK_SIZE);
|
||||
} else if (aInput.mVolume != 1.0f) {
|
||||
// Pre-multiply the input's volume
|
||||
numChannels = aInput.mChannelData.Length();
|
||||
AllocateAudioBlock(numChannels, &input);
|
||||
for (uint32_t i = 0; i < numChannels; ++i) {
|
||||
const float* src = static_cast<const float*>(aInput.mChannelData[i]);
|
||||
float* dest = static_cast<float*>(const_cast<void*>(input.mChannelData[i]));
|
||||
AudioBlockAddChannelWithScale(src, aInput.mVolume, dest);
|
||||
}
|
||||
} 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)
|
||||
@ -100,8 +185,27 @@ ConvolverNode::SetBuffer(JSContext* aCx, AudioBuffer* aBuffer, ErrorResult& aRv)
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
MOZ_ASSERT(ns, "Why don't we have a stream here?");
|
||||
if (mBuffer) {
|
||||
uint32_t length = mBuffer->Length();
|
||||
nsRefPtr<ThreadSharedFloatArrayBufferList> data =
|
||||
mBuffer->GetThreadSharedChannelsForRate(aCx);
|
||||
if (length < WEBAUDIO_BLOCK_SIZE) {
|
||||
// For very small impulse response buffers, we need to pad the
|
||||
// buffer with 0 to make sure that the Reverb implementation
|
||||
// has enough data to compute FFTs from.
|
||||
length = WEBAUDIO_BLOCK_SIZE;
|
||||
nsRefPtr<ThreadSharedFloatArrayBufferList> paddedBuffer =
|
||||
new ThreadSharedFloatArrayBufferList(data->GetChannels());
|
||||
float* channelData = (float*) malloc(sizeof(float) * length * data->GetChannels());
|
||||
for (uint32_t i = 0; i < data->GetChannels(); ++i) {
|
||||
PodCopy(channelData + length * i, data->GetData(i), mBuffer->Length());
|
||||
PodZero(channelData + length * i + mBuffer->Length(), WEBAUDIO_BLOCK_SIZE - mBuffer->Length());
|
||||
paddedBuffer->SetData(i, (i == 0) ? channelData : nullptr, channelData);
|
||||
}
|
||||
data = paddedBuffer;
|
||||
}
|
||||
SendInt32ParameterToStream(ConvolverNodeEngine::BUFFER_LENGTH, length);
|
||||
SendDoubleParameterToStream(ConvolverNodeEngine::SAMPLE_RATE,
|
||||
mBuffer->SampleRate());
|
||||
ns->SetBuffer(data.forget());
|
||||
} else {
|
||||
ns->SetBuffer(nullptr);
|
||||
|
Loading…
Reference in New Issue
Block a user