use std::fmt::{self, Debug, Display, Formatter};
use crate::bits::{bits_for, Packed};
use crate::error::ParseError;
use crate::fields::{ControlKind, Unit};
use crate::types::RangedI8;
pub type OctaveShift<const OFFSET: u8, const MIN: i8, const MAX: i8> = RangedI8<OFFSET, MIN, MAX>;
pub type Transpose<const OFFSET: u8, const MIN: i8, const MAX: i8> = RangedI8<OFFSET, MIN, MAX>;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LevelOf<const FULL: u8> {
inner: u8,
}
impl<const FULL: u8> LevelOf<FULL> {
const VALID: () = assert!(FULL > 0, "a level needs a nonzero full-scale value");
pub const MAX: u8 = {
let () = Self::VALID;
FULL
};
pub fn new(value: u8) -> Result<Self, ParseError> {
value.try_into()
}
pub fn as_u8(&self) -> u8 {
let () = Self::VALID;
self.inner
}
pub fn as_panel(&self) -> f32 {
let () = Self::VALID;
f32::from(self.inner) / f32::from(FULL) * 10.0
}
}
impl<const FULL: u8> Default for LevelOf<FULL> {
fn default() -> Self {
let () = Self::VALID;
Self { inner: 0 }
}
}
impl<const FULL: u8> TryFrom<u8> for LevelOf<FULL> {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, ParseError> {
let () = Self::VALID;
if value > FULL {
return Err(ParseError::OutOfBounds {
value: format!("{value}"),
bound: format!("0..={FULL}"),
});
}
Ok(LevelOf { inner: value })
}
}
impl<const FULL: u8> Packed for LevelOf<FULL> {
const MAX_BITS: u32 = {
let () = Self::VALID;
bits_for(FULL as u64)
};
const DECODE_BITS: u32 = u8::BITS;
const CONTROL: ControlKind = ControlKind::Knob(Unit::Panel10);
type Error = ParseError;
fn from_bits(bits: u64) -> Result<Self, ParseError> {
(bits as u8).try_into()
}
fn to_bits(&self) -> u64 {
self.inner as u64
}
}
impl<const FULL: u8> Display for LevelOf<FULL> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{} ({:.1})", self.inner, self.as_panel())
}
}
impl<const FULL: u8> Debug for LevelOf<FULL> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl<const FULL: u8> PartialEq<u8> for LevelOf<FULL> {
fn eq(&self, other: &u8) -> bool {
self.inner == *other
}
}
pub type Level = LevelOf<127>;
pub type Level6 = LevelOf<63>;
macro_rules! knob {
($(#[$meta:meta])* $name:ident, $unit:expr) => {
knob!($(#[$meta])* $name, 127, 7, ControlKind::Knob($unit));
};
($(#[$meta:meta])* $name:ident, $max:expr, $bits:expr, $control:expr) => {
$(#[$meta])*
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct $name {
inner: u8,
}
impl $name {
pub const MAX: u8 = $max;
pub fn new(value: u8) -> Result<Self, ParseError> {
value.try_into()
}
pub fn as_u8(&self) -> u8 {
self.inner
}
}
impl TryFrom<u8> for $name {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, ParseError> {
if value > Self::MAX {
return Err(ParseError::OutOfBounds {
value: format!("{value}"),
bound: format!("0..={}", Self::MAX),
});
}
Ok($name { inner: value })
}
}
impl Packed for $name {
const MAX_BITS: u32 = $bits;
const DECODE_BITS: u32 = u8::BITS;
const CONTROL: ControlKind = $control;
type Error = ParseError;
fn from_bits(bits: u64) -> Result<Self, ParseError> {
(bits as u8).try_into()
}
fn to_bits(&self) -> u64 {
self.inner as u64
}
}
impl Debug for $name {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl Display for $name {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl PartialEq<u8> for $name {
fn eq(&self, other: &u8) -> bool {
self.inner == *other
}
}
};
}
knob!(
Time,
Unit::Milliseconds
);
knob!(
Frequency,
Unit::Hertz
);
knob!(
Rate,
Unit::Hertz
);
knob!(
Tempo,
Unit::Bpm
);
knob!(
Pan,
63,
6,
ControlKind::Knob(Unit::Pan)
);
knob!(
Interval,
63,
6,
ControlKind::Shift(Unit::Semitones)
);
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Bipolar<const LIMIT: i16> {
inner: u8,
}
impl<const LIMIT: i16> Bipolar<LIMIT> {
pub const MAX: u8 = 127;
pub const CENTER: u8 = 64;
pub fn new(value: u8) -> Result<Self, ParseError> {
value.try_into()
}
pub fn as_u8(&self) -> u8 {
self.inner
}
pub fn reading(&self) -> f32 {
let from_center = f32::from(self.inner) - f32::from(Self::CENTER);
let span = if from_center < 0.0 {
f32::from(Self::CENTER)
} else {
f32::from(Self::MAX - Self::CENTER)
};
from_center / span * f32::from(LIMIT)
}
}
impl<const LIMIT: i16> TryFrom<u8> for Bipolar<LIMIT> {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, ParseError> {
if value > Self::MAX {
return Err(ParseError::OutOfBounds {
value: format!("{value}"),
bound: format!("0..={}", Self::MAX),
});
}
Ok(Bipolar { inner: value })
}
}
impl<const LIMIT: i16> Packed for Bipolar<LIMIT> {
const MAX_BITS: u32 = 7;
const DECODE_BITS: u32 = u8::BITS;
const CONTROL: ControlKind = ControlKind::Bipolar(Unit::Decibels);
type Error = ParseError;
fn from_bits(bits: u64) -> Result<Self, ParseError> {
(bits as u8).try_into()
}
fn to_bits(&self) -> u64 {
self.inner as u64
}
}
impl<const LIMIT: i16> Debug for Bipolar<LIMIT> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl<const LIMIT: i16> Display for Bipolar<LIMIT> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{} ({:+.1})", self.inner, self.reading())
}
}
impl<const LIMIT: i16> PartialEq<u8> for Bipolar<LIMIT> {
fn eq(&self, other: &u8) -> bool {
self.inner == *other
}
}
pub type EqBand = Bipolar<15>;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MorphOf<const BITS: u32> {
inner: u8,
}
impl<const BITS: u32> MorphOf<BITS> {
const VALID: () = assert!(BITS > 0 && BITS <= 8, "a morph must fit in a byte");
pub const NEUTRAL: u8 = {
let () = Self::VALID;
((1u16 << BITS) / 2 - 1) as u8
};
pub fn as_u8(&self) -> u8 {
let () = Self::VALID;
self.inner
}
pub fn is_neutral(&self) -> bool {
self.inner == Self::NEUTRAL
}
}
impl<const BITS: u32> Default for MorphOf<BITS> {
fn default() -> Self {
let () = Self::VALID;
Self { inner: 0 }
}
}
impl<const BITS: u32> Packed for MorphOf<BITS> {
const MAX_BITS: u32 = {
let () = Self::VALID;
BITS
};
const DECODE_BITS: u32 = u8::BITS;
const CONTROL: ControlKind = ControlKind::Morph;
type Error = ::core::convert::Infallible;
fn from_bits(bits: u64) -> Result<Self, Self::Error> {
let () = Self::VALID;
Ok(MorphOf { inner: bits as u8 })
}
fn to_bits(&self) -> u64 {
self.inner as u64
}
}
impl<const BITS: u32> Debug for MorphOf<BITS> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl<const BITS: u32> Display for MorphOf<BITS> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self.is_neutral() {
f.write_str("—")
} else {
write!(f, "{}", self.inner)
}
}
}
impl<const BITS: u32> PartialEq<u8> for MorphOf<BITS> {
fn eq(&self, other: &u8) -> bool {
self.inner == *other
}
}
pub type MorphTarget = MorphOf<8>;
pub type DrawbarMorph = MorphOf<5>;
pub type SwitchMorph = MorphOf<3>;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WideSelector<const BITS: u32> {
inner: u16,
}
impl<const BITS: u32> WideSelector<BITS> {
const VALID: () = assert!(BITS > 0 && BITS <= 16, "a wide selector must fit in a u16");
pub fn raw(&self) -> u16 {
let () = Self::VALID;
self.inner
}
}
impl<const BITS: u32> Default for WideSelector<BITS> {
fn default() -> Self {
let () = Self::VALID;
Self { inner: 0 }
}
}
impl<const BITS: u32> Packed for WideSelector<BITS> {
const MAX_BITS: u32 = {
let () = Self::VALID;
BITS
};
const DECODE_BITS: u32 = u16::BITS;
const CONTROL: ControlKind = ControlKind::Selector;
type Error = ::core::convert::Infallible;
fn from_bits(bits: u64) -> Result<Self, Self::Error> {
let () = Self::VALID;
Ok(WideSelector { inner: bits as u16 })
}
fn to_bits(&self) -> u64 {
self.inner as u64
}
}
impl<const BITS: u32> Debug for WideSelector<BITS> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl<const BITS: u32> Display for WideSelector<BITS> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl<const BITS: u32> PartialEq<u16> for WideSelector<BITS> {
fn eq(&self, other: &u16) -> bool {
self.inner == *other
}
}
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Drawbar {
inner: u8,
}
impl Drawbar {
pub const MAX: u8 = 8;
pub fn raw(&self) -> u8 {
self.inner
}
pub fn position(&self) -> Option<u8> {
(self.inner <= Self::MAX).then_some(self.inner)
}
}
impl Packed for Drawbar {
const MAX_BITS: u32 = 4;
const DECODE_BITS: u32 = u8::BITS;
const CONTROL: ControlKind = ControlKind::Drawbar;
type Error = ::core::convert::Infallible;
fn from_bits(bits: u64) -> Result<Self, Self::Error> {
Ok(Drawbar { inner: bits as u8 })
}
fn to_bits(&self) -> u64 {
self.inner as u64
}
}
impl Debug for Drawbar {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl Display for Drawbar {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl PartialEq<u8> for Drawbar {
fn eq(&self, other: &u8) -> bool {
self.inner == *other
}
}
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OctaveShiftNibble {
inner: i8,
}
impl OctaveShiftNibble {
pub fn octaves(&self) -> i8 {
self.inner
}
}
impl Packed for OctaveShiftNibble {
const MAX_BITS: u32 = 4;
const DECODE_BITS: u32 = 4;
const CONTROL: ControlKind = ControlKind::Shift(Unit::Octaves);
type Error = ::core::convert::Infallible;
fn from_bits(bits: u64) -> Result<Self, Self::Error> {
let nibble = (bits & 0xf) as i8;
Ok(OctaveShiftNibble {
inner: if nibble >= 8 { nibble - 16 } else { nibble },
})
}
fn to_bits(&self) -> u64 {
(self.inner as u8 & 0xf) as u64
}
}
impl Debug for OctaveShiftNibble {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl Display for OctaveShiftNibble {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:+}", self.inner)
}
}
impl PartialEq<i8> for OctaveShiftNibble {
fn eq(&self, other: &i8) -> bool {
self.inner == *other
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Selector<const BITS: u32> {
inner: u8,
}
impl<const BITS: u32> Selector<BITS> {
const VALID: () = assert!(BITS > 0 && BITS <= 8, "a selector must fit in a byte");
pub fn raw(&self) -> u8 {
let () = Self::VALID;
self.inner
}
}
impl<const BITS: u32> Default for Selector<BITS> {
fn default() -> Self {
let () = Self::VALID;
Self { inner: 0 }
}
}
impl<const BITS: u32> Packed for Selector<BITS> {
const MAX_BITS: u32 = {
let () = Self::VALID;
BITS
};
const DECODE_BITS: u32 = u8::BITS;
const CONTROL: ControlKind = ControlKind::Selector;
type Error = ::core::convert::Infallible;
fn from_bits(bits: u64) -> Result<Self, Self::Error> {
let () = Self::VALID;
Ok(Selector { inner: bits as u8 })
}
fn to_bits(&self) -> u64 {
self.inner as u64
}
}
impl<const BITS: u32> Debug for Selector<BITS> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl<const BITS: u32> Display for Selector<BITS> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl<const BITS: u32> PartialEq<u8> for Selector<BITS> {
fn eq(&self, other: &u8) -> bool {
self.inner == *other
}
}
pub type ClockDivision = Selector<4>;
#[derive(Copy, Default, Clone, PartialEq, Eq)]
pub struct PartMix {
inner: u8,
}
impl PartMix {
pub fn inner(&self) -> u8 {
self.inner
}
pub fn lower(&self) -> f32 {
let lower = 100_f32 - ((self.inner() as f32) / 127.0) * 100_f32;
if lower > 50_f32 {
50_f32
} else {
lower
}
}
pub fn upper(&self) -> f32 {
let upper = ((self.inner() as f32) / 127.0) * 100_f32;
if upper > 50_f32 {
50_f32
} else {
upper
}
}
pub fn as_string(&self) -> String {
format!("{:.1}/{:.1}", self.lower(), self.upper())
}
pub fn as_tuple(&self) -> (f32, f32) {
(self.lower(), self.upper())
}
}
impl Debug for PartMix {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_string())
}
}
impl Packed for PartMix {
const MAX_BITS: u32 = 7;
const DECODE_BITS: u32 = u8::BITS;
const CONTROL: ControlKind = ControlKind::Bipolar(Unit::None);
type Error = ParseError;
fn from_bits(bits: u64) -> Result<Self, ParseError> {
(bits as u8).try_into()
}
fn to_bits(&self) -> u64 {
self.inner() as u64
}
}
impl TryFrom<u8> for PartMix {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
if value > 127 {
return Err(ParseError::OutOfBounds {
value: format!("{value}"),
bound: "0..=127".to_string(),
});
}
Ok(PartMix { inner: value })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PercSpeed {
Off,
Soft,
Fast,
Both,
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
pub enum SplitPoint73 {
#[default]
C3,
F3,
C4,
F4,
C5,
F5,
Upper,
Lower,
}
impl TryFrom<u8> for SplitPoint73 {
type Error = &'static str;
fn try_from(value: u8) -> Result<SplitPoint73, Self::Error> {
match value {
0 => Ok(SplitPoint73::C3),
1 => Ok(SplitPoint73::F3),
2 => Ok(SplitPoint73::C4),
3 => Ok(SplitPoint73::F4),
4 => Ok(SplitPoint73::C5),
5 => Ok(SplitPoint73::F5),
6 => Ok(SplitPoint73::Upper),
7 => Ok(SplitPoint73::Lower),
_ => Err("Value is out of range for split point"),
}
}
}
impl Packed for SplitPoint73 {
const MAX_BITS: u32 = 3;
const DECODE_BITS: u32 = u8::BITS;
const CONTROL: ControlKind = ControlKind::Selector;
type Error = ParseError;
fn from_bits(bits: u64) -> Result<Self, ParseError> {
SplitPoint73::try_from(bits as u8).map_err(|_| ParseError::OutOfBounds {
value: format!("{bits}"),
bound: "0..=7 (SplitPoint73)".to_string(),
})
}
fn to_bits(&self) -> u64 {
*self as u64
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VibChorus {
V1,
C1,
V2,
C2,
V3,
C3,
}
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StageTranspose {
raw: u8,
}
impl StageTranspose {
pub fn raw(&self) -> u8 {
self.raw
}
pub fn semitones(&self) -> Option<i8> {
(self.raw <= 12).then(|| self.raw as i8 - 6)
}
}
impl Packed for StageTranspose {
const MAX_BITS: u32 = 4;
const DECODE_BITS: u32 = u8::BITS;
const CONTROL: ControlKind = ControlKind::Shift(Unit::Semitones);
type Error = ParseError;
fn from_bits(bits: u64) -> Result<Self, ParseError> {
Ok(StageTranspose { raw: bits as u8 })
}
fn to_bits(&self) -> u64 {
self.raw as u64
}
}
impl Debug for StageTranspose {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.semitones() {
Some(s) => write!(f, "{s}"),
None => write!(f, "unknown ({})", self.raw),
}
}
}
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MasterTempo {
inner: u8,
}
impl MasterTempo {
pub fn as_u8(&self) -> u8 {
self.inner
}
pub fn bpm(&self) -> u16 {
self.inner as u16 + 30
}
}
impl Packed for MasterTempo {
const MAX_BITS: u32 = 8;
const DECODE_BITS: u32 = u8::BITS;
const CONTROL: ControlKind = ControlKind::Knob(Unit::Bpm);
type Error = ParseError;
fn from_bits(bits: u64) -> Result<Self, ParseError> {
Ok(MasterTempo { inner: bits as u8 })
}
fn to_bits(&self) -> u64 {
self.inner as u64
}
}
impl Debug for MasterTempo {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.bpm())
}
}
macro_rules! sparse_enum {
(
$(#[$meta:meta])*
$name:ident, $bits:expr, { $($value:expr => $variant:ident, $label:expr;)+ }
) => {
$(#[$meta])*
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum $name {
$($variant,)+
Unknown(u8),
}
impl ::core::fmt::Debug for $name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
match self {
$($name::$variant => f.write_str(stringify!($variant)),)+
$name::Unknown(raw) => write!(f, "unknown ({raw})"),
}
}
}
impl $name {
pub fn label(&self) -> Option<&'static str> {
match self {
$($name::$variant => Some($label),)+
$name::Unknown(_) => None,
}
}
pub fn is_unknown(&self) -> bool {
matches!(self, $name::Unknown(_))
}
pub fn raw(&self) -> u8 {
<Self as $crate::bits::Packed>::to_bits(self) as u8
}
}
impl Default for $name {
fn default() -> Self {
<Self as $crate::bits::Packed>::from_bits(0).expect("decoding is total")
}
}
impl $crate::bits::Packed for $name {
const MAX_BITS: u32 = $bits;
const DECODE_BITS: u32 = u8::BITS;
const CONTROL: $crate::fields::ControlKind = $crate::fields::ControlKind::Selector;
type Error = ::core::convert::Infallible;
fn from_bits(bits: u64) -> Result<Self, Self::Error> {
Ok(match bits as u8 {
$($value => $name::$variant,)+
other => $name::Unknown(other),
})
}
fn to_bits(&self) -> u64 {
match self {
$($name::$variant => $value as u64,)+
$name::Unknown(raw) => *raw as u64,
}
}
}
impl ::core::fmt::Display for $name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
match self.label() {
Some(label) => f.write_str(label),
None => write!(f, "unknown ({})", self.raw()),
}
}
}
};
}
pub(crate) use sparse_enum;
macro_rules! switch {
(
$(#[$meta:meta])*
$name:ident, $clear:ident = $clear_label:expr, $set:ident = $set_label:expr
) => {
$(#[$meta])*
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum $name {
#[default]
$clear,
$set,
}
impl $name {
pub fn label(&self) -> &'static str {
match self {
$name::$clear => $clear_label,
$name::$set => $set_label,
}
}
pub fn is_set(&self) -> bool {
matches!(self, $name::$set)
}
}
impl $crate::bits::Packed for $name {
const MAX_BITS: u32 = 1;
const DECODE_BITS: u32 = u64::BITS;
const CONTROL: $crate::fields::ControlKind = $crate::fields::ControlKind::Toggle;
type Error = ::core::convert::Infallible;
fn from_bits(bits: u64) -> Result<Self, Self::Error> {
Ok(if bits != 0 { $name::$set } else { $name::$clear })
}
fn to_bits(&self) -> u64 {
self.is_set() as u64
}
}
impl ::core::fmt::Debug for $name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
match self {
$name::$clear => f.write_str(stringify!($clear)),
$name::$set => f.write_str(stringify!($set)),
}
}
}
impl ::core::fmt::Display for $name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.write_str(self.label())
}
}
};
}
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
pub struct ArpPattern {
inner: u32,
}
impl ArpPattern {
pub const STEPS: usize = 16;
pub fn raw(&self) -> u32 {
self.inner
}
pub fn steps(&self) -> [u8; Self::STEPS] {
std::array::from_fn(|n| ((self.inner >> (2 * n)) & 0b11) as u8)
}
pub fn is_empty(&self) -> bool {
self.inner == 0
}
}
impl Packed for ArpPattern {
const MAX_BITS: u32 = 32;
const DECODE_BITS: u32 = u32::BITS;
const CONTROL: ControlKind = ControlKind::Pattern;
type Error = ::core::convert::Infallible;
fn from_bits(bits: u64) -> Result<Self, Self::Error> {
Ok(ArpPattern { inner: bits as u32 })
}
fn to_bits(&self) -> u64 {
self.inner as u64
}
}
impl Debug for ArpPattern {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:#010x}", self.inner)
}
}
impl Display for ArpPattern {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for (n, step) in self.steps().into_iter().enumerate() {
if n > 0 && n % 4 == 0 {
f.write_str(" ")?;
}
match step {
0 => f.write_str(".")?,
s => write!(f, "{s}")?,
}
}
Ok(())
}
}
impl PartialEq<u32> for ArpPattern {
fn eq(&self, other: &u32) -> bool {
self.inner == *other
}
}
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LibraryRef {
inner: u32,
}
impl LibraryRef {
pub fn id(&self) -> u32 {
self.inner
}
pub fn is_none(&self) -> bool {
self.inner == 0
}
}
impl Packed for LibraryRef {
const MAX_BITS: u32 = 32;
const DECODE_BITS: u32 = u32::BITS;
const CONTROL: ControlKind = ControlKind::Reference;
type Error = ::core::convert::Infallible;
fn from_bits(bits: u64) -> Result<Self, Self::Error> {
Ok(LibraryRef { inner: bits as u32 })
}
fn to_bits(&self) -> u64 {
self.inner as u64
}
}
impl Debug for LibraryRef {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:#010x}", self.inner)
}
}
impl Display for LibraryRef {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self.is_none() {
f.write_str("none")
} else {
write!(f, "{:#010x}", self.inner)
}
}
}
impl PartialEq<u32> for LibraryRef {
fn eq(&self, other: &u32) -> bool {
self.inner == *other
}
}
switch!(
DelayCharacter, Normal = "normal", Analog = "analog"
);
switch!(
CompressorResponse, Normal = "normal", Fast = "fast"
);
switch!(
RotorSpeed, Slow = "slow", Fast = "fast"
);
sparse_enum!(
KbZone4, 4, {
0 => V0, "o---";
1 => V1, "-o--";
2 => V2, "--o-";
3 => V3, "---o";
4 => V4, "oo--";
5 => V5, "-oo-";
6 => V6, "--oo";
7 => V7, "ooo-";
8 => V8, "-ooo";
9 => V9, "oooo";
}
);
sparse_enum!(
KbZone3, 3, {
0 => Lo, "LO";
1 => LoUp, "LO UP";
2 => Up, "UP";
3 => UpHi, "UP HI";
4 => Hi, "HI";
5 => LoUpHi, "LO UP HI";
}
);
sparse_enum!(
SplitNote, 4, {
0 => F2, "F2";
1 => C3, "C3";
2 => F3, "F3";
3 => C4, "C4";
4 => F4, "F4";
5 => C5, "C5";
6 => F5, "F5";
7 => C6, "C6";
8 => F6, "F6";
9 => C7, "C7";
}
);
sparse_enum!(
SplitWidth, 2, {
0 => One, "1";
1 => Six, "6";
2 => Twelve, "12";
}
);
sparse_enum!(
ProgramCategory, 8, {
0x00 => Acoustic, "Acoustic";
0x01 => Bass, "Bass";
0x02 => Wind, "Wind";
0x04 => Fantasy, "Fantasy";
0x05 => Fx, "FX";
0x06 => Lead, "Lead";
0x07 => Organ, "Organ";
0x08 => Pad, "Pad";
0x0a => Pluck, "Pluck";
0x0b => String, "String";
0x0c => Synth, "Synth";
0x0d => Vocal, "Vocal";
0x0e => User, "User";
0x11 => None_, "None";
0x15 => Grand, "Grand";
0x16 => Upright, "Upright";
0x17 => EPiano1, "EPiano1";
0x18 => EPiano2, "EPiano2";
0x1b => Clavinet, "Clavinet";
0x1c => Harpsi, "Harpsi";
0x1e => Arpeggio, "Arpeggio";
0xff => Undefined, "Undefined";
}
);
sparse_enum!(
Effect1Type, 3, {
0 => APan, "A-Pan";
1 => Trem, "Trem";
2 => Rm, "RM";
3 => WaWa, "WA-WA";
4 => AWa1, "A-WA1";
5 => AWa2, "A-WA2";
}
);
sparse_enum!(
Effect2Type, 3, {
0 => Phas1, "PHAS1";
1 => Phas2, "PHAS2";
2 => Flang, "FLANG";
3 => Vibe, "VIBE";
4 => Chor1, "CHOR1";
5 => Chor2, "CHOR2";
}
);
sparse_enum!(
ReverbType, 3, {
0 => Room1, "Room 1";
1 => Room2, "Room 2";
2 => Stage1, "Stage 1";
3 => Stage2, "Stage 2";
4 => Hall1, "Hall 1";
5 => Hall2, "Hall 2";
}
);
#[cfg(test)]
mod tests {
use super::*;
use crate::fields::{ControlKind, Unit};
#[test]
fn a_type_says_what_kind_of_control_it_is() {
assert_eq!(<Level as Packed>::CONTROL, ControlKind::Knob(Unit::Panel10));
assert_eq!(
<Time as Packed>::CONTROL,
ControlKind::Knob(Unit::Milliseconds)
);
assert_eq!(
<EqBand as Packed>::CONTROL,
ControlKind::Bipolar(Unit::Decibels)
);
assert_eq!(<MorphTarget as Packed>::CONTROL, ControlKind::Morph);
assert_eq!(<Drawbar as Packed>::CONTROL, ControlKind::Drawbar);
assert_eq!(<ArpPattern as Packed>::CONTROL, ControlKind::Pattern);
assert_eq!(<LibraryRef as Packed>::CONTROL, ControlKind::Reference);
assert_eq!(<KbZone4 as Packed>::CONTROL, ControlKind::Selector);
assert_eq!(<bool as Packed>::CONTROL, ControlKind::Toggle);
assert_eq!(
<OctaveShiftNibble as Packed>::CONTROL,
ControlKind::Shift(Unit::Octaves)
);
assert_eq!(<u8 as Packed>::CONTROL, ControlKind::Number);
}
#[test]
fn a_unit_says_whether_it_can_be_computed() {
assert!(Unit::Panel10.describes_a_known_transform());
assert!(Unit::Decibels.describes_a_known_transform());
assert!(!Unit::Milliseconds.describes_a_known_transform());
assert!(!Unit::Hertz.describes_a_known_transform());
assert_eq!(Time::new(96).unwrap().to_string(), "96");
assert_eq!(Level::new(96).unwrap().to_string(), "96 (7.6)");
}
#[test]
fn the_stage4_octave_shift_wraps_where_the_others_bias() {
let read = |bits| OctaveShiftNibble::from_bits(bits).unwrap().octaves();
assert_eq!(read(0), 0);
assert_eq!(read(1), 1);
assert_eq!(read(2), 2);
assert_eq!(read(15), -1);
assert_eq!(read(14), -2);
for bits in 0..16u64 {
assert_eq!(OctaveShiftNibble::from_bits(bits).unwrap().to_bits(), bits);
}
}
#[test]
fn a_morph_slot_names_its_neutral_and_keeps_the_rest() {
assert_eq!(MorphTarget::NEUTRAL, 127);
let neutral = MorphTarget::from_bits(127).unwrap();
assert!(neutral.is_neutral());
assert_eq!(neutral.to_string(), "—");
assert_eq!(format!("{neutral:?}"), "127");
let moved = MorphTarget::from_bits(254).unwrap();
assert!(!moved.is_neutral());
assert_eq!(moved.to_string(), "254");
for bits in 0..256u64 {
assert_eq!(MorphTarget::from_bits(bits).unwrap().to_bits(), bits);
}
}
#[test]
fn an_arp_pattern_reads_as_steps() {
let accent = ArpPattern::from_bits(0x0101_0101).unwrap();
assert_eq!(
accent.steps(),
[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0]
);
assert_eq!(accent.to_string(), "1... 1... 1... 1...");
let pan = ArpPattern::from_bits(0x55aa_5500).unwrap();
assert_eq!(&pan.steps()[4..12], &[1, 1, 1, 1, 2, 2, 2, 2]);
assert!(ArpPattern::default().is_empty());
assert_eq!(format!("{pan:?}"), "0x55aa5500");
}
#[test]
fn a_bipolar_band_reads_signed() {
assert_eq!(EqBand::new(64).unwrap().reading(), 0.0);
assert_eq!(EqBand::new(0).unwrap().to_string(), "0 (-15.0)");
assert_eq!(EqBand::new(127).unwrap().to_string(), "127 (+15.0)");
assert_eq!(format!("{:?}", EqBand::new(96).unwrap()), "96");
}
#[test]
fn a_switch_names_both_of_its_states() {
let normal = DelayCharacter::from_bits(0).unwrap();
let analog = DelayCharacter::from_bits(1).unwrap();
assert_eq!(format!("{normal:?}"), "Normal");
assert_eq!(analog.to_string(), "analog");
assert_eq!(analog.to_bits(), 1);
assert!(analog.is_set());
assert_eq!(<DelayCharacter as Packed>::MAX_BITS, 1);
}
#[test]
fn a_drawbar_keeps_a_nibble_past_its_travel() {
assert_eq!(Drawbar::from_bits(8).unwrap().position(), Some(8));
assert_eq!(Drawbar::from_bits(9).unwrap().position(), None);
assert_eq!(Drawbar::from_bits(9).unwrap().raw(), 9);
for bits in 0..16u64 {
assert_eq!(Drawbar::from_bits(bits).unwrap().to_bits(), bits);
}
}
#[test]
fn a_level_carries_the_panel_transform() {
assert_eq!(Level::new(0).unwrap().to_string(), "0 (0.0)");
assert_eq!(Level::new(127).unwrap().to_string(), "127 (10.0)");
assert_eq!(Level::new(96).unwrap().to_string(), "96 (7.6)");
assert!(Level::new(128).is_err(), "128 does not fit seven bits");
}
}