#![cfg_attr(
all(
feature = "convert-time",
feature = "engine-midly-0-5",
feature = "read"
),
doc = "\
```
"
)]
#![cfg_attr(
not(all(
feature = "convert-time",
feature = "engine-midly-0-5",
feature = "read"
)),
doc = "\
```ignore
"
)]
#[cfg(feature = "engine-midly-0-5")]
pub mod midly_0_5;
#[cfg(feature = "convert-time")]
use std::num::NonZeroU64;
#[cfg(feature = "convert-time")]
use std::{
error::Error,
fmt::{Display, Formatter},
};
#[cfg(feature = "convert-time")]
use timestamp_stretcher::TimestampStretcher;
#[derive(Debug)]
#[non_exhaustive]
#[cfg(feature = "convert-time")]
pub enum TimeConversionError {
ZeroTicksPerBeatNotSupported,
ZeroTicksPerFrameNotSupported,
ZeroTempoNotSupported,
}
#[cfg(feature = "convert-time")]
impl Display for TimeConversionError {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
use TimeConversionError::*;
match self {
ZeroTicksPerBeatNotSupported => {
write!(f, "zero ticks per beat is not supported")
}
ZeroTicksPerFrameNotSupported => {
write!(f, "zero ticks per frame is not supported")
}
ZeroTempoNotSupported => {
write!(f, "a tempo of zero is not supported")
}
}
}
}
#[cfg(feature = "convert-time")]
impl Error for TimeConversionError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
#[cfg(feature = "convert-time")]
pub struct ConvertTicksToMicroseconds {
#[allow(dead_code)]
time_stretcher: TimestampStretcher,
#[allow(dead_code)]
ticks_per_beat: Option<NonZeroU64>,
}
#[cfg(feature = "convert-time")]
pub trait MidiEvent {
fn tempo(&self) -> Option<u32>;
}
#[cfg(feature = "convert-time")]
impl ConvertTicksToMicroseconds {
pub fn convert<T>(&mut self, ticks: u64, event: &T) -> u64
where
T: MidiEvent,
{
let new_factor = if let Some(ticks_per_beat) = self.ticks_per_beat {
if let Some(tempo) = event.tempo() {
Some((tempo as u64, ticks_per_beat))
} else {
None
}
} else {
None
};
self.time_stretcher.stretch(ticks, new_factor)
}
}
#[cfg(feature = "convert-time")]
pub struct ConvertMicroSecondsToTicks {
#[allow(dead_code)]
time_stretcher: TimestampStretcher,
#[allow(dead_code)]
ticks_per_beat: Option<u64>,
}
#[cfg(feature = "convert-time")]
impl ConvertMicroSecondsToTicks {
pub fn convert<T>(&mut self, microseconds: u64, event: &T) -> Result<u64, TimeConversionError>
where
T: MidiEvent,
{
let new_factor = if let Some(ticks_per_beat) = self.ticks_per_beat {
if let Some(tempo) = event.tempo() {
Some((
ticks_per_beat,
NonZeroU64::new(tempo as u64)
.ok_or(TimeConversionError::ZeroTempoNotSupported)?,
))
} else {
None
}
} else {
None
};
Ok(self.time_stretcher.stretch(microseconds, new_factor))
}
}