From e133e7d4a9530b7d10a905bd3f232467d31d0479 Mon Sep 17 00:00:00 2001 From: Bart Brouns Date: Mon, 22 Apr 2024 21:54:58 +0200 Subject: [PATCH] make graph zoomable --- src/editor.rs | 7 ++++--- src/lib.rs | 56 ++++++++++++++++++++++++++++++++++++--------------- src/params.rs | 49 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 90 insertions(+), 22 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index 084d19f..c7f141a 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -192,9 +192,9 @@ pub(crate) fn create( Label::new(cx, "").class("fader-label"); // spacer AttackReleaseGraph::new(cx, LambData::params).height(Pixels(200.0)); // .height(Pixels(260.0)); - Label::new(cx, "zoom mode").class("fader-label"); - ParamSlider::new(cx, LambData::params, |params| ¶ms.zoom_mode) - .set_style(ParamSliderStyle::CurrentStepLabeled { even: true }) + Label::new(cx, "GR graph time scale").class("fader-label"); + ParamSlider::new(cx, LambData::params, |params| ¶ms.time_scale) + .set_style(ParamSliderStyle::CurrentStep{ even: true }) .bottom(Pixels(6.0)); }) // graph + zoom .height(Auto) @@ -213,6 +213,7 @@ pub(crate) fn create( ResizeHandle::new(cx); }) } + /////////////////////////////////////////////////////////////////////////////// // AttackReleaseGraph // /////////////////////////////////////////////////////////////////////////////// diff --git a/src/lib.rs b/src/lib.rs index 2c97270..0fd9fb5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ use std::sync::{Arc, Mutex}; mod buffer; mod dsp; use buffer::*; -use cyma::utils::{PeakBuffer, MinimaBuffer, VisualizerBuffer}; +use cyma::utils::{MinimaBuffer, PeakBuffer, VisualizerBuffer}; use default_boxed::DefaultBoxed; @@ -32,25 +32,30 @@ pub struct Lamb { level_buffer_r: Arc>, gr_buffer_l: Arc>, gr_buffer_r: Arc>, + + /// If this is set at the start of the processing cycle, then the graph duration should be updated. + should_update_time_scale: Arc, } impl Default for Lamb { fn default() -> Self { + let should_update_time_scale = Arc::new(AtomicBool::new(false)); Self { - params: Arc::new(LambParams::default()), - + params: Arc::new(LambParams::new(should_update_time_scale.clone())), + // params: Arc::new(LambParams::default()), dsp: dsp::LambRs::default_boxed(), accum_buffer: TempBuffer::default(), - temp_output_buffer_l : f64::default_boxed_array::(), - temp_output_buffer_r : f64::default_boxed_array::(), - temp_output_buffer_gr_l : f64::default_boxed_array::(), - temp_output_buffer_gr_r : f64::default_boxed_array::(), + temp_output_buffer_l: f64::default_boxed_array::(), + temp_output_buffer_r: f64::default_boxed_array::(), + temp_output_buffer_gr_l: f64::default_boxed_array::(), + temp_output_buffer_gr_r: f64::default_boxed_array::(), sample_rate: 48000.0, - level_buffer_l: Arc::new(Mutex::new(PeakBuffer::new(1068, 7.0, 0.0))), - level_buffer_r: Arc::new(Mutex::new(PeakBuffer::new(1068, 7.0, 0.0))), - gr_buffer_l: Arc::new(Mutex::new(MinimaBuffer::new(1068, 7.0, 0.0))), - gr_buffer_r: Arc::new(Mutex::new(MinimaBuffer::new(1068, 7.0, 0.0))), + level_buffer_l: Arc::new(Mutex::new(PeakBuffer::new(1114, 7.0, 0.0))), + level_buffer_r: Arc::new(Mutex::new(PeakBuffer::new(1114, 7.0, 0.0))), + gr_buffer_l: Arc::new(Mutex::new(MinimaBuffer::new(1114, 7.0, 0.0))), + gr_buffer_r: Arc::new(Mutex::new(MinimaBuffer::new(1114, 7.0, 0.0))), + should_update_time_scale, } } } @@ -145,6 +150,7 @@ impl Plugin for Lamb { fn reset(&mut self) { // Reset buffers and envelopes here. This can be called from the audio thread and may not // allocate. You can remove this function if you do not need it. + self.should_update_time_scale.store(true, Ordering::Release); } fn editor(&mut self, _async_executor: AsyncExecutor) -> Option> { @@ -217,7 +223,7 @@ impl Plugin for Lamb { ); let latency_samples = self.dsp.get_param(LATENCY_PI).expect("no latency read") as u32; context.set_latency_samples(latency_samples); - + let output = buffer.as_slice(); for i in 0..count as usize { output[0][i] = self.temp_output_buffer_l[i] as f32; @@ -225,23 +231,41 @@ impl Plugin for Lamb { } if self.params.editor_state.is_open() { + if self.should_update_time_scale.load(Ordering::Relaxed) { + let time_scale = match self.params.time_scale.value() { + TimeScale::HalfSec => 0.5, + TimeScale::OneSec => 1.0, + TimeScale::TwoSec => 2.0, + TimeScale::FourSec => 4.0, + TimeScale::EightSec => 8.0, + TimeScale::SixteenSec => 16.0, + TimeScale::ThirtytwoSec => 32.0, + TimeScale::SixtyfourSec => 64.0, + }; + self.level_buffer_l.lock().unwrap().set_duration(time_scale); + self.level_buffer_r.lock().unwrap().set_duration(time_scale); + self.gr_buffer_l.lock().unwrap().set_duration(time_scale); + self.gr_buffer_r.lock().unwrap().set_duration(time_scale); + self.should_update_time_scale + .store(false, Ordering::Release); + }; for i in 0..count as usize { self.level_buffer_l .lock() .unwrap() - .enqueue(self.temp_output_buffer_l[i] as f32 ); + .enqueue(self.temp_output_buffer_l[i] as f32); self.level_buffer_r .lock() .unwrap() - .enqueue(self.temp_output_buffer_r[i] as f32 ); + .enqueue(self.temp_output_buffer_r[i] as f32); self.gr_buffer_l .lock() .unwrap() - .enqueue(self.temp_output_buffer_gr_l[i]as f32 ); + .enqueue(self.temp_output_buffer_gr_l[i] as f32); self.gr_buffer_r .lock() .unwrap() - .enqueue(self.temp_output_buffer_gr_r[i]as f32 ); + .enqueue(self.temp_output_buffer_gr_r[i] as f32); } } diff --git a/src/params.rs b/src/params.rs index 96969ff..082b1a9 100644 --- a/src/params.rs +++ b/src/params.rs @@ -1,4 +1,6 @@ +use std::sync::atomic::{AtomicBool, Ordering}; use faust_types::ParamIndex; + #[derive(Params)] struct LambParams { // nr of params: 12 @@ -34,6 +36,9 @@ struct LambParams { output_gain: FloatParam, #[id = "zoom_mode"] zoom_mode: EnumParam, + #[id = "time_scale"] + time_scale: EnumParam, + /// The editor state, saved together with the parameter state so the custom scaling can be /// restored. #[persist = "editor-state"] @@ -56,6 +61,34 @@ enum ZoomMode { Absolute, } +#[derive(Enum, Debug, PartialEq)] +enum TimeScale { + #[id = "0_5s"] + #[name = "0.5 seconds"] + HalfSec, + #[id = "1s"] + #[name = "1 second"] + OneSec, + #[id = "2s"] + #[name = "2 seconds"] + TwoSec, + #[id = "4s"] + #[name = "4 seconds"] + FourSec, + #[id = "8s"] + #[name = "8 seconds"] + EightSec, + #[id = "16s"] + #[name = "16 seconds"] + SixteenSec, + #[id = "32s"] + #[name = "32 seconds"] + ThirtytwoSec, + #[id = "64"] + #[name = "64 seconds"] + SixtyfourSec, +} + #[derive(Enum, Debug, PartialEq)] enum LatencyMode { /// Minimal, but variable latency @@ -132,8 +165,10 @@ pub fn ratio_to_strength() -> Arc Option + Send + Sync> { }) } -impl Default for LambParams { - fn default() -> Self { +impl LambParams { + pub fn new( + should_update_time_scale: Arc, + ) -> Self { Self { editor_state: editor::default_state(), @@ -273,6 +308,15 @@ impl Default for LambParams { zoom_mode: EnumParam::new("zoom_mode", ZoomMode::Relative) .hide() .hide_in_generic_ui(), + time_scale: EnumParam::new("time_scale", TimeScale::EightSec) + .with_callback( + { + let should_update_time_scale = should_update_time_scale.clone(); + Arc::new(move |_| should_update_time_scale.store(true, Ordering::Release)) + }, + ) + .hide() + .hide_in_generic_ui(), } } } @@ -295,4 +339,3 @@ pub const OUTPUT_GAIN_PI: ParamIndex = ParamIndex(14); pub const GAIN_REDUCTION_LEFT_PI: ParamIndex = ParamIndex(15); pub const GAIN_REDUCTION_RIGHT_PI: ParamIndex = ParamIndex(16); pub const LATENCY_PI: ParamIndex = ParamIndex(17); -pub const ZOOM_MODE_PI: ParamIndex = ParamIndex(18);