def testSlashDuration(self): tunes, exceptions = abc_parser.parse_tunebook("""X:1 Q:1/4=120 L:1/4 T:Test CC/C//C///C//// """) self.assertEqual(1, len(tunes)) self.assertEqual(0, len(exceptions)) expected_ns1 = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ ticks_per_quarter: 220 source_info: { source_type: SCORE_BASED encoding_type: ABC parser: MAGENTA_ABC } reference_number: 1 sequence_metadata { title: "Test" } tempos { qpm: 120 } notes { pitch: 60 velocity: 90 start_time: 0.0 end_time: 0.5 } notes { pitch: 60 velocity: 90 start_time: 0.5 end_time: 0.75 } notes { pitch: 60 velocity: 90 start_time: 0.75 end_time: 0.875 } notes { pitch: 60 velocity: 90 start_time: 0.875 end_time: 0.9375 } notes { pitch: 60 velocity: 90 start_time: 0.9375 end_time: 0.96875 } total_time: 0.96875 """) self.assertProtoEquals(expected_ns1, tunes[1])
def testParseTempos(self): # Examples from http://abcnotation.com/wiki/abc:standard:v2.1#qtempo tunes, exceptions = abc_parser.parse_tunebook(""" X:1 L:1/4 Q:60 X:2 L:1/4 Q:C=100 X:3 Q:1/2=120 X:4 Q:1/4 3/8 1/4 3/8=40 X:5 Q:5/4=40 X:6 Q: "Allegro" 1/4=120 X:7 Q: 1/4=120 "Allegro" X:8 Q: 3/8=50 "Slowly" X:9 Q:"Andante" X:10 Q:100 % define tempo using deprecated syntax % deprecated tempo syntax depends on unit note length. if it is % not defined, it is derived from the current meter. M:2/4 % define meter after tempo to verify that is supported. X:11 Q:100 % define tempo using deprecated syntax % deprecated tempo syntax depends on unit note length. L:1/4 % define note length after tempo to verify that is supported. """) self.assertEqual(11, len(tunes)) self.assertEqual(0, len(exceptions)) self.assertEqual(60, tunes[1].tempos[0].qpm) self.assertEqual(100, tunes[2].tempos[0].qpm) self.assertEqual(240, tunes[3].tempos[0].qpm) self.assertEqual(200, tunes[4].tempos[0].qpm) self.assertEqual(200, tunes[5].tempos[0].qpm) self.assertEqual(120, tunes[6].tempos[0].qpm) self.assertEqual(120, tunes[7].tempos[0].qpm) self.assertEqual(75, tunes[8].tempos[0].qpm) self.assertEqual(0, len(tunes[9].tempos)) self.assertEqual(25, tunes[10].tempos[0].qpm) self.assertEqual(100, tunes[11].tempos[0].qpm)
def testInvalidCharacter(self): tunes, exceptions = abc_parser.parse_tunebook(""" X:1 Q:1/4=120 L:1/4 T:Test invalid notes!""") self.assertEqual(0, len(tunes)) self.assertEqual(1, len(exceptions)) self.assertTrue(isinstance(exceptions[0], abc_parser.InvalidCharacterException))
def testChords(self): tunes, exceptions = abc_parser.parse_tunebook(""" X:1 Q:1/4=120 L:1/4 T:Test [CEG]""") self.assertEqual(0, len(tunes)) self.assertEqual(1, len(exceptions)) self.assertTrue(isinstance(exceptions[0], abc_parser.ChordException))
def testTuplet(self): tunes, exceptions = abc_parser.parse_tunebook(""" X:1 Q:1/4=120 L:1/4 T:Test (3abc """) self.assertEqual(0, len(tunes)) self.assertEqual(1, len(exceptions)) self.assertTrue(isinstance(exceptions[0], abc_parser.TupletException))
def testTie(self): tunes, exceptions = abc_parser.parse_tunebook(""" X:1 Q:1/4=120 L:1/4 T:Test abc-|cba c4-c4 C.-C """) self.assertEqual(1, len(tunes)) self.assertEqual(0, len(exceptions)) self.assertEqual(10, len(tunes[1].notes))
def testSlur(self): tunes, exceptions = abc_parser.parse_tunebook(""" X:1 Q:1/4=120 L:1/4 T:Test (ABC) ( a b c ) (c (d e f) g a) """) self.assertEqual(1, len(tunes)) self.assertEqual(0, len(exceptions)) self.assertEqual(12, len(tunes[1].notes))
def testDecorations(self): tunes, exceptions = abc_parser.parse_tunebook(""" X:1 Q:1/4=120 L:1/4 T:Test .a~bHcLdMeOfPgSATbucvd """) self.assertEqual(1, len(tunes)) self.assertEqual(0, len(exceptions)) self.assertEqual(11, len(tunes[1].notes))
def testParseOctaves(self): tunes, exceptions = abc_parser.parse_tunebook("""X:1 T:Test CC,',C,C'c """) self.assertEqual(1, len(tunes)) self.assertEqual(0, len(exceptions)) expected_ns1 = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ ticks_per_quarter: 220 source_info: { source_type: SCORE_BASED encoding_type: ABC parser: MAGENTA_ABC } reference_number: 1 sequence_metadata { title: "Test" } notes { pitch: 60 velocity: 90 end_time: 0.25 } notes { pitch: 48 velocity: 90 start_time: 0.25 end_time: 0.5 } notes { pitch: 48 velocity: 90 start_time: 0.5 end_time: 0.75 } notes { pitch: 72 velocity: 90 start_time: 0.75 end_time: 1.0 } notes { pitch: 72 velocity: 90 start_time: 1.0 end_time: 1.25 } total_time: 1.25 """) self.assertProtoEquals(expected_ns1, tunes[1])
def testChordAnnotations(self): tunes, exceptions = abc_parser.parse_tunebook(""" X:1 Q:1/4=120 L:1/4 T:Test "G"G % verify that an empty annotation doesn't cause problems. ""D """) self.assertEqual(1, len(tunes)) self.assertEqual(0, len(exceptions)) expected_ns1 = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ ticks_per_quarter: 220 source_info: { source_type: SCORE_BASED encoding_type: ABC parser: MAGENTA_ABC } reference_number: 1 sequence_metadata { title: "Test" } tempos { qpm: 120 } notes { pitch: 67 velocity: 90 end_time: 0.5 } notes { pitch: 62 velocity: 90 start_time: 0.5 end_time: 1.0 } text_annotations { text: "G" annotation_type: CHORD_SYMBOL } text_annotations { time: 0.5 } total_time: 1.0 """) self.assertProtoEquals(expected_ns1, tunes[1])
def testLineContinuation(self): tunes, exceptions = abc_parser.parse_tunebook(r""" X:1 Q:1/4=120 L:1/4 T:Test abc \ cba| abc\ cba| abc cba| cdef|\ \ cedf:| """) self.assertEqual(1, len(tunes)) self.assertEqual(0, len(exceptions)) self.assertEqual(26, len(tunes[1].notes))
def testParseOctaves(self): tunes = abc_parser.parse_tunebook("""X:1 T:Test CC,',C,C'c """) self.assertEqual(1, len(tunes)) expected_ns1 = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ ticks_per_quarter: 220 source_info: { source_type: SCORE_BASED encoding_type: ABC parser: MAGENTA_ABC } reference_number: 1 sequence_metadata { title: "Test" } notes { pitch: 60 velocity: 90 } notes { pitch: 48 velocity: 90 } notes { pitch: 48 velocity: 90 } notes { pitch: 72 velocity: 90 } notes { pitch: 72 velocity: 90 } """) # TODO(fjord): add timing self.assertProtoEquals(expected_ns1, tunes[0])
def testParseEnglishAbc(self): tunes = abc_parser.parse_tunebook(ENGLISH_ABC) self.assertEqual(3, len(tunes)) expected_ns1 = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ ticks_per_quarter: 220 source_info: { source_type: SCORE_BASED encoding_type: ABC parser: MAGENTA_ABC } reference_number: 1 sequence_metadata { title: "Dusty Miller, The; Binny's Jig" artist: "Trad." composers: "Trad." } time_signatures { numerator: 3 denominator: 4 } key_signatures { key: G } """) # TODO(fjord): add notes del tunes[0].notes[:] self.assertProtoEquals(expected_ns1, tunes[0]) expected_ns2 = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ ticks_per_quarter: 220 source_info: { source_type: SCORE_BASED encoding_type: ABC parser: MAGENTA_ABC } reference_number: 2 sequence_metadata { title: "Old Sir Simon the King" artist: "Trad." composers: "Trad." } time_signatures { numerator: 9 denominator: 8 } time_signatures { numerator: 12 denominator: 8 } time_signatures { numerator: 9 denominator: 8 } key_signatures { key: G } """) # TODO(fjord): add notes and times. del tunes[1].notes[:] self.assertProtoEquals(expected_ns2, tunes[1]) expected_ns3 = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ ticks_per_quarter: 220 source_info: { source_type: SCORE_BASED encoding_type: ABC parser: MAGENTA_ABC } reference_number: 3 sequence_metadata { title: "William and Nancy; New Mown Hay; Legacy, The" artist: "Trad." composers: "Trad." } time_signatures { numerator: 6 denominator: 8 } key_signatures { key: G } """) # TODO(fjord): add notes and times. del tunes[2].notes[:] del tunes[2].text_annotations[:] self.assertProtoEquals(expected_ns3, tunes[2])
def testParseBrokenRhythm(self): tunes = abc_parser.parse_tunebook("""X:1 Q:1/4=120 L:1/4 M:3/4 T:Test B>cd B<cd """) self.assertEqual(1, len(tunes)) expected_ns1 = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ ticks_per_quarter: 220 source_info: { source_type: SCORE_BASED encoding_type: ABC parser: MAGENTA_ABC } reference_number: 1 sequence_metadata { title: "Test" } time_signatures { numerator: 3 denominator: 4 } tempos { qpm: 120 } notes { pitch: 71 velocity: 90 start_time: 0.0 end_time: 0.75 } notes { pitch: 72 velocity: 90 start_time: 0.75 end_time: 1.0 } notes { pitch: 74 velocity: 90 start_time: 1.0 end_time: 1.5 } notes { pitch: 71 velocity: 90 start_time: 1.5 end_time: 1.75 } notes { pitch: 72 velocity: 90 start_time: 1.75 end_time: 2.5 } notes { pitch: 74 velocity: 90 start_time: 2.5 end_time: 3.0 } """) self.assertProtoEquals(expected_ns1, tunes[0])
def testRepeats(self): # Several equivalent versions of the same tune. tunes, exceptions = abc_parser.parse_tunebook(""" X:1 Q:1/4=120 L:1/4 T:Test Bcd ::[]|[]:: Bcd ::| X:2 Q:1/4=120 L:1/4 T:Test Bcd :::: Bcd ::| X:3 Q:1/4=120 L:1/4 T:Test |::Bcd ::|:: Bcd ::| % This version contains mismatched repeat symbols. X:4 Q:1/4=120 L:1/4 T:Test |::Bcd ::|: Bcd ::| % This version is missing a repeat symbol at the end. X:5 Q:1/4=120 L:1/4 T:Test |:: Bcd ::|: Bcd | """) self.assertEqual(3, len(tunes)) self.assertEqual(2, len(exceptions)) self.assertTrue( isinstance(exceptions[0], abc_parser.RepeatParseException)) self.assertTrue( isinstance(exceptions[1], abc_parser.RepeatParseException)) expected_ns1 = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ ticks_per_quarter: 220 source_info: { source_type: SCORE_BASED encoding_type: ABC parser: MAGENTA_ABC } reference_number: 1 sequence_metadata { title: "Test" } tempos { qpm: 120 } notes { pitch: 71 velocity: 90 start_time: 0.0 end_time: 0.5 } notes { pitch: 72 velocity: 90 start_time: 0.5 end_time: 1.0 } notes { pitch: 74 velocity: 90 start_time: 1.0 end_time: 1.5 } notes { pitch: 71 velocity: 90 start_time: 1.5 end_time: 2.0 } notes { pitch: 72 velocity: 90 start_time: 2.0 end_time: 2.5 } notes { pitch: 74 velocity: 90 start_time: 2.5 end_time: 3.0 } section_annotations { time: 0 section_id: 0 } section_annotations { time: 1.5 section_id: 1 } section_groups { sections { section_id: 0 } num_times: 3 } section_groups { sections { section_id: 1 } num_times: 3 } total_time: 3.0 """) self.assertProtoEquals(expected_ns1, tunes[1]) # Other versions are identical except for the reference number. expected_ns2 = copy.deepcopy(expected_ns1) expected_ns2.reference_number = 2 self.assertProtoEquals(expected_ns2, tunes[2]) expected_ns3 = copy.deepcopy(expected_ns2) expected_ns3.reference_number = 3 self.assertProtoEquals(expected_ns3, tunes[3])
def testOneSidedRepeat(self): tunes, exceptions = abc_parser.parse_tunebook(""" X:1 Q:1/4=120 L:1/4 T:Test Bcd :| Bcd """) self.assertEqual(1, len(tunes)) self.assertEqual(0, len(exceptions)) expected_ns1 = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ ticks_per_quarter: 220 source_info: { source_type: SCORE_BASED encoding_type: ABC parser: MAGENTA_ABC } reference_number: 1 sequence_metadata { title: "Test" } tempos { qpm: 120 } notes { pitch: 71 velocity: 90 start_time: 0.0 end_time: 0.5 } notes { pitch: 72 velocity: 90 start_time: 0.5 end_time: 1.0 } notes { pitch: 74 velocity: 90 start_time: 1.0 end_time: 1.5 } notes { pitch: 71 velocity: 90 start_time: 1.5 end_time: 2.0 } notes { pitch: 72 velocity: 90 start_time: 2.0 end_time: 2.5 } notes { pitch: 74 velocity: 90 start_time: 2.5 end_time: 3.0 } section_annotations { time: 0 section_id: 0 } section_annotations { time: 1.5 section_id: 1 } section_groups { sections { section_id: 0 } num_times: 2 } section_groups { sections { section_id: 1 } num_times: 1 } total_time: 3.0 """) self.assertProtoEquals(expected_ns1, tunes[1])
def testNoteAccidentalsPerBar(self): tunes, exceptions = abc_parser.parse_tunebook(""" X:1 Q:1/4=120 L:1/4 T:Test GF^GGg|Gg """) self.assertEqual(1, len(tunes)) self.assertEqual(0, len(exceptions)) expected_ns1 = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ ticks_per_quarter: 220 source_info: { source_type: SCORE_BASED encoding_type: ABC parser: MAGENTA_ABC } reference_number: 1 sequence_metadata { title: "Test" } tempos { qpm: 120 } notes { pitch: 67 velocity: 90 start_time: 0.0 end_time: 0.5 } notes { pitch: 65 velocity: 90 start_time: 0.5 end_time: 1.0 } notes { pitch: 68 velocity: 90 start_time: 1.0 end_time: 1.5 } notes { pitch: 68 velocity: 90 start_time: 1.5 end_time: 2.0 } notes { pitch: 80 velocity: 90 start_time: 2.0 end_time: 2.5 } notes { pitch: 67 velocity: 90 start_time: 2.5 end_time: 3.0 } notes { pitch: 79 velocity: 90 start_time: 3.0 end_time: 3.5 } total_time: 3.5 """) self.assertProtoEquals(expected_ns1, tunes[1])
def testRepeats(self): # Several equivalent versions of the same tune. tunes, exceptions = abc_parser.parse_tunebook(""" X:1 Q:1/4=120 L:1/4 T:Test Bcd ::[]|[]:: Bcd ::| X:2 Q:1/4=120 L:1/4 T:Test Bcd :::: Bcd ::| X:3 Q:1/4=120 L:1/4 T:Test |::Bcd ::|:: Bcd ::| % This version contains mismatched repeat symbols. X:4 Q:1/4=120 L:1/4 T:Test |::Bcd ::|: Bcd ::| % This version is missing a repeat symbol at the end. X:5 Q:1/4=120 L:1/4 T:Test |:: Bcd ::|: Bcd | % Ambiguous repeat that should go to the last repeat symbol. X:6 Q:1/4=120 L:1/4 T:Test |:: Bcd ::| Bcd :| % Ambiguous repeat that should go to the last double bar. X:7 Q:1/4=120 L:1/4 T:Test |:: Bcd ::| Bcd || Bcd :| % Ambiguous repeat that should go to the last double bar. X:8 Q:1/4=120 L:1/4 T:Test || Bcd ::| Bcd || Bcd :| % Ensure double bar doesn't confuse declared repeat. X:9 Q:1/4=120 L:1/4 T:Test |:: B || cd ::| Bcd || |: Bcd :| """) self.assertEqual(7, len(tunes)) self.assertEqual(2, len(exceptions)) self.assertTrue(isinstance(exceptions[0], abc_parser.RepeatParseException)) self.assertTrue(isinstance(exceptions[1], abc_parser.RepeatParseException)) expected_ns1 = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ ticks_per_quarter: 220 source_info: { source_type: SCORE_BASED encoding_type: ABC parser: MAGENTA_ABC } reference_number: 1 sequence_metadata { title: "Test" } tempos { qpm: 120 } notes { pitch: 71 velocity: 90 start_time: 0.0 end_time: 0.5 } notes { pitch: 72 velocity: 90 start_time: 0.5 end_time: 1.0 } notes { pitch: 74 velocity: 90 start_time: 1.0 end_time: 1.5 } notes { pitch: 71 velocity: 90 start_time: 1.5 end_time: 2.0 } notes { pitch: 72 velocity: 90 start_time: 2.0 end_time: 2.5 } notes { pitch: 74 velocity: 90 start_time: 2.5 end_time: 3.0 } section_annotations { time: 0 section_id: 0 } section_annotations { time: 1.5 section_id: 1 } section_groups { sections { section_id: 0 } num_times: 3 } section_groups { sections { section_id: 1 } num_times: 3 } total_time: 3.0 """) self.assertProtoEquals(expected_ns1, tunes[1]) # Other versions are identical except for the reference number. expected_ns2 = copy.deepcopy(expected_ns1) expected_ns2.reference_number = 2 self.assertProtoEquals(expected_ns2, tunes[2]) expected_ns3 = copy.deepcopy(expected_ns1) expected_ns3.reference_number = 3 self.assertProtoEquals(expected_ns3, tunes[3]) # Also identical, except the last section is played only twice. expected_ns6 = copy.deepcopy(expected_ns1) expected_ns6.reference_number = 6 expected_ns6.section_groups[-1].num_times = 2 self.assertProtoEquals(expected_ns6, tunes[6]) expected_ns7 = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ ticks_per_quarter: 220 source_info: { source_type: SCORE_BASED encoding_type: ABC parser: MAGENTA_ABC } reference_number: 7 sequence_metadata { title: "Test" } tempos { qpm: 120 } notes { pitch: 71 velocity: 90 start_time: 0.0 end_time: 0.5 } notes { pitch: 72 velocity: 90 start_time: 0.5 end_time: 1.0 } notes { pitch: 74 velocity: 90 start_time: 1.0 end_time: 1.5 } notes { pitch: 71 velocity: 90 start_time: 1.5 end_time: 2.0 } notes { pitch: 72 velocity: 90 start_time: 2.0 end_time: 2.5 } notes { pitch: 74 velocity: 90 start_time: 2.5 end_time: 3.0 } notes { pitch: 71 velocity: 90 start_time: 3.0 end_time: 3.5 } notes { pitch: 72 velocity: 90 start_time: 3.5 end_time: 4.0 } notes { pitch: 74 velocity: 90 start_time: 4.0 end_time: 4.5 } section_annotations { time: 0 section_id: 0 } section_annotations { time: 1.5 section_id: 1 } section_annotations { time: 3.0 section_id: 2 } section_groups { sections { section_id: 0 } num_times: 3 } section_groups { sections { section_id: 1 } num_times: 1 } section_groups { sections { section_id: 2 } num_times: 2 } total_time: 4.5 """) self.assertProtoEquals(expected_ns7, tunes[7]) expected_ns8 = copy.deepcopy(expected_ns7) expected_ns8.reference_number = 8 self.assertProtoEquals(expected_ns8, tunes[8]) expected_ns9 = copy.deepcopy(expected_ns7) expected_ns9.reference_number = 9 self.assertProtoEquals(expected_ns9, tunes[9])
def testParseBrokenRhythm(self): # These tunes should be equivalent. tunes, exceptions = abc_parser.parse_tunebook(""" X:1 Q:1/4=120 L:1/4 M:3/4 T:Test B>cd B<cd X:2 Q:1/4=120 L:1/4 M:3/4 T:Test B3/2c/2d B/2c3/2d X:3 Q:1/4=120 L:1/4 M:3/4 T:Test B3/c/d B/c3/d """) self.assertEqual(3, len(tunes)) self.assertEqual(0, len(exceptions)) expected_ns1 = common_testing_lib.parse_test_proto( music_pb2.NoteSequence, """ ticks_per_quarter: 220 source_info: { source_type: SCORE_BASED encoding_type: ABC parser: MAGENTA_ABC } reference_number: 1 sequence_metadata { title: "Test" } time_signatures { numerator: 3 denominator: 4 } tempos { qpm: 120 } notes { pitch: 71 velocity: 90 start_time: 0.0 end_time: 0.75 } notes { pitch: 72 velocity: 90 start_time: 0.75 end_time: 1.0 } notes { pitch: 74 velocity: 90 start_time: 1.0 end_time: 1.5 } notes { pitch: 71 velocity: 90 start_time: 1.5 end_time: 1.75 } notes { pitch: 72 velocity: 90 start_time: 1.75 end_time: 2.5 } notes { pitch: 74 velocity: 90 start_time: 2.5 end_time: 3.0 } total_time: 3.0 """) self.assertProtoEquals(expected_ns1, tunes[1]) expected_ns2 = copy.deepcopy(expected_ns1) expected_ns2.reference_number = 2 self.assertProtoEquals(expected_ns2, tunes[2]) expected_ns2.reference_number = 3 self.assertProtoEquals(expected_ns2, tunes[3])