Skip to content

Commit

Permalink
add tap meters
Browse files Browse the repository at this point in the history
  • Loading branch information
magnetophon committed Nov 15, 2024
1 parent bf40e4c commit bf4ac6f
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 23 deletions.
78 changes: 61 additions & 17 deletions src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,26 @@ use nih_plug_vizia::{
};

use crate::{
editor::dual_meter::DualMeter, util, AtomicBoolArray, AtomicByteArray, AtomicF32, Del2Params,
LastPlayedNotes, CLEAR_TAPS, LEARNING, LOCK_TAPS, MUTE_IN, MUTE_OUT, NO_LEARNED_NOTE,
editor::dual_meter::DualMeter, util, AtomicBoolArray, AtomicByteArray, AtomicF32,
AtomicF32Array, Del2Params, LastPlayedNotes, CLEAR_TAPS, LEARNING, LOCK_TAPS, MUTE_IN,
MUTE_OUT, NO_LEARNED_NOTE,
};

mod dual_meter;

const ZOOM_SMOOTH_POLE: f32 = 0.915;

/// The minimum decibel value that the meters display
const MIN_TICK: f32 = -60.0;
/// The maximum decibel value that the meters display
const MAX_TICK: f32 = 0.0;

#[derive(Lens, Clone)]
pub struct Data {
pub params: Arc<Del2Params>,
pub input_meter: Arc<AtomicF32>,
pub output_meter: Arc<AtomicF32>,
pub tap_meters: Arc<AtomicF32Array>,
pub is_learning: Arc<AtomicBool>,
pub learning_index: Arc<AtomicUsize>,
pub learned_notes: Arc<AtomicByteArray>,
Expand Down Expand Up @@ -335,7 +342,7 @@ pub fn create(editor_data: Data, editor_state: Arc<ViziaState>) -> Option<Box<dy
.class("parameters");
VStack::new(cx, |cx| {
ZStack::new(cx, |cx| {
DelayGraph::new(cx, Data::params)
DelayGraph::new(cx, Data::params, Data::tap_meters)
// .overflow(Overflow::Hidden)
;
Label::new(cx, "DEL2").class("plugin-name");
Expand Down Expand Up @@ -374,6 +381,7 @@ pub fn create(editor_data: Data, editor_state: Arc<ViziaState>) -> Option<Box<dy

pub struct DelayGraph {
params: Arc<Del2Params>,
tap_meters: Arc<AtomicF32Array>,
}

// TODO: add grid to show bars & beats
Expand All @@ -395,6 +403,7 @@ impl View for DelayGraph {
let selection_color: vg::Color = draw_context.selection_color().into();
let border_width = draw_context.border_width();
let outline_width = draw_context.outline_width();
let tap_meters = self.tap_meters.clone();

// Compute the time scaling factor
let target_time_scaling_factor = Self::compute_time_scaling_factor(
Expand Down Expand Up @@ -436,8 +445,10 @@ impl View for DelayGraph {
Self::draw_tap_velocities(
canvas,
params.clone(),
tap_meters.clone(),
bounds,
outline_color,
border_color,
outline_width,
time_scaling_factor,
border_width,
Expand All @@ -459,12 +470,18 @@ impl View for DelayGraph {
}

impl DelayGraph {
fn new<ParamsL>(cx: &mut Context, params: ParamsL) -> Handle<Self>
fn new<ParamsL, TapMetersL>(
cx: &mut Context,
params: ParamsL,
tap_meters: TapMetersL,
) -> Handle<Self>
where
ParamsL: Lens<Target = Arc<Del2Params>>,
TapMetersL: Lens<Target = Arc<AtomicF32Array>>,
{
Self {
params: params.get(cx),
tap_meters: tap_meters.get(cx),
}
.build(cx, |cx| {
Label::new(
Expand Down Expand Up @@ -574,31 +591,58 @@ impl DelayGraph {
fn draw_tap_velocities(
canvas: &mut Canvas,
params: Arc<Del2Params>,
tap_meters: Arc<AtomicF32Array>,
bounds: BoundingBox,
color: vg::Color,
velocity_color: vg::Color,
meter_color: vg::Color,
line_width: f32,
scaling_factor: f32,
border_width: f32,
) {
let mut path = vg::Path::new();
for i in 0..params.current_tap.load(std::sync::atomic::Ordering::SeqCst) {
let x_offset = (params.delay_times[i].load(std::sync::atomic::Ordering::SeqCst) as f32)
.mul_add(scaling_factor, border_width * 0.5);
let velocity_height = params.velocities[i]
.load(std::sync::atomic::Ordering::SeqCst)
.mul_add(
-border_width.mul_add(-0.5, bounds.h),
border_width.mul_add(-0.5, bounds.h),
);

let velocity_value = params.velocities[i].load(std::sync::atomic::Ordering::SeqCst);
let velocity_height = velocity_value.mul_add(bounds.h, -border_width * 1.0);

let meter_db =
util::gain_to_db(tap_meters[i].load(std::sync::atomic::Ordering::Relaxed));
let meter_height = {
let tick_fraction = (meter_db - MIN_TICK) / (MAX_TICK - MIN_TICK);
(tick_fraction * bounds.h).max(0.0) // Scale using bounds height
};

let mut path = vg::Path::new();
path.move_to(
bounds.x + x_offset,
border_width.mul_add(-0.5, bounds.y + bounds.h),
bounds.x + x_offset - (line_width * 0.75),
bounds.y + bounds.h - velocity_height,
);
path.line_to(
bounds.x + x_offset - (line_width * 0.75),
bounds.y + bounds.h,
);
path.line_to(bounds.x + x_offset, bounds.y + velocity_height);
}

canvas.stroke_path(&path, &vg::Paint::color(color).with_line_width(line_width));
canvas.stroke_path(
&path,
&vg::Paint::color(velocity_color).with_line_width(line_width * 1.5),
);

path = vg::Path::new();
path.move_to(
bounds.x + x_offset + (line_width * 0.75),
bounds.y + bounds.h - meter_height,
);
path.line_to(
bounds.x + x_offset + (line_width * 0.75),
bounds.y + bounds.h,
);

canvas.stroke_path(
&path,
&vg::Paint::color(meter_color).with_line_width(line_width * 1.5),
);
}
}

fn draw_tap_notes_and_pans(
Expand Down
40 changes: 34 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ struct Del2 {
peak_meter_decay_weight: f32,
input_meter: Arc<AtomicF32>,
output_meter: Arc<AtomicF32>,
tap_meters: Arc<AtomicF32Array>,
delay_write_index: isize,
is_learning: Arc<AtomicBool>,
// for which control are we learning?
Expand Down Expand Up @@ -447,6 +448,11 @@ impl Default for Del2 {
let ladders: [LadderFilter; NUM_TAPS] =
array_init(|i| LadderFilter::new(filter_params[i].clone()));
let amp_envelopes = array_init::array_init(|_| Smoother::none());

let tap_meters = AtomicF32Array(array_init::array_init(|_| {
Arc::new(AtomicF32::new(util::MINUS_INFINITY_DB))
}))
.into();
// let delayed_audio_l = array_init(|_| f32::default_boxed_array::<MAX_BLOCK_SIZE>());
// let delayed_audio_r = array_init(|_| f32::default_boxed_array::<MAX_BLOCK_SIZE>());
let delayed_audio_l = array_init(|_| vec![0.0; MAX_BLOCK_SIZE].into_boxed_slice());
Expand Down Expand Up @@ -488,6 +494,7 @@ impl Default for Del2 {
peak_meter_decay_weight: 1.0,
input_meter: Arc::new(AtomicF32::new(util::MINUS_INFINITY_DB)),
output_meter: Arc::new(AtomicF32::new(util::MINUS_INFINITY_DB)),
tap_meters,
delay_write_index: 0,
is_learning: Arc::new(AtomicBool::new(false)),
learning_index: Arc::new(AtomicUsize::new(0)),
Expand Down Expand Up @@ -600,6 +607,7 @@ impl Plugin for Del2 {
params: self.params.clone(),
input_meter: self.input_meter.clone(),
output_meter: self.output_meter.clone(),
tap_meters: self.tap_meters.clone(),
is_learning: self.is_learning.clone(),
learning_index: self.learning_index.clone(),
learned_notes: self.learned_notes.clone(),
Expand Down Expand Up @@ -995,14 +1003,35 @@ impl Plugin for Del2 {
}
}

let mut amplitude = 0.0;
for (value_idx, sample_idx) in (block_start..block_end).enumerate() {
let post_filter_gain = self.dry_wet[value_idx] * self.output_gain[value_idx]
/ (drive * self.global_drive[value_idx]);
let left = self.delayed_audio_l[tap_index][sample_idx] * post_filter_gain;
let right = self.delayed_audio_r[tap_index][sample_idx] * post_filter_gain;
output[0][sample_idx] += left;
output[1][sample_idx] += right;
amplitude += left.abs(); // + right.abs();
}

output[0][sample_idx] +=
self.delayed_audio_l[tap_index][sample_idx] * post_filter_gain;
output[1][sample_idx] +=
self.delayed_audio_r[tap_index][sample_idx] * post_filter_gain;
if self.params.editor_state.is_open() {
amplitude = (amplitude / block_len as f32).abs();
let current_peak_meter =
self.tap_meters[tap_index].load(std::sync::atomic::Ordering::Relaxed);
let new_peak_meter = if amplitude > current_peak_meter {
amplitude
} else {
// println!("self.peak_meter_decay_weight: {}",self.peak_meter_decay_weight);

current_peak_meter.mul_add(0.8, amplitude * (1.0 - 0.8))
// current_peak_meter.mul_add(
// self.peak_meter_decay_weight,
// amplitude * (1.0 - self.peak_meter_decay_weight),
// )
};

self.tap_meters[tap_index]
.store(new_peak_meter, std::sync::atomic::Ordering::Relaxed);
}
}

Expand Down Expand Up @@ -1451,7 +1480,6 @@ impl Del2 {
amplitude += *sample;
}

// Example of condition dependent on editor or GUI state
if self.params.editor_state.is_open() {
amplitude = (amplitude / num_samples as f32).abs();
let current_peak_meter = peak_meter.load(std::sync::atomic::Ordering::Relaxed);
Expand Down Expand Up @@ -1974,7 +2002,7 @@ impl PersistentField<'_, u8> for ArcAtomicBoolArray {
}
struct AtomicU8Array([Arc<AtomicU8>; NUM_TAPS]);
struct AtomicU32Array([Arc<AtomicU32>; NUM_TAPS]);
struct AtomicF32Array([Arc<AtomicF32>; NUM_TAPS]);
pub struct AtomicF32Array([Arc<AtomicF32>; NUM_TAPS]);

// Implement PersistentField for AtomicU8Array
impl PersistentField<'_, [u8; 8]> for AtomicU8Array {
Expand Down

0 comments on commit bf4ac6f

Please sign in to comment.