beatblox_midi 0.1.0

This crate help with parsing data from .mid files and returns a format compatible with netsblox.
Documentation
use super::Midi;
use super::types;
use num_traits;

struct RawNoteData {
    value: u8,
    beats: f32,
    velocity: u8,
}

pub fn get_ticks_per_beat(header: &midly::Header) -> f32 {
    let midly::Header { format: _, timing } = header;
    if let midly::Timing::Metrical(x) = timing {
        let ticks_per_beat: u16 = (*x).into();
        return ticks_per_beat as f32;
    }
    panic!("Timing format not supported");
}

pub fn get_bpm(track: &Vec<midly::TrackEvent>) -> u32 {
    for event in track {
        if let midly::TrackEventKind::Meta(midly::MetaMessage::Tempo(tempo)) = event.kind {
            let microseconds_per_beat: u32 = tempo.into();
            return microseconds_per_beat / 1000000 * 60;
        }
    }
    return 0;
}

pub fn get_time_signature(track: &Vec<midly::TrackEvent>) -> Vec<types::TimeSignature> {
    let mut time_signatures: Vec<types::TimeSignature> = Vec::new();
    let mut cur_time: u32 = 0;
    for event in track {
        let delta_t: u32 = event.delta.into();
        cur_time += delta_t;
        if let midly::TrackEventKind::Meta(message) = event.kind {
            if let midly::MetaMessage::TimeSignature(numerator, denominator, _, _) = message {
                time_signatures.push(types::TimeSignature {
                    beat_count: numerator,
                    beat_type: num_traits::pow(2, denominator.into()),
                    time_of_occurance: cur_time,
                });
            }
        }
    }
    return time_signatures;
}

pub fn load_tracks(midi: &mut Midi, smf: &midly::Smf) {
    let tmp = midi.clone();
    for track in &smf.tracks {
        midi.tracks.push(parse_track(&tmp, track));
    }
}

fn parse_track(midi: &Midi, track: &Vec<midly::TrackEvent>) -> types::Track {
    types::Track { 
        name: get_name(track), 
        notes: get_notes(midi, track),
    }
}

fn get_name(track: &Vec<midly::TrackEvent>) -> String {
    for event in track {
        if let midly::TrackEventKind::Meta(midly::MetaMessage::InstrumentName(s)) = event.kind {
            let raw_string: Vec<u8> = s.to_vec();
            return String::from_utf8(raw_string).unwrap();
        }
    }
    return String::from("");
}

fn get_notes(midi: &Midi, track: &Vec<midly::TrackEvent>) -> Vec<types::NoteWrapper> {
    let mut notes = Vec::new();
    let raw_note_data = get_raw_note_data(track, midi.ticks_per_beat);
    let time_signature = midi.time_signatures[0];

    for note in raw_note_data {
        let duration_type = types::beat_type_map(note.beats, time_signature.beat_type as f32);
        if duration_type.0 == types::NoteDuration::NaN {
            let tied_note = get_tied_note(note, time_signature.beat_type as f32);
            notes.push(types::NoteWrapper::ModifiedNote(tied_note));
        } else {
            notes.push(types::note_wrapper_builder(note.value, duration_type, note.velocity));
        }
    }
    
    return notes;
}

fn get_raw_note_data(track: &Vec<midly::TrackEvent>, ticks_per_beat: f32) -> Vec<RawNoteData> {
    let mut status: bool = false;
    let mut cur_note_value: u8 = 0;
    let mut cur_time: u32 = 0;
    let mut note_on_time: u32 = 0;
    let mut note_off_time: u32 = 0;
    let mut data: Vec<RawNoteData> = Vec::new();

    for event in track {
        let delta_t: u32 = event.delta.into();
        cur_time += delta_t;

        if let midly::TrackEventKind::Midi { channel: _, message } = event.kind {
            if let midly::MidiMessage::NoteOn {key, vel: _ } = message {
                if status == false {
                    cur_note_value = key.into();
                    note_on_time = cur_time;
                    status = true;
                    if note_on_time - note_off_time != 0 {
                        data.push(RawNoteData { 
                            value: 255, 
                            beats: (note_on_time - note_off_time) as f32 / ticks_per_beat,
                            velocity: 0,
                        });
                    }
                } 
            }
            else if let midly::MidiMessage::NoteOff { key, vel: _ } = message {
                if status == true && cur_note_value == key {
                    data.push(RawNoteData { 
                        value: cur_note_value, 
                        beats: (cur_time - note_on_time) as f32 / ticks_per_beat,
                        velocity: 0,
                    });
                    note_off_time = cur_time;
                    status = false;
                }
            }
        }
    }

    return data;
}

fn get_tied_note(note: RawNoteData, beat_type: f32) -> types::NoteModifier {
    let mut notes: Vec<types::NoteWrapper> = Vec::new();
    let mut remaining_beats: f32 = note.beats;
    while remaining_beats > 0.0 {
        let nested_beat_value = get_nested_beat_value(remaining_beats);
        let new_duration = types::beat_type_map(nested_beat_value, beat_type as f32);
        remaining_beats -= nested_beat_value;
        notes.push(types::note_wrapper_builder(note.value, new_duration, note.velocity));
    }
    return types::NoteModifier::TiedNote(notes);
}

fn get_nested_beat_value(beats: f32) -> f32 {
    for i in 1..types::POSSIBLE_NOTE_LENGTHS.len() {
        if types::POSSIBLE_NOTE_LENGTHS[i] > beats {
            return types::POSSIBLE_NOTE_LENGTHS[i - 1];
        }
    }
    return types::POSSIBLE_NOTE_LENGTHS[types::POSSIBLE_NOTE_LENGTHS.len() - 1];
}