use std::sync::Arc; use nih_plug::prelude::*; use nih_plug::plugin::vst3::Vst3Plugin; use nih_plug::plugin::clap::ClapPlugin; use nih_plug::wrapper::clap::features::ClapFeature; use nih_plug::wrapper::vst3::subcategories::Vst3SubCategory; mod engine; use engine::Engine; /// The main plugin struct pub struct ClipLauncher { /// The engine managing clips engine: Engine, /// Pending MIDI output events pending_midi: Vec>, } impl Default for ClipLauncher { fn default() -> Self { Self { engine: Engine::new(2, 44100.0), pending_midi: Vec::new(), } } } impl Plugin for ClipLauncher { const NAME: &'static str = "Clip Launcher"; const VENDOR: &'static str = "Your Company"; const URL: &'static str = ""; const EMAIL: &'static str = ""; const VERSION: &'static str = "0.1.0"; const AUDIO_IO_LAYOUTS: &'static [AudioIOLayout] = &[ AudioIOLayout { main_input_channels: NonZeroU32::new(2), main_output_channels: NonZeroU32::new(2), ..AudioIOLayout::const_default() }, ]; const MIDI_INPUT: MidiConfig = MidiConfig::Basic; const MIDI_OUTPUT: MidiConfig = MidiConfig::Basic; const SAMPLE_ACCURATE_AUTOMATION: bool = true; type SysExMessage = (); type BackgroundTask = (); fn initialize( &mut self, _audio_io_layout: &AudioIOLayout, buffer_config: &BufferConfig, _context: &mut impl InitContext, ) -> bool { self.engine = Engine::new( _audio_io_layout.main_input_channels.map(|c| c.get() as usize).unwrap_or(2), buffer_config.sample_rate, ); true } fn reset(&mut self) { // Clear all clips on reset self.engine.clips.clear(); self.pending_midi.clear(); } fn process( &mut self, buffer: &mut Buffer, _aux: &mut AuxiliaryBuffers, context: &mut impl ProcessContext, ) -> ProcessStatus { // Process MIDI input events while let Some(event) = context.next_event() { match event { NoteEvent::NoteOn { timing, note, velocity, channel, voice_id } => { let output_velocity = self.engine.process_midi_note(note, velocity as u8, true); // Queue output MIDI event self.pending_midi.push(NoteEvent::NoteOn { timing, note, velocity: output_velocity as f32, channel, voice_id, }); } NoteEvent::NoteOff { timing, note, velocity, channel, voice_id } => { let output_velocity = self.engine.process_midi_note(note, velocity as u8, false); self.pending_midi.push(NoteEvent::NoteOff { timing, note, velocity: output_velocity as f32, channel, voice_id, }); } _ => {} } } // Process audio let num_channels = buffer.channels() as usize; let num_frames = buffer.samples() as usize; let input_slices = buffer.as_slice(); // &[&[f32]] // Build interleaved input buffer let mut interleaved_input = vec![0.0; num_frames * num_channels]; for ch in 0..num_channels { for frame in 0..num_frames { interleaved_input[frame * num_channels + ch] = input_slices[ch][frame]; } } // Build interleaved output buffer (will be filled by engine) let mut interleaved_output = vec![0.0; num_frames * num_channels]; self.engine.process_audio(&interleaved_input, &mut interleaved_output, num_frames); // Copy back to buffer's output channels for (ch, channel_samples) in buffer.iter_samples().enumerate() { let (_, output) = *channel_samples; for frame in 0..num_frames { output[frame] = interleaved_output[frame * num_channels + ch]; } } // Send pending MIDI output events for event in self.pending_midi.drain(..) { context.send_event(event); } ProcessStatus::Normal } fn params(&self) -> Arc { Arc::new(ClipLauncherParams { dummy: BoolParam::new("Dummy", false), }) } } impl Vst3Plugin for ClipLauncher { const VST3_CLASS_ID: [u8; 16] = *b"ClipLauncher1234"; const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = &[Vst3SubCategory::Fx]; } impl ClapPlugin for ClipLauncher { const CLAP_ID: &'static str = "com.yourcompany.clip-launcher"; const CLAP_DESCRIPTION: Option<&'static str> = Some("A clip launcher plugin"); const CLAP_MANUAL_URL: Option<&'static str> = None; const CLAP_SUPPORT_URL: Option<&'static str> = None; const CLAP_FEATURES: &'static [ClapFeature] = &[ClapFeature::AudioEffect, ClapFeature::Stereo]; } /// Empty params struct (no parameters needed for this plugin) #[derive(Params)] struct ClipLauncherParams { /// Placeholder parameter to satisfy the derive macro #[id = "dummy"] dummy: BoolParam, } // Export the plugin nih_export_vst3!(ClipLauncher); nih_export_clap!(ClipLauncher);