1use 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 pub fn parameters(&self) -> Parameters<'_> {
72 Parameters { remote: self }
73 }
74}
75
76#[must_use = "set or get the value of the parameter"]
78pub struct FloatParameter<'a, const WRITE: bool = true, const READ: bool = true> {
79 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 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 pub fn get(&self) -> Result<f32, GetParameterError> {
116 self.remote.get_parameter_float(&self.name)
117 }
118}
119
120#[must_use = "set or get the value of the parameter"]
122pub struct StringParameter<'a, const WRITE: bool = true, const READ: bool = true> {
123 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 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 pub fn get(&self) -> Result<String, GetParameterError> {
145 self.remote.get_parameter_string(&self.name)
146 }
147}
148
149#[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 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 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#[must_use = "set or get the value of the parameter"]
183pub struct BoolParameter<'a, const WRITE: bool = true, const READ: bool = true> {
184 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 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 pub fn get(&self) -> Result<bool, GetParameterError> {
207 Ok(self.remote.get_parameter_float(&self.name)? == 1.0)
208 }
209}
210
211#[must_use = "set or get the value of the parameter"]
213pub struct IntParameter<'a, const WRITE: bool = true, const READ: bool = true> {
214 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 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 pub fn get(&self) -> Result<i32, GetParameterError> {
251 Ok(self.remote.get_parameter_float(&self.name)? as i32)
252 }
253}
254
255pub trait StripIndex {
257 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
297pub trait BusIndex {
299 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
341pub struct Parameters<'a> {
355 remote: &'a VoicemeeterRemote,
356}
357
358impl<'a> Parameters<'a> {
360 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 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 pub fn option(&self) -> VoicemeeterOption<'a> {
423 VoicemeeterOption::new(self.remote)
424 }
425
426 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 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 pub fn vban(&self) -> VoicemeeterVban<'a> {
463 VoicemeeterVban::new(self.remote)
464 }
465}