use crate::bits::Packed;
use crate::components::{PercSpeed, VibChorus};
use crate::error::ParseError;
use crate::types::RangedU8;
use nord_bits_derive::bitbody;
use std::fmt::{self, Debug, Display, Formatter};
const ORGAN_LEN: usize = 0x92 - 0x4d;
type Bar = RangedU8<{ Drawbars::MAX }>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OrganModel {
B3,
Vox,
Farfisa,
Pipe,
}
#[bitbody(69)]
pub struct OrganPanel {
#[bits(24..=26)]
pub b3_vib: B3Vib,
#[bits(27..=27)]
pub b3_perc_third: bool,
#[bits(28..=29)]
pub b3_perc_speed: B3PercSpeed,
#[bits(41..=41)]
pub b3_preset2_selected: bool,
#[bits(56..=91)]
pub b3_preset1_drawbars: Drawbars,
#[bits(92..=92)]
pub b3_preset1_vib: bool,
#[bits(93..=93)]
pub b3_preset1_perc: bool,
#[bits(94..=97)]
pub b3_bass_bar1: Bar,
#[bits(98..=101)]
pub b3_bass_bar2: Bar,
#[bits(112..=147)]
pub b3_preset2_drawbars: Drawbars,
#[bits(148..=148)]
pub b3_preset2_vib: bool,
#[bits(149..=149)]
pub b3_preset2_perc: bool,
#[bits(168..=170)]
pub vox_vib: VoxVib,
#[bits(185..=185)]
pub vox_preset2_selected: bool,
#[bits(200..=235)]
pub vox_preset1_drawbars: Drawbars,
#[bits(236..=236)]
pub vox_preset1_vib: bool,
#[bits(248..=283)]
pub vox_preset2_drawbars: Drawbars,
#[bits(284..=284)]
pub vox_preset2_vib: bool,
#[bits(296..=298)]
pub farfisa_vib: FarfisaVib,
#[bits(313..=313)]
pub farfisa_preset2_selected: bool,
#[bits(328..=363)]
pub farfisa_preset1_drawbars: Drawbars,
#[bits(364..=364)]
pub farfisa_preset1_vib: bool,
#[bits(376..=411)]
pub farfisa_preset2_drawbars: Drawbars,
#[bits(412..=412)]
pub farfisa_preset2_vib: bool,
#[bits(441..=441)]
pub pipe_preset2_selected: bool,
#[bits(456..=491)]
pub pipe_preset1_drawbars: Drawbars,
#[bits(504..=539)]
pub pipe_preset2_drawbars: Drawbars,
}
impl Default for OrganPanel {
fn default() -> Self {
OrganPanel::try_from([0; ORGAN_LEN]).expect("every organ field decodes totally")
}
}
impl OrganPanel {
pub fn preset(&self, model: OrganModel) -> u8 {
if *self.preset_selected(model) {
2
} else {
1
}
}
pub fn drawbars(&self, model: OrganModel, preset: u8) -> [u8; 9] {
self.drawbar_block(model, preset).positions()
}
pub fn b3_bass_drawbars(&self) -> [u8; 2] {
[self.b3_bass_bar1.as_u8(), self.b3_bass_bar2.as_u8()]
}
pub fn farfisa_tabs(&self, preset: u8) -> [bool; 9] {
self.drawbars(OrganModel::Farfisa, preset)
.map(|bar| bar >= 5)
}
pub fn vib_on(&self, model: OrganModel, preset: u8) -> bool {
self.vib_flag(model, preset).is_some_and(|on| *on)
}
pub fn vib_type(&self, model: OrganModel) -> Option<VibChorus> {
match model {
OrganModel::B3 => self.b3_vib.get(),
OrganModel::Vox => self.vox_vib.get(),
OrganModel::Farfisa => self.farfisa_vib.get(),
OrganModel::Pipe => None,
}
}
pub fn b3_perc_on(&self, preset: u8) -> bool {
if preset == 2 {
self.b3_preset2_perc
} else {
self.b3_preset1_perc
}
}
pub fn b3_perc_third(&self) -> bool {
self.b3_perc_third
}
pub fn b3_perc_speed(&self) -> PercSpeed {
self.b3_perc_speed
.get()
.expect("all four two-bit indices are named")
}
pub fn set_preset(&mut self, model: OrganModel, preset: u8) {
*self.preset_selected_mut(model) = preset == 2;
}
pub fn set_drawbars(
&mut self,
model: OrganModel,
preset: u8,
bars: [u8; 9],
) -> Result<(), ParseError> {
*self.drawbar_block_mut(model, preset) = Drawbars::new(bars)?;
Ok(())
}
pub fn set_farfisa_tabs(&mut self, preset: u8, tabs: [bool; 9]) {
let bars = tabs.map(|on| if on { Drawbars::MAX } else { 0 });
self.set_drawbars(OrganModel::Farfisa, preset, bars)
.expect("0 and 8 are both in range");
}
pub fn set_vib_on(&mut self, model: OrganModel, preset: u8, on: bool) {
if let Some(flag) = self.vib_flag_mut(model, preset) {
*flag = on;
}
}
pub fn set_vib_type(&mut self, model: OrganModel, vib: VibChorus) -> Result<(), ParseError> {
let refuse = |why: &str| ParseError::OutOfBounds {
value: format!("{vib:?}"),
bound: format!("{model:?} {why}"),
};
match model {
OrganModel::B3 => {
self.b3_vib = B3Vib::select(vib).ok_or_else(|| refuse("does not offer it"))?
}
OrganModel::Vox => {
self.vox_vib = VoxVib::select(vib).ok_or_else(|| refuse("does not offer it"))?
}
OrganModel::Farfisa => {
self.farfisa_vib =
FarfisaVib::select(vib).ok_or_else(|| refuse("does not offer it"))?
}
OrganModel::Pipe => return Err(refuse("has no vibrato/chorus")),
}
Ok(())
}
pub fn set_b3_perc_on(&mut self, preset: u8, on: bool) {
if preset == 2 {
self.b3_preset2_perc = on;
} else {
self.b3_preset1_perc = on;
}
}
pub fn set_b3_perc_third(&mut self, on: bool) {
self.b3_perc_third = on;
}
pub fn set_b3_perc_speed(&mut self, speed: PercSpeed) {
self.b3_perc_speed =
B3PercSpeed::select(speed).expect("all four speeds have a two-bit index");
}
pub fn set_b3_bass_drawbars(&mut self, bars: [u8; 2]) -> Result<(), ParseError> {
self.b3_bass_bar1 = bars[0].try_into()?;
self.b3_bass_bar2 = bars[1].try_into()?;
Ok(())
}
fn drawbar_block(&self, model: OrganModel, preset: u8) -> &Drawbars {
match (model, preset == 2) {
(OrganModel::B3, false) => &self.b3_preset1_drawbars,
(OrganModel::B3, true) => &self.b3_preset2_drawbars,
(OrganModel::Vox, false) => &self.vox_preset1_drawbars,
(OrganModel::Vox, true) => &self.vox_preset2_drawbars,
(OrganModel::Farfisa, false) => &self.farfisa_preset1_drawbars,
(OrganModel::Farfisa, true) => &self.farfisa_preset2_drawbars,
(OrganModel::Pipe, false) => &self.pipe_preset1_drawbars,
(OrganModel::Pipe, true) => &self.pipe_preset2_drawbars,
}
}
fn drawbar_block_mut(&mut self, model: OrganModel, preset: u8) -> &mut Drawbars {
match (model, preset == 2) {
(OrganModel::B3, false) => &mut self.b3_preset1_drawbars,
(OrganModel::B3, true) => &mut self.b3_preset2_drawbars,
(OrganModel::Vox, false) => &mut self.vox_preset1_drawbars,
(OrganModel::Vox, true) => &mut self.vox_preset2_drawbars,
(OrganModel::Farfisa, false) => &mut self.farfisa_preset1_drawbars,
(OrganModel::Farfisa, true) => &mut self.farfisa_preset2_drawbars,
(OrganModel::Pipe, false) => &mut self.pipe_preset1_drawbars,
(OrganModel::Pipe, true) => &mut self.pipe_preset2_drawbars,
}
}
fn preset_selected(&self, model: OrganModel) -> &bool {
match model {
OrganModel::B3 => &self.b3_preset2_selected,
OrganModel::Vox => &self.vox_preset2_selected,
OrganModel::Farfisa => &self.farfisa_preset2_selected,
OrganModel::Pipe => &self.pipe_preset2_selected,
}
}
fn preset_selected_mut(&mut self, model: OrganModel) -> &mut bool {
match model {
OrganModel::B3 => &mut self.b3_preset2_selected,
OrganModel::Vox => &mut self.vox_preset2_selected,
OrganModel::Farfisa => &mut self.farfisa_preset2_selected,
OrganModel::Pipe => &mut self.pipe_preset2_selected,
}
}
fn vib_flag(&self, model: OrganModel, preset: u8) -> Option<&bool> {
Some(match (model, preset == 2) {
(OrganModel::B3, false) => &self.b3_preset1_vib,
(OrganModel::B3, true) => &self.b3_preset2_vib,
(OrganModel::Vox, false) => &self.vox_preset1_vib,
(OrganModel::Vox, true) => &self.vox_preset2_vib,
(OrganModel::Farfisa, false) => &self.farfisa_preset1_vib,
(OrganModel::Farfisa, true) => &self.farfisa_preset2_vib,
(OrganModel::Pipe, _) => return None,
})
}
fn vib_flag_mut(&mut self, model: OrganModel, preset: u8) -> Option<&mut bool> {
Some(match (model, preset == 2) {
(OrganModel::B3, false) => &mut self.b3_preset1_vib,
(OrganModel::B3, true) => &mut self.b3_preset2_vib,
(OrganModel::Vox, false) => &mut self.vox_preset1_vib,
(OrganModel::Vox, true) => &mut self.vox_preset2_vib,
(OrganModel::Farfisa, false) => &mut self.farfisa_preset1_vib,
(OrganModel::Farfisa, true) => &mut self.farfisa_preset2_vib,
(OrganModel::Pipe, _) => return None,
})
}
}
macro_rules! model_index {
($(#[$meta:meta])* $name:ident, $bits:expr, $of:ty, [$($variant:ident),+ $(,)?]) => {
$(#[$meta])*
#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub struct $name(u8);
impl $name {
pub fn get(&self) -> Option<$of> {
Self::TABLE.get(self.0 as usize).copied()
}
pub fn select(value: $of) -> Option<Self> {
Self::TABLE.iter().position(|&v| v == value).map(|i| $name(i as u8))
}
pub fn raw(&self) -> u8 {
self.0
}
const TABLE: &'static [$of] = &[$(<$of>::$variant),+];
}
impl 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($name(bits as u8))
}
fn to_bits(&self) -> u64 {
self.0 as u64
}
}
impl Debug for $name {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.get() {
Some(value) => write!(f, "{value:?}"),
None => write!(f, "unknown ({})", self.0),
}
}
}
impl Display for $name {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}
};
}
model_index!(
B3Vib, 3, VibChorus, [V1, C1, V2, C2, V3, C3]
);
model_index!(
VoxVib, 3, VibChorus, [V1, V2, V3]
);
model_index!(
FarfisaVib, 3, VibChorus, [V1, V2, C2, C3]
);
model_index!(
B3PercSpeed, 2, PercSpeed, [Off, Fast, Soft, Both]
);
#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub struct Drawbars([u8; 9]);
impl Drawbars {
pub const BARS: usize = 9;
pub const MAX: u8 = 8;
pub fn new(bars: [u8; Self::BARS]) -> Result<Self, ParseError> {
match bars.iter().find(|&&b| b > Self::MAX) {
Some(&bad) => Err(ParseError::OutOfBounds {
value: format!("{bad}"),
bound: format!("0..={}", Self::MAX),
}),
None => Ok(Drawbars(bars)),
}
}
pub fn positions(&self) -> [u8; Self::BARS] {
self.0
}
}
impl Packed for Drawbars {
const MAX_BITS: u32 = 4 * Drawbars::BARS as u32;
const DECODE_BITS: u32 = Self::MAX_BITS;
const CONTROL: crate::fields::ControlKind = crate::fields::ControlKind::Drawbar;
type Error = ::core::convert::Infallible;
fn from_bits(bits: u64) -> Result<Self, Self::Error> {
Ok(Drawbars(std::array::from_fn(|n| {
let shift = Self::MAX_BITS - 4 * (n as u32 + 1);
((bits >> shift) & 0xf) as u8
})))
}
fn to_bits(&self) -> u64 {
self.0
.iter()
.fold(0, |bits, &bar| (bits << 4) | (bar as u64 & 0xf))
}
}
impl Debug for Drawbars {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.0)
}
}
impl Display for Drawbars {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for bar in self.0 {
write!(f, "{bar}")?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
const fn org(abs: usize) -> usize {
abs - 0x4e
}
fn panel(bytes: &[(usize, u8)]) -> OrganPanel {
let mut raw = [0u8; ORGAN_LEN];
for &(at, b) in bytes {
raw[org(at)] = b;
}
OrganPanel::try_from(raw).expect("every organ field decodes totally")
}
#[test]
fn b3_bass_drawbars_decode_from_the_packed_field() {
assert_eq!(
panel(&[(0x59, 0x01), (0x5a, 0x00)]).b3_bass_drawbars(),
[4, 0]
);
assert_eq!(
panel(&[(0x59, 0x00), (0x5a, 0x10)]).b3_bass_drawbars(),
[0, 4]
);
assert_eq!(
panel(&[(0x59, 0x02), (0x5a, 0x1c)]).b3_bass_drawbars(),
[8, 7]
);
assert_eq!(
panel(&[(0x59, 0x02), (0x5a, 0x20)]).b3_bass_drawbars(),
[8, 8]
);
assert_eq!(panel(&[]).b3_bass_drawbars(), [0, 0]);
}
#[test]
fn b3_bass_drawbars_ignore_the_flags_sharing_that_byte() {
assert_eq!(
panel(&[(0x59, 0x82), (0x5a, 0x20)]).b3_bass_drawbars(),
[8, 8]
);
assert_eq!(
panel(&[(0x59, 0x0e), (0x5a, 0x20)]).b3_bass_drawbars(),
[8, 8]
);
assert_eq!(
panel(&[(0x59, 0xfe), (0x5a, 0x20)]).b3_bass_drawbars(),
[8, 8]
);
}
#[test]
fn farfisa_drawbars_are_on_off_tabs() {
let p = panel(&[
(0x77, 0x87),
(0x78, 0x65),
(0x79, 0x43),
(0x7a, 0x21),
(0x7b, 0x00),
]);
assert_eq!(
p.drawbars(OrganModel::Farfisa, 1),
[8, 7, 6, 5, 4, 3, 2, 1, 0]
);
assert_eq!(
p.farfisa_tabs(1),
[true, true, true, true, false, false, false, false, false]
);
let edge = panel(&[
(0x77, 0x54),
(0x78, 0x00),
(0x79, 0x00),
(0x7a, 0x00),
(0x7b, 0x00),
]);
assert!(edge.farfisa_tabs(1)[0], "5 should read as on");
assert!(!edge.farfisa_tabs(1)[1], "4 should read as off");
}
#[test]
fn farfisa_presets_are_independent() {
let p = panel(&[(0x77, 0x80), (0x7d, 0x08)]);
assert!(p.farfisa_tabs(1)[0]);
assert!(!p.farfisa_tabs(1)[1]);
assert!(!p.farfisa_tabs(2)[0]);
assert!(p.farfisa_tabs(2)[1]);
}
#[test]
fn every_model_and_preset_has_its_own_block() {
for (n, (model, preset)) in [
OrganModel::B3,
OrganModel::Vox,
OrganModel::Farfisa,
OrganModel::Pipe,
]
.into_iter()
.flat_map(|m| [(m, 1u8), (m, 2)])
.enumerate()
{
let bars = [(n as u8) % 9; 9];
let mut p = OrganPanel::default();
p.set_drawbars(model, preset, bars).unwrap();
let raw = <[u8; ORGAN_LEN]>::from(&p);
let back = OrganPanel::try_from(raw).unwrap();
assert_eq!(
back.drawbars(model, preset),
bars,
"{model:?} preset {preset}"
);
let others: Vec<_> = [
OrganModel::B3,
OrganModel::Vox,
OrganModel::Farfisa,
OrganModel::Pipe,
]
.into_iter()
.flat_map(|m| [(m, 1u8), (m, 2)])
.filter(|&(m, p)| !(m == model && p == preset))
.filter(|&(m, p)| back.drawbars(m, p) != [0; 9])
.collect();
assert!(
others.is_empty(),
"{model:?} preset {preset} also wrote {others:?}"
);
}
}
#[test]
fn a_model_only_accepts_the_modes_it_has() {
let mut p = OrganPanel::default();
p.set_vib_type(OrganModel::Vox, VibChorus::V3).unwrap();
assert_eq!(p.vib_type(OrganModel::Vox), Some(VibChorus::V3));
assert!(p.set_vib_type(OrganModel::Vox, VibChorus::C1).is_err());
assert!(p.set_vib_type(OrganModel::Pipe, VibChorus::V1).is_err());
p.set_vib_type(OrganModel::B3, VibChorus::C3).unwrap();
assert_eq!(p.vib_type(OrganModel::B3), Some(VibChorus::C3));
assert_eq!(p.vib_type(OrganModel::Vox), Some(VibChorus::V3));
}
#[test]
fn perc_speed_stores_soft_fast_and_both_as_2_1_and_3() {
let bits = |speed| {
let mut p = OrganPanel::default();
p.set_b3_perc_speed(speed);
(<[u8; ORGAN_LEN]>::from(&p)[org(0x51)] >> 2) & 0b11
};
assert_eq!(bits(PercSpeed::Off), 0);
assert_eq!(bits(PercSpeed::Fast), 1);
assert_eq!(bits(PercSpeed::Soft), 2);
assert_eq!(bits(PercSpeed::Both), 3);
let mut p = OrganPanel::default();
p.set_b3_perc_speed(PercSpeed::Soft);
assert_eq!(p.b3_perc_speed(), PercSpeed::Soft);
}
}