use std::sync::Arc; use nih_plug::prelude::*; use nih_plug_iced::IcedState; mod dfpwm; mod editor; pub struct Worm { params: Arc, compress_contexts: Vec, decompress_contexts: Vec, } #[derive(Params)] struct WormParams { #[persist = "editor-state"] editor_state: Arc, #[id = "response-increase"] pub response_increase: FloatParam, #[id = "response-decrease"] pub response_decrease: FloatParam, } impl Default for Worm { fn default() -> Self { Self { params: Arc::new(WormParams::default()), compress_contexts: Vec::new(), decompress_contexts: Vec::new(), } } } impl Default for WormParams { fn default() -> Self { Self { editor_state: editor::default_state(), response_increase: FloatParam::new( "Response Increase", dfpwm::DEFAULT_RESPONSE_INCREASE, FloatRange::Linear { min: 0.0, max: 1.0 }, ), response_decrease: FloatParam::new( "Response Decrease", dfpwm::DEFAULT_RESPONSE_DECREASE, FloatRange::Linear { min: 0.0, max: 1.0 }, ), } } } impl Plugin for Worm { const NAME: &'static str = "Dirty Frying Pan Worm"; const VENDOR: &'static str = "fogwaves"; const URL: &'static str = "https://example.com"; const EMAIL: &'static str = "info@example.com"; const VERSION: &'static str = env!("CARGO_PKG_VERSION"); const AUDIO_IO_LAYOUTS: &'static [AudioIOLayout] = &[ AudioIOLayout { main_input_channels: NonZeroU32::new(2), main_output_channels: NonZeroU32::new(2), ..AudioIOLayout::const_default() }, AudioIOLayout { main_input_channels: NonZeroU32::new(1), main_output_channels: NonZeroU32::new(1), ..AudioIOLayout::const_default() }, ]; const SAMPLE_ACCURATE_AUTOMATION: bool = true; type SysExMessage = (); type BackgroundTask = (); fn params(&self) -> Arc { self.params.clone() } fn editor(&mut self, _async_executor: AsyncExecutor) -> Option> { editor::create(self.params.clone(), self.params.editor_state.clone()) } fn initialize( &mut self, audio_io_layout: &AudioIOLayout, _buffer_config: &BufferConfig, _context: &mut impl InitContext, ) -> bool { if let Some(n) = audio_io_layout.main_input_channels { for _i in 0..n.into() { self.compress_contexts.push(dfpwm::Context::new()); self.decompress_contexts.push(dfpwm::Context::new()); } } true } fn reset(&mut self) { for ctx in self.compress_contexts.iter_mut() { ctx.reset(); } for ctx in self.decompress_contexts.iter_mut() { ctx.reset(); } } fn deactivate(&mut self) {} fn process( &mut self, buffer: &mut Buffer, _aux: &mut AuxiliaryBuffers, _context: &mut impl ProcessContext, ) -> ProcessStatus { for channel_samples in buffer.iter_samples() { let response_increase = self.params.response_increase.smoothed.next(); let response_decrease = self.params.response_decrease.smoothed.next(); for (i, sample) in channel_samples.into_iter().enumerate() { self.compress_contexts[i].response_increase = response_increase; self.decompress_contexts[i].response_decrease = response_decrease; let bit = self.compress_contexts[i].compress(*sample); *sample = self.decompress_contexts[i].decompress(bit); } } ProcessStatus::Normal } } impl ClapPlugin for Worm { const CLAP_ID: &'static str = "fogwaves.dfpworm"; const CLAP_DESCRIPTION: Option<&'static str> = Some("Runs audio through the DFPWM 1-bit audio codec by Ben \"GreaseMonkey\" Russel."); const CLAP_MANUAL_URL: Option<&'static str> = Some(Self::URL); const CLAP_SUPPORT_URL: Option<&'static str> = None; const CLAP_FEATURES: &'static [ClapFeature] = &[ ClapFeature::AudioEffect, ClapFeature::Stereo, ClapFeature::Mono, ClapFeature::Distortion, ]; } impl Vst3Plugin for Worm { const VST3_CLASS_ID: [u8; 16] = *b"FogwavesDFPWormC"; const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = &[Vst3SubCategory::Fx, Vst3SubCategory::Distortion]; } nih_export_clap!(Worm); nih_export_vst3!(Worm);