use block::{Chunk, Tick};
use node::{AudioNodeEngine, AudioScheduledSourceNodeMessage, BlockInfo, OnEndedCallback};
use node::{AudioNodeType, ChannelInfo, ShouldPlay};
use num_traits::cast::NumCast;
use param::{Param, ParamType};
#[derive(Clone, Debug)]
pub struct PeriodicWaveOptions {
}
#[derive(Clone, Debug)]
pub enum OscillatorType {
Sine,
Square,
Sawtooth,
Triangle,
Custom,
}
#[derive(Clone, Debug)]
pub struct OscillatorNodeOptions {
pub oscillator_type: OscillatorType,
pub freq: f32,
pub detune: f32,
pub periodic_wave_options: Option<PeriodicWaveOptions>,
}
impl Default for OscillatorNodeOptions {
fn default() -> Self {
OscillatorNodeOptions {
oscillator_type: OscillatorType::Sine,
freq: 440.,
detune: 0.,
periodic_wave_options: None,
}
}
}
#[derive(Clone, Debug)]
pub enum OscillatorNodeMessage {
SetOscillatorType(OscillatorType),
}
#[derive(AudioScheduledSourceNode, AudioNodeCommon)]
pub(crate) struct OscillatorNode {
channel_info: ChannelInfo,
oscillator_type: OscillatorType,
frequency: Param,
detune: Param,
phase: f64,
start_at: Option<Tick>,
stop_at: Option<Tick>,
onended_callback: Option<OnEndedCallback>,
}
impl OscillatorNode {
pub fn new(options: OscillatorNodeOptions, channel_info: ChannelInfo) -> Self {
Self {
channel_info,
oscillator_type: options.oscillator_type,
frequency: Param::new(options.freq.into()),
detune: Param::new(options.detune.into()),
phase: 0.,
start_at: None,
stop_at: None,
onended_callback: None,
}
}
pub fn update_parameters(&mut self, info: &BlockInfo, tick: Tick) -> bool {
self.frequency.update(info, tick)
}
fn handle_oscillator_message(&mut self, message: OscillatorNodeMessage, _sample_rate: f32) {
match message {
OscillatorNodeMessage::SetOscillatorType(o) => {
self.oscillator_type = o;
}
}
}
}
impl AudioNodeEngine for OscillatorNode {
fn node_type(&self) -> AudioNodeType {
AudioNodeType::OscillatorNode
}
fn process(&mut self, mut inputs: Chunk, info: &BlockInfo) -> Chunk {
use std::f64::consts::PI;
debug_assert!(inputs.len() == 0);
inputs.blocks.push(Default::default());
let (start_at, stop_at) = match self.should_play_at(info.frame) {
ShouldPlay::No => {
return inputs;
}
ShouldPlay::Between(start, end) => (start, end),
};
{
inputs.blocks[0].explicit_silence();
let mut iter = inputs.blocks[0].iter();
let vol: f32 = 1.0;
let sample_rate = info.sample_rate as f64;
let two_pi = 2.0 * PI;
let mut step = two_pi * self.frequency.value() as f64 / sample_rate;
while let Some(mut frame) = iter.next() {
let tick = frame.tick();
if tick < start_at {
continue;
} else if tick > stop_at {
break;
}
if self.update_parameters(info, tick) {
step = two_pi * self.frequency.value() as f64 / sample_rate;
}
let mut value = vol;
match self.oscillator_type {
OscillatorType::Sine => {
value = vol * f32::sin(NumCast::from(self.phase).unwrap());
}
OscillatorType::Square => {
if self.phase >= PI && self.phase < two_pi {
value = vol * 1.0;
} else if self.phase > 0.0 && self.phase < PI {
value = vol * (-1.0);
}
}
OscillatorType::Sawtooth => {
value = vol * ((self.phase as f64) / (PI)) as f32;
}
OscillatorType::Triangle => {
if self.phase >= 0. && self.phase < PI / 2. {
value = vol * 2.0 * ((self.phase as f64) / (PI)) as f32;
} else if self.phase >= PI / 2. && self.phase < PI {
value =
vol * (1. - (((self.phase as f64) - (PI / 2.)) * (2. / PI)) as f32);
} else if self.phase >= PI && self.phase < (3. * PI / 2.) {
value = vol
* -1.
* (1. - (((self.phase as f64) - (PI / 2.)) * (2. / PI)) as f32);
} else if self.phase >= 3. * PI / 2. && self.phase < 2. * PI {
value = vol * (-2.0) * ((self.phase as f64) / (PI)) as f32;
}
}
OscillatorType::Custom => {}
}
frame.mutate_with(|sample, _| *sample = value);
self.phase += step;
if self.phase >= two_pi {
self.phase -= two_pi;
}
}
}
inputs
}
fn input_count(&self) -> u32 {
0
}
fn get_param(&mut self, id: ParamType) -> &mut Param {
match id {
ParamType::Frequency => &mut self.frequency,
ParamType::Detune => &mut self.detune,
_ => panic!("Unknown param {:?} for OscillatorNode", id),
}
}
make_message_handler!(
AudioScheduledSourceNode: handle_source_node_message,
OscillatorNode: handle_oscillator_message
);
}