#![allow(rustdoc::invalid_rust_codeblocks)]
#![doc = include_str!("../README.md")]
use std::{
fmt,
sync::atomic::{AtomicU32, Ordering},
time::Duration,
};
use smol_str::{SmolStr, format_smolstr};
mod controller;
#[cfg(all(feature = "midi", feature = "controller-thread"))]
pub use self::controller::midi::context::SingleMidiControllerContext;
#[cfg(feature = "midi")]
pub use self::controller::midi::{BoxedMidiController, MidiController};
#[cfg(feature = "controller-thread")]
pub use self::controller::thread::ControllerThread;
pub use self::controller::{
BoxedControllerTask, Controller, ControllerDescriptor, ControllerTypes,
};
pub mod devices;
mod input;
pub use self::input::{
ButtonInput, CenterSliderInput, ControlInputEvent, ControlInputEventSink, CrossfaderCurve,
InputEvent, PadButtonInput, SelectorInput, SliderEncoderInput, SliderInput, StepEncoderInput,
input_events_ordered_chronologically, split_crossfader_input_amplitude_preserving_approx,
split_crossfader_input_energy_preserving_approx, split_crossfader_input_linear,
split_crossfader_input_square,
};
mod output;
#[cfg(feature = "blinking-led-task")]
pub use self::output::blinking_led_task;
#[cfg(feature = "blinking-led-task-tokio-rt")]
pub use self::output::spawn_blinking_led_task;
pub use self::output::{
BlinkingLedOutput, BlinkingLedTicker, ControlOutputGateway, DEFAULT_BLINKING_LED_PERIOD,
DimLedOutput, LedOutput, LedState, OutputError, OutputResult, RgbLedOutput, SendOutputsError,
VirtualLed,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AudioInterfaceDescriptor {
pub num_input_channels: u8,
pub num_output_channels: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeviceDescriptor {
pub vendor_name: SmolStr,
pub product_name: SmolStr,
pub audio_interface: Option<AudioInterfaceDescriptor>,
}
impl DeviceDescriptor {
#[must_use]
pub fn name(&self) -> SmolStr {
let Self {
vendor_name,
product_name,
..
} = self;
debug_assert!(!product_name.is_empty());
if vendor_name.is_empty() {
product_name.clone()
} else {
format_smolstr!("{vendor_name} {product_name}")
}
}
}
#[derive(
Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Display,
)]
#[repr(transparent)]
pub struct PortIndex {
value: u32,
}
impl PortIndex {
pub const INVALID: Self = Self::new(0);
pub const MIN: Self = Self::new(1);
pub const MAX: Self = Self::new(u32::MAX);
#[must_use]
pub const fn new(value: u32) -> Self {
Self { value }
}
#[must_use]
pub const fn value(self) -> u32 {
let Self { value } = self;
value
}
#[must_use]
pub const fn as_usize(self) -> usize {
self.value() as usize
}
#[must_use]
pub fn is_valid(self) -> bool {
self != Self::INVALID
}
#[must_use]
pub fn next(self) -> Self {
let Self { mut value } = self;
let next_value = loop {
let next_value = value.wrapping_add(1);
if next_value != Self::INVALID.value() {
break next_value;
}
value = next_value;
};
let next = Self { value: next_value };
debug_assert!(next.is_valid());
next
}
}
#[derive(Debug)]
pub struct PortIndexGenerator(AtomicU32);
impl PortIndexGenerator {
#[must_use]
pub const fn new() -> Self {
Self(AtomicU32::new(PortIndex::INVALID.value()))
}
#[must_use]
pub fn next(&self) -> PortIndex {
loop {
let prev_value = self.0.fetch_add(1, Ordering::Relaxed);
let next_value = prev_value.wrapping_add(1);
if next_value != PortIndex::INVALID.value() {
return PortIndex::new(next_value);
}
}
}
}
impl Default for PortIndexGenerator {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Display)]
#[repr(transparent)]
pub struct ControlIndex {
value: u32,
}
impl ControlIndex {
#[must_use]
pub const fn new(value: u32) -> Self {
Self { value }
}
#[must_use]
pub const fn value(self) -> u32 {
let Self { value } = self;
value
}
#[must_use]
pub const fn as_usize(self) -> usize {
self.value() as usize
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display)]
#[repr(transparent)]
pub struct ControlValue {
bits: u32,
}
impl ControlValue {
#[must_use]
pub const fn from_bits(bits: u32) -> Self {
Self { bits }
}
#[must_use]
pub const fn to_bits(self) -> u32 {
let Self { bits } = self;
bits
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Control {
pub index: ControlIndex,
pub value: ControlValue,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TimeStamp(u64);
impl TimeStamp {
#[must_use]
pub const fn from_micros(micros: u64) -> Self {
Self(micros)
}
#[must_use]
pub const fn to_micros(self) -> u64 {
let Self(micros) = self;
micros
}
#[must_use]
pub const fn to_duration(self) -> Duration {
Duration::from_micros(self.to_micros())
}
}
impl fmt::Display for TimeStamp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!(
"{micros} \u{00B5}s",
micros = self.to_micros()
))
}
}
#[must_use]
pub fn u7_be_to_u14(hi: u8, lo: u8) -> u16 {
debug_assert_eq!(hi, hi & 0x7f);
debug_assert_eq!(lo, lo & 0x7f);
(u16::from(hi) << 7) | u16::from(lo)
}
#[cfg(all(feature = "hid", not(target_family = "wasm")))]
pub mod hid;
#[cfg(all(feature = "hid", not(target_family = "wasm")))]
pub use self::hid::{
HidApi, HidDevice, HidDeviceError, HidError, HidResult, HidThread, HidUsagePage,
};
#[cfg(feature = "midi")]
mod midi;
#[cfg(feature = "midir")]
pub use self::midi::midir::{
MidiPortError, MidirDevice, MidirDeviceManager, MidirInputPort, MidirOutputPort,
};
#[cfg(feature = "midi")]
pub use self::midi::{
BoxedMidiOutputConnection, MidiControlOutputGateway, MidiDeviceDescriptor, MidiInputConnector,
MidiInputDecodeError, MidiInputEventDecoder, MidiInputGateway, MidiInputHandler,
MidiOutputConnection, MidiOutputGateway, MidiPortDescriptor, NewMidiInputGateway,
consume_midi_input_event,
};
pub mod deck;
#[cfg(feature = "observables")]
pub use self::deck::Observables as DeckObservables;
pub use self::deck::{Adapter as DeckAdapter, Input as DeckInput};
#[cfg(feature = "experimental-param")]
pub mod param;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_port_index() {
assert_eq!(PortIndex::INVALID, PortIndex::default());
}
#[test]
fn next_port_index() {
assert_eq!(PortIndex::MIN, PortIndex::INVALID.next());
assert!(PortIndex::MIN < PortIndex::MIN.next());
assert_eq!(PortIndex::MIN, PortIndex::MAX.next());
}
}