| /* |
| * Copyright (C) 2012, 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/OscillatorNode.h" |
| #include <algorithm> |
| #include "bindings/core/v8/ExceptionMessages.h" |
| #include "bindings/core/v8/ExceptionState.h" |
| #include "core/dom/ExceptionCode.h" |
| #include "modules/webaudio/AudioNodeOutput.h" |
| #include "modules/webaudio/PeriodicWave.h" |
| #include "platform/audio/AudioUtilities.h" |
| #include "platform/audio/VectorMath.h" |
| #include "platform/wtf/MathExtras.h" |
| #include "platform/wtf/StdLibExtras.h" |
| |
| namespace blink { |
| |
| using namespace VectorMath; |
| |
| OscillatorHandler::OscillatorHandler(AudioNode& node, |
| float sample_rate, |
| const String& oscillator_type, |
| PeriodicWave* wave_table, |
| AudioParamHandler& frequency, |
| AudioParamHandler& detune) |
| : AudioScheduledSourceHandler(kNodeTypeOscillator, node, sample_rate), |
| frequency_(&frequency), |
| detune_(&detune), |
| first_render_(true), |
| virtual_read_index_(0), |
| phase_increments_(AudioUtilities::kRenderQuantumFrames), |
| detune_values_(AudioUtilities::kRenderQuantumFrames) { |
| if (wave_table) { |
| // A PeriodicWave overrides any value for the oscillator type, |
| // forcing the type to be 'custom". |
| SetPeriodicWave(wave_table); |
| } else { |
| if (oscillator_type == "sine") |
| SetType(SINE); |
| else if (oscillator_type == "square") |
| SetType(SQUARE); |
| else if (oscillator_type == "sawtooth") |
| SetType(SAWTOOTH); |
| else if (oscillator_type == "triangle") |
| SetType(TRIANGLE); |
| else |
| NOTREACHED(); |
| } |
| |
| // An oscillator is always mono. |
| AddOutput(1); |
| |
| Initialize(); |
| } |
| |
| scoped_refptr<OscillatorHandler> OscillatorHandler::Create( |
| AudioNode& node, |
| float sample_rate, |
| const String& oscillator_type, |
| PeriodicWave* wave_table, |
| AudioParamHandler& frequency, |
| AudioParamHandler& detune) { |
| return base::AdoptRef(new OscillatorHandler( |
| node, sample_rate, oscillator_type, wave_table, frequency, detune)); |
| } |
| |
| OscillatorHandler::~OscillatorHandler() { |
| Uninitialize(); |
| } |
| |
| String OscillatorHandler::GetType() const { |
| switch (type_) { |
| case SINE: |
| return "sine"; |
| case SQUARE: |
| return "square"; |
| case SAWTOOTH: |
| return "sawtooth"; |
| case TRIANGLE: |
| return "triangle"; |
| case CUSTOM: |
| return "custom"; |
| default: |
| NOTREACHED(); |
| return "custom"; |
| } |
| } |
| |
| void OscillatorHandler::SetType(const String& type, |
| ExceptionState& exception_state) { |
| if (type == "sine") { |
| SetType(SINE); |
| } else if (type == "square") { |
| SetType(SQUARE); |
| } else if (type == "sawtooth") { |
| SetType(SAWTOOTH); |
| } else if (type == "triangle") { |
| SetType(TRIANGLE); |
| } else if (type == "custom") { |
| exception_state.ThrowDOMException(kInvalidStateError, |
| "'type' cannot be set directly to " |
| "'custom'. Use setPeriodicWave() to " |
| "create a custom Oscillator type."); |
| } |
| } |
| |
| bool OscillatorHandler::SetType(unsigned type) { |
| PeriodicWave* periodic_wave = nullptr; |
| |
| switch (type) { |
| case SINE: |
| periodic_wave = Context()->GetPeriodicWave(SINE); |
| break; |
| case SQUARE: |
| periodic_wave = Context()->GetPeriodicWave(SQUARE); |
| break; |
| case SAWTOOTH: |
| periodic_wave = Context()->GetPeriodicWave(SAWTOOTH); |
| break; |
| case TRIANGLE: |
| periodic_wave = Context()->GetPeriodicWave(TRIANGLE); |
| break; |
| case CUSTOM: |
| default: |
| // Return false for invalid types, including CUSTOM since |
| // setPeriodicWave() method must be called explicitly. |
| NOTREACHED(); |
| return false; |
| } |
| |
| SetPeriodicWave(periodic_wave); |
| type_ = type; |
| return true; |
| } |
| |
| bool OscillatorHandler::CalculateSampleAccuratePhaseIncrements( |
| size_t frames_to_process) { |
| bool is_good = frames_to_process <= phase_increments_.size() && |
| frames_to_process <= detune_values_.size(); |
| DCHECK(is_good); |
| if (!is_good) |
| return false; |
| |
| if (first_render_) { |
| first_render_ = false; |
| frequency_->ResetSmoothedValue(); |
| detune_->ResetSmoothedValue(); |
| } |
| |
| bool has_sample_accurate_values = false; |
| bool has_frequency_changes = false; |
| float* phase_increments = phase_increments_.Data(); |
| |
| float final_scale = periodic_wave_->RateScale(); |
| |
| if (frequency_->HasSampleAccurateValues()) { |
| has_sample_accurate_values = true; |
| has_frequency_changes = true; |
| |
| // Get the sample-accurate frequency values and convert to phase increments. |
| // They will be converted to phase increments below. |
| frequency_->CalculateSampleAccurateValues(phase_increments, |
| frames_to_process); |
| } else { |
| // Handle ordinary parameter changes if there are no scheduled changes. |
| float frequency = frequency_->Value(); |
| final_scale *= frequency; |
| } |
| |
| if (detune_->HasSampleAccurateValues()) { |
| has_sample_accurate_values = true; |
| |
| // Get the sample-accurate detune values. |
| float* detune_values = |
| has_frequency_changes ? detune_values_.Data() : phase_increments; |
| detune_->CalculateSampleAccurateValues(detune_values, frames_to_process); |
| |
| // Convert from cents to rate scalar. |
| float k = 1.0 / 1200; |
| Vsmul(detune_values, 1, &k, detune_values, 1, frames_to_process); |
| for (unsigned i = 0; i < frames_to_process; ++i) |
| detune_values[i] = powf( |
| 2, detune_values[i]); // FIXME: converting to expf() will be faster. |
| |
| if (has_frequency_changes) { |
| // Multiply frequencies by detune scalings. |
| Vmul(detune_values, 1, phase_increments, 1, phase_increments, 1, |
| frames_to_process); |
| } |
| } else { |
| // Handle ordinary parameter changes if there are no scheduled |
| // changes. |
| float detune = detune_->Value(); |
| float detune_scale = powf(2, detune / 1200); |
| final_scale *= detune_scale; |
| } |
| |
| if (has_sample_accurate_values) { |
| // Convert from frequency to wavetable increment. |
| Vsmul(phase_increments, 1, &final_scale, phase_increments, 1, |
| frames_to_process); |
| } |
| |
| return has_sample_accurate_values; |
| } |
| |
| static float DoInterpolation(double virtual_read_index, |
| float incr, |
| unsigned read_index_mask, |
| float table_interpolation_factor, |
| const float* lower_wave_data, |
| const float* higher_wave_data) { |
| DCHECK_GE(incr, 0); |
| |
| double sample_lower = 0; |
| double sample_higher = 0; |
| |
| unsigned read_index_0 = static_cast<unsigned>(virtual_read_index); |
| |
| // Consider a typical sample rate of 44100 Hz and max periodic wave |
| // size of 4096. The relationship between |incr| and the frequency |
| // of the oscillator is |incr| = freq * 4096/44100. Or freq = |
| // |incr|*44100/4096 = 10.8*|incr|. |
| // |
| // For the |incr| thresholds below, this means that we use linear |
| // interpolation for all freq >= 3.2 Hz, 3-point Lagrange |
| // for freq >= 1.7 Hz and 5-point Lagrange for every thing else. |
| // |
| // We use Lagrange interpolation because it's relatively simple to |
| // implement and fairly inexpensive, and the interpolator always |
| // passes through known points. |
| if (incr >= 0.3) { |
| // Increment is fairly large, so we're doing no more than about 3 |
| // points between each wave table entry. Assume linear |
| // interpolation between points is good enough. |
| unsigned read_index2 = read_index_0 + 1; |
| |
| // Contain within valid range. |
| read_index_0 = read_index_0 & read_index_mask; |
| read_index2 = read_index2 & read_index_mask; |
| |
| float sample1_lower = lower_wave_data[read_index_0]; |
| float sample2_lower = lower_wave_data[read_index2]; |
| float sample1_higher = higher_wave_data[read_index_0]; |
| float sample2_higher = higher_wave_data[read_index2]; |
| |
| // Linearly interpolate within each table (lower and higher). |
| double interpolation_factor = |
| static_cast<float>(virtual_read_index) - read_index_0; |
| sample_higher = (1 - interpolation_factor) * sample1_higher + |
| interpolation_factor * sample2_higher; |
| sample_lower = (1 - interpolation_factor) * sample1_lower + |
| interpolation_factor * sample2_lower; |
| |
| } else if (incr >= .16) { |
| // We're doing about 6 interpolation values between each wave |
| // table sample. Just use a 3-point Lagrange interpolator to get a |
| // better estimate than just linear. |
| // |
| // See 3-point formula in http://dlmf.nist.gov/3.3#ii |
| unsigned read_index[3]; |
| |
| for (int k = -1; k <= 1; ++k) { |
| read_index[k + 1] = (read_index_0 + k) & read_index_mask; |
| } |
| |
| double a[3]; |
| double t = virtual_read_index - read_index_0; |
| |
| a[0] = 0.5 * t * (t - 1); |
| a[1] = 1 - t * t; |
| a[2] = 0.5 * t * (t + 1); |
| |
| for (int k = 0; k < 3; ++k) { |
| sample_lower += a[k] * lower_wave_data[read_index[k]]; |
| sample_higher += a[k] * higher_wave_data[read_index[k]]; |
| } |
| } else { |
| // For everything else (more than 6 points per entry), we'll do a |
| // 5-point Lagrange interpolator. This is a trade-off between |
| // quality and speed. |
| // |
| // See 5-point formula in http://dlmf.nist.gov/3.3#ii |
| unsigned read_index[5]; |
| for (int k = -2; k <= 2; ++k) { |
| read_index[k + 2] = (read_index_0 + k) & read_index_mask; |
| } |
| |
| double a[5]; |
| double t = virtual_read_index - read_index_0; |
| double t2 = t * t; |
| |
| a[0] = t * (t2 - 1) * (t - 2) / 24; |
| a[1] = -t * (t - 1) * (t2 - 4) / 6; |
| a[2] = (t2 - 1) * (t2 - 4) / 4; |
| a[3] = -t * (t + 1) * (t2 - 4) / 6; |
| a[4] = t * (t2 - 1) * (t + 2) / 24; |
| |
| for (int k = 0; k < 5; ++k) { |
| sample_lower += a[k] * lower_wave_data[read_index[k]]; |
| sample_higher += a[k] * higher_wave_data[read_index[k]]; |
| } |
| } |
| |
| // Then interpolate between the two tables. |
| float sample = (1 - table_interpolation_factor) * sample_higher + |
| table_interpolation_factor * sample_lower; |
| return sample; |
| } |
| |
| void OscillatorHandler::Process(size_t frames_to_process) { |
| AudioBus* output_bus = Output(0).Bus(); |
| |
| if (!IsInitialized() || !output_bus->NumberOfChannels()) { |
| output_bus->Zero(); |
| return; |
| } |
| |
| DCHECK_LE(frames_to_process, phase_increments_.size()); |
| if (frames_to_process > phase_increments_.size()) |
| return; |
| |
| // The audio thread can't block on this lock, so we call tryLock() instead. |
| MutexTryLocker try_locker(process_lock_); |
| if (!try_locker.Locked()) { |
| // Too bad - the tryLock() failed. We must be in the middle of changing |
| // wave-tables. |
| output_bus->Zero(); |
| return; |
| } |
| |
| // We must access m_periodicWave only inside the lock. |
| if (!periodic_wave_.Get()) { |
| output_bus->Zero(); |
| return; |
| } |
| |
| size_t quantum_frame_offset; |
| size_t non_silent_frames_to_process; |
| double start_frame_offset; |
| |
| UpdateSchedulingInfo(frames_to_process, output_bus, quantum_frame_offset, |
| non_silent_frames_to_process, start_frame_offset); |
| |
| if (!non_silent_frames_to_process) { |
| output_bus->Zero(); |
| return; |
| } |
| |
| unsigned periodic_wave_size = periodic_wave_->PeriodicWaveSize(); |
| double inv_periodic_wave_size = 1.0 / periodic_wave_size; |
| |
| float* dest_p = output_bus->Channel(0)->MutableData(); |
| |
| DCHECK_LE(quantum_frame_offset, frames_to_process); |
| |
| // We keep virtualReadIndex double-precision since we're accumulating values. |
| double virtual_read_index = virtual_read_index_; |
| |
| float rate_scale = periodic_wave_->RateScale(); |
| float inv_rate_scale = 1 / rate_scale; |
| bool has_sample_accurate_values = |
| CalculateSampleAccuratePhaseIncrements(frames_to_process); |
| |
| float frequency = 0; |
| float* higher_wave_data = nullptr; |
| float* lower_wave_data = nullptr; |
| float table_interpolation_factor = 0; |
| |
| if (!has_sample_accurate_values) { |
| frequency = frequency_->Value(); |
| float detune = detune_->Value(); |
| float detune_scale = powf(2, detune / 1200); |
| frequency *= detune_scale; |
| periodic_wave_->WaveDataForFundamentalFrequency(frequency, lower_wave_data, |
| higher_wave_data, |
| table_interpolation_factor); |
| } |
| |
| float incr = frequency * rate_scale; |
| float* phase_increments = phase_increments_.Data(); |
| |
| unsigned read_index_mask = periodic_wave_size - 1; |
| |
| // Start rendering at the correct offset. |
| dest_p += quantum_frame_offset; |
| int n = non_silent_frames_to_process; |
| |
| // If startFrameOffset is not 0, that means the oscillator doesn't actually |
| // start at quantumFrameOffset, but just past that time. Adjust destP and n |
| // to reflect that, and adjust virtualReadIndex to start the value at |
| // startFrameOffset. |
| if (start_frame_offset > 0) { |
| ++dest_p; |
| --n; |
| virtual_read_index += (1 - start_frame_offset) * frequency * rate_scale; |
| DCHECK(virtual_read_index < periodic_wave_size); |
| } else if (start_frame_offset < 0) { |
| virtual_read_index = -start_frame_offset * frequency * rate_scale; |
| } |
| |
| while (n--) { |
| if (has_sample_accurate_values) { |
| incr = *phase_increments++; |
| |
| frequency = inv_rate_scale * incr; |
| periodic_wave_->WaveDataForFundamentalFrequency( |
| frequency, lower_wave_data, higher_wave_data, |
| table_interpolation_factor); |
| } |
| |
| float sample = DoInterpolation(virtual_read_index, fabs(incr), |
| read_index_mask, table_interpolation_factor, |
| lower_wave_data, higher_wave_data); |
| |
| *dest_p++ = sample; |
| |
| // Increment virtual read index and wrap virtualReadIndex into the range |
| // 0 -> periodicWaveSize. |
| virtual_read_index += incr; |
| virtual_read_index -= |
| floor(virtual_read_index * inv_periodic_wave_size) * periodic_wave_size; |
| } |
| |
| virtual_read_index_ = virtual_read_index; |
| |
| output_bus->ClearSilentFlag(); |
| } |
| |
| void OscillatorHandler::SetPeriodicWave(PeriodicWave* periodic_wave) { |
| DCHECK(IsMainThread()); |
| DCHECK(periodic_wave); |
| |
| // This synchronizes with process(). |
| MutexLocker process_locker(process_lock_); |
| periodic_wave_ = periodic_wave; |
| type_ = CUSTOM; |
| } |
| |
| bool OscillatorHandler::PropagatesSilence() const { |
| return !IsPlayingOrScheduled() || HasFinished() || !periodic_wave_.Get(); |
| } |
| |
| // ---------------------------------------------------------------- |
| |
| OscillatorNode::OscillatorNode(BaseAudioContext& context, |
| const String& oscillator_type, |
| PeriodicWave* wave_table) |
| : AudioScheduledSourceNode(context), |
| // Use musical pitch standard A440 as a default. |
| frequency_(AudioParam::Create(context, |
| kParamTypeOscillatorFrequency, |
| "Oscillator.frequency", |
| 440, |
| -context.sampleRate() / 2, |
| context.sampleRate() / 2)), |
| // Default to no detuning. |
| detune_(AudioParam::Create(context, |
| kParamTypeOscillatorDetune, |
| "Oscillator.detune", |
| 0)) { |
| SetHandler(OscillatorHandler::Create( |
| *this, context.sampleRate(), oscillator_type, wave_table, |
| frequency_->Handler(), detune_->Handler())); |
| } |
| |
| OscillatorNode* OscillatorNode::Create(BaseAudioContext& context, |
| const String& oscillator_type, |
| PeriodicWave* wave_table, |
| ExceptionState& exception_state) { |
| DCHECK(IsMainThread()); |
| |
| if (context.IsContextClosed()) { |
| context.ThrowExceptionForClosedState(exception_state); |
| return nullptr; |
| } |
| |
| return new OscillatorNode(context, oscillator_type, wave_table); |
| } |
| |
| OscillatorNode* OscillatorNode::Create(BaseAudioContext* context, |
| const OscillatorOptions& options, |
| ExceptionState& exception_state) { |
| if (options.type() == "custom" && !options.hasPeriodicWave()) { |
| exception_state.ThrowDOMException( |
| kInvalidStateError, |
| "A PeriodicWave must be specified if the type is set to \"custom\""); |
| return nullptr; |
| } |
| |
| OscillatorNode* node = |
| Create(*context, options.type(), options.periodicWave(), exception_state); |
| |
| if (!node) |
| return nullptr; |
| |
| node->HandleChannelOptions(options, exception_state); |
| |
| node->detune()->setValue(options.detune()); |
| node->frequency()->setValue(options.frequency()); |
| |
| return node; |
| } |
| |
| void OscillatorNode::Trace(blink::Visitor* visitor) { |
| visitor->Trace(frequency_); |
| visitor->Trace(detune_); |
| AudioScheduledSourceNode::Trace(visitor); |
| } |
| |
| OscillatorHandler& OscillatorNode::GetOscillatorHandler() const { |
| return static_cast<OscillatorHandler&>(Handler()); |
| } |
| |
| String OscillatorNode::type() const { |
| return GetOscillatorHandler().GetType(); |
| } |
| |
| void OscillatorNode::setType(const String& type, |
| ExceptionState& exception_state) { |
| GetOscillatorHandler().SetType(type, exception_state); |
| } |
| |
| AudioParam* OscillatorNode::frequency() { |
| return frequency_; |
| } |
| |
| AudioParam* OscillatorNode::detune() { |
| return detune_; |
| } |
| |
| void OscillatorNode::setPeriodicWave(PeriodicWave* wave) { |
| GetOscillatorHandler().SetPeriodicWave(wave); |
| } |
| |
| } // namespace blink |