diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index 83c28ce..220e116 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -372,11 +372,12 @@ macro_rules! node_list { (1 trig n_id n_id r_id f_def stp_d -1.0, 1.0, 0.0) (2 offs n_id n_id r_id f_def stp_d 0.0, 1.0, 0.0) (3 len n_id n_id r_id f_def stp_d 0.0, 1.0, 1.0) - (4 dcms n_declick d_declick r_ms f_ms stp_m 0.0, 1.0, 3.0) - (5 det n_det d_det r_det f_det stp_f -0.2, 0.2, 0.0) - {6 0 sample audio_unloaded("") f_def 0 0} - {7 1 pmode setting(0) fa_sampl_pmode 0 1} - {8 2 dclick setting(0) fa_sampl_dclick 0 1} + (4 mxlen n_id n_id r_id f_def stp_d 0.0, 1.0, 1.0) + (5 dcms n_declick d_declick r_ms f_ms stp_m 0.0, 1.0, 3.0) + (6 det n_det d_det r_det f_det stp_f -0.2, 0.2, 0.0) + {7 0 sample audio_unloaded("") f_def 0 0} + {8 1 pmode setting(0) fa_sampl_pmode 0 1} + {9 2 dclick setting(0) fa_sampl_dclick 0 1} [0 sig], // node_param_idx // name denorm round format steps norm norm denorm diff --git a/src/dsp/node_sampl.rs b/src/dsp/node_sampl.rs index 01ee3c2..9657bf3 100644 --- a/src/dsp/node_sampl.rs +++ b/src/dsp/node_sampl.rs @@ -61,7 +61,15 @@ impl Sampl { pub const offs : &'static str = "Sampl offs\nStart position offset.\nRange: (0..1)\n"; pub const len : &'static str = - "Sampl len\nLength of the sample, after the offset has been applied.\nRange: (0..1)\n"; + "Sampl len\nAdjusts the remaining length of the sample, after the \ + offset has been applied. This means the absolute length in seconds \ + depends on the offset and this ratio.\ + \nRange: (0..1)\n"; + pub const mxlen : &'static str = + "Sampl mxlen\nLimits the remaining length of the sample relative to the \ + original length of the sample. In constrast to 'len' this paramter \ + modifies the length independently to the 'offs' parameter. \ + \nRange: (0..1)\n"; pub const dcms : &'static str = "Sampl dcms\nDeclick fade time in milliseconds.\nNot audio rate!\nRange: (0..1)\n"; pub const det : &'static str = @@ -111,6 +119,12 @@ You can adjust the playback speed of the sample either by the 'freq' parameter or the 'det' parameter. You can offset into the sample using the 'offs' parameter and modify the remaining length using the 'len' parameter. +The maximum length can be modified using the 'mxlen' parameter. +It limits the maximum of the remaining length, but relative to the original +length of the sample and not to the length after the offset has been applied. +If you apply length modification using the 'len' parameter, +then the playback length of the sample will change with the 'offs' parameter. + Even though you are advised to use an envelope for controlling the playback volume of the sample to prevent clicks a simple in and out ramp is provided using by the 'dclick' setting. The length of these ramps can be controlled @@ -168,12 +182,13 @@ impl Sampl { sample_data: &[f32], out: &mut ProcBuf, do_loop: bool, declick: bool) { - let freq = inp::Sampl::freq(inputs); - let trig = inp::Sampl::trig(inputs); - let offs = inp::Sampl::offs(inputs); - let len = inp::Sampl::len(inputs); - let dcms = inp::Sampl::dcms(inputs); - let det = inp::Sampl::det(inputs); + let freq = inp::Sampl::freq(inputs); + let trig = inp::Sampl::trig(inputs); + let offs = inp::Sampl::offs(inputs); + let len = inp::Sampl::len(inputs); + let mxlen = inp::Sampl::mxlen(inputs); + let dcms = inp::Sampl::dcms(inputs); + let det = inp::Sampl::det(inputs); let sample_srate = sample_data[0] as f64; let sample_data = &sample_data[1..]; @@ -189,8 +204,9 @@ impl Sampl { is_playing = true; } - let mut prev_offs = -10.0; - let mut prev_len = -10.0; + let mut prev_offs = -10.0; + let mut prev_len = -10.0; + let mut prev_mxlen = -10.0; let mut start_idx = 0; let mut end_idx_plus1 = sample_data.len(); @@ -230,15 +246,31 @@ impl Sampl { false }; + let cur_mxlen = + denorm::Sampl::mxlen(mxlen, frame).abs().min(1.0) + as f64; let cur_len = denorm::Sampl::len(len, frame).abs().min(0.999999) as f64; - if recalc_end || prev_len != cur_len { - let remain_s_len = sd_len - start_idx; + if recalc_end + || prev_len != cur_len + || prev_mxlen != cur_mxlen + { + let max_sd_len = + ((sd_len as f64 * cur_mxlen as f64) + as usize).max(1); + + let remain_s_len = + if start_idx <= sd_len { + (sd_len - start_idx).min(max_sd_len) + } else { 0 }; + end_idx_plus1 = ((remain_s_len as f64 * cur_len) .ceil() as usize).min(remain_s_len); - prev_len = cur_len; + + prev_mxlen = cur_mxlen; + prev_len = cur_len; } let sample_slice = diff --git a/src/matrix.rs b/src/matrix.rs index 74efe8c..831be4b 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -1071,7 +1071,7 @@ mod tests { let prog = node_exec.get_prog(); assert_eq!(prog.prog.len(), 2); assert_eq!(prog.prog[0].to_string(), "Op(i=0 out=(0-1) in=(0-2) at=(0-0))"); - assert_eq!(prog.prog[1].to_string(), "Op(i=1 out=(1-1) in=(2-4) at=(0-1) cpy=(o0 => i2))"); + assert_eq!(prog.prog[1].to_string(), "Op(i=1 out=(1-1) in=(2-5) at=(0-1) cpy=(o0 => i2))"); } #[test] @@ -1102,7 +1102,7 @@ mod tests { let prog = node_exec.get_prog(); assert_eq!(prog.prog.len(), 2); assert_eq!(prog.prog[0].to_string(), "Op(i=2 out=(2-3) in=(4-6) at=(0-0))"); - assert_eq!(prog.prog[1].to_string(), "Op(i=3 out=(3-3) in=(6-8) at=(0-1) cpy=(o2 => i6))"); + assert_eq!(prog.prog[1].to_string(), "Op(i=3 out=(3-3) in=(6-9) at=(0-1) cpy=(o2 => i6))"); } #[test] diff --git a/src/matrix_repr.rs b/src/matrix_repr.rs index 4df414b..011ea5a 100644 --- a/src/matrix_repr.rs +++ b/src/matrix_repr.rs @@ -469,7 +469,7 @@ mod tests { let s = mr.serialize(); assert_eq!(s, - "{\"VERSION\":1,\"atoms\":[[\"out\",0,\"mono\",[\"i\",0]]],\"cells\":[[\"sin\",2,0,0,[-1,-1,-1],[-1,0,-1]],[\"out\",0,1,0,[-1,0,-1],[-1,-1,0]]],\"params\":[[\"out\",0,\"ch1\",0.0],[\"out\",0,\"ch2\",0.0],[\"sin\",0,\"det\",0.0],[\"sin\",1,\"det\",0.0],[\"sin\",2,\"det\",0.0],[\"sin\",0,\"freq\",0.0],[\"sin\",1,\"freq\",0.0],[\"sin\",2,\"freq\",-0.10000000149011612]],\"patterns\":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}"); + "{\"VERSION\":1,\"atoms\":[[\"out\",0,\"mono\",[\"i\",0]]],\"cells\":[[\"sin\",2,0,0,[-1,-1,-1],[-1,0,-1]],[\"out\",0,1,0,[-1,0,-1],[-1,-1,0]]],\"params\":[[\"out\",0,\"ch1\",0.0],[\"out\",0,\"ch2\",0.0],[\"sin\",0,\"det\",0.0],[\"sin\",1,\"det\",0.0],[\"sin\",2,\"det\",0.0],[\"sin\",0,\"freq\",0.0],[\"sin\",1,\"freq\",0.0],[\"sin\",2,\"freq\",-0.10000000149011612],[\"out\",0,\"gain\",0.7071067690849304]],\"patterns\":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}"); let mut mr2 = MatrixRepr::deserialize(&s).unwrap(); diff --git a/tests/node_sampl.rs b/tests/node_sampl.rs index 6492a87..404fe6f 100644 --- a/tests/node_sampl.rs +++ b/tests/node_sampl.rs @@ -367,6 +367,41 @@ fn check_node_sampl_offs_len() { assert_minmax_of_rms!(rmsvec[2], (0.5, 0.55)); } +#[test] +fn check_node_sampl_offs_mxlen() { + let (node_conf, mut node_exec) = new_node_engine(); + let mut matrix = Matrix::new(node_conf, 3, 3); + + let smpl = NodeId::Sampl(0); + let out = NodeId::Out(0); + matrix.place(0, 0, Cell::empty(smpl) + .out(None, None, smpl.out("sig"))); + matrix.place(0, 1, Cell::empty(out) + .input(out.inp("ch1"), None, None)); + matrix.sync().unwrap(); + + let sample_p = smpl.inp_param("sample").unwrap(); + let pmode_p = smpl.inp_param("pmode").unwrap(); + let offs_p = smpl.inp_param("offs").unwrap(); + let mxlen_p = smpl.inp_param("mxlen").unwrap(); + + matrix.set_param(sample_p, create_1sec_ramp()); + matrix.set_param(pmode_p, SAtom::setting(0)); + + // Select part 0.5 to 0.75 of the sample: + matrix.set_param(offs_p, SAtom::param(0.5)); + matrix.set_param(mxlen_p, SAtom::param(0.25)); + + let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 50.0); + assert_minmax_of_rms!(rmsvec[0], (0.001113, 0.54999)); + assert_minmax_of_rms!(rmsvec[2], (0.6, 0.65)); + + let rmsvec = run_and_get_each_rms_mimax(&mut node_exec, 50.0); + assert_minmax_of_rms!(rmsvec[0], (0.65, 0.6999)); + assert_minmax_of_rms!(rmsvec[1], (0.70, 0.75)); + assert_minmax_of_rms!(rmsvec[2], (0.5, 0.55)); +} + #[test] fn check_node_sampl_offs_len_zero_crash() {