use crate::parse_error::*;
use crate::util::*;
use alloc::vec::Vec;
use bstr::BString;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FileReferenceMsg {
Open {
ctx: u16,
file_type: FileReferenceType,
url: BString,
},
SelectContents {
ctx: u16,
map: SelectMap,
},
OpenSelectContents {
ctx: u16,
file_type: FileReferenceType,
url: BString,
map: SelectMap,
},
Close {
ctx: u16,
},
}
impl FileReferenceMsg {
pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
match self {
Self::Open {
ctx,
file_type,
url,
} => {
push_u14(*ctx, v);
let len = 4 + url.len().min(260) + 1;
push_u14(len as u16, v);
file_type.extend_midi(v);
v.extend_from_slice(&url[0..url.len().min(260)]);
v.push(0); }
Self::SelectContents { ctx, map } => {
push_u14(*ctx, v);
push_u14(map.len() as u16, v);
map.extend_midi(v);
}
Self::OpenSelectContents {
ctx,
file_type,
url,
map,
} => {
push_u14(*ctx, v);
let len = 4 + url.len().min(260) + 1 + map.len();
push_u14(len as u16, v);
file_type.extend_midi(v);
v.extend_from_slice(&url[0..url.len().min(260)]);
v.push(0); map.extend_midi(v);
}
Self::Close { ctx } => {
push_u14(*ctx, v);
v.push(0); v.push(0); }
}
}
#[allow(dead_code)]
pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
Err(ParseError::NotImplemented("FileReferenceMsg"))
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FileReferenceType {
DLS,
SF2,
WAV,
}
impl FileReferenceType {
fn extend_midi(&self, v: &mut Vec<u8>) {
match self {
Self::DLS => b"DLS ".iter().for_each(|c| v.push(*c)),
Self::SF2 => b"SF2 ".iter().for_each(|c| v.push(*c)),
Self::WAV => b"WAV ".iter().for_each(|c| v.push(*c)),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct SoundFileMap {
pub dst_bank: u16,
pub dst_prog: u8,
pub src_bank: u16,
pub src_prog: u8,
pub src_drum: bool,
pub dst_drum: bool,
pub volume: u8,
}
impl Default for SoundFileMap {
fn default() -> Self {
Self {
dst_bank: 0,
dst_prog: 0,
src_bank: 0,
src_prog: 0,
src_drum: false,
dst_drum: false,
volume: 0x7F,
}
}
}
impl SoundFileMap {
fn extend_midi(&self, v: &mut Vec<u8>) {
push_u14(self.dst_bank, v);
push_u7(self.dst_prog, v);
push_u14(self.src_bank, v);
push_u7(self.src_prog, v);
let mut flags: u8 = 0;
if self.src_drum {
flags += 1 << 0;
}
if self.dst_drum {
flags += 1 << 1;
}
v.push(flags);
push_u7(self.volume, v);
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct WAVMap {
pub dst_bank: u16,
pub dst_prog: u8,
pub base: u8,
pub lokey: u8,
pub hikey: u8,
pub fine: i16,
pub volume: u8,
}
impl WAVMap {
fn extend_midi(&self, v: &mut Vec<u8>) {
push_u14(self.dst_bank, v);
push_u7(self.dst_prog, v);
push_u7(self.base, v);
push_u7(self.lokey, v);
push_u7(self.hikey, v);
let [msb, lsb] = i_to_u14(self.fine);
v.push(lsb);
v.push(msb);
push_u7(self.volume, v);
}
}
impl Default for WAVMap {
fn default() -> Self {
Self {
dst_bank: 0,
dst_prog: 0,
base: 60,
lokey: 0,
hikey: 0x7F,
fine: 0,
volume: 0x7F,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SelectMap {
SoundFile(Vec<SoundFileMap>),
WAV(WAVMap),
SoundFileBankOffset {
bank_offset: u16,
src_drum: bool,
},
WAVBankOffset {
map: WAVMap,
bank_offset: u16,
src_drum: bool,
},
}
impl SelectMap {
fn extend_midi(&self, v: &mut Vec<u8>) {
match self {
Self::WAV(m) => m.extend_midi(v),
Self::WAVBankOffset {
map,
bank_offset,
src_drum,
} => {
map.extend_midi(v);
v.push(0); v.push(0); v.push(1); v.push(3); push_u14(*bank_offset, v);
let mut flags: u8 = 0;
if *src_drum {
flags += 1 << 0;
}
push_u7(flags, v);
}
Self::SoundFileBankOffset {
bank_offset,
src_drum,
} => {
v.push(0); v.push(0); v.push(1); v.push(3); push_u14(*bank_offset, v);
let mut flags: u8 = 0;
if *src_drum {
flags += 1 << 0;
}
push_u7(flags, v);
}
Self::SoundFile(maps) => {
let count = maps.len().min(127);
push_u7(count as u8, v);
for m in maps[0..count].iter() {
m.extend_midi(v);
}
}
}
}
fn len(&self) -> usize {
match self {
Self::WAV(_) => 9,
Self::WAVBankOffset { .. } => 9 + 6,
Self::SoundFileBankOffset { .. } => 7,
Self::SoundFile(maps) => {
let count = maps.len().min(127);
1 + count * 8
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::*;
use alloc::vec;
#[test]
fn serialize_sample_dump_msg() {
assert_eq!(
MidiMsg::SystemExclusive {
msg: SystemExclusiveMsg::UniversalNonRealTime {
device: DeviceID::AllCall,
msg: UniversalNonRealTimeMsg::FileReference(
FileReferenceMsg::OpenSelectContents {
ctx: 44,
file_type: FileReferenceType::DLS,
url: BString::from("file://foo.dls"),
map: SelectMap::SoundFile(vec![SoundFileMap {
dst_bank: 1 << 10,
src_prog: 1,
..Default::default()
}]),
}
),
},
}
.to_midi(),
vec![
0xF0, 0x7E, 0x7F, 0x0B, 0x03, 44, 00, 28, 0, b"D"[0], b"L"[0], b"S"[0], b" "[0], b"f"[0], b"i"[0], b"l"[0], b"e"[0], b":"[0], b"/"[0], b"/"[0], b"f"[0], b"o"[0],
b"o"[0], b"."[0], b"d"[0], b"l"[0], b"s"[0], 0, 1, 0, 8, 0, 0, 0, 1, 0, 0x7f, 0xF7
]
);
}
}