diff --git a/src/bin/copy.rs b/src/bin/copy.rs index 174fac4..add9f72 100644 --- a/src/bin/copy.rs +++ b/src/bin/copy.rs @@ -1,8 +1,9 @@ extern crate rimd; -use rimd::{SMF,SMFError,SMFWriter}; +use rimd::{SMF,SMFWriter}; use std::env::{args,Args}; use std::path::Path; +use std::convert::TryFrom; fn main() { let mut args: Args = args(); @@ -15,18 +16,13 @@ fn main() { Some(s) => s, None => { panic!("Need a destination path") }, }; - match SMF::from_file(&Path::new(&pathstr[..])) { + match SMF::try_from(Path::new(&pathstr[..])) { Ok(smf) => { - let writer = SMFWriter::from_smf(smf); + let writer = SMFWriter::from(smf); writer.write_to_file(&Path::new(&deststr[..])).unwrap(); } Err(e) => { - match e { - SMFError::InvalidSMFFile(s) => {println!("{}",s);} - SMFError::Error(e) => {println!("io: {}",e);} - SMFError::MidiError(_) => {println!("Midi Error");} - SMFError::MetaError(_) => {println!("Meta Error");} - } + println!("{}",e) } } } diff --git a/src/bin/test.rs b/src/bin/test.rs index 63b642d..9c9b521 100644 --- a/src/bin/test.rs +++ b/src/bin/test.rs @@ -1,8 +1,9 @@ extern crate rimd; -use rimd::{SMF,SMFError}; +use rimd::{SMF}; use std::env::{args,Args}; use std::path::Path; +use std::convert::TryFrom; fn main() { let mut args: Args = args(); @@ -12,11 +13,9 @@ fn main() { None => { panic!("Please pass a path to an SMF to test") }, }; println!("Reading: {}",pathstr); - match SMF::from_file(&Path::new(&pathstr[..])) { + match SMF::try_from(Path::new(&pathstr[..])) { Ok(smf) => { - println!("format: {}",smf.format); - println!("tracks: {}",smf.tracks.len()); - println!("division: {}",smf.division); + println!("{}", smf); let mut tnum = 1; for track in smf.tracks.iter() { let mut time: u64 = 0; @@ -29,12 +28,7 @@ fn main() { } } Err(e) => { - match e { - SMFError::InvalidSMFFile(s) => {println!("{}",s);} - SMFError::Error(e) => {println!("io: {}",e);} - SMFError::MidiError(e) => {println!("Midi Error: {}",e);} - SMFError::MetaError(_) => {println!("Meta Error");} - } + println!("{}", e); } } } diff --git a/src/builder.rs b/src/builder.rs index 3a46cd2..82d74c0 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -8,19 +8,19 @@ use ::{SMF,Event,SMFFormat,MetaEvent,MidiMessage,Track,TrackEvent}; /// This is useful for apps that want to store events internally /// with absolute times and then quickly build an SMF file for saving etc... pub struct AbsoluteEvent { - time: u64, - event: Event, + pub time: u64, + pub event: Event, } impl AbsoluteEvent { - pub fn new_midi(time: u64, midi: MidiMessage) -> AbsoluteEvent { - AbsoluteEvent { + pub fn new_midi(time: u64, midi: MidiMessage) -> Self { + Self { time: time, event: Event::Midi(midi), } } - pub fn new_meta(time: u64, meta: MetaEvent) -> AbsoluteEvent { - AbsoluteEvent { + pub fn new_meta(time: u64, meta: MetaEvent) -> Self { + Self { time: time, event: Event::Meta(meta), } @@ -29,27 +29,13 @@ impl AbsoluteEvent { /// Return true if the event inside this AbsoluteEvent is a midi /// event, false if it's a meta event pub fn is_midi(&self) -> bool { - match self.event { - Event::Midi(_) => true, - Event::Meta(_) => false, - } + self.event.is_midi() } /// Return true if the event inside this AbsoluteEvent is a meta /// event, false if it's a midi event pub fn is_meta(&self) -> bool { - match self.event { - Event::Midi(_) => false, - Event::Meta(_) => true, - } - } - - pub fn get_event(&self) -> &Event { - &self.event - } - - pub fn get_time(&self) -> u64 { - self.time + self.event.is_meta() } } @@ -187,14 +173,14 @@ pub struct SMFBuilder { impl SMFBuilder { /// Create a new SMFBuilder. Initially the builder will have no tracks - pub fn new() -> SMFBuilder { - SMFBuilder { + pub fn new() -> Self { + Self { tracks: Vec::new(), } } /// Get the number of tracks currenly in the builder - pub fn num_tracks(&self) -> usize { + pub fn len(&self) -> usize { self.tracks.len() } diff --git a/src/lib.rs b/src/lib.rs index 745f9af..dfdb9f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,6 +117,20 @@ impl Event { } } } + + pub fn is_midi(&self) -> bool { + match self { + Event::Midi(_) => true, + Event::Meta(_) => false, + } + } + + pub fn is_meta(&self) -> bool { + match self { + Event::Midi(_) => false, + Event::Meta(_) => true, + } + } } /// An event occuring in the track. @@ -185,25 +199,25 @@ pub enum SMFError { } impl From for SMFError { - fn from(err: Error) -> SMFError { + fn from(err: Error) -> Self { SMFError::Error(err) } } impl From for SMFError { - fn from(err: MidiError) -> SMFError { + fn from(err: MidiError) -> Self { SMFError::MidiError(err) } } impl From for SMFError { - fn from(err: MetaError) -> SMFError { + fn from(err: MetaError) -> Self { SMFError::MetaError(err) } } impl From for SMFError { - fn from(_: FromUtf8Error) -> SMFError { + fn from(_: FromUtf8Error) -> Self { SMFError::InvalidSMFFile("Invalid UTF8 data in file") } } @@ -255,21 +269,16 @@ pub struct SMF { impl SMF { - /// Read an SMF file at the given path - pub fn from_file(path: &Path) -> Result { - let mut file = try!(File::open(path)); - SMFReader::read_smf(&mut file) - } - /// Read an SMF from the given reader - pub fn from_reader(reader: &mut dyn Read) -> Result { - SMFReader::read_smf(reader) + pub fn len(&self) -> usize { + self.tracks.len() } + /// Read an SMF file at the given path /// Convert a type 0 (single track) to type 1 (multi track) SMF /// Does nothing if the SMF is already in type 1 /// Returns None if the SMF is in type 2 (multi song) - pub fn to_multi_track(&self) -> Option { + pub fn to_multi_track(&self) -> Option { match self.format { SMFFormat::MultiTrack => Some(self.clone()), SMFFormat::MultiSong => None, @@ -320,3 +329,24 @@ impl SMF { } } +impl fmt::Display for SMF { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "format: {}, tracks: {}, division: {}", self.format, self.len(), self.division) + } +} + +impl<'a> std::convert::TryFrom<&'a Path> for SMF { + type Error = SMFError; + fn try_from(path: &'a Path) -> Result { + let mut file = File::open(path)?; + SMFReader::read_smf(&mut file) + } +} + +impl<'a> std::convert::TryFrom<&'a mut dyn Read> for SMF { + type Error = SMFError; + /// Read an SMF from the given reader + fn try_from(reader: &'a mut dyn Read) -> Result { + SMFReader::read_smf(reader) + } +} diff --git a/src/meta.rs b/src/meta.rs index afec7d1..b50310f 100644 --- a/src/meta.rs +++ b/src/meta.rs @@ -17,7 +17,7 @@ pub enum MetaError { } impl From for MetaError { - fn from(err: Error) -> MetaError { + fn from(err: Error) -> Self { MetaError::Error(err) } } @@ -82,8 +82,8 @@ pub struct MetaEvent { } impl Clone for MetaEvent { - fn clone(&self) -> MetaEvent { - MetaEvent { + fn clone(&self) -> Self { + Self { command: self.command, length: self.length, data: self.data.clone(), @@ -151,22 +151,22 @@ impl MetaEvent { } /// Extract the next meta event from a reader - pub fn next_event(reader: &mut dyn Read) -> Result { + pub fn next_event(reader: &mut dyn Read) -> Result { let command = - match MetaCommand::from_u8(try!(read_byte(reader))) { + match MetaCommand::from_u8(read_byte(reader)?) { Some(c) => {c}, None => MetaCommand::Unknown, }; - let len = match SMFReader::read_vtime(reader) { + let length = match SMFReader::read_vtime(reader) { Ok(t) => { t } Err(_) => { return Err(MetaError::OtherErr("Couldn't read time for meta command")); } }; let mut data = Vec::new(); - try!(read_amount(reader,&mut data,len as usize)); - Ok(MetaEvent{ - command: command, - length: len, - data: data + read_amount(reader,&mut data, length as usize)?; + Ok(Self{ + command, + length, + data }) } @@ -191,8 +191,8 @@ impl MetaEvent { // event constructors below /// Create a sequence number meta event - pub fn sequence_number(sequence_number: u16) -> MetaEvent { - MetaEvent { + pub fn sequence_number(sequence_number: u16) -> Self { + Self { command: MetaCommand::SequenceNumber, length: 0x02, data: MetaEvent::u16_to_vec(sequence_number), @@ -200,8 +200,8 @@ impl MetaEvent { } /// Create a text meta event - pub fn text_event(text: String) -> MetaEvent { - MetaEvent { + pub fn text_event(text: String) -> Self { + Self { command: MetaCommand::TextEvent, length: text.len() as u64, data: text.into_bytes(), @@ -209,8 +209,8 @@ impl MetaEvent { } /// Create a copyright notice meta event - pub fn copyright_notice(copyright: String) -> MetaEvent { - MetaEvent { + pub fn copyright_notice(copyright: String) -> Self { + Self { command: MetaCommand::CopyrightNotice, length: copyright.len() as u64, data: copyright.into_bytes(), @@ -218,8 +218,8 @@ impl MetaEvent { } /// Create a name meta event - pub fn sequence_or_track_name(name: String) -> MetaEvent { - MetaEvent { + pub fn sequence_or_track_name(name: String) -> Self { + Self { command: MetaCommand::SequenceOrTrackName, length: name.len() as u64, data: name.into_bytes(), @@ -227,8 +227,8 @@ impl MetaEvent { } /// Create an instrument name meta event - pub fn instrument_name(name: String) -> MetaEvent { - MetaEvent { + pub fn instrument_name(name: String) -> Self { + Self { command: MetaCommand::InstrumentName, length: name.len() as u64, data: name.into_bytes(), @@ -236,8 +236,8 @@ impl MetaEvent { } /// Create a lyric text meta event - pub fn lyric_text(text: String) -> MetaEvent { - MetaEvent { + pub fn lyric_text(text: String) -> Self { + Self { command: MetaCommand::LyricText, length: text.len() as u64, data: text.into_bytes(), @@ -246,8 +246,8 @@ impl MetaEvent { /// Create a marker text meta event - pub fn marker_text(text: String) -> MetaEvent { - MetaEvent { + pub fn marker_text(text: String) -> Self { + Self { command: MetaCommand::MarkerText, length: text.len() as u64, data: text.into_bytes(), @@ -255,8 +255,8 @@ impl MetaEvent { } /// Create a cue point meta event - pub fn cue_point(text: String) -> MetaEvent { - MetaEvent { + pub fn cue_point(text: String) -> Self { + Self { command: MetaCommand::CuePoint, length: text.len() as u64, data: text.into_bytes(), @@ -264,8 +264,8 @@ impl MetaEvent { } /// Create a midi channel prefix assignment meta event - pub fn midichannel_prefix_assignment(channel: u8) -> MetaEvent { - MetaEvent { + pub fn midichannel_prefix_assignment(channel: u8) -> Self { + Self { command: MetaCommand::MIDIChannelPrefixAssignment, length: 1, data: vec![channel], @@ -273,8 +273,8 @@ impl MetaEvent { } /// Create a midi port prefix assignment meta event - pub fn midiport_prefix_assignment(port: u8) -> MetaEvent { - MetaEvent { + pub fn midiport_prefix_assignment(port: u8) -> Self { + Self { command: MetaCommand::MIDIPortPrefixAssignment, length: 1, data: vec![port], @@ -282,8 +282,8 @@ impl MetaEvent { } /// Create an end of track meta event - pub fn end_of_track() -> MetaEvent { - MetaEvent { + pub fn end_of_track() -> Self { + Self { command: MetaCommand::EndOfTrack, length: 0, data: vec![], @@ -293,8 +293,8 @@ impl MetaEvent { /// Create an event to set track tempo. This is stored /// as a 24-bit value. This method will fail an assertion if /// the supplied tempo is greater than 2^24. - pub fn tempo_setting(tempo: u32) -> MetaEvent { - MetaEvent { + pub fn tempo_setting(tempo: u32) -> Self { + Self { command: MetaCommand::TempoSetting, length: 3, data: MetaEvent::u24_to_vec(tempo), @@ -302,8 +302,8 @@ impl MetaEvent { } /// Create an smpte offset meta event - pub fn smpte_offset(hours: u8, minutes: u8, seconds: u8, frames: u8, fractional: u8) -> MetaEvent { - MetaEvent { + pub fn smpte_offset(hours: u8, minutes: u8, seconds: u8, frames: u8, fractional: u8) -> Self { + Self { command: MetaCommand::SMPTEOffset, length: 5, data: vec![hours,minutes,seconds,frames,fractional], @@ -322,8 +322,8 @@ impl MetaEvent { /// The parameter `num_32nd_notes_per_24_clocks` defines this in terms of the /// number of 1/32 notes which make up the usual 24 MIDI Clocks /// (the 'standard' quarter note). 8 is standard - pub fn time_signature(numerator: u8, denominator: u8, clocks_per_tick: u8, num_32nd_notes_per_24_clocks: u8) -> MetaEvent { - MetaEvent { + pub fn time_signature(numerator: u8, denominator: u8, clocks_per_tick: u8, num_32nd_notes_per_24_clocks: u8) -> Self { + Self { command: MetaCommand::TimeSignature, length: 4, data: vec![numerator,denominator,clocks_per_tick,num_32nd_notes_per_24_clocks], @@ -335,8 +335,8 @@ impl MetaEvent { /// `sharps_flats` of 0 represents a key of C, negative numbers represent /// 'flats', while positive numbers represent 'sharps'. - pub fn key_signature(sharps_flats: u8, major_minor: u8) -> MetaEvent { - MetaEvent { + pub fn key_signature(sharps_flats: u8, major_minor: u8) -> Self { + Self { command: MetaCommand::KeySignature, length: 2, data: vec![sharps_flats, major_minor], @@ -346,8 +346,8 @@ impl MetaEvent { /// This is the MIDI-file equivalent of the System Exclusive Message. /// sequencer-specific directives can be incorporated into a /// MIDI file using this event. - pub fn sequencer_specific_event(data: Vec) -> MetaEvent { - MetaEvent { + pub fn sequencer_specific_event(data: Vec) -> Self { + Self { command: MetaCommand::SequencerSpecificEvent, length: data.len() as u64, data: data, diff --git a/src/midi.rs b/src/midi.rs index 89fdd4f..10c8029 100644 --- a/src/midi.rs +++ b/src/midi.rs @@ -16,7 +16,7 @@ pub enum MidiError { } impl From for MidiError { - fn from(err: Error) -> MidiError { + fn from(err: Error) -> Self { MidiError::Error(err) } } @@ -141,9 +141,9 @@ impl MidiMessage { /// Create a midi message from a vector of bytes #[inline(always)] - pub fn from_bytes(bytes: Vec) -> MidiMessage{ + pub fn from_bytes(bytes: Vec) -> Self { // TODO: Validate bytes - MidiMessage { + Self { data: bytes, } } @@ -186,19 +186,19 @@ impl MidiMessage { /// Get the next midi message from the reader given that the /// status `stat` has just been read - pub fn next_message_given_status(stat: u8, reader: &mut dyn Read) -> Result { + pub fn next_message_given_status(stat: u8, reader: &mut dyn Read) -> Result { let mut ret:Vec = Vec::with_capacity(3); ret.push(stat); match MidiMessage::data_bytes(stat) { 0 => {} - 1 => { ret.push(try!(read_byte(reader))); } - 2 => { ret.push(try!(read_byte(reader))); - ret.push(try!(read_byte(reader))); } + 1 => { ret.push(read_byte(reader)?); } + 2 => { ret.push(read_byte(reader)?); + ret.push(read_byte(reader)?); } -1 => { return Err(MidiError::OtherErr("Don't handle variable sized yet")); } -2 => { // skip SysEx message while { - let byte = try!(read_byte(reader)); + let byte = read_byte(reader)?; ret.push(byte); byte != Status::SysExEnd as u8 } {} @@ -217,7 +217,7 @@ impl MidiMessage { match MidiMessage::data_bytes(stat) { 0 => { panic!("Can't have zero length message with running status"); } 1 => { } // already read it - 2 => { ret.push(try!(read_byte(reader))); } // only need one more byte + 2 => { ret.push(read_byte(reader)?); } // only need one more byte -1 => { return Err(MidiError::OtherErr("Don't handle variable sized yet")); } -2 => { return Err(MidiError::OtherErr("Running status not permitted with meta and sysex event")); } _ => { return Err(MidiError::InvalidStatus(stat)); } @@ -227,7 +227,7 @@ impl MidiMessage { /// Extract next midi message from a reader pub fn next_message(reader: &mut dyn Read) -> Result { - let stat = try!(read_byte(reader)); + let stat = read_byte(reader)?; MidiMessage::next_message_given_status(stat,reader) } @@ -235,23 +235,23 @@ impl MidiMessage { // Functions to build midi messages /// Create a note on message - pub fn note_on(note: u8, velocity: u8, channel: u8) -> MidiMessage { - MidiMessage { + pub fn note_on(note: u8, velocity: u8, channel: u8) -> Self { + Self { data: vec![make_status(Status::NoteOn,channel), note, velocity], } } /// Create a note off message - pub fn note_off(note: u8, velocity: u8, channel: u8) -> MidiMessage { - MidiMessage { + pub fn note_off(note: u8, velocity: u8, channel: u8) -> Self { + Self { data: vec![make_status(Status::NoteOff,channel), note, velocity], } } /// Create a polyphonic aftertouch message /// This message is most often sent by pressing down on the key after it "bottoms out". - pub fn polyphonic_aftertouch(note: u8, pressure: u8, channel: u8) -> MidiMessage { - MidiMessage { + pub fn polyphonic_aftertouch(note: u8, pressure: u8, channel: u8) -> Self { + Self { data: vec![make_status(Status::PolyphonicAftertouch,channel), note, pressure], } } @@ -259,16 +259,16 @@ impl MidiMessage { /// Create a control change message /// This message is sent when a controller value changes. Controllers include devices such as /// pedals and levers. Controller numbers 120-127 are reserved as "Channel Mode Messages". - pub fn control_change(controler: u8, data: u8, channel: u8) -> MidiMessage { - MidiMessage { + pub fn control_change(controler: u8, data: u8, channel: u8) -> Self { + Self { data: vec![make_status(Status::ControlChange,channel), controler, data], } } /// Create a program change message /// This message sent when the patch number changes. `program` is the new program number. - pub fn program_change(program: u8, channel: u8) -> MidiMessage { - MidiMessage { + pub fn program_change(program: u8, channel: u8) -> Self { + Self { data: vec![make_status(Status::ProgramChange,channel), program], } } @@ -277,8 +277,8 @@ impl MidiMessage { /// This message is most often sent by pressing down on the key after it "bottoms out". This message /// is different from polyphonic after-touch. Use this message to send the single greatest pressure /// value (of all the current depressed keys). `pressure` is the pressure value. - pub fn channel_aftertouch(pressure: u8, channel: u8) -> MidiMessage { - MidiMessage { + pub fn channel_aftertouch(pressure: u8, channel: u8) -> Self { + Self { data: vec![make_status(Status::ChannelAftertouch,channel), pressure], } } @@ -288,8 +288,8 @@ impl MidiMessage { /// The pitch bender is measured by a fourteen bit value. Center (no pitch change) is 2000H. /// Sensitivity is a function of the transmitter. `lsb` are the least significant 7 bits. /// `msb` are the most significant 7 bits. - pub fn pitch_bend(lsb: u8, msb: u8, channel: u8) -> MidiMessage { - MidiMessage { + pub fn pitch_bend(lsb: u8, msb: u8, channel: u8) -> Self { + Self { data: vec![make_status(Status::PitchBend,channel), lsb, msb], } } diff --git a/src/reader.rs b/src/reader.rs index fe2127c..84fbae6 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -12,7 +12,7 @@ pub struct SMFReader; impl SMFReader { fn parse_header(reader: &mut dyn Read) -> Result { let mut header:[u8;14] = [0;14]; - try!(fill_buf(reader,&mut header)); + fill_buf(reader,&mut header)?; // skip RIFF header if present if header[0] == 0x52 && @@ -20,8 +20,8 @@ impl SMFReader { header[2] == 0x46 && header[3] == 0x46 { let mut skip:[u8; 6] = [0; 6]; - try!(fill_buf(reader, &mut skip)); - try!(fill_buf(reader, &mut header)); + fill_buf(reader, &mut skip)?; + fill_buf(reader, &mut header)?; } if header[0] != 0x4D || @@ -46,8 +46,8 @@ impl SMFReader { } fn next_event(reader: &mut dyn Read, laststat: u8, was_running: &mut bool) -> Result { - let time = try!(SMFReader::read_vtime(reader)); - let stat = try!(read_byte(reader)); + let time = Self::read_vtime(reader)?; + let stat = read_byte(reader)?; if (stat & 0x80) == 0 { *was_running = true; @@ -57,7 +57,7 @@ impl SMFReader { match stat { 0xFF => { - let event = try!(MetaEvent::next_event(reader)); + let event = MetaEvent::next_event(reader)?; Ok( TrackEvent { vtime: time, event: Event::Meta(event), @@ -67,9 +67,9 @@ impl SMFReader { let msg = if (stat & 0x80) == 0 { // this is a running status, so assume we have the same status as last time - try!(MidiMessage::next_message_running_status(laststat,stat,reader)) + MidiMessage::next_message_running_status(laststat,stat,reader)? } else { - try!(MidiMessage::next_message_given_status(stat,reader)) + MidiMessage::next_message_given_status(stat,reader)? }; Ok( TrackEvent { vtime: time, @@ -86,14 +86,14 @@ impl SMFReader { let mut copyright = None; let mut name = None; - try!(fill_buf(reader,&mut buf)); + fill_buf(reader,&mut buf)?; if buf[0] != 0x4D || // "MTrk" buf[1] != 0x54 || buf[2] != 0x72 || buf[3] != 0x6B { return Err(SMFError::InvalidSMFFile("Invalid track magic")); } - try!(fill_buf(reader,&mut buf)); + fill_buf(reader,&mut buf)?; let len = ((buf[0] as u32) << 24 | (buf[1] as u32) << 16 | @@ -174,7 +174,7 @@ impl SMFReader { if i > 9 { return Err(SMFError::InvalidSMFFile("Variable length value too long")); } - let next = try!(read_byte(reader)); + let next = read_byte(reader)?; res |= next as u64 & val_mask; if (next & cont_mask) == 0 { break; @@ -190,7 +190,7 @@ impl SMFReader { match smf { Ok(ref mut s) => { for _ in 0..s.tracks.capacity() { - s.tracks.push(try!(SMFReader::parse_track(reader))); + s.tracks.push(SMFReader::parse_track(reader)?); } } _ => {} diff --git a/src/util.rs b/src/util.rs index ea36718..31a02a8 100644 --- a/src/util.rs +++ b/src/util.rs @@ -21,7 +21,7 @@ pub fn note_num_to_name(num: u32) -> String { /// Read a single byte from a Reader pub fn read_byte(reader: &mut dyn Read) -> Result { let mut b = [0; 1]; - try!(reader.read(&mut b)); + reader.read(&mut b)?; Ok(b[0]) } @@ -29,7 +29,7 @@ pub fn read_byte(reader: &mut dyn Read) -> Result { pub fn fill_buf(reader: &mut dyn Read, buf: &mut [u8]) -> Result<(),Error> { let mut read = 0; while read < buf.len() { - let bytes_read = try!(reader.read(&mut buf[read..])); + let bytes_read = reader.read(&mut buf[read..])?; if bytes_read == 0 { return Err(Error::new(ErrorKind::InvalidData, "file ends before it should")); } diff --git a/src/writer.rs b/src/writer.rs index 7866074..e8df1c3 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -1,6 +1,8 @@ -use std::fs::OpenOptions; -use std::io::{Error,Write}; -use std::path::Path; +use std::{ + fs::OpenOptions, + io::{Error,Write}, + path::Path +}; use byteorder::{BigEndian, WriteBytesExt}; @@ -19,7 +21,7 @@ use ::{Event,AbsoluteEvent,MetaEvent,MetaCommand,SMFFormat}; /// let mut builder = SMFBuilder::new(); /// // add some events to builder /// let smf = builder.result(); -/// let writer = SMFWriter::from_smf(smf); +/// let writer = SMFWriter::from(smf); /// let result = writer.write_to_file(Path::new("/path/to/file.smf")); /// // handle result pub struct SMFWriter { @@ -30,47 +32,32 @@ pub struct SMFWriter { impl SMFWriter { - /// Create a new SMFWriter with the given number of units per - /// beat. The SMFWriter will initially have no tracks. - pub fn new_with_division(ticks: i16) -> SMFWriter { - SMFWriter { - format: 1, - ticks: ticks, - tracks: Vec::new(), + pub fn new(format: u16, ticks: i16, tracks: Vec>) -> Self { + Self { + format, + ticks, + tracks, } } - /// Create a new SMFWriter with the given format and number of - /// units per beat. The SMFWriter will initially have no tracks. - pub fn new_with_division_and_format(format: SMFFormat, ticks: i16) -> SMFWriter { - SMFWriter { - format: format as u16, - ticks: ticks, - tracks: Vec::new(), - } + pub fn len(&self) -> usize { + self.tracks.len() } - /// Create a writer that has all the tracks from the given SMF already added - pub fn from_smf(smf: SMF) -> SMFWriter { - let mut writer = SMFWriter::new_with_division_and_format - (smf.format, smf.division); - - for track in smf.tracks.iter() { - let mut length = 0; - let mut saw_eot = false; - let mut vec = Vec::new(); - writer.start_track_header(&mut vec); - - for event in track.events.iter() { - length += SMFWriter::write_vtime(event.vtime as u64, &mut vec).unwrap(); // TODO: Handle error - writer.write_event(&mut vec, &(event.event), &mut length, &mut saw_eot); - } - - writer.finish_track_write(&mut vec, &mut length, saw_eot); - writer.tracks.push(vec); - } + /// Create a new SMFWriter with the given number of units per + /// beat. The SMFWriter will initially have no tracks. + pub fn new_with_division(ticks: i16) -> Self { + Self::new(1, ticks, Vec::new()) + } - writer + /// Create a new SMFWriter with the given format and number of + /// units per beat. The SMFWriter will initially have no tracks. + pub fn new_with_division_and_format(format: SMFFormat, ticks: i16) -> Self { + Self::new( + format as u16, + ticks, + Vec::new() + ) } pub fn vtime_to_vec(val: u64) -> Vec { @@ -97,7 +84,7 @@ impl SMFWriter { // Write a variable length value. Return number of bytes written. pub fn write_vtime(val: u64, writer: &mut dyn Write) -> Result { let storage = SMFWriter::vtime_to_vec(val); - try!(writer.write_all(&storage[..])); + writer.write_all(&storage[..])?; Ok(storage.len() as u32) } @@ -177,10 +164,10 @@ impl SMFWriter { } for ev in track { - let vtime = ev.get_time() - cur_time; + let vtime = ev.time - cur_time; cur_time = vtime; length += SMFWriter::write_vtime(vtime as u64,&mut vec).unwrap(); // TODO: Handle error - self.write_event(&mut vec, ev.get_event(), &mut length, &mut saw_eot); + self.write_event(&mut vec, &ev.event, &mut length, &mut saw_eot); } self.finish_track_write(&mut vec, &mut length, saw_eot); @@ -191,20 +178,20 @@ impl SMFWriter { // actual writing stuff below fn write_header(&self, writer: &mut dyn Write) -> Result<(),Error> { - try!(writer.write_all(&[0x4D,0x54,0x68,0x64])); - try!(writer.write_u32::(6)); - try!(writer.write_u16::(self.format)); - try!(writer.write_u16::(self.tracks.len() as u16)); - try!(writer.write_i16::(self.ticks)); + writer.write_all(&[0x4D,0x54,0x68,0x64])?; + writer.write_u32::(6)?; + writer.write_u16::(self.format)?; + writer.write_u16::(self.len() as u16)?; + writer.write_i16::(self.ticks)?; Ok(()) } /// Write out all the tracks that have been added to this /// SMFWriter to the passed writer pub fn write_all(self, writer: &mut dyn Write) -> Result<(),Error> { - try!(self.write_header(writer)); + self.write_header(writer)?; for track in self.tracks.into_iter() { - try!(writer.write_all(&track[..])); + writer.write_all(&track[..])?; } Ok(()) } @@ -213,10 +200,34 @@ impl SMFWriter { /// file. /// Warning: This will overwrite an existing file pub fn write_to_file(self, path: &Path) -> Result<(),Error> { - let mut file = try!(OpenOptions::new().write(true).truncate(true).create(true).open(path)); + let mut file = OpenOptions::new().write(true).truncate(true).create(true).open(path)?; self.write_all(&mut file) } +} + +impl From for SMFWriter { + /// Create a writer that has all the tracks from the given SMF already added + fn from(smf: SMF) -> Self { + let mut writer = Self::new_with_division_and_format + (smf.format, smf.division); + for track in smf.tracks.iter() { + let mut length = 0; + let mut saw_eot = false; + let mut vec = Vec::new(); + writer.start_track_header(&mut vec); + + for event in track.events.iter() { + length += Self::write_vtime(event.vtime as u64, &mut vec).unwrap(); // TODO: Handle error + writer.write_event(&mut vec, &(event.event), &mut length, &mut saw_eot); + } + + writer.finish_track_write(&mut vec, &mut length, saw_eot); + writer.tracks.push(vec); + } + + writer + } } #[test]