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 fill_skips(self, voice): """ Add Skips at the end of the bar, so that it is filled. We assume that any elements already added are placed at the correct timepos. """ # nt = short for "next timepos", the timepos to start fill skips to if voice.get_timeposes_of(self): nt = voice.get_timeposes_of(self)[-1] nt = nt + voice.m_tdict[nt]['elem'][0].m_duration.get_rat_value() else: # we get here if the bar is empty nt = self.m_timepos default_skip = Rat(1, 4) # pos within default skip pp = nt - int (nt / default_skip) * default_skip if pp != Rat(0, 1): # Here we add a skip so that the next column will be X times # default_skip voice.set_elem([Skip(Duration.new_from_rat(default_skip - pp))], nt) nt += (default_skip - pp) # And the we fill the bar with Skips as long as default_skip. while nt < self.end(): voice.set_elem([Skip(Duration.new_from_rat(default_skip))], nt) nt += default_skip
def fill_skips(self, voice): """ Add Skips at the end of the bar, so that it is filled. We assume that any elements already added are placed at the correct timepos. """ # nt = short for "next timepos", the timepos to start fill skips to if voice.get_timeposes_of(self): nt = voice.get_timeposes_of(self)[-1] nt = nt + voice.m_tdict[nt]['elem'][0].m_duration.get_rat_value() else: # we get here if the bar is empty nt = self.m_timepos default_skip = Rat(1, 4) # pos within default skip pp = nt - int(nt / default_skip) * default_skip if pp != Rat(0, 1): # Here we add a skip so that the next column will be X times # default_skip voice.set_elem([Skip(Duration.new_from_rat(default_skip - pp))], nt) nt += (default_skip - pp) # And the we fill the bar with Skips as long as default_skip. while nt < self.end(): voice.set_elem([Skip(Duration.new_from_rat(default_skip))], nt) nt += default_skip
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 test_misc(self): d1 = Duration(4, 0, Rat(1, 1)) d2 = Duration(4, 1, Rat(1, 1)) self.assertFalse(d1 == d2) self.assertEquals(d1.get_rat_value(), Rat(1, 4)) self.assertEquals(d2.get_rat_value(), Rat(3, 8)) d3 = Duration(4, 2, Rat(2, 3)) self.assertEquals(d3.get_rat_value(), Rat(7, 24))
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 __init__(self, duration, dots): """duration: integer 1 for 1/1 note 4 for 1/4 etc """ if duration: self.m_duration = Duration(duration, dots, Rat(1, 1)) else: self.m_duration = None
def __init__(self, s): if not isinstance(s, str): s = s.decode("utf-8") assert isinstance(s, str) self.m_string = s self.m_notelen = Duration(4, 0) self.m_idx = 0 self.m_last_idx = None
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 test_add_partial_bar(self): self.score.add_partial_bar(Duration.new_from_string("4"), TimeSignature(4, 4)) self.score.add_bar(None) self.assertEqual(self.score.m_bars[0].m_timepos, Rat(0, 1)) self.assertEqual(self.score.m_bars[0].end(), Rat(1, 4)) self.assertEqual(self.score.m_bars[1].m_timepos, Rat(1, 4)) self.assertEqual(self.score.get_bar_at(Rat(0, 1)), self.score.m_bars[0]) self.assertEqual(self.score.get_bar_at(Rat(1, 4)), self.score.m_bars[1]) self.score.voice11.append(Note.new_from_string("c4")) self.score.voice11.append(Note.new_from_string("c1")) self.score.voice11.append(Note.new_from_string("c1"))
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_misc(self): d1=Duration(4, 0, Rat(1, 1)) d2=Duration(4, 1, Rat(1, 1)) self.assertFalse(d1 == d2) self.assertEquals(d1.get_rat_value(), Rat(1, 4)) self.assertEquals(d2.get_rat_value(), Rat(3, 8)) d3=Duration(4, 2, Rat(2, 3)) self.assertEquals(d3.get_rat_value(), Rat(7, 24))
def test_midigen_rest(self): self.score.voice11.append(Note.new_from_string("c4")) self.score.voice11.append(Rest(Duration.new_from_string("4"))) self.score.voice11.append(Note.new_from_string("c4")) t = mpd.score_to_tracks(self.score) self.assertEqual(t[0].str_repr(), "n48 d1/4 o48 d1/4 n48 d1/4 o48")
def test_set_from_rat(self): for i in (1, 2, 4, 8, 16, 32, 64): d = Duration.new_from_rat(Rat(1, i)) self.assertEquals(d, Duration(i, 0)) d = Duration.new_from_rat(Rat(3, 8)) self.assertEquals(d.get_rat_value(), Rat(3, 8))
def test_new_from_string(self): d = Duration.new_from_string("4") self.assertEquals(d.get_rat_value(), Rat(1, 4)) self.assertRaises(Duration.BadStringException, Duration.new_from_string, "44x")
def test_constructor(self): for a, b, f in ((1, 0, 1.0), (2, 0, 0.5), (2, 1, 0.75)): d = Duration(a, b) r = d.get_rat_value() self.assertEquals(float(r), f)
def test_add_rest(self): self.score.voice11.append(Rest(Duration.new_from_string("8"))) self.assertIsInstance(self.score.voice11.m_tdict[Rat(0, 1)]['elem'][0], Rest) self.assertIsInstance(self.score.voice11.m_tdict[Rat(0, 1)]['elem'][0].w_parent(), Voice)
def test_add_rest(self): self.score.voice11.append(Rest(Duration.new_from_string("8"))) self.assertIsInstance(self.score.voice11.m_tdict[Rat(0, 1)]['elem'][0], Rest) self.assertIsInstance( self.score.voice11.m_tdict[Rat(0, 1)]['elem'][0].w_parent(), Voice)
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 new_from_string(string): return Skip(Duration.new_from_string(string))
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 _next(self): # Doing this while loop inside the exception clause is a little # faster than using a regular expression. try: while self.m_string[self.m_idx] in (' ', '\n', '\t'): self.m_idx += 1 except IndexError: raise StopIteration self.m_last_idx = self.m_idx m = self.re_rest.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() resttype, notelen, dots = m.groups() numdots = len(dots) if notelen: notelen = int(notelen) else: notelen = 0 if numdots: raise LexerError( 'Need a digit before dots. Write "%(goodcode)s", not "%(badcode)s".' % { 'badcode': m.group().strip(), 'goodcode': '%s%i%s' % (resttype, self.m_notelen.m_nh, dots) }, self) if notelen is 0: return self.REST, RestRequest(None, None) else: self.m_notelen = Duration(notelen, numdots) return self.REST, RestRequest(notelen, numdots) m = self.re_skip.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() IGN1, skiplen, dots = m.groups() numdots = len(dots) if skiplen: skiplen = int(skiplen) self.m_notelen = Duration(skiplen, numdots) else: skiplen = 0 if numdots: raise LexerError( 'Need a digit before dots. Write "%(goodcode)s", not "%(badcode)s".' % { 'badcode': m.group().strip(), 'goodcode': 's%i%s' % (self.m_notelen.m_nh, dots) }, self) if skiplen is 0: return self.SKIP, SkipRequest(skiplen, numdots) else: self.m_notelen = Duration(skiplen, numdots) return self.SKIP, SkipRequest(skiplen, numdots) m = self.re_partial.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() num, dot = m.groups() num = int(num) dot = len(dot) return self.PARTIAL, Duration(num, dot) m = self.re_melodic.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() notename, IGN1, IGN2, notelen, dots = m.groups() numdots = len(dots) if notelen: notelen = int(notelen) self.m_notelen = Duration(notelen, numdots) else: notelen = 0 if dots: raise LexerError( 'Need a digit before dots. Write "%(goodcode)s", not "%(badcode)s".' % { 'badcode': m.group().strip(), 'goodcode': '%s%i%s' % (notename, self.m_notelen.m_nh, dots) }, self) n = MusicRequest(notename, notelen, numdots) return self.NOTE, n m = self.re_staff.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() return self.STAFF, None m = self.re_rhythmstaff.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() return self.RHYTHMSTAFF, None m = self.re_voice.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() return self.VOICE, None m = self.re_relative.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() return self.RELATIVE, MusicalPitch.new_from_notename(m.group(1)) m = self.re_clef_quoted.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() return self.CLEF, m.group(1) m = self.re_clef.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() return self.CLEF, m.group(1) m = self.re_stem_updown.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() d = [const.UP, const.DOWN, const.BOTH][['Up', 'Down', 'Both'].index(m.group(2))] return self.STEMDIR, d m = self.re_tuplet_updown.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() d = [const.UP, const.DOWN, const.BOTH][['Up', 'Down', 'Both'].index(m.group(2))] return self.TUPLETDIR, d m = self.re_transpose.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() return self.TRANSPOSE, MusicalPitch.new_from_notename(m.group(1)) m = self.re_time.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() return self.TIME, elems.TimeSignature(int(m.group(1)), int(m.group(2))) m = self.re_key.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() return self.KEY, (m.group(1), m.group(2)) m = self.re_times.match(self.m_string, self.m_idx) if m: self.m_idx = m.end() return self.TIMES, Rat(int(m.groups()[0]), int(m.groups()[1])) if self.m_idx == len(self.m_string): raise StopIteration self.m_idx += 1 return self.m_string[self.m_idx - 1], None
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 new_from_string(string): return Rest(Duration.new_from_string(string))
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