Skip to content

Commit

Permalink
add gui crossfader
Browse files Browse the repository at this point in the history
  • Loading branch information
fruitsbat committed Oct 31, 2024
1 parent 7f89876 commit f959eeb
Show file tree
Hide file tree
Showing 19 changed files with 1,594 additions and 48 deletions.
1,284 changes: 1,246 additions & 38 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ authors = ["Zoe Oosting <zoe@kittycat.homes>"]
crate-type = ["cdylib"]

[workspace]
members = [ "x_fader","xtask", "xy_fader", "xyz_fader"]
members = [ "style","crossfader_gui", "x_fader","xtask", "xy_fader", "xyz_fader", "audio_util"]

[dependencies]
nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git" }
Expand Down
11 changes: 11 additions & 0 deletions audio_util/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "audio_util"
version = "0.1.0"
edition = "2021"

[crate]
type = "cdylib"

[dependencies]
float-cmp = "0.10.0"
nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git" }
20 changes: 20 additions & 0 deletions audio_util/src/buffer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use nih_plug::prelude::AuxiliaryBuffers;

/// checks sidechain sample at the position the main sample is
/// just returns zero when there is an error
pub fn get_sample_at_position(
channel_index: usize,
sample_index: usize,
aux: &mut AuxiliaryBuffers,
) -> f32 {
if let Some(buffer) = aux.inputs.first() {
let buffer = buffer.as_slice_immutable();
if let Some(sample) = buffer.get(channel_index) {
if let Some(channel) = sample.get(sample_index) {
return *channel;
}
}
}

0.0
}
28 changes: 28 additions & 0 deletions audio_util/src/crossfade.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
converts from a linear crossfade between 0 and 1 to
an equal power crossfade between 0 and 1
*/
pub fn constant_power(linear_fade_strength: f32) -> f32 {
let linear_fade_strength = linear_fade_strength.clamp(0., 1.);
return f32::sqrt(linear_fade_strength);
}

#[cfg(test)]
mod tests {
use super::*;
use float_cmp::{approx_eq, assert_approx_eq};

#[test]
fn test_equal_power() {
// center
assert_approx_eq!(f32, constant_power(0.5), 0.70710677);
// left
assert_approx_eq!(f32, constant_power(0.0), 0.0);
// right
assert_approx_eq!(f32, constant_power(1.0), 1.0);
//value too high
assert!(approx_eq!(f32, constant_power(100.0), 1.0));
// value too low
assert!(approx_eq!(f32, constant_power(-100.0), 0.0))
}
}
2 changes: 2 additions & 0 deletions audio_util/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod buffer;
pub mod crossfade;
2 changes: 2 additions & 0 deletions build_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"xy_fader",
"-p",
"xyz_fader",
"-p",
"crossfader_gui",
"--release"
]
)
17 changes: 17 additions & 0 deletions crossfader_gui/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "crossfader_gui"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git" }
nih_plug_iced = { git = "https://github.com/robbert-vdh/nih-plug.git" }
style = { path = "../style" }
audio_util = { path = "../audio_util" }
parking_lot = "0.12.3"
atomic_float = { version = "1", features = ["serde"] }
99 changes: 99 additions & 0 deletions crossfader_gui/src/editor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use std::sync::Arc;

use nih_plug::{editor::Editor, prelude::GuiContext};
use nih_plug_iced::widgets as nih_widgets;
use nih_plug_iced::*;
use style::{self};

use crate::XFaderParams;

pub(crate) fn default_state() -> Arc<IcedState> {
return IcedState::from_size(180, 150);
}

pub(crate) fn create(
params: Arc<XFaderParams>,
editor_state: Arc<IcedState>,
) -> Option<Box<dyn Editor>> {
create_iced_editor::<XFaderEditor>(editor_state, params)
}

struct XFaderEditor {
params: Arc<XFaderParams>,
context: Arc<dyn GuiContext>,
// params
fade_strength_state: nih_widgets::param_slider::State,
}

#[derive(Debug, Clone, Copy)]
enum Message {
ParamUpdate(nih_widgets::ParamMessage),
}

impl IcedEditor for XFaderEditor {
type Executor = executor::Default;
type Message = Message;
type InitializationFlags = Arc<XFaderParams>;

fn new(
params: Self::InitializationFlags,
context: Arc<dyn GuiContext>,
) -> (Self, Command<Self::Message>) {
let editor = Self {
params,
context,
fade_strength_state: Default::default(),
};
(editor, Command::none())
}

fn context(&self) -> &dyn GuiContext {
self.context.as_ref()
}

fn update(
&mut self,
_window: &mut WindowQueue,
message: Self::Message,
) -> Command<Self::Message> {
match message {
Message::ParamUpdate(message) => self.handle_param_message(message),
}

return Command::none();
}

fn view(&mut self) -> Element<'_, Self::Message> {
Column::new()
.align_items(Alignment::Center)
.spacing(style::spacing::MD)
.push(
Text::new("X¹Fader")
.font(style::font::FIRA_BOLD)
.size(style::font::size::XL)
.color(style::color::BASE_CONTENT)
.horizontal_alignment(alignment::Horizontal::Center)
.vertical_alignment(alignment::Vertical::Center),
)
.push(Space::with_height(style::spacing::MD.into()))
.push(
Text::new("Fade Strength")
.font(style::font::FIRA_REGULAR)
.size(style::font::size::MD)
.color(style::color::BASE_CONTENT),
)
.push(
nih_widgets::ParamSlider::new(
&mut self.fade_strength_state,
&self.params.fade_strength,
)
.font(style::font::FIRA_REGULAR)
.map(Message::ParamUpdate),
)
.into()
}

