pub mod chunk;
pub mod reader;
pub mod writer;
use chunk::{header::HeaderChunk, track::TrackChunk, ChunkParseError, ParsedChunk};
use reader::MidiStream;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use writer::MidiWriteable;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct RawMidi {
pub chunks: Vec<ParsedChunk>,
}
impl RawMidi {
pub fn try_from_midi_stream<STREAM>(stream: STREAM) -> Result<Self, ChunkParseError>
where
STREAM: MidiStream,
{
Self::try_from(StreamWrapper(stream))
}
pub fn check_into_midi(self) -> Result<Midi, MidiSanitizerError> {
self.try_into()
}
}
impl MidiWriteable for RawMidi {
fn to_midi_bytes(self) -> Vec<u8> {
let mut res = vec![];
for chunk in self.chunks {
res.extend(chunk.to_midi_bytes());
}
res
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Midi {
pub header: HeaderChunk,
pub tracks: Vec<TrackChunk>,
}
impl MidiWriteable for Midi {
fn to_midi_bytes(self) -> Vec<u8> {
let mut res = vec![];
res.extend(ParsedChunk::Header(self.header).to_midi_bytes());
for track in self.tracks {
let wrapped = ParsedChunk::Track(track);
res.extend(wrapped.to_midi_bytes());
}
res
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MidiSanitizerError {
NoStartHeader,
TooManyHeaders,
NoChunks,
}
impl core::error::Error for MidiSanitizerError {}
impl core::fmt::Display for MidiSanitizerError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::NoStartHeader => write![f, "First ParsedChunk in sequence isn't a header"],
Self::TooManyHeaders => write![f, "More than one header chunk identified"],
Self::NoChunks => write![f, "No chunks present"],
}
}
}
impl TryFrom<RawMidi> for Midi {
type Error = MidiSanitizerError;
fn try_from(value: RawMidi) -> Result<Self, Self::Error> {
let mut chunks = value.chunks.into_iter();
let first = chunks.next().ok_or(MidiSanitizerError::NoChunks)?;
let header = match first {
ParsedChunk::Header(header) => header,
_ => return Err(MidiSanitizerError::NoStartHeader),
};
let mut tracks = vec![];
for track in chunks {
match track {
ParsedChunk::Track(track) => tracks.push(track),
_ => return Err(MidiSanitizerError::TooManyHeaders),
}
}
Ok(Self { header, tracks })
}
}
pub struct StreamWrapper<STREAM>(STREAM)
where
STREAM: MidiStream;
impl<STREAM> TryFrom<StreamWrapper<STREAM>> for RawMidi
where
STREAM: MidiStream,
{
type Error = ChunkParseError;
fn try_from(value: StreamWrapper<STREAM>) -> Result<Self, Self::Error> {
let mut data = value.0;
let mut chunks = vec![];
while let Some(parsed) = data.read_chunk_data_pair().map(ParsedChunk::try_from) {
let parsed = parsed?;
chunks.push(parsed);
}
Ok(Self { chunks })
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Chunk {
pub chunk_type: [char; 4],
length: u32,
}
impl Chunk {
pub fn len(&self) -> usize {
self.length as usize
}
pub fn is_empty(&self) -> bool {
self.length == 0
}
}
impl From<u64> for Chunk {
fn from(value: u64) -> Self {
let high = (value >> 32) as u32;
let low = value as u32;
let a = (high >> 24) as u8 as char;
let b = (high >> 16) as u8 as char;
let c = (high >> 8) as u8 as char;
let d = high as u8 as char;
Self {
chunk_type: [a, b, c, d],
length: low,
}
}
}
#[cfg(test)]
mod tests {
use crate::Chunk;
#[test]
fn chunk_from_raw_u64_behaves_normally() {
let message = 0x74657374_0000000au64;
let expected = Chunk {
chunk_type: ['t', 'e', 's', 't'],
length: 10,
};
assert_eq!(expected, message.into())
}
}