Skip to content

Commit

Permalink
wip LastPlayedNotes
Browse files Browse the repository at this point in the history
  • Loading branch information
magnetophon committed Oct 22, 2024
1 parent 31b84bf commit fa03425
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 12 deletions.
14 changes: 10 additions & 4 deletions src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -21,6 +22,7 @@ pub(crate) struct Data {
pub is_learning: Arc<AtomicBool>,
pub learning_index: Arc<AtomicUsize>,
pub learned_notes: Arc<AtomicByteArray>,
pub last_played_notes: Arc<LastPlayedNotes>,
}

impl Model for Data {}
Expand Down Expand Up @@ -89,6 +91,7 @@ pub fn create(editor_data: Data, editor_state: Arc<ViziaState>) -> Option<Box<dy
Data::learning_index,
0,
Data::learned_notes,
Data::last_played_notes,
);

HStack::new(cx, |cx| {
Expand Down Expand Up @@ -530,27 +533,30 @@ pub struct ActionTrigger {
learning_index: Arc<AtomicUsize>,
own_index: usize,
learned_notes: Arc<AtomicByteArray>,
last_played_notes: Arc<LastPlayedNotes>,
}
impl ActionTrigger {
pub fn new<IsLearningL, LearningIndexL, LearnedNoteL>(
pub fn new<IsLearningL, LearningIndexL, LearnedNotesL, LastPlayedNotesL>(
cx: &mut Context,

is_learning: IsLearningL,
learning_index: LearningIndexL,
own_index: usize,
learned_notes: LearnedNoteL,
learned_notes: LearnedNotesL,
last_played_notes: LastPlayedNotesL,
) -> Handle<Self>
where
IsLearningL: Lens<Target = Arc<AtomicBool>>,
LearningIndexL: Lens<Target = Arc<AtomicUsize>>,
LearnedNoteL: Lens<Target = Arc<AtomicByteArray>>,
LearnedNotesL: Lens<Target = Arc<AtomicByteArray>>,
LastPlayedNotesL: Lens<Target = Arc<LastPlayedNotes>>,
{
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");
Expand Down
151 changes: 143 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -72,10 +72,11 @@ struct Del2 {
input_meter: Arc<AtomicF32>,
output_meter: Arc<AtomicF32>,
delay_write_index: usize,
// for which control are we learning?
is_learning: Arc<AtomicBool>,
// for which control are we learning?
learning_index: Arc<AtomicUsize>,
learned_notes: Arc<AtomicByteArray>,
last_played_notes: Arc<LastPlayedNotes>,
samples_since_last_event: u32,
timing_last_event: u32,
min_tap_samples: u32,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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(),
)
Expand Down Expand Up @@ -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);

0 comments on commit fa03425

Please sign in to comment.