diff --git a/src/midi.rs b/src/midi.rs index b8e78056..c2a674e9 100644 --- a/src/midi.rs +++ b/src/midi.rs @@ -256,6 +256,14 @@ pub enum NoteEvent { /// The note's brightness amount, from 0 to 1. brightness: f32, }, + /// A MIDI program change event, available on [`MidiConfig::MidiCCs`] and up. + MidiProgramChange { + timing: u32, + /// The affected channel, from 0 to 16. + channel: u8, + /// The program number. + program: u8, + }, /// A MIDI channel pressure event, available on [`MidiConfig::MidiCCs`] and up. MidiChannelPressure { timing: u32, @@ -307,6 +315,7 @@ impl NoteEvent { NoteEvent::PolyVibrato { timing, .. } => *timing, NoteEvent::PolyExpression { timing, .. } => *timing, NoteEvent::PolyBrightness { timing, .. } => *timing, + NoteEvent::MidiProgramChange { timing, .. } => *timing, NoteEvent::MidiChannelPressure { timing, .. } => *timing, NoteEvent::MidiPitchBend { timing, .. } => *timing, NoteEvent::MidiCC { timing, .. } => *timing, @@ -329,6 +338,7 @@ impl NoteEvent { NoteEvent::PolyVibrato { voice_id, .. } => *voice_id, NoteEvent::PolyExpression { voice_id, .. } => *voice_id, NoteEvent::PolyBrightness { voice_id, .. } => *voice_id, + NoteEvent::MidiProgramChange { .. } => None, NoteEvent::MidiChannelPressure { .. } => None, NoteEvent::MidiPitchBend { .. } => None, NoteEvent::MidiCC { .. } => None, @@ -363,6 +373,11 @@ impl NoteEvent { note: midi_data[1], pressure: midi_data[2] as f32 / 127.0, }), + midi::PROGRAM_CHANGE => Ok(NoteEvent::MidiProgramChange { + timing, + channel, + program: midi_data[1], + }), midi::CHANNEL_KEY_PRESSURE => Ok(NoteEvent::MidiChannelPressure { timing, channel, @@ -422,6 +437,15 @@ impl NoteEvent { note, (pressure * 127.0).round().clamp(0.0, 127.0) as u8, ]), + NoteEvent::MidiProgramChange { + timing: _, + channel, + program, + } => Some([ + midi::PROGRAM_CHANGE | channel, + program, + 0, + ]), NoteEvent::MidiChannelPressure { timing: _, channel, @@ -487,6 +511,7 @@ impl NoteEvent { NoteEvent::PolyVibrato { timing, .. } => *timing -= samples, NoteEvent::PolyExpression { timing, .. } => *timing -= samples, NoteEvent::PolyBrightness { timing, .. } => *timing -= samples, + NoteEvent::MidiProgramChange { timing, .. } => *timing -= samples, NoteEvent::MidiChannelPressure { timing, .. } => *timing -= samples, NoteEvent::MidiPitchBend { timing, .. } => *timing -= samples, NoteEvent::MidiCC { timing, .. } => *timing -= samples, @@ -549,6 +574,20 @@ mod tests { ); } + #[test] + fn test_program_change_midi_conversion() { + let event = NoteEvent::MidiProgramChange { + timing: TIMING, + channel: 1, + program: 42, + }; + + assert_eq!( + NoteEvent::from_midi(TIMING, event.as_midi().unwrap()).unwrap(), + event + ); + } + #[test] fn test_channel_pressure_midi_conversion() { let event = NoteEvent::MidiChannelPressure { diff --git a/src/wrapper/clap/wrapper.rs b/src/wrapper/clap/wrapper.rs index 6b020c16..1ffb1efa 100644 --- a/src/wrapper/clap/wrapper.rs +++ b/src/wrapper/clap/wrapper.rs @@ -1212,6 +1212,29 @@ impl Wrapper

{ clap_call! { out=>try_push(out, &event.header) } } + NoteEvent::MidiProgramChange { + timing: _, + channel, + program, + } if P::MIDI_OUTPUT >= MidiConfig::MidiCCs => { + let event = clap_event_midi { + header: clap_event_header { + size: mem::size_of::() as u32, + time, + space_id: CLAP_CORE_EVENT_SPACE_ID, + type_: CLAP_EVENT_MIDI, + flags: 0, + }, + port_index: 0, + data: [ + midi::PROGRAM_CHANGE | channel as u8, + program, + 0, + ], + }; + + clap_call! { out=>try_push(out, &event.header) } + } NoteEvent::MidiChannelPressure { timing: _, channel, diff --git a/src/wrapper/vst3/wrapper.rs b/src/wrapper/vst3/wrapper.rs index bc918c70..5559c1a2 100644 --- a/src/wrapper/vst3/wrapper.rs +++ b/src/wrapper/vst3/wrapper.rs @@ -1667,6 +1667,19 @@ impl IAudioProcessor for Wrapper

{ } } } + NoteEvent::MidiProgramChange { + timing: _, + channel, + program, + } if P::MIDI_OUTPUT >= MidiConfig::MidiCCs => { + vst3_event.type_ = EventTypes::kLegacyMIDICCOutEvent as u16; + vst3_event.event.legacy_midi_cc_out = LegacyMidiCCOutEvent { + control_number: 130, // kCtrlProgramChange + channel: channel as i8, + value: program as i8, + value2: 0, + }; + } NoteEvent::MidiChannelPressure { timing: _, channel,