From 58a0250c2580cb6bbdd641f780f0b149c840ed04 Mon Sep 17 00:00:00 2001 From: pommicket Date: Thu, 6 Oct 2022 11:26:49 -0400 Subject: metronome working --- src/main.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/soundfont.rs | 12 +++++---- 2 files changed, 81 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/main.rs b/src/main.rs index 7dd7706..5d8e110 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,9 @@ extern crate cpal; extern crate rhai; +extern crate chrono; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; +use std::fs::File; use std::io::Write; use std::sync::Mutex; @@ -9,7 +11,8 @@ mod midi_input; mod soundfont; const NOTE_FALLOFF: f32 = 0.1; // falloff when note is released -const CHANNEL_COUNT: usize = 16; +const CHANNEL_COUNT: usize = 17; // 16 MIDI channels + metronome +const METRONOME_CHANNEL: i64 = 16; struct Note { key: u8, @@ -18,26 +21,56 @@ struct Note { kill: bool, // only used briefly } +#[derive(Clone)] +struct Metronome { + bpm: f32, + key: i64, + volume: f32 +} + +struct MidiRecording { + file: File, + last_event_time: std::time::Instant, +} + struct NoteInfo { + midi_out: Option, pitch_bend: i32, // in cents pedal_down: bool, presets: [usize; CHANNEL_COUNT], notes: [Vec; CHANNEL_COUNT], channel_volumes: [f32; CHANNEL_COUNT], master_volume: f32, + metronome: Metronome, } static NOTE_INFO: Mutex = Mutex::new(NoteInfo { + midi_out: None, pitch_bend: 0, // this is ridiculous. - notes: [vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![]], + notes: [vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![], vec![]], pedal_down: false, presets: [0; CHANNEL_COUNT], channel_volumes: [1.0; CHANNEL_COUNT], master_volume: 1.0, + metronome: Metronome { + bpm: 0.0, + key: 0, + volume: 0.0 + }, }); + static SOUNDFONT: Mutex> = Mutex::new(None); +fn write_u16_be(file: &mut File, n: u16) -> std::io::Result<()> { + file.write(&mut u16::to_be_bytes(n))?; + Ok(()) +} + +fn lock_note_info<'a>() -> std::sync::MutexGuard<'a, NoteInfo> { + NOTE_INFO.lock().expect("couldn't lock notes") +} + fn get_midi_device(idx: i32) -> Result { let mut device_mgr = midi_input::DeviceManager::new()?; device_mgr.set_quiet(true); @@ -288,6 +321,22 @@ fn set_volume(channel: i64, volume: f64) { note_info.channel_volumes[channel] = volume as f32; } +fn set_metronome(key: i64, bpm: f64, volume: f64) { + let mut note_info = lock_note_info(); + note_info.metronome = Metronome { + key, + bpm: bpm as f32, + volume: volume as f32, + } +} + +// fn start_midi_recording() { +// let name = chrono::Local::now().format("%Y-%m-%d-%H-%M-%S.mid").to_string(); +// let file = File::create(name); +// let note_info = lock_notes(); +// +// } +// fn call_fn_if_exists(engine: &rhai::Engine, ast: &rhai::AST, name: &str, args: impl rhai::FuncArgs) { let mut scope = rhai::Scope::new(); match engine.call_fn::<()>(&mut scope, &ast, name, args).map_err(|e| *e) { @@ -308,6 +357,12 @@ fn playmidi_main() -> Result<(), String> { engine.register_fn("pm_set_pedal", set_pedal_down); engine.register_fn("pm_bend_pitch", set_pitch_bend); engine.register_fn("pm_set_volume", set_volume); + engine.register_fn("pm_set_metronome", set_metronome); + // allow integer bpm as well + engine.register_fn("pm_set_metronome", |key: i64, bpm: i64, volume: f64| { + set_metronome(key, bpm as f64, volume); + }); +// engine.register_fn("pm_start_midi_recording", start_midi_recording); let engine = engine; // de-multablify let mut ast = engine.compile_file("config.rhai".into()).map_err(|e| format!("{}", e))?; @@ -347,7 +402,23 @@ fn playmidi_main() -> Result<(), String> { let stream = get_audio_stream()?; stream.play().map_err(|e| format!("{}", e))?; + let mut last_metronome_tick = std::time::Instant::now(); + while midi_device.is_connected() { + + let metronome_time = last_metronome_tick.elapsed().as_secs_f32(); + + let metronome; + { + let note_info = NOTE_INFO.lock().expect("couldn't lock notes"); + metronome = note_info.metronome.clone(); + } + if metronome.bpm > 0.0 && metronome_time >= 60.0 / metronome.bpm { + play_note(METRONOME_CHANNEL, metronome.key, (metronome.volume * 127.0) as i64); + last_metronome_tick = std::time::Instant::now(); + } + + while let Some(event) = midi_device.read_event() { use midi_input::Event::*; match event { @@ -371,7 +442,7 @@ fn playmidi_main() -> Result<(), String> { eprintln!("Error: {}", err); midi_device.clear_error(); } - std::thread::sleep(std::time::Duration::from_millis(5)); + std::thread::sleep(std::time::Duration::from_millis(1)); } Ok(()) diff --git a/src/soundfont.rs b/src/soundfont.rs index 950ace6..92fc49c 100644 --- a/src/soundfont.rs +++ b/src/soundfont.rs @@ -150,6 +150,7 @@ pub struct SamplesRequest { key: u8, vel: u8, falloff: f32, + falloff_speed: f32, tune: i32, volume: f32, zones: Vec, @@ -647,7 +648,7 @@ impl SamplesRequest { /// every t seconds, volume will be multiplied by amount ^ t /// e.g. when a user lets go of a piano key, you might call `set_falloff(0.01)` pub fn set_falloff(&mut self, amount: f32) { - self.falloff = amount; + self.falloff_speed = amount; } } @@ -1207,6 +1208,7 @@ impl SoundFont { tune: 0, t: 0.0, falloff: 1.0, + falloff_speed: 1.0, zones, }) } @@ -1299,8 +1301,8 @@ impl SoundFont { let t_inc = freq_modulation / sample_rate; let data = &sample.data[data_start as usize..data_end as usize]; let tmul = sample.sample_rate as f64; - let mut falloff = 1.0; // falloff accrued from these samples - let falloff_mul = f32::powf(request.falloff, (1.0 / sample_rate) as f32); + let mut falloff = request.falloff; + let falloff_mul = f32::powf(request.falloff_speed, (1.0 / sample_rate) as f32); for i in 0..samples.len() / 2 { let mut s = (t * tmul) as u64; if zone.loops && s >= startloop { @@ -1319,8 +1321,8 @@ impl SoundFont { t += t_inc; } - request.volume *= falloff; - if request.volume < 1.0 / 32767.0 { + request.falloff = falloff; + if falloff * request.volume < 1.0 / 32767.0 { this_held = false; } final_t = f64::max(final_t, t); -- cgit v1.2.3