From f65ebc6c9e0b027cc38eb9e1681497df516078eb Mon Sep 17 00:00:00 2001 From: ardura Date: Mon, 26 Feb 2024 11:22:45 -0700 Subject: [PATCH 1/3] Added gui code for pitch envelope, changed labels, and fixed old preset loads! --- Cargo.toml | 2 +- src/lib.rs | 1582 +++++++++++++++++++++++-------------- src/old_preset_structs.rs | 794 ++++++++++++++++++- 3 files changed, 1765 insertions(+), 613 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 35efe62..696e10d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "Actuate" -version = "1.2.0" +version = "1.2.1" edition = "2021" authors = ["Ardura "] license = "GPL-3.0-or-later" diff --git a/src/lib.rs b/src/lib.rs index 4367002..35a0926 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,7 +69,7 @@ use fx::{ StateVariableFilter::{ResonanceType, StateVariableFilter}, VCFilter::ResponseType as VCResponseType, }; -use old_preset_structs::load_unserialized_old; +use old_preset_structs::{load_unserialized_old, load_unserialized_v114}; use CustomWidgets::{ toggle_switch, ui_knob, BoolButton, CustomParamSlider, CustomParamSlider::ParamSlider as HorizontalParamSlider, @@ -110,9 +110,10 @@ pub const FONT_COLOR: Color32 = Color32::from_rgb(10, 103, 210); // Gui for which filter to display on bottom #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] -enum FilterSelect { +enum UIBottomSelection { Filter1, Filter2, + Pitch } // Gui for which panel to display in bottom right @@ -230,6 +231,19 @@ pub enum FilterRouting { Series21, } +// Pitch Envelope routing +#[allow(non_camel_case_types)] +#[derive(Enum, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub enum PitchRouting { + All, + Osc1, + Osc2, + Osc3, + Osc1_Osc2, + Osc1_Osc3, + Osc2_Osc3, +} + // Fonts const FONT: nih_plug_egui::egui::FontId = FontId::proportional(14.0); const LOADING_FONT: nih_plug_egui::egui::FontId = FontId::proportional(20.0); @@ -421,6 +435,18 @@ pub struct ActuatePreset { filter_routing: FilterRouting, filter_cutoff_link: bool, + // Pitch Env + pitch_enable: bool, + pitch_routing: PitchRouting, + pitch_env_peak: f32, + pitch_env_attack: f32, + pitch_env_decay: f32, + pitch_env_sustain: f32, + pitch_env_release: f32, + pitch_env_atk_curve: Oscillator::SmoothStyle, + pitch_env_dec_curve: Oscillator::SmoothStyle, + pitch_env_rel_curve: Oscillator::SmoothStyle, + // LFOs lfo1_enable: bool, lfo2_enable: bool, @@ -913,6 +939,18 @@ impl Default for Actuate { filter_routing: FilterRouting::Parallel, filter_cutoff_link: false, + // Pitch Routing + pitch_enable: false, + pitch_routing: PitchRouting::Osc1, + pitch_env_peak: 0.0, + pitch_env_attack: 0.0, + pitch_env_decay: 300.0, + pitch_env_sustain: 0.0, + pitch_env_release: 0.0, + pitch_env_atk_curve: SmoothStyle::Linear, + pitch_env_dec_curve: SmoothStyle::Linear, + pitch_env_rel_curve: SmoothStyle::Linear, + // LFOs lfo1_enable: false, lfo2_enable: false, @@ -1336,6 +1374,28 @@ pub struct ActuateParams { #[id = "vcf_filter_type_2"] pub vcf_filter_type_2: EnumParam, + // Pitch Envelope + #[id = "pitch_enable"] + pub pitch_enable: BoolParam, + #[id = "pitch_routing"] + pub pitch_routing: EnumParam, + #[id = "pitch_env_peak"] + pub pitch_env_peak: FloatParam, + #[id = "pitch_env_attack"] + pub pitch_env_attack: FloatParam, + #[id = "pitch_env_decay"] + pub pitch_env_decay: FloatParam, + #[id = "pitch_env_sustain"] + pub pitch_env_sustain: FloatParam, + #[id = "pitch_env_release"] + pub pitch_env_release: FloatParam, + #[id = "pitch_env_atk_curve"] + pub pitch_env_atk_curve: EnumParam, + #[id = "pitch_env_dec_curve"] + pub pitch_env_dec_curve: EnumParam, + #[id = "pitch_env_rel_curve"] + pub pitch_env_rel_curve: EnumParam, + // LFOS #[id = "lfo1_enable"] pub lfo1_enable: BoolParam, @@ -2204,7 +2264,7 @@ impl ActuateParams { // Filters //////////////////////////////////////////////////////////////////////////////////// filter_lp_amount: FloatParam::new( - "Low Pass", + "LPF", 1.0, FloatRange::Linear { min: 0.0, max: 1.0 }, ) @@ -2214,7 +2274,7 @@ impl ActuateParams { Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) }), filter_hp_amount: FloatParam::new( - "High Pass", + "HPF", 0.0, FloatRange::Linear { min: 0.0, max: 1.0 }, ) @@ -2224,7 +2284,7 @@ impl ActuateParams { Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) }), filter_bp_amount: FloatParam::new( - "Band Pass", + "BPF", 0.0, FloatRange::Linear { min: 0.0, max: 1.0 }, ) @@ -2235,7 +2295,7 @@ impl ActuateParams { }), filter_wet: FloatParam::new( - "Filter Wet", + "Filter", 1.0, FloatRange::Linear { min: 0.0, max: 1.0 }, ) @@ -2246,18 +2306,18 @@ impl ActuateParams { Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) }), filter_resonance: FloatParam::new( - "Resonance", + "Res", 1.0, FloatRange::Reversed(&FloatRange::Linear { min: 0.1, max: 1.0 }), ) .with_unit("%") .with_value_to_string(formatters::v2s_f32_percentage(0)), - filter_res_type: EnumParam::new("Filter Type", ResonanceType::Default).with_callback({ + filter_res_type: EnumParam::new("Res Type", ResonanceType::Default).with_callback({ let update_something = update_something.clone(); Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) }), filter_cutoff: FloatParam::new( - "Frequency", + "Cutoff", 20000.0, FloatRange::Skewed { min: 20.0, @@ -2275,7 +2335,7 @@ impl ActuateParams { vcf_filter_type: EnumParam::new("Filter Type", VCResponseType::Lowpass), filter_env_peak: FloatParam::new( - "Env Peak", + "Env Mod", 0.0, FloatRange::Linear { min: -14980.0, @@ -2365,7 +2425,7 @@ impl ActuateParams { }), filter_lp_amount_2: FloatParam::new( - "Low Pass", + "LPF", 1.0, FloatRange::Linear { min: 0.0, max: 1.0 }, ) @@ -2375,7 +2435,7 @@ impl ActuateParams { Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) }), filter_hp_amount_2: FloatParam::new( - "High Pass", + "HPF", 0.0, FloatRange::Linear { min: 0.0, max: 1.0 }, ) @@ -2385,7 +2445,7 @@ impl ActuateParams { Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) }), filter_bp_amount_2: FloatParam::new( - "Band Pass", + "BPF", 0.0, FloatRange::Linear { min: 0.0, max: 1.0 }, ) @@ -2396,7 +2456,7 @@ impl ActuateParams { }), filter_wet_2: FloatParam::new( - "Filter Wet", + "Filter", 1.0, FloatRange::Linear { min: 0.0, max: 1.0 }, ) @@ -2407,20 +2467,20 @@ impl ActuateParams { Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) }), filter_resonance_2: FloatParam::new( - "Resonance", + "Res", 1.0, FloatRange::Reversed(&FloatRange::Linear { min: 0.1, max: 1.0 }), ) .with_unit("%") .with_value_to_string(formatters::v2s_f32_percentage(0)), - filter_res_type_2: EnumParam::new("Filter Type", ResonanceType::Default).with_callback( + filter_res_type_2: EnumParam::new("Res Type", ResonanceType::Default).with_callback( { let update_something = update_something.clone(); Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) }, ), filter_cutoff_2: FloatParam::new( - "Frequency", + "Cutoff", 20000.0, FloatRange::Skewed { min: 20.0, @@ -2438,7 +2498,7 @@ impl ActuateParams { vcf_filter_type_2: EnumParam::new("Filter Type", VCResponseType::Lowpass), filter_env_peak_2: FloatParam::new( - "Env Peak", + "Env Mod", 0.0, FloatRange::Linear { min: -14980.0, @@ -2529,6 +2589,105 @@ impl ActuateParams { filter_cutoff_link: BoolParam::new("Filter Cutoffs Linked", false), + // Pitch Envelope + //////////////////////////////////////////////////////////////////////////////////// + pitch_env_peak: FloatParam::new( + "Env Mod", + 0.0, + FloatRange::Linear { + min: -14980.0, + max: 14980.0, + }, + ) + .with_step_size(1.0) + .with_value_to_string(format_nothing()) + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_env_attack: FloatParam::new( + "Env Attack", + 0.0001, + FloatRange::Skewed { + min: 0.0001, + max: 999.9, + factor: 0.2, + }, + ) + .with_value_to_string(format_nothing()) + .with_unit("A") + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_env_decay: FloatParam::new( + "Env Decay", + 0.0001, + FloatRange::Skewed { + min: 0.0001, + max: 999.9, + factor: 0.2, + }, + ) + .with_value_to_string(format_nothing()) + .with_unit("D") + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_env_sustain: FloatParam::new( + "Env Sustain", + 999.9, + FloatRange::Skewed { + min: 0.0001, + max: 999.9, + factor: 0.2, + }, + ) + .with_value_to_string(format_nothing()) + .with_unit("S") + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_env_release: FloatParam::new( + "Env Release", + 0.0001, + FloatRange::Skewed { + min: 0.0001, + max: 999.9, + factor: 0.2, + }, + ) + .with_value_to_string(format_nothing()) + .with_unit("R") + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_env_atk_curve: EnumParam::new("Atk Curve", Oscillator::SmoothStyle::Linear) + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_env_dec_curve: EnumParam::new("Dec Curve", Oscillator::SmoothStyle::Linear) + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_env_rel_curve: EnumParam::new("Rel Curve", Oscillator::SmoothStyle::Linear) + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_enable: BoolParam::new("Pitch Enable", false), + pitch_routing: EnumParam::new("Routing", PitchRouting::Osc1) + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + + // LFOs //////////////////////////////////////////////////////////////////////////////////// lfo1_enable: BoolParam::new("LFO 1 Enable", false), @@ -2997,8 +3156,8 @@ impl Plugin for Actuate { let export_preset: Arc> = Arc::clone(&self.export_preset); let loading: Arc = Arc::clone(&self.file_dialog); - let filter_select_outside: Arc> = - Arc::new(Mutex::new(FilterSelect::Filter1)); + let filter_select_outside: Arc> = + Arc::new(Mutex::new(UIBottomSelection::Filter1)); let lfo_select_outside: Arc> = Arc::new(Mutex::new(LFOSelect::INFO)); let mod_source_1_tracker_outside: Arc> = Arc::new(Mutex::new(ModulationSource::None)); @@ -3523,283 +3682,457 @@ impl Plugin for Actuate { }); }); //ui.add_space(32.0); - ui.label("Filters"); + ui.label("Filters and Modulations"); // Filter section ui.horizontal(|ui| { ui.vertical(|ui|{ ui.horizontal(|ui|{ - if *filter_select.lock().unwrap() == FilterSelect::Filter1 { - match params.filter_alg_type.value() { - FilterAlgorithms::SVF => { - ui.vertical(|ui|{ - let filter_wet_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_wet, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(filter_wet_knob); - let filter_resonance_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_resonance, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(filter_resonance_knob); - }); - ui.vertical(|ui|{ - let filter_cutoff_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_cutoff, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(filter_cutoff_knob); - let filter_res_type_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_res_type, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(filter_res_type_knob); - }); - ui.vertical(|ui|{ - let filter_hp_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_hp_amount, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(filter_hp_knob); - let filter_env_peak = ui_knob::ArcKnob::for_param( - ¶ms.filter_env_peak, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_readable_box(false) - .set_text_size(TEXT_SIZE); - ui.add(filter_env_peak); - }); - ui.vertical(|ui| { - let filter_lp_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_lp_amount, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(filter_lp_knob); - let filter_bp_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_bp_amount, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(filter_bp_knob); - }); - }, - FilterAlgorithms::TILT => { - ui.vertical(|ui|{ - let filter_wet_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_wet, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(filter_wet_knob); - let filter_resonance_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_resonance, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(filter_resonance_knob); - }); - ui.vertical(|ui|{ - let filter_cutoff_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_cutoff, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(filter_cutoff_knob); - let filter_tilt_type_knob = ui_knob::ArcKnob::for_param( - ¶ms.tilt_filter_type, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(filter_tilt_type_knob); - }); - ui.vertical(|ui|{ - let filter_env_peak = ui_knob::ArcKnob::for_param( - ¶ms.filter_env_peak, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_readable_box(false) - .set_text_size(TEXT_SIZE); - ui.add(filter_env_peak); - }); - ui.add_space(KNOB_SIZE*2.0); - }, - FilterAlgorithms::VCF => { - ui.vertical(|ui|{ - let filter_wet_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_wet, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(filter_wet_knob); - let filter_resonance_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_resonance, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(filter_resonance_knob); - }); - ui.vertical(|ui|{ - let filter_cutoff_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_cutoff, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(filter_cutoff_knob); - let vcf_filter_type_knob = ui_knob::ArcKnob::for_param( - ¶ms.vcf_filter_type, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_text_size(TEXT_SIZE); - ui.add(vcf_filter_type_knob); - }); - ui.vertical(|ui|{ - let filter_env_peak = ui_knob::ArcKnob::for_param( - ¶ms.filter_env_peak, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(SYNTH_MIDDLE_BLUE) - .set_readable_box(false) - .set_text_size(TEXT_SIZE); - ui.add(filter_env_peak); - }); - ui.add_space(KNOB_SIZE*2.0); - }, - } - - // Middle bottom light section - ui.painter().rect_filled( - Rect::from_x_y_ranges( - RangeInclusive::new((WIDTH as f32)*0.35, (WIDTH as f32)*0.64), - RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)), - Rounding::from(16.0), - SYNTH_SOFT_BLUE - ); - // Middle Bottom Filter select background - ui.painter().rect_filled( - Rect::from_x_y_ranges( - RangeInclusive::new((WIDTH as f32)*0.43, (WIDTH as f32)*0.58), - RangeInclusive::new((HEIGHT as f32) - 26.0, (HEIGHT as f32) - 2.0)), - Rounding::from(16.0), - A_BACKGROUND_COLOR_TOP - ); - } else { - match params.filter_alg_type_2.value() { - FilterAlgorithms::SVF => { - ui.vertical(|ui|{ - let filter_wet_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_wet_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(filter_wet_knob); + match *filter_select.lock().unwrap() { + UIBottomSelection::Filter1 => { + match params.filter_alg_type.value() { + FilterAlgorithms::SVF => { + ui.vertical(|ui|{ + let filter_wet_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_wet, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(filter_wet_knob); + let filter_resonance_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_resonance, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(filter_resonance_knob); + }); + ui.vertical(|ui|{ + let filter_cutoff_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_cutoff, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(filter_cutoff_knob); + let filter_res_type_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_res_type, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(filter_res_type_knob); + }); + ui.vertical(|ui|{ + let filter_hp_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_hp_amount, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(filter_hp_knob); + let filter_env_peak = ui_knob::ArcKnob::for_param( + ¶ms.filter_env_peak, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_readable_box(false) + .set_text_size(TEXT_SIZE); + ui.add(filter_env_peak); + }); + ui.vertical(|ui| { + let filter_lp_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_lp_amount, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(filter_lp_knob); + let filter_bp_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_bp_amount, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(filter_bp_knob); + }); + }, + FilterAlgorithms::TILT => { + ui.vertical(|ui|{ + let filter_wet_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_wet, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(filter_wet_knob); + let filter_resonance_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_resonance, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(filter_resonance_knob); + }); + ui.vertical(|ui|{ + let filter_cutoff_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_cutoff, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(filter_cutoff_knob); + let filter_tilt_type_knob = ui_knob::ArcKnob::for_param( + ¶ms.tilt_filter_type, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(filter_tilt_type_knob); + }); + ui.vertical(|ui|{ + let filter_env_peak = ui_knob::ArcKnob::for_param( + ¶ms.filter_env_peak, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_readable_box(false) + .set_text_size(TEXT_SIZE); + ui.add(filter_env_peak); + }); + ui.add_space(KNOB_SIZE*2.0); + }, + FilterAlgorithms::VCF => { + ui.vertical(|ui|{ + let filter_wet_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_wet, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(filter_wet_knob); + let filter_resonance_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_resonance, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(filter_resonance_knob); + }); + ui.vertical(|ui|{ + let filter_cutoff_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_cutoff, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(filter_cutoff_knob); + let vcf_filter_type_knob = ui_knob::ArcKnob::for_param( + ¶ms.vcf_filter_type, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_text_size(TEXT_SIZE); + ui.add(vcf_filter_type_knob); + }); + ui.vertical(|ui|{ + let filter_env_peak = ui_knob::ArcKnob::for_param( + ¶ms.filter_env_peak, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(SYNTH_MIDDLE_BLUE) + .set_readable_box(false) + .set_text_size(TEXT_SIZE); + ui.add(filter_env_peak); + }); + ui.add_space(KNOB_SIZE*2.0); + }, + } - let filter_resonance_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_resonance_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(filter_resonance_knob); + // Middle bottom light section + ui.painter().rect_filled( + Rect::from_x_y_ranges( + RangeInclusive::new((WIDTH as f32)*0.35, (WIDTH as f32)*0.64), + RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)), + Rounding::from(16.0), + SYNTH_SOFT_BLUE + ); + // Middle Bottom Filter select background + ui.painter().rect_filled( + Rect::from_x_y_ranges( + RangeInclusive::new((WIDTH as f32)*0.38, (WIDTH as f32)*0.62), + RangeInclusive::new((HEIGHT as f32) - 26.0, (HEIGHT as f32) - 2.0)), + Rounding::from(16.0), + A_BACKGROUND_COLOR_TOP + ); + }, + UIBottomSelection::Filter2 => { + match params.filter_alg_type_2.value() { + FilterAlgorithms::SVF => { + ui.vertical(|ui|{ + let filter_wet_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_wet_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(filter_wet_knob); + + let filter_resonance_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_resonance_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(filter_resonance_knob); + }); + ui.vertical(|ui|{ + let filter_cutoff_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_cutoff_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(filter_cutoff_knob); + + let filter_res_type_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_res_type_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(filter_res_type_knob); + }); + ui.vertical(|ui|{ + let filter_hp_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_hp_amount_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(filter_hp_knob); + let filter_env_peak = ui_knob::ArcKnob::for_param( + ¶ms.filter_env_peak_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_readable_box(false) + .set_text_size(TEXT_SIZE); + ui.add(filter_env_peak); + }); + ui.vertical(|ui| { + let filter_lp_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_lp_amount_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(filter_lp_knob); + let filter_bp_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_bp_amount_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(filter_bp_knob); + }); + }, + FilterAlgorithms::TILT => { + ui.vertical(|ui|{ + let filter_wet_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_wet_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(filter_wet_knob); + let filter_resonance_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_resonance_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(filter_resonance_knob); + }); + ui.vertical(|ui|{ + let filter_cutoff_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_cutoff_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(filter_cutoff_knob); + let filter_tilt_type_knob = ui_knob::ArcKnob::for_param( + ¶ms.tilt_filter_type_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(filter_tilt_type_knob); + }); + ui.vertical(|ui|{ + let filter_env_peak = ui_knob::ArcKnob::for_param( + ¶ms.filter_env_peak_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_readable_box(false) + .set_text_size(TEXT_SIZE); + ui.add(filter_env_peak); + }); + ui.add_space(KNOB_SIZE*2.0); + }, + FilterAlgorithms::VCF => { + ui.vertical(|ui|{ + let filter_wet_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_wet_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(filter_wet_knob); + let filter_resonance_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_resonance_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(filter_resonance_knob); + }); + ui.vertical(|ui|{ + let filter_cutoff_knob = ui_knob::ArcKnob::for_param( + ¶ms.filter_cutoff_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(filter_cutoff_knob); + let vcf_filter_type_knob = ui_knob::ArcKnob::for_param( + ¶ms.vcf_filter_type_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_text_size(TEXT_SIZE); + ui.add(vcf_filter_type_knob); + }); + ui.vertical(|ui|{ + let filter_env_peak = ui_knob::ArcKnob::for_param( + ¶ms.filter_env_peak_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_readable_box(false) + .set_text_size(TEXT_SIZE); + ui.add(filter_env_peak); + }); + ui.add_space(KNOB_SIZE*2.0); + }, + } + // Middle bottom light section + ui.painter().rect_filled( + Rect::from_x_y_ranges( + RangeInclusive::new((WIDTH as f32)*0.35, (WIDTH as f32)*0.64), + RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)), + Rounding::from(16.0), + SYNTH_SOFT_BLUE2 + ); + // Middle Bottom Filter select background + ui.painter().rect_filled( + Rect::from_x_y_ranges( + RangeInclusive::new((WIDTH as f32)*0.38, (WIDTH as f32)*0.62), + RangeInclusive::new((HEIGHT as f32) - 26.0, (HEIGHT as f32) - 2.0)), + Rounding::from(16.0), + A_BACKGROUND_COLOR_TOP + ); + }, + UIBottomSelection::Pitch => { + ui.vertical(|ui|{ + ui.horizontal(|ui|{ + let pitch_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.pitch_enable, setter); + ui.add(pitch_toggle); + ui.label(RichText::new("Enable Pitch Envelope") + .font(SMALLER_FONT) + .color(A_BACKGROUND_COLOR_TOP) + ); }); - ui.vertical(|ui|{ - let filter_cutoff_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_cutoff_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(filter_cutoff_knob); - let filter_res_type_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_res_type_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(filter_res_type_knob); - }); - ui.vertical(|ui|{ - let filter_hp_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_hp_amount_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(filter_hp_knob); - let filter_env_peak = ui_knob::ArcKnob::for_param( - ¶ms.filter_env_peak_2, + ui.horizontal(|ui|{ + let pitch_env_peak_knob = ui_knob::ArcKnob::for_param( + ¶ms.pitch_env_peak, setter, KNOB_SIZE) .preset_style(ui_knob::KnobStyle::NewPresets1) @@ -3807,128 +4140,10 @@ impl Plugin for Actuate { .set_line_color(A_KNOB_OUTSIDE_COLOR) .set_readable_box(false) .set_text_size(TEXT_SIZE); - ui.add(filter_env_peak); - }); - ui.vertical(|ui| { - let filter_lp_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_lp_amount_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(filter_lp_knob); - let filter_bp_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_bp_amount_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(filter_bp_knob); - }); - }, - FilterAlgorithms::TILT => { - ui.vertical(|ui|{ - let filter_wet_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_wet_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(filter_wet_knob); - let filter_resonance_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_resonance_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(filter_resonance_knob); - }); - ui.vertical(|ui|{ - let filter_cutoff_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_cutoff_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(filter_cutoff_knob); - let filter_tilt_type_knob = ui_knob::ArcKnob::for_param( - ¶ms.tilt_filter_type_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(filter_tilt_type_knob); - }); - ui.vertical(|ui|{ - let filter_env_peak = ui_knob::ArcKnob::for_param( - ¶ms.filter_env_peak_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_readable_box(false) - .set_text_size(TEXT_SIZE); - ui.add(filter_env_peak); - }); - ui.add_space(KNOB_SIZE*2.0); - }, - FilterAlgorithms::VCF => { - ui.vertical(|ui|{ - let filter_wet_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_wet_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(filter_wet_knob); - let filter_resonance_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_resonance_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(filter_resonance_knob); - }); - ui.vertical(|ui|{ - let filter_cutoff_knob = ui_knob::ArcKnob::for_param( - ¶ms.filter_cutoff_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(filter_cutoff_knob); - let vcf_filter_type_knob = ui_knob::ArcKnob::for_param( - ¶ms.vcf_filter_type_2, - setter, - KNOB_SIZE) - .preset_style(ui_knob::KnobStyle::NewPresets1) - .set_fill_color(SYNTH_BARS_PURPLE) - .set_line_color(A_KNOB_OUTSIDE_COLOR) - .set_text_size(TEXT_SIZE); - ui.add(vcf_filter_type_knob); - }); - ui.vertical(|ui|{ - let filter_env_peak = ui_knob::ArcKnob::for_param( - ¶ms.filter_env_peak_2, + ui.add(pitch_env_peak_knob); + + let pitch_routing_knob = ui_knob::ArcKnob::for_param( + ¶ms.pitch_routing, setter, KNOB_SIZE) .preset_style(ui_knob::KnobStyle::NewPresets1) @@ -3936,27 +4151,29 @@ impl Plugin for Actuate { .set_line_color(A_KNOB_OUTSIDE_COLOR) .set_readable_box(false) .set_text_size(TEXT_SIZE); - ui.add(filter_env_peak); + ui.add(pitch_routing_knob); }); - ui.add_space(KNOB_SIZE*2.0); - }, + }); + ui.add_space(KNOB_SIZE*3.5); + ui.add_space(8.0); + + // Middle bottom light section + ui.painter().rect_filled( + Rect::from_x_y_ranges( + RangeInclusive::new((WIDTH as f32)*0.35, (WIDTH as f32)*0.64), + RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)), + Rounding::from(16.0), + SYNTH_MIDDLE_BLUE + ); + // Middle Bottom Filter select background + ui.painter().rect_filled( + Rect::from_x_y_ranges( + RangeInclusive::new((WIDTH as f32)*0.38, (WIDTH as f32)*0.62), + RangeInclusive::new((HEIGHT as f32) - 26.0, (HEIGHT as f32) - 2.0)), + Rounding::from(16.0), + A_BACKGROUND_COLOR_TOP + ); } - // Middle bottom light section - ui.painter().rect_filled( - Rect::from_x_y_ranges( - RangeInclusive::new((WIDTH as f32)*0.35, (WIDTH as f32)*0.64), - RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)), - Rounding::from(16.0), - SYNTH_SOFT_BLUE2 - ); - // Middle Bottom Filter select background - ui.painter().rect_filled( - Rect::from_x_y_ranges( - RangeInclusive::new((WIDTH as f32)*0.43, (WIDTH as f32)*0.58), - RangeInclusive::new((HEIGHT as f32) - 26.0, (HEIGHT as f32) - 2.0)), - Rounding::from(16.0), - A_BACKGROUND_COLOR_TOP - ); } }); }); @@ -3967,185 +4184,293 @@ impl Plugin for Actuate { const VERT_BAR_WIDTH: f32 = 14.0; const HCURVE_WIDTH: f32 = 120.0; const HCURVE_BWIDTH: f32 = 28.0; + ui.vertical(|ui|{ + ui.horizontal(|ui|{ + match *filter_select.lock().unwrap() { + UIBottomSelection::Filter1 => { + // ADSR + ui.add( + VerticalParamSlider::for_param(¶ms.filter_env_attack, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + SYNTH_MIDDLE_BLUE, + ), + ); + ui.add( + VerticalParamSlider::for_param(¶ms.filter_env_decay, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + SYNTH_MIDDLE_BLUE, + ), + ); + ui.add( + VerticalParamSlider::for_param(¶ms.filter_env_sustain, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + SYNTH_MIDDLE_BLUE, + ), + ); + ui.add( + VerticalParamSlider::for_param(¶ms.filter_env_release, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + SYNTH_MIDDLE_BLUE, + ), + ); + }, + UIBottomSelection::Filter2 => { + // ADSR + ui.add( + VerticalParamSlider::for_param(¶ms.filter_env_attack_2, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + A_KNOB_OUTSIDE_COLOR, + ), + ); + ui.add( + VerticalParamSlider::for_param(¶ms.filter_env_decay_2, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + A_KNOB_OUTSIDE_COLOR, + ), + ); + ui.add( + VerticalParamSlider::for_param(¶ms.filter_env_sustain_2, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + A_KNOB_OUTSIDE_COLOR, + ), + ); + ui.add( + VerticalParamSlider::for_param(¶ms.filter_env_release_2, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + A_KNOB_OUTSIDE_COLOR, + ), + ); + }, + UIBottomSelection::Pitch => { + // ADSR + ui.add( + VerticalParamSlider::for_param(¶ms.pitch_env_attack, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + SYNTH_SOFT_BLUE2, + ), + ); + ui.add( + VerticalParamSlider::for_param(¶ms.pitch_env_decay, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + SYNTH_SOFT_BLUE2, + ), + ); + ui.add( + VerticalParamSlider::for_param(¶ms.pitch_env_sustain, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + SYNTH_SOFT_BLUE2, + ), + ); + ui.add( + VerticalParamSlider::for_param(¶ms.pitch_env_release, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + SYNTH_SOFT_BLUE2, + ), + ); + } + } - if *filter_select.lock().unwrap() == FilterSelect::Filter1 { - // ADSR - ui.add( - VerticalParamSlider::for_param(¶ms.filter_env_attack, setter) - .with_width(VERT_BAR_WIDTH) - .with_height(VERT_BAR_HEIGHT) - .set_reversed(true) - .override_colors( - SYNTH_BARS_PURPLE, - SYNTH_MIDDLE_BLUE, - ), - ); - ui.add( - VerticalParamSlider::for_param(¶ms.filter_env_decay, setter) - .with_width(VERT_BAR_WIDTH) - .with_height(VERT_BAR_HEIGHT) - .set_reversed(true) - .override_colors( - SYNTH_BARS_PURPLE, - SYNTH_MIDDLE_BLUE, - ), - ); - ui.add( - VerticalParamSlider::for_param(¶ms.filter_env_sustain, setter) - .with_width(VERT_BAR_WIDTH) - .with_height(VERT_BAR_HEIGHT) - .set_reversed(true) - .override_colors( - SYNTH_BARS_PURPLE, - SYNTH_MIDDLE_BLUE, - ), - ); - ui.add( - VerticalParamSlider::for_param(¶ms.filter_env_release, setter) - .with_width(VERT_BAR_WIDTH) - .with_height(VERT_BAR_HEIGHT) - .set_reversed(true) - .override_colors( - SYNTH_BARS_PURPLE, - SYNTH_MIDDLE_BLUE, - ), - ); - } else { - // ADSR - ui.add( - VerticalParamSlider::for_param(¶ms.filter_env_attack_2, setter) - .with_width(VERT_BAR_WIDTH) - .with_height(VERT_BAR_HEIGHT) - .set_reversed(true) - .override_colors( - SYNTH_BARS_PURPLE, - A_KNOB_OUTSIDE_COLOR, - ), - ); - ui.add( - VerticalParamSlider::for_param(¶ms.filter_env_decay_2, setter) - .with_width(VERT_BAR_WIDTH) - .with_height(VERT_BAR_HEIGHT) - .set_reversed(true) - .override_colors( - SYNTH_BARS_PURPLE, - A_KNOB_OUTSIDE_COLOR, - ), - ); - ui.add( - VerticalParamSlider::for_param(¶ms.filter_env_sustain_2, setter) - .with_width(VERT_BAR_WIDTH) - .with_height(VERT_BAR_HEIGHT) - .set_reversed(true) - .override_colors( - SYNTH_BARS_PURPLE, - A_KNOB_OUTSIDE_COLOR, - ), - ); - ui.add( - VerticalParamSlider::for_param(¶ms.filter_env_release_2, setter) - .with_width(VERT_BAR_WIDTH) - .with_height(VERT_BAR_HEIGHT) - .set_reversed(true) - .override_colors( - SYNTH_BARS_PURPLE, - A_KNOB_OUTSIDE_COLOR, - ), - ); - } - - // Curve sliders - ui.vertical(|ui| { - if *filter_select.lock().unwrap() == FilterSelect::Filter1 { - ui.add( - HorizontalParamSlider::for_param(¶ms.filter_env_atk_curve, setter) - .with_width(HCURVE_BWIDTH) - .slimmer(0.7) - .set_left_sided_label(true) - .set_label_width(HCURVE_WIDTH) - .override_colors( - DARK_GREY_UI_COLOR, - SYNTH_MIDDLE_BLUE), - ); - ui.add( - HorizontalParamSlider::for_param(¶ms.filter_env_dec_curve, setter) - .with_width(HCURVE_BWIDTH) - .slimmer(0.7) - .set_left_sided_label(true) - .set_label_width(HCURVE_WIDTH) - .override_colors( - DARK_GREY_UI_COLOR, - SYNTH_MIDDLE_BLUE), - ); - ui.add( - HorizontalParamSlider::for_param(¶ms.filter_env_rel_curve, setter) - .with_width(HCURVE_BWIDTH) - .slimmer(0.7) - .set_left_sided_label(true) - .set_label_width(HCURVE_WIDTH) - .override_colors( - DARK_GREY_UI_COLOR, - SYNTH_MIDDLE_BLUE), - ); - } else { - ui.add( - HorizontalParamSlider::for_param(¶ms.filter_env_atk_curve_2, setter) - .with_width(HCURVE_BWIDTH) - .slimmer(0.7) - .set_left_sided_label(true) - .set_label_width(HCURVE_WIDTH) - .override_colors( - SYNTH_BARS_PURPLE, - A_KNOB_OUTSIDE_COLOR), - ); - ui.add( - HorizontalParamSlider::for_param(¶ms.filter_env_dec_curve_2, setter) - .with_width(HCURVE_BWIDTH) - .slimmer(0.7) - .set_left_sided_label(true) - .set_label_width(HCURVE_WIDTH) - .override_colors( - SYNTH_BARS_PURPLE, - A_KNOB_OUTSIDE_COLOR), - ); - ui.add( - HorizontalParamSlider::for_param(¶ms.filter_env_rel_curve_2, setter) - .with_width(HCURVE_BWIDTH) - .slimmer(0.7) - .set_left_sided_label(true) - .set_label_width(HCURVE_WIDTH) - .override_colors( - SYNTH_BARS_PURPLE, - A_KNOB_OUTSIDE_COLOR), - ); - } - ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_alg_type, setter) - .slimmer(0.4) - .set_left_sided_label(true) - .set_label_width(120.0) - .override_colors(Color32::WHITE, Color32::BLACK) - .with_width(30.0) - ); - ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_alg_type_2, setter) - .slimmer(0.4) - .set_left_sided_label(true) - .set_label_width(120.0) - .override_colors(Color32::WHITE, Color32::BLACK) - .with_width(30.0) - ); - ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_routing, setter) - .slimmer(0.4) - .set_left_sided_label(true) - .set_label_width(120.0) - .override_colors(Color32::WHITE, Color32::BLACK) - .with_width(30.0) - ); - //ui.add_space(2.0); + // Curve sliders + ui.vertical(|ui| { + match *filter_select.lock().unwrap() { + UIBottomSelection::Filter1 => { + ui.add( + HorizontalParamSlider::for_param(¶ms.filter_env_atk_curve, setter) + .with_width(HCURVE_BWIDTH) + .slimmer(0.7) + .set_left_sided_label(true) + .set_label_width(HCURVE_WIDTH) + .override_colors( + DARK_GREY_UI_COLOR, + SYNTH_MIDDLE_BLUE), + ); + ui.add( + HorizontalParamSlider::for_param(¶ms.filter_env_dec_curve, setter) + .with_width(HCURVE_BWIDTH) + .slimmer(0.7) + .set_left_sided_label(true) + .set_label_width(HCURVE_WIDTH) + .override_colors( + DARK_GREY_UI_COLOR, + SYNTH_MIDDLE_BLUE), + ); + ui.add( + HorizontalParamSlider::for_param(¶ms.filter_env_rel_curve, setter) + .with_width(HCURVE_BWIDTH) + .slimmer(0.7) + .set_left_sided_label(true) + .set_label_width(HCURVE_WIDTH) + .override_colors( + DARK_GREY_UI_COLOR, + SYNTH_MIDDLE_BLUE), + ); + ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_alg_type, setter) + .slimmer(0.4) + .set_left_sided_label(true) + .set_label_width(120.0) + .override_colors(Color32::WHITE, Color32::BLACK) + .with_width(30.0) + ); + ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_alg_type_2, setter) + .slimmer(0.4) + .set_left_sided_label(true) + .set_label_width(120.0) + .override_colors(Color32::WHITE, Color32::BLACK) + .with_width(30.0) + ); + ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_routing, setter) + .slimmer(0.4) + .set_left_sided_label(true) + .set_label_width(120.0) + .override_colors(Color32::WHITE, Color32::BLACK) + .with_width(30.0) + ); + }, + UIBottomSelection::Filter2 => { + ui.add( + HorizontalParamSlider::for_param(¶ms.filter_env_atk_curve_2, setter) + .with_width(HCURVE_BWIDTH) + .slimmer(0.7) + .set_left_sided_label(true) + .set_label_width(HCURVE_WIDTH) + .override_colors( + SYNTH_BARS_PURPLE, + A_KNOB_OUTSIDE_COLOR), + ); + ui.add( + HorizontalParamSlider::for_param(¶ms.filter_env_dec_curve_2, setter) + .with_width(HCURVE_BWIDTH) + .slimmer(0.7) + .set_left_sided_label(true) + .set_label_width(HCURVE_WIDTH) + .override_colors( + SYNTH_BARS_PURPLE, + A_KNOB_OUTSIDE_COLOR), + ); + ui.add( + HorizontalParamSlider::for_param(¶ms.filter_env_rel_curve_2, setter) + .with_width(HCURVE_BWIDTH) + .slimmer(0.7) + .set_left_sided_label(true) + .set_label_width(HCURVE_WIDTH) + .override_colors( + SYNTH_BARS_PURPLE, + A_KNOB_OUTSIDE_COLOR), + ); + ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_alg_type, setter) + .slimmer(0.4) + .set_left_sided_label(true) + .set_label_width(120.0) + .override_colors(Color32::WHITE, Color32::BLACK) + .with_width(30.0) + ); + ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_alg_type_2, setter) + .slimmer(0.4) + .set_left_sided_label(true) + .set_label_width(120.0) + .override_colors(Color32::WHITE, Color32::BLACK) + .with_width(30.0) + ); + ui.add(CustomParamSlider::ParamSlider::for_param(¶ms.filter_routing, setter) + .slimmer(0.4) + .set_left_sided_label(true) + .set_label_width(120.0) + .override_colors(Color32::WHITE, Color32::BLACK) + .with_width(30.0) + ); + }, + UIBottomSelection::Pitch => { + ui.add( + HorizontalParamSlider::for_param(¶ms.pitch_env_atk_curve, setter) + .with_width(HCURVE_BWIDTH) + .slimmer(0.7) + .set_left_sided_label(true) + .set_label_width(HCURVE_WIDTH) + .override_colors( + DARK_GREY_UI_COLOR, + SYNTH_SOFT_BLUE2) + .with_width(30.0), + ); + ui.add( + HorizontalParamSlider::for_param(¶ms.pitch_env_dec_curve, setter) + .with_width(HCURVE_BWIDTH) + .slimmer(0.7) + .set_left_sided_label(true) + .set_label_width(HCURVE_WIDTH) + .override_colors( + DARK_GREY_UI_COLOR, + SYNTH_SOFT_BLUE2) + .with_width(30.0), + ); + ui.add( + HorizontalParamSlider::for_param(¶ms.pitch_env_rel_curve, setter) + .with_width(HCURVE_BWIDTH) + .slimmer(0.7) + .set_left_sided_label(true) + .set_label_width(HCURVE_WIDTH) + .override_colors( + DARK_GREY_UI_COLOR, + SYNTH_SOFT_BLUE2) + .with_width(30.0), + ); + ui.add_space(67.0); + } + } + }); + }); ui.horizontal(|ui|{ - //ui.horizontal(|ui| { - ui.selectable_value(&mut *filter_select.lock().unwrap(), FilterSelect::Filter1, RichText::new("Filter 1").color(Color32::BLACK)); - ui.selectable_value(&mut *filter_select.lock().unwrap(), FilterSelect::Filter2, RichText::new("Filter 2").color(Color32::BLACK)); - //}); + ui.add_space(50.0); + ui.selectable_value(&mut *filter_select.lock().unwrap(), UIBottomSelection::Filter1, RichText::new("Filter 1").color(Color32::BLACK)); + ui.selectable_value(&mut *filter_select.lock().unwrap(), UIBottomSelection::Filter2, RichText::new("Filter 2").color(Color32::BLACK)); + ui.selectable_value(&mut *filter_select.lock().unwrap(), UIBottomSelection::Pitch, RichText::new("Pitch Env").color(Color32::BLACK)) }); }); @@ -7308,6 +7633,18 @@ impl Actuate { filter_routing: FilterRouting::Parallel, filter_cutoff_link: false, + // Pitch + pitch_enable: false, + pitch_env_peak: 0.0, + pitch_env_atk_curve: SmoothStyle::Linear, + pitch_env_dec_curve: SmoothStyle::Linear, + pitch_env_rel_curve: SmoothStyle::Linear, + pitch_env_attack: 0.0, + pitch_env_decay: 300.0, + pitch_env_release: 0.0, + pitch_env_sustain: 0.0, + pitch_routing: PitchRouting::Osc1, + // LFOs lfo1_enable: false, lfo2_enable: false, @@ -7407,10 +7744,17 @@ impl Actuate { limiter_threshold: 0.5, limiter_knee: 0.5, }); + + // Attempt to load the previous version preset type + if unserialized.preset_name.contains("Error") { + // Try loading the previous preset struct version + unserialized = load_unserialized_v114(file_string_data.clone()); + } + // Attempt to load the oldest preset type if unserialized.preset_name.contains("Error") { // Try loading the previous preset struct version - unserialized = load_unserialized_old(file_string_data); + unserialized = load_unserialized_old(file_string_data.clone()); } return (return_name, Some(unserialized)); @@ -7599,6 +7943,17 @@ impl Actuate { filter_routing: FilterRouting::Parallel, filter_cutoff_link: false, + pitch_enable: false, + pitch_env_atk_curve: SmoothStyle::Linear, + pitch_env_dec_curve: SmoothStyle::Linear, + pitch_env_rel_curve: SmoothStyle::Linear, + pitch_env_attack: 0.0, + pitch_env_decay: 300.0, + pitch_env_sustain: 0.0, + pitch_env_release: 0.0, + pitch_env_peak: 0.0, + pitch_routing: PitchRouting::Osc1, + // LFOs lfo1_enable: false, lfo2_enable: false, @@ -8382,6 +8737,19 @@ impl Actuate { filter_routing: self.params.filter_routing.value(), filter_cutoff_link: self.params.filter_cutoff_link.value(), + // Pitch + pitch_enable: self.params.pitch_enable.value(), + pitch_env_atk_curve: self.params.pitch_env_atk_curve.value(), + pitch_env_dec_curve: self.params.pitch_env_dec_curve.value(), + pitch_env_rel_curve: self.params.pitch_env_rel_curve.value(), + pitch_env_attack: self.params.pitch_env_attack.value(), + pitch_env_decay: self.params.pitch_env_decay.value(), + pitch_env_sustain: self.params.pitch_env_sustain.value(), + pitch_env_release: self.params.pitch_env_release.value(), + pitch_env_peak: self.params.pitch_env_peak.value(), + pitch_routing: self.params.pitch_routing.value(), + + // LFOs lfo1_enable: self.params.lfo1_enable.value(), lfo2_enable: self.params.lfo2_enable.value(), lfo3_enable: self.params.lfo3_enable.value(), diff --git a/src/old_preset_structs.rs b/src/old_preset_structs.rs index ec1671b..f3c02d4 100644 --- a/src/old_preset_structs.rs +++ b/src/old_preset_structs.rs @@ -2,15 +2,12 @@ use crate::{ audio_module::{ AudioModuleType, Oscillator::{self, RetriggerStyle, SmoothStyle, VoiceType}, - }, - fx::{ + }, fx::{ delay::{DelaySnapValues, DelayType}, saturation::SaturationType, ArduraFilter, StateVariableFilter::ResonanceType, - }, - ActuatePreset, FilterAlgorithms, FilterRouting, LFOController, ModulationDestination, - ModulationSource, PresetType, + }, ActuatePreset, FilterAlgorithms, FilterRouting, LFOController, ModulationDestination, ModulationSource, PitchRouting, PresetType }; use serde::{Deserialize, Serialize}; @@ -290,6 +287,537 @@ pub struct ActuatePresetOld { limiter_knee: f32, } +/// This is the structure that represents a storable preset value +#[derive(Serialize, Deserialize, Clone)] +pub struct ActuatePresetV114 { + // Information + preset_name: String, + preset_info: String, + preset_category: PresetType, + // Preset tag information - made into bools to make my life easier + tag_acid: bool, + tag_analog: bool, + tag_bright: bool, + tag_chord: bool, + tag_crisp: bool, + tag_deep: bool, + tag_delicate: bool, + tag_hard: bool, + tag_harsh: bool, + tag_lush: bool, + tag_mellow: bool, + tag_resonant: bool, + tag_rich: bool, + tag_sharp: bool, + tag_silky: bool, + tag_smooth: bool, + tag_soft: bool, + tag_stab: bool, + tag_warm: bool, + + // Modules 1 + /////////////////////////////////////////////////////////// + mod1_audio_module_type: AudioModuleType, + mod1_audio_module_level: f32, + // Granulizer/Sampler + mod1_loaded_sample: Vec>, + mod1_sample_lib: Vec>>, + mod1_loop_wavetable: bool, + mod1_single_cycle: bool, + mod1_restretch: bool, + mod1_prev_restretch: bool, + mod1_grain_hold: i32, + mod1_grain_gap: i32, + mod1_start_position: f32, + mod1_end_position: f32, + mod1_grain_crossfade: i32, + + // Osc module knob storage + mod1_osc_type: VoiceType, + mod1_osc_octave: i32, + mod1_osc_semitones: i32, + mod1_osc_detune: f32, + mod1_osc_attack: f32, + mod1_osc_decay: f32, + mod1_osc_sustain: f32, + mod1_osc_release: f32, + mod1_osc_retrigger: RetriggerStyle, + mod1_osc_atk_curve: SmoothStyle, + mod1_osc_dec_curve: SmoothStyle, + mod1_osc_rel_curve: SmoothStyle, + mod1_osc_unison: i32, + mod1_osc_unison_detune: f32, + mod1_osc_stereo: f32, + + // Modules 2 + /////////////////////////////////////////////////////////// + mod2_audio_module_type: AudioModuleType, + mod2_audio_module_level: f32, + // Granulizer/Sampler + mod2_loaded_sample: Vec>, + mod2_sample_lib: Vec>>, + mod2_loop_wavetable: bool, + mod2_single_cycle: bool, + mod2_restretch: bool, + mod2_prev_restretch: bool, + mod2_grain_hold: i32, + mod2_grain_gap: i32, + mod2_start_position: f32, + mod2_end_position: f32, + mod2_grain_crossfade: i32, + + // Osc module knob storage + mod2_osc_type: VoiceType, + mod2_osc_octave: i32, + mod2_osc_semitones: i32, + mod2_osc_detune: f32, + mod2_osc_attack: f32, + mod2_osc_decay: f32, + mod2_osc_sustain: f32, + mod2_osc_release: f32, + mod2_osc_retrigger: RetriggerStyle, + mod2_osc_atk_curve: SmoothStyle, + mod2_osc_dec_curve: SmoothStyle, + mod2_osc_rel_curve: SmoothStyle, + mod2_osc_unison: i32, + mod2_osc_unison_detune: f32, + mod2_osc_stereo: f32, + + // Modules 3 + /////////////////////////////////////////////////////////// + mod3_audio_module_type: AudioModuleType, + mod3_audio_module_level: f32, + // Granulizer/Sampler + mod3_loaded_sample: Vec>, + mod3_sample_lib: Vec>>, + mod3_loop_wavetable: bool, + mod3_single_cycle: bool, + mod3_restretch: bool, + mod3_prev_restretch: bool, + mod3_grain_hold: i32, + mod3_grain_gap: i32, + mod3_start_position: f32, + mod3_end_position: f32, + mod3_grain_crossfade: i32, + + // Osc module knob storage + mod3_osc_type: VoiceType, + mod3_osc_octave: i32, + mod3_osc_semitones: i32, + mod3_osc_detune: f32, + mod3_osc_attack: f32, + mod3_osc_decay: f32, + mod3_osc_sustain: f32, + mod3_osc_release: f32, + mod3_osc_retrigger: RetriggerStyle, + mod3_osc_atk_curve: SmoothStyle, + mod3_osc_dec_curve: SmoothStyle, + mod3_osc_rel_curve: SmoothStyle, + mod3_osc_unison: i32, + mod3_osc_unison_detune: f32, + mod3_osc_stereo: f32, + + // Filters + filter_wet: f32, + filter_cutoff: f32, + filter_resonance: f32, + filter_res_type: ResonanceType, + filter_lp_amount: f32, + filter_hp_amount: f32, + filter_bp_amount: f32, + filter_env_peak: f32, + filter_env_attack: f32, + filter_env_decay: f32, + filter_env_sustain: f32, + filter_env_release: f32, + filter_env_atk_curve: Oscillator::SmoothStyle, + filter_env_dec_curve: Oscillator::SmoothStyle, + filter_env_rel_curve: Oscillator::SmoothStyle, + filter_alg_type: FilterAlgorithms, + tilt_filter_type: ArduraFilter::ResponseType, + + filter_wet_2: f32, + filter_cutoff_2: f32, + filter_resonance_2: f32, + filter_res_type_2: ResonanceType, + filter_lp_amount_2: f32, + filter_hp_amount_2: f32, + filter_bp_amount_2: f32, + filter_env_peak_2: f32, + filter_env_attack_2: f32, + filter_env_decay_2: f32, + filter_env_sustain_2: f32, + filter_env_release_2: f32, + filter_env_atk_curve_2: Oscillator::SmoothStyle, + filter_env_dec_curve_2: Oscillator::SmoothStyle, + filter_env_rel_curve_2: Oscillator::SmoothStyle, + filter_alg_type_2: FilterAlgorithms, + tilt_filter_type_2: ArduraFilter::ResponseType, + + filter_routing: FilterRouting, + filter_cutoff_link: bool, + + /////////////////////////////////////////////////////////////////////////// + // MISSING IN OLDER STRUCTS + //PITCH STUFF + /////////////////////////////////////////////////////////////////////////// + + // LFOs + lfo1_enable: bool, + lfo2_enable: bool, + lfo3_enable: bool, + + lfo1_freq: f32, + lfo1_retrigger: LFOController::LFORetrigger, + lfo1_sync: bool, + lfo1_snap: LFOController::LFOSnapValues, + lfo1_waveform: LFOController::Waveform, + lfo1_phase: f32, + + lfo2_freq: f32, + lfo2_retrigger: LFOController::LFORetrigger, + lfo2_sync: bool, + lfo2_snap: LFOController::LFOSnapValues, + lfo2_waveform: LFOController::Waveform, + lfo2_phase: f32, + + lfo3_freq: f32, + lfo3_retrigger: LFOController::LFORetrigger, + lfo3_sync: bool, + lfo3_snap: LFOController::LFOSnapValues, + lfo3_waveform: LFOController::Waveform, + lfo3_phase: f32, + + // Modulation + mod_source_1: ModulationSource, + mod_source_2: ModulationSource, + mod_source_3: ModulationSource, + mod_source_4: ModulationSource, + mod_dest_1: ModulationDestination, + mod_dest_2: ModulationDestination, + mod_dest_3: ModulationDestination, + mod_dest_4: ModulationDestination, + mod_amount_1: f32, + mod_amount_2: f32, + mod_amount_3: f32, + mod_amount_4: f32, + + // EQ + pre_use_eq: bool, + pre_low_freq: f32, + pre_mid_freq: f32, + pre_high_freq: f32, + pre_low_gain: f32, + pre_mid_gain: f32, + pre_high_gain: f32, + + // FX + use_fx: bool, + + use_compressor: bool, + comp_amt: f32, + comp_atk: f32, + comp_rel: f32, + comp_drive: f32, + + use_abass: bool, + abass_amount: f32, + + use_saturation: bool, + sat_amount: f32, + sat_type: SaturationType, + + use_delay: bool, + delay_amount: f32, + delay_time: DelaySnapValues, + delay_decay: f32, + delay_type: DelayType, + + use_reverb: bool, + reverb_amount: f32, + reverb_size: f32, + reverb_feedback: f32, + + use_phaser: bool, + phaser_amount: f32, + phaser_depth: f32, + phaser_rate: f32, + phaser_feedback: f32, + + use_buffermod: bool, + buffermod_amount: f32, + buffermod_depth: f32, + buffermod_rate: f32, + buffermod_spread: f32, + buffermod_timing: f32, + + use_flanger: bool, + flanger_amount: f32, + flanger_depth: f32, + flanger_rate: f32, + flanger_feedback: f32, + + use_limiter: bool, + limiter_threshold: f32, + limiter_knee: f32, +} + +// This takes the deserialized message pack and converts it into the struct from 1.1.4 +// This then attempts to return the newer preset format after +pub fn load_unserialized_v114(file_data: Vec) -> ActuatePreset { + let old_unserialized: ActuatePresetV114 = + rmp_serde::from_slice(&file_data).unwrap_or(ActuatePresetV114 { + preset_name: "Error Importing".to_string(), + preset_info: "Corrupted preset or incompatible version".to_string(), + preset_category: PresetType::Select, + tag_acid: false, + tag_analog: false, + tag_bright: false, + tag_chord: false, + tag_crisp: false, + tag_deep: false, + tag_delicate: false, + tag_hard: false, + tag_harsh: false, + tag_lush: false, + tag_mellow: false, + tag_resonant: false, + tag_rich: false, + tag_sharp: false, + tag_silky: false, + tag_smooth: false, + tag_soft: false, + tag_stab: false, + tag_warm: false, + mod1_audio_module_type: AudioModuleType::Osc, + mod1_audio_module_level: 1.0, + mod1_loaded_sample: vec![vec![0.0, 0.0]], + mod1_sample_lib: vec![vec![vec![0.0, 0.0]]], + mod1_loop_wavetable: false, + mod1_single_cycle: false, + mod1_restretch: true, + mod1_prev_restretch: false, + mod1_grain_hold: 200, + mod1_grain_gap: 200, + mod1_start_position: 0.0, + mod1_end_position: 1.0, + mod1_grain_crossfade: 50, + mod1_osc_type: VoiceType::Sine, + mod1_osc_octave: 0, + mod1_osc_semitones: 0, + mod1_osc_detune: 0.0, + mod1_osc_attack: 0.0001, + mod1_osc_decay: 0.0001, + mod1_osc_sustain: 999.9, + mod1_osc_release: 5.0, + mod1_osc_retrigger: RetriggerStyle::Retrigger, + mod1_osc_atk_curve: SmoothStyle::Linear, + mod1_osc_dec_curve: SmoothStyle::Linear, + mod1_osc_rel_curve: SmoothStyle::Linear, + mod1_osc_unison: 1, + mod1_osc_unison_detune: 0.0, + mod1_osc_stereo: 0.0, + + mod2_audio_module_type: AudioModuleType::Off, + mod2_audio_module_level: 1.0, + mod2_loaded_sample: vec![vec![0.0, 0.0]], + mod2_sample_lib: vec![vec![vec![0.0, 0.0]]], + mod2_loop_wavetable: false, + mod2_single_cycle: false, + mod2_restretch: true, + mod2_prev_restretch: false, + mod2_grain_hold: 200, + mod2_grain_gap: 200, + mod2_start_position: 0.0, + mod2_end_position: 1.0, + mod2_grain_crossfade: 50, + mod2_osc_type: VoiceType::Sine, + mod2_osc_octave: 0, + mod2_osc_semitones: 0, + mod2_osc_detune: 0.0, + mod2_osc_attack: 0.0001, + mod2_osc_decay: 0.0001, + mod2_osc_sustain: 999.9, + mod2_osc_release: 5.0, + mod2_osc_retrigger: RetriggerStyle::Retrigger, + mod2_osc_atk_curve: SmoothStyle::Linear, + mod2_osc_dec_curve: SmoothStyle::Linear, + mod2_osc_rel_curve: SmoothStyle::Linear, + mod2_osc_unison: 1, + mod2_osc_unison_detune: 0.0, + mod2_osc_stereo: 0.0, + + mod3_audio_module_type: AudioModuleType::Off, + mod3_audio_module_level: 1.0, + mod3_loaded_sample: vec![vec![0.0, 0.0]], + mod3_sample_lib: vec![vec![vec![0.0, 0.0]]], + mod3_loop_wavetable: false, + mod3_single_cycle: false, + mod3_restretch: true, + mod3_prev_restretch: false, + mod3_grain_hold: 200, + mod3_grain_gap: 200, + mod3_start_position: 0.0, + mod3_end_position: 1.0, + mod3_grain_crossfade: 50, + mod3_osc_type: VoiceType::Sine, + mod3_osc_octave: 0, + mod3_osc_semitones: 0, + mod3_osc_detune: 0.0, + mod3_osc_attack: 0.0001, + mod3_osc_decay: 0.0001, + mod3_osc_sustain: 999.9, + mod3_osc_release: 5.0, + mod3_osc_retrigger: RetriggerStyle::Retrigger, + mod3_osc_atk_curve: SmoothStyle::Linear, + mod3_osc_dec_curve: SmoothStyle::Linear, + mod3_osc_rel_curve: SmoothStyle::Linear, + mod3_osc_unison: 1, + mod3_osc_unison_detune: 0.0, + mod3_osc_stereo: 0.0, + + filter_wet: 1.0, + filter_cutoff: 20000.0, + filter_resonance: 1.0, + filter_res_type: ResonanceType::Default, + filter_lp_amount: 1.0, + filter_hp_amount: 0.0, + filter_bp_amount: 0.0, + filter_env_peak: 0.0, + filter_env_attack: 0.0, + filter_env_decay: 0.0001, + filter_env_sustain: 999.9, + filter_env_release: 5.0, + filter_env_atk_curve: SmoothStyle::Linear, + filter_env_dec_curve: SmoothStyle::Linear, + filter_env_rel_curve: SmoothStyle::Linear, + filter_alg_type: FilterAlgorithms::SVF, + tilt_filter_type: ArduraFilter::ResponseType::Lowpass, + + filter_wet_2: 1.0, + filter_cutoff_2: 20000.0, + filter_resonance_2: 1.0, + filter_res_type_2: ResonanceType::Default, + filter_lp_amount_2: 1.0, + filter_hp_amount_2: 0.0, + filter_bp_amount_2: 0.0, + filter_env_peak_2: 0.0, + filter_env_attack_2: 0.0, + filter_env_decay_2: 0.0001, + filter_env_sustain_2: 999.9, + filter_env_release_2: 5.0, + filter_env_atk_curve_2: SmoothStyle::Linear, + filter_env_dec_curve_2: SmoothStyle::Linear, + filter_env_rel_curve_2: SmoothStyle::Linear, + filter_alg_type_2: FilterAlgorithms::SVF, + tilt_filter_type_2: ArduraFilter::ResponseType::Lowpass, + + filter_routing: FilterRouting::Parallel, + filter_cutoff_link: false, + + // LFOs + lfo1_enable: false, + lfo2_enable: false, + lfo3_enable: false, + + lfo1_freq: 2.0, + lfo1_retrigger: LFOController::LFORetrigger::None, + lfo1_sync: true, + lfo1_snap: LFOController::LFOSnapValues::Half, + lfo1_waveform: LFOController::Waveform::Sine, + lfo1_phase: 0.0, + + lfo2_freq: 2.0, + lfo2_retrigger: LFOController::LFORetrigger::None, + lfo2_sync: true, + lfo2_snap: LFOController::LFOSnapValues::Half, + lfo2_waveform: LFOController::Waveform::Sine, + lfo2_phase: 0.0, + + lfo3_freq: 2.0, + lfo3_retrigger: LFOController::LFORetrigger::None, + lfo3_sync: true, + lfo3_snap: LFOController::LFOSnapValues::Half, + lfo3_waveform: LFOController::Waveform::Sine, + lfo3_phase: 0.0, + + // Modulations + mod_source_1: ModulationSource::None, + mod_source_2: ModulationSource::None, + mod_source_3: ModulationSource::None, + mod_source_4: ModulationSource::None, + mod_dest_1: ModulationDestination::None, + mod_dest_2: ModulationDestination::None, + mod_dest_3: ModulationDestination::None, + mod_dest_4: ModulationDestination::None, + mod_amount_1: 0.0, + mod_amount_2: 0.0, + mod_amount_3: 0.0, + mod_amount_4: 0.0, + + // EQ + pre_use_eq: false, + pre_low_freq: 800.0, + pre_mid_freq: 3000.0, + pre_high_freq: 10000.0, + pre_low_gain: 0.0, + pre_mid_gain: 0.0, + pre_high_gain: 0.0, + + // FX + use_fx: true, + + use_compressor: false, + comp_amt: 0.5, + comp_atk: 0.5, + comp_rel: 0.5, + comp_drive: 0.5, + + use_abass: false, + abass_amount: 0.0011, + + use_saturation: false, + sat_amount: 0.0, + sat_type: SaturationType::Tape, + + use_delay: false, + delay_amount: 0.0, + delay_time: DelaySnapValues::Quarter, + delay_decay: 0.0, + delay_type: DelayType::Stereo, + + use_reverb: false, + reverb_amount: 0.5, + reverb_size: 0.5, + reverb_feedback: 0.5, + + use_phaser: false, + phaser_amount: 0.5, + phaser_depth: 0.5, + phaser_rate: 0.5, + phaser_feedback: 0.5, + + use_buffermod: false, + buffermod_amount: 0.5, + buffermod_depth: 0.5, + buffermod_rate: 0.5, + buffermod_spread: 0.0, + buffermod_timing: 620.0, + + use_flanger: false, + flanger_amount: 0.5, + flanger_depth: 0.5, + flanger_rate: 0.5, + flanger_feedback: 0.5, + + use_limiter: false, + limiter_threshold: 0.5, + limiter_knee: 0.5, + }); + convert_preset_v114(old_unserialized) +} + + // This takes the deserialized message pack and converts it into the old struct if it can // This then attempts to return the newer preset format after pub fn load_unserialized_old(file_data: Vec) -> ActuatePreset { @@ -547,6 +1075,249 @@ pub fn load_unserialized_old(file_data: Vec) -> ActuatePreset { convert_preset(old_unserialized) } +// This will get cloned each time we change preset styles in actuate +fn convert_preset_v114(preset: ActuatePresetV114) -> ActuatePreset { + let new_format: ActuatePreset = ActuatePreset { + preset_name: preset.preset_name, + preset_info: preset.preset_info, + preset_category: preset.preset_category, + tag_acid: preset.tag_acid, + tag_analog: preset.tag_analog, + tag_bright: preset.tag_bright, + tag_chord: preset.tag_chord, + tag_crisp: preset.tag_crisp, + tag_deep: preset.tag_deep, + tag_delicate: preset.tag_delicate, + tag_hard: preset.tag_hard, + tag_harsh: preset.tag_harsh, + tag_lush: preset.tag_lush, + tag_mellow: preset.tag_mellow, + tag_resonant: preset.tag_resonant, + tag_rich: preset.tag_rich, + tag_sharp: preset.tag_sharp, + tag_silky: preset.tag_silky, + tag_smooth: preset.tag_smooth, + tag_soft: preset.tag_soft, + tag_stab: preset.tag_stab, + tag_warm: preset.tag_warm, + mod1_audio_module_type: preset.mod1_audio_module_type, + mod1_audio_module_level: preset.mod1_audio_module_level, + mod1_loaded_sample: preset.mod1_loaded_sample, + mod1_sample_lib: preset.mod1_sample_lib, + mod1_loop_wavetable: preset.mod1_loop_wavetable, + mod1_single_cycle: preset.mod1_single_cycle, + mod1_restretch: preset.mod1_restretch, + mod1_prev_restretch: preset.mod1_prev_restretch, + mod1_grain_hold: preset.mod1_grain_hold, + mod1_grain_gap: preset.mod1_grain_gap, + mod1_start_position: preset.mod1_start_position, + mod1_end_position: preset.mod1_end_position, + mod1_grain_crossfade: preset.mod1_grain_crossfade, + mod1_osc_type: preset.mod1_osc_type, + mod1_osc_octave: preset.mod1_osc_octave, + mod1_osc_semitones: preset.mod1_osc_semitones, + mod1_osc_detune: preset.mod1_osc_detune, + mod1_osc_attack: preset.mod1_osc_attack, + mod1_osc_decay: preset.mod1_osc_decay, + mod1_osc_sustain: preset.mod1_osc_sustain, + mod1_osc_release: preset.mod1_osc_release, + mod1_osc_retrigger: preset.mod1_osc_retrigger, + mod1_osc_atk_curve: preset.mod1_osc_atk_curve, + mod1_osc_dec_curve: preset.mod1_osc_dec_curve, + mod1_osc_rel_curve: preset.mod1_osc_rel_curve, + mod1_osc_unison: preset.mod1_osc_unison, + mod1_osc_unison_detune: preset.mod1_osc_unison_detune, + mod1_osc_stereo: preset.mod1_osc_stereo, + mod2_audio_module_type: preset.mod2_audio_module_type, + mod2_audio_module_level: preset.mod2_audio_module_level, + mod2_loaded_sample: preset.mod2_loaded_sample, + mod2_sample_lib: preset.mod2_sample_lib, + mod2_loop_wavetable: preset.mod2_loop_wavetable, + mod2_single_cycle: preset.mod2_single_cycle, + mod2_restretch: preset.mod2_restretch, + mod2_prev_restretch: preset.mod2_prev_restretch, + mod2_grain_hold: preset.mod2_grain_hold, + mod2_grain_gap: preset.mod2_grain_gap, + mod2_start_position: preset.mod2_start_position, + mod2_end_position: preset.mod2_end_position, + mod2_grain_crossfade: preset.mod2_grain_crossfade, + mod2_osc_type: preset.mod2_osc_type, + mod2_osc_octave: preset.mod2_osc_octave, + mod2_osc_semitones: preset.mod2_osc_semitones, + mod2_osc_detune: preset.mod2_osc_detune, + mod2_osc_attack: preset.mod2_osc_attack, + mod2_osc_decay: preset.mod2_osc_decay, + mod2_osc_sustain: preset.mod2_osc_sustain, + mod2_osc_release: preset.mod2_osc_release, + mod2_osc_retrigger: preset.mod2_osc_retrigger, + mod2_osc_atk_curve: preset.mod2_osc_atk_curve, + mod2_osc_dec_curve: preset.mod2_osc_dec_curve, + mod2_osc_rel_curve: preset.mod2_osc_rel_curve, + mod2_osc_unison: preset.mod2_osc_unison, + mod2_osc_unison_detune: preset.mod2_osc_unison_detune, + mod2_osc_stereo: preset.mod2_osc_stereo, + mod3_audio_module_type: preset.mod3_audio_module_type, + mod3_audio_module_level: preset.mod3_audio_module_level, + mod3_loaded_sample: preset.mod3_loaded_sample, + mod3_sample_lib: preset.mod3_sample_lib, + mod3_loop_wavetable: preset.mod3_loop_wavetable, + mod3_single_cycle: preset.mod3_single_cycle, + mod3_restretch: preset.mod3_restretch, + mod3_prev_restretch: preset.mod3_prev_restretch, + mod3_grain_hold: preset.mod3_grain_hold, + mod3_grain_gap: preset.mod3_grain_gap, + mod3_start_position: preset.mod3_start_position, + mod3_end_position: preset.mod3_end_position, + mod3_grain_crossfade: preset.mod3_grain_crossfade, + mod3_osc_type: preset.mod3_osc_type, + mod3_osc_octave: preset.mod3_osc_octave, + mod3_osc_semitones: preset.mod3_osc_semitones, + mod3_osc_detune: preset.mod3_osc_detune, + mod3_osc_attack: preset.mod3_osc_attack, + mod3_osc_decay: preset.mod3_osc_decay, + mod3_osc_sustain: preset.mod3_osc_sustain, + mod3_osc_release: preset.mod3_osc_release, + mod3_osc_retrigger: preset.mod3_osc_retrigger, + mod3_osc_atk_curve: preset.mod3_osc_atk_curve, + mod3_osc_dec_curve: preset.mod3_osc_dec_curve, + mod3_osc_rel_curve: preset.mod3_osc_rel_curve, + mod3_osc_unison: preset.mod3_osc_unison, + mod3_osc_unison_detune: preset.mod3_osc_unison_detune, + mod3_osc_stereo: preset.mod3_osc_stereo, + filter_wet: preset.filter_wet, + filter_cutoff: preset.filter_cutoff, + filter_resonance: preset.filter_resonance, + filter_res_type: preset.filter_res_type, + filter_lp_amount: preset.filter_lp_amount, + filter_hp_amount: preset.filter_hp_amount, + filter_bp_amount: preset.filter_bp_amount, + filter_env_peak: preset.filter_env_peak, + filter_env_attack: preset.filter_env_attack, + filter_env_decay: preset.filter_env_decay, + filter_env_sustain: preset.filter_env_sustain, + filter_env_release: preset.filter_env_release, + filter_env_atk_curve: preset.filter_env_atk_curve, + filter_env_dec_curve: preset.filter_env_dec_curve, + filter_env_rel_curve: preset.filter_env_rel_curve, + filter_alg_type: preset.filter_alg_type, + tilt_filter_type: preset.tilt_filter_type, + filter_wet_2: preset.filter_wet_2, + filter_cutoff_2: preset.filter_cutoff_2, + filter_resonance_2: preset.filter_resonance_2, + filter_res_type_2: preset.filter_res_type_2, + filter_lp_amount_2: preset.filter_lp_amount_2, + filter_hp_amount_2: preset.filter_hp_amount_2, + filter_bp_amount_2: preset.filter_bp_amount_2, + filter_env_peak_2: preset.filter_env_peak_2, + filter_env_attack_2: preset.filter_env_attack_2, + filter_env_decay_2: preset.filter_env_decay_2, + filter_env_sustain_2: preset.filter_env_sustain_2, + filter_env_release_2: preset.filter_env_release_2, + filter_env_atk_curve_2: preset.filter_env_atk_curve_2, + filter_env_dec_curve_2: preset.filter_env_dec_curve_2, + filter_env_rel_curve_2: preset.filter_env_rel_curve_2, + filter_alg_type_2: preset.filter_alg_type_2, + tilt_filter_type_2: preset.tilt_filter_type_2, + filter_routing: preset.filter_routing, + /////////////////////////////////////////////////////////////////// + // Added in 1.1.4 + filter_cutoff_link: preset.filter_cutoff_link, + /////////////////////////////////////////////////////////////////// + // Added in pitch update 1.2.1 + pitch_enable: false, + pitch_routing: PitchRouting::Osc1, + pitch_env_peak: 0.0, + pitch_env_atk_curve: SmoothStyle::Linear, + pitch_env_dec_curve: SmoothStyle::Linear, + pitch_env_rel_curve: SmoothStyle::Linear, + pitch_env_attack: 0.0, + pitch_env_decay: 300.0, + pitch_env_release: 0.0, + pitch_env_sustain: 0.0, + /////////////////////////////////////////////////////////////////// + lfo1_enable: preset.lfo1_enable, + lfo2_enable: preset.lfo2_enable, + lfo3_enable: preset.lfo3_enable, + lfo1_freq: preset.lfo1_freq, + lfo1_retrigger: preset.lfo1_retrigger, + lfo1_sync: preset.lfo1_sync, + lfo1_snap: preset.lfo1_snap, + lfo1_waveform: preset.lfo1_waveform, + lfo1_phase: preset.lfo1_phase, + lfo2_freq: preset.lfo2_freq, + lfo2_retrigger: preset.lfo2_retrigger, + lfo2_sync: preset.lfo2_sync, + lfo2_snap: preset.lfo2_snap, + lfo2_waveform: preset.lfo2_waveform, + lfo2_phase: preset.lfo2_phase, + lfo3_freq: preset.lfo3_freq, + lfo3_retrigger: preset.lfo3_retrigger, + lfo3_sync: preset.lfo3_sync, + lfo3_snap: preset.lfo3_snap, + lfo3_waveform: preset.lfo3_waveform, + lfo3_phase: preset.lfo3_phase, + mod_source_1: preset.mod_source_1, + mod_source_2: preset.mod_source_2, + mod_source_3: preset.mod_source_3, + mod_source_4: preset.mod_source_4, + mod_dest_1: preset.mod_dest_1, + mod_dest_2: preset.mod_dest_2, + mod_dest_3: preset.mod_dest_3, + mod_dest_4: preset.mod_dest_4, + mod_amount_1: preset.mod_amount_1, + mod_amount_2: preset.mod_amount_2, + mod_amount_3: preset.mod_amount_3, + mod_amount_4: preset.mod_amount_4, + pre_use_eq: preset.pre_use_eq, + pre_low_freq: preset.pre_low_freq, + pre_mid_freq: preset.pre_mid_freq, + pre_high_freq: preset.pre_high_freq, + pre_low_gain: preset.pre_low_gain, + pre_mid_gain: preset.pre_mid_gain, + pre_high_gain: preset.pre_high_gain, + use_fx: preset.use_fx, + use_compressor: preset.use_compressor, + comp_amt: preset.comp_amt, + comp_atk: preset.comp_atk, + comp_rel: preset.comp_rel, + comp_drive: preset.comp_drive, + use_abass: preset.use_abass, + abass_amount: preset.abass_amount, + use_saturation: preset.use_saturation, + sat_amount: preset.sat_amount, + sat_type: preset.sat_type, + use_delay: preset.use_delay, + delay_amount: preset.delay_amount, + delay_time: preset.delay_time, + delay_decay: preset.delay_decay, + delay_type: preset.delay_type, + use_reverb: preset.use_reverb, + reverb_amount: preset.reverb_amount, + reverb_size: preset.reverb_size, + reverb_feedback: preset.reverb_feedback, + use_phaser: preset.use_phaser, + phaser_amount: preset.phaser_amount, + phaser_depth: preset.phaser_depth, + phaser_rate: preset.phaser_rate, + phaser_feedback: preset.phaser_feedback, + use_buffermod: preset.use_buffermod, + buffermod_amount: preset.buffermod_amount, + buffermod_depth: preset.buffermod_depth, + buffermod_rate: preset.buffermod_rate, + buffermod_spread: preset.buffermod_spread, + buffermod_timing: preset.buffermod_timing, + use_flanger: preset.use_flanger, + flanger_amount: preset.flanger_amount, + flanger_depth: preset.flanger_depth, + flanger_rate: preset.flanger_rate, + flanger_feedback: preset.flanger_feedback, + use_limiter: preset.use_limiter, + limiter_threshold: preset.limiter_threshold, + limiter_knee: preset.limiter_knee, + }; + new_format +} + // This will get cloned each time we change preset styles in actuate fn convert_preset(preset: ActuatePresetOld) -> ActuatePreset { let new_format: ActuatePreset = ActuatePreset { @@ -692,8 +1463,21 @@ fn convert_preset(preset: ActuatePresetOld) -> ActuatePreset { tilt_filter_type_2: preset.tilt_filter_type_2, filter_routing: preset.filter_routing, /////////////////////////////////////////////////////////////////// + // Added in 1.1.4 filter_cutoff_link: false, /////////////////////////////////////////////////////////////////// + // Added in pitch update 1.2.1 + pitch_enable: false, + pitch_routing: PitchRouting::Osc1, + pitch_env_peak: 0.0, + pitch_env_atk_curve: SmoothStyle::Linear, + pitch_env_dec_curve: SmoothStyle::Linear, + pitch_env_rel_curve: SmoothStyle::Linear, + pitch_env_attack: 0.0, + pitch_env_decay: 300.0, + pitch_env_release: 0.0, + pitch_env_sustain: 0.0, + /////////////////////////////////////////////////////////////////// lfo1_enable: preset.lfo1_enable, lfo2_enable: preset.lfo2_enable, lfo3_enable: preset.lfo3_enable, From 8d0cfb38dc98aa31662f69c8db7534a2928a325c Mon Sep 17 00:00:00 2001 From: ardura Date: Thu, 29 Feb 2024 10:14:24 -0700 Subject: [PATCH 2/3] Pitch Env 1 fully working poc --- src/audio_module.rs | 284 +++++++++++++++++++++++++++++++++++++++++--- src/lib.rs | 10 +- 2 files changed, 273 insertions(+), 21 deletions(-) diff --git a/src/audio_module.rs b/src/audio_module.rs index 898ef30..ef3d4ae 100644 --- a/src/audio_module.rs +++ b/src/audio_module.rs @@ -54,6 +54,7 @@ use crate::{ SYNTH_MIDDLE_BLUE, SYNTH_SOFT_BLUE, SYNTH_SOFT_BLUE2, + PitchRouting }; use CustomParamSlider::ParamSlider as HorizontalParamSlider; use CustomVerticalSlider::ParamSlider as VerticalParamSlider; @@ -98,6 +99,14 @@ struct SingleVoice { osc_attack: Smoother, osc_decay: Smoother, osc_release: Smoother, + // Pitch modulation info + pitch_enabled: bool, + pitch_current: f32, + pitch_env_peak: f32, + pitch_state: Oscillator::OscState, + pitch_attack: Smoother, + pitch_decay: Smoother, + pitch_release: Smoother, // Final info for a note to work _detune: f32, _unison_detune_value: f32, @@ -198,6 +207,17 @@ pub struct AudioModule { // Noise variables noise_obj: Oscillator::DeterministicWhiteNoiseGenerator, + + // Pitch mod storage + pitch_enable: bool, + pitch_env_peak: f32, + pitch_env_attack: f32, + pitch_env_decay: f32, + pitch_env_sustain: f32, + pitch_env_release: f32, + pitch_env_atk_curve: SmoothStyle, + pitch_env_dec_curve: SmoothStyle, + pitch_env_rel_curve: SmoothStyle, } // When you create a new audio module you need to add its default creation here as well @@ -254,6 +274,17 @@ impl Default for AudioModule { // Noise variables noise_obj: DeterministicWhiteNoiseGenerator::new(371722539), + + // Pitch mod storage + pitch_enable: false, + pitch_env_peak: 0.0, + pitch_env_attack: 0.0, + pitch_env_decay: 300.0, + pitch_env_sustain: 0.0, + pitch_env_release: 0.0, + pitch_env_atk_curve: SmoothStyle::Linear, + pitch_env_dec_curve: SmoothStyle::Linear, + pitch_env_rel_curve: SmoothStyle::Linear, } } } @@ -1738,6 +1769,31 @@ impl AudioModule { uni_voice.note -= semi_shift as u8; } } + match params.pitch_routing.value() { + PitchRouting::Osc1 | PitchRouting::Osc1_Osc2 | PitchRouting::Osc1_Osc3 | PitchRouting::All => { + self.pitch_enable = params.pitch_enable.value(); + self.pitch_env_peak = params.pitch_env_peak.value(); + self.pitch_env_attack = params.pitch_env_attack.value(); + self.pitch_env_decay = params.pitch_env_decay.value(); + self.pitch_env_release = params.pitch_env_release.value(); + self.pitch_env_atk_curve = params.pitch_env_atk_curve.value(); + self.pitch_env_dec_curve = params.pitch_env_dec_curve.value(); + self.pitch_env_rel_curve = params.pitch_env_rel_curve.value(); + } + _ => { + self.pitch_enable = false; + // I ended up copying these over to prevent panics on init + /* + self.pitch_env_peak = params.pitch_env_peak.value(); + self.pitch_env_attack = params.pitch_env_attack.value(); + self.pitch_env_decay = params.pitch_env_decay.value(); + self.pitch_env_release = params.pitch_env_release.value(); + self.pitch_env_atk_curve = params.pitch_env_atk_curve.value(); + self.pitch_env_dec_curve = params.pitch_env_dec_curve.value(); + self.pitch_env_rel_curve = params.pitch_env_rel_curve.value(); + */ + } + } self.osc_semitones = params.osc_1_semitones.value(); self.osc_detune = params.osc_1_detune.value(); self.osc_attack = params.osc_1_attack.value(); @@ -1782,6 +1838,20 @@ impl AudioModule { uni_voice.note -= semi_shift as u8; } } + match params.pitch_routing.value() { + PitchRouting::Osc2 | PitchRouting::Osc1_Osc2 | PitchRouting::Osc2_Osc3 | PitchRouting::All => { + self.pitch_enable = params.pitch_enable.value(); + self.pitch_env_attack = params.pitch_env_attack.value(); + self.pitch_env_decay = params.pitch_env_decay.value(); + self.pitch_env_release = params.pitch_env_release.value(); + self.pitch_env_atk_curve = params.pitch_env_atk_curve.value(); + self.pitch_env_dec_curve = params.pitch_env_dec_curve.value(); + self.pitch_env_rel_curve = params.pitch_env_rel_curve.value(); + } + _ => { + self.pitch_enable = false; + } + } self.osc_semitones = params.osc_2_semitones.value(); self.osc_detune = params.osc_2_detune.value(); self.osc_attack = params.osc_2_attack.value(); @@ -1826,6 +1896,20 @@ impl AudioModule { uni_voice.note -= semi_shift as u8; } } + match params.pitch_routing.value() { + PitchRouting::Osc3 | PitchRouting::Osc2_Osc3 | PitchRouting::Osc1_Osc3 | PitchRouting::All => { + self.pitch_enable = params.pitch_enable.value(); + self.pitch_env_attack = params.pitch_env_attack.value(); + self.pitch_env_decay = params.pitch_env_decay.value(); + self.pitch_env_release = params.pitch_env_release.value(); + self.pitch_env_atk_curve = params.pitch_env_atk_curve.value(); + self.pitch_env_dec_curve = params.pitch_env_dec_curve.value(); + self.pitch_env_rel_curve = params.pitch_env_rel_curve.value(); + } + _ => { + self.pitch_enable = false; + } + } self.osc_semitones = params.osc_3_semitones.value(); self.osc_detune = params.osc_3_detune.value(); self.osc_attack = params.osc_3_attack.value(); @@ -1877,10 +1961,6 @@ impl AudioModule { // The event was valid Some(mut event) => { event = event_passed.unwrap(); - // Testing removing this - //if event.timing() > sample_id as u32 { - //return (0.0, 0.0, false, false); - //} match event { //////////////////////////////////////////////////////////// // MIDI EVENT NOTE ON @@ -1892,6 +1972,77 @@ impl AudioModule { note_on = true; let mut new_phase: f32 = 0.0; + // Calculate our pitch mod stuff if applicable + let pitch_attack_smoother: Smoother; + let pitch_decay_smoother: Smoother; + let pitch_release_smoother: Smoother; + let pitch_mod_current: f32; + if self.pitch_enable { + pitch_attack_smoother = match self.pitch_env_atk_curve { + SmoothStyle::Linear => { + Smoother::new(SmoothingStyle::Linear(self.pitch_env_attack)) + } + SmoothStyle::Logarithmic => Smoother::new(SmoothingStyle::Logarithmic( + self.pitch_env_attack.clamp(0.0001, 999.9), + )), + SmoothStyle::Exponential => { + Smoother::new(SmoothingStyle::Exponential(self.pitch_env_attack)) + } + SmoothStyle::LogSteep => { + Smoother::new(SmoothingStyle::LogSteep(self.pitch_env_attack)) + } + }; + + pitch_decay_smoother = match self.pitch_env_dec_curve { + SmoothStyle::Linear => { + Smoother::new(SmoothingStyle::Linear(self.pitch_env_decay)) + } + SmoothStyle::Logarithmic => Smoother::new(SmoothingStyle::Logarithmic( + self.pitch_env_decay.clamp(0.0001, 999.9), + )), + SmoothStyle::Exponential => { + Smoother::new(SmoothingStyle::Exponential(self.pitch_env_decay)) + } + SmoothStyle::LogSteep => { + Smoother::new(SmoothingStyle::LogSteep(self.pitch_env_decay)) + } + }; + + pitch_release_smoother = match self.pitch_env_rel_curve { + SmoothStyle::Linear => { + Smoother::new(SmoothingStyle::Linear(self.pitch_env_release)) + } + SmoothStyle::Logarithmic => Smoother::new(SmoothingStyle::Logarithmic( + self.pitch_env_release.clamp(0.0001, 999.9), + )), + SmoothStyle::Exponential => { + Smoother::new(SmoothingStyle::Exponential(self.pitch_env_release)) + } + SmoothStyle::LogSteep => { + Smoother::new(SmoothingStyle::LogSteep(self.pitch_env_release)) + } + }; + + match pitch_attack_smoother.style { + SmoothingStyle::Logarithmic(_) => { + pitch_attack_smoother.reset(0.0001); + pitch_attack_smoother + .set_target(self.sample_rate, self.pitch_env_peak.max(0.0001)); + } + _ => { + pitch_attack_smoother.reset(0.0); + pitch_attack_smoother.set_target(self.sample_rate, self.pitch_env_peak); + } + } + + pitch_mod_current = pitch_attack_smoother.next(); + } else { + pitch_attack_smoother = Smoother::new(SmoothingStyle::None); + pitch_decay_smoother = Smoother::new(SmoothingStyle::None); + pitch_release_smoother = Smoother::new(SmoothingStyle::None); + pitch_mod_current = 0.0; + } + // Sampler when single cycle needs this!!! if self.single_cycle { // 31 comes from comparing with 3xOsc position in MIDI notes @@ -1921,14 +2072,14 @@ impl AudioModule { // Shift our note per detune // I'm so glad nih-plug has this helper for f32 conversions! let base_note = if velocity_mod <= 0.0 { - note as f32 + self.osc_detune + detune_mod + note as f32 + self.osc_detune + detune_mod + pitch_mod_current } else { note as f32 + self.osc_detune + detune_mod + velocity_mod.clamp(0.0, 1.0) * velocity + + pitch_mod_current }; - //let detuned_note: f32 = util::f32_midi_note_to_freq(base_note); // Reset the retrigger on Oscs match self.osc_retrigger { @@ -1990,14 +2141,16 @@ impl AudioModule { base_note + uni_detune_mod + (uni_velocity_mod.clamp(0.0, 1.0) * velocity) - + detune_step * (unison_voice + 1) as f32, + + detune_step * (unison_voice + 1) as f32 + + pitch_mod_current, ); } else { unison_notes[unison_voice] = util::f32_midi_note_to_freq( base_note - uni_detune_mod - (uni_velocity_mod.clamp(0.0, 1.0) * velocity) - - detune_step * (unison_voice) as f32, + - detune_step * (unison_voice) as f32 + - pitch_mod_current, ); } } @@ -2132,6 +2285,13 @@ impl AudioModule { osc_attack: attack_smoother.clone(), osc_decay: decay_smoother.clone(), osc_release: release_smoother.clone(), + pitch_enabled: self.pitch_enable, + pitch_env_peak: self.pitch_env_peak, + pitch_current: pitch_mod_current, + pitch_state: OscState::Attacking, + pitch_attack: pitch_attack_smoother.clone(), + pitch_decay: pitch_decay_smoother.clone(), + pitch_release: pitch_release_smoother.clone(), _detune: self.osc_detune, _unison_detune_value: self.osc_unison_detune, //frequency: detuned_note, @@ -2199,6 +2359,13 @@ impl AudioModule { osc_attack: attack_smoother.clone(), osc_decay: decay_smoother.clone(), osc_release: release_smoother.clone(), + pitch_enabled: self.pitch_enable, + pitch_env_peak: self.pitch_env_peak, + pitch_current: pitch_mod_current, + pitch_state: OscState::Attacking, + pitch_attack: pitch_attack_smoother.clone(), + pitch_decay: pitch_decay_smoother.clone(), + pitch_release: pitch_release_smoother.clone(), _detune: self.osc_detune, _unison_detune_value: self.osc_unison_detune, //frequency: unison_notes[unison_voice], @@ -2245,6 +2412,13 @@ impl AudioModule { osc_attack: attack_smoother.clone(), osc_decay: decay_smoother.clone(), osc_release: release_smoother.clone(), + pitch_enabled: self.pitch_enable, + pitch_env_peak: self.pitch_env_peak, + pitch_current: 0.0, + pitch_state: OscState::Attacking, + pitch_attack: pitch_attack_smoother.clone(), + pitch_decay: pitch_decay_smoother.clone(), + pitch_release: pitch_release_smoother.clone(), _detune: 0.0, _unison_detune_value: 0.0, frequency: 0.0, @@ -2286,6 +2460,13 @@ impl AudioModule { osc_attack: attack_smoother.clone(), osc_decay: decay_smoother.clone(), osc_release: release_smoother.clone(), + pitch_enabled: self.pitch_enable, + pitch_env_peak: 0.0, + pitch_current: 0.0, + pitch_state: OscState::Off, + pitch_attack: pitch_attack_smoother.clone(), + pitch_decay: pitch_decay_smoother.clone(), + pitch_release: pitch_release_smoother.clone(), _detune: 0.0, _unison_detune_value: 0.0, frequency: 0.0, @@ -2371,7 +2552,7 @@ impl AudioModule { unison_voice.osc_release.reset(unison_voice.amp_current); // Set our new release target to 0.0 so the note fades match unison_voice.osc_release.style { - SmoothingStyle::Logarithmic(_) => { + SmoothingStyle::Logarithmic(_) | SmoothingStyle::LogSteep(_)=> { unison_voice .osc_release .set_target(self.sample_rate, 0.0001); @@ -2399,7 +2580,7 @@ impl AudioModule { // Set our new release target to 0.0 so the note fades match voice.osc_release.style { - SmoothingStyle::Logarithmic(_) => { + SmoothingStyle::Logarithmic(_) | SmoothingStyle::LogSteep(_) => { voice.osc_release.set_target(self.sample_rate, 0.0001); } _ => { @@ -2439,6 +2620,13 @@ impl AudioModule { osc_attack: Smoother::new(SmoothingStyle::None), osc_decay: Smoother::new(SmoothingStyle::None), osc_release: Smoother::new(SmoothingStyle::None), + pitch_enabled: false, + pitch_env_peak: 0.0, + pitch_current: 0.0, + pitch_state: OscState::Off, + pitch_attack: Smoother::new(SmoothingStyle::None), + pitch_decay: Smoother::new(SmoothingStyle::None), + pitch_release: Smoother::new(SmoothingStyle::None), _detune: 0.0, _unison_detune_value: 0.0, frequency: 0.0, @@ -2494,6 +2682,40 @@ impl AudioModule { if voice.phase > 1.0 { voice.phase -= 1.0; } + // This happens on extreme pitch envelope values only and catches wild increments + if voice.phase > 1.0 { + voice.phase = voice.phase % 1.0; + } + + // Move our pitch envelopes if this is an Osc + if self.audio_module_type == AudioModuleType::Osc && voice.pitch_enabled { + // Attack is over so use decay amount to reach sustain level - reusing current smoother + if voice.pitch_attack.steps_left() == 0 && voice.pitch_state == OscState::Attacking { + voice.pitch_state = OscState::Decaying; + voice.pitch_current = voice.pitch_attack.next(); + // Now we will use decay smoother from here + voice.pitch_decay.reset(voice.pitch_current); + let sustain_scaled = self.pitch_env_sustain / 999.9; + voice.pitch_decay.set_target(self.sample_rate, sustain_scaled.clamp(0.0001, 999.9)); + } + + // Move from Decaying to Sustain hold + if voice.pitch_decay.steps_left() == 0 && voice.pitch_state == OscState::Decaying { + let sustain_scaled = self.osc_sustain / 999.9; + voice.pitch_current = sustain_scaled; + voice.pitch_decay.set_target(self.sample_rate, sustain_scaled.clamp(0.0001, 999.9)); + voice.pitch_state = OscState::Sustaining; + } + + // End of release + if voice.pitch_state == OscState::Releasing && voice.pitch_release.steps_left() == 0 { + voice.pitch_state = OscState::Off; + } + } else { + // Reassign here for safety + voice.pitch_current = 0.0; + voice.pitch_state = OscState::Off; + } // Move from attack to decay if needed // Attack is over so use decay amount to reach sustain level - reusing current smoother @@ -2593,6 +2815,13 @@ impl AudioModule { osc_attack: voice.osc_attack.clone(), osc_decay: voice.osc_decay.clone(), osc_release: voice.osc_release.clone(), + pitch_enabled: voice.pitch_enabled, + pitch_env_peak: voice.pitch_env_peak, + pitch_current: voice.pitch_current, + pitch_state: voice.pitch_state, + pitch_attack: voice.pitch_attack.clone(), + pitch_decay: voice.pitch_decay.clone(), + pitch_release: voice.pitch_release.clone(), _detune: voice._detune, _unison_detune_value: voice._unison_detune_value, frequency: voice.frequency, @@ -2695,6 +2924,25 @@ impl AudioModule { let mut stereo_voices_r: f32 = 0.0; let mut center_voices: f32 = 0.0; for voice in self.playing_voices.voices.iter_mut() { + // Move the pitch envelope stuff independently of the MIDI info + if voice.pitch_enabled { + voice.pitch_current = match voice.pitch_state { + OscState::Attacking => { + voice.pitch_attack.next() + }, + OscState::Decaying => { + voice.pitch_decay.next() + }, + OscState::Sustaining => { + self.pitch_env_sustain / 999.9 + }, + OscState::Releasing => { + voice.pitch_release.next() + }, + OscState::Off => 0.0, + } + } + let temp_osc_gain_multiplier: f32; // Get our current gain amount for use in match below // Include gain scaling if mod is there @@ -2726,17 +2974,19 @@ impl AudioModule { voice.amp_current = temp_osc_gain_multiplier; + let nyquist = self.sample_rate/2.0; if voice.vel_mod_amount == 0.0 { - let base_note = voice.note as f32 + voice._detune + detune_mod; + let base_note = voice.note as f32 + voice._detune + detune_mod + voice.pitch_current; voice.phase_delta = - util::f32_midi_note_to_freq(base_note) / self.sample_rate; + util::f32_midi_note_to_freq(base_note).min(nyquist) / self.sample_rate; } else { let base_note = voice.note as f32 + voice._detune + detune_mod - + (voice.vel_mod_amount * voice._velocity); + + (voice.vel_mod_amount * voice._velocity) + + voice.pitch_current; voice.phase_delta = - util::f32_midi_note_to_freq(base_note) / self.sample_rate; + util::f32_midi_note_to_freq(base_note).min(nyquist) / self.sample_rate; } if self.audio_module_type == AudioModuleType::Osc { @@ -2820,14 +3070,16 @@ impl AudioModule { if unison_voice.vel_mod_amount == 0.0 { let base_note = unison_voice.note as f32 + unison_voice._unison_detune_value - + uni_detune_mod; + + uni_detune_mod + + unison_voice.pitch_current; unison_voice.phase_delta = util::f32_midi_note_to_freq(base_note) / self.sample_rate; } else { let base_note = unison_voice.note as f32 + unison_voice._unison_detune_value + uni_detune_mod - + (unison_voice.vel_mod_amount * unison_voice._velocity); + + (unison_voice.vel_mod_amount * unison_voice._velocity) + + unison_voice.pitch_current; unison_voice.phase_delta = util::f32_midi_note_to_freq(base_note) / self.sample_rate; } diff --git a/src/lib.rs b/src/lib.rs index 35a0926..4fa82f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2592,15 +2592,15 @@ impl ActuateParams { // Pitch Envelope //////////////////////////////////////////////////////////////////////////////////// pitch_env_peak: FloatParam::new( - "Env Mod", + "PITCHAA", 0.0, FloatRange::Linear { - min: -14980.0, - max: 14980.0, + min: -144.0, + max: 144.0, }, ) - .with_step_size(1.0) - .with_value_to_string(format_nothing()) + //.with_step_size(1.0) + //.with_value_to_string(format_nothing()) .with_callback({ let update_something = update_something.clone(); Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) From bc898964c21cc20c727a70f1e40bf7b85df5edf9 Mon Sep 17 00:00:00 2001 From: ardura Date: Fri, 1 Mar 2024 08:27:06 -0700 Subject: [PATCH 3/3] Working pitches and preset load --- src/CustomWidgets/ui_knob.rs | 38 ++- src/audio_module.rs | 346 ++++++++++++++++++++++++++-- src/lib.rs | 435 +++++++++++++++++++++++++++++++---- src/old_preset_structs.rs | 51 +++- 4 files changed, 785 insertions(+), 85 deletions(-) diff --git a/src/CustomWidgets/ui_knob.rs b/src/CustomWidgets/ui_knob.rs index a5f653d..f9f2d6c 100644 --- a/src/CustomWidgets/ui_knob.rs +++ b/src/CustomWidgets/ui_knob.rs @@ -22,7 +22,8 @@ use nih_plug_egui::egui::{ /// When shift+dragging a parameter, one pixel dragged corresponds to this much change in the /// noramlized parameter. -const GRANULAR_DRAG_MULTIPLIER: f32 = 0.0015; +const GRANULAR_DRAG_MULTIPLIER: f32 = 0.001; +const NORMAL_DRAG_MULTIPLIER: f32 = 0.005; lazy_static! { static ref DRAG_NORMALIZED_START_VALUE_MEMORY_ID: egui::Id = egui::Id::new((file!(), 0)); @@ -99,7 +100,7 @@ impl<'a, P: Param> SliderRegion<'a, P> { Self::get_drag_normalized_start_value_memory(ui) }; - let total_drag_distance = drag_delta.x + Self::get_drag_amount_memory(ui); + let total_drag_distance = -drag_delta.y + Self::get_drag_amount_memory(ui); Self::set_drag_amount_memory(ui, total_drag_distance); self.set_normalized_value( @@ -107,6 +108,23 @@ impl<'a, P: Param> SliderRegion<'a, P> { ); } + // Copied this to modify the normal drag behavior to not match a slider + fn normal_drag(&self, ui: &Ui, drag_delta: Vec2) { + let start_value = if Self::get_drag_amount_memory(ui) == 0.0 { + Self::set_drag_normalized_start_value_memory(ui, self.normalized_value()); + self.normalized_value() + } else { + Self::get_drag_normalized_start_value_memory(ui) + }; + + let total_drag_distance = -drag_delta.y + Self::get_drag_amount_memory(ui); + Self::set_drag_amount_memory(ui, total_drag_distance); + + self.set_normalized_value( + (start_value + (total_drag_distance * NORMAL_DRAG_MULTIPLIER)).clamp(0.0, 1.0), + ); + } + // Handle the input for a given response. Returns an f32 containing the normalized value of // the parameter. fn handle_response(&self, ui: &Ui, response: &mut Response) -> f32 { @@ -119,7 +137,7 @@ impl<'a, P: Param> SliderRegion<'a, P> { self.param_setter.begin_set_parameter(self.param); Self::set_drag_amount_memory(ui, 0.0); } - if let Some(click_pos) = response.interact_pointer_pos() { + if let Some(_clicked_pos) = response.interact_pointer_pos() { if ui.input(|mem| mem.modifiers.command) { // Like double clicking, Ctrl+Click should reset the parameter self.reset_param(); @@ -129,12 +147,9 @@ impl<'a, P: Param> SliderRegion<'a, P> { self.granular_drag(ui, response.drag_delta()); response.mark_changed(); } else { - let proportion = - egui::emath::remap_clamp(click_pos.y, response.rect.y_range(), 0.0..=1.0) - as f64; - self.set_normalized_value(1.0 - proportion as f32); + self.normal_drag(ui, response.drag_delta()); response.mark_changed(); - Self::set_drag_amount_memory(ui, 0.0); + //Self::set_drag_amount_memory(ui, 0.0); } } if response.double_clicked() { @@ -459,8 +474,11 @@ impl<'a, P: Param> Widget for ArcKnob<'a, P> { y: response.rect.right_bottom().y - 12.0, }, ); - ui.painter() - .rect_filled(readability_box, Rounding::from(16.0), self.fill_color); + ui.painter().rect_filled( + readability_box, + Rounding::from(16.0), + self.fill_color, + ); } let text_color: Color32; diff --git a/src/audio_module.rs b/src/audio_module.rs index ef3d4ae..033d835 100644 --- a/src/audio_module.rs +++ b/src/audio_module.rs @@ -107,6 +107,14 @@ struct SingleVoice { pitch_attack: Smoother, pitch_decay: Smoother, pitch_release: Smoother, + // Pitch modulation info 2 + pitch_enabled_2: bool, + pitch_current_2: f32, + pitch_env_peak_2: f32, + pitch_state_2: Oscillator::OscState, + pitch_attack_2: Smoother, + pitch_decay_2: Smoother, + pitch_release_2: Smoother, // Final info for a note to work _detune: f32, _unison_detune_value: f32, @@ -218,6 +226,15 @@ pub struct AudioModule { pitch_env_atk_curve: SmoothStyle, pitch_env_dec_curve: SmoothStyle, pitch_env_rel_curve: SmoothStyle, + pitch_enable_2: bool, + pitch_env_peak_2: f32, + pitch_env_attack_2: f32, + pitch_env_decay_2: f32, + pitch_env_sustain_2: f32, + pitch_env_release_2: f32, + pitch_env_atk_curve_2: SmoothStyle, + pitch_env_dec_curve_2: SmoothStyle, + pitch_env_rel_curve_2: SmoothStyle, } // When you create a new audio module you need to add its default creation here as well @@ -285,6 +302,16 @@ impl Default for AudioModule { pitch_env_atk_curve: SmoothStyle::Linear, pitch_env_dec_curve: SmoothStyle::Linear, pitch_env_rel_curve: SmoothStyle::Linear, + + pitch_enable_2: false, + pitch_env_peak_2: 0.0, + pitch_env_attack_2: 0.0, + pitch_env_decay_2: 300.0, + pitch_env_sustain_2: 0.0, + pitch_env_release_2: 0.0, + pitch_env_atk_curve_2: SmoothStyle::Linear, + pitch_env_dec_curve_2: SmoothStyle::Linear, + pitch_env_rel_curve_2: SmoothStyle::Linear, } } } @@ -1775,6 +1802,7 @@ impl AudioModule { self.pitch_env_peak = params.pitch_env_peak.value(); self.pitch_env_attack = params.pitch_env_attack.value(); self.pitch_env_decay = params.pitch_env_decay.value(); + self.pitch_env_sustain = params.pitch_env_sustain.value(); self.pitch_env_release = params.pitch_env_release.value(); self.pitch_env_atk_curve = params.pitch_env_atk_curve.value(); self.pitch_env_dec_curve = params.pitch_env_dec_curve.value(); @@ -1782,16 +1810,22 @@ impl AudioModule { } _ => { self.pitch_enable = false; - // I ended up copying these over to prevent panics on init - /* - self.pitch_env_peak = params.pitch_env_peak.value(); - self.pitch_env_attack = params.pitch_env_attack.value(); - self.pitch_env_decay = params.pitch_env_decay.value(); - self.pitch_env_release = params.pitch_env_release.value(); - self.pitch_env_atk_curve = params.pitch_env_atk_curve.value(); - self.pitch_env_dec_curve = params.pitch_env_dec_curve.value(); - self.pitch_env_rel_curve = params.pitch_env_rel_curve.value(); - */ + } + } + match params.pitch_routing_2.value() { + PitchRouting::Osc1 | PitchRouting::Osc1_Osc2 | PitchRouting::Osc1_Osc3 | PitchRouting::All => { + self.pitch_enable_2 = params.pitch_enable_2.value(); + self.pitch_env_peak_2 = params.pitch_env_peak_2.value(); + self.pitch_env_attack_2 = params.pitch_env_attack_2.value(); + self.pitch_env_decay_2 = params.pitch_env_decay_2.value(); + self.pitch_env_sustain_2 = params.pitch_env_sustain_2.value(); + self.pitch_env_release_2 = params.pitch_env_release_2.value(); + self.pitch_env_atk_curve_2 = params.pitch_env_atk_curve_2.value(); + self.pitch_env_dec_curve_2 = params.pitch_env_dec_curve_2.value(); + self.pitch_env_rel_curve_2 = params.pitch_env_rel_curve_2.value(); + } + _ => { + self.pitch_enable_2 = false; } } self.osc_semitones = params.osc_1_semitones.value(); @@ -1815,7 +1849,7 @@ impl AudioModule { self.grain_hold = params.grain_hold_1.value(); self.grain_gap = params.grain_gap_1.value(); self.grain_crossfade = params.grain_crossfade_1.value(); - } + }, 2 => { self.audio_module_type = params._audio_module_2_type.value(); self.osc_type = params.osc_2_type.value(); @@ -1841,8 +1875,10 @@ impl AudioModule { match params.pitch_routing.value() { PitchRouting::Osc2 | PitchRouting::Osc1_Osc2 | PitchRouting::Osc2_Osc3 | PitchRouting::All => { self.pitch_enable = params.pitch_enable.value(); + self.pitch_env_peak = params.pitch_env_peak.value(); self.pitch_env_attack = params.pitch_env_attack.value(); self.pitch_env_decay = params.pitch_env_decay.value(); + self.pitch_env_sustain = params.pitch_env_sustain.value(); self.pitch_env_release = params.pitch_env_release.value(); self.pitch_env_atk_curve = params.pitch_env_atk_curve.value(); self.pitch_env_dec_curve = params.pitch_env_dec_curve.value(); @@ -1851,7 +1887,23 @@ impl AudioModule { _ => { self.pitch_enable = false; } - } + } + match params.pitch_routing_2.value() { + PitchRouting::Osc2 | PitchRouting::Osc1_Osc2 | PitchRouting::Osc2_Osc3 | PitchRouting::All => { + self.pitch_enable_2 = params.pitch_enable_2.value(); + self.pitch_env_peak_2 = params.pitch_env_peak_2.value(); + self.pitch_env_attack_2 = params.pitch_env_attack_2.value(); + self.pitch_env_decay_2 = params.pitch_env_decay_2.value(); + self.pitch_env_sustain_2 = params.pitch_env_sustain_2.value(); + self.pitch_env_release_2 = params.pitch_env_release_2.value(); + self.pitch_env_atk_curve_2 = params.pitch_env_atk_curve_2.value(); + self.pitch_env_dec_curve_2 = params.pitch_env_dec_curve_2.value(); + self.pitch_env_rel_curve_2 = params.pitch_env_rel_curve_2.value(); + } + _ => { + self.pitch_enable_2 = false; + } + } self.osc_semitones = params.osc_2_semitones.value(); self.osc_detune = params.osc_2_detune.value(); self.osc_attack = params.osc_2_attack.value(); @@ -1873,7 +1925,7 @@ impl AudioModule { self.grain_hold = params.grain_hold_2.value(); self.grain_gap = params.grain_gap_2.value(); self.grain_crossfade = params.grain_crossfade_2.value(); - } + }, 3 => { self.audio_module_type = params._audio_module_3_type.value(); self.osc_type = params.osc_3_type.value(); @@ -1899,8 +1951,10 @@ impl AudioModule { match params.pitch_routing.value() { PitchRouting::Osc3 | PitchRouting::Osc2_Osc3 | PitchRouting::Osc1_Osc3 | PitchRouting::All => { self.pitch_enable = params.pitch_enable.value(); + self.pitch_env_peak = params.pitch_env_peak.value(); self.pitch_env_attack = params.pitch_env_attack.value(); self.pitch_env_decay = params.pitch_env_decay.value(); + self.pitch_env_sustain = params.pitch_env_sustain.value(); self.pitch_env_release = params.pitch_env_release.value(); self.pitch_env_atk_curve = params.pitch_env_atk_curve.value(); self.pitch_env_dec_curve = params.pitch_env_dec_curve.value(); @@ -1909,7 +1963,23 @@ impl AudioModule { _ => { self.pitch_enable = false; } - } + } + match params.pitch_routing_2.value() { + PitchRouting::Osc3 | PitchRouting::Osc2_Osc3 | PitchRouting::Osc1_Osc3 | PitchRouting::All => { + self.pitch_enable_2 = params.pitch_enable_2.value(); + self.pitch_env_peak_2 = params.pitch_env_peak_2.value(); + self.pitch_env_attack_2 = params.pitch_env_attack_2.value(); + self.pitch_env_decay_2 = params.pitch_env_decay_2.value(); + self.pitch_env_sustain_2 = params.pitch_env_sustain_2.value(); + self.pitch_env_release_2 = params.pitch_env_release_2.value(); + self.pitch_env_atk_curve_2 = params.pitch_env_atk_curve_2.value(); + self.pitch_env_dec_curve_2 = params.pitch_env_dec_curve_2.value(); + self.pitch_env_rel_curve_2 = params.pitch_env_rel_curve_2.value(); + } + _ => { + self.pitch_enable_2 = false; + } + } self.osc_semitones = params.osc_3_semitones.value(); self.osc_detune = params.osc_3_detune.value(); self.osc_attack = params.osc_3_attack.value(); @@ -1931,8 +2001,8 @@ impl AudioModule { self.grain_hold = params.grain_hold_3.value(); self.grain_gap = params.grain_gap_3.value(); self.grain_crossfade = params.grain_crossfade_3.value(); - } - _ => {} + }, + _ => {}, } } @@ -1977,6 +2047,10 @@ impl AudioModule { let pitch_decay_smoother: Smoother; let pitch_release_smoother: Smoother; let pitch_mod_current: f32; + let pitch_attack_smoother_2: Smoother; + let pitch_decay_smoother_2: Smoother; + let pitch_release_smoother_2: Smoother; + let pitch_mod_current_2: f32; if self.pitch_enable { pitch_attack_smoother = match self.pitch_env_atk_curve { SmoothStyle::Linear => { @@ -2042,6 +2116,72 @@ impl AudioModule { pitch_release_smoother = Smoother::new(SmoothingStyle::None); pitch_mod_current = 0.0; } + // Pitch mod 2 + if self.pitch_enable_2 { + pitch_attack_smoother_2 = match self.pitch_env_atk_curve_2 { + SmoothStyle::Linear => { + Smoother::new(SmoothingStyle::Linear(self.pitch_env_attack_2)) + } + SmoothStyle::Logarithmic => Smoother::new(SmoothingStyle::Logarithmic( + self.pitch_env_attack_2.clamp(0.0001, 999.9), + )), + SmoothStyle::Exponential => { + Smoother::new(SmoothingStyle::Exponential(self.pitch_env_attack_2)) + } + SmoothStyle::LogSteep => { + Smoother::new(SmoothingStyle::LogSteep(self.pitch_env_attack_2)) + } + }; + + pitch_decay_smoother_2 = match self.pitch_env_dec_curve_2 { + SmoothStyle::Linear => { + Smoother::new(SmoothingStyle::Linear(self.pitch_env_decay_2)) + } + SmoothStyle::Logarithmic => Smoother::new(SmoothingStyle::Logarithmic( + self.pitch_env_decay_2.clamp(0.0001, 999.9), + )), + SmoothStyle::Exponential => { + Smoother::new(SmoothingStyle::Exponential(self.pitch_env_decay_2)) + } + SmoothStyle::LogSteep => { + Smoother::new(SmoothingStyle::LogSteep(self.pitch_env_decay_2)) + } + }; + + pitch_release_smoother_2 = match self.pitch_env_rel_curve_2 { + SmoothStyle::Linear => { + Smoother::new(SmoothingStyle::Linear(self.pitch_env_release_2)) + } + SmoothStyle::Logarithmic => Smoother::new(SmoothingStyle::Logarithmic( + self.pitch_env_release_2.clamp(0.0001, 999.9), + )), + SmoothStyle::Exponential => { + Smoother::new(SmoothingStyle::Exponential(self.pitch_env_release_2)) + } + SmoothStyle::LogSteep => { + Smoother::new(SmoothingStyle::LogSteep(self.pitch_env_release_2)) + } + }; + + match pitch_attack_smoother_2.style { + SmoothingStyle::Logarithmic(_) => { + pitch_attack_smoother_2.reset(0.0001); + pitch_attack_smoother_2 + .set_target(self.sample_rate, self.pitch_env_peak_2.max(0.0001)); + } + _ => { + pitch_attack_smoother_2.reset(0.0); + pitch_attack_smoother_2.set_target(self.sample_rate, self.pitch_env_peak_2); + } + } + + pitch_mod_current_2 = pitch_attack_smoother_2.next(); + } else { + pitch_attack_smoother_2 = Smoother::new(SmoothingStyle::None); + pitch_decay_smoother_2 = Smoother::new(SmoothingStyle::None); + pitch_release_smoother_2 = Smoother::new(SmoothingStyle::None); + pitch_mod_current_2 = 0.0; + } // Sampler when single cycle needs this!!! if self.single_cycle { @@ -2072,13 +2212,14 @@ impl AudioModule { // Shift our note per detune // I'm so glad nih-plug has this helper for f32 conversions! let base_note = if velocity_mod <= 0.0 { - note as f32 + self.osc_detune + detune_mod + pitch_mod_current + note as f32 + self.osc_detune + detune_mod + pitch_mod_current + pitch_mod_current_2 } else { note as f32 + self.osc_detune + detune_mod + velocity_mod.clamp(0.0, 1.0) * velocity + pitch_mod_current + + pitch_mod_current_2 }; // Reset the retrigger on Oscs @@ -2142,7 +2283,8 @@ impl AudioModule { + uni_detune_mod + (uni_velocity_mod.clamp(0.0, 1.0) * velocity) + detune_step * (unison_voice + 1) as f32 - + pitch_mod_current, + + pitch_mod_current + + pitch_mod_current_2, ); } else { unison_notes[unison_voice] = util::f32_midi_note_to_freq( @@ -2150,7 +2292,8 @@ impl AudioModule { - uni_detune_mod - (uni_velocity_mod.clamp(0.0, 1.0) * velocity) - detune_step * (unison_voice) as f32 - - pitch_mod_current, + - pitch_mod_current + - pitch_mod_current_2, ); } } @@ -2292,6 +2435,13 @@ impl AudioModule { pitch_attack: pitch_attack_smoother.clone(), pitch_decay: pitch_decay_smoother.clone(), pitch_release: pitch_release_smoother.clone(), + pitch_enabled_2: self.pitch_enable_2, + pitch_env_peak_2: self.pitch_env_peak_2, + pitch_current_2: pitch_mod_current_2, + pitch_state_2: OscState::Attacking, + pitch_attack_2: pitch_attack_smoother_2.clone(), + pitch_decay_2: pitch_decay_smoother_2.clone(), + pitch_release_2: pitch_release_smoother_2.clone(), _detune: self.osc_detune, _unison_detune_value: self.osc_unison_detune, //frequency: detuned_note, @@ -2366,6 +2516,13 @@ impl AudioModule { pitch_attack: pitch_attack_smoother.clone(), pitch_decay: pitch_decay_smoother.clone(), pitch_release: pitch_release_smoother.clone(), + pitch_enabled_2: self.pitch_enable_2, + pitch_env_peak_2: self.pitch_env_peak_2, + pitch_current_2: pitch_mod_current_2, + pitch_state_2: OscState::Attacking, + pitch_attack_2: pitch_attack_smoother_2.clone(), + pitch_decay_2: pitch_decay_smoother_2.clone(), + pitch_release_2: pitch_release_smoother_2.clone(), _detune: self.osc_detune, _unison_detune_value: self.osc_unison_detune, //frequency: unison_notes[unison_voice], @@ -2419,6 +2576,13 @@ impl AudioModule { pitch_attack: pitch_attack_smoother.clone(), pitch_decay: pitch_decay_smoother.clone(), pitch_release: pitch_release_smoother.clone(), + pitch_enabled_2: self.pitch_enable_2, + pitch_env_peak_2: self.pitch_env_peak_2, + pitch_current_2: 0.0, + pitch_state_2: OscState::Attacking, + pitch_attack_2: pitch_attack_smoother_2.clone(), + pitch_decay_2: pitch_decay_smoother_2.clone(), + pitch_release_2: pitch_release_smoother_2.clone(), _detune: 0.0, _unison_detune_value: 0.0, frequency: 0.0, @@ -2467,6 +2631,13 @@ impl AudioModule { pitch_attack: pitch_attack_smoother.clone(), pitch_decay: pitch_decay_smoother.clone(), pitch_release: pitch_release_smoother.clone(), + pitch_enabled_2: self.pitch_enable_2, + pitch_env_peak_2: 0.0, + pitch_current_2: 0.0, + pitch_state_2: OscState::Off, + pitch_attack_2: pitch_attack_smoother_2.clone(), + pitch_decay_2: pitch_decay_smoother_2.clone(), + pitch_release_2: pitch_release_smoother_2.clone(), _detune: 0.0, _unison_detune_value: 0.0, frequency: 0.0, @@ -2627,6 +2798,13 @@ impl AudioModule { pitch_attack: Smoother::new(SmoothingStyle::None), pitch_decay: Smoother::new(SmoothingStyle::None), pitch_release: Smoother::new(SmoothingStyle::None), + pitch_enabled_2: false, + pitch_env_peak_2: 0.0, + pitch_current_2: 0.0, + pitch_state_2: OscState::Off, + pitch_attack_2: Smoother::new(SmoothingStyle::None), + pitch_decay_2: Smoother::new(SmoothingStyle::None), + pitch_release_2: Smoother::new(SmoothingStyle::None), _detune: 0.0, _unison_detune_value: 0.0, frequency: 0.0, @@ -2683,6 +2861,7 @@ impl AudioModule { voice.phase -= 1.0; } // This happens on extreme pitch envelope values only and catches wild increments + // or pitches above nyquist that would alias into other pitches if voice.phase > 1.0 { voice.phase = voice.phase % 1.0; } @@ -2701,7 +2880,7 @@ impl AudioModule { // Move from Decaying to Sustain hold if voice.pitch_decay.steps_left() == 0 && voice.pitch_state == OscState::Decaying { - let sustain_scaled = self.osc_sustain / 999.9; + let sustain_scaled = self.pitch_env_sustain / 999.9; voice.pitch_current = sustain_scaled; voice.pitch_decay.set_target(self.sample_rate, sustain_scaled.clamp(0.0001, 999.9)); voice.pitch_state = OscState::Sustaining; @@ -2716,6 +2895,34 @@ impl AudioModule { voice.pitch_current = 0.0; voice.pitch_state = OscState::Off; } + if self.audio_module_type == AudioModuleType::Osc && voice.pitch_enabled_2 { + // Attack is over so use decay amount to reach sustain level - reusing current smoother + if voice.pitch_attack_2.steps_left() == 0 && voice.pitch_state_2 == OscState::Attacking { + voice.pitch_state_2 = OscState::Decaying; + voice.pitch_current_2 = voice.pitch_attack_2.next(); + // Now we will use decay smoother from here + voice.pitch_decay_2.reset(voice.pitch_current_2); + let sustain_scaled_2 = self.pitch_env_sustain_2 / 999.9; + voice.pitch_decay_2.set_target(self.sample_rate, sustain_scaled_2.clamp(0.0001, 999.9)); + } + + // Move from Decaying to Sustain hold + if voice.pitch_decay_2.steps_left() == 0 && voice.pitch_state_2 == OscState::Decaying { + let sustain_scaled_2 = self.pitch_env_sustain_2 / 999.9; + voice.pitch_current_2 = sustain_scaled_2; + voice.pitch_decay_2.set_target(self.sample_rate, sustain_scaled_2.clamp(0.0001, 999.9)); + voice.pitch_state_2 = OscState::Sustaining; + } + + // End of release + if voice.pitch_state_2 == OscState::Releasing && voice.pitch_release_2.steps_left() == 0 { + voice.pitch_state_2 = OscState::Off; + } + } else { + // Reassign here for safety + voice.pitch_current_2 = 0.0; + voice.pitch_state_2 = OscState::Off; + } // Move from attack to decay if needed // Attack is over so use decay amount to reach sustain level - reusing current smoother @@ -2822,6 +3029,13 @@ impl AudioModule { pitch_attack: voice.pitch_attack.clone(), pitch_decay: voice.pitch_decay.clone(), pitch_release: voice.pitch_release.clone(), + pitch_enabled_2: voice.pitch_enabled_2, + pitch_env_peak_2: voice.pitch_env_peak_2, + pitch_current_2: voice.pitch_current_2, + pitch_state_2: voice.pitch_state_2, + pitch_attack_2: voice.pitch_attack_2.clone(), + pitch_decay_2: voice.pitch_decay_2.clone(), + pitch_release_2: voice.pitch_release_2.clone(), _detune: voice._detune, _unison_detune_value: voice._unison_detune_value, frequency: voice.frequency, @@ -2870,6 +3084,70 @@ impl AudioModule { if unison_voice.phase > 1.0 { unison_voice.phase -= 1.0; } + // This happens on extreme pitch envelope values only and catches wild increments + // or pitches above nyquist that would alias into other pitches + if unison_voice.phase > 1.0 { + unison_voice.phase = unison_voice.phase % 1.0; + } + + // Move our pitch envelopes if this is an Osc + if self.audio_module_type == AudioModuleType::Osc && unison_voice.pitch_enabled { + // Attack is over so use decay amount to reach sustain level - reusing current smoother + if unison_voice.pitch_attack.steps_left() == 0 && unison_voice.pitch_state == OscState::Attacking { + unison_voice.pitch_state = OscState::Decaying; + unison_voice.pitch_current = unison_voice.pitch_attack.next(); + // Now we will use decay smoother from here + unison_voice.pitch_decay.reset(unison_voice.pitch_current); + let sustain_scaled = self.pitch_env_sustain / 999.9; + unison_voice.pitch_decay.set_target(self.sample_rate, sustain_scaled.clamp(0.0001, 999.9)); + } + + // Move from Decaying to Sustain hold + if unison_voice.pitch_decay.steps_left() == 0 && unison_voice.pitch_state == OscState::Decaying { + let sustain_scaled = self.pitch_env_sustain / 999.9; + unison_voice.pitch_current = sustain_scaled; + unison_voice.pitch_decay.set_target(self.sample_rate, sustain_scaled.clamp(0.0001, 999.9)); + unison_voice.pitch_state = OscState::Sustaining; + } + + // End of release + if unison_voice.pitch_state == OscState::Releasing && unison_voice.pitch_release.steps_left() == 0 { + unison_voice.pitch_state = OscState::Off; + } + } else { + // Reassign here for safety + unison_voice.pitch_current = 0.0; + unison_voice.pitch_state = OscState::Off; + } + if self.audio_module_type == AudioModuleType::Osc && unison_voice.pitch_enabled_2 { + // Attack is over so use decay amount to reach sustain level - reusing current smoother + if unison_voice.pitch_attack_2.steps_left() == 0 && unison_voice.pitch_state_2 == OscState::Attacking { + unison_voice.pitch_state_2 = OscState::Decaying; + unison_voice.pitch_current_2 = unison_voice.pitch_attack_2.next(); + // Now we will use decay smoother from here + unison_voice.pitch_decay_2.reset(unison_voice.pitch_current_2); + let sustain_scaled_2 = self.pitch_env_sustain_2 / 999.9; + unison_voice.pitch_decay_2.set_target(self.sample_rate, sustain_scaled_2.clamp(0.0001, 999.9)); + } + + // Move from Decaying to Sustain hold + if unison_voice.pitch_decay_2.steps_left() == 0 && unison_voice.pitch_state_2 == OscState::Decaying { + let sustain_scaled_2 = self.pitch_env_sustain_2 / 999.9; + unison_voice.pitch_current_2 = sustain_scaled_2; + unison_voice.pitch_decay_2.set_target(self.sample_rate, sustain_scaled_2.clamp(0.0001, 999.9)); + unison_voice.pitch_state_2 = OscState::Sustaining; + } + + // End of release + if unison_voice.pitch_state_2 == OscState::Releasing && unison_voice.pitch_release_2.steps_left() == 0 { + unison_voice.pitch_state_2 = OscState::Off; + } + } else { + // Reassign here for safety + unison_voice.pitch_current_2 = 0.0; + unison_voice.pitch_state_2 = OscState::Off; + } + // Move from attack to decay if needed // Attack is over so use decay amount to reach sustain level - reusing current smoother if unison_voice.osc_attack.steps_left() == 0 @@ -2942,6 +3220,23 @@ impl AudioModule { OscState::Off => 0.0, } } + if voice.pitch_enabled_2 { + voice.pitch_current_2 = match voice.pitch_state_2 { + OscState::Attacking => { + voice.pitch_attack_2.next() + }, + OscState::Decaying => { + voice.pitch_decay_2.next() + }, + OscState::Sustaining => { + self.pitch_env_sustain_2 / 999.9 + }, + OscState::Releasing => { + voice.pitch_release_2.next() + }, + OscState::Off => 0.0, + } + } let temp_osc_gain_multiplier: f32; // Get our current gain amount for use in match below @@ -2976,7 +3271,7 @@ impl AudioModule { let nyquist = self.sample_rate/2.0; if voice.vel_mod_amount == 0.0 { - let base_note = voice.note as f32 + voice._detune + detune_mod + voice.pitch_current; + let base_note = voice.note as f32 + voice._detune + detune_mod + voice.pitch_current + voice.pitch_current_2; voice.phase_delta = util::f32_midi_note_to_freq(base_note).min(nyquist) / self.sample_rate; } else { @@ -2984,7 +3279,8 @@ impl AudioModule { + voice._detune + detune_mod + (voice.vel_mod_amount * voice._velocity) - + voice.pitch_current; + + voice.pitch_current + + voice.pitch_current_2; voice.phase_delta = util::f32_midi_note_to_freq(base_note).min(nyquist) / self.sample_rate; } @@ -3071,7 +3367,8 @@ impl AudioModule { let base_note = unison_voice.note as f32 + unison_voice._unison_detune_value + uni_detune_mod - + unison_voice.pitch_current; + + unison_voice.pitch_current + + unison_voice.pitch_current_2; unison_voice.phase_delta = util::f32_midi_note_to_freq(base_note) / self.sample_rate; } else { @@ -3079,7 +3376,8 @@ impl AudioModule { + unison_voice._unison_detune_value + uni_detune_mod + (unison_voice.vel_mod_amount * unison_voice._velocity) - + unison_voice.pitch_current; + + unison_voice.pitch_current + + unison_voice.pitch_current_2; unison_voice.phase_delta = util::f32_midi_note_to_freq(base_note) / self.sample_rate; } diff --git a/src/lib.rs b/src/lib.rs index 4fa82f8..d0f2e57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,7 +113,8 @@ pub const FONT_COLOR: Color32 = Color32::from_rgb(10, 103, 210); enum UIBottomSelection { Filter1, Filter2, - Pitch + Pitch1, + Pitch2, } // Gui for which panel to display in bottom right @@ -447,6 +448,17 @@ pub struct ActuatePreset { pitch_env_dec_curve: Oscillator::SmoothStyle, pitch_env_rel_curve: Oscillator::SmoothStyle, + pitch_enable_2: bool, + pitch_routing_2: PitchRouting, + pitch_env_peak_2: f32, + pitch_env_attack_2: f32, + pitch_env_decay_2: f32, + pitch_env_sustain_2: f32, + pitch_env_release_2: f32, + pitch_env_atk_curve_2: Oscillator::SmoothStyle, + pitch_env_dec_curve_2: Oscillator::SmoothStyle, + pitch_env_rel_curve_2: Oscillator::SmoothStyle, + // LFOs lfo1_enable: bool, lfo2_enable: bool, @@ -951,6 +963,17 @@ impl Default for Actuate { pitch_env_dec_curve: SmoothStyle::Linear, pitch_env_rel_curve: SmoothStyle::Linear, + pitch_enable_2: false, + pitch_routing_2: PitchRouting::Osc1, + pitch_env_peak_2: 0.0, + pitch_env_attack_2: 0.0, + pitch_env_decay_2: 300.0, + pitch_env_sustain_2: 0.0, + pitch_env_release_2: 0.0, + pitch_env_atk_curve_2: SmoothStyle::Linear, + pitch_env_dec_curve_2: SmoothStyle::Linear, + pitch_env_rel_curve_2: SmoothStyle::Linear, + // LFOs lfo1_enable: false, lfo2_enable: false, @@ -1396,6 +1419,27 @@ pub struct ActuateParams { #[id = "pitch_env_rel_curve"] pub pitch_env_rel_curve: EnumParam, + #[id = "pitch_enable_2"] + pub pitch_enable_2: BoolParam, + #[id = "pitch_routing_2"] + pub pitch_routing_2: EnumParam, + #[id = "pitch_env_peak_2"] + pub pitch_env_peak_2: FloatParam, + #[id = "pitch_env_attack_2"] + pub pitch_env_attack_2: FloatParam, + #[id = "pitch_env_decay_2"] + pub pitch_env_decay_2: FloatParam, + #[id = "pitch_env_sustain_2"] + pub pitch_env_sustain_2: FloatParam, + #[id = "pitch_env_release_2"] + pub pitch_env_release_2: FloatParam, + #[id = "pitch_env_atk_curve_2"] + pub pitch_env_atk_curve_2: EnumParam, + #[id = "pitch_env_dec_curve_2"] + pub pitch_env_dec_curve_2: EnumParam, + #[id = "pitch_env_rel_curve_2"] + pub pitch_env_rel_curve_2: EnumParam, + // LFOS #[id = "lfo1_enable"] pub lfo1_enable: BoolParam, @@ -2294,17 +2338,13 @@ impl ActuateParams { Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) }), - filter_wet: FloatParam::new( - "Filter", - 1.0, - FloatRange::Linear { min: 0.0, max: 1.0 }, - ) - .with_unit("%") - .with_value_to_string(formatters::v2s_f32_percentage(0)) - .with_callback({ - let update_something = update_something.clone(); - Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) - }), + filter_wet: FloatParam::new("Filter", 1.0, FloatRange::Linear { min: 0.0, max: 1.0 }) + .with_unit("%") + .with_value_to_string(formatters::v2s_f32_percentage(0)) + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), filter_resonance: FloatParam::new( "Res", 1.0, @@ -2455,17 +2495,13 @@ impl ActuateParams { Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) }), - filter_wet_2: FloatParam::new( - "Filter", - 1.0, - FloatRange::Linear { min: 0.0, max: 1.0 }, - ) - .with_unit("%") - .with_value_to_string(formatters::v2s_f32_percentage(0)) - .with_callback({ - let update_something = update_something.clone(); - Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) - }), + filter_wet_2: FloatParam::new("Filter", 1.0, FloatRange::Linear { min: 0.0, max: 1.0 }) + .with_unit("%") + .with_value_to_string(formatters::v2s_f32_percentage(0)) + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), filter_resonance_2: FloatParam::new( "Res", 1.0, @@ -2473,12 +2509,10 @@ impl ActuateParams { ) .with_unit("%") .with_value_to_string(formatters::v2s_f32_percentage(0)), - filter_res_type_2: EnumParam::new("Res Type", ResonanceType::Default).with_callback( - { - let update_something = update_something.clone(); - Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) - }, - ), + filter_res_type_2: EnumParam::new("Res Type", ResonanceType::Default).with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), filter_cutoff_2: FloatParam::new( "Cutoff", 20000.0, @@ -2592,7 +2626,7 @@ impl ActuateParams { // Pitch Envelope //////////////////////////////////////////////////////////////////////////////////// pitch_env_peak: FloatParam::new( - "PITCHAA", + "Pitch Env", 0.0, FloatRange::Linear { min: -144.0, @@ -2622,7 +2656,7 @@ impl ActuateParams { }), pitch_env_decay: FloatParam::new( "Env Decay", - 0.0001, + 300.0, FloatRange::Skewed { min: 0.0001, max: 999.9, @@ -2637,7 +2671,7 @@ impl ActuateParams { }), pitch_env_sustain: FloatParam::new( "Env Sustain", - 999.9, + 0.0001, FloatRange::Skewed { min: 0.0001, max: 999.9, @@ -2681,12 +2715,105 @@ impl ActuateParams { Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) }), pitch_enable: BoolParam::new("Pitch Enable", false), - pitch_routing: EnumParam::new("Routing", PitchRouting::Osc1) + pitch_routing: EnumParam::new("Routing", PitchRouting::Osc1).with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + + pitch_env_peak_2: FloatParam::new( + "Pitch Env", + 0.0, + FloatRange::Linear { + min: -144.0, + max: 144.0, + }, + ) + //.with_step_size(1.0) + //.with_value_to_string(format_nothing()) + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_env_attack_2: FloatParam::new( + "Env Attack", + 0.0001, + FloatRange::Skewed { + min: 0.0001, + max: 999.9, + factor: 0.2, + }, + ) + .with_value_to_string(format_nothing()) + .with_unit("A") + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_env_decay_2: FloatParam::new( + "Env Decay", + 300.0, + FloatRange::Skewed { + min: 0.0001, + max: 999.9, + factor: 0.2, + }, + ) + .with_value_to_string(format_nothing()) + .with_unit("D") + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_env_sustain_2: FloatParam::new( + "Env Sustain", + 0.0001, + FloatRange::Skewed { + min: 0.0001, + max: 999.9, + factor: 0.2, + }, + ) + .with_value_to_string(format_nothing()) + .with_unit("S") + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_env_release_2: FloatParam::new( + "Env Release", + 0.0001, + FloatRange::Skewed { + min: 0.0001, + max: 999.9, + factor: 0.2, + }, + ) + .with_value_to_string(format_nothing()) + .with_unit("R") + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_env_atk_curve_2: EnumParam::new("Atk Curve", Oscillator::SmoothStyle::Linear) .with_callback({ let update_something = update_something.clone(); Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) }), - + pitch_env_dec_curve_2: EnumParam::new("Dec Curve", Oscillator::SmoothStyle::Linear) + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_env_rel_curve_2: EnumParam::new("Rel Curve", Oscillator::SmoothStyle::Linear) + .with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), + pitch_enable_2: BoolParam::new("Pitch Enable", false), + pitch_routing_2: EnumParam::new("Routing", PitchRouting::Osc1).with_callback({ + let update_something = update_something.clone(); + Arc::new(move |_| update_something.store(true, Ordering::Relaxed)) + }), // LFOs //////////////////////////////////////////////////////////////////////////////////// @@ -3602,7 +3729,6 @@ impl Plugin for Actuate { .preset_style(ui_knob::KnobStyle::NewPresets1) .set_fill_color(DARK_GREY_UI_COLOR) .set_line_color(SYNTH_MIDDLE_BLUE) - .override_text_color(Color32::GRAY) .set_text_size(TEXT_SIZE); ui.add(audio_module_1_filter_routing); }); @@ -4119,7 +4245,7 @@ impl Plugin for Actuate { A_BACKGROUND_COLOR_TOP ); }, - UIBottomSelection::Pitch => { + UIBottomSelection::Pitch1 => { ui.vertical(|ui|{ ui.horizontal(|ui|{ let pitch_toggle = toggle_switch::ToggleSwitch::for_param(¶ms.pitch_enable, setter); @@ -4173,6 +4299,61 @@ impl Plugin for Actuate { Rounding::from(16.0), A_BACKGROUND_COLOR_TOP ); + }, + UIBottomSelection::Pitch2 => { + ui.vertical(|ui|{ + ui.horizontal(|ui|{ + let pitch_toggle_2 = toggle_switch::ToggleSwitch::for_param(¶ms.pitch_enable_2, setter); + ui.add(pitch_toggle_2); + ui.label(RichText::new("Enable Pitch Envelope") + .font(SMALLER_FONT) + .color(A_BACKGROUND_COLOR_TOP) + ); + }); + + ui.horizontal(|ui|{ + let pitch_env_peak_knob_2 = ui_knob::ArcKnob::for_param( + ¶ms.pitch_env_peak_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_readable_box(false) + .set_text_size(TEXT_SIZE); + ui.add(pitch_env_peak_knob_2); + + let pitch_routing_knob_2 = ui_knob::ArcKnob::for_param( + ¶ms.pitch_routing_2, + setter, + KNOB_SIZE) + .preset_style(ui_knob::KnobStyle::NewPresets1) + .set_fill_color(SYNTH_BARS_PURPLE) + .set_line_color(A_KNOB_OUTSIDE_COLOR) + .set_readable_box(false) + .set_text_size(TEXT_SIZE); + ui.add(pitch_routing_knob_2); + }); + }); + ui.add_space(KNOB_SIZE*3.5); + ui.add_space(8.0); + + // Middle bottom light section + ui.painter().rect_filled( + Rect::from_x_y_ranges( + RangeInclusive::new((WIDTH as f32)*0.35, (WIDTH as f32)*0.64), + RangeInclusive::new((HEIGHT as f32)*0.73, (HEIGHT as f32) - 4.0)), + Rounding::from(16.0), + SYNTH_MIDDLE_BLUE.linear_multiply(0.8) + ); + // Middle Bottom Filter select background + ui.painter().rect_filled( + Rect::from_x_y_ranges( + RangeInclusive::new((WIDTH as f32)*0.38, (WIDTH as f32)*0.62), + RangeInclusive::new((HEIGHT as f32) - 26.0, (HEIGHT as f32) - 2.0)), + Rounding::from(16.0), + A_BACKGROUND_COLOR_TOP + ); } } }); @@ -4273,7 +4454,7 @@ impl Plugin for Actuate { ), ); }, - UIBottomSelection::Pitch => { + UIBottomSelection::Pitch1 => { // ADSR ui.add( VerticalParamSlider::for_param(¶ms.pitch_env_attack, setter) @@ -4282,7 +4463,7 @@ impl Plugin for Actuate { .set_reversed(true) .override_colors( SYNTH_BARS_PURPLE, - SYNTH_SOFT_BLUE2, + SYNTH_SOFT_BLUE, ), ); ui.add( @@ -4292,7 +4473,7 @@ impl Plugin for Actuate { .set_reversed(true) .override_colors( SYNTH_BARS_PURPLE, - SYNTH_SOFT_BLUE2, + SYNTH_SOFT_BLUE, ), ); ui.add( @@ -4302,11 +4483,54 @@ impl Plugin for Actuate { .set_reversed(true) .override_colors( SYNTH_BARS_PURPLE, - SYNTH_SOFT_BLUE2, + SYNTH_SOFT_BLUE, ), ); ui.add( VerticalParamSlider::for_param(¶ms.pitch_env_release, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + SYNTH_SOFT_BLUE, + ), + ); + }, + UIBottomSelection::Pitch2 => { + // ADSR + ui.add( + VerticalParamSlider::for_param(¶ms.pitch_env_attack_2, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + SYNTH_SOFT_BLUE2, + ), + ); + ui.add( + VerticalParamSlider::for_param(¶ms.pitch_env_decay_2, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + SYNTH_SOFT_BLUE2, + ), + ); + ui.add( + VerticalParamSlider::for_param(¶ms.pitch_env_sustain_2, setter) + .with_width(VERT_BAR_WIDTH) + .with_height(VERT_BAR_HEIGHT) + .set_reversed(true) + .override_colors( + SYNTH_BARS_PURPLE, + SYNTH_SOFT_BLUE2, + ), + ); + ui.add( + VerticalParamSlider::for_param(¶ms.pitch_env_release_2, setter) .with_width(VERT_BAR_WIDTH) .with_height(VERT_BAR_HEIGHT) .set_reversed(true) @@ -4427,7 +4651,7 @@ impl Plugin for Actuate { .with_width(30.0) ); }, - UIBottomSelection::Pitch => { + UIBottomSelection::Pitch1 => { ui.add( HorizontalParamSlider::for_param(¶ms.pitch_env_atk_curve, setter) .with_width(HCURVE_BWIDTH) @@ -4462,15 +4686,52 @@ impl Plugin for Actuate { .with_width(30.0), ); ui.add_space(67.0); + }, + UIBottomSelection::Pitch2 => { + ui.add( + HorizontalParamSlider::for_param(¶ms.pitch_env_atk_curve_2, setter) + .with_width(HCURVE_BWIDTH) + .slimmer(0.7) + .set_left_sided_label(true) + .set_label_width(HCURVE_WIDTH) + .override_colors( + DARK_GREY_UI_COLOR, + SYNTH_SOFT_BLUE) + .with_width(30.0), + ); + ui.add( + HorizontalParamSlider::for_param(¶ms.pitch_env_dec_curve_2, setter) + .with_width(HCURVE_BWIDTH) + .slimmer(0.7) + .set_left_sided_label(true) + .set_label_width(HCURVE_WIDTH) + .override_colors( + DARK_GREY_UI_COLOR, + SYNTH_SOFT_BLUE) + .with_width(30.0), + ); + ui.add( + HorizontalParamSlider::for_param(¶ms.pitch_env_rel_curve_2, setter) + .with_width(HCURVE_BWIDTH) + .slimmer(0.7) + .set_left_sided_label(true) + .set_label_width(HCURVE_WIDTH) + .override_colors( + DARK_GREY_UI_COLOR, + SYNTH_SOFT_BLUE) + .with_width(30.0), + ); + ui.add_space(67.0); } } }); }); ui.horizontal(|ui|{ - ui.add_space(50.0); + ui.add_space(25.0); ui.selectable_value(&mut *filter_select.lock().unwrap(), UIBottomSelection::Filter1, RichText::new("Filter 1").color(Color32::BLACK)); ui.selectable_value(&mut *filter_select.lock().unwrap(), UIBottomSelection::Filter2, RichText::new("Filter 2").color(Color32::BLACK)); - ui.selectable_value(&mut *filter_select.lock().unwrap(), UIBottomSelection::Pitch, RichText::new("Pitch Env").color(Color32::BLACK)) + ui.selectable_value(&mut *filter_select.lock().unwrap(), UIBottomSelection::Pitch1, RichText::new("Pitch 1").color(Color32::BLACK)); + ui.selectable_value(&mut *filter_select.lock().unwrap(), UIBottomSelection::Pitch2, RichText::new("Pitch 2").color(Color32::BLACK)); }); }); @@ -5984,17 +6245,14 @@ impl Actuate { // Trigger passing variables to the audio modules when the GUI input changes if self.update_something.load(Ordering::Relaxed) { self.audio_module_1 - .clone() .lock() .unwrap() .consume_params(self.params.clone(), 1); self.audio_module_2 - .clone() .lock() .unwrap() .consume_params(self.params.clone(), 2); self.audio_module_3 - .clone() .lock() .unwrap() .consume_params(self.params.clone(), 3); @@ -7645,6 +7903,17 @@ impl Actuate { pitch_env_sustain: 0.0, pitch_routing: PitchRouting::Osc1, + pitch_enable_2: false, + pitch_env_peak_2: 0.0, + pitch_env_atk_curve_2: SmoothStyle::Linear, + pitch_env_dec_curve_2: SmoothStyle::Linear, + pitch_env_rel_curve_2: SmoothStyle::Linear, + pitch_env_attack_2: 0.0, + pitch_env_decay_2: 300.0, + pitch_env_release_2: 0.0, + pitch_env_sustain_2: 0.0, + pitch_routing_2: PitchRouting::Osc1, + // LFOs lfo1_enable: false, lfo2_enable: false, @@ -7744,7 +8013,7 @@ impl Actuate { limiter_threshold: 0.5, limiter_knee: 0.5, }); - + // Attempt to load the previous version preset type if unserialized.preset_name.contains("Error") { // Try loading the previous preset struct version @@ -7954,6 +8223,17 @@ impl Actuate { pitch_env_peak: 0.0, pitch_routing: PitchRouting::Osc1, + pitch_enable_2: false, + pitch_env_peak_2: 0.0, + pitch_env_atk_curve_2: SmoothStyle::Linear, + pitch_env_dec_curve_2: SmoothStyle::Linear, + pitch_env_rel_curve_2: SmoothStyle::Linear, + pitch_env_attack_2: 0.0, + pitch_env_decay_2: 300.0, + pitch_env_release_2: 0.0, + pitch_env_sustain_2: 0.0, + pitch_routing_2: PitchRouting::Osc1, + // LFOs lfo1_enable: false, lfo2_enable: false, @@ -8400,6 +8680,56 @@ impl Actuate { _ => PresetType::Select, }; + // 1.2.1 Pitch update + setter.set_parameter(¶ms.pitch_enable, loaded_preset.pitch_enable); + setter.set_parameter(¶ms.pitch_env_peak, loaded_preset.pitch_env_peak); + setter.set_parameter( + ¶ms.pitch_env_atk_curve, + loaded_preset.pitch_env_atk_curve, + ); + setter.set_parameter( + ¶ms.pitch_env_dec_curve, + loaded_preset.pitch_env_dec_curve, + ); + setter.set_parameter( + ¶ms.pitch_env_rel_curve, + loaded_preset.pitch_env_rel_curve, + ); + setter.set_parameter(¶ms.pitch_env_attack, loaded_preset.pitch_env_attack); + setter.set_parameter(¶ms.pitch_env_decay, loaded_preset.pitch_env_decay); + setter.set_parameter(¶ms.pitch_env_sustain, loaded_preset.pitch_env_sustain); + setter.set_parameter(¶ms.pitch_env_release, loaded_preset.pitch_env_release); + setter.set_parameter(¶ms.pitch_routing, loaded_preset.pitch_routing.clone()); + + setter.set_parameter(¶ms.pitch_enable_2, loaded_preset.pitch_enable_2); + setter.set_parameter(¶ms.pitch_env_peak_2, loaded_preset.pitch_env_peak_2); + setter.set_parameter( + ¶ms.pitch_env_atk_curve_2, + loaded_preset.pitch_env_atk_curve_2, + ); + setter.set_parameter( + ¶ms.pitch_env_dec_curve_2, + loaded_preset.pitch_env_dec_curve_2, + ); + setter.set_parameter( + ¶ms.pitch_env_rel_curve_2, + loaded_preset.pitch_env_rel_curve_2, + ); + setter.set_parameter(¶ms.pitch_env_attack_2, loaded_preset.pitch_env_attack_2); + setter.set_parameter(¶ms.pitch_env_decay_2, loaded_preset.pitch_env_decay_2); + setter.set_parameter( + ¶ms.pitch_env_sustain_2, + loaded_preset.pitch_env_sustain_2, + ); + setter.set_parameter( + ¶ms.pitch_env_release_2, + loaded_preset.pitch_env_release_2, + ); + setter.set_parameter( + ¶ms.pitch_routing_2, + loaded_preset.pitch_routing_2.clone(), + ); + // Assign the preset tags setter.set_parameter(¶ms.tag_acid, loaded_preset.tag_acid); setter.set_parameter(¶ms.tag_analog, loaded_preset.tag_analog); @@ -8749,6 +9079,17 @@ impl Actuate { pitch_env_peak: self.params.pitch_env_peak.value(), pitch_routing: self.params.pitch_routing.value(), + pitch_enable_2: self.params.pitch_enable_2.value(), + pitch_env_atk_curve_2: self.params.pitch_env_atk_curve_2.value(), + pitch_env_dec_curve_2: self.params.pitch_env_dec_curve_2.value(), + pitch_env_rel_curve_2: self.params.pitch_env_rel_curve_2.value(), + pitch_env_attack_2: self.params.pitch_env_attack_2.value(), + pitch_env_decay_2: self.params.pitch_env_decay_2.value(), + pitch_env_sustain_2: self.params.pitch_env_sustain_2.value(), + pitch_env_release_2: self.params.pitch_env_release_2.value(), + pitch_env_peak_2: self.params.pitch_env_peak_2.value(), + pitch_routing_2: self.params.pitch_routing_2.value(), + // LFOs lfo1_enable: self.params.lfo1_enable.value(), lfo2_enable: self.params.lfo2_enable.value(), diff --git a/src/old_preset_structs.rs b/src/old_preset_structs.rs index f3c02d4..5b3d136 100644 --- a/src/old_preset_structs.rs +++ b/src/old_preset_structs.rs @@ -2,12 +2,15 @@ use crate::{ audio_module::{ AudioModuleType, Oscillator::{self, RetriggerStyle, SmoothStyle, VoiceType}, - }, fx::{ + }, + fx::{ delay::{DelaySnapValues, DelayType}, saturation::SaturationType, ArduraFilter, StateVariableFilter::ResonanceType, - }, ActuatePreset, FilterAlgorithms, FilterRouting, LFOController, ModulationDestination, ModulationSource, PitchRouting, PresetType + }, + ActuatePreset, FilterAlgorithms, FilterRouting, LFOController, ModulationDestination, + ModulationSource, PitchRouting, PresetType, }; use serde::{Deserialize, Serialize}; @@ -459,7 +462,28 @@ pub struct ActuatePresetV114 { /////////////////////////////////////////////////////////////////////////// // MISSING IN OLDER STRUCTS - //PITCH STUFF + // PITCH STUFF + // + // pitch_enable: false, + // pitch_env_peak: 0.0, + // pitch_env_atk_curve: SmoothStyle::Linear, + // pitch_env_dec_curve: SmoothStyle::Linear, + // pitch_env_rel_curve: SmoothStyle::Linear, + // pitch_env_attack: 0.0, + // pitch_env_decay: 300.0, + // pitch_env_release: 0.0, + // pitch_env_sustain: 0.0, + // pitch_routing: PitchRouting::Osc1, + // pitch_enable_2: false, + // pitch_env_peak_2: 0.0, + // pitch_env_atk_curve_2: SmoothStyle::Linear, + // pitch_env_dec_curve_2: SmoothStyle::Linear, + // pitch_env_rel_curve_2: SmoothStyle::Linear, + // pitch_env_attack_2: 0.0, + // pitch_env_decay_2: 300.0, + // pitch_env_release_2: 0.0, + // pitch_env_sustain_2: 0.0, + // pitch_routing_2: PitchRouting::Osc1, /////////////////////////////////////////////////////////////////////////// // LFOs @@ -817,7 +841,6 @@ pub fn load_unserialized_v114(file_data: Vec) -> ActuatePreset { convert_preset_v114(old_unserialized) } - // This takes the deserialized message pack and converts it into the old struct if it can // This then attempts to return the newer preset format after pub fn load_unserialized_old(file_data: Vec) -> ActuatePreset { @@ -1234,6 +1257,16 @@ fn convert_preset_v114(preset: ActuatePresetV114) -> ActuatePreset { pitch_env_decay: 300.0, pitch_env_release: 0.0, pitch_env_sustain: 0.0, + pitch_enable_2: false, + pitch_env_peak_2: 0.0, + pitch_env_atk_curve_2: SmoothStyle::Linear, + pitch_env_dec_curve_2: SmoothStyle::Linear, + pitch_env_rel_curve_2: SmoothStyle::Linear, + pitch_env_attack_2: 0.0, + pitch_env_decay_2: 300.0, + pitch_env_release_2: 0.0, + pitch_env_sustain_2: 0.0, + pitch_routing_2: PitchRouting::Osc1, /////////////////////////////////////////////////////////////////// lfo1_enable: preset.lfo1_enable, lfo2_enable: preset.lfo2_enable, @@ -1477,6 +1510,16 @@ fn convert_preset(preset: ActuatePresetOld) -> ActuatePreset { pitch_env_decay: 300.0, pitch_env_release: 0.0, pitch_env_sustain: 0.0, + pitch_enable_2: false, + pitch_env_peak_2: 0.0, + pitch_env_atk_curve_2: SmoothStyle::Linear, + pitch_env_dec_curve_2: SmoothStyle::Linear, + pitch_env_rel_curve_2: SmoothStyle::Linear, + pitch_env_attack_2: 0.0, + pitch_env_decay_2: 300.0, + pitch_env_release_2: 0.0, + pitch_env_sustain_2: 0.0, + pitch_routing_2: PitchRouting::Osc1, /////////////////////////////////////////////////////////////////// lfo1_enable: preset.lfo1_enable, lfo2_enable: preset.lfo2_enable,