use super::{tcat::tcd22xx_spec::*, *};
#[derive(Debug)]
pub struct SPro24DspProtocol;
impl Tcd22xxSpecOperation for SPro24DspProtocol {
const INPUTS: &'static [Input] = &[
Input {
id: SrcBlkId::Ins0,
offset: 2,
count: 2,
label: Some("Mic"),
},
Input {
id: SrcBlkId::Ins0,
offset: 0,
count: 2,
label: Some("Line"),
},
Input {
id: SrcBlkId::Ins0,
offset: 8,
count: 2,
label: Some("Ch-strip"),
},
Input {
id: SrcBlkId::Ins0,
offset: 14,
count: 2,
label: Some("Reverb"),
},
Input {
id: SrcBlkId::Aes,
offset: 6,
count: 2,
label: Some("S/PDIF-coax"),
},
Input {
id: SrcBlkId::Adat,
offset: 0,
count: 8,
label: None,
},
Input {
id: SrcBlkId::Aes,
offset: 4,
count: 2,
label: Some("S/PDIF-opt"),
},
];
const OUTPUTS: &'static [Output] = &[
Output {
id: DstBlkId::Ins0,
offset: 0,
count: 6,
label: None,
},
Output {
id: DstBlkId::Aes,
offset: 6,
count: 2,
label: Some("S/PDIF-coax"),
},
Output {
id: DstBlkId::Ins0,
offset: 8,
count: 2,
label: Some("Ch-strip"),
},
Output {
id: DstBlkId::Ins0,
offset: 14,
count: 2,
label: Some("Reverb"),
},
];
const FIXED: &'static [SrcBlk] = &[
SrcBlk {
id: SrcBlkId::Ins0,
ch: 2,
},
SrcBlk {
id: SrcBlkId::Ins0,
ch: 3,
},
SrcBlk {
id: SrcBlkId::Ins0,
ch: 0,
},
SrcBlk {
id: SrcBlkId::Ins0,
ch: 1,
},
];
}
impl SaffireproSwNoticeOperation for SPro24DspProtocol {
const SW_NOTICE_OFFSET: usize = 0x05ec;
}
impl SaffireproOutGroupOperation for SPro24DspProtocol {
const ENTRY_COUNT: usize = 6;
const HAS_VOL_HWCTL: bool = false;
const OUT_CTL_OFFSET: usize = 0x000c;
const SRC_NOTICE: u32 = 0x00000001;
const DIM_MUTE_NOTICE: u32 = 0x00000002;
}
impl SaffireproInputOperation for SPro24DspProtocol {
const MIC_INPUT_OFFSET: usize = 0x0058;
const LINE_INPUT_OFFSET: usize = 0x005c;
}
#[allow(dead_code)]
const DSP_ENABLE_OFFSET: usize = 0x0070; const CH_STRIP_FLAG_OFFSET: usize = 0x0078;
const CH_STRIP_FLAG_EQ_ENABLE: u16 = 0x0001;
const CH_STRIP_FLAG_COMP_ENABLE: u16 = 0x0002;
const CH_STRIP_FLAG_EQ_AFTER_COMP: u16 = 0x0004;
const CH_STRIP_FLAG_SW_NOTICE: u32 = 0x00000005;
const COEF_OFFSET: usize = 0x0190;
const COEF_BLOCK_SIZE: usize = 0x88;
const COEF_BLOCK_COUNT: usize = 8;
const EQ_COEF_COUNT: usize = 5;
const COMP_OUTPUT_OFFSET: usize = 0x04;
const COMP_THRESHOLD_OFFSET: usize = 0x08;
const COMP_RATIO_OFFSET: usize = 0x0c;
const COMP_ATTACK_OFFSET: usize = 0x10;
const COMP_RELEASE_OFFSET: usize = 0x14;
const COMP_CH0_SW_NOTICE: u32 = 0x00000006;
const COMP_CH1_SW_NOTICE: u32 = 0x00000007;
const EQ_OUTPUT_OFFSET: usize = 0x18;
const EQ_LOW_FREQ_OFFSET: usize = 0x20;
const EQ_LOW_MIDDLE_FREQ_OFFSET: usize = 0x34;
const EQ_HIGH_MIDDLE_FREQ_OFFSET: usize = 0x48;
const EQ_HIGH_FREQ_OFFSET: usize = 0x5c;
const EQ_OUTPUT_CH0_SW_NOTICE: u32 = 0x09;
const EQ_OUTPUT_CH1_SW_NOTICE: u32 = 0x0a;
const EQ_LOW_FREQ_CH0_SW_NOTICE: u32 = 0x0c;
const EQ_LOW_FREQ_CH1_SW_NOTICE: u32 = 0x0c;
const EQ_LOW_MIDDLE_FREQ_CH0_SW_NOTICE: u32 = 0x0f;
const EQ_LOW_MIDDLE_FREQ_CH1_SW_NOTICE: u32 = 0x10;
const EQ_HIGH_MIDDLE_FREQ_CH0_SW_NOTICE: u32 = 0x12;
const EQ_HIGH_MIDDLE_FREQ_CH1_SW_NOTICE: u32 = 0x13;
const EQ_HIGH_FREQ_CH0_SW_NOTICE: u32 = 0x15;
const EQ_HIGH_FREQ_CH1_SW_NOTICE: u32 = 0x16;
const REVERB_SIZE_OFFSET: usize = 0x70;
const REVERB_AIR_OFFSET: usize = 0x74;
const REVERB_ENABLE_OFFSET: usize = 0x78;
const REVERB_DISABLE_OFFSET: usize = 0x7c;
const REVERB_PRE_FILTER_VALUE_OFFSET: usize = 0x80;
const REVERB_PRE_FILTER_SIGN_OFFSET: usize = 0x84;
const REVERB_SW_NOTICE: u32 = 0x0000001a;
trait QuadletConvert {
fn parse(&mut self, quads: &[f32]);
fn build(&self, quads: &mut [f32]);
}
#[derive(Default, Debug, Copy, Clone, PartialEq)]
pub struct Spro24DspCompressorState {
pub output: [f32; 2],
pub threshold: [f32; 2],
pub ratio: [f32; 2],
pub attack: [f32; 2],
pub release: [f32; 2],
}
#[derive(Default, Debug, Copy, Clone, PartialEq)]
pub struct Spro24DspEqualizerFrequencyBandState([f32; EQ_COEF_COUNT]);
#[derive(Default, Debug, Copy, Clone, PartialEq)]
pub struct Spro24DspEqualizerState {
pub output: [f32; 2],
pub low_coef: [Spro24DspEqualizerFrequencyBandState; 2],
pub low_middle_coef: [Spro24DspEqualizerFrequencyBandState; 2],
pub high_middle_coef: [Spro24DspEqualizerFrequencyBandState; 2],
pub high_coef: [Spro24DspEqualizerFrequencyBandState; 2],
}
#[derive(Default, Debug, Copy, Clone, PartialEq)]
pub struct Spro24DspReverbState {
pub size: f32,
pub air: f32,
pub enabled: bool,
pub pre_filter: f32,
}
const COEF_BLOCK_COMP: usize = 2;
const COEF_BLOCK_EQ: usize = 2;
const COEF_BLOCK_REVERB: usize = 3;
impl QuadletConvert for Spro24DspCompressorState {
fn parse(&mut self, quads: &[f32]) {
(0..2).for_each(|ch| {
let base_offset = (COEF_BLOCK_COMP + ch) * COEF_BLOCK_SIZE;
let pos = (base_offset + COMP_OUTPUT_OFFSET) / 4;
self.output[ch] = quads[pos];
let pos = (base_offset + COMP_THRESHOLD_OFFSET) / 4;
self.threshold[ch] = quads[pos];
let pos = (base_offset + COMP_RATIO_OFFSET) / 4;
self.ratio[ch] = quads[pos];
let pos = (base_offset + COMP_ATTACK_OFFSET) / 4;
self.attack[ch] = quads[pos];
let pos = (base_offset + COMP_RELEASE_OFFSET) / 4;
self.release[ch] = quads[pos];
});
}
fn build(&self, quads: &mut [f32]) {
(0..COEF_BLOCK_COUNT).for_each(|i| {
let ch = i % 2;
let base_offset = i * COEF_BLOCK_SIZE;
let pos = (base_offset + COMP_OUTPUT_OFFSET) / 4;
quads[pos] = self.output[ch];
let pos = (base_offset + COMP_THRESHOLD_OFFSET) / 4;
quads[pos] = self.threshold[ch];
let pos = (base_offset + COMP_RATIO_OFFSET) / 4;
quads[pos] = self.ratio[ch];
let pos = (base_offset + COMP_ATTACK_OFFSET) / 4;
quads[pos] = self.attack[ch];
let pos = (base_offset + COMP_RELEASE_OFFSET) / 4;
quads[pos] = self.release[ch];
});
}
}
impl QuadletConvert for Spro24DspEqualizerState {
fn parse(&mut self, quads: &[f32]) {
(0..2).for_each(|ch| {
let base_offset = (COEF_BLOCK_EQ + ch) * COEF_BLOCK_SIZE;
let pos = (base_offset + EQ_OUTPUT_OFFSET) / 4;
self.output[ch] = quads[pos];
let pos = (base_offset + EQ_LOW_FREQ_OFFSET) / 4;
self.low_coef[ch]
.0
.copy_from_slice(&quads[pos..(pos + EQ_COEF_COUNT)]);
let pos = (base_offset + EQ_LOW_MIDDLE_FREQ_OFFSET) / 4;
self.low_middle_coef[ch]
.0
.copy_from_slice(&quads[pos..(pos + EQ_COEF_COUNT)]);
let pos = (base_offset + EQ_HIGH_MIDDLE_FREQ_OFFSET) / 4;
self.high_middle_coef[ch]
.0
.copy_from_slice(&quads[pos..(pos + EQ_COEF_COUNT)]);
let pos = (base_offset + EQ_HIGH_FREQ_OFFSET) / 4;
self.high_coef[ch]
.0
.copy_from_slice(&quads[pos..(pos + EQ_COEF_COUNT)]);
});
}
fn build(&self, quads: &mut [f32]) {
(0..COEF_BLOCK_COUNT).for_each(|i| {
let ch = i % 2;
let base_offset = (COEF_BLOCK_EQ + ch) * COEF_BLOCK_SIZE;
let pos = (base_offset + EQ_OUTPUT_OFFSET) / 4;
quads[pos] = self.output[ch];
let pos = (base_offset + EQ_LOW_FREQ_OFFSET) / 4;
quads[pos..(pos + EQ_COEF_COUNT)].copy_from_slice(&self.low_coef[ch].0);
let pos = (base_offset + EQ_LOW_MIDDLE_FREQ_OFFSET) / 4;
quads[pos..(pos + EQ_COEF_COUNT)].copy_from_slice(&self.low_middle_coef[ch].0);
let pos = (base_offset + EQ_HIGH_MIDDLE_FREQ_OFFSET) / 4;
quads[pos..(pos + EQ_COEF_COUNT)].copy_from_slice(&self.high_middle_coef[ch].0);
let pos = (base_offset + EQ_HIGH_FREQ_OFFSET) / 4;
quads[pos..(pos + EQ_COEF_COUNT)].copy_from_slice(&self.high_coef[ch].0);
});
}
}
impl QuadletConvert for Spro24DspReverbState {
fn parse(&mut self, quads: &[f32]) {
let base_offset = COEF_BLOCK_REVERB * COEF_BLOCK_SIZE;
let pos = (base_offset + REVERB_SIZE_OFFSET) / 4;
self.size = quads[pos];
let pos = (base_offset + REVERB_AIR_OFFSET) / 4;
self.air = quads[pos];
let pos = (base_offset + REVERB_ENABLE_OFFSET) / 4;
self.enabled = quads[pos] > 0.0;
let pos = (base_offset + REVERB_PRE_FILTER_VALUE_OFFSET) / 4;
let mut val = quads[pos];
let pos = (base_offset + REVERB_PRE_FILTER_SIGN_OFFSET) / 4;
if quads[pos] == 0.0 {
val *= -1.0;
}
self.pre_filter = val;
}
fn build(&self, quads: &mut [f32]) {
(0..COEF_BLOCK_COUNT).for_each(|i| {
let base_offset = i * COEF_BLOCK_SIZE;
let pos = (base_offset + REVERB_SIZE_OFFSET) / 4;
quads[pos] = self.size;
let pos = (base_offset + REVERB_AIR_OFFSET) / 4;
quads[pos] = self.air;
let enable_pos = (base_offset + REVERB_ENABLE_OFFSET) / 4;
let disable_pos = (base_offset + REVERB_DISABLE_OFFSET) / 4;
if self.enabled {
quads[enable_pos] = 1.0;
quads[disable_pos] = 0.0
} else {
quads[enable_pos] = 0.0;
quads[disable_pos] = 1.0
}
let mut val = self.pre_filter;
let pos = (base_offset + REVERB_PRE_FILTER_SIGN_OFFSET) / 4;
quads[pos] = if val > 0.0 { 1.0 } else { 0.0 };
if val < 0.0 {
val *= -1.0;
}
let pos = (base_offset + REVERB_PRE_FILTER_VALUE_OFFSET) / 4;
quads[pos] = val;
});
}
}
#[derive(Default, Debug, Copy, Clone, PartialEq)]
pub struct Spro24DspEffectState {
pub eq_after_comp: [bool; 2],
pub comp_enable: [bool; 2],
pub eq_enable: [bool; 2],
pub comp: Spro24DspCompressorState,
pub eq: Spro24DspEqualizerState,
pub reverb: Spro24DspReverbState,
}
impl SPro24DspProtocol {
pub const COMPRESSOR_OUTPUT_MIN: f32 = 0.0;
pub const COMPRESSOR_OUTPUT_MAX: f32 = 64.0;
pub const COMPRESSOR_THRESHOLD_MIN: f32 = -1.25;
pub const COMPRESSOR_THRESHOLD_MAX: f32 = 0.0;
pub const COMPRESSOR_RATIO_MIN: f32 = 0.03125;
pub const COMPRESSOR_RATIO_MAX: f32 = 0.5;
pub const COMPRESSOR_ATTACK_MIN: f32 = -0.9375;
pub const COMPRESSOR_ATTACK_MAX: f32 = -1.0;
pub const COMPRESSOR_RELEASE_MIN: f32 = 0.9375;
pub const COMPRESSOR_RELEASE_MAX: f32 = 1.0;
pub const EQUALIZER_OUTPUT_MIN: f32 = 0.0;
pub const EQUALIZER_OUTPUT_MAX: f32 = 1.0;
pub const REVERB_SIZE_MIN: f32 = 0.0;
pub const REVERB_SIZE_MAX: f32 = 1.0;
pub const REVERB_AIR_MIN: f32 = 0.0;
pub const REVERB_AIR_MAX: f32 = 1.0;
pub const REVERB_PRE_FILTER_MIN: f32 = -1.0;
pub const REVERB_PRE_FILTER_MAX: f32 = 1.0;
pub fn read_effect_state(
req: &mut FwReq,
node: &mut FwNode,
sections: &ExtensionSections,
state: &mut Spro24DspEffectState,
timeout_ms: u32,
) -> Result<(), Error> {
let mut raw = [0; 4];
ApplSectionProtocol::read_appl_data(
req,
node,
sections,
CH_STRIP_FLAG_OFFSET,
&mut raw,
timeout_ms,
)
.map(|_| {
let val = u32::from_be_bytes(raw);
(0..2).for_each(|i| {
let flags = (val >> (i * 16)) as u16;
state.eq_after_comp[i] = flags & CH_STRIP_FLAG_EQ_AFTER_COMP > 0;
state.comp_enable[i] = flags & CH_STRIP_FLAG_COMP_ENABLE > 0;
state.eq_enable[i] = flags & CH_STRIP_FLAG_EQ_ENABLE > 0;
});
})?;
let mut raw = vec![0; COEF_BLOCK_SIZE * COEF_BLOCK_COUNT];
ApplSectionProtocol::read_appl_data(req, node, sections, COEF_OFFSET, &mut raw, timeout_ms)
.map(|_| {
let mut quad = [0; 4];
let quads: Vec<f32> = (0..raw.len())
.step_by(4)
.map(|pos| {
quad.copy_from_slice(&raw[pos..(pos + 4)]);
f32::from_be_bytes(quad)
})
.collect();
state.comp.parse(&quads);
state.eq.parse(&quads);
state.reverb.parse(&quads);
})
}
pub fn write_eq_after_comp(
req: &mut FwReq,
node: &mut FwNode,
sections: &ExtensionSections,
eq_after_comp: &[bool],
state: &mut Spro24DspEffectState,
timeout_ms: u32,
) -> Result<(), Error> {
Self::write_flags(
req,
node,
sections,
eq_after_comp,
&state.comp_enable,
&state.eq_enable,
timeout_ms,
)
.map(|_| state.eq_after_comp.copy_from_slice(eq_after_comp))
}
pub fn write_comp_enable(
req: &mut FwReq,
node: &mut FwNode,
sections: &ExtensionSections,
comp_enable: &[bool],
state: &mut Spro24DspEffectState,
timeout_ms: u32,
) -> Result<(), Error> {
Self::write_flags(
req,
node,
sections,
&state.eq_after_comp,
comp_enable,
&state.eq_enable,
timeout_ms,
)
.map(|_| state.comp_enable.copy_from_slice(comp_enable))
}
pub fn write_eq_enable(
req: &mut FwReq,
node: &mut FwNode,
sections: &ExtensionSections,
eq_enable: &[bool],
state: &mut Spro24DspEffectState,
timeout_ms: u32,
) -> Result<(), Error> {
Self::write_flags(
req,
node,
sections,
&state.eq_after_comp,
&state.comp_enable,
eq_enable,
timeout_ms,
)
.map(|_| state.eq_enable.copy_from_slice(eq_enable))
}
fn write_flags(
req: &mut FwReq,
node: &mut FwNode,
sections: &ExtensionSections,
eq_after_comp: &[bool],
comp_enable: &[bool],
eq_enable: &[bool],
timeout_ms: u32,
) -> Result<(), Error> {
assert_eq!(eq_after_comp.len(), 2);
assert_eq!(comp_enable.len(), 2);
assert_eq!(eq_enable.len(), 2);
let val = (0..2).fold(0u32, |mut val, i| {
let shift = i * 16;
if eq_after_comp[i] {
val |= (CH_STRIP_FLAG_EQ_AFTER_COMP as u32) << shift;
}
if comp_enable[i] {
val |= (CH_STRIP_FLAG_COMP_ENABLE as u32) << shift;
}
if eq_enable[i] {
val |= (CH_STRIP_FLAG_EQ_ENABLE as u32) << shift;
}
val
});
ApplSectionProtocol::write_appl_data(
req,
node,
sections,
CH_STRIP_FLAG_OFFSET,
&mut val.to_be_bytes(),
timeout_ms,
)?;
Self::write_sw_notice(req, node, sections, CH_STRIP_FLAG_SW_NOTICE, timeout_ms)
}
pub fn write_comp_effect(
req: &mut FwReq,
node: &mut FwNode,
sections: &ExtensionSections,
comp: &Spro24DspCompressorState,
state: &mut Spro24DspEffectState,
timeout_ms: u32,
) -> Result<(), Error> {
Self::write_effect(req, node, sections, comp, &state.comp, timeout_ms)?;
Self::write_sw_notice(req, node, sections, COMP_CH0_SW_NOTICE, timeout_ms)?;
Self::write_sw_notice(req, node, sections, COMP_CH1_SW_NOTICE, timeout_ms)?;
state.comp = *comp;
Ok(())
}
pub fn write_eq_effect(
req: &mut FwReq,
node: &mut FwNode,
sections: &ExtensionSections,
eq: &Spro24DspEqualizerState,
state: &mut Spro24DspEffectState,
timeout_ms: u32,
) -> Result<(), Error> {
Self::write_effect(req, node, sections, eq, &state.eq, timeout_ms)?;
Self::write_sw_notice(req, node, sections, EQ_OUTPUT_CH0_SW_NOTICE, timeout_ms)?;
Self::write_sw_notice(req, node, sections, EQ_OUTPUT_CH1_SW_NOTICE, timeout_ms)?;
Self::write_sw_notice(req, node, sections, EQ_LOW_FREQ_CH0_SW_NOTICE, timeout_ms)?;
Self::write_sw_notice(req, node, sections, EQ_LOW_FREQ_CH1_SW_NOTICE, timeout_ms)?;
Self::write_sw_notice(
req,
node,
sections,
EQ_LOW_MIDDLE_FREQ_CH0_SW_NOTICE,
timeout_ms,
)?;
Self::write_sw_notice(
req,
node,
sections,
EQ_LOW_MIDDLE_FREQ_CH1_SW_NOTICE,
timeout_ms,
)?;
Self::write_sw_notice(
req,
node,
sections,
EQ_HIGH_MIDDLE_FREQ_CH0_SW_NOTICE,
timeout_ms,
)?;
Self::write_sw_notice(
req,
node,
sections,
EQ_HIGH_MIDDLE_FREQ_CH1_SW_NOTICE,
timeout_ms,
)?;
Self::write_sw_notice(req, node, sections, EQ_HIGH_FREQ_CH0_SW_NOTICE, timeout_ms)?;
Self::write_sw_notice(req, node, sections, EQ_HIGH_FREQ_CH1_SW_NOTICE, timeout_ms)?;
state.eq = *eq;
Ok(())
}
pub fn write_reverb_effect(
req: &mut FwReq,
node: &mut FwNode,
sections: &ExtensionSections,
reverb: &Spro24DspReverbState,
state: &mut Spro24DspEffectState,
timeout_ms: u32,
) -> Result<(), Error> {
Self::write_effect(req, node, sections, reverb, &state.reverb, timeout_ms)?;
Self::write_sw_notice(req, node, sections, REVERB_SW_NOTICE, timeout_ms)?;
state.reverb = *reverb;
Ok(())
}
fn write_effect<T: QuadletConvert>(
req: &mut FwReq,
node: &mut FwNode,
sections: &ExtensionSections,
new: &T,
old: &T,
timeout_ms: u32,
) -> Result<(), Error> {
let mut new_quads = [0.0; COEF_BLOCK_SIZE * COEF_BLOCK_COUNT];
let mut old_quads = [0.0; COEF_BLOCK_SIZE * COEF_BLOCK_COUNT];
new.build(&mut new_quads);
old.build(&mut old_quads);
new_quads
.iter()
.zip(old_quads)
.enumerate()
.filter(|(_, (n, o))| *n != o)
.try_for_each(|(i, (val, _))| {
let pos = COEF_OFFSET + i * 4;
ApplSectionProtocol::write_appl_data(
req,
node,
sections,
pos,
&mut val.to_be_bytes(),
timeout_ms,
)
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn comp_state() {
let orig = Spro24DspCompressorState {
output: [0.04, 0.05],
threshold: [0.16, 0.17],
ratio: [0.20, 0.21],
attack: [0.32, 0.33],
release: [0.44, 0.45],
};
let mut new_quads = [0.0; COEF_BLOCK_SIZE * COEF_BLOCK_COUNT];
orig.build(&mut new_quads);
let mut curr = Spro24DspCompressorState::default();
curr.parse(&new_quads);
assert_eq!(orig, curr);
}
#[test]
fn eq_state() {
let orig = Spro24DspEqualizerState {
output: [0.06, 0.07],
low_coef: [
Spro24DspEqualizerFrequencyBandState([0.00, 0.01, 0.02, 0.03, 0.04]),
Spro24DspEqualizerFrequencyBandState([0.10, 0.11, 0.12, 0.13, 0.14]),
],
low_middle_coef: [
Spro24DspEqualizerFrequencyBandState([0.20, 0.21, 0.22, 0.23, 0.24]),
Spro24DspEqualizerFrequencyBandState([0.30, 0.31, 0.32, 0.33, 0.34]),
],
high_middle_coef: [
Spro24DspEqualizerFrequencyBandState([0.40, 0.41, 0.42, 0.43, 0.44]),
Spro24DspEqualizerFrequencyBandState([0.50, 0.51, 0.52, 0.53, 0.54]),
],
high_coef: [
Spro24DspEqualizerFrequencyBandState([0.60, 0.61, 0.62, 0.63, 0.64]),
Spro24DspEqualizerFrequencyBandState([0.70, 0.71, 0.72, 0.73, 0.74]),
],
};
let mut new_quads = [0.0; COEF_BLOCK_SIZE * COEF_BLOCK_COUNT];
orig.build(&mut new_quads);
let mut curr = Spro24DspEqualizerState::default();
curr.parse(&new_quads);
assert_eq!(orig, curr);
}
#[test]
fn reverb_state() {
let orig = Spro24DspReverbState {
size: 0.04,
air: 0.14,
enabled: false,
pre_filter: -0.1,
};
let mut new_quads = [0.0; COEF_BLOCK_SIZE * COEF_BLOCK_COUNT];
orig.build(&mut new_quads);
let mut curr = Spro24DspReverbState::default();
curr.parse(&new_quads);
assert_eq!(orig, curr);
}
}