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}