baton_studio/lib.rs
1#![warn(missing_docs)]
2//! Talk to your Presonus STUDIO1824c
3//!
4//! This library allows you to control most of the functions
5//! of the 1824c audio interface.
6//!
7//! The Presonus STUDIO1824c is a USB audio interface that features
8//! - 18 input channels:
9//! - 8 analog input channels with mic/line/instr preamps
10//! - 2 didgital S/PDIF input channels
11//! - 8 digital ADAT input channels
12//! - 18 DAW channels i.e. channels that appear as output channels from the point of view of the computer
13//! - 24 output channels:
14//! - 8 analog line output channels
15//! - 2 stereo headphone outputs (total 4 channels)
16//! - 2 Main output channels
17//! - 2 digital S/PDIF output channels
18//! - 8 digital ADAT output channels
19//! - Button for 48V phantom power for the 8 mic input channels
20//! - Button for Main output mute
21//! - Button for Main output mono
22//! - Button to switch between instrument- and line-level on the 1/4 inch inputs on channel 1 and 2
23//!
24//! ## Mixer
25//! The internals of the 1824c contains 9 separate mixes.
26//! One mix contains stereo fader controls for all 18 input channels plus all 18 DAW channels for a total of 36 channels.
27//! The output for mix number one is output channels 1 and 2, and at the same time Main output channels 1 and 2, and stereo headphone output number 1.
28//! The output for mix number two is output channels 3 and 4, and also stereo headphone output number 2.
29//! The output for mix number three is output channels 5 and 6.
30//! Mix four is output channels 7 and 8.
31//! Mix five is the 2 digital S/PDIF output channels.
32//! Mix six through nine is the ADAT digital output channels (six -> ADAT 1,2, seven -> ADAT 3,4, etc.)
33//!
34//! Think of it as a mixer with 36 input channels and 9 buses.
35//!
36//! ## Signal flow
37//! A signal flow from one input channel to all nine buses looks something like this:
38//! ```text
39//! +-->Fader left--->\ /-->Line Output 1
40//! Input 1-->| |Stereo fader 1|
41//! +-->Fader right-->/ \-->Line Output 2
42//! |
43//! ...
44//! |
45//! +-->Fader left--->\ /-->Line Output 7
46//! | |Stereo fader 4|
47//! +-->Fader right-->/ \-->Line Output 8
48//! |
49//! |
50//! +-->Fader left--->\ /-->S/PDIF Output left
51//! | |Stereo fader 5|
52//! +-->Fader right-->/ \-->S/PDIF Output right
53//! |
54//! |
55//! +-->Fader left--->\ /-->ADAT Output 1
56//! | |Stereo fader 6|
57//! +-->Fader right-->/ \-->ADAT Output 2
58//! |
59//! ...
60//! |
61//! +-->Fader left--->\ /-->ADAT Output 7
62//! | |Stereo fader 9|
63//! +-->Fader right-->/ \-->ADAT Output 8
64//! ```
65//! The signal flow is identical for all of the 36 input channels.
66//! With this library you can control all of the Fader left and Fader right faders, and all of the Stereo faders.
67//! In total you can control 36*2=72 left/right Faders and 9 Stereo faders.
68//!
69//! The Fader left and Fader right, or input faders are controlled with [`set_input_fader()`](Command::set_input_fader).
70//! The Stereo faders, or output faders are controlled with [`set_output_fader()`](Command::set_output_fader).
71//!
72//! ## Buttons
73//! The four buttons on the front panel of the audio interface can be controlled with [`set_button()`](Command::set_button).
74//!
75use nusb::{
76 Device, MaybeFuture,
77 transfer::{ControlIn, ControlOut, ControlType, Recipient, TransferError},
78};
79use std::time::Duration;
80
81// Fader presets
82/// A fader gain value that corresponds to muted.
83const MUTED: u32 = 0x00;
84/// A fader gain value that corresponds to unity gain.
85const UNITY: u32 = 0x0100_0000;
86
87/// Push buttons on the front panel of the Studio 1824c.
88#[derive(Clone, Copy)]
89pub enum Button {
90 /// Switches between instrument- and line-level on
91 /// the 1/4-inch inputs for channel 1 and 2.
92 Line = 0x00,
93 /// Mutes the Main output signal.
94 Mute = 0x01,
95 /// Sums the Main stereo output signal to mono.
96 Mono = 0x02,
97 /// 48V phantom power for all microphone inputs.
98 Phantom = 0x04,
99}
100
101/// Value for faders
102pub enum Value {
103 /// Decibel
104 DB(f64),
105 /// Raw gain value
106 Gain(u32),
107 /// Unity gain
108 Unity,
109 /// Zero gain or muted
110 Muted,
111}
112
113impl Value {
114 /// Convert Value to gain u32
115 fn to_gain(&self) -> u32 {
116 match self {
117 Value::DB(db) => db_to_gain(*db),
118 Value::Gain(g) => *g,
119 Value::Unity => UNITY,
120 Value::Muted => MUTED,
121 }
122 }
123}
124
125/// Output channels
126#[derive(Clone, Copy)]
127pub enum Channel {
128 /// Left channel
129 Left = 0x00,
130 /// Right channel
131 Right = 0x01,
132}
133
134#[derive(Clone, Copy)]
135enum Mode {
136 Button = 0x00,
137 ChannelStrip = 0x64,
138 BusStrip = 0x65,
139}
140
141/// Commands to send to the audio device.
142///
143/// Command is used to prepare and send commands to the
144/// Presonus STUDIO1824c. A Command can be prepared to
145/// set the buttons on the front panel, or to set the
146/// faders.
147///
148/// # Examples
149/// ```
150/// # use std::error::Error;
151/// use baton_studio::{Button, Channel, Command, Value};
152/// use nusb::MaybeFuture;
153///
154/// # fn main() -> Result<(), Box<dyn Error>> {
155/// // Open the 1824c usb device.
156/// let my_1824c = nusb::list_devices()
157/// .wait()?
158/// .find(|dev| dev.vendor_id() == 0x194f && dev.product_id() == 0x010d)
159/// .ok_or(std::io::Error::new(std::io::ErrorKind::NotFound, "device not found"))?
160/// .open()
161/// .wait()?;
162///
163/// let mut command = Command::new();
164///
165/// // Prepare command to set the Mute button to true.
166/// command.set_button(Button::Mute, true);
167/// // Send the command.
168/// command.send(&my_1824c)?;
169///
170/// // Prepare and send command to set fader.
171/// command
172/// .set_input_fader(0, 0, Channel::Left, Value::Unity)
173/// .send(&my_1824c)?;
174///
175/// # Ok(())
176/// # }
177/// ```
178pub struct Command {
179 mode: Mode,
180 input_strip: u32,
181 output_bus: u32,
182 output_channel: Channel,
183 button: Button,
184 value: u32,
185}
186
187impl Default for Command {
188 fn default() -> Self {
189 Self::new()
190 }
191}
192
193impl Command {
194 /// Initialize the Command struct.
195 pub fn new() -> Self {
196 Command {
197 mode: Mode::ChannelStrip,
198 input_strip: 0x00,
199 output_bus: 0x00,
200 output_channel: Channel::Left,
201 button: Button::Line,
202 value: 0x00000000,
203 }
204 }
205
206 fn as_array(&self) -> [u8; 28] {
207 const FIX1: u32 = 0x50617269;
208 const FIX2: u32 = 0x14;
209
210 let mut arr = [0u8; 28];
211 let mut i = 0;
212
213 for b in (self.mode as u32).to_le_bytes() {
214 arr[i] = b;
215 i += 1;
216 }
217 let value = match self.mode {
218 Mode::Button => 0x00,
219 Mode::ChannelStrip => self.input_strip,
220 Mode::BusStrip => self.output_bus,
221 };
222 for b in value.to_le_bytes() {
223 arr[i] = b;
224 i += 1;
225 }
226 for b in FIX1.to_le_bytes() {
227 arr[i] = b;
228 i += 1;
229 }
230 for b in FIX2.to_le_bytes() {
231 arr[i] = b;
232 i += 1;
233 }
234 for b in self.output_bus.to_le_bytes() {
235 arr[i] = b;
236 i += 1;
237 }
238 let value = match self.mode {
239 Mode::Button => self.button as u32,
240 Mode::ChannelStrip => self.output_channel as u32,
241 Mode::BusStrip => Channel::Left as u32,
242 };
243 for b in value.to_le_bytes() {
244 arr[i] = b;
245 i += 1;
246 }
247 for b in self.value.to_le_bytes() {
248 arr[i] = b;
249 i += 1;
250 }
251
252 arr
253 }
254
255 /// Prepare a Button command.
256 ///
257 /// Used to turn on or off one of the four buttons
258 /// on the front panel of the 1824c.
259 ///
260 /// # Examples
261 /// ```
262 /// # use std::error::Error;
263 /// # use baton_studio::*;
264 /// # use nusb::MaybeFuture;
265 /// # fn main() -> Result<(), Box<dyn Error>> {
266 /// # let my_1824c = nusb::list_devices()
267 /// # .wait()?.find(|dev| dev.vendor_id() == 0x194f && dev.product_id() == 0x010d)
268 /// # .ok_or(std::io::Error::new(std::io::ErrorKind::NotFound, "device not found"))?
269 /// # .open().wait()?;
270 /// # let mut command = Command::new();
271 /// // Prepare and send command to turn off phantom power.
272 /// command.set_button(Button::Phantom, false).send(&my_1824c)?;
273 /// # Ok(())
274 /// # }
275 /// ```
276 pub fn set_button(&mut self, button: Button, value: bool) -> &mut Self {
277 self.input_strip = 0x00;
278 self.output_bus = 0x00;
279 self.mode = Mode::Button;
280 self.button = button;
281 self.value = match value {
282 true => 1,
283 false => 0,
284 };
285 self
286 }
287
288 /// Prepare a command to set an input fader.
289 ///
290 /// The input faders are the faders of the 36
291 ///
292 /// # Arguments
293 /// - `input` The input channel is a number between 0 and 35.
294 /// - `output` The ouput stere bus is a number between 0 and 8.
295 /// - `channel` The channel of the output stere bus.
296 /// [`Left`](Channel::Left) or [`Right`](Channel::Right).
297 /// - `value` The fader value. See [`Value`]enum for options.
298 ///
299 /// # Examples
300 /// ```
301 /// # use std::error::Error;
302 /// # use baton_studio::*;
303 /// # use nusb::MaybeFuture;
304 /// # fn main() -> Result<(), Box<dyn Error>> {
305 /// # let my_1824c = nusb::list_devices()
306 /// # .wait()?.find(|dev| dev.vendor_id() == 0x194f && dev.product_id() == 0x010d)
307 /// # .ok_or(std::io::Error::new(std::io::ErrorKind::NotFound, "device not found"))?
308 /// # .open().wait()?;
309 /// # let mut command = Command::new();
310 /// // Prepare and send command to set the fader of the first
311 /// // input channel of the left channel of the first stereo
312 /// // mix to unity gain.
313 /// command.set_input_fader(0, 0, Channel::Left, Value::Unity).send(&my_1824c)?;
314 ///
315 /// // Set value to zero i.e. muted.
316 /// command.set_input_fader(0, 0, Channel::Left, Value::Muted).send(&my_1824c)?;
317 ///
318 /// // Use the helper function db_to_gain()
319 /// command.set_input_fader(0, 0, Channel::Left, Value::DB(-6.0)).send(&my_1824c)?;
320 /// # Ok(())
321 /// # }
322 /// ```
323 pub fn set_input_fader(
324 &mut self,
325 input: u32,
326 output: u32,
327 channel: Channel,
328 value: Value,
329 ) -> &mut Self {
330 self.mode = Mode::ChannelStrip;
331 self.input_strip = input.clamp(0, 35);
332 self.output_bus = output.clamp(0, 8);
333 self.output_channel = channel;
334 self.value = value.to_gain();
335 self
336 }
337
338 /// Prepare a command to set an output fader.
339 pub fn set_output_fader(&mut self, output: u32, value: Value) -> &mut Self {
340 self.mode = Mode::BusStrip;
341 self.output_bus = output.clamp(0, 8);
342 self.value = value.to_gain();
343 self
344 }
345
346 /// Send the prepared command to the device.
347 pub fn send(&self, device: &Device) -> Result<(), TransferError> {
348 let fader_control: ControlOut = ControlOut {
349 control_type: ControlType::Vendor,
350 recipient: Recipient::Device,
351 request: 160,
352 value: 0x0000,
353 index: 0,
354 data: &self.as_array(),
355 };
356
357 device
358 .control_out(fader_control, Duration::from_millis(100))
359 .wait()
360 }
361}
362
363/// State of the Presonus STUDIO1824c audio interface.
364pub struct State {
365 counter: u16,
366 /// Microphone input meters.
367 pub mic: [u32; 8],
368 /// S/PDIF input meters.
369 pub spdif: [u32; 2],
370 /// ADAT input meters.
371 pub adat: [u32; 8],
372 /// DAW input meters.
373 pub daw: [u32; 18],
374 /// Stereo busses meters.
375 pub bus: [u32; 18],
376 /// 48V phantom power button.
377 pub phantom: u32,
378 /// Channel 1-2 line mode button.
379 pub line: u32,
380 /// Main mix mute button.
381 pub mute: u32,
382 /// Main mix mono button.
383 pub mono: u32,
384}
385
386impl Default for State {
387 fn default() -> Self {
388 Self::new()
389 }
390}
391
392impl State {
393 /// Initialize the State struct.
394 pub fn new() -> Self {
395 State {
396 counter: 0x01,
397 mic: [0x00; 8],
398 spdif: [0x00; 2],
399 adat: [0x00; 8],
400 daw: [0x00; 18],
401 bus: [0x00; 18],
402 phantom: 0x00,
403 line: 0x00,
404 mute: 0x00,
405 mono: 0x00,
406 }
407 }
408
409 // Reset all values to zero.
410 // This is used before requesting state from device.
411 fn reset(&mut self) {
412 self.mic = [0x00; 8];
413 self.spdif = [0x00; 2];
414 self.adat = [0x00; 8];
415 self.daw = [0x00; 18];
416 self.bus = [0x00; 18];
417 self.phantom = 0x00;
418 self.line = 0x00;
419 self.mute = 0x00;
420 self.mono = 0x00;
421 }
422
423 /// Return the state as an array of bytes.
424 fn as_array(&self) -> [u8; 252] {
425 const FIX1: u32 = 0x64656d73;
426 const FIX2: u32 = 0xf4;
427 const ZERO: u32 = 0x00;
428
429 let mut arr = [0u8; 252];
430 let mut i = 0;
431
432 for b in ZERO.to_le_bytes() {
433 arr[i] = b;
434 i += 1;
435 }
436 for b in ZERO.to_le_bytes() {
437 arr[i] = b;
438 i += 1;
439 }
440 for b in FIX1.to_le_bytes() {
441 arr[i] = b;
442 i += 1;
443 }
444 for b in FIX2.to_le_bytes() {
445 arr[i] = b;
446 i += 1;
447 }
448 for m in self.mic {
449 for b in m.to_le_bytes() {
450 arr[i] = b;
451 i += 1;
452 }
453 }
454 for m in self.spdif {
455 for b in m.to_le_bytes() {
456 arr[i] = b;
457 i += 1;
458 }
459 }
460 for m in self.adat {
461 for b in m.to_le_bytes() {
462 arr[i] = b;
463 i += 1;
464 }
465 }
466 for m in self.daw {
467 for b in m.to_le_bytes() {
468 arr[i] = b;
469 i += 1;
470 }
471 }
472 for m in self.bus {
473 for b in m.to_le_bytes() {
474 arr[i] = b;
475 i += 1;
476 }
477 }
478 for b in self.phantom.to_le_bytes() {
479 arr[i] = b;
480 i += 1;
481 }
482 for b in self.line.to_le_bytes() {
483 arr[i] = b;
484 i += 1;
485 }
486 for b in self.mute.to_le_bytes() {
487 arr[i] = b;
488 i += 1;
489 }
490 for b in self.mono.to_le_bytes() {
491 arr[i] = b;
492 i += 1;
493 }
494 for b in ZERO.to_le_bytes() {
495 arr[i] = b;
496 i += 1;
497 }
498
499 arr
500 }
501
502 // Convert a slice of 4 bytes to a u32.
503 fn slice_to_u32(slice: &[u8]) -> u32 {
504 let mut out: u32 = slice[0] as u32;
505 out += slice[1] as u32 * 0x100;
506 out += slice[2] as u32 * 0x100 * 0x100;
507 out += slice[3] as u32 * 0x100 * 0x100 * 0x100;
508
509 out
510 }
511
512 fn parse_state(&mut self, slice: Vec<u8>) {
513 const MIC_INDEX: usize = 0x10;
514 const ADAT_INDEX: usize = 0x38;
515 const SPDIF_INDEX: usize = 0x30;
516 const DAW_INDEX: usize = 0x58;
517 const BUS_INDEX: usize = 0xa0;
518
519 for i in 0..self.mic.len() {
520 self.mic[i] = Self::slice_to_u32(&slice[MIC_INDEX + 4 * i..=MIC_INDEX + 4 * i + 4]);
521 }
522 for i in 0..self.adat.len() {
523 self.adat[i] = Self::slice_to_u32(&slice[ADAT_INDEX + 4 * i..=ADAT_INDEX + 4 * i + 4]);
524 }
525 for i in 0..self.spdif.len() {
526 self.spdif[i] =
527 Self::slice_to_u32(&slice[SPDIF_INDEX + 4 * i..=SPDIF_INDEX + 4 * i + 4]);
528 }
529 for i in 0..self.daw.len() {
530 self.daw[i] = Self::slice_to_u32(&slice[DAW_INDEX + 4 * i..=DAW_INDEX + 4 * i + 4]);
531 }
532 for i in 0..self.bus.len() {
533 self.bus[i] = Self::slice_to_u32(&slice[BUS_INDEX + 4 * i..=BUS_INDEX + 4 * i + 4]);
534 }
535
536 self.phantom = slice[0xe8] as u32;
537 self.line = slice[0xec] as u32;
538 self.mute = slice[0xf0] as u32;
539 self.mono = slice[0xf4] as u32;
540 }
541
542 /// Read state from device
543 ///
544 /// # Examples
545 /// ```
546 /// # use std::error::Error;
547 /// use baton_studio::{gain_to_db, State};
548 /// use nusb::MaybeFuture;
549 ///
550 /// # fn main() -> Result<(), Box<dyn Error>> {
551 /// // Open the 1824c usb device.
552 /// let my_1824c = nusb::list_devices()
553 /// .wait()?
554 /// .find(|dev| dev.vendor_id() == 0x194f && dev.product_id() == 0x010d)
555 /// .ok_or(std::io::Error::new(std::io::ErrorKind::NotFound, "device not found"))?
556 /// .open()
557 /// .wait()?;
558 ///
559 /// let mut state = State::new();
560 /// state.poll(&my_1824c)?;
561 ///
562 /// let fader_value = gain_to_db(state.mic[0]);
563 ///
564 /// # Ok(())
565 /// # }
566 /// ```
567 pub fn poll(&mut self, device: &Device) -> Result<(), TransferError> {
568 self.reset();
569
570 let control: ControlOut = ControlOut {
571 control_type: ControlType::Vendor,
572 recipient: Recipient::Device,
573 request: 161,
574 value: self.counter.to_le(),
575 index: 0,
576 data: &self.as_array(),
577 };
578
579 device
580 .control_out(control, Duration::from_millis(100))
581 .wait()?;
582
583 let control: ControlIn = ControlIn {
584 control_type: ControlType::Vendor,
585 recipient: Recipient::Device,
586 request: 162,
587 value: self.counter.to_le(),
588 index: 0,
589 length: self.as_array().len() as u16,
590 };
591
592 if self.counter == 0xffff {
593 self.counter = 0x00;
594 }
595 self.counter += 1;
596
597 self.parse_state(
598 device
599 .control_in(control, Duration::from_millis(100))
600 .wait()?,
601 );
602 Ok(())
603 }
604}
605
606/// Convert from dB to integer gain
607pub fn db_to_gain(db: f64) -> u32 {
608 (UNITY as f64 * 10.0_f64.powf(db.clamp(-120.0, 10.0) / 20.0)) as u32
609}
610
611/// Convert from integer gain to dB
612pub fn gain_to_db(input: u32) -> f64 {
613 const ZERO_DBFS: u32 = 0x8000_0000;
614 20.0 * (input as f64 / ZERO_DBFS as f64).log10()
615}