use crate::{cast, AifcError, AifcResult, MarkerId, Write};
fn read_u16_from_pos(data: &[u8], pos: &mut usize) -> AifcResult<u16> {
let Some(pos_end) = pos.checked_add(2) else {
return Err(AifcError::StdIoError(crate::unexpectedeof()));
};
if pos_end > data.len() {
return Err(AifcError::StdIoError(crate::unexpectedeof()));
}
let mut buf = [0u8; 2];
buf.copy_from_slice(&data[*pos..pos_end]);
*pos = pos_end;
Ok(u16::from_be_bytes(buf))
}
fn read_i16_from_pos(data: &[u8], pos: &mut usize) -> AifcResult<i16> {
let Some(pos_end) = pos.checked_add(2) else {
return Err(AifcError::StdIoError(crate::unexpectedeof()));
};
if pos_end > data.len() {
return Err(AifcError::StdIoError(crate::unexpectedeof()));
}
let mut buf = [0u8; 2];
buf.copy_from_slice(&data[*pos..pos_end]);
*pos = pos_end;
Ok(i16::from_be_bytes(buf))
}
fn read_u32_from_pos(data: &[u8], pos: &mut usize) -> AifcResult<u32> {
let Some(pos_end) = pos.checked_add(4) else {
return Err(AifcError::StdIoError(crate::unexpectedeof()));
};
if pos_end > data.len() {
return Err(AifcError::StdIoError(crate::unexpectedeof()));
}
let mut buf = [0u8; 4];
buf.copy_from_slice(&data[*pos..pos_end]);
*pos = pos_end;
Ok(u32::from_be_bytes(buf))
}
fn read_pstring_from_pos<'a>(data: &'a [u8], pos: &mut usize) -> AifcResult<&'a [u8]> {
let Some(pos_end) = pos.checked_add(1) else {
return Err(AifcError::StdIoError(crate::unexpectedeof()));
};
if pos_end > data.len() {
return Err(AifcError::StdIoError(crate::unexpectedeof()));
}
let pstr_len = usize::from(data[*pos]);
let Some(pos2_end) = pos_end.checked_add(pstr_len) else {
return Err(AifcError::StdIoError(crate::unexpectedeof()));
};
if pos2_end > data.len() {
return Err(AifcError::StdIoError(crate::unexpectedeof()));
}
let pstr = &data[*pos+1..pos2_end];
*pos = pos2_end;
if crate::is_even_usize(pstr_len) { *pos = pos.checked_add(1).ok_or(AifcError::StdIoError(crate::unexpectedeof()))?;
}
Ok(pstr)
}
#[derive(Debug, Clone, PartialEq)]
pub struct Marker<'a> {
pub id: MarkerId,
pub position: u32,
pub name: &'a [u8],
}
impl Marker<'_> {
pub fn write_chunk_data(write: &mut dyn Write, markers: &[Marker]) -> AifcResult<()> {
let mlen = u16::try_from(markers.len()).map_err(|_| AifcError::SizeTooLarge)?;
write.write_all(&mlen.to_be_bytes())?;
for marker in markers {
write.write_all(&marker.id.to_be_bytes())?;
write.write_all(&marker.position.to_be_bytes())?;
let namelen = u8::try_from(marker.name.len()).map_err(|_| AifcError::SizeTooLarge)?;
write.write_all(&[ namelen ])?;
write.write_all(marker.name)?;
if crate::is_even_usize(marker.name.len()) { write.write_all(&[ 0 ])?;
}
}
Ok(())
}
pub fn chunk_data_size(markers: &[Marker]) -> AifcResult<u32> {
let mut size_calc = SizeCalculator::new();
Marker::write_chunk_data(&mut size_calc, markers)?;
Ok(size_calc.size)
}
}
pub struct Markers<'a> {
slice: &'a [u8],
slice_pos: usize,
num_markers: usize,
}
impl<'a> Markers<'a> {
pub fn new(slice: &'a [u8]) -> AifcResult<Markers<'a>> {
let mut slice_pos: usize = 0;
let num_markers = usize::from(read_u16_from_pos(slice, &mut slice_pos)?);
Ok(Markers {
slice,
slice_pos,
num_markers,
})
}
}
impl<'a> Iterator for Markers<'a> {
type Item = AifcResult<Marker<'a>>;
fn next(&mut self) -> Option<Self::Item> {
if self.num_markers == 0 {
return None;
}
self.num_markers -= 1;
let id = match read_i16_from_pos(self.slice, &mut self.slice_pos) {
Ok(id) => id,
Err(e) => { return Some(Err(e)); }
};
let position = match read_u32_from_pos(self.slice, &mut self.slice_pos) {
Ok(position) => position,
Err(e) => { return Some(Err(e)); }
};
let name = match read_pstring_from_pos(self.slice, &mut self.slice_pos) {
Ok(name) => name,
Err(e) => { return Some(Err(e)); }
};
Some(Ok(Marker { id, position, name }))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.num_markers, Some(self.num_markers))
}
}
impl ExactSizeIterator for Markers<'_> {
}
#[derive(Debug, Clone, PartialEq)]
pub struct Comment<'a> {
pub timestamp: u32,
pub marker_id: MarkerId,
pub text: &'a [u8],
}
impl Comment<'_> {
pub fn unix_timestamp(&self) -> i64 {
i64::from(self.timestamp) - crate::UNIX_TIMESTAMP_OFFSET
}
pub fn set_unix_timestamp(&mut self, unix_timestamp: i64) -> AifcResult<()> {
self.timestamp = unix_timestamp
.checked_add(crate::UNIX_TIMESTAMP_OFFSET)
.and_then(|val| val.try_into().ok())
.ok_or(AifcError::TimestampOutOfBounds)?;
Ok(())
}
pub fn write_chunk_data(write: &mut dyn Write, comments: &[Comment]) -> AifcResult<()> {
let clen = u16::try_from(comments.len()).map_err(|_| AifcError::SizeTooLarge)?;
write.write_all(&(clen).to_be_bytes())?;
for comment in comments {
write.write_all(&comment.timestamp.to_be_bytes())?;
write.write_all(&comment.marker_id.to_be_bytes())?;
let textlen = u16::try_from(comment.text.len())
.map_err(|_| AifcError::SizeTooLarge)?;
write.write_all(&textlen.to_be_bytes())?;
write.write_all(comment.text)?;
if !crate::is_even_usize(comment.text.len()) { write.write_all(&[ 0 ])?;
}
}
Ok(())
}
pub fn chunk_data_size(comments: &[Comment]) -> AifcResult<u32> {
let mut size_calc = SizeCalculator::new();
Comment::write_chunk_data(&mut size_calc, comments)?;
Ok(size_calc.size)
}
}
pub struct Comments<'a> {
slice: &'a [u8],
slice_pos: usize,
num_comments: usize,
}
impl<'a> Comments<'a> {
pub fn new(slice: &'a [u8]) -> AifcResult<Comments<'a>> {
let mut slice_pos: usize = 0;
let num_comments = usize::from(read_u16_from_pos(slice, &mut slice_pos)?);
Ok(Comments {
slice,
slice_pos,
num_comments,
})
}
}
impl<'a> Iterator for Comments<'a> {
type Item = AifcResult<Comment<'a>>;
fn next(&mut self) -> Option<Self::Item> {
if self.num_comments == 0 {
return None;
}
self.num_comments -= 1;
let timestamp = match read_u32_from_pos(self.slice, &mut self.slice_pos) {
Ok(timestamp) => timestamp,
Err(e) => { return Some(Err(e)); }
};
let marker_id = match read_i16_from_pos(self.slice, &mut self.slice_pos) {
Ok(marker_id) => marker_id,
Err(e) => { return Some(Err(e)); }
};
let count = match read_u16_from_pos(self.slice, &mut self.slice_pos) {
Ok(count) => count,
Err(e) => { return Some(Err(e)); }
};
let count = usize::from(count);
let Some(new_slice_pos) = self.slice_pos.checked_add(count) else {
return Some(Err(AifcError::StdIoError(crate::unexpectedeof())));
};
if new_slice_pos > self.slice.len() {
return Some(Err(AifcError::StdIoError(crate::unexpectedeof())));
}
let text = &self.slice[self.slice_pos..new_slice_pos];
self.slice_pos = new_slice_pos;
if !crate::is_even_usize(count) { self.slice_pos = match self.slice_pos.checked_add(1) {
Some(s) => s,
None => { return Some(Err(AifcError::StdIoError(crate::unexpectedeof()))); }
};
}
Some(Ok(Comment { timestamp, marker_id, text }))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.num_comments, Some(self.num_comments))
}
}
impl ExactSizeIterator for Comments<'_> {
}
#[derive(Debug, Clone, PartialEq)]
pub struct Loop {
pub play_mode: i16,
pub begin_loop: MarkerId,
pub end_loop: MarkerId,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Instrument {
pub base_note: i8,
pub detune: i8,
pub low_note: i8,
pub high_note: i8,
pub low_velocity: i8,
pub high_velocity: i8,
pub gain: i16,
pub sustain_loop: Loop,
pub release_loop: Loop
}
impl Instrument {
pub fn from_bytes(slice: &[u8]) -> AifcResult<Instrument> {
if slice.len() < 20 {
return Err(AifcError::StdIoError(crate::unexpectedeof()));
}
Ok(Instrument {
base_note: cast::u8_to_i8(slice[0]),
detune: cast::u8_to_i8(slice[1]),
low_note: cast::u8_to_i8(slice[2]),
high_note: cast::u8_to_i8(slice[3]),
low_velocity: cast::u8_to_i8(slice[4]),
high_velocity: cast::u8_to_i8(slice[5]),
gain: cast::u16_to_i16(u16::from(slice[6]) << 8 | u16::from(slice[7])),
sustain_loop: Loop {
play_mode: cast::u16_to_i16(u16::from(slice[8]) << 8 | u16::from(slice[9])),
begin_loop: cast::u16_to_i16(u16::from(slice[10]) << 8 | u16::from(slice[11])),
end_loop: cast::u16_to_i16(u16::from(slice[12]) << 8 | u16::from(slice[13])),
},
release_loop: Loop {
play_mode: cast::u16_to_i16(u16::from(slice[14]) << 8 | u16::from(slice[15])),
begin_loop: cast::u16_to_i16(u16::from(slice[16]) << 8 | u16::from(slice[17])),
end_loop: cast::u16_to_i16(u16::from(slice[18]) << 8 | u16::from(slice[19])),
}
})
}
fn write_loop(write: &mut dyn Write, iloop: &Loop) -> AifcResult<()> {
write.write_all(&iloop.play_mode.to_be_bytes())?;
write.write_all(&iloop.begin_loop.to_be_bytes())?;
write.write_all(&iloop.end_loop.to_be_bytes())?;
Ok(())
}
pub fn write_chunk_data(&self, write: &mut dyn Write) -> AifcResult<()> {
write.write_all(&[
cast::i8_to_u8(self.base_note),
cast::i8_to_u8(self.detune),
cast::i8_to_u8(self.low_note),
cast::i8_to_u8(self.high_note),
cast::i8_to_u8(self.low_velocity),
cast::i8_to_u8(self.high_velocity),
])?;
write.write_all(&self.gain.to_be_bytes())?;
Instrument::write_loop(write, &self.sustain_loop)?;
Instrument::write_loop(write, &self.release_loop)?;
Ok(())
}
pub fn copy_to_slice(&self, slice: &mut [u8; 20]) {
slice[0] = cast::i8_to_u8(self.base_note);
slice[1] = cast::i8_to_u8(self.detune);
slice[2] = cast::i8_to_u8(self.low_note);
slice[3] = cast::i8_to_u8(self.high_note);
slice[4] = cast::i8_to_u8(self.low_velocity);
slice[5] = cast::i8_to_u8(self.high_velocity);
let buf = self.gain.to_be_bytes();
slice[6] = buf[0];
slice[7] = buf[1];
let buf = self.sustain_loop.play_mode.to_be_bytes();
slice[8] = buf[0];
slice[9] = buf[1];
let buf = self.sustain_loop.begin_loop.to_be_bytes();
slice[10] = buf[0];
slice[11] = buf[1];
let buf = self.sustain_loop.end_loop.to_be_bytes();
slice[12] = buf[0];
slice[13] = buf[1];
let buf = self.release_loop.play_mode.to_be_bytes();
slice[14] = buf[0];
slice[15] = buf[1];
let buf = self.release_loop.begin_loop.to_be_bytes();
slice[16] = buf[0];
slice[17] = buf[1];
let buf = self.release_loop.end_loop.to_be_bytes();
slice[18] = buf[0];
slice[19] = buf[1];
}
}
struct SizeCalculator {
size: u32
}
impl SizeCalculator {
pub fn new() -> SizeCalculator {
SizeCalculator { size: 0 }
}
}
impl Write for SizeCalculator {
fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
let Ok(buflen_u32) = u32::try_from(buf.len()) else {
return Err(std::io::Error::from(std::io::ErrorKind::Other));
};
self.size = match self.size.checked_add(buflen_u32) {
Some(s) => s,
None => {
return Err(std::io::Error::from(std::io::ErrorKind::Other));
}
};
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), std::io::Error> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_instrument_from_bytes() -> AifcResult<()> {
assert_eq!(Instrument::from_bytes(&[
50, 250, 20, 120, 40, 80, 1, 5, 0, 1, 0, 2, 0, 3, 0, 0, 0, 7, 0, 9
])?, Instrument {
base_note: 50,
detune: -6,
low_note: 20,
high_note: 120,
low_velocity: 40,
high_velocity: 80,
gain: 261,
sustain_loop: Loop { play_mode: 1, begin_loop: 2, end_loop: 3 },
release_loop: Loop { play_mode: 0, begin_loop: 7, end_loop: 9 },
});
Ok(())
}
#[test]
fn test_instrument_copy_to_slice() {
let mut bytes = [0; 20];
Instrument {
base_note: 53,
detune: -7,
low_note: 21,
high_note: 121,
low_velocity: 10,
high_velocity: 70,
gain: 258,
sustain_loop: Loop { play_mode: 0, begin_loop: 3, end_loop: 9 },
release_loop: Loop { play_mode: 1, begin_loop: 2, end_loop: 3 },
}.copy_to_slice(&mut bytes);
assert_eq!(bytes, [ 53, 249, 21, 121, 10, 70, 1, 2, 0, 0, 0, 3, 0, 9, 0, 1, 0, 2, 0, 3 ]);
}
}