From fa03425568f7eb6f978a7591e76c09cd6d5cfb5c Mon Sep 17 00:00:00 2001 From: Bart Brouns Date: Tue, 22 Oct 2024 22:32:10 +0200 Subject: [PATCH] wip LastPlayedNotes --- src/editor.rs | 14 +++-- src/lib.rs | 151 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 153 insertions(+), 12 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index 64f9b0a..fdf25ea 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -11,6 +11,7 @@ use crate::AtomicByteArray; use crate::Del2Params; use crate::DelayData; use crate::DelayDataOutput; +use crate::LastPlayedNotes; #[derive(Lens, Clone)] pub(crate) struct Data { @@ -21,6 +22,7 @@ pub(crate) struct Data { pub is_learning: Arc, pub learning_index: Arc, pub learned_notes: Arc, + pub last_played_notes: Arc, } impl Model for Data {} @@ -89,6 +91,7 @@ pub fn create(editor_data: Data, editor_state: Arc) -> Option, own_index: usize, learned_notes: Arc, + last_played_notes: Arc, } impl ActionTrigger { - pub fn new( + pub fn new( cx: &mut Context, is_learning: IsLearningL, learning_index: LearningIndexL, own_index: usize, - learned_notes: LearnedNoteL, + learned_notes: LearnedNotesL, + last_played_notes: LastPlayedNotesL, ) -> Handle where IsLearningL: Lens>, LearningIndexL: Lens>, - LearnedNoteL: Lens>, + LearnedNotesL: Lens>, + LastPlayedNotesL: Lens>, { Self { is_learning: is_learning.get(cx), learning_index: learning_index.get(cx), own_index, learned_notes: learned_notes.get(cx), - // delay_data: delay_data.get(cx), + last_played_notes: last_played_notes.get(cx), } .build(cx, |_cx| { // Label::new(cx, "XXX").class("global-title"); diff --git a/src/lib.rs b/src/lib.rs index 206e156..ef3c08f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ use nih_plug::prelude::*; use nih_plug_vizia::vizia::prelude::*; use nih_plug_vizia::ViziaState; use std::simd::f32x4; -use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU64, AtomicU8, AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use synfx_dsp::fh_va::{FilterParams, LadderFilter, LadderMode}; use triple_buffer::TripleBuffer; @@ -72,10 +72,11 @@ struct Del2 { input_meter: Arc, output_meter: Arc, delay_write_index: usize, - // for which control are we learning? is_learning: Arc, + // for which control are we learning? learning_index: Arc, learned_notes: Arc, + last_played_notes: Arc, samples_since_last_event: u32, timing_last_event: u32, min_tap_samples: u32, @@ -396,9 +397,6 @@ impl Default for Del2 { let ladders: [LadderFilter; MAX_NR_TAPS] = array_init(|i| LadderFilter::new(filter_params[i].clone())); let amp_envelopes = array_init::array_init(|_| Smoother::none()); - let is_learning = Arc::new(AtomicBool::new(false)); - let learning_index = Arc::new(AtomicUsize::new(0)); - let learned_notes = Arc::new(AtomicByteArray::new()); Self { params: Arc::new(Del2Params::new(should_update_filter.clone())), filter_params, @@ -422,9 +420,10 @@ impl Default for Del2 { input_meter: Arc::new(AtomicF32::new(util::MINUS_INFINITY_DB)), output_meter: Arc::new(AtomicF32::new(util::MINUS_INFINITY_DB)), delay_write_index: 0, - is_learning, - learning_index, - learned_notes, + is_learning: Arc::new(AtomicBool::new(false)), + learning_index: Arc::new(AtomicUsize::new(0)), + learned_notes: Arc::new(AtomicByteArray::new()), + last_played_notes: Arc::new(LastPlayedNotes::new()), samples_since_last_event: 0, timing_last_event: 0, min_tap_samples: 0, @@ -509,6 +508,7 @@ impl Plugin for Del2 { is_learning: self.is_learning.clone(), learning_index: self.learning_index.clone(), learned_notes: self.learned_notes.clone(), + last_played_notes: self.last_played_notes.clone(), }, self.params.editor_state.clone(), ) @@ -1108,5 +1108,140 @@ impl AtomicByteArray { } } +struct LastPlayedNotes { + state: AtomicU8, + notes: AtomicByteArray, + sequence: AtomicByteArray, + current_sequence: AtomicU8, +} + +impl LastPlayedNotes { + fn new() -> Self { + Self { + state: AtomicU8::new(0), + notes: AtomicByteArray::new(), + sequence: AtomicByteArray::new(), + current_sequence: AtomicU8::new(1), + } + } + + fn note_on(&self, note: u8) { + let mut current_state = self.state.load(Ordering::SeqCst); + + // Check if the note is already in the table + if let Some(index) = (0..8).find(|&i| self.notes.load(i, Ordering::SeqCst) == note) { + // Note already exists, update the sequence + self.sequence.store( + index, + self.current_sequence.fetch_add(1, Ordering::SeqCst), + Ordering::SeqCst, + ); + return; + } + + loop { + if let Some(index) = (0..8).find(|i| (current_state & (1 << i)) == 0) { + // Occupy an empty spot + let new_state = current_state | (1 << index); + if self + .state + .compare_exchange_weak( + current_state, + new_state, + Ordering::SeqCst, + Ordering::SeqCst, + ) + .is_ok() + { + self.notes.store(index, note, Ordering::SeqCst); + self.sequence.store( + index, + self.current_sequence.fetch_add(1, Ordering::SeqCst), + Ordering::SeqCst, + ); + break; + } else { + current_state = self.state.load(Ordering::SeqCst); + } + } else { + // Overwrite the oldest active note + let oldest_index = (0..8) + .min_by_key(|&i| self.sequence.load(i, Ordering::SeqCst)) + .unwrap(); + self.notes.store(oldest_index, note, Ordering::SeqCst); + self.sequence.store( + oldest_index, + self.current_sequence.fetch_add(1, Ordering::SeqCst), + Ordering::SeqCst, + ); + break; + } + } + } + + fn note_off(&self, note: u8) { + let mut current_state = self.state.load(Ordering::SeqCst); + loop { + if let Some(index) = (0..8).find(|&i| self.notes.load(i, Ordering::SeqCst) == note) { + let new_state = current_state & !(1 << index); + if self + .state + .compare_exchange_weak( + current_state, + new_state, + Ordering::SeqCst, + Ordering::SeqCst, + ) + .is_ok() + { + self.sequence.store(index, 0, Ordering::SeqCst); + break; + } else { + current_state = self.state.load(Ordering::SeqCst); + } + } else { + break; + } + } + } + + fn is_played(&self, note: u8) -> bool { + if let Some(index) = (0..8).find(|&i| self.notes.load(i, Ordering::SeqCst) == note) { + let current_state = self.state.load(Ordering::SeqCst); + (current_state & (1 << index)) != 0 + } else { + false + } + } + /// for testing + fn print_notes(&self, action: &str) { + // Adjust the width as needed for alignment + const WIDTH: usize = 4; + + print!("{:^25} | ", action); + for i in 0..8 { + let note = self.notes.load(i, Ordering::SeqCst); + if self.is_played(note) { + print!("{:>WIDTH$}", note); + } else { + print!("{:>WIDTH$}", "_"); + } + } + + println!(); + + print!("{:^25} | ", "Sequence"); + for i in 0..8 { + let seq = self.sequence.load(i, Ordering::SeqCst); + if self.is_played(self.notes.load(i, Ordering::SeqCst)) { + print!("{:>WIDTH$}", seq); + } else { + print!("{:>WIDTH$}", "_"); + } + } + println!(); + } +} + nih_export_clap!(Del2); nih_export_vst3!(Del2);