def test_contructor(self): Note(MusicalPitch.new_from_notename("c'"), Duration.new_from_string("4.")) self.assertRaises(AssertionError, Note, "4.", MusicalPitch.new_from_notename("c'")) self.assertRaises(AssertionError, Note, Duration.new_from_string("4."), "c'")
def test_add_d5(self): a = MusicalPitch.new_from_notename("b") b = Interval("-d5") self.assertEquals((a+b).get_octave_notename(), "eis") a = MusicalPitch.new_from_notename("bis") b = Interval("-d5") self.assertEquals((a+b).get_octave_notename(), "eisis") a = MusicalPitch.new_from_notename("bisis") b = Interval("-d5") self.assertEquals((a+b).get_octave_notename(), "fisis")
def test_add_d5(self): a = MusicalPitch.new_from_notename("b") b = Interval("-d5") self.assertEquals((a + b).get_octave_notename(), "eis") a = MusicalPitch.new_from_notename("bis") b = Interval("-d5") self.assertEquals((a + b).get_octave_notename(), "eisis") a = MusicalPitch.new_from_notename("bisis") b = Interval("-d5") self.assertEquals((a + b).get_octave_notename(), "fisis")
def test_tuplets(self): n1 = Note(MusicalPitch.new_from_notename("g'"), Duration(8, 0, Rat(2, 3))) n2 = Note(MusicalPitch.new_from_notename("g'"), Duration(8, 0, Rat(2, 3))) n3 = Note(MusicalPitch.new_from_notename("g'"), Duration(8, 0, Rat(2, 3))) self.score.voice11.append(n1) self.score.voice11.append(n2) self.score.voice11.append(n3) self.score.voice11.tuplet(Rat(2, 3), const.UP, [n1, n2, n3])
def question(f, b, c): low = 60 tone_a = MusicalPitch.new_from_int(low) tone_b = MusicalPitch.new_from_int(low + b) tone_c = MusicalPitch.new_from_int(low + c) print >> f, 'question {' print >> f, ' question_text = _("Sing the three tones")' print >> f, ' music = voice("< %s2 %s %s >")' % ( (tone_a.get_octave_notename(), tone_b.get_octave_notename(), tone_c.get_octave_notename())) print >> f, ' answer = voice("%s8 %s %s")' % ( (tone_a.get_octave_notename(), tone_b.get_octave_notename(), tone_c.get_octave_notename())) print >> f, '}'
def question(f, b, c): low = 60 tone_a = MusicalPitch.new_from_int(low) tone_b = MusicalPitch.new_from_int(low + b) tone_c = MusicalPitch.new_from_int(low + c) f.write('question {\n') f.write(' question_text = _("Sing the three tones")\n') f.write(' music = voice("< %s2 %s %s >")\n' % ( (tone_a.get_octave_notename(), tone_b.get_octave_notename(), tone_c.get_octave_notename()))) f.write(' answer = voice("%s8 %s %s")\n' % ( (tone_a.get_octave_notename(), tone_b.get_octave_notename(), tone_c.get_octave_notename()))) f.write('}\n')
def test_trans(self): gettext.translation('solfege', './share/locale/', languages=['nb_NO']).install() n = MusicalPitch.new_from_notename("b,,") self.assertEqual(n.get_octave_notename(), "b,,") self.assertEqual(n.get_user_octave_notename(), "<sub>1</sub>H") self.assertEqual(n.get_user_notename(), "h")
def test_bar_fill_skips(self): n1 = Note(MusicalPitch.new_from_notename("g'"), Duration(4, 0)) self.score.voice11.append(n1) self.score.voice11.append(Rest(Duration.new_from_string("4"))) self.bp.fill_skips() self.assertTrue(isinstance(self.score.voice11.m_tdict[Rat(0, 1)]['elem'][0], Note)) self.assertTrue(isinstance(self.score.voice11.m_tdict[Rat(1, 4)]['elem'][0], Rest)) self.assertTrue(isinstance(self.score.voice11.m_tdict[Rat(1, 2)]['elem'][0], Skip)) self.assertTrue(isinstance(self.score.voice11.m_tdict[Rat(3, 4)]['elem'][0], Skip))
def new_from_string(string): s = string.strip() m = re_melodic.match(s) if m.end() < len(s) - 1: # FIXME: raise ValueError like rest raise Note.Exception("characters left in string", string) return Note( MusicalPitch.new_from_notename("%s%s" % (m.group('notename'), m.group('octave'))), Duration.new_from_string("%s%s" % (m.group('len'), m.group('dots'))) )
def new_from_string(string): s = string.strip() m = re_melodic.match(s) if m.end() < len(s) - 1: # FIXME: raise ValueError like rest raise Note.Exception("characters left in string", string) return Note( MusicalPitch.new_from_notename( "%s%s" % (m.group('notename'), m.group('octave'))), Duration.new_from_string("%s%s" % (m.group('len'), m.group('dots'))))
def test_bar_fill_skips(self): n1 = Note(MusicalPitch.new_from_notename("g'"), Duration(4, 0)) self.score.voice11.append(n1) self.score.voice11.append(Rest(Duration.new_from_string("4"))) self.bp.fill_skips() self.assertTrue( isinstance(self.score.voice11.m_tdict[Rat(0, 1)]['elem'][0], Note)) self.assertTrue( isinstance(self.score.voice11.m_tdict[Rat(1, 4)]['elem'][0], Rest)) self.assertTrue( isinstance(self.score.voice11.m_tdict[Rat(1, 2)]['elem'][0], Skip)) self.assertTrue( isinstance(self.score.voice11.m_tdict[Rat(3, 4)]['elem'][0], Skip))
def refill_accidentals_info(self, key): """Fill the .m_accidentals_info dict with the accidentals that exist in the key signature `key`. """ self.m_accidentals_info = {} for step in range(MusicalPitch.LOWEST_STEPS, MusicalPitch.HIGHEST_STEPS + 1): self.m_accidentals_info[step] = 0 for a in mpdutils.key_to_accidentals(key): n = MusicalPitch.new_from_notename(a) for octave in range(-4, 7): n.m_octave_i = octave if n.semitone_pitch() < 128: if a[-4:] == 'eses': self.m_accidentals_info[n.steps()] = -2 elif a[-2:] == 'es': self.m_accidentals_info[n.steps()] = -1 elif a[-4:] == 'isis': self.m_accidentals_info[n.steps()] = 2 else: self.m_accidentals_info[n.steps()] = 1
def test_normalize_double_accidental(self): for a, b in (("c", "c"), ("cisis", "d"), ("disis", "e"), ("eisis", "fis"), ("fisis", "g"), ("gisis", "a"), ("aisis", "b"), ("bisis", "cis'"), ("ceses", "bes,"), ("deses", "c"), ("eses", "d"), ("feses", "ees"), ("geses", "f"), ("ases", "g"), ("beses", "a"), ): n = MusicalPitch.new_from_notename(a) n.normalize_double_accidental() self.assertEquals(n.get_octave_notename(), b)
def test_normalize_double_accidental(self): for a, b in ( ("c", "c"), ("cisis", "d"), ("disis", "e"), ("eisis", "fis"), ("fisis", "g"), ("gisis", "a"), ("aisis", "b"), ("bisis", "cis'"), ("ceses", "bes,"), ("deses", "c"), ("eses", "d"), ("feses", "ees"), ("geses", "f"), ("ases", "g"), ("beses", "a"), ): n = MusicalPitch.new_from_notename(a) n.normalize_double_accidental() self.assertEqual(n.get_octave_notename(), b)
def test_add_interval_fail(self): n = MusicalPitch.new_from_int(120) i = Interval("M10") self.assertRaises(ValueError, lambda: n + i)
def test_add_integer_fail(self): n = MusicalPitch.new_from_int(120) self.assertRaises(ValueError, lambda: n + 20)
def test_subtract(self): a = MusicalPitch.new_from_notename("g") b = MusicalPitch.new_from_notename("f") self.assertEquals(a - b, 2)
def test_internals(self): a = MusicalPitch() self.assertTrue(a.m_octave_i == a.m_accidental_i == 0)
def notename_to_ylinepos(n): n = MusicalPitch.new_from_notename(n) return self.steps_to_ylinepos(n.steps())
def test_add(self): n = MusicalPitch.new_from_notename('c') n = n + 2 self.assertEqual(n.get_octave_notename(), 'd')
def test_addition(self): a = MusicalPitch.new_from_notename("c'") b = Interval("m2") self.assertEquals((a + b).get_octave_notename(), "des'")
def parse_to_score_object(music): lexer = Lexer(music) relative_mode = None relto = None transpose_pitch = None TOPLEVEL = 1 # 'toplevel' NOTES = 2 # 'notes' START_OF_CHORD = 3 # 'start-of-chord' CHORD = 4 # 'chord' context = TOPLEVEL score = elems.Score() chord_duration = None cur_duration = Duration(4, 0) tie_is_in_the_air = 0 beam = None # None when not parsing notes in a tuplet. Item 0 is the ration and 1.. is the notes times = None cur_staff = None # we set it just so pyflakes does not complain. relto_backup = None # This variable is set to the duration of the pickup bar from we parse # \partial nn until the bar has been created. partial = None for toc, toc_data in lexer: try: if toc_data.m_duration: cur_duration = toc_data.m_duration.clone() except AttributeError: pass if toc == Lexer.STAFF: assert context == TOPLEVEL cur_staff = score.add_staff(elems.Staff) cur_voice = cur_staff.m_voices[-1] stem_dir = const.BOTH tuplet_dir = const.BOTH relative_mode = None timepos = Rat(0) last_pos = timepos elif toc == Lexer.RHYTHMSTAFF: assert context == TOPLEVEL cur_staff = score.add_staff(elems.RhythmStaff) cur_voice = cur_staff.m_voices[-1] stem_dir = const.BOTH tuplet_dir = const.BOTH relative_mode = None timepos = Rat(0) last_pos = timepos elif toc == Lexer.VOICE: if not cur_staff: raise ParseError("Don't use \\addvoice before \\staff", lexer) relative_mode = None timepos = Rat(0) cur_voice = cur_staff.add_voice() elif toc == Lexer.RELATIVE: assert not relative_mode relative_mode = 1 relto = toc_data elif toc == Lexer.TRANSPOSE: transpose_pitch = toc_data elif toc == Lexer.PARTIAL: partial = toc_data elif toc == Lexer.TIME: if not cur_staff: raise ParseError("\\time can not be used before \\staff", lexer) # FIXME # Also now, we only allow the first voice to change time signatures if cur_staff.m_voices.index(cur_voice) != 0: raise ParseError("only timesig in first voice", lexer) # FIXME: we are stricter with time signatures that both solfege 3.16 # and LilyPond if not cur_voice.is_bar_full(): raise ParseError("timesig change only when bar is full!", lexer) if partial: score.add_partial_bar(partial, toc_data) partial = None else: score.add_bar(toc_data) elif toc == Lexer.KEY: p = MusicalPitch.new_from_notename(toc_data[0]) if transpose_pitch: p.transpose_by_musicalpitch(transpose_pitch) k = (p.get_notename(), toc_data[1]) if not cur_staff: raise ParseError("\\key can not be used before \\staff", lexer) cur_staff.set_key_signature(k, timepos) elif toc == Lexer.TIMES: if not times: times = [toc_data] else: raise ParseError(r"\times nn/nn does not nest", lexer) elif toc == Lexer.CLEF: try: cur_staff.set_clef(toc_data, timepos) except elems.UnknownClefException as e: e.m_lineno, e.m_linepos1, e.m_linepos2 = lexer.get_error_location() raise elif toc == '|': if timepos != score.get_bar_at(last_pos).end(): logging.warning("Bar check failed at %s", timepos) elif toc == '{': if (context == TOPLEVEL): context = NOTES # if not cur_staff.m_coldict[Rat(0, 1)].m_keysignature: # FIXME dont understand if transpose_pitch: k = (transpose_pitch.get_notename(), 'major') else: k = ('c', 'major') cur_voice.set_key_signature(k) else: raise ParseError("Token '{' not allowed here.", lexer) elif toc == '<': if context == NOTES: context = START_OF_CHORD else: raise ParseError("Token '<' not allowed here.", lexer) elif toc == '>': if context == CHORD: if tie_is_in_the_air: # The 3.16-parser only handles ties between whole chords, not # single tones of a chord. if tie_is_in_the_air: for last_note in cur_voice.m_tdict[last_pos]['elem']: for cur_note in cur_voice.m_tdict[timepos]['elem']: if last_note.m_musicalpitch.get_octave_notename() == cur_note.m_musicalpitch.get_octave_notename(): cur_voice.tie([last_note, cur_note]) tie_is_in_the_air = 0 last_pos = timepos timepos = timepos + chord_duration.get_rat_value() chord_duration = None relto = relto_backup relto_backup = None context = NOTES else: raise ParseError("Token '>' not allowed here.", lexer) elif toc == '}': if context == NOTES: if times: cur_voice.tuplet(times[0], tuplet_dir, times[1:]) times = None cur_duration.m_tuplet = Rat(1, 1) else: context = TOPLEVEL else: raise ParseError("Token '}' not allowed here.", lexer) elif toc == '[': beam = [] elif toc == ']': cur_voice.beam(beam) beam = None elif toc == '~': tie_is_in_the_air = 1 elif toc == Lexer.NOTE and (context in [NOTES, CHORD, START_OF_CHORD]): # FIXME check if toc_data.m_duration will ever be undefined. # If not we can do this: if not toc_data.m_duration: if not getattr(toc_data, 'm_duration', None): toc_data.m_duration = cur_duration.clone() if times: toc_data.m_duration.m_tuplet = times[0].clone() if relative_mode: toc_data.m_pitch = musicalpitch_relative( relto, toc_data.m_pitch) relto = toc_data.m_pitch.clone() if transpose_pitch: toc_data.transpose(transpose_pitch) if partial: score.add_partial_bar(partial, None) partial = None if context == NOTES: note = elems.Note(toc_data.m_pitch, toc_data.m_duration) try: cur_voice.append(note, stem_dir) except elems.Voice.BarFullException as e: raise ParseError(str(e), lexer) if beam is not None: beam.append(note) if times is not None: times.append(note) # The 3.16-parser only handles ties between whole chords, not # single tones of a chord. if tie_is_in_the_air: for note in cur_voice.m_tdict[last_pos]['elem']: for n in cur_voice.m_tdict[timepos]['elem']: if n.m_musicalpitch.get_octave_notename() == note.m_musicalpitch.get_octave_notename(): cur_voice.tie([note, n]) tie_is_in_the_air = 0 last_pos = timepos timepos = timepos + toc_data.m_duration.get_rat_value() elif context == START_OF_CHORD: cur_voice.append(elems.Note(toc_data.m_pitch, toc_data.m_duration), stem_dir) relto_backup = relto chord_duration = toc_data.m_duration context = CHORD elif context == CHORD: cur_voice.add_to(timepos, elems.Note(toc_data.m_pitch, toc_data.m_duration)) elif toc == Lexer.SKIP and context == NOTES: if toc_data.m_duration: cur_duration = toc_data.m_duration.clone() else: toc_data.m_duration = cur_duration.clone() skip = elems.Skip(toc_data.m_duration) cur_voice.append(skip) last_pos = timepos timepos = timepos + toc_data.m_duration.get_rat_value() elif toc == Lexer.REST and context == NOTES: if toc_data.m_duration: cur_duration = toc_data.m_duration.clone() else: toc_data.m_duration = cur_duration.clone() rest = elems.Rest(toc_data.m_duration) cur_voice.append(rest) last_pos = timepos timepos += toc_data.m_duration.get_rat_value() elif toc == Lexer.STEMDIR: stem_dir = toc_data elif toc == Lexer.TUPLETDIR: tuplet_dir = toc_data else: raise ParseError(toc, lexer) return score
def int_to_user_octave_notename(i): return MusicalPitch.new_from_int(i).get_user_octave_notename()
def test_trans(self): gettext.translation('solfege', './share/locale/', languages=['nb_NO']).install() n = MusicalPitch.new_from_notename("b,,") self.assertEquals(n.get_octave_notename(), "b,,") self.assertEquals(n.get_user_octave_notename(), "<sub>1</sub>H") self.assertEquals(n.get_user_notename(), "h")
def test_single_tuplet(self): n1 = Note(MusicalPitch.new_from_notename("g'"), Duration(8, 0, Rat(2, 3))) self.score.voice11.append(n1) self.score.voice11.tuplet(Rat(2, 3), const.UP, [n1]) self.assertEqual(n1.w_parent().m_tupletinfo, 'end')
def test_pitch_class(self): for n, i in (("c", 0), ("cis", 1), ("g", 7), ("ges", 6), ("gisis", 9), ("b", 11), ("bis", 0), ("bisis", 1), ("ces", 11)): p = MusicalPitch.new_from_notename(n) self.assertEquals(p.pitch_class(), i)
def parse_to_score_object(music): lexer = Lexer(music) relative_mode = None relto = None transpose_pitch = None TOPLEVEL = 1#'toplevel' NOTES = 2#'notes' START_OF_CHORD = 3#'start-of-chord' CHORD = 4#'chord' context = TOPLEVEL score = elems.Score() chord_duration = None cur_duration = Duration(4, 0) tie_is_in_the_air = 0 beam = None # None when not parsing notes in a tuplet. Item 0 is the ration and 1.. is the notes times = None cur_staff = None # This variable is set to the duration of the pickup bar from we parse # \partial nn until the bar has been created. partial = None for toc, toc_data in lexer: try: if toc_data.m_duration: cur_duration = toc_data.m_duration.clone() except AttributeError: pass if toc == Lexer.STAFF: assert context == TOPLEVEL cur_staff = score.add_staff(elems.Staff) cur_voice = cur_staff.m_voices[-1] stem_dir = const.BOTH tuplet_dir = const.BOTH relative_mode = None timepos = Rat(0) last_pos = timepos elif toc == Lexer.RHYTHMSTAFF: assert context == TOPLEVEL cur_staff = score.add_staff(elems.RhythmStaff) cur_voice = cur_staff.m_voices[-1] stem_dir = const.BOTH tuplet_dir = const.BOTH relative_mode = None timepos = Rat(0) last_pos = timepos elif toc == Lexer.VOICE: if not cur_staff: raise ParseError("Don't use \\addvoice before \\staff", lexer) relative_mode = None timepos = Rat(0) cur_voice = cur_staff.add_voice() elif toc == Lexer.RELATIVE: assert not relative_mode relative_mode = 1 relto = toc_data elif toc == Lexer.TRANSPOSE: transpose_pitch = toc_data elif toc == Lexer.PARTIAL: partial = toc_data elif toc == Lexer.TIME: if not cur_staff: raise ParseError(u"\\time can not be used before \\staff", lexer) # FIXME # Also now, we only allow the first voice to change time signatures if cur_staff.m_voices.index(cur_voice) != 0: raise ParseError(u"only timesig in first voice", lexer) # FIXME: we are stricter with time signatures that both solfege 3.16 # and LilyPond if not cur_voice.is_bar_full(): raise ParseError(u"timesig change only when bar is full!", lexer) if partial: score.add_partial_bar(partial, toc_data) partial = None else: score.add_bar(toc_data) elif toc == Lexer.KEY: p = MusicalPitch.new_from_notename(toc_data[0]) if transpose_pitch: p.transpose_by_musicalpitch(transpose_pitch) k = (p.get_notename(), toc_data[1]) if not cur_staff: raise ParseError(u"\\key can not be used before \\staff", lexer) cur_staff.set_key_signature(k, timepos) elif toc == Lexer.TIMES: if not times: times = [toc_data] else: raise ParseError(r"\times nn/nn does not nest", lexer) elif toc == Lexer.CLEF: try: cur_staff.set_clef(toc_data, timepos) except elems.UnknownClefException, e: e.m_lineno, e.m_linepos1, e.m_linepos2 = lexer.get_error_location() raise elif toc == '|': if timepos != score.get_bar_at(last_pos).end(): logging.warning("Bar check failed at %s", timepos)
def test_subtract(self): a = MusicalPitch.new_from_notename("g") b = MusicalPitch.new_from_notename("f") self.assertEqual(a - b, 2)
def __init__(self, notename, duration, dots): ItHasDuration.__init__(self, duration, dots) self.m_pitch = MusicalPitch.new_from_notename(notename)
def test_new_from_int(self): for x in range(-12, 12): i = Interval.new_from_int(x) a = MusicalPitch.new_from_notename("bisis") b = a + i self.assertEquals(int(a) + x, int(b))
def parse_to_score_object(music): lexer = Lexer(music) relative_mode = None relto = None transpose_pitch = None TOPLEVEL = 1 #'toplevel' NOTES = 2 #'notes' START_OF_CHORD = 3 #'start-of-chord' CHORD = 4 #'chord' context = TOPLEVEL score = elems.Score() chord_duration = None cur_duration = Duration(4, 0) tie_is_in_the_air = 0 beam = None # None when not parsing notes in a tuplet. Item 0 is the ration and 1.. is the notes times = None cur_staff = None # This variable is set to the duration of the pickup bar from we parse # \partial nn until the bar has been created. partial = None for toc, toc_data in lexer: try: if toc_data.m_duration: cur_duration = toc_data.m_duration.clone() except AttributeError: pass if toc == Lexer.STAFF: assert context == TOPLEVEL cur_staff = score.add_staff(elems.Staff) cur_voice = cur_staff.m_voices[-1] stem_dir = const.BOTH tuplet_dir = const.BOTH relative_mode = None timepos = Rat(0) last_pos = timepos elif toc == Lexer.RHYTHMSTAFF: assert context == TOPLEVEL cur_staff = score.add_staff(elems.RhythmStaff) cur_voice = cur_staff.m_voices[-1] stem_dir = const.BOTH tuplet_dir = const.BOTH relative_mode = None timepos = Rat(0) last_pos = timepos elif toc == Lexer.VOICE: if not cur_staff: raise ParseError("Don't use \\addvoice before \\staff", lexer) relative_mode = None timepos = Rat(0) cur_voice = cur_staff.add_voice() elif toc == Lexer.RELATIVE: assert not relative_mode relative_mode = 1 relto = toc_data elif toc == Lexer.TRANSPOSE: transpose_pitch = toc_data elif toc == Lexer.PARTIAL: partial = toc_data elif toc == Lexer.TIME: if not cur_staff: raise ParseError(u"\\time can not be used before \\staff", lexer) # FIXME # Also now, we only allow the first voice to change time signatures if cur_staff.m_voices.index(cur_voice) != 0: raise ParseError(u"only timesig in first voice", lexer) # FIXME: we are stricter with time signatures that both solfege 3.16 # and LilyPond if not cur_voice.is_bar_full(): raise ParseError(u"timesig change only when bar is full!", lexer) if partial: score.add_partial_bar(partial, toc_data) partial = None else: score.add_bar(toc_data) elif toc == Lexer.KEY: p = MusicalPitch.new_from_notename(toc_data[0]) if transpose_pitch: p.transpose_by_musicalpitch(transpose_pitch) k = (p.get_notename(), toc_data[1]) if not cur_staff: raise ParseError(u"\\key can not be used before \\staff", lexer) cur_staff.set_key_signature(k, timepos) elif toc == Lexer.TIMES: if not times: times = [toc_data] else: raise ParseError(r"\times nn/nn does not nest", lexer) elif toc == Lexer.CLEF: try: cur_staff.set_clef(toc_data, timepos) except elems.UnknownClefException, e: e.m_lineno, e.m_linepos1, e.m_linepos2 = lexer.get_error_location( ) raise elif toc == '|': if timepos != score.get_bar_at(last_pos).end(): logging.warning("Bar check failed at %s", timepos)
def test_add(self): n = MusicalPitch.new_from_notename('c') n = n + 2 self.assertEquals(n.get_octave_notename(), 'd')
def notename_to_int(n): return MusicalPitch.new_from_notename(n).semitone_pitch()
def test_pitch_class(self): for n, i in (("c", 0), ("cis", 1), ("g", 7), ("ges", 6), ("gisis", 9), ("b", 11), ("bis", 0), ("bisis", 1), ("ces", 11)): p = MusicalPitch.new_from_notename(n) self.assertEqual(p.pitch_class(), i)
def test_addition(self): a = MusicalPitch.new_from_notename("c'") b = Interval("m2") self.assertEquals((a+b).get_octave_notename(), "des'")
def int_to_user_notename(i): p = MusicalPitch() p.set_from_int(i) return p.get_user_octave_notename()
def parse_to_score_object(music): lexer = Lexer(music) relative_mode = None relto = None transpose_pitch = None TOPLEVEL = 1 # 'toplevel' NOTES = 2 # 'notes' START_OF_CHORD = 3 # 'start-of-chord' CHORD = 4 # 'chord' context = TOPLEVEL score = elems.Score() chord_duration = None cur_duration = Duration(4, 0) tie_is_in_the_air = 0 beam = None # None when not parsing notes in a tuplet. Item 0 is the ration and 1.. is the notes times = None cur_staff = None # we set it just so pyflakes does not complain. relto_backup = None # This variable is set to the duration of the pickup bar from we parse # \partial nn until the bar has been created. partial = None for toc, toc_data in lexer: try: if toc_data.m_duration: cur_duration = toc_data.m_duration.clone() except AttributeError: pass if toc == Lexer.STAFF: assert context == TOPLEVEL cur_staff = score.add_staff(elems.Staff) cur_voice = cur_staff.m_voices[-1] stem_dir = const.BOTH tuplet_dir = const.BOTH relative_mode = None timepos = Rat(0) last_pos = timepos elif toc == Lexer.RHYTHMSTAFF: assert context == TOPLEVEL cur_staff = score.add_staff(elems.RhythmStaff) cur_voice = cur_staff.m_voices[-1] stem_dir = const.BOTH tuplet_dir = const.BOTH relative_mode = None timepos = Rat(0) last_pos = timepos elif toc == Lexer.VOICE: if not cur_staff: raise ParseError("Don't use \\addvoice before \\staff", lexer) relative_mode = None timepos = Rat(0) cur_voice = cur_staff.add_voice() elif toc == Lexer.RELATIVE: assert not relative_mode relative_mode = 1 relto = toc_data elif toc == Lexer.TRANSPOSE: transpose_pitch = toc_data elif toc == Lexer.PARTIAL: partial = toc_data elif toc == Lexer.TIME: if not cur_staff: raise ParseError("\\time can not be used before \\staff", lexer) # FIXME # Also now, we only allow the first voice to change time signatures if cur_staff.m_voices.index(cur_voice) != 0: raise ParseError("only timesig in first voice", lexer) # FIXME: we are stricter with time signatures that both solfege 3.16 # and LilyPond if not cur_voice.is_bar_full(): raise ParseError("timesig change only when bar is full!", lexer) if partial: score.add_partial_bar(partial, toc_data) partial = None else: score.add_bar(toc_data) elif toc == Lexer.KEY: p = MusicalPitch.new_from_notename(toc_data[0]) if transpose_pitch: p.transpose_by_musicalpitch(transpose_pitch) k = (p.get_notename(), toc_data[1]) if not cur_staff: raise ParseError("\\key can not be used before \\staff", lexer) cur_staff.set_key_signature(k, timepos) elif toc == Lexer.TIMES: if not times: times = [toc_data] else: raise ParseError(r"\times nn/nn does not nest", lexer) elif toc == Lexer.CLEF: try: cur_staff.set_clef(toc_data, timepos) except elems.UnknownClefException as e: e.m_lineno, e.m_linepos1, e.m_linepos2 = lexer.get_error_location( ) raise elif toc == '|': if timepos != score.get_bar_at(last_pos).end(): logging.warning("Bar check failed at %s", timepos) elif toc == '{': if (context == TOPLEVEL): context = NOTES # if not cur_staff.m_coldict[Rat(0, 1)].m_keysignature: # FIXME dont understand if transpose_pitch: k = (transpose_pitch.get_notename(), 'major') else: k = ('c', 'major') cur_voice.set_key_signature(k) else: raise ParseError("Token '{' not allowed here.", lexer) elif toc == '<': if context == NOTES: context = START_OF_CHORD else: raise ParseError("Token '<' not allowed here.", lexer) elif toc == '>': if context == CHORD: if tie_is_in_the_air: # The 3.16-parser only handles ties between whole chords, not # single tones of a chord. if tie_is_in_the_air: for last_note in cur_voice.m_tdict[last_pos]['elem']: for cur_note in cur_voice.m_tdict[timepos]['elem']: if last_note.m_musicalpitch.get_octave_notename( ) == cur_note.m_musicalpitch.get_octave_notename( ): cur_voice.tie([last_note, cur_note]) tie_is_in_the_air = 0 last_pos = timepos timepos = timepos + chord_duration.get_rat_value() chord_duration = None relto = relto_backup relto_backup = None context = NOTES else: raise ParseError("Token '>' not allowed here.", lexer) elif toc == '}': if context == NOTES: if times: cur_voice.tuplet(times[0], tuplet_dir, times[1:]) times = None cur_duration.m_tuplet = Rat(1, 1) else: context = TOPLEVEL else: raise ParseError("Token '}' not allowed here.", lexer) elif toc == '[': beam = [] elif toc == ']': cur_voice.beam(beam) beam = None elif toc == '~': tie_is_in_the_air = 1 elif toc == Lexer.NOTE and (context in [NOTES, CHORD, START_OF_CHORD]): # FIXME check if toc_data.m_duration will ever be undefined. # If not we can do this: if not toc_data.m_duration: if not getattr(toc_data, 'm_duration', None): toc_data.m_duration = cur_duration.clone() if times: toc_data.m_duration.m_tuplet = times[0].clone() if relative_mode: toc_data.m_pitch = musicalpitch_relative( relto, toc_data.m_pitch) relto = toc_data.m_pitch.clone() if transpose_pitch: toc_data.transpose(transpose_pitch) if partial: score.add_partial_bar(partial, None) partial = None if context == NOTES: note = elems.Note(toc_data.m_pitch, toc_data.m_duration) try: cur_voice.append(note, stem_dir) except elems.Voice.BarFullException as e: raise ParseError(str(e), lexer) if beam is not None: beam.append(note) if times is not None: times.append(note) # The 3.16-parser only handles ties between whole chords, not # single tones of a chord. if tie_is_in_the_air: for note in cur_voice.m_tdict[last_pos]['elem']: for n in cur_voice.m_tdict[timepos]['elem']: if n.m_musicalpitch.get_octave_notename( ) == note.m_musicalpitch.get_octave_notename(): cur_voice.tie([note, n]) tie_is_in_the_air = 0 last_pos = timepos timepos = timepos + toc_data.m_duration.get_rat_value() elif context == START_OF_CHORD: cur_voice.append( elems.Note(toc_data.m_pitch, toc_data.m_duration), stem_dir) relto_backup = relto chord_duration = toc_data.m_duration context = CHORD elif context == CHORD: cur_voice.add_to( timepos, elems.Note(toc_data.m_pitch, toc_data.m_duration)) elif toc == Lexer.SKIP and context == NOTES: if toc_data.m_duration: cur_duration = toc_data.m_duration.clone() else: toc_data.m_duration = cur_duration.clone() skip = elems.Skip(toc_data.m_duration) cur_voice.append(skip) last_pos = timepos timepos = timepos + toc_data.m_duration.get_rat_value() elif toc == Lexer.REST and context == NOTES: if toc_data.m_duration: cur_duration = toc_data.m_duration.clone() else: toc_data.m_duration = cur_duration.clone() rest = elems.Rest(toc_data.m_duration) cur_voice.append(rest) last_pos = timepos timepos += toc_data.m_duration.get_rat_value() elif toc == Lexer.STEMDIR: stem_dir = toc_data elif toc == Lexer.TUPLETDIR: tuplet_dir = toc_data else: raise ParseError(toc, lexer) return score