use crate::{Music, MusicChunk};
pub struct Midi {
header: Header,
track: Track,
}
impl Midi {
pub fn new(music: Music) -> Self {
let header = Header::new();
let track = Track::new(music.chunks);
Midi { header, track }
}
pub fn to_file(&self, filepath: &std::path::Path) {
let mut file = std::fs::File::create(filepath).unwrap();
let mut bin = std::io::Cursor::new(self.bin());
std::io::copy(&mut bin, &mut file).unwrap();
}
}
impl Bin for Midi {
type Output = Vec<u8>;
fn bin(&self) -> Vec<u8> {
let mut vec = Vec::new();
vec.extend(self.header.bin());
vec.extend(self.track.bin());
vec
}
}
struct Header {
format: u16,
num_tracks: u16,
tickdiv: u16,
}
impl Header {
pub fn new() -> Self {
Header { format: 0, num_tracks: 1, tickdiv: 1 }
}
}
struct Track {
events: Vec<Event>,
}
impl Track {
pub fn new<I: IntoIterator<Item=MusicChunk>>(iterable: I) -> Self {
let mut events = Vec::new();
let mut dt = 0u8;
for music_chunk in iterable {
use MusicChunk::*;
use Event::*;
match music_chunk {
Tone(dur) => {
events.push(NoteOn(dt));
events.push(NoteOff(dur));
dt = 0;
}
Rest(dur) => {
dt += dur;
}
}
}
events.push(Event::EndOfTrack);
Track { events }
}
}
#[derive(Copy, Clone, Debug)]
enum Event {
NoteOn(u8),
NoteOff(u8),
EndOfTrack,
}
trait Bin {
type Output: IntoIterator<Item=u8>;
fn bin(&self) -> Self::Output;
}
impl Bin for Header {
type Output = Vec<u8>;
fn bin(&self) -> Vec<u8> {
const HEADER_SIZE: usize = 14;
let mut vec = Vec::with_capacity(HEADER_SIZE);
{
vec.extend("MThd".bytes());
}
{
vec.extend(6u32.to_be_bytes())
}
{
vec.extend(self.format.to_be_bytes())
}
{
vec.extend(self.num_tracks.to_be_bytes())
}
{
vec.extend(self.tickdiv.to_be_bytes())
}
vec
}
}
impl Bin for Track {
type Output = Vec<u8>;
fn bin(&self) -> Vec<u8> {
let mut track_data = Vec::new();
for data in &self.events {
track_data.extend(data.bin());
}
let data_len = track_data.len();
let mut vec = Vec::with_capacity(data_len + 8);
vec.extend("MTrk".bytes());
{
vec.extend((data_len as u32).to_be_bytes());
}
vec.extend(track_data);
vec
}
}
use local_vec::LocalVec;
enum Switch {
On,
Off,
}
fn encode_note(dur: u8, switch: Switch) -> LocalVec<u8, 4> {
let mut vec = LocalVec::new();
vec.extend(to_vli(dur));
let switch_code: u8 = match switch {
Switch::Off => 0x80,
Switch::On => 0x90
};
vec.push(switch_code);
vec.push(0x3c);
vec.push(0x64);
vec
}
fn to_vli(val: u8) -> LocalVec<u8, 4> {
let mut vec = LocalVec::new();
vec.push(val & 0x7f);
vec
}
impl Bin for Event {
type Output = LocalVec<u8, 4>;
fn bin(&self) -> LocalVec<u8, 4> {
let mut vec = LocalVec::new();
use Event::*;
match *self {
NoteOn(dt) => {
vec.extend(encode_note(dt, Switch::On));
}
NoteOff(dt) => {
vec.extend(encode_note(dt, Switch::Off))
}
EndOfTrack => {
vec.extend(0x00ff2f00_u32.to_be_bytes())
}
}
vec
}
}