diff --git a/src/core/message.rs b/src/core/message.rs index 6712411..39bb7f5 100644 --- a/src/core/message.rs +++ b/src/core/message.rs @@ -5,8 +5,7 @@ use crate::core::{ }; use crate::error::{self, LibResult}; use crate::scribe::Scribe; -use log::trace; -use log::warn; +use log::{trace, warn}; use snafu::{OptionExt, ResultExt}; use std::convert::TryFrom; use std::io::{Read, Write}; @@ -118,9 +117,19 @@ impl WriteBytes for PitchBendMessage { write_status_byte(w, StatusType::PitchBend, self.channel)?; let lsb = (self.pitch_bend.get() & 0x74) as u8; let msb = ((self.pitch_bend.get() >> 7) & 0x74) as u8; + println!("WRITING LSB MSB: {} {}", lsb, msb); write_u8!(w, lsb)?; write_u8!(w, msb)?; Ok(()) + // write_status_byte(w, StatusType::PitchBend, self.channel)?; + // println!("pitch bend value: {}", self.pitch_bend.get()); + // let msb = (self.pitch_bend.get() >> 8) as u8; + // let lsb = (self.pitch_bend.get() & 0b0000_0000_1111_1111) as u8; + // println!("pitch bend msb: {}", msb); + // println!("pitch bend lsb: {}", lsb); + // write_u8!(w, msb)?; + // write_u8!(w, lsb)?; + // Ok(()) } } @@ -334,7 +343,23 @@ impl Message { noimpl!("channel pressure: https://github.com/webern/midi_file/issues/X") } StatusType::PitchBend => { - noimpl!("pitch bend: https://github.com/webern/midi_file/issues/10") + let lsb = iter.read_or_die().context(io!())?; + let msb = iter.read_or_die().context(io!())?; + println!("pitch bend read msb lsb {} {}", msb, lsb); + let high_bits = (msb as u16) << 7; + let low_bits = (lsb as u16) & 0b0000_0000_0111_111; + println!( + "pitch bend read high_bits low_bits {} {}", + high_bits, low_bits + ); + let value = high_bits | low_bits; + println!("______________________PITCH BEND READ: {}", value); + + // let value = println!("value {}", value); + Ok(Message::PitchBend(PitchBendMessage { + channel, + pitch_bend: PitchBendValue::new(value), + })) } StatusType::System => noimpl!("system: https://github.com/webern/midi_file/issues/10"), } diff --git a/tests/data/pitch_bend.info b/tests/data/pitch_bend.info new file mode 100644 index 0000000..f10dc4f --- /dev/null +++ b/tests/data/pitch_bend.info @@ -0,0 +1,3 @@ +Something I exported from Logic so that I know the exact pitch bend values. It has the following +pitch bend values on each beat 0, 20, 40, 127 , 125, 101, 40, 20. +Creative commons, Public domain, etc. diff --git a/tests/data/pitch_bend.mid b/tests/data/pitch_bend.mid new file mode 100644 index 0000000..e7a02bc Binary files /dev/null and b/tests/data/pitch_bend.mid differ diff --git a/tests/data/pitch_bend_two_bytes.info b/tests/data/pitch_bend_two_bytes.info new file mode 100644 index 0000000..8bd1f58 --- /dev/null +++ b/tests/data/pitch_bend_two_bytes.info @@ -0,0 +1,3 @@ +Created with this amazing tool https://signal.vercel.app/edit +Pitch bend values are 8192, 8292, 8092, 16383, 16384, 0, 1, 8192. +Creative commons, Public domain, etc. diff --git a/tests/data/pitch_bend_two_bytes.mid b/tests/data/pitch_bend_two_bytes.mid new file mode 100644 index 0000000..df95913 Binary files /dev/null and b/tests/data/pitch_bend_two_bytes.mid differ diff --git a/tests/integ.rs b/tests/integ.rs index 66ab24c..386c0d3 100644 --- a/tests/integ.rs +++ b/tests/integ.rs @@ -1,10 +1,12 @@ mod utils; +use crate::utils::{PITCH_BEND, PITCH_BEND_TWO_BYTES}; use midi_file::core::{Clocks, Control, DurationName, Message}; use midi_file::file::{Division, Event, Format, MetaEvent, QuarterNoteDivision}; use midi_file::MidiFile; use std::fs::File; use std::io::Read; +use tempfile::tempdir; use utils::{enable_logging, test_file, AVE_MARIS_STELLA}; #[test] @@ -176,3 +178,87 @@ fn ave_maris_stella_finale_export() { assert_eq!(original, written); } } + +#[test] +fn pitch_bend() { + enable_logging(); + let midi_file = MidiFile::load(test_file(PITCH_BEND)).unwrap(); + let track = midi_file.tracks().next().unwrap(); + + fn assert_pitch_bend(event: &Event, expected: u16) { + let message = match event { + Event::Midi(message) => message, + _ => panic!("wrong event type {:?}", event), + }; + let pitch_bend_message = match message { + Message::PitchBend(p) => p, + _ => panic!("wrong message type {:?}", message), + }; + assert_eq!(pitch_bend_message.pitch_bend().get(), expected); + } + + // assert_pitch_bend(track.events().skip(8).next().unwrap().event(), 0); + // assert_pitch_bend(track.events().skip(9).next().unwrap().event(), 20); + // assert_pitch_bend(track.events().skip(10).next().unwrap().event(), 40); + // assert_pitch_bend(track.events().skip(11).next().unwrap().event(), 127); + // assert_pitch_bend(track.events().skip(12).next().unwrap().event(), 125); + // assert_pitch_bend(track.events().skip(13).next().unwrap().event(), 101); + // assert_pitch_bend(track.events().skip(14).next().unwrap().event(), 40); + // assert_pitch_bend(track.events().skip(15).next().unwrap().event(), 20); + + let tempdir = tempdir().unwrap(); + let path = tempdir.path().join("file.mid"); + midi_file.save(&path).unwrap(); + // let midi_file = MidiFile::load(&path).unwrap(); + // let track = midi_file.tracks().next().unwrap(); + // assert_pitch_bend(track.events().skip(8).next().unwrap().event(), 0); + // assert_pitch_bend(track.events().skip(9).next().unwrap().event(), 20); + // assert_pitch_bend(track.events().skip(10).next().unwrap().event(), 40); + // assert_pitch_bend(track.events().skip(11).next().unwrap().event(), 127); + // assert_pitch_bend(track.events().skip(12).next().unwrap().event(), 125); + // assert_pitch_bend(track.events().skip(13).next().unwrap().event(), 101); + // assert_pitch_bend(track.events().skip(14).next().unwrap().event(), 40); + // assert_pitch_bend(track.events().skip(15).next().unwrap().event(), 20); +} + +#[test] +fn pitch_bend_two_byte() { + enable_logging(); + let midi_file = MidiFile::load(test_file(PITCH_BEND_TWO_BYTES)).unwrap(); + let track = midi_file.tracks().next().unwrap(); + + fn assert_pitch_bend(event: &Event, expected: u16) { + let message = match event { + Event::Midi(message) => message, + _ => panic!("wrong event type {:?}", event), + }; + let pitch_bend_message = match message { + Message::PitchBend(p) => p, + _ => panic!("wrong message type {:?}", message), + }; + assert_eq!(pitch_bend_message.pitch_bend().get(), expected); + } + + // assert_pitch_bend(track.events().skip(8).next().unwrap().event(), 0); + // assert_pitch_bend(track.events().skip(9).next().unwrap().event(), 20); + // assert_pitch_bend(track.events().skip(10).next().unwrap().event(), 40); + // assert_pitch_bend(track.events().skip(11).next().unwrap().event(), 127); + // assert_pitch_bend(track.events().skip(12).next().unwrap().event(), 125); + // assert_pitch_bend(track.events().skip(13).next().unwrap().event(), 101); + // assert_pitch_bend(track.events().skip(14).next().unwrap().event(), 40); + // assert_pitch_bend(track.events().skip(15).next().unwrap().event(), 20); + // + // let tempdir = tempdir().unwrap(); + // let path = tempdir.path().join("file.mid"); + // midi_file.save(&path).unwrap(); + // let midi_file = MidiFile::load(&path).unwrap(); + // let track = midi_file.tracks().next().unwrap(); + // assert_pitch_bend(track.events().skip(8).next().unwrap().event(), 0); + // assert_pitch_bend(track.events().skip(9).next().unwrap().event(), 20); + // assert_pitch_bend(track.events().skip(10).next().unwrap().event(), 40); + // assert_pitch_bend(track.events().skip(11).next().unwrap().event(), 127); + // assert_pitch_bend(track.events().skip(12).next().unwrap().event(), 125); + // assert_pitch_bend(track.events().skip(13).next().unwrap().event(), 101); + // assert_pitch_bend(track.events().skip(14).next().unwrap().event(), 40); + // assert_pitch_bend(track.events().skip(15).next().unwrap().event(), 20); +} diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs index d82e1f5..5b39495 100644 --- a/tests/roundtrip.rs +++ b/tests/roundtrip.rs @@ -6,9 +6,8 @@ use std::path::{Path, PathBuf}; use tempfile::TempDir; use utils::{ enable_logging, test_file, ADESTE_FIDELES, ALS_DIE_ROEMER, AVE_MARIS_STELLA, BARITONE_SAX, - B_GUAJEO, LATER_FOLIA, LOGIC_PRO, PHOBOS_DORICO, TOBEFREE, + B_GUAJEO, LATER_FOLIA, LOGIC_PRO, PHOBOS_DORICO, PITCH_BEND, TOBEFREE, }; - type RtResult = std::result::Result<(), RtErr>; enum RtErr { @@ -241,6 +240,11 @@ fn phobos_dorico() { round_trip_test(PHOBOS_DORICO).unwrap(); } +#[test] +fn pitch_bend() { + round_trip_test(PITCH_BEND).unwrap(); +} + #[test] fn tobeefree() { round_trip_test(TOBEFREE).unwrap(); diff --git a/tests/utils.rs b/tests/utils.rs index 1a0ea63..098c3ed 100644 --- a/tests/utils.rs +++ b/tests/utils.rs @@ -13,6 +13,8 @@ pub const B_GUAJEO: &str = "b_guajeo.mid"; pub const LATER_FOLIA: &str = "later_folia.mid"; pub const LOGIC_PRO: &str = "logic_pro.mid"; pub const PHOBOS_DORICO: &str = "phobos_dorico.mid"; +pub const PITCH_BEND: &str = "pitch_bend.mid"; +pub const PITCH_BEND_TWO_BYTES: &str = "pitch_bend_two_bytes.mid"; pub const TOBEFREE: &str = "tobefree.mid"; static LOGGER: Once = Once::new();