| /* |
| * 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 <algorithm> |
| #include <limits> |
| |
| #include "build/build_config.h" |
| #include "third_party/blink/renderer/modules/webaudio/audio_node_output.h" |
| #include "third_party/blink/renderer/modules/webaudio/oscillator_node.h" |
| #include "third_party/blink/renderer/modules/webaudio/periodic_wave.h" |
| #include "third_party/blink/renderer/platform/audio/audio_utilities.h" |
| #include "third_party/blink/renderer/platform/audio/vector_math.h" |
| #include "third_party/blink/renderer/platform/bindings/enumeration_base.h" |
| #include "third_party/blink/renderer/platform/bindings/exception_state.h" |
| #include "third_party/blink/renderer/platform/heap/persistent.h" |
| #include "third_party/blink/renderer/platform/wtf/math_extras.h" |
| #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" |
| |
| namespace blink { |
| |
| // Breakpoints where we deicde to do linear interoplation, 3-point |
| // interpolation or 5-point interpolation. See DoInterpolation(). |
| constexpr float kInterpolate2Point = 0.3; |
| constexpr float kInterpolate3Point = 0.16; |
| |
| 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_(audio_utilities::kRenderQuantumFrames), |
| detune_values_(audio_utilities::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(DOMExceptionCode::kInvalidStateError, |
| "'type' cannot be set directly to " |
| "'custom'. Use setPeriodicWave() to " |
| "create a custom Oscillator type."); |
| } |
| } |
| |
| bool OscillatorHandler::SetType(uint8_t 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; |
| } |
| |
| // Convert the detune value (in cents) to a frequency scale multiplier: |
| // 2^(d/1200) |
| static float DetuneToFrequencyMultiplier(float detune_value) { |
| return std::exp2(detune_value / 1200); |
| } |
| |
| // Clamp the frequency value to lie with Nyquist frequency. For NaN, arbitrarily |
| // clamp to +Nyquist. |
| static void ClampFrequency(float* frequency, |
| int frames_to_process, |
| float nyquist) { |
| for (int k = 0; k < frames_to_process; ++k) { |
| float f = frequency[k]; |
| |
| if (std::isnan(f)) { |
| frequency[k] = nyquist; |
| } else { |
| frequency[k] = clampTo(f, -nyquist, nyquist); |
| } |
| } |
| } |
| |
| bool OscillatorHandler::CalculateSampleAccuratePhaseIncrements( |
| uint32_t frames_to_process) { |
| DCHECK_LE(frames_to_process, phase_increments_.size()); |
| DCHECK_LE(frames_to_process, detune_values_.size()); |
| |
| 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_.Lock()->RateScale(); |
| |
| if (frequency_->HasSampleAccurateValues() && frequency_->IsAudioRate()) { |
| 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_->FinalValue(); |
| final_scale *= frequency; |
| } |
| |
| if (detune_->HasSampleAccurateValues() && detune_->IsAudioRate()) { |
| 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; |
| vector_math::Vsmul(detune_values, 1, &k, detune_values, 1, |
| frames_to_process); |
| for (unsigned i = 0; i < frames_to_process; ++i) { |
| detune_values[i] = std::exp2(detune_values[i]); |
| } |
| |
| if (has_frequency_changes) { |
| // Multiply frequencies by detune scalings. |
| vector_math::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_->FinalValue(); |
| float detune_scale = DetuneToFrequencyMultiplier(detune); |
| final_scale *= detune_scale; |
| } |
| |
| if (has_sample_accurate_values) { |
| ClampFrequency(phase_increments, frames_to_process, |
| Context()->sampleRate() / 2); |
| // Convert from frequency to wavetable increment. |
| vector_math::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); |
| DCHECK(std::isfinite(virtual_read_index)); |
| |
| 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 >= kInterpolate2Point) { |
| // 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 >= kInterpolate3Point) { |
| // 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; |
| } |
| |
| #if !(defined(ARCH_CPU_X86_FAMILY) || defined(CPU_ARM_NEON)) |
| // Vector operations not supported, so there's nothing to do except return 0 and |
| // virtual_read_index. The scalar version will do the necessary processing. |
| std::tuple<int, double> OscillatorHandler::ProcessKRateVector( |
| int n, |
| float* dest_p, |
| double virtual_read_index, |
| float frequency, |
| float rate_scale) const { |
| DCHECK_GE(frequency * rate_scale, kInterpolate2Point); |
| return std::make_tuple(0, virtual_read_index); |
| } |
| #endif |
| |
| #if !(defined(ARCH_CPU_X86_FAMILY) || defined(CPU_ARM_NEON)) |
| double OscillatorHandler::ProcessARateVectorKernel( |
| float* dest_p, |
| double virtual_read_index, |
| const float* phase_increments, |
| unsigned periodic_wave_size, |
| const float* const lower_wave_data[4], |
| const float* const higher_wave_data[4], |
| const float table_interpolation_factor[4]) const { |
| double inv_periodic_wave_size = 1.0 / periodic_wave_size; |
| unsigned read_index_mask = periodic_wave_size - 1; |
| |
| for (int m = 0; m < 4; ++m) { |
| unsigned read_index_0 = static_cast<unsigned>(virtual_read_index); |
| |
| // 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[m][read_index_0]; |
| float sample2_lower = lower_wave_data[m][read_index2]; |
| float sample1_higher = higher_wave_data[m][read_index_0]; |
| float sample2_higher = higher_wave_data[m][read_index2]; |
| |
| // Linearly interpolate within each table (lower and higher). |
| double interpolation_factor = |
| static_cast<float>(virtual_read_index) - read_index_0; |
| // Doing linear interpolation via x0 + f*(x1-x0) gives slightly |
| // different results from (1-f)*x0 + f*x1, but requires fewer |
| // operations. This causes a very slight decrease in SNR (< 0.05 dB) in |
| // oscillator sweep tests. |
| float sample_higher = |
| sample1_higher + |
| interpolation_factor * (sample2_higher - sample1_higher); |
| float sample_lower = |
| sample1_lower + interpolation_factor * (sample2_lower - sample1_lower); |
| |
| // Then interpolate between the two tables. |
| float sample = sample_higher + table_interpolation_factor[m] * |
| (sample_lower - sample_higher); |
| |
| dest_p[m] = sample; |
| |
| // Increment virtual read index and wrap virtualReadIndex into the range |
| // 0 -> periodicWaveSize. |
| virtual_read_index += phase_increments[m]; |
| virtual_read_index -= |
| floor(virtual_read_index * inv_periodic_wave_size) * periodic_wave_size; |
| } |
| |
| return virtual_read_index; |
| } |
| #endif |
| |
| double OscillatorHandler::ProcessKRateScalar(int start, |
| int n, |
| float* dest_p, |
| double virtual_read_index, |
| float frequency, |
| float rate_scale) const { |
| auto periodic_wave = periodic_wave_.Lock(); |
| const unsigned periodic_wave_size = periodic_wave->PeriodicWaveSize(); |
| const double inv_periodic_wave_size = 1.0 / periodic_wave_size; |
| const unsigned read_index_mask = periodic_wave_size - 1; |
| |
| float* higher_wave_data = nullptr; |
| float* lower_wave_data = nullptr; |
| float table_interpolation_factor = 0; |
| |
| periodic_wave->WaveDataForFundamentalFrequency( |
| frequency, lower_wave_data, higher_wave_data, table_interpolation_factor); |
| |
| const float incr = frequency * rate_scale; |
| DCHECK_GE(incr, kInterpolate2Point); |
| |
| for (int k = start; k < n; ++k) { |
| // Get indices for the current and next sample, and contain them within the |
| // valid range |
| const unsigned read_index_0 = |
| static_cast<unsigned>(virtual_read_index) & read_index_mask; |
| const unsigned read_index_1 = (read_index_0 + 1) & read_index_mask; |
| |
| const float sample1_lower = lower_wave_data[read_index_0]; |
| const float sample2_lower = lower_wave_data[read_index_1]; |
| const float sample1_higher = higher_wave_data[read_index_0]; |
| const float sample2_higher = higher_wave_data[read_index_1]; |
| |
| // Linearly interpolate within each table (lower and higher). |
| const float interpolation_factor = |
| static_cast<float>(virtual_read_index) - read_index_0; |
| const float sample_higher = |
| sample1_higher + |
| interpolation_factor * (sample2_higher - sample1_higher); |
| const float sample_lower = |
| sample1_lower + interpolation_factor * (sample2_lower - sample1_lower); |
| |
| // Then interpolate between the two tables. |
| const float sample = sample_higher + table_interpolation_factor * |
| (sample_lower - sample_higher); |
| |
| dest_p[k] = 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; |
| } |
| |
| return virtual_read_index; |
| } |
| |
| double OscillatorHandler::ProcessKRate(int n, |
| float* dest_p, |
| double virtual_read_index) const { |
| auto periodic_wave = periodic_wave_.Lock(); |
| const unsigned periodic_wave_size = periodic_wave->PeriodicWaveSize(); |
| const double inv_periodic_wave_size = 1.0 / periodic_wave_size; |
| const unsigned read_index_mask = periodic_wave_size - 1; |
| |
| float* higher_wave_data = nullptr; |
| float* lower_wave_data = nullptr; |
| float table_interpolation_factor = 0; |
| |
| float frequency = frequency_->FinalValue(); |
| const float detune_scale = DetuneToFrequencyMultiplier(detune_->FinalValue()); |
| frequency *= detune_scale; |
| ClampFrequency(&frequency, 1, Context()->sampleRate() / 2); |
| periodic_wave->WaveDataForFundamentalFrequency( |
| frequency, lower_wave_data, higher_wave_data, table_interpolation_factor); |
| |
| const float rate_scale = periodic_wave->RateScale(); |
| const float incr = frequency * rate_scale; |
| |
| if (incr >= kInterpolate2Point) { |
| int k; |
| double v_index = virtual_read_index; |
| |
| std::tie(k, v_index) = |
| ProcessKRateVector(n, dest_p, v_index, frequency, rate_scale); |
| |
| if (k < n) { |
| // In typical cases, this won't be run because the number of frames is 128 |
| // so the vector version will process all the samples. |
| v_index = |
| ProcessKRateScalar(k, n, dest_p, v_index, frequency, rate_scale); |
| } |
| |
| // Recompute to reduce round-off introduced when processing the samples |
| // above. |
| virtual_read_index += n * incr; |
| virtual_read_index -= |
| floor(virtual_read_index * inv_periodic_wave_size) * periodic_wave_size; |
| } else { |
| for (int k = 0; k < n; ++k) { |
| 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; |
| } |
| } |
| |
| return virtual_read_index; |
| } |
| |
| std::tuple<int, double> OscillatorHandler::ProcessARateVector( |
| int n, |
| float* destination, |
| double virtual_read_index, |
| const float* phase_increments) const { |
| auto periodic_wave = periodic_wave_.Lock(); |
| float rate_scale = periodic_wave->RateScale(); |
| float inv_rate_scale = 1 / rate_scale; |
| unsigned periodic_wave_size = periodic_wave->PeriodicWaveSize(); |
| double inv_periodic_wave_size = 1.0 / periodic_wave_size; |
| unsigned read_index_mask = periodic_wave_size - 1; |
| |
| float* higher_wave_data[4]; |
| float* lower_wave_data[4]; |
| float table_interpolation_factor[4] __attribute__((aligned(16))); |
| |
| int k = 0; |
| int n_loops = n / 4; |
| |
| for (int loop = 0; loop < n_loops; ++loop, k += 4) { |
| bool is_big_increment = true; |
| float frequency[4]; |
| |
| for (int m = 0; m < 4; ++m) { |
| float phase_incr = phase_increments[k + m]; |
| is_big_increment = |
| is_big_increment && (fabs(phase_incr) >= kInterpolate2Point); |
| frequency[m] = inv_rate_scale * phase_incr; |
| } |
| |
| periodic_wave->WaveDataForFundamentalFrequency(frequency, lower_wave_data, |
| higher_wave_data, |
| table_interpolation_factor); |
| |
| // If all the phase increments are large enough, we can use linear |
| // interpolation with a possibly vectorized implementation. If not, we need |
| // to call DoInterpolation to handle it correctly. |
| if (is_big_increment) { |
| virtual_read_index = ProcessARateVectorKernel( |
| destination + k, virtual_read_index, phase_increments + k, |
| periodic_wave_size, lower_wave_data, higher_wave_data, |
| table_interpolation_factor); |
| } else { |
| for (int m = 0; m < 4; ++m) { |
| float sample = |
| DoInterpolation(virtual_read_index, fabs(phase_increments[k + m]), |
| read_index_mask, table_interpolation_factor[m], |
| lower_wave_data[m], higher_wave_data[m]); |
| |
| destination[k + m] = sample; |
| |
| // Increment virtual read index and wrap virtualReadIndex into the range |
| // 0 -> periodicWaveSize. |
| virtual_read_index += phase_increments[k + m]; |
| virtual_read_index -= |
| floor(virtual_read_index * inv_periodic_wave_size) * |
| periodic_wave_size; |
| } |
| } |
| } |
| |
| return std::make_tuple(k, virtual_read_index); |
| } |
| |
| double OscillatorHandler::ProcessARateScalar( |
| int k, |
| int n, |
| float* destination, |
| double virtual_read_index, |
| const float* phase_increments) const { |
| auto periodic_wave = periodic_wave_.Lock(); |
| float rate_scale = periodic_wave->RateScale(); |
| float inv_rate_scale = 1 / rate_scale; |
| unsigned periodic_wave_size = periodic_wave->PeriodicWaveSize(); |
| double inv_periodic_wave_size = 1.0 / periodic_wave_size; |
| unsigned read_index_mask = periodic_wave_size - 1; |
| |
| float* higher_wave_data = nullptr; |
| float* lower_wave_data = nullptr; |
| float table_interpolation_factor = 0; |
| |
| for (int m = k; m < n; ++m) { |
| float incr = phase_increments[m]; |
| |
| float 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); |
| |
| destination[m] = 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; |
| } |
| |
| return virtual_read_index; |
| } |
| |
| double OscillatorHandler::ProcessARate(int n, |
| float* destination, |
| double virtual_read_index, |
| float* phase_increments) const { |
| int frames_processed = 0; |
| |
| std::tie(frames_processed, virtual_read_index) = |
| ProcessARateVector(n, destination, virtual_read_index, phase_increments); |
| |
| virtual_read_index = ProcessARateScalar(frames_processed, n, destination, |
| virtual_read_index, phase_increments); |
| |
| return virtual_read_index; |
| } |
| |
| void OscillatorHandler::Process(uint32_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()); |
| |
| // 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; |
| } |
| |
| auto periodic_wave = periodic_wave_.Lock(); |
| |
| // We must access m_periodicWave only inside the lock. |
| if (!periodic_wave.Get()) { |
| output_bus->Zero(); |
| return; |
| } |
| |
| size_t quantum_frame_offset; |
| uint32_t non_silent_frames_to_process; |
| double start_frame_offset; |
| |
| std::tie(quantum_frame_offset, non_silent_frames_to_process, |
| start_frame_offset) = |
| UpdateSchedulingInfo(frames_to_process, output_bus); |
| |
| if (!non_silent_frames_to_process) { |
| output_bus->Zero(); |
| return; |
| } |
| |
| unsigned periodic_wave_size = periodic_wave->PeriodicWaveSize(); |
| |
| 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(); |
| 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_->FinalValue(); |
| float detune = detune_->FinalValue(); |
| float detune_scale = DetuneToFrequencyMultiplier(detune); |
| frequency *= detune_scale; |
| ClampFrequency(&frequency, 1, Context()->sampleRate() / 2); |
| periodic_wave->WaveDataForFundamentalFrequency(frequency, lower_wave_data, |
| higher_wave_data, |
| table_interpolation_factor); |
| } |
| |
| float* phase_increments = phase_increments_.Data(); |
| |
| // 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; |
| } |
| |
| if (has_sample_accurate_values) { |
| virtual_read_index = |
| ProcessARate(n, dest_p, virtual_read_index, phase_increments); |
| } else { |
| virtual_read_index = ProcessKRate(n, dest_p, virtual_read_index); |
| } |
| |
| 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_; |
| } |
| |
| void OscillatorHandler::HandleStoppableSourceNode() { |
| double now = Context()->currentTime(); |
| |
| MutexTryLocker try_locker(process_lock_); |
| if (!try_locker.Locked()) { |
| // Can't get the lock, so just return. It's ok to handle these at a later |
| // time; this was just a hint anyway so stopping them a bit later is ok. |
| return; |
| } |
| |
| // If we know the end time, and the source was started and the current time is |
| // definitely past the end time, we can stop this node. (This handles the |
| // case where the this source is not connected to the destination and we want |
| // to stop it.) |
| if (end_time_ != kUnknownTime && IsPlayingOrScheduled() && |
| now >= end_time_ + kExtraStopFrames / Context()->sampleRate()) { |
| Finish(); |
| } |
| } |
| |
| // ---------------------------------------------------------------- |
| |
| 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, |
| Uuid(), |
| AudioParamHandler::kParamTypeOscillatorFrequency, |
| 440, |
| AudioParamHandler::AutomationRate::kAudio, |
| AudioParamHandler::AutomationRateMode::kVariable, |
| -context.sampleRate() / 2, |
| context.sampleRate() / 2)), |
| // Default to no detuning. |
| detune_( |
| AudioParam::Create(context, |
| Uuid(), |
| AudioParamHandler::kParamTypeOscillatorDetune, |
| 0, |
| AudioParamHandler::AutomationRate::kAudio, |
| AudioParamHandler::AutomationRateMode::kVariable, |
| -1200 * log2f(std::numeric_limits<float>::max()), |
| 1200 * log2f(std::numeric_limits<float>::max()))), |
| periodic_wave_(wave_table) { |
| 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()); |
| |
| return MakeGarbageCollected<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( |
| DOMExceptionCode::kInvalidStateError, |
| "A PeriodicWave must be specified if the type is set to \"custom\""); |
| return nullptr; |
| } |
| |
| // TODO(crbug.com/1070871): Use periodicWaveOr(nullptr). |
| OscillatorNode* node = |
| Create(*context, IDLEnumAsString(options->type()), |
| options->hasPeriodicWave() ? options->periodicWave() : nullptr, |
| 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(Visitor* visitor) const { |
| visitor->Trace(frequency_); |
| visitor->Trace(detune_); |
| visitor->Trace(periodic_wave_); |
| 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) { |
| periodic_wave_ = wave; |
| GetOscillatorHandler().SetPeriodicWave(wave); |
| } |
| |
| void OscillatorNode::ReportDidCreate() { |
| GraphTracer().DidCreateAudioNode(this); |
| GraphTracer().DidCreateAudioParam(detune_); |
| GraphTracer().DidCreateAudioParam(frequency_); |
| } |
| |
| void OscillatorNode::ReportWillBeDestroyed() { |
| GraphTracer().WillDestroyAudioParam(detune_); |
| GraphTracer().WillDestroyAudioParam(frequency_); |
| GraphTracer().WillDestroyAudioNode(this); |
| } |
| |
| } // namespace blink |