pub mod exports {
pub use midly_0_5::*;
}
use self::exports::{num::u28, MetaMessage, Track, TrackEvent, TrackEventKind};
#[cfg(all(test, feature = "convert-time"))]
use self::exports::{
num::{u15, u24},
Format,
};
#[cfg(all(test, any(feature = "read", feature = "convert-time")))]
use self::exports::{
num::{u4, u7},
MidiMessage,
};
#[cfg(feature = "convert-time")]
use self::exports::{Fps, Header, Timing};
#[cfg(feature = "convert-time")]
use crate::{
ConvertMicroSecondsToTicks, ConvertTicksToMicroseconds, MidiEvent, TimeConversionError,
};
#[cfg(feature = "read")]
use itertools::Itertools; use std::iter::FromIterator;
#[cfg(feature = "convert-time")]
use std::num::NonZeroU64;
use std::num::TryFromIntError;
use std::{
convert::TryFrom,
error::Error,
fmt::{Display, Formatter},
};
#[cfg(feature = "convert-time")]
use timestamp_stretcher::TimestampStretcher;
#[cfg(feature = "convert-time")]
const MICROSECONDS_PER_SECOND: u64 = 1_000_000;
#[cfg(feature = "convert-time")]
const SECONDS_PER_MINUTE: u64 = 60;
#[cfg(feature = "convert-time")]
const MICROSECONDS_PER_MINUTE: u64 = SECONDS_PER_MINUTE * MICROSECONDS_PER_SECOND;
#[cfg(feature = "convert-time")]
const DEFAULT_BEATS_PER_MINUTE: u64 = 120;
#[cfg(feature = "read")]
pub fn merge_tracks<'a, 'b>(
tracks: &'b [Track<'a>],
) -> impl Iterator<Item = (u64, usize, TrackEventKind<'a>)> + 'b {
let mut track_index = 0;
tracks
.iter()
.map(|t| {
let mut offset = 0;
let result = t.iter().map(move |e| {
offset += e.delta.as_int() as u64;
(offset, track_index, e.kind)
});
track_index += 1;
result
})
.kmerge_by(|(t1, _, _), (t2, _, _)| t1 < t2)
}
#[cfg(feature = "read")]
#[test]
fn merge_tracks_has_sufficiently_flexible_lifetime_annotation() {
fn wrap(bytes: &[u8]) -> Track {
let event = TrackEvent {
delta: u28::new(123),
kind: TrackEventKind::SysEx(bytes),
};
merge_tracks(&[vec![event]])
.map(|(_, _, kind)| TrackEvent {
delta: u28::new(0),
kind,
})
.collect()
}
let bytes = "abc".to_string(); wrap(bytes.as_bytes());
}
#[cfg(feature = "read")]
#[test]
fn merge_tracks_works() {
fn kind(channel: u8) -> TrackEventKind<'static> {
TrackEventKind::Midi {
channel: u4::new(channel),
message: MidiMessage::NoteOn {
key: u7::new(1),
vel: u7::new(1),
},
}
}
fn track_event(delta: u32, channel: u8) -> TrackEvent<'static> {
TrackEvent {
delta: u28::new(delta),
kind: kind(channel),
}
}
let tracks = vec![
vec![track_event(1, 0), track_event(2, 1), track_event(4, 2)],
vec![track_event(2, 3), track_event(2, 4), track_event(5, 5)],
];
let result: Vec<_> = merge_tracks(&tracks[..]).collect();
assert_eq!(
result,
vec![
(1, 0, kind(0)),
(2, 1, kind(3)),
(3, 0, kind(1)),
(4, 1, kind(4)),
(7, 0, kind(2)),
(9, 1, kind(5))
]
)
}
#[cfg(feature = "convert-time")]
impl TryFrom<Header> for ConvertTicksToMicroseconds {
type Error = TimeConversionError;
fn try_from(header: Header) -> Result<Self, Self::Error> {
let time_stretcher;
let ticks_per_beat;
use TimeConversionError::*;
match header.timing {
Timing::Metrical(t) => {
let tpb = NonZeroU64::new(t.as_int() as u64).ok_or(ZeroTicksPerBeatNotSupported)?;
time_stretcher = TimestampStretcher::new(
MICROSECONDS_PER_MINUTE / DEFAULT_BEATS_PER_MINUTE,
tpb,
);
ticks_per_beat = Some(tpb);
}
Timing::Timecode(Fps::Fps29, ticks_per_frame) => {
time_stretcher = TimestampStretcher::new(
MICROSECONDS_PER_SECOND * 1001,
NonZeroU64::new((ticks_per_frame as u64) * 30000)
.ok_or(ZeroTicksPerFrameNotSupported)?,
);
ticks_per_beat = None;
}
Timing::Timecode(fps, ticks_per_frame) => {
time_stretcher = TimestampStretcher::new(
MICROSECONDS_PER_SECOND,
NonZeroU64::new((fps.as_int() as u64) * (ticks_per_frame as u64))
.ok_or(ZeroTicksPerFrameNotSupported)?,
);
ticks_per_beat = None;
}
}
Ok(Self {
time_stretcher,
ticks_per_beat,
})
}
}
#[cfg(feature = "convert-time")]
impl<'a> MidiEvent for TrackEventKind<'a> {
fn tempo(&self) -> Option<u32> {
if let TrackEventKind::Meta(MetaMessage::Tempo(tempo)) = self {
Some(tempo.as_int())
} else {
None
}
}
}
#[cfg(feature = "convert-time")]
#[test]
pub fn convert_ticks_to_microseconds_works_with_one_event() {
let tempo_in_microseconds_per_beat = 500000;
let ticks_per_beat = 32;
let event_time_in_ticks: u64 = 64;
let header = Header {
timing: Timing::Metrical(u15::from(ticks_per_beat)),
format: Format::SingleTrack,
};
let mut converter =
ConvertTicksToMicroseconds::try_from(header).expect("No error expected at this point.");
let microseconds = converter.convert(
0,
&TrackEventKind::Meta(MetaMessage::Tempo(u24::from(
tempo_in_microseconds_per_beat,
))),
);
assert_eq!(microseconds, 0);
let microseconds = converter.convert(
event_time_in_ticks,
&TrackEventKind::Midi {
channel: u4::from(0),
message: MidiMessage::NoteOn {
key: u7::from(60),
vel: u7::from(90),
},
},
);
assert_eq!(microseconds, 1000000);
}
#[cfg(feature = "convert-time")]
impl From<Header> for ConvertMicroSecondsToTicks {
fn from(header: Header) -> Self {
let time_stretcher;
let ticks_per_beat;
match header.timing {
Timing::Metrical(t) => {
let tpb = t.as_int() as u64;
time_stretcher = TimestampStretcher::new(
tpb,
NonZeroU64::new(MICROSECONDS_PER_MINUTE / DEFAULT_BEATS_PER_MINUTE)
.expect("Bug: MICROSECONDS_PER_MINUTE / DEFAULT_BEATS_PER_MINUTE should not be zero."),
);
ticks_per_beat = Some(tpb);
}
Timing::Timecode(Fps::Fps29, ticks_per_frame) => {
time_stretcher = TimestampStretcher::new(
(ticks_per_frame as u64) * 30000,
NonZeroU64::new(MICROSECONDS_PER_SECOND * 1001)
.expect("Bug: MICROSECONDS_PER_SECOND * 1001 should not be zero."),
);
ticks_per_beat = None;
}
Timing::Timecode(fps, ticks_per_frame) => {
time_stretcher = TimestampStretcher::new(
(fps.as_int() as u64) * (ticks_per_frame as u64),
NonZeroU64::new(MICROSECONDS_PER_SECOND)
.expect("Bug: MICROSECONDS_PER_SECOND should not be zero."),
);
ticks_per_beat = None;
}
}
Self {
time_stretcher,
ticks_per_beat,
}
}
}
#[cfg(feature = "convert-time")]
#[test]
pub fn convert_microseconds_to_ticks_works_with_one_event() {
let tempo_in_microseconds_per_beat = 500000;
let ticks_per_beat = 32;
let expected_vent_time_in_ticks: u64 = 64;
let event_time_in_microseconds: u64 = 1000000;
let header = Header {
timing: Timing::Metrical(u15::from(ticks_per_beat)),
format: Format::SingleTrack,
};
let mut converter = ConvertMicroSecondsToTicks::from(header);
let microseconds = converter
.convert(
0,
&TrackEventKind::Meta(MetaMessage::Tempo(u24::from(
tempo_in_microseconds_per_beat,
))),
)
.expect("No error expected at this point.");
assert_eq!(microseconds, 0);
let observed_event_time_in_ticks = converter
.convert(
event_time_in_microseconds,
&TrackEventKind::Midi {
channel: u4::from(0),
message: MidiMessage::NoteOn {
key: u7::from(60),
vel: u7::from(90),
},
},
)
.expect("No error expected at this point");
assert_eq!(expected_vent_time_in_ticks, observed_event_time_in_ticks);
}
#[derive(Debug)]
#[non_exhaustive]
pub enum SeparateTracksError {
TimeOverflow,
TimeCanOnlyIncrease,
}
impl From<TryFromIntError> for SeparateTracksError {
fn from(_: TryFromIntError) -> Self {
SeparateTracksError::TimeOverflow
}
}
impl Display for SeparateTracksError {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
SeparateTracksError::TimeOverflow => {
write!(
f,
"the time overflows what can be represented in a midi file."
)
}
SeparateTracksError::TimeCanOnlyIncrease => {
write!(f, "subsequent events with decreasing time found.")
}
}
}
}
impl Error for SeparateTracksError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
#[derive(Clone)]
struct TrackWriter<'a> {
track: Track<'a>,
ticks: u64,
}
impl<'a> TrackWriter<'a> {
fn new() -> Self {
TrackWriter {
track: Vec::new(),
ticks: 0,
}
}
fn push(&mut self, ticks: u64, kind: TrackEventKind<'a>) -> Result<(), SeparateTracksError> {
use SeparateTracksError::*;
if self.ticks > ticks {
return Err(TimeCanOnlyIncrease);
}
let delta = ticks - self.ticks;
let delta = u28::try_from(u32::try_from(delta)?).ok_or(TimeOverflow)?;
self.ticks = ticks;
self.track.push(TrackEvent { delta, kind });
Ok(())
}
}
pub struct TrackSeparator<'a> {
tracks: Vec<TrackWriter<'a>>,
}
impl<'a> TrackSeparator<'a> {
#[inline]
pub fn new() -> Self {
TrackSeparator { tracks: Vec::new() }
}
#[inline]
pub fn push(
&mut self,
ticks: u64,
track_index: usize,
event: TrackEventKind<'a>,
) -> Result<(), SeparateTracksError> {
if self.tracks.len() <= track_index {
self.tracks.resize(track_index + 1, TrackWriter::new());
}
self.tracks[track_index].push(ticks, event)
}
#[inline]
pub fn extend<I>(&mut self, iter: I) -> Result<(), SeparateTracksError>
where
I: IntoIterator<Item = (u64, usize, TrackEventKind<'a>)>,
{
for (ticks, track_index, event) in iter {
self.push(ticks, track_index, event)?
}
Ok(())
}
pub fn collect<B>(self) -> B
where
B: FromIterator<Track<'a>>,
{
self.tracks
.into_iter()
.map(|t| {
let mut track = t.track;
let end_of_track_is_marked_with_event = if let Some(last_event) = track.last() {
last_event.kind == TrackEventKind::Meta(MetaMessage::EndOfTrack)
} else {
false
};
if !end_of_track_is_marked_with_event {
track.push(TrackEvent {
kind: TrackEventKind::Meta(MetaMessage::EndOfTrack),
delta: u28::new(0),
});
}
track
})
.collect()
}
}