blob: f9e2cefe30743e21d3d044f06b60073e5f53718c [file] [log] [blame]
/*
* Copyright (C) 2011, 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "modules/webaudio/WaveShaperDSPKernel.h"
#include <algorithm>
#include "platform/audio/AudioUtilities.h"
#include "platform/wtf/PtrUtil.h"
#include "platform/wtf/Threading.h"
namespace blink {
WaveShaperDSPKernel::WaveShaperDSPKernel(WaveShaperProcessor* processor)
: AudioDSPKernel(processor) {
if (processor->Oversample() != WaveShaperProcessor::kOverSampleNone)
LazyInitializeOversampling();
}
void WaveShaperDSPKernel::LazyInitializeOversampling() {
if (!temp_buffer_) {
temp_buffer_ = WTF::WrapUnique(
new AudioFloatArray(AudioUtilities::kRenderQuantumFrames * 2));
temp_buffer2_ = WTF::WrapUnique(
new AudioFloatArray(AudioUtilities::kRenderQuantumFrames * 4));
up_sampler_ =
WTF::WrapUnique(new UpSampler(AudioUtilities::kRenderQuantumFrames));
down_sampler_ = WTF::WrapUnique(
new DownSampler(AudioUtilities::kRenderQuantumFrames * 2));
up_sampler2_ = WTF::WrapUnique(
new UpSampler(AudioUtilities::kRenderQuantumFrames * 2));
down_sampler2_ = WTF::WrapUnique(
new DownSampler(AudioUtilities::kRenderQuantumFrames * 4));
}
}
void WaveShaperDSPKernel::Process(const float* source,
float* destination,
size_t frames_to_process) {
switch (GetWaveShaperProcessor()->Oversample()) {
case WaveShaperProcessor::kOverSampleNone:
ProcessCurve(source, destination, frames_to_process);
break;
case WaveShaperProcessor::kOverSample2x:
ProcessCurve2x(source, destination, frames_to_process);
break;
case WaveShaperProcessor::kOverSample4x:
ProcessCurve4x(source, destination, frames_to_process);
break;
default:
NOTREACHED();
}
}
void WaveShaperDSPKernel::ProcessCurve(const float* source,
float* destination,
size_t frames_to_process) {
DCHECK(source);
DCHECK(destination);
DCHECK(GetWaveShaperProcessor());
Vector<float>* curve = GetWaveShaperProcessor()->Curve();
if (!curve) {
// Act as "straight wire" pass-through if no curve is set.
memcpy(destination, source, sizeof(float) * frames_to_process);
return;
}
float* curve_data = curve->data();
int curve_length = curve->size();
DCHECK(curve_data);
if (!curve_data || !curve_length) {
memcpy(destination, source, sizeof(float) * frames_to_process);
return;
}
// Apply waveshaping curve.
for (unsigned i = 0; i < frames_to_process; ++i) {
const float input = source[i];
// Calculate a virtual index based on input -1 -> +1 with -1 being curve[0],
// +1 being curve[curveLength - 1], and 0 being at the center of the curve
// data. Then linearly interpolate between the two points in the curve.
double virtual_index = 0.5 * (input + 1) * (curve_length - 1);
double output;
if (virtual_index < 0) {
// input < -1, so use curve[0]
output = curve_data[0];
} else if (virtual_index >= curve_length - 1) {
// input >= 1, so use last curve value
output = curve_data[curve_length - 1];
} else {
// The general case where -1 <= input < 1, where 0 <= virtualIndex <
// curveLength - 1, so interpolate between the nearest samples on the
// curve.
unsigned index1 = static_cast<unsigned>(virtual_index);
unsigned index2 = index1 + 1;
double interpolation_factor = virtual_index - index1;
double value1 = curve_data[index1];
double value2 = curve_data[index2];
output =
(1.0 - interpolation_factor) * value1 + interpolation_factor * value2;
}
destination[i] = output;
}
}
void WaveShaperDSPKernel::ProcessCurve2x(const float* source,
float* destination,
size_t frames_to_process) {
bool is_safe = frames_to_process == AudioUtilities::kRenderQuantumFrames;
DCHECK(is_safe);
if (!is_safe)
return;
float* temp_p = temp_buffer_->Data();
up_sampler_->Process(source, temp_p, frames_to_process);
// Process at 2x up-sampled rate.
ProcessCurve(temp_p, temp_p, frames_to_process * 2);
down_sampler_->Process(temp_p, destination, frames_to_process * 2);
}
void WaveShaperDSPKernel::ProcessCurve4x(const float* source,
float* destination,
size_t frames_to_process) {
bool is_safe = frames_to_process == AudioUtilities::kRenderQuantumFrames;
DCHECK(is_safe);
if (!is_safe)
return;
float* temp_p = temp_buffer_->Data();
float* temp_p2 = temp_buffer2_->Data();
up_sampler_->Process(source, temp_p, frames_to_process);
up_sampler2_->Process(temp_p, temp_p2, frames_to_process * 2);
// Process at 4x up-sampled rate.
ProcessCurve(temp_p2, temp_p2, frames_to_process * 4);
down_sampler2_->Process(temp_p2, temp_p, frames_to_process * 4);
down_sampler_->Process(temp_p, destination, frames_to_process * 2);
}
void WaveShaperDSPKernel::Reset() {
if (up_sampler_) {
up_sampler_->Reset();
down_sampler_->Reset();
up_sampler2_->Reset();
down_sampler2_->Reset();
}
}
double WaveShaperDSPKernel::LatencyTime() const {
size_t latency_frames = 0;
WaveShaperDSPKernel* kernel = const_cast<WaveShaperDSPKernel*>(this);
switch (kernel->GetWaveShaperProcessor()->Oversample()) {
case WaveShaperProcessor::kOverSampleNone:
break;
case WaveShaperProcessor::kOverSample2x:
latency_frames += up_sampler_->LatencyFrames();
latency_frames += down_sampler_->LatencyFrames();
break;
case WaveShaperProcessor::kOverSample4x: {
// Account for first stage upsampling.
latency_frames += up_sampler_->LatencyFrames();
latency_frames += down_sampler_->LatencyFrames();
// Account for second stage upsampling.
// and divide by 2 to get back down to the regular sample-rate.
size_t latency_frames2 =
(up_sampler2_->LatencyFrames() + down_sampler2_->LatencyFrames()) / 2;
latency_frames += latency_frames2;
break;
}
default:
NOTREACHED();
}
return static_cast<double>(latency_frames) / SampleRate();
}
} // namespace blink