You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
8.9 KiB
356 lines
8.9 KiB
5 years ago
|
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);
|
||
|
}
|
||
|
}
|
||
|
}
|