#[cfg(feature = "backend-combined-midly-0-5")]
use crate::backend::combined::midly::midly_0_5::TrackEventKind;
#[cfg(all(test, feature = "backend-combined-midly-0-5"))]
use crate::backend::combined::midly::midly_0_5::{
num::{u4, u7},
MidiMessage,
};
use core::num::NonZeroU64;
use gcd::Gcd;
use std::convert::{AsMut, AsRef, TryFrom};
use std::error::Error;
use std::fmt::{Debug, Display, Formatter, Write};
pub mod event_queue;
pub trait EventHandler<E> {
fn handle_event(&mut self, event: E);
}
pub trait EventHandlerExt<E> {
fn map<EE, F>(&mut self, function: F) -> Map<Self, F>
where
F: FnMut(EE) -> E,
{
Map {
inner: self,
function,
}
}
}
impl<T, E> EventHandlerExt<E> for T where T: EventHandler<E> + ?Sized {}
pub struct Map<'a, H, F>
where
H: ?Sized,
{
inner: &'a mut H,
function: F,
}
impl<'a, E, EE, F, H> EventHandler<EE> for Map<'a, H, F>
where
H: EventHandler<E>,
F: FnMut(EE) -> E,
{
fn handle_event(&mut self, event: EE) {
self.inner.handle_event((self.function)(event))
}
}
pub trait ContextualEventHandler<E, Context> {
fn handle_event(&mut self, event: E, context: &mut Context);
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct SysExEvent<'a> {
data: &'a [u8],
}
impl<'a> Debug for SysExEvent<'a> {
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
write!(f, "SysExEvent{{data (length: {:?}): &[", self.data.len())?;
for byte in self.data {
write!(f, "{:X} ", byte)?;
}
write!(f, "]}}")
}
}
impl<'a> SysExEvent<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data }
}
pub fn data(&self) -> &'a [u8] {
self.data
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct RawMidiEvent {
data: [u8; 3],
length: usize,
}
impl Debug for RawMidiEvent {
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
match self.length {
1 => write!(f, "RawMidiEvent({:X})", self.data[0]),
2 => write!(f, "RawMidiEvent({:X} {:X})", self.data[0], self.data[1]),
3 => write!(
f,
"RawMidiEvent({:X} {:X} {:X})",
self.data[0], self.data[1], self.data[2]
),
_ => unreachable!("Raw midi event is expected to have length 1, 2 or 3."),
}
}
}
impl RawMidiEvent {
#[inline]
pub fn new(bytes: &[u8]) -> Self {
match Self::try_new(bytes) {
Some(s) => s,
None => {
Self::panic_data_too_long(bytes);
}
}
}
fn panic_data_too_long(bytes: &[u8]) -> ! {
let mut event_as_string = String::new();
write!(event_as_string, "data : &[");
for (index, byte) in bytes.iter().enumerate() {
write!(event_as_string, "{:X} ", byte);
if index > 64 {
write!(event_as_string, "... ");
break;
}
}
write!(event_as_string, "]");
panic!(
"Raw midi event is expected to have length 1, 2 or 3. Actual length: {}, data: {}",
bytes.len(),
event_as_string
);
}
pub fn try_new(data: &[u8]) -> Option<Self> {
match data.len() {
1 => Some(Self {
data: [data[0], 0, 0],
length: data.len(),
}),
2 => Some(Self {
data: [data[0], data[1], 0],
length: data.len(),
}),
3 => Some(Self {
data: [data[0], data[1], data[2]],
length: data.len(),
}),
_ => None,
}
}
pub fn data(&self) -> &[u8; 3] {
&self.data
}
pub fn bytes(&self) -> &[u8] {
&self.data[0..self.length]
}
}
#[cfg(feature = "backend-combined-midly-0-5")]
use crate::backend::combined::midly::midly_0_5::io::CursorError;
#[cfg(feature = "backend-combined-midly-0-5")]
#[derive(Debug, Clone)]
pub enum MidlyConversionError {
NotALiveEvent,
CursorError(CursorError),
}
#[cfg(feature = "backend-combined-midly-0-5")]
impl Display for MidlyConversionError {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
MidlyConversionError::NotALiveEvent => write!(f, "Not a live event."),
MidlyConversionError::CursorError(e) => match e {
CursorError::InvalidInput(msg) => {
write!(f, "Technical error: the input SMF was invalid: {}", msg)
}
CursorError::OutOfSpace => {
write!(f, "Technical error: the in-memory buffer was too small")
}
},
}
}
}
#[cfg(feature = "backend-combined-midly-0-5")]
impl Error for MidlyConversionError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
#[cfg(feature = "backend-combined-midly-0-5")]
impl From<CursorError> for MidlyConversionError {
fn from(e: CursorError) -> Self {
MidlyConversionError::CursorError(e)
}
}
#[cfg(feature = "backend-combined-midly-0-5")]
impl<'a> TryFrom<TrackEventKind<'a>> for RawMidiEvent {
type Error = MidlyConversionError;
fn try_from(value: TrackEventKind<'a>) -> Result<Self, Self::Error> {
let mut raw_data: [u8; 3] = [0, 0, 0];
let mut slice = &mut raw_data[0..3];
value
.as_live_event()
.ok_or(MidlyConversionError::NotALiveEvent)?
.write(&mut slice)?;
let number_of_bytes = 3 - slice.len();
Ok(RawMidiEvent::new(&raw_data[0..number_of_bytes]))
}
}
#[cfg(feature = "backend-combined-midly-0-5")]
#[test]
fn conversion_from_midly_to_raw_midi_event_works() {
let channel = 1;
let program = 2;
let event_kind = TrackEventKind::Midi {
channel: u4::from(channel),
message: MidiMessage::ProgramChange {
program: u7::from(program),
},
};
let raw_midi_event = RawMidiEvent::try_from(event_kind).unwrap();
assert_eq!(raw_midi_event.length, 2);
assert_eq!(
raw_midi_event.data,
[
channel | midi_consts::channel_event::PROGRAM_CHANGE,
program,
0
]
);
}
impl AsRef<Self> for RawMidiEvent {
fn as_ref(&self) -> &RawMidiEvent {
self
}
}
impl AsMut<Self> for RawMidiEvent {
fn as_mut(&mut self) -> &mut RawMidiEvent {
self
}
}
#[derive(PartialEq, Eq, Debug)]
pub struct Timed<E> {
pub time_in_frames: u32,
pub event: E,
}
impl<E> Timed<E> {
pub fn new(time_in_frames: u32, event: E) -> Self {
Self {
time_in_frames,
event,
}
}
}
impl<E> Clone for Timed<E>
where
E: Clone,
{
fn clone(&self) -> Self {
Timed {
time_in_frames: self.time_in_frames,
event: self.event.clone(),
}
}
}
impl<E> Copy for Timed<E> where E: Copy {}
impl<E> AsRef<E> for Timed<E> {
fn as_ref(&self) -> &E {
&self.event
}
}
impl<E> AsMut<E> for Timed<E> {
fn as_mut(&mut self) -> &mut E {
&mut self.event
}
}
#[derive(PartialEq, Eq, Debug)]
pub struct Indexed<E> {
pub index: usize,
pub event: E,
}
impl<E> Indexed<E> {
pub fn new(index: usize, event: E) -> Self {
Self { index, event }
}
}
impl<E> Clone for Indexed<E>
where
E: Clone,
{
fn clone(&self) -> Self {
Self {
index: self.index,
event: self.event.clone(),
}
}
}
impl<E> Copy for Indexed<E> where E: Copy {}
impl<E> AsRef<E> for Indexed<E> {
fn as_ref(&self) -> &E {
&self.event
}
}
impl<E> AsMut<E> for Indexed<E> {
fn as_mut(&mut self) -> &mut E {
&mut self.event
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DeltaEvent<E> {
pub microseconds_since_previous_event: u64,
pub event: E,
}
pub struct TimeStretcher {
nominator: u64,
denominator: NonZeroU64,
input_offset: u64,
output_offset: u64,
drifting_correction: f64,
}
impl TimeStretcher {
pub fn new(nominator: u64, denominator: NonZeroU64) -> Self {
Self {
nominator,
denominator,
input_offset: 0,
output_offset: 0,
drifting_correction: 0.0,
}
}
pub fn stretch(
&mut self,
absolute_input_time: u64,
new_speed: Option<(u64, NonZeroU64)>,
) -> u64 {
let self_denominator: u64 = self.denominator.into();
let input_offset_delta: u64 = (absolute_input_time - self.input_offset) / self_denominator;
self.input_offset += input_offset_delta * self_denominator;
self.output_offset += input_offset_delta * self.nominator;
let new_time = (absolute_input_time - self.input_offset) * self.nominator
/ self_denominator
+ self.output_offset;
if let Some((mut new_nominator, mut new_denominator)) = new_speed {
let gcd = new_nominator.gcd(new_denominator.into());
new_nominator = new_nominator / gcd;
new_denominator =
unsafe { NonZeroU64::new_unchecked(<_ as Into<u64>>::into(new_denominator) / gcd) };
if new_nominator != self.nominator || new_denominator != self.denominator {
let error = ((absolute_input_time - self.input_offset) * self.nominator) as f64
/ (self_denominator as f64)
- ((new_time - self.output_offset) as f64);
self.drifting_correction += error;
let correction = self.drifting_correction as u64;
self.drifting_correction -= correction as f64;
self.input_offset = absolute_input_time;
self.output_offset = new_time + correction;
self.nominator = new_nominator;
self.denominator = new_denominator;
}
}
new_time
}
}
#[test]
pub fn event_time_stretcher_works_when_no_drifting_correction_needed() {
let mut stretcher = TimeStretcher::new(1, NonZeroU64::new(1).unwrap());
let input = vec![
(0, Some((2, NonZeroU64::new(3).unwrap()))),
(3, None),
(6, Some((7, NonZeroU64::new(5).unwrap()))),
(11, None),
(16, None),
];
let mut observed_output = Vec::new();
for input in input.into_iter() {
observed_output.push(stretcher.stretch(input.0, input.1));
}
let expected_output = vec![0, 2, 4, 11, 18];
assert_eq!(expected_output, observed_output);
}
#[test]
pub fn event_time_stretcher_works_when_drifting_correction_needed() {
let mut stretcher = TimeStretcher::new(1, NonZeroU64::new(1).unwrap());
let input = vec![
(0, Some((1, NonZeroU64::new(2).unwrap()))),
(2, None),
(3, Some((1, NonZeroU64::new(3).unwrap()))),
(6, None),
(7, Some((1, NonZeroU64::new(2).unwrap()))),
(9, None),
(10, Some((1, NonZeroU64::new(3).unwrap()))),
(13, None),
];
let mut observed_output = Vec::new();
for input in input.into_iter() {
observed_output.push(stretcher.stretch(input.0, input.1));
}
let expected_output = vec![0, 1, 1, 2, 2, 3, 3, 5];
assert_eq!(expected_output, observed_output);
}