use crate::param::{
PairMode, PartialParam, PartialType, RhythmKey, TimbreParam, TvaParam,
TvfParam, TvpParam,
};
use crate::rom::{Meta, PCM_SAMPLES, PcmMeta, RawTimbreBank, Rom};
const PCM_BYTES: usize = PCM_SAMPLES * 2;
const RAW_TIMBRES_BYTES: usize = 256 * 256;
#[repr(C, align(2))]
struct AlignedPcm([u8; PCM_BYTES]);
static PCM_ALIGNED: AlignedPcm =
AlignedPcm(*include_bytes!(concat!(env!("OUT_DIR"), "/pcm.bin")));
static PCM: &[i16; PCM_SAMPLES] =
unsafe { &*(PCM_ALIGNED.0.as_ptr() as *const [i16; PCM_SAMPLES]) };
#[repr(C)]
struct RawTimbresBytes([u8; RAW_TIMBRES_BYTES]);
static RAW_TIMBRES_BYTES_ALIGNED: RawTimbresBytes = RawTimbresBytes(
*include_bytes!(concat!(env!("OUT_DIR"), "/raw_timbres.bin")),
);
static RAW_TIMBRES: &RawTimbreBank =
unsafe { &*(RAW_TIMBRES_BYTES_ALIGNED.0.as_ptr() as *const RawTimbreBank) };
include!(concat!(env!("OUT_DIR"), "/rom_static.rs"));
static BUNDLED_META: Meta = Meta {
pcm_metas: PCM_METAS,
rhythm_keys: RHYTHM_KEYS,
rhythm_timbres: RHYTHM_TIMBRES,
melodic_timbres: MELODIC_TIMBRES,
default_programs: DEFAULT_PROGRAMS,
default_panpots: DEFAULT_PANPOTS,
patch_max_table: PATCH_MAX_TABLE,
rhythm_max_table: RHYTHM_MAX_TABLE,
system_max_table: SYSTEM_MAX_TABLE,
timbre_max_table: TIMBRE_MAX_TABLE,
reserve_settings: RESERVE_SETTINGS,
};
pub const fn bundled_rom() -> Rom {
Rom::from_static(&BUNDLED_META, PCM, RAW_TIMBRES)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::param::RHYTHM_KEYS_COUNT;
use crate::rom::{ControlArray, Error, PCM_META_COUNT, PcmArray};
const CTRL: &ControlArray = include_bytes!("../rom/CM32L_CONTROL.ROM");
const PCM_RAW: &PcmArray = include_bytes!("../rom/CM32L_PCM.ROM");
#[test]
fn test_small_rom() {
const TOO_SMALL: &[u8] = &[0; 1024];
let result = Rom::new(CTRL, TOO_SMALL);
assert_eq!(result.unwrap_err(), Error::WrongLength);
let result = Rom::new(TOO_SMALL, PCM_RAW);
assert_eq!(result.unwrap_err(), Error::WrongLength);
let result = Rom::new(TOO_SMALL, TOO_SMALL);
assert_eq!(result.unwrap_err(), Error::WrongLength);
}
#[test]
fn test_new() {
Rom::new(CTRL, PCM_RAW).unwrap();
}
#[test]
fn test_bundled_rom() {
let rom = Rom::bundled();
assert_eq!(rom.pcm().len(), PCM_SAMPLES);
assert_eq!(rom.meta().pcm_metas.len(), PCM_META_COUNT);
}
#[test]
fn test_bundled_matches_runtime() {
let runtime = Rom::new(CTRL, PCM_RAW).unwrap();
assert_eq!(runtime, Rom::bundled());
}
#[test]
fn test_pcm_metas_parsed() {
let rom = Rom::bundled();
let meta = &rom.meta().pcm_metas[0];
assert!(meta.len > 0, "PCM metadata 0 should have non-zero length");
assert!(
meta.addr < PCM_SAMPLES,
"PCM metadata 0 address should be within PCM ROM"
);
}
#[test]
fn test_rhythm_keys_parsed() {
let rom = Rom::bundled();
for i in 0..RHYTHM_KEYS_COUNT {
let setting = &rom.meta().rhythm_keys[i];
assert!(
setting.timbre <= 127,
"Timbre {} out of range at index {}",
setting.timbre,
i
);
assert!(
setting.level <= 100,
"Output level {} out of range at index {}",
setting.level,
i
);
assert!(
setting.panpot <= 14,
"Panpot {} out of range at index {}",
setting.panpot,
i
);
}
}
#[test]
fn test_cowbell_rhythm_setting() {
let rom = Rom::bundled();
let cowbell_key = 56;
let rhythm_key_start = 24;
let rhythm_idx = cowbell_key - rhythm_key_start;
let setting = &rom.meta().rhythm_keys[rhythm_idx];
assert!(setting.timbre < 128, "Cowbell timbre should be valid");
assert!(
setting.level > 0,
"Cowbell should have non-zero output level"
);
}
}