use crate::parse_error::*;
use crate::util::*;
use ascii::{AsciiChar, AsciiString};
#[derive(Debug, Clone, PartialEq)]
pub enum FileReferenceMsg {
Open {
ctx: u16,
file_type: FileReferenceType,
url: AsciiString,
},
SelectContents {
ctx: u16,
map: SelectMap,
},
OpenSelectContents {
ctx: u16,
file_type: FileReferenceType,
url: AsciiString,
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.as_bytes()[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.as_bytes()[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::Invalid(format!("TODO: Not implemented")))
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FileReferenceType {
DLS,
SF2,
WAV,
}
impl FileReferenceType {
fn extend_midi(&self, v: &mut Vec<u8>) {
match self {
Self::DLS => {
v.push(AsciiChar::D.as_byte());
v.push(AsciiChar::L.as_byte());
v.push(AsciiChar::S.as_byte());
v.push(AsciiChar::Space.as_byte());
}
Self::SF2 => {
v.push(AsciiChar::S.as_byte());
v.push(AsciiChar::F.as_byte());
v.push(AsciiChar::_2.as_byte());
v.push(AsciiChar::Space.as_byte());
}
Self::WAV => {
v.push(AsciiChar::W.as_byte());
v.push(AsciiChar::A.as_byte());
v.push(AsciiChar::V.as_byte());
v.push(AsciiChar::Space.as_byte());
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
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)]
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)]
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::*;
#[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: AsciiString::from_ascii("file://foo.dls").unwrap(),
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,
AsciiChar::D.as_byte(),
AsciiChar::L.as_byte(),
AsciiChar::S.as_byte(),
AsciiChar::Space.as_byte(),
AsciiChar::f.as_byte(),
AsciiChar::i.as_byte(),
AsciiChar::l.as_byte(),
AsciiChar::e.as_byte(),
AsciiChar::Colon.as_byte(),
AsciiChar::Slash.as_byte(),
AsciiChar::Slash.as_byte(),
AsciiChar::f.as_byte(),
AsciiChar::o.as_byte(),
AsciiChar::o.as_byte(),
AsciiChar::Dot.as_byte(),
AsciiChar::d.as_byte(),
AsciiChar::l.as_byte(),
AsciiChar::s.as_byte(),
0,
1,
0,
8,
0,
0,
0,
1,
0,
0x7f,
0xF7
]
);
}
}