use crate::byte_iter::ByteIter;
use crate::core::vlq::Vlq;
use crate::core::{Channel, Clocks, DurationName, PortValue};
use crate::error::{self, LibResult};
use crate::scribe::Scribe;
use crate::{Result, Text};
use snafu::{ensure, OptionExt, ResultExt};
use std::convert::TryFrom;
use std::io::{Read, Write};
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash, Default)]
pub enum MetaEvent {
SequenceNumber,
OtherText(Text),
Copyright(Text),
TrackName(Text),
InstrumentName(Text),
Lyric(Text),
Marker(Text),
CuePoint(Text),
ProgramName(Text),
DeviceName(Text),
MidiChannelPrefix(Channel),
#[default]
EndOfTrack,
SetTempo(MicrosecondsPerQuarter),
SmpteOffset(SmpteOffsetValue),
TimeSignature(TimeSignatureValue),
KeySignature(KeySignatureValue),
Sequencer,
Port(PortValue),
}
impl MetaEvent {
pub(crate) fn parse<R: Read>(iter: &mut ByteIter<R>) -> LibResult<Self> {
iter.read_expect(0xff).context(io!())?;
let meta_type_byte = iter.read_or_die().context(io!())?;
match meta_type_byte {
META_SEQUENCE_NUM => {
noimpl!("Sequence Number: https://github.com/webern/midi_file/issues/8")
}
META_TEXT..=META_DEVICE_NAME => MetaEvent::parse_text(iter),
META_CHAN_PREFIX => {
iter.read_expect(LEN_META_CHAN_PREFIX).context(io!())?;
Ok(MetaEvent::MidiChannelPrefix(Channel::new(
iter.read_or_die().context(io!())?,
)))
}
META_END_OF_TRACK => Ok(MetaEvent::parse_end_of_track(iter)?),
META_SET_TEMPO => Ok(MetaEvent::SetTempo(MicrosecondsPerQuarter::parse(iter)?)),
META_SMTPE_OFFSET => Ok(MetaEvent::SmpteOffset(SmpteOffsetValue::parse(iter)?)),
META_TIME_SIG => Ok(MetaEvent::TimeSignature(TimeSignatureValue::parse(iter)?)),
META_KEY_SIG => Ok(MetaEvent::KeySignature(KeySignatureValue::parse(iter)?)),
META_SEQ_SPECIFIC => {
noimpl!("Sequencer-Specific: https://github.com/webern/midi_file/issues/9")
}
META_PORT => Ok(MetaEvent::Port(PortValue::new({
iter.read_expect(1).context(io!())?;
iter.read_or_die().context(io!())?
}))),
_ => invalid_file!("unrecognized byte {:#04X}", meta_type_byte),
}
}
pub(crate) fn write<W: Write>(&self, w: &mut Scribe<W>) -> LibResult<()> {
w.write_all(&[0xff]).context(wr!())?;
match self {
MetaEvent::SequenceNumber => {
noimpl!("Sequence Number: https://github.com/webern/midi_file/issues/8")
}
MetaEvent::OtherText(s) => write_text(w, 0x01, s),
MetaEvent::Copyright(s) => write_text(w, 0x02, s),
MetaEvent::TrackName(s) => write_text(w, 0x03, s),
MetaEvent::InstrumentName(s) => write_text(w, 0x04, s),
MetaEvent::Lyric(s) => write_text(w, 0x05, s),
MetaEvent::Marker(s) => write_text(w, 0x06, s),
MetaEvent::CuePoint(s) => write_text(w, 0x07, s),
MetaEvent::ProgramName(s) => write_text(w, 0x08, s),
MetaEvent::DeviceName(s) => write_text(w, 0x09, s),
MetaEvent::MidiChannelPrefix(channel) => {
write_u8!(w, META_CHAN_PREFIX)?;
write_u8!(w, LEN_META_CHAN_PREFIX)?;
write_u8!(w, channel.get())
}
MetaEvent::EndOfTrack => {
write_u8!(w, META_END_OF_TRACK)?;
write_u8!(w, LEN_META_END_OF_TRACK)?;
Ok(())
}
MetaEvent::SetTempo(value) => {
write_u8!(w, META_SET_TEMPO)?;
write_u8!(w, LEN_META_SET_TEMPO)?;
let bytes = u32::to_be_bytes(value.get());
debug_assert_eq!(bytes.len(), 4);
w.write_all(&bytes[1..]).context(wr!())
}
MetaEvent::SmpteOffset(value) => value.write(w),
MetaEvent::TimeSignature(value) => value.write(w),
MetaEvent::KeySignature(value) => value.write(w),
MetaEvent::Sequencer => {
noimpl!("Sequencer-Specific: https://github.com/webern/midi_file/issues/9")
}
MetaEvent::Port(value) => {
write_u8!(w, META_PORT)?;
write_u8!(w, 1)?;
write_u8!(w, value.get())
}
}
}
pub(crate) fn parse_end_of_track<R: Read>(iter: &mut ByteIter<R>) -> LibResult<Self> {
iter.read_expect(0x00).context(io!())?;
Ok(MetaEvent::EndOfTrack)
}
pub(crate) fn parse_text<R: Read>(iter: &mut ByteIter<R>) -> LibResult<Self> {
let text_type = iter
.current()
.context(error::OtherSnafu { site: site!() })?;
let length = iter.read_vlq_u32().context(io!())?;
let bytes = iter.read_n(length as usize).context(io!())?;
let s: Text = bytes.into();
match text_type {
META_TEXT => Ok(MetaEvent::OtherText(s)),
META_COPYRIGHT => Ok(MetaEvent::Copyright(s)),
META_TRACK_NAME => Ok(MetaEvent::TrackName(s)),
META_INSTR_NAME => Ok(MetaEvent::InstrumentName(s)),
META_LYRIC => Ok(MetaEvent::Lyric(s)),
META_MARKER => Ok(MetaEvent::Marker(s)),
META_CUE_POINT => Ok(MetaEvent::CuePoint(s)),
META_PROG_NAME => Ok(MetaEvent::ProgramName(s)),
META_DEVICE_NAME => Ok(MetaEvent::DeviceName(s)),
_ => invalid_file!("unrecognized byte {:#04X}", text_type),
}
}
}
fn write_text<W: Write>(w: &mut Scribe<W>, text_type: u8, text: &Text) -> LibResult<()> {
w.write_all(&text_type.to_be_bytes()).context(wr!())?;
let bytes = text.as_bytes();
let size_u32 =
u32::try_from(bytes.len()).context(error::StringTooLongSnafu { site: site!() })?;
let size = Vlq::new(size_u32).to_bytes();
w.write_all(&size).context(wr!())?;
w.write_all(bytes).context(wr!())?;
Ok(())
}
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Hash)]
pub struct SmpteOffsetValue {
pub(crate) hr: u8,
pub(crate) mn: u8,
pub(crate) se: u8,
pub(crate) fr: u8,
pub(crate) ff: u8,
}
impl SmpteOffsetValue {
pub fn new(hr: u8, mn: u8, se: u8, fr: u8, ff: u8) -> Self {
Self { hr, mn, se, fr, ff }
}
pub fn hr(&self) -> u8 {
self.hr
}
pub fn mn(&self) -> u8 {
self.mn
}
pub fn se(&self) -> u8 {
self.se
}
pub fn fr(&self) -> u8 {
self.fr
}
pub fn ff(&self) -> u8 {
self.ff
}
pub(crate) fn parse<R: Read>(iter: &mut ByteIter<R>) -> LibResult<Self> {
iter.read_expect(LEN_META_SMTPE_OFFSET).context(io!())?;
Ok(Self {
hr: iter.read_or_die().context(io!())?,
mn: iter.read_or_die().context(io!())?,
se: iter.read_or_die().context(io!())?,
fr: iter.read_or_die().context(io!())?,
ff: iter.read_or_die().context(io!())?,
})
}
pub(crate) fn write<W: Write>(&self, w: &mut Scribe<W>) -> LibResult<()> {
write_u8!(w, META_SMTPE_OFFSET)?;
write_u8!(w, LEN_META_SMTPE_OFFSET)?;
write_u8!(w, self.hr)?;
write_u8!(w, self.mn)?;
write_u8!(w, self.se)?;
write_u8!(w, self.fr)?;
write_u8!(w, self.ff)?;
Ok(())
}
}
pub(crate) const META_SEQUENCE_NUM: u8 = 0x00;
pub(crate) const META_TEXT: u8 = 0x01;
pub(crate) const META_COPYRIGHT: u8 = 0x02;
pub(crate) const META_TRACK_NAME: u8 = 0x03;
pub(crate) const META_INSTR_NAME: u8 = 0x04;
pub(crate) const META_LYRIC: u8 = 0x05;
pub(crate) const META_MARKER: u8 = 0x06;
pub(crate) const META_CUE_POINT: u8 = 0x07;
pub(crate) const META_PROG_NAME: u8 = 0x08;
pub(crate) const META_DEVICE_NAME: u8 = 0x09;
pub(crate) const META_CHAN_PREFIX: u8 = 0x20;
pub(crate) const META_END_OF_TRACK: u8 = 0x2f;
pub(crate) const META_SET_TEMPO: u8 = 0x51;
pub(crate) const META_SMTPE_OFFSET: u8 = 0x54;
pub(crate) const META_TIME_SIG: u8 = 0x58;
pub(crate) const META_KEY_SIG: u8 = 0x59;
pub(crate) const META_SEQ_SPECIFIC: u8 = 0x7f;
pub(crate) const META_PORT: u8 = 0x21;
pub(crate) const LEN_META_CHAN_PREFIX: u8 = 1;
pub(crate) const LEN_META_END_OF_TRACK: u8 = 0;
pub(crate) const LEN_META_SET_TEMPO: u8 = 3;
pub(crate) const LEN_META_SMTPE_OFFSET: u8 = 5;
pub(crate) const LEN_META_TIME_SIG: u8 = 4;
pub(crate) const LEN_META_KEY_SIG: u8 = 2;
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Hash)]
pub struct TimeSignatureValue {
numerator: u8,
denominator: DurationName,
click: Clocks,
tpq: u8,
}
impl TimeSignatureValue {
pub fn new(numerator: u8, denominator: DurationName, click: Clocks) -> Result<Self> {
ensure!(numerator > 0, error::OtherSnafu { site: site!() });
Ok(Self {
numerator,
denominator,
click,
..Self::default()
})
}
pub fn numerator(&self) -> u8 {
self.numerator
}
pub fn denominator(&self) -> DurationName {
self.denominator
}
pub fn click(&self) -> Clocks {
self.click
}
pub(crate) fn parse<R: Read>(iter: &mut ByteIter<R>) -> LibResult<Self> {
iter.read_expect(LEN_META_TIME_SIG).context(io!())?;
Ok(Self {
numerator: iter.read_or_die().context(io!())?,
denominator: DurationName::from_u8(iter.read_or_die().context(io!())?)?,
click: Clocks::from_u8(iter.read_or_die().context(io!())?),
tpq: iter.read_or_die().context(io!())?,
})
}
pub(crate) fn write<W: Write>(&self, w: &mut Scribe<W>) -> LibResult<()> {
write_u8!(w, META_TIME_SIG)?;
write_u8!(w, LEN_META_TIME_SIG)?;
write_u8!(w, self.numerator)?;
write_u8!(w, self.denominator as u8)?;
write_u8!(w, self.click.to_u8())?;
write_u8!(w, self.tpq)?;
Ok(())
}
}
clamp!(
KeyAccidentals,
i8,
-7,
7,
0,
pub
);
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash, Default)]
pub enum KeyMode {
#[default]
Major,
Minor,
}
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Hash)]
pub struct KeySignatureValue {
accidentals: KeyAccidentals,
mode: KeyMode,
}
impl KeySignatureValue {
pub fn new(accidentals: KeyAccidentals, mode: KeyMode) -> Self {
Self { accidentals, mode }
}
pub fn accidentals(&self) -> KeyAccidentals {
self.accidentals
}
pub fn mode(&self) -> KeyMode {
self.mode
}
pub(crate) fn parse<R: Read>(iter: &mut ByteIter<R>) -> LibResult<Self> {
iter.read_expect(LEN_META_KEY_SIG).context(io!())?;
let raw_accidentals_byte = iter.read_or_die().context(io!())?;
let casted_accidentals = raw_accidentals_byte as i8;
Ok(Self {
accidentals: casted_accidentals.into(),
mode: match iter.read_or_die().context(io!())? {
1 => KeyMode::Minor,
_ => KeyMode::Major,
},
})
}
pub(crate) fn write<W: Write>(&self, w: &mut Scribe<W>) -> LibResult<()> {
write_u8!(w, META_KEY_SIG)?;
write_u8!(w, LEN_META_KEY_SIG)?;
write_u8!(w, self.accidentals.get() as u8)?;
write_u8!(w, self.mode as u8)?;
Ok(())
}
}
pub(crate) const DEFAULT_MICROSECONDS_PER_QUARTER: u32 = 500_000;
pub(crate) const MAX_24BIT_UINT_VALUE: u32 = 16_777_215;
clamp!(
MicrosecondsPerQuarter,
u32,
1,
MAX_24BIT_UINT_VALUE,
DEFAULT_MICROSECONDS_PER_QUARTER,
pub
);
impl MicrosecondsPerQuarter {
pub(crate) fn parse<R: Read>(iter: &mut ByteIter<R>) -> LibResult<Self> {
iter.read_expect(LEN_META_SET_TEMPO).context(io!())?;
let bytes = iter.read_n(LEN_META_SET_TEMPO as usize).context(io!())?;
let beu32 = [0u8, bytes[0], bytes[1], bytes[2]];
let parsed_number = u32::from_be_bytes(beu32);
Ok(MicrosecondsPerQuarter::new(parsed_number))
}
}
clamp!(
QuartersPerMinute,
u8,
1,
u8::MAX,
120,
pub
);