diff --git a/src/delay_tap.rs b/src/delay_tap.rs index bdc834f..502458d 100644 --- a/src/delay_tap.rs +++ b/src/delay_tap.rs @@ -35,6 +35,8 @@ pub struct DelayTap { /// Is set to true when the tap is created and false is created /// and false when the amplitude envelope hits 0 while the note is releasing. pub is_alive: bool, + /// Only used for indexing into tap_meters + pub tap_index: usize, } impl DelayTap { @@ -56,8 +58,9 @@ impl DelayTap { note: NO_LEARNED_NOTE, velocity: 0.0, releasing: false, - is_muted: false, + is_muted: true, is_alive: false, + tap_index: 0, } } @@ -68,6 +71,7 @@ impl DelayTap { delay_time: u32, note: u8, velocity: f32, + index: usize, ) { self.amp_envelope = amp_envelope; self.internal_id = internal_id; @@ -75,7 +79,8 @@ impl DelayTap { self.note = note; self.velocity = velocity; self.releasing = false; - self.is_muted = false; + self.is_muted = true; self.is_alive = true; + self.tap_index = index; } } diff --git a/src/editor.rs b/src/editor.rs index 6f9dddb..d691930 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -13,8 +13,9 @@ use nih_plug_vizia::{ }; use crate::{ - format_time, util, AtomicBoolArray, AtomicByteArray, AtomicF32, AtomicF32Array, Del2Params, - LastPlayedNotes, CLEAR_TAPS, LEARNING, LOCK_TAPS, MUTE_IN, MUTE_OUT, NO_LEARNED_NOTE, NUM_TAPS, + format_time, util, AtomicBoolArray, AtomicByteArray, AtomicF32, AtomicF32Array, + AtomicUsizeArray, Del2Params, LastPlayedNotes, CLEAR_TAPS, LEARNING, LOCK_TAPS, MUTE_IN, + MUTE_OUT, NO_LEARNED_NOTE, NUM_TAPS, }; const GUI_SMOOTHING_DECAY_MS: f64 = 240.0; @@ -34,6 +35,7 @@ pub struct Data { pub input_meter: Arc, pub output_meter: Arc, pub tap_meters: Arc, + pub meter_indexes: Arc, pub is_learning: Arc, pub learning_index: Arc, pub learned_notes: Arc, @@ -284,7 +286,7 @@ pub fn create(editor_data: Data, editor_state: Arc) -> Option, input_meter: Arc, output_meter: Arc, + meter_indexes: Arc, } // TODO: add grid to show bars & beats @@ -314,8 +317,6 @@ impl View for DelayGraph { } fn draw(&self, draw_context: &mut DrawContext, canvas: &mut Canvas) { - // let params = self.params.clone(); - let params = self.params.clone(); let bounds = draw_context.bounds(); @@ -343,6 +344,7 @@ impl View for DelayGraph { ); let first_note = params.first_note.load(Ordering::SeqCst); + let meter_indexes = self.meter_indexes.clone(); // Draw components Self::draw_background(canvas, bounds, background_color); @@ -360,6 +362,7 @@ impl View for DelayGraph { canvas, ¶ms, &tap_meters, + &meter_indexes, &input_meter, &output_meter, bounds, @@ -397,24 +400,27 @@ impl View for DelayGraph { } impl DelayGraph { - fn new( + fn new( cx: &mut Context, params: ParamsL, tap_meters: TapMetersL, input_meter: InputMeterL, output_meter: OutputMeterL, + meter_indexes: MeterIndexL, ) -> Handle where ParamsL: Lens>, TapMetersL: Lens>, InputMeterL: Lens>, OutputMeterL: Lens>, + MeterIndexL: Lens>, { Self { params: params.get(cx), tap_meters: tap_meters.get(cx), input_meter: input_meter.get(cx), output_meter: output_meter.get(cx), + meter_indexes: meter_indexes.get(cx), } .build(cx, |cx| { Label::new( @@ -659,6 +665,7 @@ impl DelayGraph { canvas: &mut Canvas, params: &Arc, tap_meters: &Arc, + meter_indexes: &Arc, input_meter: &Arc, output_meter: &Arc, bounds: BoundingBox, @@ -677,7 +684,8 @@ impl DelayGraph { let velocity_value = params.velocities[i].load(Ordering::SeqCst); let velocity_height = velocity_value.mul_add(bounds.h, -border_width); - let meter_db = util::gain_to_db(tap_meters[i].load(Ordering::Relaxed)); + let meter_index = meter_indexes[i].load(Ordering::Relaxed); + let meter_db = util::gain_to_db(tap_meters[meter_index].load(Ordering::Relaxed)); let meter_height = { let tick_fraction = (meter_db - MIN_TICK) / (MAX_TICK - MIN_TICK); (tick_fraction * bounds.h).max(0.0) diff --git a/src/lib.rs b/src/lib.rs index 8934a13..65a0f39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -99,6 +99,7 @@ struct Del2 { input_meter: Arc, output_meter: Arc, tap_meters: Arc, + meter_indexes: Arc, delay_write_index: isize, is_learning: Arc, // for which control are we learning? @@ -474,6 +475,7 @@ impl Default for Del2 { Arc::new(AtomicF32::new(util::MINUS_INFINITY_DB)) })) .into(), + meter_indexes: AtomicUsizeArray(array_init(|_| Arc::new(AtomicUsize::new(0)))).into(), delay_write_index: 0, is_learning: Arc::new(AtomicBool::new(false)), learning_index: Arc::new(AtomicUsize::new(0)), @@ -578,6 +580,7 @@ impl Plugin for Del2 { input_meter: self.input_meter.clone(), output_meter: self.output_meter.clone(), tap_meters: self.tap_meters.clone(), + meter_indexes: self.meter_indexes.clone(), is_learning: self.is_learning.clone(), learning_index: self.learning_index.clone(), learned_notes: self.learned_notes.clone(), @@ -589,6 +592,8 @@ impl Plugin for Del2 { ) } + // After this function [`reset()`][Self::reset()] will always be called. If you need to clear + // state, such as filters or envelopes, then you should do so in that function instead. fn initialize( &mut self, _audio_io_layout: &AudioIOLayout, @@ -620,7 +625,7 @@ impl Plugin for Del2 { delay_tap.ladders.s = [f32x4::splat(0.); 4]; } - for i in 0..self.params.tap_counter.load(Ordering::SeqCst) { + for i in 0..NUM_TAPS { self.load_and_configure_tap(i); } @@ -753,7 +758,7 @@ impl Plugin for Del2 { self.delay_taps .iter_mut() .enumerate() - .for_each(|(tap_index, delay_tap)| { + .for_each(|(meter_index, delay_tap)| { if delay_tap.is_alive { let pan = ((f32::from(delay_tap.note) - panning_center) * panning_amount) .clamp(-1.0, 1.0); @@ -820,16 +825,19 @@ impl Plugin for Del2 { if self.params.editor_state.is_open() { let weight = self.peak_meter_decay_weight * 0.91; + let gui_index = delay_tap.tap_index; + + self.meter_indexes[gui_index].store(meter_index, Ordering::Relaxed); amplitude = (amplitude / block_len as f32).min(1.0); let current_peak_meter = - self.tap_meters[tap_index].load(Ordering::Relaxed); + self.tap_meters[meter_index].load(Ordering::Relaxed); let new_peak_meter = if amplitude > current_peak_meter { amplitude } else { current_peak_meter.mul_add(weight, amplitude * (1.0 - weight)) }; - self.tap_meters[tap_index].store(new_peak_meter, Ordering::Relaxed); + self.tap_meters[meter_index].store(new_peak_meter, Ordering::Relaxed); } } }); @@ -1332,7 +1340,6 @@ impl Del2 { self.enabled_actions.store(MUTE_OUT, false); } - // Create and setup amplitude envelope, outside the loop for potential reuse. let amp_envelope = Smoother::new(SmoothingStyle::Linear(global_params.attack_ms.value())); if global_params.mute_is_toggle.value() { amp_envelope.set_target(sample_rate, 1.0); @@ -1371,6 +1378,7 @@ impl Del2 { delay_time, note, velocity, + new_index, ); self.next_internal_id = self.next_internal_id.wrapping_add(1); } else if let Some(oldest_delay_tap) = found_oldest { @@ -1381,6 +1389,7 @@ impl Del2 { delay_time, note, velocity, + new_index, ); self.next_internal_id = self.next_internal_id.wrapping_add(1); } @@ -1692,6 +1701,7 @@ impl PersistentField<'_, u8> for ArcAtomicBoolArray { } } struct AtomicU8Array([Arc; NUM_TAPS]); +pub struct AtomicUsizeArray([Arc; NUM_TAPS]); struct AtomicU32Array([Arc; NUM_TAPS]); pub struct AtomicF32Array([Arc; NUM_TAPS]); @@ -1777,6 +1787,7 @@ macro_rules! impl_index_for_atomic_array { // Apply the macro to different types impl_index_for_atomic_array!(AtomicU8Array, AtomicU8); +impl_index_for_atomic_array!(AtomicUsizeArray, AtomicUsize); impl_index_for_atomic_array!(AtomicU32Array, AtomicU32); impl_index_for_atomic_array!(AtomicF32Array, AtomicF32);