1
0
Fork 0

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:
Robbert van der Helm 2022-04-11 20:47:00 +02:00
parent 5e486ab3d9
commit 49f1a45b76
5 changed files with 224 additions and 0 deletions

7
Cargo.lock generated
View file

@ -1940,6 +1940,13 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f2dd5c7f8aaf48a76e389068ab25ed80bdbc226b887f9013844c415698c9952"
[[package]]
name = "midi_inverter"
version = "0.1.0"
dependencies = [
"nih_plug",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"

View file

@ -24,6 +24,7 @@ members = [
"plugins/examples/gain-gui-egui",
"plugins/examples/gain-gui-iced",
"plugins/examples/gain-gui-vizia",
"plugins/examples/midi-inverter",
"plugins/examples/sine",
"plugins/examples/stft",

View file

@ -145,6 +145,9 @@ examples.
[egui](plugins/examples/gain-gui-egui),
[iced](plugins/examples/gain-gui-iced), and
[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
frequency smoothing that can also make use of MIDI input instead of generating
a static signal based on the plugin's parameters.

View 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"] }

View 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);