use nice_plug_core::midi::{NoteEvent, sysex::SysExMessage};
use vst3::Steinberg::Vst::{NoteExpressionValueEvent, NoteOnEvent};
type MidiNote = u8;
type MidiChannel = u8;
type NoteId = i32;
const NOTE_IDS_LEN: usize = 32;
pub const VOLUME_EXPRESSION_ID: u32 = 0;
pub const PAN_EXPRESSION_ID: u32 = 1;
pub const TUNING_EXPRESSION_ID: u32 = 2;
pub const VIBRATO_EXPRESSION_ID: u32 = 3;
pub const EXPRESSION_EXPRESSION_ID: u32 = 4;
pub const BRIGHTNESS_EXPRESSION_ID: u32 = 5;
pub const KNOWN_NOTE_EXPRESSIONS: [NoteExpressionInfo; 6] = [
NoteExpressionInfo {
type_id: VOLUME_EXPRESSION_ID,
title: "Volume",
unit: "dB",
},
NoteExpressionInfo {
type_id: PAN_EXPRESSION_ID,
title: "Pan",
unit: "",
},
NoteExpressionInfo {
type_id: TUNING_EXPRESSION_ID,
title: "Tuning",
unit: "semitones",
},
NoteExpressionInfo {
type_id: VIBRATO_EXPRESSION_ID,
title: "Vibrato",
unit: "",
},
NoteExpressionInfo {
type_id: EXPRESSION_EXPRESSION_ID,
title: "Expression",
unit: "",
},
NoteExpressionInfo {
type_id: BRIGHTNESS_EXPRESSION_ID,
title: "Brightness",
unit: "",
},
];
#[derive(Debug, Default)]
pub struct NoteExpressionController {
note_ids: [(NoteId, MidiNote, MidiChannel); NOTE_IDS_LEN],
note_ids_idx: usize,
}
pub struct NoteExpressionInfo {
pub type_id: u32,
pub title: &'static str,
pub unit: &'static str,
}
impl NoteExpressionController {
pub fn register_note(&mut self, event: &NoteOnEvent) {
self.note_ids[self.note_ids_idx] = (event.noteId, event.pitch as u8, event.channel as u8);
self.note_ids_idx = (self.note_ids_idx + 1) % NOTE_IDS_LEN;
}
pub fn translate_event<S: SysExMessage>(
&self,
timing: u32,
event: &NoteExpressionValueEvent,
) -> Option<NoteEvent<S>> {
let (note_id, note, channel) = *self
.note_ids
.iter()
.find(|(note_id, _, _)| *note_id == event.noteId)?;
match event.typeId {
VOLUME_EXPRESSION_ID => Some(NoteEvent::PolyVolume {
timing,
voice_id: Some(note_id),
channel,
note,
gain: event.value as f32 * 4.0,
}),
PAN_EXPRESSION_ID => Some(NoteEvent::PolyPan {
timing,
voice_id: Some(note_id),
channel,
note,
pan: (event.value as f32 * 2.0) - 1.0,
}),
TUNING_EXPRESSION_ID => Some(NoteEvent::PolyTuning {
timing,
voice_id: Some(note_id),
channel,
note,
tuning: 240.0 * (event.value as f32 - 0.5),
}),
VIBRATO_EXPRESSION_ID => Some(NoteEvent::PolyVibrato {
timing,
voice_id: Some(note_id),
channel,
note,
vibrato: event.value as f32,
}),
EXPRESSION_EXPRESSION_ID => Some(NoteEvent::PolyBrightness {
timing,
voice_id: Some(note_id),
channel,
note,
brightness: event.value as f32,
}),
BRIGHTNESS_EXPRESSION_ID => Some(NoteEvent::PolyExpression {
timing,
voice_id: Some(note_id),
channel,
note,
expression: event.value as f32,
}),
_ => None,
}
}
pub fn translate_event_reverse(
note_id: i32,
event: &NoteEvent<impl SysExMessage>,
) -> Option<NoteExpressionValueEvent> {
match &event {
NoteEvent::PolyVolume { gain, .. } => Some(NoteExpressionValueEvent {
typeId: VOLUME_EXPRESSION_ID,
noteId: note_id,
value: *gain as f64 / 4.0,
}),
NoteEvent::PolyPan { pan, .. } => Some(NoteExpressionValueEvent {
typeId: PAN_EXPRESSION_ID,
noteId: note_id,
value: (*pan as f64 + 1.0) / 2.0,
}),
NoteEvent::PolyTuning { tuning, .. } => Some(NoteExpressionValueEvent {
typeId: TUNING_EXPRESSION_ID,
noteId: note_id,
value: (*tuning as f64 / 240.0) + 0.5,
}),
NoteEvent::PolyVibrato { vibrato, .. } => Some(NoteExpressionValueEvent {
typeId: VIBRATO_EXPRESSION_ID,
noteId: note_id,
value: *vibrato as f64,
}),
NoteEvent::PolyExpression { expression, .. } => Some(NoteExpressionValueEvent {
typeId: EXPRESSION_EXPRESSION_ID,
noteId: note_id,
value: *expression as f64,
}),
NoteEvent::PolyBrightness { brightness, .. } => Some(NoteExpressionValueEvent {
typeId: BRIGHTNESS_EXPRESSION_ID,
noteId: note_id,
value: *brightness as f64,
}),
_ => None,
}
}
}