fn background_color(&self) -> Color {
return style::color::BASE;
}
}
117 changes: 117 additions & 0 deletions crossfader_gui/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use nih_plug::{
params::{FloatParam, Params},
plugin::Plugin,
prelude::*,
};
use nih_plug_iced::IcedState;
use std::sync::Arc;
mod editor;
use audio_util::{self, buffer::get_sample_at_position, crossfade};

struct XFader {
params: Arc<XFaderParams>,
}

impl Default for XFader {
fn default() -> Self {
Self {
params: Arc::new(XFaderParams::default()),
}
}
}

#[derive(Params)]
pub struct XFaderParams {
#[persist = "editor-state"]
editor_state: Arc<IcedState>,

#[id = "53c56370-01c5-4820-a977-1dec2eef1af3"]
pub fade_strength: FloatParam,
}

impl Default for XFaderParams {
fn default() -> Self {
Self {
editor_state: editor::default_state(),
fade_strength: FloatParam::new(
"Fade Strength",
0.5,
FloatRange::Linear { min: 0.0, max: 1.0 },
)
.with_smoother(SmoothingStyle::Linear(5.0)),
}
}
}

impl Plugin for XFader {
const NAME: &'static str = "X¹Fader";
const VENDOR: &'static str = "kittycat.homes";
const URL: &'static str = "https://github.com/fruitsbat/audioplugins";
const EMAIL: &'static str = "zoe@kittycat.homes";
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const SAMPLE_ACCURATE_AUTOMATION: bool = true;
const AUDIO_IO_LAYOUTS: &'static [AudioIOLayout] = &[
// stereo
AudioIOLayout {
main_input_channels: NonZeroU32::new(2),
main_output_channels: NonZeroU32::new(2),

aux_input_ports: &[new_nonzero_u32(2)],
aux_output_ports: &[],

names: PortNames {
layout: Some("Stereo"),
main_input: Some("Main"),
main_output: Some("Out"),
aux_inputs: &["Sidechain"],
aux_outputs: &[],
},
},
];

type SysExMessage = ();
type BackgroundTask = ();

fn params(&self) -> Arc<dyn Params> {
return self.params.clone();
}

fn editor(&mut self, _async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
editor::create(self.params.clone(), self.params.editor_state.clone())
}

fn process(
&mut self,
buffer: &mut nih_plug::prelude::Buffer,
aux: &mut nih_plug::prelude::AuxiliaryBuffers,
_context: &mut impl nih_plug::prelude::ProcessContext<Self>,
) -> nih_plug::prelude::ProcessStatus {
{
let linear_crossfade_power = self.params.fade_strength.smoothed.next();
let constant_power_crossfade = crossfade::constant_power(linear_crossfade_power);
let x0_fade_strength = constant_power_crossfade;
let x1_fade_strength = 1.0 - constant_power_crossfade;

// apply to main audio input
for (sample_index, channel_samples) in buffer.iter_samples().enumerate() {
for (channel_index, sample) in channel_samples.into_iter().enumerate() {
*sample *= x0_fade_strength;
*sample +=
get_sample_at_position(channel_index, sample_index, aux) * x1_fade_strength;
}
}

ProcessStatus::Normal
}
}
}

impl ClapPlugin for XFader {
const CLAP_ID: &'static str = "1b06d8ac-4c9c-4aef-a105-16bcb76d989b";
const CLAP_DESCRIPTION: Option<&'static str> = Some("DJ Style Crossfader");
const CLAP_MANUAL_URL: Option<&'static str> = None;
const CLAP_SUPPORT_URL: Option<&'static str> = None;
const CLAP_FEATURES: &'static [ClapFeature] = &[ClapFeature::Utility, ClapFeature::Stereo];
}

nih_export_clap!(XFader);
7 changes: 7 additions & 0 deletions style/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "style"
version = "0.1.0"
edition = "2021"

[dependencies]
nih_plug_iced = { git = "https://github.com/robbert-vdh/nih-plug.git" }
Binary file added style/assets/FiraSans-Bold.ttf
Binary file not shown.
Binary file added style/assets/FiraSans-Regular.ttf
Binary file not shown.
15 changes: 15 additions & 0 deletions style/src/color/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use nih_plug_iced::Color;

pub const BASE: Color = Color {
r: 0.98,
g: 0.98,
b: 0.98,
a: 1.0,
};

pub const BASE_CONTENT: Color = Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 1.0,
};
13 changes: 13 additions & 0 deletions style/src/font/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use nih_plug_iced::Font;

pub mod size;

pub const FIRA_BOLD: Font = Font::External {
name: "Fira Sans Bold",
bytes: include_bytes!("../../assets/FiraSans-Bold.ttf"),
};

pub const FIRA_REGULAR: Font = Font::External {
name: "Fira Sans Regular",
bytes: include_bytes!("../../assets/FiraSans-Regular.ttf"),
};
6 changes: 6 additions & 0 deletions style/src/font/size.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub const XS: u16 = 14;
pub const SM: u16 = 18;
pub const MD: u16 = 23;
pub const LG: u16 = 29;
pub const XL: u16 = 37;
pub const XXL: u16 = 47;
3 changes: 3 additions & 0 deletions style/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod color;
pub mod font;
pub mod spacing;
1 change: 1 addition & 0 deletions style/src/spacing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub const MD: u16 = 4;
Loading

0 comments on commit f959eeb

Please sign in to comment.