use crate::components::sparse_enum;
use crate::components::PartMix;
use crate::formats::ne5::{Instrument, Level, OctaveShift, SplitPoint, Transpose};
use nord_bits_derive::bitbody;
#[bitbody(7)]
#[derive(Default)]
pub struct CenterPanel {
#[bits(0..=2)]
pub lower_part: Instrument,
#[bits(3..=5)]
pub upper_part: Instrument,
#[bits(6..=9)]
pub lower_octave_shift: OctaveShift,
#[bits(10..=13)]
pub upper_octave_shift: OctaveShift,
#[bits(14..=14)]
pub lower_sustain: bool,
#[bits(15..=15)]
pub upper_sustain: bool,
#[bits(16..=16)]
pub lower_control: bool,
#[bits(17..=17)]
pub upper_control: bool,
#[bits(18..=18)]
pub unknown_boolean1: bool,
#[bits(19..=19)]
pub split: bool,
#[bits(20..=22)]
pub split_point: SplitPoint,
#[bits(23..=23)]
pub transpose_enabled: bool,
#[bits(24..=27)]
pub transpose: Transpose,
#[bits(28..=34)]
pub part_mix: PartMix,
#[bits(35..=41)]
pub gain: Level,
#[bits(42..=44)]
pub organ_type: OrganType,
#[bits(45..=45)]
pub lower_enabled: bool,
#[bits(46..=46)]
pub upper_enabled: bool,
#[bits(47..=47)]
pub drawbar_live: bool,
}
sparse_enum!(
OrganType, 3, {
0 => B3, "b3";
1 => B3Bass, "b3+bass";
2 => Pipe, "pipe";
3 => Vox, "vox";
4 => Farfisa, "farfisa";
}
);
impl OrganType {
pub fn storage(&self) -> Option<crate::formats::ne5::program::OrganModel> {
use crate::formats::ne5::program::OrganModel;
match self {
OrganType::B3 | OrganType::B3Bass => Some(OrganModel::B3),
OrganType::Pipe => Some(OrganModel::Pipe),
OrganType::Vox => Some(OrganModel::Vox),
OrganType::Farfisa => Some(OrganModel::Farfisa),
OrganType::Unknown(_) => None,
}
}
pub fn is_b3_bass(&self) -> bool {
matches!(self, OrganType::B3Bass)
}
}
#[cfg(test)]
mod tests {
use super::super::{self as program, OrganModel, FILE_LEN};
use super::*;
use crate::bits::Packed;
use crate::types::RangedU8;
use std::io::Cursor;
#[test]
fn an_out_of_range_value_is_refused_where_it_is_written() {
let too_wide: Result<RangedU8<127>, _> = 200u8.try_into();
assert!(too_wide.is_err(), "200 must not be a valid seven-bit gain");
assert!(
RangedU8::<127>::new(127).is_ok(),
"127 is the largest that fits"
);
let mut program = program::new((0, 0).try_into().unwrap());
program.center_panel.gain = 96u8.try_into().unwrap();
let mut bytes = Vec::new();
program
.write_to(&mut Cursor::new(&mut bytes))
.expect("a panel built from ranged values always writes");
assert_eq!(bytes.len(), FILE_LEN);
}
#[test]
fn the_default_panel_encodes_and_decodes() {
let panel = CenterPanel::default();
let raw = <[u8; 7]>::from(&panel);
let back = CenterPanel::try_from(raw).expect("default panel must decode");
assert_eq!(back.lower_octave_shift, 0);
assert_eq!(back.upper_octave_shift, 0);
assert_eq!(back.transpose, 0);
assert_eq!(back.lower_part, Instrument::Organ);
}
#[test]
fn organ_type_values_round_trip() {
for bits in 0..5u64 {
assert_eq!(OrganType::from_bits(bits).unwrap().to_bits(), bits);
}
assert!(OrganType::from_bits(6).unwrap().is_unknown());
assert_eq!(OrganType::B3Bass.storage(), Some(OrganModel::B3));
assert!(OrganType::B3Bass.is_b3_bass());
assert!(!OrganType::B3.is_b3_bass());
}
}