blob: 16914b80ac0f2c2790daf78e0d4ec7dfa19097c2 [file] [log] [blame]
/*
* 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 "platform/audio/ReverbAccumulationBuffer.h"
#include "platform/audio/ReverbConvolver.h"
#include "platform/audio/ReverbConvolverStage.h"
#include "platform/audio/ReverbInputBuffer.h"
#include "platform/audio/VectorMath.h"
#include "wtf/PtrUtil.h"
namespace blink {
using namespace VectorMath;
ReverbConvolverStage::ReverbConvolverStage(
const float* impulseResponse,
size_t,
size_t reverbTotalLatency,
size_t stageOffset,
size_t stageLength,
size_t fftSize,
size_t renderPhase,
size_t renderSliceSize,
ReverbAccumulationBuffer* accumulationBuffer,
bool directMode)
: m_accumulationBuffer(accumulationBuffer),
m_accumulationReadIndex(0),
m_inputReadIndex(0),
m_directMode(directMode) {
ASSERT(impulseResponse);
ASSERT(accumulationBuffer);
if (!m_directMode) {
m_fftKernel = wrapUnique(new FFTFrame(fftSize));
m_fftKernel->doPaddedFFT(impulseResponse + stageOffset, stageLength);
m_fftConvolver = wrapUnique(new FFTConvolver(fftSize));
} else {
ASSERT(!stageOffset);
ASSERT(stageLength <= fftSize / 2);
m_directKernel = wrapUnique(new AudioFloatArray(fftSize / 2));
m_directKernel->copyToRange(impulseResponse, 0, stageLength);
m_directConvolver = wrapUnique(new DirectConvolver(renderSliceSize));
}
m_temporaryBuffer.allocate(renderSliceSize);
// The convolution stage at offset stageOffset needs to have a corresponding
// delay to cancel out the offset.
size_t totalDelay = stageOffset + reverbTotalLatency;
// But, the FFT convolution itself incurs fftSize / 2 latency, so subtract
// this out...
size_t halfSize = fftSize / 2;
if (!m_directMode) {
ASSERT(totalDelay >= halfSize);
if (totalDelay >= halfSize)
totalDelay -= halfSize;
}
// We divide up the total delay, into pre and post delay sections so that we
// can schedule at exactly the moment when the FFT will happen. This is
// coordinated with the other stages, so they don't all do their FFTs at the
// same time...
int maxPreDelayLength = std::min(halfSize, totalDelay);
m_preDelayLength = totalDelay > 0 ? renderPhase % maxPreDelayLength : 0;
if (m_preDelayLength > totalDelay)
m_preDelayLength = 0;
m_postDelayLength = totalDelay - m_preDelayLength;
m_preReadWriteIndex = 0;
m_framesProcessed = 0; // total frames processed so far
size_t delayBufferSize =
m_preDelayLength < fftSize ? fftSize : m_preDelayLength;
delayBufferSize =
delayBufferSize < renderSliceSize ? renderSliceSize : delayBufferSize;
m_preDelayBuffer.allocate(delayBufferSize);
}
void ReverbConvolverStage::processInBackground(ReverbConvolver* convolver,
size_t framesToProcess) {
ReverbInputBuffer* inputBuffer = convolver->inputBuffer();
float* source =
inputBuffer->directReadFrom(&m_inputReadIndex, framesToProcess);
process(source, framesToProcess);
}
void ReverbConvolverStage::process(const float* source,
size_t framesToProcess) {
ASSERT(source);
if (!source)
return;
// Deal with pre-delay stream : note special handling of zero delay.
const float* preDelayedSource;
float* preDelayedDestination;
float* temporaryBuffer;
bool isTemporaryBufferSafe = false;
if (m_preDelayLength > 0) {
// Handles both the read case (call to process() ) and the write case
// (memcpy() )
bool isPreDelaySafe =
m_preReadWriteIndex + framesToProcess <= m_preDelayBuffer.size();
ASSERT(isPreDelaySafe);
if (!isPreDelaySafe)
return;
isTemporaryBufferSafe = framesToProcess <= m_temporaryBuffer.size();
preDelayedDestination = m_preDelayBuffer.data() + m_preReadWriteIndex;
preDelayedSource = preDelayedDestination;
temporaryBuffer = m_temporaryBuffer.data();
} else {
// Zero delay
preDelayedDestination = 0;
preDelayedSource = source;
temporaryBuffer = m_preDelayBuffer.data();
isTemporaryBufferSafe = framesToProcess <= m_preDelayBuffer.size();
}
ASSERT(isTemporaryBufferSafe);
if (!isTemporaryBufferSafe)
return;
if (m_framesProcessed < m_preDelayLength) {
// For the first m_preDelayLength frames don't process the convolver,
// instead simply buffer in the pre-delay. But while buffering the
// pre-delay, we still need to update our index.
m_accumulationBuffer->updateReadIndex(&m_accumulationReadIndex,
framesToProcess);
} else {
// Now, run the convolution (into the delay buffer).
// An expensive FFT will happen every fftSize / 2 frames.
// We process in-place here...
if (!m_directMode)
m_fftConvolver->process(m_fftKernel.get(), preDelayedSource,
temporaryBuffer, framesToProcess);
else
m_directConvolver->process(m_directKernel.get(), preDelayedSource,
temporaryBuffer, framesToProcess);
// Now accumulate into reverb's accumulation buffer.
m_accumulationBuffer->accumulate(temporaryBuffer, framesToProcess,
&m_accumulationReadIndex,
m_postDelayLength);
}
// Finally copy input to pre-delay.
if (m_preDelayLength > 0) {
memcpy(preDelayedDestination, source, sizeof(float) * framesToProcess);
m_preReadWriteIndex += framesToProcess;
ASSERT(m_preReadWriteIndex <= m_preDelayLength);
if (m_preReadWriteIndex >= m_preDelayLength)
m_preReadWriteIndex = 0;
}
m_framesProcessed += framesToProcess;
}
void ReverbConvolverStage::reset() {
if (!m_directMode)
m_fftConvolver->reset();
else
m_directConvolver->reset();
m_preDelayBuffer.zero();
m_accumulationReadIndex = 0;
m_inputReadIndex = 0;
m_framesProcessed = 0;
}
} // namespace blink