voicemeeter\interface/
parameters.rs

1//! Functions for setting and getting parameter values.
2//!
3//! Note that if your application exits quickly after setting a parameter, voicemeeter may not update.
4//! If you do this, you should maybe insert a small sleep before dropping the remote.'
5//!
6//! # Functions
7//!
8//! * [`parameters`](VoicemeeterRemote::parameters)
9
10// FIXME: file above as an issue upstream, is this an issue?
11use std::borrow::Cow;
12use std::fmt::Debug;
13use std::fmt::Display;
14use std::ops::{Bound, RangeBounds};
15
16use crate::types::{BusMode, Device, ParameterNameRef, VoicemeeterApplication, ZIndex};
17use crate::VoicemeeterRemote;
18
19mod errors;
20
21pub mod bus;
22pub mod eq;
23pub mod fx;
24pub mod get_parameters;
25pub mod option;
26pub mod recorder;
27pub mod set_parameters;
28pub mod strip;
29pub mod vban;
30
31pub use bus::*;
32pub use eq::*;
33pub use errors::*;
34pub use fx::*;
35pub use option::*;
36pub use recorder::*;
37pub use strip::*;
38pub use vban::*;
39
40use self::get_parameters::GetParameterError;
41use self::set_parameters::SetParameterError;
42
43pub(crate) static BUS: &str = "Bus";
44pub(crate) static FX: &str = "Recorder";
45pub(crate) static RECORDER: &str = "Recorder";
46pub(crate) static STRIP: &str = "Strip";
47pub(crate) static VOICEMEETER_OPTION: &str = "Option";
48pub(crate) static VBAN: &str = "vban";
49
50impl VoicemeeterRemote {
51    /// Get access to [parameters](Parameters) of the application.
52    ///
53    /// # Examples
54    ///
55    /// ```rust,no_run
56    /// use voicemeeter::VoicemeeterRemote;
57    ///
58    /// # let remote: VoicemeeterRemote = todo!();
59    /// println!("Strip 1: {}", remote.parameters().strip(0)?.label().get()?);
60    ///
61    /// println!(
62    ///     "Bus 0 (A1) Device: {}",
63    ///     remote.parameters().bus(0)?.device().name().get()?
64    /// );
65    ///
66    /// // Enable A1 on strip 1
67    /// remote.parameters().strip(0)?.a1().set(true)?;
68    ///
69    /// # Ok::<(), Box<dyn std::error::Error>>(())
70    /// ```
71    pub fn parameters(&self) -> Parameters<'_> {
72        Parameters { remote: self }
73    }
74}
75
76/// A float parameter
77#[must_use = "set or get the value of the parameter"]
78pub struct FloatParameter<'a, const WRITE: bool = true, const READ: bool = true> {
79    /// The name of the parameter, fully qualified
80    pub name: Cow<'a, ParameterNameRef>,
81
82    remote: &'a VoicemeeterRemote,
83    _range: Option<(Bound<f32>, Bound<f32>)>,
84}
85
86impl<'a, const WRITE: bool, const READ: bool> FloatParameter<'a, WRITE, READ> {
87    fn new<R>(name: Cow<'a, ParameterNameRef>, remote: &'a VoicemeeterRemote, range: R) -> Self
88    where
89        R: RangeBounds<f32>,
90    {
91        Self {
92            name,
93            remote,
94            _range: Some((range.start_bound().cloned(), range.end_bound().cloned())),
95        }
96    }
97    fn new_unranged(name: Cow<'a, ParameterNameRef>, remote: &'a VoicemeeterRemote) -> Self {
98        Self {
99            name,
100            remote,
101            _range: None,
102        }
103    }
104}
105
106impl<'a, const READ: bool> FloatParameter<'a, true, READ> {
107    /// Set the value of this parameter
108    pub fn set(&self, val: f32) -> Result<(), SetParameterError> {
109        self.remote.set_parameter_float(&self.name, val)
110    }
111}
112
113impl<'a, const WRITE: bool> FloatParameter<'a, WRITE, true> {
114    /// Get the value of this parameter
115    pub fn get(&self) -> Result<f32, GetParameterError> {
116        self.remote.get_parameter_float(&self.name)
117    }
118}
119
120/// A string parameter
121#[must_use = "set or get the value of the parameter"]
122pub struct StringParameter<'a, const WRITE: bool = true, const READ: bool = true> {
123    /// The name of the parameter, fully qualified
124    pub name: Cow<'a, ParameterNameRef>,
125
126    remote: &'a VoicemeeterRemote,
127}
128
129impl<'a, const WRITE: bool, const READ: bool> StringParameter<'a, WRITE, READ> {
130    fn new(name: Cow<'a, ParameterNameRef>, remote: &'a VoicemeeterRemote) -> Self {
131        Self { name, remote }
132    }
133}
134
135impl<'a, const READ: bool> StringParameter<'a, true, READ> {
136    /// Set the value of this parameter
137    pub fn set(&self, val: &str) -> Result<(), SetParameterError> {
138        self.remote.set_parameter_string(&self.name, val)
139    }
140}
141
142impl<'a, const WRITE: bool> StringParameter<'a, WRITE, true> {
143    /// Get the value of this parameter
144    pub fn get(&self) -> Result<String, GetParameterError> {
145        self.remote.get_parameter_string(&self.name)
146    }
147}
148
149/// A tuple parameter
150#[must_use = "set or get the value of the parameter"]
151pub struct TupleParameter<'a, A, B, const WRITE: bool = true, const READ: bool = true> {
152    /// The name of the parameter, fully qualified
153    pub name: Cow<'a, ParameterNameRef>,
154
155    remote: &'a VoicemeeterRemote,
156    _pd: std::marker::PhantomData<(A, B)>,
157}
158
159impl<'a, A, B, const WRITE: bool, const READ: bool> TupleParameter<'a, A, B, WRITE, READ> {
160    fn new(name: Cow<'a, ParameterNameRef>, remote: &'a VoicemeeterRemote) -> Self {
161        Self {
162            name,
163            remote,
164            _pd: Default::default(),
165        }
166    }
167}
168
169impl<'a, A, B, const READ: bool> TupleParameter<'a, A, B, true, READ>
170where
171    A: Debug,
172    B: Debug,
173{
174    /// Set the value of this parameter
175    pub fn set(&self, val_a: A, val_b: B) -> Result<(), SetParameterError> {
176        self.remote
177            .set_parameter_string(&self.name, &format!("({val_a:?}, {val_b:?})"))
178    }
179}
180
181/// A boolean parameter
182#[must_use = "set or get the value of the parameter"]
183pub struct BoolParameter<'a, const WRITE: bool = true, const READ: bool = true> {
184    /// The name of the parameter, fully qualified
185    pub name: Cow<'a, ParameterNameRef>,
186
187    remote: &'a VoicemeeterRemote,
188}
189
190impl<'a, const WRITE: bool, const READ: bool> BoolParameter<'a, WRITE, READ> {
191    fn new(name: Cow<'a, ParameterNameRef>, remote: &'a VoicemeeterRemote) -> Self {
192        Self { name, remote }
193    }
194}
195
196impl<'a, const READ: bool> BoolParameter<'a, true, READ> {
197    /// Set the value of this parameter
198    pub fn set(&self, val: bool) -> Result<(), SetParameterError> {
199        self.remote
200            .set_parameter_float(&self.name, if val { 1.0 } else { 0.0 })
201    }
202}
203
204impl<'a, const WRITE: bool> BoolParameter<'a, WRITE, true> {
205    /// Get the value of this parameter
206    pub fn get(&self) -> Result<bool, GetParameterError> {
207        Ok(self.remote.get_parameter_float(&self.name)? == 1.0)
208    }
209}
210
211/// A integer parameter
212#[must_use = "set or get the value of the parameter"]
213pub struct IntParameter<'a, const WRITE: bool = true, const READ: bool = true> {
214    /// The name of the parameter, fully qualified
215    pub name: Cow<'a, ParameterNameRef>,
216
217    remote: &'a VoicemeeterRemote,
218    _range: Option<(Bound<i32>, Bound<i32>)>,
219}
220
221impl<'a, const WRITE: bool, const READ: bool> IntParameter<'a, WRITE, READ> {
222    fn new<R>(name: Cow<'a, ParameterNameRef>, remote: &'a VoicemeeterRemote, range: R) -> Self
223    where
224        R: RangeBounds<i32>,
225    {
226        Self {
227            name,
228            remote,
229            _range: Some((range.start_bound().cloned(), range.end_bound().cloned())),
230        }
231    }
232    fn new_unranged(name: Cow<'a, ParameterNameRef>, remote: &'a VoicemeeterRemote) -> Self {
233        Self {
234            name,
235            remote,
236            _range: None,
237        }
238    }
239}
240
241impl<'a, const READ: bool> IntParameter<'a, true, READ> {
242    /// Set the value of this parameter
243    pub fn set(&self, val: i32) -> Result<(), SetParameterError> {
244        self.remote.set_parameter_float(&self.name, val as f32)
245    }
246}
247
248impl<'a, const WRITE: bool> IntParameter<'a, WRITE, true> {
249    /// Get the value of this parameter
250    pub fn get(&self) -> Result<i32, GetParameterError> {
251        Ok(self.remote.get_parameter_float(&self.name)? as i32)
252    }
253}
254
255/// Strip index helper
256pub trait StripIndex {
257    /// Get the strip index
258    fn into_strip_index(self, program: &VoicemeeterApplication) -> Result<ZIndex, ParameterError>;
259}
260
261impl StripIndex for i32 {
262    fn into_strip_index(self, _: &VoicemeeterApplication) -> Result<ZIndex, ParameterError> {
263        Ok(ZIndex(self))
264    }
265}
266
267impl StripIndex for usize {
268    fn into_strip_index(self, _: &VoicemeeterApplication) -> Result<ZIndex, ParameterError> {
269        Ok(ZIndex(self as _))
270    }
271}
272
273impl StripIndex for ZIndex {
274    fn into_strip_index(self, _: &VoicemeeterApplication) -> Result<ZIndex, ParameterError> {
275        Ok(self)
276    }
277}
278
279impl StripIndex for Device {
280    fn into_strip_index(self, program: &VoicemeeterApplication) -> Result<ZIndex, ParameterError> {
281        if !self.is_strip() {
282            return Err(InvalidTypeError::ExpectedStrip {
283                device: format!("{self:?}"),
284            }
285            .into());
286        }
287        self.as_strip_index(program).ok_or_else(|| {
288            DeviceError {
289                program: *program,
290                device: self,
291            }
292            .into()
293        })
294    }
295}
296
297/// Bus index helper
298pub trait BusIndex {
299    /// Get the bus index
300    fn into_bus_index(self, program: &VoicemeeterApplication) -> Result<ZIndex, ParameterError>;
301}
302
303impl BusIndex for i32 {
304    fn into_bus_index(self, _: &VoicemeeterApplication) -> Result<ZIndex, ParameterError> {
305        Ok(ZIndex(self))
306    }
307}
308
309impl BusIndex for usize {
310    fn into_bus_index(self, _: &VoicemeeterApplication) -> Result<ZIndex, ParameterError> {
311        Ok(ZIndex(self as _))
312    }
313}
314
315impl BusIndex for ZIndex {
316    fn into_bus_index(self, _: &VoicemeeterApplication) -> Result<ZIndex, ParameterError> {
317        Ok(self)
318    }
319}
320
321impl BusIndex for Device {
322    fn into_bus_index(self, program: &VoicemeeterApplication) -> Result<ZIndex, ParameterError> {
323        if !self.is_bus() {
324            return Err(InvalidTypeError::ExpectedBus {
325                device: format!("{self:?}"),
326            }
327            .into());
328        }
329        self.as_bus_index(program)
330            .ok_or_else(|| {
331                DeviceError {
332                    program: *program,
333                    device: self,
334                }
335                .into()
336            })
337            .map(|i| i.0)
338    }
339}
340
341/// Parameter abstraction
342///
343/// # Examples
344///
345/// ```rust,no_run
346/// # use voicemeeter::VoicemeeterRemote;
347/// # let remote: VoicemeeterRemote = todo!();
348/// println!(
349///     "Strip 1: {}",
350///     remote.parameters().strip(0)?.device()?.name().get()?
351/// );
352/// # Ok::<(), Box<dyn std::error::Error>>(())
353/// ```
354pub struct Parameters<'a> {
355    remote: &'a VoicemeeterRemote,
356}
357
358// TODO: `patch`
359impl<'a> Parameters<'a> {
360    /// Parameters of a [strip](Strip).
361    ///
362    /// A strip is a input that can be physical or virtual
363    ///
364    ///
365    /// # Availability
366    ///
367    /// On each Voicemeeter application, there are different amounts of strips
368    ///
369    /// Application | Strips | Physical | Virtual
370    /// :--- | :--- | :--- | :---
371    /// Voicemeeter | total: `3` | total: `2` _(starting on strip #0)_ | total: `1` _(starting on strip #2)_
372    /// Voicemeeter Banana | total: `5` | total: `3` _(starting on strip #0)_ | total: `2` _(starting on strip #3)_
373    /// Voicemeeter Potato | total: `8` | total: `5` _(starting on strip #0)_ | total: `3` _(starting on strip #5)_
374    pub fn strip(&self, index: impl StripIndex) -> Result<Strip<'a>, ParameterError> {
375        let index = index.into_strip_index(&self.remote.program)?;
376        Ok(match (self.remote.program, index.0) {
377            (VoicemeeterApplication::Voicemeeter, 0..=2) => Strip::new(self.remote, index),
378            (VoicemeeterApplication::VoicemeeterBanana, 0..=4) => Strip::new(self.remote, index),
379            (VoicemeeterApplication::VoicemeeterPotato, 0..=7)
380            | (VoicemeeterApplication::PotatoX64Bits, 0..=7) => Strip::new(self.remote, index),
381            _ => {
382                return Err(Into::into(OutOfRangeError {
383                    name: STRIP.to_owned(),
384                    index,
385                    program: self.remote.program,
386                }));
387            }
388        })
389    }
390
391    /// Parameters of a [bus](Bus).
392    ///
393    /// A bus is a output. In the interface, these are called `A1`, `A2`, `A3`, `B1`, etc.
394    ///
395    /// # Availability
396    ///
397    /// On each Voicemeeter application, there are different amounts of busses
398    ///
399    /// Application | Busses | Physical | Virtual
400    /// :--- | :--- | :--- | :---
401    /// Voicemeeter | total: `2` | total: `2` _(starting on bus #0)_ | total: `0`
402    /// Voicemeeter Banana | total: `5` | total: `3` _(starting on bus #0)_ | total: `2` _(starting on bus #3)_
403    /// Voicemeeter Potato | total: `8` | total: `5` _(starting on bus #0)_ | total: `3` _(starting on bus #5)_
404    pub fn bus(&self, index: impl BusIndex) -> Result<Bus<'a>, ParameterError> {
405        let index = index.into_bus_index(&self.remote.program)?;
406        Ok(match (self.remote.program, index.0) {
407            (VoicemeeterApplication::Voicemeeter, 0..=1) => Bus::new(self.remote, index),
408            (VoicemeeterApplication::VoicemeeterBanana, 0..=4) => Bus::new(self.remote, index),
409            (VoicemeeterApplication::VoicemeeterPotato, 0..=7)
410            | (VoicemeeterApplication::PotatoX64Bits, 0..=7) => Bus::new(self.remote, index),
411            _ => {
412                return Err(Into::into(OutOfRangeError {
413                    name: BUS.to_owned(),
414                    index,
415                    program: self.remote.program,
416                }));
417            }
418        })
419    }
420
421    /// Options for Voicemeeter
422    pub fn option(&self) -> VoicemeeterOption<'a> {
423        VoicemeeterOption::new(self.remote)
424    }
425
426    /// Voicemeeter recorder with playback
427    pub fn recorder(&self) -> Result<VoicemeeterRecorder<'a>, ParameterError> {
428        const VALID: &[VoicemeeterApplication] = &[
429            VoicemeeterApplication::VoicemeeterBanana,
430            VoicemeeterApplication::VoicemeeterPotato,
431            VoicemeeterApplication::PotatoX64Bits,
432        ];
433        if !VALID.contains(&self.remote.program) {
434            Err(ParameterError::Version(InvalidVoicemeeterVersion {
435                expected: VALID,
436                found: self.remote.program,
437                parameter: RECORDER.to_owned(),
438            }))
439        } else {
440            Ok(VoicemeeterRecorder::new(self.remote))
441        }
442    }
443
444    /// Voicemeeter FX
445    pub fn fx(&self) -> Result<VoicemeeterFx<'a>, ParameterError> {
446        const VALID: &[VoicemeeterApplication] = &[
447            VoicemeeterApplication::VoicemeeterPotato,
448            VoicemeeterApplication::PotatoX64Bits,
449        ];
450        if !VALID.contains(&self.remote.program) {
451            Err(ParameterError::Version(InvalidVoicemeeterVersion {
452                expected: VALID,
453                found: self.remote.program,
454                parameter: FX.to_owned(),
455            }))
456        } else {
457            Ok(VoicemeeterFx::new(self.remote))
458        }
459    }
460
461    /// Voicemeeter VBAN
462    pub fn vban(&self) -> VoicemeeterVban<'a> {
463        VoicemeeterVban::new(self.remote)
464    }
465}