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 { 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::(&ac.device, &ac.config.into())?, cpal::SampleFormat::I16 => run::(&ac.device, &ac.config.into())?, cpal::SampleFormat::U16 => run::(&ac.device, &ac.config.into())?, } Ok(()) } fn run(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(output: &mut [T], channels: usize, handle: &Arc>) 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::(&sample); } else { let sample = wg.sample_stereo(); frame[0] = cpal::Sample::from::(&sample.left); frame[1] = cpal::Sample::from::(&sample.right); } } }