1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
use clap::{Parser, ValueEnum};
use std::num::NonZeroU32;
use crate::prelude::{AudioIOLayout, Plugin};
/// Configuration for a standalone plugin that would normally be provided by the DAW.
#[derive(Debug, Clone, Parser)]
#[clap(about = None, long_about = None)]
pub struct WrapperConfig {
/// The audio and MIDI backend to use.
///
/// The 'auto' option will try all backends in order, and falls back to the dummy backend with
/// no audio input or output if the other backends are not available.
#[clap(value_parser, short = 'b', long, default_value = "auto")]
pub backend: BackendType,
/// The audio layout to use. Defaults to the first layout.
///
/// Specifying an empty argument or other invalid value will list all available audio layouts.
//
// NOTE: This takes a `String` instead of a `usize` so we can list the layouts when the argument
// is invalid
#[clap(value_parser, short = 'l', long)]
pub audio_layout: Option<String>,
/// The audio backend's sample rate.
///
/// This setting is ignored when using the JACK backend.
#[clap(value_parser, short = 'r', long, default_value = "48000")]
pub sample_rate: f32,
/// The audio backend's period size.
///
/// This setting is ignored when using the JACK backend.
#[clap(value_parser, short = 'p', long, default_value = "512")]
pub period_size: u32,
/// The input device for the ALSA, CoreAudio, and WASAPI backends. No input will be connected if
/// this is not specified.
///
/// Specifying an empty string or other invalid value will list all available input devices.
#[clap(value_parser, long)]
pub input_device: Option<String>,
/// The output device for the ALSA, CoreAudio, and WASAPI backends.
///
/// Specifying an empty string or other invalid value will list all available output devices.
#[clap(value_parser, long)]
pub output_device: Option<String>,
/// The input MIDI device for the ALSA, CoreAudio, and WASAPI backends.
///
/// Specifying an empty string or other invalid value will list all available MIDI inputs.
#[clap(value_parser, long)]
pub midi_input: Option<String>,
/// The output output device for the ALSA, CoreAudio, and WASAPI backends.
///
/// Specifying an empty string or other invalid value will list all available MIDI output.
#[clap(value_parser, long)]
pub midi_output: Option<String>,
/// If set to a port name ('foo:bar_1'), then all all inputs will be connected to that port. If
/// the option is set to a comma separated list of port names ('foo:bar_1,foo:bar_2') then the
/// input ports will be connected in that order. No inputs will be connected if the port option
/// is not set.
///
/// This option is only used with the JACK backend.
#[clap(value_parser, long)]
pub connect_jack_inputs: Option<String>,
/// If set, then the plugin's MIDI input port will be connected to this JACK MIDI output port.
///
/// This option is only used with the JACK backend.
#[clap(value_parser, long)]
pub connect_jack_midi_input: Option<String>,
/// If set, then the plugin's MIDI output port will be connected to this JACK MIDI input port.
///
/// This option is only used with the JACK backend.
#[clap(value_parser, long)]
pub connect_jack_midi_output: Option<String>,
/// The editor's DPI scaling factor.
///
/// This option is ignored on macOS.
//
// Currently baseview has no way to report this to us, so we'll expose it as a command line
// option instead.
#[clap(value_parser, long, default_value = "1.0")]
pub dpi_scale: f32,
/// The transport's tempo.
#[clap(value_parser, long, default_value = "120")]
pub tempo: f32,
/// The time signature's numerator.
#[clap(value_parser, long, default_value = "4")]
pub timesig_num: u32,
/// The time signature's denominator.
#[clap(value_parser, long, default_value = "4")]
pub timesig_denom: u32,
}
/// Determines which audio and MIDI backend should be used.
#[derive(Debug, Clone, ValueEnum)]
pub enum BackendType {
/// Automatically pick the backend depending on what's available.
///
/// This defaults to JACK if JACK is available, and falls back to the dummy backend if not.
Auto,
/// Use JACK for audio and MIDI.
Jack,
/// Use ALSA for audio and MIDI.
#[cfg(target_os = "linux")]
Alsa,
/// Use CoreAudio for audio and MIDI.
#[cfg(target_os = "macos")]
CoreAudio,
/// Use WASAPI for audio and MIDI.
#[cfg(target_os = "windows")]
Wasapi,
/// Does not playback or receive any audio or MIDI.
Dummy,
}
impl WrapperConfig {
/// Get the audio IO layout for a plugin based on this configuration. Exits the application if
/// the IO layout could not be parsed from the config. This doesn't return a `Result` to be able to differentiate between backend-specific errors and config parsing errors.
pub fn audio_io_layout_or_exit<P: Plugin>(&self) -> AudioIOLayout {
// The layouts are one-indexed here
match &self.audio_layout {
Some(audio_layout) if !P::AUDIO_IO_LAYOUTS.is_empty() => {
match audio_layout.parse::<usize>() {
Ok(n) if n >= 1 && n - 1 < P::AUDIO_IO_LAYOUTS.len() => {
P::AUDIO_IO_LAYOUTS[n - 1]
}
_ => {
// This is made to be consistent with how audio input and output devices are
// listed in the CPAL backend
let mut layouts_str = String::new();
for (idx, layout) in P::AUDIO_IO_LAYOUTS.iter().enumerate() {
let num_input_channels = layout
.main_input_channels
.map(NonZeroU32::get)
.unwrap_or_default();
let num_output_channels = layout
.main_output_channels
.map(NonZeroU32::get)
.unwrap_or_default();
layouts_str.push_str(&format!(
"\n{}: {} ({} input {}, {} output {}{}{})",
idx + 1,
layout.name(),
num_input_channels,
if num_input_channels == 1 {
"channel"
} else {
"channels"
},
num_output_channels,
if num_output_channels == 1 {
"channel"
} else {
"channels"
},
if layout.aux_input_ports.is_empty() {
String::new()
} else {
format!("{} sidechain inputs", layout.aux_input_ports.len())
},
if layout.aux_output_ports.is_empty() {
String::new()
} else {
format!("{} sidechain outputs", layout.aux_output_ports.len())
},
))
}
nih_log!("The available audio layouts are:{layouts_str}");
std::process::exit(1);
}
}
}
_ => P::AUDIO_IO_LAYOUTS.first().copied().unwrap_or_default(),
}
}
}