/* * 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 #include #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::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(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(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 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(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(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(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(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 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]; alignas(16) float table_interpolation_factor[4]; 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::max()), 1200 * log2f(std::numeric_limits::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(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(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