From c6a728c9ab5e42b4420f65f6999abd895b733745 Mon Sep 17 00:00:00 2001 From: pommicket Date: Thu, 6 Oct 2022 10:18:52 -0400 Subject: volume function, better pitch bend, etc. --- Cargo.toml | 2 +- build.rs | 1 + config.rhai | 2 ++ src/main.rs | 30 +++++++++++++++++++++++++----- src/soundfont.rs | 28 ++++++++++++++-------------- 5 files changed, 43 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 11c2457..907e097 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,4 @@ cc = "1.0" [dependencies] cpal = "0.14" libc = "0.2" -rhai = "1.10" +rhai = { version = "1.10", features = [ "only_i64" ] } diff --git a/build.rs b/build.rs index f386285..7e1382c 100644 --- a/build.rs +++ b/build.rs @@ -1,5 +1,6 @@ extern crate cc; fn main() { + println!("cargo:rerun-if-changed=src/vorbis.c"); cc::Build::new().file("src/vorbis.c").compile("stb_vorbis"); } diff --git a/config.rhai b/config.rhai index aed41d4..5483ade 100644 --- a/config.rhai +++ b/config.rhai @@ -16,6 +16,8 @@ fn pm_control_changed(channel, controller, value) { if controller == 64 { // pedal down if value < 127. pm_set_pedal(value < 127); + } else if controller == 1 { + pm_set_volume(0, value / 127.0); } } diff --git a/src/main.rs b/src/main.rs index 9c3001b..7dd7706 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,8 @@ struct NoteInfo { pedal_down: bool, presets: [usize; CHANNEL_COUNT], notes: [Vec; CHANNEL_COUNT], + channel_volumes: [f32; CHANNEL_COUNT], + master_volume: f32, } static NOTE_INFO: Mutex = Mutex::new(NoteInfo { @@ -31,6 +33,8 @@ static NOTE_INFO: Mutex = Mutex::new(NoteInfo { notes: [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, }); static SOUNDFONT: Mutex> = Mutex::new(None); @@ -124,9 +128,11 @@ fn get_audio_stream() -> Result { } let pitch_bend = note_info.pitch_bend; for channel in 0..CHANNEL_COUNT { + let volume = 0.1 * note_info.master_volume * note_info.channel_volumes[channel]; let notes = &mut note_info.notes[channel]; for note in notes.iter_mut() { note.req.set_tune(pitch_bend as i32); + note.req.set_volume(volume); match sf.add_samples_interlaced(&mut note.req, data, sample_rate) { Ok(true) => {} Ok(false) => note.kill = true, @@ -171,8 +177,7 @@ fn play_note(channel: i64, note: i64, vel: i64) { let preset = note_info.presets[channel]; if let Some(sf) = maybe_sf.as_mut() { match sf.request(preset, note, vel) { - Ok(mut req) => { - req.set_volume(0.1); + Ok(req) => { note_info.notes[channel].push(Note { key: note, req, @@ -249,11 +254,11 @@ fn load_soundfont(filename: &str) { } fn load_preset(channel: i64, preset: i64) { - if !check_channel(channel as usize) { - return; - } let preset = preset as usize; let channel = channel as usize; + if !check_channel(channel) { + return; + } let mut note_info = NOTE_INFO.lock().expect("couldn't lock notes"); let mut soundfont = SOUNDFONT.lock().expect("couldn't lock soundfont."); if let Some(sf) = soundfont.as_mut() { @@ -269,6 +274,20 @@ fn load_preset(channel: i64, preset: i64) { } +fn set_volume(channel: i64, volume: f64) { + let volume = if volume.is_nan() { 0.0 } else { volume as f32 }; + let mut note_info = NOTE_INFO.lock().expect("couldn't lock notes"); + if channel == -1 { + note_info.master_volume = volume as f32; + return; + } + let channel = channel as usize; + if !check_channel(channel) { + return; + } + note_info.channel_volumes[channel] = volume as f32; +} + 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) { @@ -288,6 +307,7 @@ fn playmidi_main() -> Result<(), String> { engine.register_fn("pm_release_note", release_note); 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); let engine = engine; // de-multablify let mut ast = engine.compile_file("config.rhai".into()).map_err(|e| format!("{}", e))?; diff --git a/src/soundfont.rs b/src/soundfont.rs index 4acae12..950ace6 100644 --- a/src/soundfont.rs +++ b/src/soundfont.rs @@ -141,7 +141,12 @@ pub enum SampleError { } pub struct SamplesRequest { - hold_time: f64, + // time note has been held down for, multiplied by frequency + // so if a note has been played for 2s at 440Hz, this would be 880 + // if a note is played for 2s at 440Hz, then 1s at 110Hz, this is 990 + // storing it like this allows a note to change pitch without causing clicks + t: f64, + key: u8, vel: u8, falloff: f32, @@ -629,12 +634,6 @@ fn read_gen_zones( /// request for sound font samples. impl SamplesRequest { - /// set amount of time note has been playing for - #[allow(unused)] - pub fn set_hold_time(&mut self, t: f64) { - self.hold_time = t; - } - /// `tune` is in cents pub fn set_tune(&mut self, tune: i32) { self.tune = tune; @@ -1206,7 +1205,7 @@ impl SoundFont { key, vel, tune: 0, - hold_time: 0.0, + t: 0.0, falloff: 1.0, zones, }) @@ -1226,6 +1225,7 @@ impl SoundFont { let vel = request.vel; let mut held = false; + let mut final_t = 0.0; for zone in request.zones.iter() { let sample = match zone.reference { @@ -1295,12 +1295,12 @@ impl SoundFont { // so a 10x larger sample will have 100x the power (and will be 20dB, not 10dB louder). */ - let mut t = request.hold_time; - let t_inc = 1.0 / sample_rate; + let mut t = request.t; + let t_inc = freq_modulation / sample_rate; let data = &sample.data[data_start as usize..data_end as usize]; - let tmul = freq_modulation * (sample.sample_rate as f64); + let tmul = sample.sample_rate as f64; let mut falloff = 1.0; // falloff accrued from these samples - let falloff_mul = f32::powf(request.falloff, t_inc as f32); + let falloff_mul = f32::powf(request.falloff, (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 { @@ -1323,10 +1323,10 @@ impl SoundFont { if request.volume < 1.0 / 32767.0 { this_held = false; } - + final_t = f64::max(final_t, t); held |= this_held; } - request.hold_time += samples.len() as f64 / (2.0 * sample_rate); + request.t = final_t; Ok(held) } -- cgit v1.2.3