Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion configurator/src/components/ManualTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const apps: ManualAppData[] = [
"Muted (if 'Store state' enabled)",
"Attenuation",
],
text: "This app is designed to provide a simple way to manually control any parameters using either CV or MIDI CC. The MIDI channel and CC numbers can be adjusted in the app's settings, and both MIDI and CV outputs are always active simultaneously. The range can be adjusted using Shift + Fader, which affects both CC and CV ranges. The fader controls the level of the CV or CC. The button behavior is set by the Button mode parameter: Mute toggles the output on and off; CC toggle sends a CC value that alternates between 0 and 127; CC momentary sends 127 while held and 0 on release; Program Change sends a MIDI Program Change message on press using the Button CC / PC number as the program number—useful for switching presets on external devices. The fader level and the mute state can be saved in scenes if the 'Store state' parameter is enabled (active by default). You can then use this app as a way to save and recall CV voltage allowing for preset in a modular system for example. The curve can be adjusted in the settings; however, this only affects the CV output. Two voltage ranges are available in the settings: 0V to 10V or -5V to 5V. Note that this range also affects the level at which CV and CC are set when muting. In the 0V to 10V range, mute is at 0V and CC 0, making it ideal for controlling volume, send levels, or similar parameters. In the -5V to 5V range, mute is at 0V and CC 64, making it suitable for controlling panning, crossfading, or similar functions. The mute behavior can be set to trigger on press or on release, depending on your preference. Due to popular demand, the app's action can also be inverted—this means that when the fader is at the top, the output will be set to the minimum value, and when at the bottom, it will send the maximum CC and CV value. As with all apps where the LED color does not serve any specific function, you are free to configure it in the settings.",
text: "This app is designed to provide a simple way to manually control any parameters using either CV or MIDI CC. The MIDI channel and CC numbers can be adjusted in the app's settings, and both MIDI and CV outputs are always active simultaneously. The range can be adjusted using Shift + Fader, which affects both CC and CV ranges. The fader controls the level of the CV or CC. The button behavior is set by the Button mode parameter: Mute toggles the output on and off; CC toggle sends CC Send Value when active and CC Default Value when inactive (defaults 127 and −1, where −1 sends no CC); CC momentary sends CC Send Value while held and CC Default Value on release; Program Change sends a MIDI Program Change message on press using the Button CC / PC number as the program number—useful for switching presets on external devices. The fader level and the mute state can be saved in scenes if the 'Store state' parameter is enabled (active by default). You can then use this app as a way to save and recall CV voltage allowing for preset in a modular system for example. The curve can be adjusted in the settings; however, this only affects the CV output. Two voltage ranges are available in the settings: 0V to 10V or -5V to 5V. Note that this range also affects the level at which CV and CC are set when muting. In the 0V to 10V range, mute is at 0V and CC 0, making it ideal for controlling volume, send levels, or similar parameters. In the -5V to 5V range, mute is at 0V and CC 64, making it suitable for controlling panning, crossfading, or similar functions. The mute behavior can be set to trigger on press or on release, depending on your preference. Due to popular demand, the app's action can also be inverted—this means that when the fader is at the top, the output will be set to the minimum value, and when at the bottom, it will send the maximum CC and CV value. As with all apps where the LED color does not serve any specific function, you are free to configure it in the settings.",
channels: [
{
jackTitle: "Output",
Expand Down
2 changes: 2 additions & 0 deletions configurator/src/demo/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ const APPS: App[] = [
midiCc("Button CC"),
MIDI_NRPN,
MIDI_OUT,
i32("CC Send Value", 0, 127),
i32("CC Default Value", -1, 127),
]),
makeApp(2, 1, "Yellow", "Sine", "LFO", "Multi shape LFO", [
enumParam("Speed", ["Normal", "Slow", "Slowest"]),
Expand Down
47 changes: 41 additions & 6 deletions faderpunk/src/apps/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@ use libfp::{Config, Curve, Param, Range, Value};

use crate::app::{App, AppParams, AppStorage, Led, ManagedStorage, ParamStore, SceneEvent};

/// Values below 0 mean no CC is sent (used by CC Default Value).
fn button_cc_value_12(value: i32) -> Option<u16> {
(value >= 0).then(|| ((value.clamp(0, 127) as u32 * 4095) / 127) as u16)
}

pub const CHANNELS: usize = 1;
pub const PARAMS: usize = 13;
pub const PARAMS: usize = 15;

pub static CONFIG: Config<PARAMS> = Config::new(
"Control",
Expand Down Expand Up @@ -66,7 +71,17 @@ pub static CONFIG: Config<PARAMS> = Config::new(
})
.add_param(Param::MidiCc { name: "Button CC / PC" })
.add_param(Param::MidiNrpn)
.add_param(Param::MidiOut);
.add_param(Param::MidiOut)
.add_param(Param::i32 {
name: "CC Send Value",
min: 0,
max: 127,
})
.add_param(Param::i32 {
name: "CC Default Value",
min: -1,
max: 127,
});

pub struct Params {
curve: Curve,
Expand All @@ -82,6 +97,8 @@ pub struct Params {
button_ch: MidiChannel,
button_cc: MidiCc,
nrpn: bool,
cc_send_value: i32,
cc_default_value: i32,
}

impl AppParams for Params {
Expand All @@ -103,6 +120,8 @@ impl AppParams for Params {
button_cc: MidiCc::from_value(values[10]),
nrpn: bool::from_value(values[11]),
midi_out: MidiOut::from_value(values[12]),
cc_send_value: i32::from_value(values[13]),
cc_default_value: i32::from_value(values[14]),
})
}

Expand All @@ -121,6 +140,8 @@ impl AppParams for Params {
vec.push(self.button_cc.into()).unwrap();
vec.push(Value::MidiNrpn(self.nrpn)).unwrap();
vec.push(self.midi_out.into()).unwrap();
vec.push(self.cc_send_value.into()).unwrap();
vec.push(self.cc_default_value.into()).unwrap();
vec
}
}
Expand Down Expand Up @@ -164,6 +185,8 @@ pub async fn wrapper(app: App<CHANNELS>, exit_signal: &'static Signal<NoopRawMut
button_ch: MidiChannel::default(),
button_cc: MidiCc::from(48u8.saturating_add(ch)),
nrpn: false,
cc_send_value: 127,
cc_default_value: -1,
},
);
let storage = ManagedStorage::<Storage>::new(app.app_id, app.layout_id);
Expand Down Expand Up @@ -204,6 +227,8 @@ pub async fn run(
button_ch,
button_cc,
nrpn,
cc_send_value,
cc_default_value,
) = params.query(|p| {
(
p.curve,
Expand All @@ -219,6 +244,8 @@ pub async fn run(
p.button_ch,
p.button_cc,
p.nrpn,
p.cc_send_value,
p.cc_default_value,
)
});

Expand Down Expand Up @@ -411,11 +438,15 @@ pub async fn run(
// Momentary mode: handle both press and release
buttons.wait_for_down(0).await;
leds.set(0, Led::Button, led_color, Brightness::Mid);
midi_button.send_cc(button_cc, 4095).await;
if let Some(v) = button_cc_value_12(cc_send_value) {
midi_button.send_cc(button_cc, v).await;
}

buttons.wait_for_up(0).await;
leds.unset(0, Led::Button);
midi_button.send_cc(button_cc, 0).await;
if let Some(v) = button_cc_value_12(cc_default_value) {
midi_button.send_cc(button_cc, v).await;
}
}
5 => {
// Invert momentary: default = CC 127 + LED Mid; press = CC 0 + LED off
Expand Down Expand Up @@ -464,12 +495,16 @@ pub async fn run(
if muted {
leds.unset(0, Led::Button);
if button_mode == 1 {
midi_button.send_cc(button_cc, 0).await;
if let Some(v) = button_cc_value_12(cc_default_value) {
midi_button.send_cc(button_cc, v).await;
}
}
} else {
leds.set(0, Led::Button, led_color, Brightness::Mid);
if button_mode == 1 {
midi_button.send_cc(button_cc, 4095).await;
if let Some(v) = button_cc_value_12(cc_send_value) {
midi_button.send_cc(button_cc, v).await;
}
}
}
}
Expand Down