Add a MIDI effect that inverts all signals
To demonstrates the API. The CLAP version does not seem to be able to output anything other than basic notes in Bitwig Studio 4.2.3.
This commit is contained in:
parent
5e486ab3d9
commit
49f1a45b76
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -1940,6 +1940,13 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f2dd5c7f8aaf48a76e389068ab25ed80bdbc226b887f9013844c415698c9952"
|
checksum = "6f2dd5c7f8aaf48a76e389068ab25ed80bdbc226b887f9013844c415698c9952"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "midi_inverter"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"nih_plug",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
|
|
@ -24,6 +24,7 @@ members = [
|
||||||
"plugins/examples/gain-gui-egui",
|
"plugins/examples/gain-gui-egui",
|
||||||
"plugins/examples/gain-gui-iced",
|
"plugins/examples/gain-gui-iced",
|
||||||
"plugins/examples/gain-gui-vizia",
|
"plugins/examples/gain-gui-vizia",
|
||||||
|
"plugins/examples/midi-inverter",
|
||||||
"plugins/examples/sine",
|
"plugins/examples/sine",
|
||||||
"plugins/examples/stft",
|
"plugins/examples/stft",
|
||||||
|
|
||||||
|
|
|
@ -145,6 +145,9 @@ examples.
|
||||||
[egui](plugins/examples/gain-gui-egui),
|
[egui](plugins/examples/gain-gui-egui),
|
||||||
[iced](plugins/examples/gain-gui-iced), and
|
[iced](plugins/examples/gain-gui-iced), and
|
||||||
[VIZIA](plugins/examples/gain-gui-vizia).
|
[VIZIA](plugins/examples/gain-gui-vizia).
|
||||||
|
- [**midi-inverter**](plugins/examples/midi-inverter) takes note/MIDI events and
|
||||||
|
flips around the note, channel, expression, pressure, and CC values. This
|
||||||
|
example demonstrates how to receive and output those events.
|
||||||
- [**sine**](plugins/examples/sine) is a simple test tone generator plugin with
|
- [**sine**](plugins/examples/sine) is a simple test tone generator plugin with
|
||||||
frequency smoothing that can also make use of MIDI input instead of generating
|
frequency smoothing that can also make use of MIDI input instead of generating
|
||||||
a static signal based on the plugin's parameters.
|
a static signal based on the plugin's parameters.
|
||||||
|
|
12
plugins/examples/midi-inverter/Cargo.toml
Normal file
12
plugins/examples/midi-inverter/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "midi_inverter"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Robbert van der Helm <mail@robbertvanderhelm.nl>"]
|
||||||
|
license = "ISC"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nih_plug = { path = "../../../", features = ["assert_process_allocs"] }
|
201
plugins/examples/midi-inverter/src/lib.rs
Normal file
201
plugins/examples/midi-inverter/src/lib.rs
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
use nih_plug::prelude::*;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// A plugin that inverts all MIDI note numbers, channels, CCs, velocitires, pressures, and
|
||||||
|
/// everything else you don't want to be inverted.
|
||||||
|
struct MidiInverter {
|
||||||
|
params: Arc<MidiInverterParams>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Params)]
|
||||||
|
struct MidiInverterParams {}
|
||||||
|
|
||||||
|
impl Default for MidiInverter {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
params: Arc::new(MidiInverterParams::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Plugin for MidiInverter {
|
||||||
|
const NAME: &'static str = "MIDI Inverter";
|
||||||
|
const VENDOR: &'static str = "Moist Plugins GmbH";
|
||||||
|
const URL: &'static str = "https://youtu.be/dQw4w9WgXcQ";
|
||||||
|
const EMAIL: &'static str = "info@example.com";
|
||||||
|
|
||||||
|
const VERSION: &'static str = "0.0.1";
|
||||||
|
|
||||||
|
const DEFAULT_NUM_INPUTS: u32 = 0;
|
||||||
|
const DEFAULT_NUM_OUTPUTS: u32 = 0;
|
||||||
|
|
||||||
|
const MIDI_INPUT: MidiConfig = MidiConfig::MidiCCs;
|
||||||
|
const MIDI_OUTPUT: MidiConfig = MidiConfig::MidiCCs;
|
||||||
|
const SAMPLE_ACCURATE_AUTOMATION: bool = true;
|
||||||
|
|
||||||
|
fn params(&self) -> Arc<dyn Params> {
|
||||||
|
// The explicit cast is not needed, Rust Analyzer just doesn't get it otherwise
|
||||||
|
self.params.clone() as Arc<dyn Params>
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process(
|
||||||
|
&mut self,
|
||||||
|
_buffer: &mut Buffer,
|
||||||
|
context: &mut impl ProcessContext,
|
||||||
|
) -> ProcessStatus {
|
||||||
|
// Act on the next MIDI event
|
||||||
|
while let Some(event) = context.next_event() {
|
||||||
|
match event {
|
||||||
|
NoteEvent::NoteOn {
|
||||||
|
timing,
|
||||||
|
channel,
|
||||||
|
note,
|
||||||
|
velocity,
|
||||||
|
} => context.send_event(NoteEvent::NoteOn {
|
||||||
|
timing,
|
||||||
|
channel: 15 - channel,
|
||||||
|
note: 127 - note,
|
||||||
|
velocity: 1.0 - velocity,
|
||||||
|
}),
|
||||||
|
NoteEvent::NoteOff {
|
||||||
|
timing,
|
||||||
|
channel,
|
||||||
|
note,
|
||||||
|
velocity,
|
||||||
|
} => context.send_event(NoteEvent::NoteOff {
|
||||||
|
timing,
|
||||||
|
channel: 15 - channel,
|
||||||
|
note: 127 - note,
|
||||||
|
velocity: 1.0 - velocity,
|
||||||
|
}),
|
||||||
|
NoteEvent::PolyPressure {
|
||||||
|
timing,
|
||||||
|
channel,
|
||||||
|
note,
|
||||||
|
pressure,
|
||||||
|
} => context.send_event(NoteEvent::PolyPressure {
|
||||||
|
timing,
|
||||||
|
channel: 15 - channel,
|
||||||
|
note: 127 - note,
|
||||||
|
pressure: 1.0 - pressure,
|
||||||
|
}),
|
||||||
|
NoteEvent::PolyVolume {
|
||||||
|
timing,
|
||||||
|
channel,
|
||||||
|
note,
|
||||||
|
gain,
|
||||||
|
} => context.send_event(NoteEvent::PolyVolume {
|
||||||
|
timing,
|
||||||
|
channel: 15 - channel,
|
||||||
|
note: 127 - note,
|
||||||
|
gain: 1.0 - gain,
|
||||||
|
}),
|
||||||
|
NoteEvent::PolyPan {
|
||||||
|
timing,
|
||||||
|
channel,
|
||||||
|
note,
|
||||||
|
pan,
|
||||||
|
} => context.send_event(NoteEvent::PolyPan {
|
||||||
|
timing,
|
||||||
|
channel: 15 - channel,
|
||||||
|
note: 127 - note,
|
||||||
|
pan: 1.0 - pan,
|
||||||
|
}),
|
||||||
|
NoteEvent::PolyTuning {
|
||||||
|
timing,
|
||||||
|
channel,
|
||||||
|
note,
|
||||||
|
tuning,
|
||||||
|
} => context.send_event(NoteEvent::PolyTuning {
|
||||||
|
timing,
|
||||||
|
channel: 15 - channel,
|
||||||
|
note: 127 - note,
|
||||||
|
tuning: 1.0 - tuning,
|
||||||
|
}),
|
||||||
|
NoteEvent::PolyVibrato {
|
||||||
|
timing,
|
||||||
|
channel,
|
||||||
|
note,
|
||||||
|
vibrato,
|
||||||
|
} => context.send_event(NoteEvent::PolyVibrato {
|
||||||
|
timing,
|
||||||
|
channel: 15 - channel,
|
||||||
|
note: 127 - note,
|
||||||
|
vibrato: 1.0 - vibrato,
|
||||||
|
}),
|
||||||
|
NoteEvent::PolyExpression {
|
||||||
|
timing,
|
||||||
|
channel,
|
||||||
|
note,
|
||||||
|
expression,
|
||||||
|
} => context.send_event(NoteEvent::PolyExpression {
|
||||||
|
timing,
|
||||||
|
channel: 15 - channel,
|
||||||
|
note: 127 - note,
|
||||||
|
expression: 1.0 - expression,
|
||||||
|
}),
|
||||||
|
NoteEvent::PolyBrightness {
|
||||||
|
timing,
|
||||||
|
channel,
|
||||||
|
note,
|
||||||
|
brightness,
|
||||||
|
} => context.send_event(NoteEvent::PolyBrightness {
|
||||||
|
timing,
|
||||||
|
channel: 15 - channel,
|
||||||
|
note: 127 - note,
|
||||||
|
brightness: 1.0 - brightness,
|
||||||
|
}),
|
||||||
|
NoteEvent::MidiChannelPressure {
|
||||||
|
timing,
|
||||||
|
channel,
|
||||||
|
pressure,
|
||||||
|
} => context.send_event(NoteEvent::MidiChannelPressure {
|
||||||
|
timing,
|
||||||
|
channel: 15 - channel,
|
||||||
|
pressure: 1.0 - pressure,
|
||||||
|
}),
|
||||||
|
NoteEvent::MidiPitchBend {
|
||||||
|
timing,
|
||||||
|
channel,
|
||||||
|
value,
|
||||||
|
} => context.send_event(NoteEvent::MidiPitchBend {
|
||||||
|
timing,
|
||||||
|
channel: 15 - channel,
|
||||||
|
value: 1.0 - value,
|
||||||
|
}),
|
||||||
|
NoteEvent::MidiCC {
|
||||||
|
timing,
|
||||||
|
channel,
|
||||||
|
cc,
|
||||||
|
value,
|
||||||
|
} => context.send_event(NoteEvent::MidiCC {
|
||||||
|
timing,
|
||||||
|
channel: 15 - channel,
|
||||||
|
// The one thing we won't invert, because uuhhhh
|
||||||
|
cc,
|
||||||
|
value: 1.0 - value,
|
||||||
|
}),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessStatus::Normal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClapPlugin for MidiInverter {
|
||||||
|
const CLAP_ID: &'static str = "com.moist-plugins-gmbh.midi-inverter";
|
||||||
|
const CLAP_DESCRIPTION: &'static str =
|
||||||
|
"Inverts all note and MIDI signals in ways you don't want to";
|
||||||
|
const CLAP_FEATURES: &'static [&'static str] = &["note_effect", "utility"];
|
||||||
|
const CLAP_MANUAL_URL: &'static str = Self::URL;
|
||||||
|
const CLAP_SUPPORT_URL: &'static str = Self::URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vst3Plugin for MidiInverter {
|
||||||
|
const VST3_CLASS_ID: [u8; 16] = *b"M1d1Inv3r70rzAaA";
|
||||||
|
const VST3_CATEGORIES: &'static str = "Instrument|Tools";
|
||||||
|
}
|
||||||
|
|
||||||
|
nih_export_clap!(MidiInverter);
|
||||||
|
nih_export_vst3!(MidiInverter);
|
Loading…
Reference in a new issue