├── LICENSE ├── README.md ├── cookiecutter.json └── {{ cookiecutter.project_name }} ├── .cargo └── config ├── .gitignore ├── Cargo.toml ├── README.md ├── bundler.toml ├── src └── lib.rs └── xtask ├── Cargo.toml └── src └── main.rs /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2022 Robbert van der Helm 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 14 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 | PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NIH-plug template 2 | 3 | This template can be used with 4 | [cookiecutter](https://github.com/cookiecutter/cookiecutter) to create a new 5 | [NIH-plug](https://github.com/robbert-vdh/nih-plug) project: 6 | 7 | ```bash 8 | cookiecutter gh:robbert-vdh/nih-plug-template 9 | ``` 10 | 11 | Check out the generated project's readme for building instructions. 12 | -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_name": "your_plugin_name (use underscores)", 3 | "struct_name": "{{ cookiecutter.project_name|replace('_', ' ')|title|replace(' ', '') }}", 4 | "plugin_name": "{{ cookiecutter.project_name|replace('_', ' ')|title }}", 5 | "author": "Your Name", 6 | "email_address": "your@email.com", 7 | "url": "https://youtu.be/dQw4w9WgXcQ", 8 | "description": "A short description of your plugin", 9 | "clap_id": "com.your-domain.{{ cookiecutter.project_name|replace('_', '-') }}", 10 | "vst3_id": "Exactly16Chars!!", 11 | "license": [ 12 | "GPL-3.0-or-later", 13 | "ISC", 14 | "Other licenses can be set in Cargo.toml, but using the project needs to be GPLv3 compliant to be able to use the VST3 exporter. Check Cargo.toml for more information." 15 | ], 16 | "\nDone\nMake sure to change the CLAP features and VST3 categories in src/lib.rs": "press enter to finish" 17 | } 18 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_name }}/.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run --package xtask --release --" 3 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_name }}/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_name }}/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "{{ cookiecutter.project_name }}" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["{{ cookiecutter.author }} <{{ cookiecutter.email_address }}>"] 6 | license = "{{ cookiecutter.license }}" 7 | homepage = "{{ cookiecutter.url }}" 8 | description = "{{ cookiecutter.description }}" 9 | 10 | [workspace] 11 | members = ["xtask"] 12 | 13 | [lib] 14 | crate-type = ["cdylib"] 15 | 16 | [dependencies] 17 | # Remove the `assert_process_allocs` feature to allow allocations on the audio 18 | # thread in debug builds. 19 | nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git", features = ["assert_process_allocs"] } 20 | # Uncomment the below line to disable the on-by-default VST3 feature to remove 21 | # the GPL compatibility requirement 22 | # nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git", default-features = false, features = ["assert_process_allocs"] } 23 | 24 | [profile.release] 25 | lto = "thin" 26 | strip = "symbols" 27 | 28 | [profile.profiling] 29 | inherits = "release" 30 | debug = true 31 | strip = "none" 32 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_name }}/README.md: -------------------------------------------------------------------------------- 1 | # {{ cookiecutter.plugin_name }} 2 | 3 | ## Building 4 | 5 | After installing [Rust](https://rustup.rs/), you can compile {{ cookiecutter.plugin_name }} as follows: 6 | 7 | ```shell 8 | cargo xtask bundle {{ cookiecutter.project_name }} --release 9 | ``` 10 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_name }}/bundler.toml: -------------------------------------------------------------------------------- 1 | # This provides metadata for NIH-plug's `cargo xtask bundle ` plugin 2 | # bundler. This file's syntax is as follows: 3 | # 4 | # [package_name] 5 | # name = "Human Readable Plugin Name" # defaults to 6 | 7 | [{{ cookiecutter.project_name }}] 8 | name = "{{ cookiecutter.plugin_name }}" 9 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_name }}/src/lib.rs: -------------------------------------------------------------------------------- 1 | use nih_plug::prelude::*; 2 | use std::sync::Arc; 3 | 4 | // This is a shortened version of the gain example with most comments removed, check out 5 | // https://github.com/robbert-vdh/nih-plug/blob/master/plugins/examples/gain/src/lib.rs to get 6 | // started 7 | 8 | struct {{ cookiecutter.struct_name }} { 9 | params: Arc<{{ cookiecutter.struct_name }}Params>, 10 | } 11 | 12 | #[derive(Params)] 13 | struct {{ cookiecutter.struct_name }}Params { 14 | /// The parameter's ID is used to identify the parameter in the wrappred plugin API. As long as 15 | /// these IDs remain constant, you can rename and reorder these fields as you wish. The 16 | /// parameters are exposed to the host in the same order they were defined. In this case, this 17 | /// gain parameter is stored as linear gain while the values are displayed in decibels. 18 | #[id = "gain"] 19 | pub gain: FloatParam, 20 | } 21 | 22 | impl Default for {{ cookiecutter.struct_name }} { 23 | fn default() -> Self { 24 | Self { 25 | params: Arc::new({{ cookiecutter.struct_name }}Params::default()), 26 | } 27 | } 28 | } 29 | 30 | impl Default for {{ cookiecutter.struct_name }}Params { 31 | fn default() -> Self { 32 | Self { 33 | // This gain is stored as linear gain. NIH-plug comes with useful conversion functions 34 | // to treat these kinds of parameters as if we were dealing with decibels. Storing this 35 | // as decibels is easier to work with, but requires a conversion for every sample. 36 | gain: FloatParam::new( 37 | "Gain", 38 | util::db_to_gain(0.0), 39 | FloatRange::Skewed { 40 | min: util::db_to_gain(-30.0), 41 | max: util::db_to_gain(30.0), 42 | // This makes the range appear as if it was linear when displaying the values as 43 | // decibels 44 | factor: FloatRange::gain_skew_factor(-30.0, 30.0), 45 | }, 46 | ) 47 | // Because the gain parameter is stored as linear gain instead of storing the value as 48 | // decibels, we need logarithmic smoothing 49 | .with_smoother(SmoothingStyle::Logarithmic(50.0)) 50 | .with_unit(" dB") 51 | // There are many predefined formatters we can use here. If the gain was stored as 52 | // decibels instead of as a linear gain value, we could have also used the 53 | // `.with_step_size(0.1)` function to get internal rounding. 54 | .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) 55 | .with_string_to_value(formatters::s2v_f32_gain_to_db()), 56 | } 57 | } 58 | } 59 | 60 | impl Plugin for {{ cookiecutter.struct_name }} { 61 | const NAME: &'static str = "{{ cookiecutter.plugin_name }}"; 62 | const VENDOR: &'static str = "{{ cookiecutter.author }}"; 63 | const URL: &'static str = env!("CARGO_PKG_HOMEPAGE"); 64 | const EMAIL: &'static str = "{{ cookiecutter.email_address }}"; 65 | 66 | const VERSION: &'static str = env!("CARGO_PKG_VERSION"); 67 | 68 | // The first audio IO layout is used as the default. The other layouts may be selected either 69 | // explicitly or automatically by the host or the user depending on the plugin API/backend. 70 | const AUDIO_IO_LAYOUTS: &'static [AudioIOLayout] = &[AudioIOLayout { 71 | main_input_channels: NonZeroU32::new(2), 72 | main_output_channels: NonZeroU32::new(2), 73 | 74 | aux_input_ports: &[], 75 | aux_output_ports: &[], 76 | 77 | // Individual ports and the layout as a whole can be named here. By default these names 78 | // are generated as needed. This layout will be called 'Stereo', while a layout with 79 | // only one input and output channel would be called 'Mono'. 80 | names: PortNames::const_default(), 81 | }]; 82 | 83 | 84 | const MIDI_INPUT: MidiConfig = MidiConfig::None; 85 | const MIDI_OUTPUT: MidiConfig = MidiConfig::None; 86 | 87 | const SAMPLE_ACCURATE_AUTOMATION: bool = true; 88 | 89 | // If the plugin can send or receive SysEx messages, it can define a type to wrap around those 90 | // messages here. The type implements the `SysExMessage` trait, which allows conversion to and 91 | // from plain byte buffers. 92 | type SysExMessage = (); 93 | // More advanced plugins can use this to run expensive background tasks. See the field's 94 | // documentation for more information. `()` means that the plugin does not have any background 95 | // tasks. 96 | type BackgroundTask = (); 97 | 98 | fn params(&self) -> Arc { 99 | self.params.clone() 100 | } 101 | 102 | fn initialize( 103 | &mut self, 104 | _audio_io_layout: &AudioIOLayout, 105 | _buffer_config: &BufferConfig, 106 | _context: &mut impl InitContext, 107 | ) -> bool { 108 | // Resize buffers and perform other potentially expensive initialization operations here. 109 | // The `reset()` function is always called right after this function. You can remove this 110 | // function if you do not need it. 111 | true 112 | } 113 | 114 | fn reset(&mut self) { 115 | // Reset buffers and envelopes here. This can be called from the audio thread and may not 116 | // allocate. You can remove this function if you do not need it. 117 | } 118 | 119 | fn process( 120 | &mut self, 121 | buffer: &mut Buffer, 122 | _aux: &mut AuxiliaryBuffers, 123 | _context: &mut impl ProcessContext, 124 | ) -> ProcessStatus { 125 | for channel_samples in buffer.iter_samples() { 126 | // Smoothing is optionally built into the parameters themselves 127 | let gain = self.params.gain.smoothed.next(); 128 | 129 | for sample in channel_samples { 130 | *sample *= gain; 131 | } 132 | } 133 | 134 | ProcessStatus::Normal 135 | } 136 | } 137 | 138 | impl ClapPlugin for {{ cookiecutter.struct_name }} { 139 | const CLAP_ID: &'static str = "{{ cookiecutter.clap_id }}"; 140 | const CLAP_DESCRIPTION: Option<&'static str> = Some("{{ cookiecutter.description }}"); 141 | const CLAP_MANUAL_URL: Option<&'static str> = Some(Self::URL); 142 | const CLAP_SUPPORT_URL: Option<&'static str> = None; 143 | 144 | // Don't forget to change these features 145 | const CLAP_FEATURES: &'static [ClapFeature] = &[ClapFeature::AudioEffect, ClapFeature::Stereo]; 146 | } 147 | 148 | impl Vst3Plugin for {{ cookiecutter.struct_name }} { 149 | const VST3_CLASS_ID: [u8; 16] = *b"{{ cookiecutter.vst3_id }}"; 150 | 151 | // And also don't forget to change these categories 152 | const VST3_SUBCATEGORIES: &'static [Vst3SubCategory] = 153 | &[Vst3SubCategory::Fx, Vst3SubCategory::Dynamics]; 154 | } 155 | 156 | nih_export_clap!({{ cookiecutter.struct_name }}); 157 | nih_export_vst3!({{ cookiecutter.struct_name }}); 158 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_name }}/xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | nih_plug_xtask = { git = "https://github.com/robbert-vdh/nih-plug.git" } 8 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_name }}/xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() -> nih_plug_xtask::Result<()> { 2 | nih_plug_xtask::main() 3 | } 4 | --------------------------------------------------------------------------------