summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2022-10-06 10:18:52 -0400
committerpommicket <pommicket@gmail.com>2022-10-06 10:18:52 -0400
commitc6a728c9ab5e42b4420f65f6999abd895b733745 (patch)
treed144836267740643096fc53bd1d169f31415b1c4
parenta772f040ef6a0204440464733a3f64301f6f1db8 (diff)
volume function, better pitch bend, etc.
-rw-r--r--Cargo.toml2
-rw-r--r--build.rs1
-rw-r--r--config.rhai2
-rw-r--r--src/main.rs30
-rw-r--r--src/soundfont.rs28
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<Note>; CHANNEL_COUNT],
+ channel_volumes: [f32; CHANNEL_COUNT],
+ master_volume: f32,
}
static NOTE_INFO: Mutex<NoteInfo> = Mutex::new(NoteInfo {
@@ -31,6 +33,8 @@ static NOTE_INFO: Mutex<NoteInfo> = 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<Option<soundfont::SoundFont>> = Mutex::new(None);
@@ -124,9 +128,11 @@ fn get_audio_stream() -> Result<cpal::Stream, String> {
}
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<Item: SFObject>(
/// 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)
}