parent
							
								
									4b697f6664
								
							
						
					
					
						commit
						03ea8cef87
					
				@ -1,152 +0,0 @@ | 
				
			||||
use std::sync::Arc; | 
				
			||||
 | 
				
			||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; | 
				
			||||
use std::ops::{Add, AddAssign, Sub}; | 
				
			||||
use parking_lot::RwLock; | 
				
			||||
use std::time::Duration; | 
				
			||||
use std::thread; | 
				
			||||
use cpal::SampleFormat; | 
				
			||||
use crate::beep::osc::{Instrument, Oscillator}; | 
				
			||||
 | 
				
			||||
pub mod osc; | 
				
			||||
 | 
				
			||||
struct AudioContext { | 
				
			||||
    device: cpal::Device, | 
				
			||||
    sample_format: SampleFormat, | 
				
			||||
    config: cpal::StreamConfig | 
				
			||||
} | 
				
			||||
 | 
				
			||||
fn al_init() -> Result<AudioContext, anyhow::Error> { | 
				
			||||
    let host = cpal::default_host(); | 
				
			||||
 | 
				
			||||
    let device = host.default_output_device() | 
				
			||||
        .ok_or_else(|| anyhow::anyhow!("No audio output device!"))?; | 
				
			||||
 | 
				
			||||
    let config = device.default_output_config()?; | 
				
			||||
 | 
				
			||||
    Ok(AudioContext { | 
				
			||||
        device, | 
				
			||||
        sample_format: config.sample_format(), | 
				
			||||
        config: config.into() | 
				
			||||
    }) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
pub fn beep() -> Result<(), anyhow::Error> { | 
				
			||||
    let ac = al_init()?; | 
				
			||||
 | 
				
			||||
    match ac.sample_format { | 
				
			||||
        cpal::SampleFormat::F32 => run::<f32>(&ac.device, &ac.config.into())?, | 
				
			||||
        cpal::SampleFormat::I16 => run::<i16>(&ac.device, &ac.config.into())?, | 
				
			||||
        cpal::SampleFormat::U16 => run::<u16>(&ac.device, &ac.config.into())?, | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    Ok(()) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
 | 
				
			||||
fn run<T>(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error> | 
				
			||||
    where | 
				
			||||
        T: cpal::Sample, | 
				
			||||
{ | 
				
			||||
    let sample_rate = config.sample_rate.0 as f32; | 
				
			||||
    let channels = config.channels as usize; | 
				
			||||
 | 
				
			||||
    println!("SR={}",sample_rate); | 
				
			||||
 | 
				
			||||
    // Produce a sinusoid of maximum amplitude.
 | 
				
			||||
 | 
				
			||||
    let mut ins = Instrument::new(vec![ | 
				
			||||
        Oscillator::new(440.0, 1.0, sample_rate), | 
				
			||||
        //Oscillator::new(441.0, 1.0, sample_rate),
 | 
				
			||||
    ], sample_rate); | 
				
			||||
 | 
				
			||||
    let instrument = Arc::new(parking_lot::RwLock::new(ins)); | 
				
			||||
 | 
				
			||||
    let err_fn = |err| eprintln!("an error occurred on stream: {}", err); | 
				
			||||
 | 
				
			||||
    let m_instrument = instrument.clone(); | 
				
			||||
    let stream = device.build_output_stream( | 
				
			||||
        config, | 
				
			||||
        move |data: &mut [T], _: &cpal::OutputCallbackInfo| { | 
				
			||||
            write_data(data, channels, &m_instrument) | 
				
			||||
        }, | 
				
			||||
        err_fn, | 
				
			||||
    )?; | 
				
			||||
    stream.play()?; | 
				
			||||
 | 
				
			||||
    // instrument.write().normalize(false);
 | 
				
			||||
    // instrument.write().set_amp(1.0);
 | 
				
			||||
    // thread::sleep(Duration::from_millis(500));
 | 
				
			||||
    //
 | 
				
			||||
    // instrument.write().set_amp(0.0);
 | 
				
			||||
    // thread::sleep(Duration::from_millis(250));
 | 
				
			||||
 | 
				
			||||
    instrument.write().set_amp(1.0); | 
				
			||||
    instrument.write().normalize(true); | 
				
			||||
    thread::sleep(Duration::from_millis(500)); | 
				
			||||
 | 
				
			||||
    instrument.write().set_amp(0.0); | 
				
			||||
    thread::sleep(Duration::from_millis(250)); | 
				
			||||
 | 
				
			||||
    instrument.write().set_amp(0.5); | 
				
			||||
    thread::sleep(Duration::from_millis(500)); | 
				
			||||
 | 
				
			||||
    instrument.write().set_amp(0.0); | 
				
			||||
    thread::sleep(Duration::from_millis(250)); | 
				
			||||
 | 
				
			||||
    instrument.write().fade_amp(1.0, Duration::from_millis(10)); | 
				
			||||
    instrument.write().waveforms[0].fade_balance(-1.0, Duration::from_millis(10)); | 
				
			||||
    thread::sleep(Duration::from_millis(500)); | 
				
			||||
    instrument.write().fade_amp(0.0, Duration::from_millis(10)); | 
				
			||||
    thread::sleep(Duration::from_millis(500)); | 
				
			||||
 | 
				
			||||
 | 
				
			||||
    /* | 
				
			||||
 | 
				
			||||
    //instrument.write().set_amp_fade(0.0, 4.0);
 | 
				
			||||
 | 
				
			||||
    thread::sleep(Duration::from_millis(1000)); | 
				
			||||
    instrument.write().waveforms[1].fade_amp(0.0, Duration::from_millis(500)); | 
				
			||||
    thread::sleep(Duration::from_millis(500)); | 
				
			||||
 | 
				
			||||
    instrument.write().waveforms[0].fade_balance(-1.0, Duration::from_millis(1000)); | 
				
			||||
    thread::sleep(Duration::from_millis(1000)); | 
				
			||||
    instrument.write().waveforms[0].fade_balance(1.0, Duration::from_millis(2000)); | 
				
			||||
    instrument.write().waveforms[0].fade_freq(880.0, Duration::from_millis(1000)); | 
				
			||||
    thread::sleep(Duration::from_millis(2000)); | 
				
			||||
    instrument.write().waveforms[0].fade_balance(-1.0, Duration::from_millis(1000)); | 
				
			||||
    instrument.write().waveforms[0].fade_amp(0.0, Duration::from_millis(1000)); | 
				
			||||
    thread::sleep(Duration::from_millis(1000)); | 
				
			||||
*/ | 
				
			||||
 | 
				
			||||
    // for _ in 0..10
 | 
				
			||||
    // {
 | 
				
			||||
    //     instrument.write().harmonics[0].set_balance_fade(-1.0, 0.1);
 | 
				
			||||
    //     thread::sleep(Duration::from_millis(100));
 | 
				
			||||
    //     instrument.write().harmonics[0].set_balance_fade(1.0, 0.1);
 | 
				
			||||
    //     thread::sleep(Duration::from_millis(100));
 | 
				
			||||
    // }
 | 
				
			||||
    //
 | 
				
			||||
    // instrument.write().harmonics[0].set_balance_fade(0.0, 0.5);
 | 
				
			||||
    // instrument.write().harmonics[0].set_amp_fade(0.0, 0.05);
 | 
				
			||||
    // thread::sleep(Duration::from_millis(200));
 | 
				
			||||
 | 
				
			||||
    Ok(()) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
fn write_data<T>(output: &mut [T], channels: usize, instrument: &Arc<RwLock<Instrument>>) | 
				
			||||
    where | 
				
			||||
        T: cpal::Sample, | 
				
			||||
{ | 
				
			||||
    let mut instrument = instrument.write(); | 
				
			||||
    for frame in output.chunks_mut(channels) { | 
				
			||||
        let sample = instrument.sample(); | 
				
			||||
 | 
				
			||||
        if channels == 1 { | 
				
			||||
            frame[0] = cpal::Sample::from::<f32>(&((sample.left + sample.right) / 2.0)); | 
				
			||||
        } else { | 
				
			||||
            frame[0] = cpal::Sample::from::<f32>(&sample.left); | 
				
			||||
            frame[1] = cpal::Sample::from::<f32>(&sample.right); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,18 @@ | 
				
			||||
/// Work-around for `f32::clamp()` being unstable
 | 
				
			||||
pub(crate) trait StableClamp { | 
				
			||||
    fn clamp_(self, min: f32, max: f32) -> f32; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl StableClamp for f32 { | 
				
			||||
    fn clamp_(self, min: f32, max: f32) -> f32 { | 
				
			||||
        assert!(min <= max); | 
				
			||||
        let mut x = self; | 
				
			||||
        if x < min { | 
				
			||||
            x = min; | 
				
			||||
        } | 
				
			||||
        if x > max { | 
				
			||||
            x = max; | 
				
			||||
        } | 
				
			||||
        x | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,145 @@ | 
				
			||||
use crate::beep::osc::HarmonicOscillator; | 
				
			||||
use crate::beep::tween::{Tween, GainScale}; | 
				
			||||
use std::time::Duration; | 
				
			||||
use crate::beep::sample::StereoSample; | 
				
			||||
use crate::beep::hack::StableClamp; | 
				
			||||
 | 
				
			||||
/// Waveform generator with multiple frequencies / waveforms
 | 
				
			||||
#[derive(Clone,Debug)] | 
				
			||||
pub struct Instrument { | 
				
			||||
    /// Waveforms to combine to produce the final sound
 | 
				
			||||
    pub waveforms: Vec<HarmonicOscillator>, | 
				
			||||
    /// Balance fader -1..1
 | 
				
			||||
    balance: Tween, | 
				
			||||
    /// 0-1 left gain
 | 
				
			||||
    pub(crate) ratio_left : f32, | 
				
			||||
    /// 0-1 right gain
 | 
				
			||||
    pub(crate) ratio_right : f32, | 
				
			||||
    /// Master gain
 | 
				
			||||
    gain: Tween, | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl Instrument { | 
				
			||||
    pub fn new(mut waveforms: Vec<HarmonicOscillator>, sample_rate: f32) -> Self { | 
				
			||||
        for (n, o) in waveforms.iter_mut().enumerate() { | 
				
			||||
            o.set_sample_rate(sample_rate); | 
				
			||||
            o.set_freq_imm(sample_rate * (n as f32 + 1.0)); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        let mut o = Self { | 
				
			||||
            waveforms, | 
				
			||||
            balance: Tween { | 
				
			||||
                actual: 0.0, | 
				
			||||
                target: 0.0, | 
				
			||||
                min: -1.0, | 
				
			||||
                max: 1.0, | 
				
			||||
                step: None, | 
				
			||||
                sample_rate | 
				
			||||
            }, | 
				
			||||
            ratio_left: 0.7, | 
				
			||||
            ratio_right: 0.7, | 
				
			||||
            gain: Tween { | 
				
			||||
                actual: 0.0, | 
				
			||||
                target: 0.0, | 
				
			||||
                step: None, | 
				
			||||
                min: 0.0, | 
				
			||||
                max: 1.0, | 
				
			||||
                sample_rate | 
				
			||||
            } | 
				
			||||
        }; | 
				
			||||
        o.update_side_ratios(); | 
				
			||||
        o | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Update the pre-computed channel gain variables
 | 
				
			||||
    pub fn update_side_ratios(&mut self) { | 
				
			||||
        let angle = ((1.0 + self.balance.actual) / 2.0) * std::f32::consts::FRAC_PI_2; | 
				
			||||
        self.ratio_left = angle.sin(); | 
				
			||||
        self.ratio_right = angle.cos(); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Get amplitude
 | 
				
			||||
    pub fn get_amp(&self) -> f32 { | 
				
			||||
        self.gain.actual() | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Set amplitude (0..1), change at the end of the current waveform to reduce popping
 | 
				
			||||
    pub fn set_amp(&mut self, amp : f32) { | 
				
			||||
        self.gain.set_at_crossover(amp); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Set amplitude (0..1) immediate
 | 
				
			||||
    pub fn set_amp_imm(&mut self, amp : f32) { | 
				
			||||
        self.gain.set_immediate(amp); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Fade amplitude over a given time
 | 
				
			||||
    pub fn fade_amp(&mut self, amp : f32, time: Duration) { | 
				
			||||
        self.gain.fade(amp, time); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Get balance value (-1..1)
 | 
				
			||||
    pub fn get_balance(&self) -> f32 { | 
				
			||||
        self.balance.actual | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Set balance (-1..1), change at the end of the current waveform to reduce popping
 | 
				
			||||
    pub fn set_balance(&mut self, balance : f32) { | 
				
			||||
        self.balance.set_at_crossover(balance); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Set balance (-1..1) immediate
 | 
				
			||||
    pub fn set_balance_imm(&mut self, balance : f32) { | 
				
			||||
        self.balance.set_immediate(balance); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Fade to balance (-1..1) over a given time
 | 
				
			||||
    pub fn fade_balance(&mut self, balance : f32, time: Duration) { | 
				
			||||
        self.balance.fade(balance, time); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn get_peak_amplitude(&self) -> f32 { | 
				
			||||
        self.waveforms.iter() | 
				
			||||
                .fold((0.0), |acc, item| acc + item.gain.actual) | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Get base frequency value (1..22050)
 | 
				
			||||
    pub fn get_freq(&self) -> f32 { | 
				
			||||
        self.waveforms[0].get_freq() // TODO error checking
 | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Set base and harmonics frequency (1..22050), change at the end of the current waveform to reduce popping
 | 
				
			||||
    pub fn set_freq(&mut self, freq : f32) { | 
				
			||||
        let mut f = freq; | 
				
			||||
        self.waveforms.iter_mut().for_each(|w| { | 
				
			||||
            w.set_freq(f); | 
				
			||||
        }); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Set base and harmonics frequency (1..22050) immediate
 | 
				
			||||
    pub fn set_freq_imm(&mut self, freq : f32) { | 
				
			||||
        let mut f = freq; | 
				
			||||
        self.waveforms.iter_mut().for_each(|w| { | 
				
			||||
            w.set_freq_imm(f); | 
				
			||||
        }); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Fade base and harmonics to frequency (1..22050) over a given time
 | 
				
			||||
    pub fn fade_freq(&mut self, freq : f32, time: Duration) { | 
				
			||||
        let mut f = freq; | 
				
			||||
        self.waveforms.iter_mut().for_each(|w| { | 
				
			||||
            w.fade_freq(f, time); | 
				
			||||
        }); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Get a raw sample (with panning)
 | 
				
			||||
    pub fn sample(&mut self) -> f32 { | 
				
			||||
        self.balance.tick(); | 
				
			||||
        self.gain.tick(); | 
				
			||||
 | 
				
			||||
        self.waveforms.iter_mut() | 
				
			||||
            .fold(0.0, |mut acc, item| { | 
				
			||||
                acc + item.sample() | 
				
			||||
            }) * self.gain.actual | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,355 @@ | 
				
			||||
use std::sync::Arc; | 
				
			||||
 | 
				
			||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; | 
				
			||||
use std::ops::{Add, AddAssign, Sub}; | 
				
			||||
use parking_lot::RwLock; | 
				
			||||
use std::time::Duration; | 
				
			||||
use std::{thread, panic}; | 
				
			||||
use cpal::SampleFormat; | 
				
			||||
use crate::beep::instrument::Instrument; | 
				
			||||
use crate::beep::orchestra::Orchestra; | 
				
			||||
use crate::beep::osc::HarmonicOscillator; | 
				
			||||
use std::panic::UnwindSafe; | 
				
			||||
 | 
				
			||||
mod hack; | 
				
			||||
pub mod sample; | 
				
			||||
pub mod tween; | 
				
			||||
pub mod osc; | 
				
			||||
pub mod instrument; | 
				
			||||
pub mod orchestra; | 
				
			||||
 | 
				
			||||
struct AudioContext { | 
				
			||||
    device: cpal::Device, | 
				
			||||
    sample_format: SampleFormat, | 
				
			||||
    config: cpal::StreamConfig | 
				
			||||
} | 
				
			||||
 | 
				
			||||
fn al_init() -> Result<AudioContext, anyhow::Error> { | 
				
			||||
    let host = cpal::default_host(); | 
				
			||||
 | 
				
			||||
    let device = host.default_output_device() | 
				
			||||
        .ok_or_else(|| anyhow::anyhow!("No audio output device!"))?; | 
				
			||||
 | 
				
			||||
    let config = device.default_output_config()?; | 
				
			||||
 | 
				
			||||
    Ok(AudioContext { | 
				
			||||
        device, | 
				
			||||
        sample_format: config.sample_format(), | 
				
			||||
        config: config.into() | 
				
			||||
    }) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
pub fn beep() -> Result<(), anyhow::Error> { | 
				
			||||
    let ac = al_init()?; | 
				
			||||
 | 
				
			||||
    match ac.sample_format { | 
				
			||||
        cpal::SampleFormat::F32 => run::<f32>(&ac.device, &ac.config.into())?, | 
				
			||||
        cpal::SampleFormat::I16 => run::<i16>(&ac.device, &ac.config.into())?, | 
				
			||||
        cpal::SampleFormat::U16 => run::<u16>(&ac.device, &ac.config.into())?, | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    Ok(()) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
 | 
				
			||||
fn run<T>(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error> | 
				
			||||
    where | 
				
			||||
        T: cpal::Sample, | 
				
			||||
{ | 
				
			||||
    let sr = config.sample_rate.0 as f32; | 
				
			||||
    let channels = config.channels as usize; | 
				
			||||
 | 
				
			||||
    println!("SR={}", sr); | 
				
			||||
 | 
				
			||||
    // Produce a sinusoid of maximum amplitude.
 | 
				
			||||
 | 
				
			||||
    let ins1 = Instrument::new(vec![ | 
				
			||||
        HarmonicOscillator::new(1.0, 1.0), | 
				
			||||
        HarmonicOscillator::new(0.5, 5.0), | 
				
			||||
    ], sr); | 
				
			||||
 | 
				
			||||
    let ins2 = Instrument::new(vec![ | 
				
			||||
        HarmonicOscillator::new(1.0, 1.0), | 
				
			||||
        HarmonicOscillator::new(0.5, 5.0), | 
				
			||||
    ], sr); | 
				
			||||
 | 
				
			||||
    let mut orch = Orchestra::new(vec![ins1, ins2], sr); | 
				
			||||
    orch.normalize(true); | 
				
			||||
 | 
				
			||||
    let handle = Arc::new(parking_lot::RwLock::new(orch)); | 
				
			||||
 | 
				
			||||
    let err_fn = |err| eprintln!("an error occurred on stream: {}", err); | 
				
			||||
 | 
				
			||||
    let m_handle = handle.clone(); | 
				
			||||
    let stream = device.build_output_stream( | 
				
			||||
        config, | 
				
			||||
        move |data: &mut [T], _: &cpal::OutputCallbackInfo| { | 
				
			||||
            write_data(data, channels, &m_handle) | 
				
			||||
        }, | 
				
			||||
        err_fn, | 
				
			||||
    )?; | 
				
			||||
    stream.play()?; | 
				
			||||
 | 
				
			||||
    const C3 : i32 = 28; | 
				
			||||
    const C3X : i32 = 29; | 
				
			||||
    const D3 : i32 = 30; | 
				
			||||
    const D3X : i32 = 31; | 
				
			||||
    const E3 : i32 = 32; | 
				
			||||
    const F3 : i32 = 33; | 
				
			||||
    const F3X : i32 = 34; | 
				
			||||
    const G3 : i32 = 35; | 
				
			||||
    const G3X : i32 = 36; | 
				
			||||
    const A3 : i32 = 37; | 
				
			||||
    const A3X : i32 = 38; | 
				
			||||
    const B3 : i32 = 39; | 
				
			||||
 | 
				
			||||
    const C4 : i32 = 40; | 
				
			||||
    const C4X : i32 = 41; | 
				
			||||
    const D4 : i32 = 42; | 
				
			||||
    const D4X : i32 = 43; | 
				
			||||
    const E4 : i32 = 44; | 
				
			||||
    const F4 : i32 = 45; | 
				
			||||
    const F4X : i32 = 46; | 
				
			||||
    const G4 : i32 = 47; | 
				
			||||
    const G4X : i32 = 48; | 
				
			||||
    const A4 : i32 = 49; | 
				
			||||
    const A4X : i32 = 50; | 
				
			||||
    const B4 : i32 = 51; | 
				
			||||
 | 
				
			||||
    const C5 : i32 = 52; | 
				
			||||
    const C5X : i32 = 53; | 
				
			||||
    const D5 : i32 = 54; | 
				
			||||
    const D5X : i32 = 55; | 
				
			||||
    const E5 : i32 = 56; | 
				
			||||
    const F5 : i32 = 57; | 
				
			||||
    const F5X : i32 = 58; | 
				
			||||
    const G5 : i32 = 59; | 
				
			||||
    const G5X : i32 = 60; | 
				
			||||
    const A5 : i32 = 61; | 
				
			||||
    const A5X : i32 = 62; | 
				
			||||
    const B5 : i32 = 63; | 
				
			||||
 | 
				
			||||
    let key2freq = |key| { | 
				
			||||
        match key { | 
				
			||||
            // C0 => 16.35,
 | 
				
			||||
            // C0X => 17.32,
 | 
				
			||||
            // D0 => 18.35,
 | 
				
			||||
            // D0X => 19.45,
 | 
				
			||||
            // E0 => 20.60,
 | 
				
			||||
            // F0 => 21.83,
 | 
				
			||||
            // F0X => 23.12,
 | 
				
			||||
            // G0 => 24.50,
 | 
				
			||||
            // G0X => 25.96,
 | 
				
			||||
            // A0 => 27.50,
 | 
				
			||||
            // A0X => 29.14,
 | 
				
			||||
            // B0 => 30.87,
 | 
				
			||||
            // C1 => 32.70,
 | 
				
			||||
            // C1X => 34.65,
 | 
				
			||||
            // D1 => 36.71,
 | 
				
			||||
            // D1X => 38.89,
 | 
				
			||||
            // E1 => 41.20,
 | 
				
			||||
            // F1 => 43.65,
 | 
				
			||||
            // F1X => 46.25,
 | 
				
			||||
            // G1 => 49.00,
 | 
				
			||||
            // G1X => 51.91,
 | 
				
			||||
            // A1 => 55.00,
 | 
				
			||||
            // A1X => 58.27,
 | 
				
			||||
            // B1 => 61.74,
 | 
				
			||||
            // C2 => 65.41,
 | 
				
			||||
            // C2X => 69.30,
 | 
				
			||||
            // D2 => 73.42,
 | 
				
			||||
            // D2X => 77.78,
 | 
				
			||||
            // E2 => 82.41,
 | 
				
			||||
            // F2 => 87.31,
 | 
				
			||||
            // F2X => 92.50,
 | 
				
			||||
            // G2 => 98.00,
 | 
				
			||||
            // G2X => 103.83,
 | 
				
			||||
            // A2 => 110.00,
 | 
				
			||||
            // A2X => 116.54,
 | 
				
			||||
            // B2 => 123.47,
 | 
				
			||||
            C3 => 130.81, | 
				
			||||
            C3X => 138.59, | 
				
			||||
            D3 => 146.83, | 
				
			||||
            D3X => 155.56, | 
				
			||||
            E3 => 164.81, | 
				
			||||
            F3 => 174.61, | 
				
			||||
            F3X => 185.00, | 
				
			||||
            G3 => 196.00, | 
				
			||||
            G3X => 207.65, | 
				
			||||
            A3 => 220.00, | 
				
			||||
            A3X => 233.08, | 
				
			||||
            B3 => 246.94, | 
				
			||||
            C4 => 261.63, | 
				
			||||
            C4X => 277.18, | 
				
			||||
            D4 => 293.66, | 
				
			||||
            D4X => 311.13, | 
				
			||||
            E4 => 329.63, | 
				
			||||
            F4 => 349.23, | 
				
			||||
            F4X => 369.99, | 
				
			||||
            G4 => 392.00, | 
				
			||||
            G4X => 415.30, | 
				
			||||
            A4 => 440.00, | 
				
			||||
            A4X => 466.16, | 
				
			||||
            B4 => 493.88, | 
				
			||||
            C5 => 523.25, | 
				
			||||
            C5X => 554.37, | 
				
			||||
            D5 => 587.33, | 
				
			||||
            D5X => 622.25, | 
				
			||||
            E5 => 659.25, | 
				
			||||
            F5 => 698.46, | 
				
			||||
            F5X => 739.99, | 
				
			||||
            G5 => 783.99, | 
				
			||||
            G5X => 830.61, | 
				
			||||
            A5 => 880.00, | 
				
			||||
            A5X => 932.33, | 
				
			||||
            B5 => 987.77, | 
				
			||||
            // C6 => 1046.50,
 | 
				
			||||
            // C6X => 1108.73,
 | 
				
			||||
            // D6 => 1174.66,
 | 
				
			||||
            // D6X => 1244.51,
 | 
				
			||||
            // E6 => 1318.51,
 | 
				
			||||
            // F6 => 1396.91,
 | 
				
			||||
            // F6X => 1479.98,
 | 
				
			||||
            // G6 => 1567.98,
 | 
				
			||||
            // G6X => 1661.22,
 | 
				
			||||
            // A6 => 1760.00,
 | 
				
			||||
            // A6X => 1864.66,
 | 
				
			||||
            // B6 => 1975.53,
 | 
				
			||||
            // C7 => 2093.00,
 | 
				
			||||
            // C7X => 2217.46,
 | 
				
			||||
            // D7 => 2349.32,
 | 
				
			||||
            // D7X => 2489.02,
 | 
				
			||||
            // E7 => 2637.02,
 | 
				
			||||
            // F7 => 2793.83,
 | 
				
			||||
            // F7X => 2959.96,
 | 
				
			||||
            // G7 => 3135.96,
 | 
				
			||||
            // G7X => 3322.44,
 | 
				
			||||
            // A7 => 3520.00,
 | 
				
			||||
            // A7X => 3729.31,
 | 
				
			||||
            // B7 => 3951.07,
 | 
				
			||||
            // C8 => 4186.01,
 | 
				
			||||
            // C8X => 4434.92,
 | 
				
			||||
            // D8 => 4698.63,
 | 
				
			||||
            // D8X => 4978.03,
 | 
				
			||||
            // E8 => 5274.04,
 | 
				
			||||
            // F8 => 5587.65,
 | 
				
			||||
            // F8X => 5919.91,
 | 
				
			||||
            // G8 => 6271.93,
 | 
				
			||||
            // G8X => 6644.88,
 | 
				
			||||
            // A8 => 7040.00,
 | 
				
			||||
            // A8X => 7458.62,
 | 
				
			||||
            // B8 => 7902.13,
 | 
				
			||||
 | 
				
			||||
            _ => 440.0 | 
				
			||||
        } | 
				
			||||
    }; | 
				
			||||
 | 
				
			||||
    // this is greensleeves painfully transcribed from the sheet here:
 | 
				
			||||
    //   https://www.guitarcommand.com/greensleeves-guitar-tab/
 | 
				
			||||
 | 
				
			||||
    let alt = vec![ | 
				
			||||
        (2, A4), | 
				
			||||
 | 
				
			||||
        (4, C5), | 
				
			||||
        (2, D5), | 
				
			||||
        (3, E5), (1, F5X), (2, E5), | 
				
			||||
 | 
				
			||||
        (4, D5), | 
				
			||||
        (2, B4), | 
				
			||||
        (3, G4), (1, A4), (2, B4), | 
				
			||||
 | 
				
			||||
        (4, C5), | 
				
			||||
        (2, A4), | 
				
			||||
        (3, A4), (1, G4X), (2, A4), | 
				
			||||
 | 
				
			||||
        (4, B4), | 
				
			||||
        (2, G4X), | 
				
			||||
        (4, E4), | 
				
			||||
    ]; | 
				
			||||
 | 
				
			||||
    // Bass line, must be kept in sync with alt. Insert -1 notes for padding where needed.
 | 
				
			||||
    let bass = vec![ | 
				
			||||
        (2, -1), | 
				
			||||
 | 
				
			||||
        (6, A3), | 
				
			||||
        (6, C4), | 
				
			||||
 | 
				
			||||
        (6, G3), | 
				
			||||
        (6, G4), | 
				
			||||
 | 
				
			||||
        (6, A3), | 
				
			||||
        (6, A4), | 
				
			||||
 | 
				
			||||
        (6, E3), | 
				
			||||
        (6, E4), | 
				
			||||
    ]; | 
				
			||||
 | 
				
			||||
    const D_ONOFF: Duration = Duration::from_millis(10); | 
				
			||||
 | 
				
			||||
    let bpm = 25.0; | 
				
			||||
 | 
				
			||||
    let mut ticks_alt = 0; | 
				
			||||
    let mut ticks_bass = 0; | 
				
			||||
 | 
				
			||||
    let mut alt_iter = alt.iter(); | 
				
			||||
    let mut bass_iter = bass.iter(); | 
				
			||||
    loop { | 
				
			||||
        let mut wg = handle.write(); | 
				
			||||
        if ticks_alt == 0 { | 
				
			||||
            if let Some((len, key)) = alt_iter.next() { | 
				
			||||
                ticks_alt = *len; | 
				
			||||
                if *key >= 0 { | 
				
			||||
                    let f = key2freq(*key); | 
				
			||||
                    wg.instruments[0].fade_amp(1.0, D_ONOFF); | 
				
			||||
                    wg.instruments[0].set_freq_imm(f); | 
				
			||||
                } else { | 
				
			||||
                    wg.instruments[0].fade_amp(0.0, D_ONOFF); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        if ticks_bass == 0 { | 
				
			||||
            if let Some((len, key)) = bass_iter.next() { | 
				
			||||
                ticks_bass = len - 1; | 
				
			||||
                if *key >= 0 { | 
				
			||||
                    let f = key2freq(*key); | 
				
			||||
                    wg.instruments[1].fade_amp(0.5, D_ONOFF); | 
				
			||||
                    wg.instruments[1].set_freq_imm(f); | 
				
			||||
                } else { | 
				
			||||
                    wg.instruments[1].fade_amp(0.0, D_ONOFF); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        drop(wg); | 
				
			||||
        if ticks_alt == 0 && ticks_bass == 0 { | 
				
			||||
            break; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        thread::sleep(Duration::from_secs_f32((60.0/bpm) / 16.0)); | 
				
			||||
 | 
				
			||||
        if ticks_alt > 0 { | 
				
			||||
            ticks_alt -= 1; | 
				
			||||
        } | 
				
			||||
        if ticks_bass > 0 { | 
				
			||||
            ticks_bass -= 1; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    Ok(()) | 
				
			||||
} | 
				
			||||
 | 
				
			||||
fn write_data<T>(output: &mut [T], channels: usize, handle: &Arc<RwLock<Orchestra>>) | 
				
			||||
    where | 
				
			||||
        T: cpal::Sample, | 
				
			||||
{ | 
				
			||||
    let mut wg = handle.write(); | 
				
			||||
    for frame in output.chunks_mut(channels) { | 
				
			||||
        if channels == 1 { | 
				
			||||
            let sample = wg.sample_mono(); | 
				
			||||
            frame[0] = cpal::Sample::from::<f32>(&sample); | 
				
			||||
        } else { | 
				
			||||
            let sample = wg.sample_stereo(); | 
				
			||||
            frame[0] = cpal::Sample::from::<f32>(&sample.left); | 
				
			||||
            frame[1] = cpal::Sample::from::<f32>(&sample.right); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,86 @@ | 
				
			||||
use crate::beep::instrument::Instrument; | 
				
			||||
use crate::beep::tween::Tween; | 
				
			||||
use std::time::Duration; | 
				
			||||
use crate::beep::sample::StereoSample; | 
				
			||||
use crate::beep::hack::StableClamp; | 
				
			||||
 | 
				
			||||
/// A set of instruments
 | 
				
			||||
pub struct Orchestra { | 
				
			||||
    pub instruments: Vec<Instrument>, | 
				
			||||
    /// Normalize the output to produce constant amplitude of 1.0 (instead of clipping)
 | 
				
			||||
    normalize: bool, | 
				
			||||
    /// Sample rate, used to calculate time based effects
 | 
				
			||||
    sample_rate : f32, | 
				
			||||
    /// Master gain
 | 
				
			||||
    gain: Tween, | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl Orchestra { | 
				
			||||
    pub fn new(instruments: Vec<Instrument>, sample_rate: f32) -> Self { | 
				
			||||
        let mut o = Self { | 
				
			||||
            instruments, | 
				
			||||
            normalize: false, | 
				
			||||
            sample_rate, | 
				
			||||
            gain: Tween { | 
				
			||||
                actual: 1.0, | 
				
			||||
                target: 1.0, | 
				
			||||
                step: None, | 
				
			||||
                min: 0.0, | 
				
			||||
                max: 1.0, | 
				
			||||
                sample_rate | 
				
			||||
            } | 
				
			||||
        }; | 
				
			||||
        o | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn normalize(&mut self, normalize: bool) { | 
				
			||||
        self.normalize = normalize; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn get_amp(&self) -> f32 { | 
				
			||||
        self.gain.actual() | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn set_amp(&mut self, amp : f32) { | 
				
			||||
        self.gain.set_immediate(amp); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn fade_amp(&mut self, amp : f32, time: Duration) { | 
				
			||||
        self.gain.fade(amp, time); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    fn calc_gain(&self) -> f32 { | 
				
			||||
        (if self.normalize { | 
				
			||||
            1.0 / ( | 
				
			||||
                self.instruments.iter() | 
				
			||||
                    .fold(0.0, |acc, item| acc + item.get_peak_amplitude()) | 
				
			||||
            ) | 
				
			||||
        } else { 1.0 }) * self.gain.actual | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn sample_stereo(&mut self) -> StereoSample { | 
				
			||||
        let gain = self.calc_gain(); | 
				
			||||
 | 
				
			||||
        self.instruments | 
				
			||||
            .iter_mut() | 
				
			||||
            .fold(StereoSample::default(), |mut acc, item| { | 
				
			||||
                let s = item.sample() * gain; | 
				
			||||
                acc.left += s * item.ratio_left; | 
				
			||||
                acc.right += s * item.ratio_left; | 
				
			||||
                acc | 
				
			||||
            }) | 
				
			||||
            .clip() | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    pub fn sample_mono(&mut self) -> f32 { | 
				
			||||
        let gain = self.calc_gain(); | 
				
			||||
 | 
				
			||||
        self.instruments | 
				
			||||
            .iter_mut() | 
				
			||||
            .fold(0.0, |mut acc, item| { | 
				
			||||
                acc + item.sample() * gain | 
				
			||||
            }) | 
				
			||||
            .clamp_(0.0, 1.0) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,68 @@ | 
				
			||||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign}; | 
				
			||||
use crate::beep::hack::StableClamp; | 
				
			||||
 | 
				
			||||
#[derive(Default, Clone, Copy)] | 
				
			||||
pub struct StereoSample { | 
				
			||||
    pub left: f32, | 
				
			||||
    pub right: f32, | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl StereoSample { | 
				
			||||
    pub fn clip(self) -> Self { | 
				
			||||
        Self { | 
				
			||||
            left: self.left.clamp_(-1.0, 1.0), | 
				
			||||
            right: self.right.clamp_(-1.0, 1.0), | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl Add for StereoSample { | 
				
			||||
    type Output = Self; | 
				
			||||
 | 
				
			||||
    fn add(self, rhs: Self) -> Self::Output { | 
				
			||||
        Self { | 
				
			||||
            left: self.left + rhs.left, | 
				
			||||
            right: self.right + rhs.right, | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl Div<f32> for StereoSample { | 
				
			||||
    type Output = Self; | 
				
			||||
 | 
				
			||||
    fn div(self, rhs: f32) -> Self::Output { | 
				
			||||
        Self { | 
				
			||||
            left: self.left / rhs, | 
				
			||||
            right: self.right / rhs, | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl Mul<f32> for StereoSample { | 
				
			||||
    type Output = Self; | 
				
			||||
 | 
				
			||||
    fn mul(self, rhs: f32) -> Self::Output { | 
				
			||||
        Self { | 
				
			||||
            left: self.left * rhs, | 
				
			||||
            right: self.right * rhs, | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl AddAssign<Self> for StereoSample { | 
				
			||||
    fn add_assign(&mut self, rhs: Self) { | 
				
			||||
        *self = *self + rhs | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl DivAssign<f32> for StereoSample { | 
				
			||||
    fn div_assign(&mut self, rhs: f32) { | 
				
			||||
        *self = *self / rhs | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl MulAssign<f32> for StereoSample { | 
				
			||||
    fn mul_assign(&mut self, rhs: f32) { | 
				
			||||
        *self = *self * rhs | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,89 @@ | 
				
			||||
use crate::beep::hack::StableClamp; | 
				
			||||
use std::time::Duration; | 
				
			||||
 | 
				
			||||
/// Scale function amplitude by a gain
 | 
				
			||||
pub trait GainScale { | 
				
			||||
    fn scale_gain(self, gain: f32) -> Self; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl GainScale for f32 { | 
				
			||||
    /// Scale by gain 0-1
 | 
				
			||||
    fn scale_gain(self, gain: f32) -> Self { | 
				
			||||
        // TODO what scaling to use?
 | 
				
			||||
        // self * (1.0 + gain * 9.0).log10()
 | 
				
			||||
        // self * gain.sqrt()
 | 
				
			||||
        self * gain | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/// Value fader
 | 
				
			||||
#[derive(Clone,Debug)] | 
				
			||||
pub(crate) struct Tween { | 
				
			||||
    /// Actual value
 | 
				
			||||
    pub actual: f32, | 
				
			||||
    /// Target value, approached by adding "step" to :actual" every sample
 | 
				
			||||
    pub target: f32, | 
				
			||||
    /// Value added to "actual" per sample. None = set to target at the end of a cycle
 | 
				
			||||
    pub step: Option<f32>, | 
				
			||||
    // Min value
 | 
				
			||||
    pub min: f32, | 
				
			||||
    // Max value
 | 
				
			||||
    pub max: f32, | 
				
			||||
    /// Sample rate, used to calculate time based effects
 | 
				
			||||
    pub sample_rate : f32, | 
				
			||||
} | 
				
			||||
 | 
				
			||||
 | 
				
			||||
impl Tween { | 
				
			||||
    /// Update values for the next sample
 | 
				
			||||
    pub fn tick(&mut self) { | 
				
			||||
        if let Some(step) = self.step { | 
				
			||||
            let actual0 = self.actual; | 
				
			||||
            self.actual += step; | 
				
			||||
            if (self.target > actual0 && self.target <= self.actual) | 
				
			||||
                || (self.target <= actual0 && self.target > self.actual) | 
				
			||||
            { | 
				
			||||
                self.step = None; | 
				
			||||
                self.actual = self.target; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Get actual value
 | 
				
			||||
    pub fn actual(&self) -> f32 { | 
				
			||||
        self.actual | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Check if the change from actual to target is scheduled to happen instantly
 | 
				
			||||
    /// at the end of a cycle.
 | 
				
			||||
    pub fn is_change_scheduled_at_crossover(&self) -> bool { | 
				
			||||
        self.target != self.actual | 
				
			||||
            && self.step.is_none() | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Check if the fading is in progress
 | 
				
			||||
    pub fn is_fading(&self) -> bool { | 
				
			||||
        self.step.is_some() | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Schedule change at crossover
 | 
				
			||||
    pub(crate) fn set_at_crossover(&mut self, val: f32) { | 
				
			||||
        self.target = val.clamp_(self.min, self.max); | 
				
			||||
        self.step = None; // change at the earliest convenience
 | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Start fading
 | 
				
			||||
    pub(crate) fn fade(&mut self, val: f32, time: Duration) { | 
				
			||||
        self.target = val.clamp_(self.min, self.max); | 
				
			||||
        let nsamples = (time.as_secs_f32() * self.sample_rate).ceil(); | 
				
			||||
        self.step = Some((self.target - self.actual) / nsamples); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /// Set value immediately
 | 
				
			||||
    pub(crate) fn set_immediate(&mut self, val: f32) { | 
				
			||||
        self.target = val.clamp_(self.min, self.max); | 
				
			||||
        self.actual = self.target; | 
				
			||||
        self.step = None; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -1,7 +1,9 @@ | 
				
			||||
mod beep; | 
				
			||||
 | 
				
			||||
extern crate anyhow; | 
				
			||||
extern crate cpal; | 
				
			||||
 | 
				
			||||
mod beep; | 
				
			||||
 | 
				
			||||
fn main() -> Result<(), anyhow::Error> { | 
				
			||||
    beep::beep() | 
				
			||||
} | 
				
			||||
 | 
				
			||||
					Loading…
					
					
				
		Reference in new issue