use crate::rom::Rom;
use crate::{CM32L, ControlCommand, ControlError, Frame, Synth};
use alloc::{vec, vec::Vec};
const PROGRAM_MAP: [u8; 128] = [
0, 1, 2, 3, 4, 5, 7, 19, 22, 101, 99, 97, 104, 103, 102, 100, 8, 9, 10, 12, 13, 15, 87, 11, 59, 60, 61, 62, 61, 62, 62, 62, 64, 66, 67, 70, 68, 69, 28, 30, 52, 53, 54, 56, 48, 51, 57, 112, 48, 49, 50, 50, 34, 39, 34, 122, 88, 90, 94, 88, 92, 95, 24, 26, 78, 79, 80, 81, 84, 85, 86, 82, 74, 72, 76, 77, 110, 107, 108, 77, 47, 47, 42, 24, 26, 34, 32, 24, 36, 38, 33, 34, 35, 37, 36, 32, 41, 36, 35, 37, 38, 32, 43, 36, 63, 59, 107, 105, 112, 77, 52, 106, 112, 117, 113, 113, 117, 113, 114, 119, 62, 111, 36, 124, 123, 122, 36, 96, ];
const DRUM_MAP: [u8; 128] = {
let mut m = [0u8; 128];
let mut i = 0;
while i < 128 {
m[i] = i as u8;
i += 1;
}
m[52] = 49;
m[53] = 51;
m[55] = 49;
m[57] = 49;
m[58] = 73;
m[59] = 51;
m[74] = 73;
m[76] = 75;
m[77] = 75;
m[79] = 78;
m[80] = 121;
m[81] = 121;
m
};
fn build_sysex(addr: &[u8; 3], data: &[u8]) -> Vec<u8> {
let mut msg = vec![0xF0, 0x41, 0x10, 0x16, 0x12];
msg.extend_from_slice(addr);
msg.extend_from_slice(data);
let mut sum = 0u8;
for &b in &msg[5..] {
sum = sum.wrapping_add(b);
}
let checksum = (128 - (sum & 0x7F)) & 0x7F;
msg.push(checksum);
msg.push(0xF7);
msg
}
fn translate_msg(msg: u32) -> Option<u32> {
let status = msg & 0xF0;
let channel = msg & 0x0F;
match status {
0xC0 => {
let program = (msg >> 8) & 0x7F;
let mapped = PROGRAM_MAP[program as usize];
Some(0xC0 | channel | ((mapped as u32) << 8))
}
0xB0 => {
let cc = (msg >> 8) & 0x7F;
if cc == 10 {
let value = (msg >> 16) & 0x7F;
let reversed = 127 - value;
Some((msg & 0xFF00FF) | (reversed << 16))
} else {
Some(msg)
}
}
0x90 | 0x80 if channel == 9 => {
let note = (msg >> 8) & 0x7F;
let mapped = DRUM_MAP[note as usize];
let rest = msg & 0xFF0000FF;
Some(rest | ((mapped as u32) << 8))
}
_ => Some(msg),
}
}
fn is_device_reset(sysex: &[u8]) -> bool {
if sysex.len() < 8 {
return false;
}
if sysex[0] != 0xF0 || sysex[sysex.len() - 1] != 0xF7 {
return false;
}
if sysex[1] != 0x41 || sysex[2] > 0x10 || sysex[3] != 0x16 {
return false;
}
if sysex[4] != 0x12 || sysex[5] != 0x7F {
return false;
}
let mut sum = 0u8;
for i in 5..sysex.len() - 2 {
sum = sum.wrapping_add(sysex[i]);
}
let expected = (128 - (sum & 0x7F)) & 0x7F;
sysex[sysex.len() - 2] == expected
}
#[non_exhaustive]
#[derive(Debug)]
pub struct GmSynth {
inner: CM32L,
}
impl GmSynth {
pub fn new(rom: Rom) -> GmSynth {
let mut s = GmSynth {
inner: CM32L::new(rom),
};
s.apply_gm_defaults();
s
}
fn apply_gm_defaults(&mut self) {
let sysex =
build_sysex(&[0x10, 0x00, 0x0D], &[0, 1, 2, 3, 4, 5, 6, 7, 9]);
self.inner.play_sysex(&sysex);
for part in 0u8..9 {
let base = 0x030000u32 + part as u32 * 16 + 4;
let a0 = ((base >> 16) & 0x7F) as u8;
let a1 = ((base >> 8) & 0x7F) as u8;
let a2 = (base & 0x7F) as u8;
let sysex = build_sysex(&[a0, a1, a2], &[2]);
self.inner.play_sysex(&sysex);
}
}
fn apply_gm_defaults_at(&mut self, time: u32) {
let sysex =
build_sysex(&[0x10, 0x00, 0x0D], &[0, 1, 2, 3, 4, 5, 6, 7, 9]);
self.inner.play_sysex_at(&sysex, time);
for part in 0u8..9 {
let base = 0x030000u32 + part as u32 * 16 + 4;
let a0 = ((base >> 16) & 0x7F) as u8;
let a1 = ((base >> 8) & 0x7F) as u8;
let a2 = (base & 0x7F) as u8;
let sysex = build_sysex(&[a0, a1, a2], &[2]);
self.inner.play_sysex_at(&sysex, time);
}
}
}
impl Synth for GmSynth {
fn current_time(&self) -> u32 {
self.inner.current_time()
}
fn play_msg_at(&mut self, msg: u32, time: u32) -> bool {
match translate_msg(msg) {
Some(m) => self.inner.play_msg_at(m, time),
None => true,
}
}
fn play_sysex_at(&mut self, sysex: &[u8], time: u32) -> bool {
let ok = self.inner.play_sysex_at(sysex, time);
if is_device_reset(sysex) {
self.apply_gm_defaults_at(time);
}
ok
}
fn render(&mut self, out: &mut [Frame]) {
self.inner.render(out);
}
fn apply_command(
&mut self,
command: ControlCommand,
) -> Result<(), ControlError> {
let r = self.inner.apply_command(command);
if matches!(command, ControlCommand::Reset) && r.is_ok() {
self.apply_gm_defaults();
}
r
}
}