def test_relative(self): s = r"\staff\relative d'{ d f }" score = parse_to_score_object(s) self.assertEquals(score.voice11.m_tdict[Rat(0, 1)]['elem'][0], Note.new_from_string("d'4")) self.assertEquals(score.voice11.m_tdict[Rat(1, 4)]['elem'][0], Note.new_from_string("f'4"))
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_transpose(self): s = r"\staff\transpose d'{ c d }" score = parse_to_score_object(s) self.assertEquals(score.voice11.m_tdict[Rat(0, 1)]['elem'][0], Note.new_from_string("d4")) self.assertEquals(score.voice11.m_tdict[Rat(1, 4)]['elem'][0], Note.new_from_string("e4")) s = r"\staff\transpose d''{ c d }" score = parse_to_score_object(s) self.assertEquals(score.voice11.m_tdict[Rat(0, 1)]['elem'][0], Note.new_from_string("d'4")) self.assertEquals(score.voice11.m_tdict[Rat(1, 4)]['elem'][0], Note.new_from_string("e'4"))
def test_transpose_relative(self): s = r"\staff\transpose d'\relative c'{ c d }" score = parse_to_score_object(s) self.assertEqual(score.voice11.m_tdict[Rat(0, 1)]['elem'][0], Note.new_from_string("d'4")) self.assertEqual(score.voice11.m_tdict[Rat(1, 4)]['elem'][0], Note.new_from_string("e'4")) s = r"\staff\transpose d''\relative c'{ c d e}" score = parse_to_score_object(s) self.assertEqual(score.voice11.m_tdict[Rat(0, 1)]['elem'][0], Note.new_from_string("d''4")) self.assertEqual(score.voice11.m_tdict[Rat(1, 4)]['elem'][0], Note.new_from_string("e''4")) self.assertEqual(score.voice11.m_tdict[Rat(1, 2)]['elem'][0], Note.new_from_string("fis''4"))
def _update(self): """ Updates the buttons above the action_area where you have one or more buttons with a small note pixmap on. Each of the buttons will play one part of the music in the question. """ # tmp func used as callback function def f(w, start, end, self=self): try: utils.play_music( self.m_t.m_P.get_music(), self.m_t.m_P.get_tempo(), cfg.get_int('config/preferred_instrument'), cfg.get_int('config/preferred_instrument_volume'), start, end) except Exception as e: if not self.standard_exception_handler(e, soundcard.synth.stop): raise for i in self.g_partbox.get_children(): i.destroy() # if the lessonfile was invalid, m_P could be None if self.m_t.m_P and self.m_t.m_P.m_questions: if 'name' in self.m_t.m_P.get_question(): self.g_question_title.set_text(self.m_t.m_P.get_name()) else: self.g_question_title.set_text("") v = self.m_t.m_P.get_breakpoints() if v == []: # we display one button that will play the whole music if # there are not breakpoints in the music btn = self.create_pixmap_button() btn.connect('clicked', f, None, None) btn.show() self.g_partbox.pack_start(btn, True, True, 0) return tmp = [Rat(0, 1)] + v + [Rat(2**30, 1)] for i in range(len(tmp) - 1): btn = self.create_pixmap_button() btn.show() btn.connect('clicked', f, tmp[i], tmp[i + 1]) self.g_partbox.pack_start(btn, True, True, 0) # q_status is QSTATUS_NO if the question is invalid (from the lessonfile) if self.m_t.q_status == self.QSTATUS_NO: self.g_partbox.set_sensitive(False) else: self.g_partbox.set_sensitive(True)
def try_set_elem(self, elem, timepos, insert_mode): """ Replace whatever is at timepos with elem. Return True if succuessful. """ bp = BarProxy(self, timepos) if isinstance(self.m_tdict[timepos]['elem'][0], Skip): if timepos + elem.m_duration.get_rat_value() <= bp.end(): if isinstance(elem, Note): stem = Stem(self, [elem], const.UP) self.set_elem(stem, timepos) else: self.set_elem([elem], timepos) elem.w_parent = weakref.ref(self) bp.remove_skips() bp.repack() bp.fill_skips() return True else: max_free = bp.get_free_time() #flytt til bar-class delta = elem.m_duration.get_rat_value( ) - self.m_tdict[timepos]['elem'][0].m_duration.get_rat_value() if insert_mode: if max_free >= elem.m_duration.get_rat_value(): self.m_tdict[timepos + Rat(1, 1000000)] = self.m_tdict[timepos] del self.m_tdict[timepos] bp.remove_trailing(elem.m_duration.get_rat_value()) stem = Stem(self, [elem], const.UP) self.set_elem(stem, timepos) bp.remove_skips() bp.repack() bp.fill_skips() return True if not insert_mode: if (max_free >= delta): # We have space to add. # Delete skips (and rests) from the end of the bar # until we have enough space to add the elem. if delta > Rat(0, 1): bp.remove_trailing(delta) stem = Stem(self, [elem], const.UP) self.set_elem(stem, timepos) bp.remove_skips() bp.repack() bp.fill_skips() return True
def concat(s1, s2): """ Concatenate the two scores, and return a new score. Both scores need to have the exact same staff and voice layout. """ assert isinstance(s1, Score) assert isinstance(s2, Score) if len(s1.m_staffs) != len(s2.m_staffs): raise Score.StaffCountException() if [type(x) for x in s1.m_staffs] != [type(x) for x in s2.m_staffs]: raise Score.StaffTypeException() for idx, (st1, st2) in enumerate(zip(s1.m_staffs, s2.m_staffs)): if len(st1.m_voices) != len(st2.m_voices): raise Score.VoiceCountException() ret = s1.copy() if not s1.m_staffs: return s1 # do the adding for bar in s2.m_bars: bar.m_timepos = ret.m_bars[-1].end() ret.m_bars.append(bar) ret.create_shortcuts() if s1.m_bars: start = s1.m_bars[-1].end() else: start = Rat(0, 1) s2.create_shortcuts() # FIXME why? for staff_idx, staff in enumerate(s1.m_staffs): for voice_idx, voice in enumerate(staff.m_voices): for k in s2.m_staffs[staff_idx].m_voices[voice_idx].m_tdict: ret.m_staffs[staff_idx].m_voices[voice_idx].m_tdict[ k + start] = s2.m_staffs[staff_idx].m_voices[ voice_idx].m_tdict[k] return ret
def display_start_of_music(self): """ Callers must catch exceptions. """ fontsize = self.get_int('config/feta_font_size=20') try: if self.m_t.m_P.get_clue_music(): self.g_music_displayer.display( self.m_t.m_P.get_clue_music().get_mpd_music_string( self.m_t.m_P), fontsize) elif self.m_t.m_P.get_clue_end(): self.g_music_displayer.display(self.m_t.m_P.get_music(), fontsize, self.m_t.m_P.get_clue_end()) else: self.g_music_displayer.display(self.m_t.m_P.get_music(), fontsize, Rat(0, 1)) except mpd.MpdException, e: if self.m_t.m_P.get_clue_music(): e.m_mpd_varname = 'clue_music' else: e.m_mpd_varname = 'music' self.m_t.m_P.get_question()['music'].complete_to_musicdata_coords( self.m_t.m_P, e) if 'm_mpd_badcode' not in dir(e): e.m_mpd_badcode = self.m_t.m_P.get_question()[ e.m_mpd_varname].get_err_context(e, self.m_t.m_P) raise
def concat2(s1, s2): """ Return a new Score object concatenating the two scores. This is intended return value is intended for playback only, since the staffs placed below each other. So the first score will have empty bars at the end, and the last score will have empty bars at the beginning. """ assert isinstance(s1, Score) assert isinstance(s2, Score) ret = s1.copy() if s1.m_bars: start = s1.m_bars[-1].end() else: start = Rat(0, 1) for bar in s2.m_bars: ret.m_bars.append(Bar(bar.m_timesig, ret.m_bars[-1].end())) for staff_idx, staff in enumerate(s2.m_staffs): ret.add_staff(staff_class=staff.__class__) for k in staff.m_tdict: ret.m_staffs[-1].m_tdict[start + k] = staff.m_tdict[k] for voice_idx, voice in enumerate(staff.m_voices): if voice_idx != 0: ret.m_staffs[-1].add_voice() # This line make the music from sc2 continue after the # point where the music from sc1 ends. ret.m_staffs[-1].m_voices[-1].m_length = s1.m_bars[-1].end() for elem in s2.m_staffs[staff_idx].m_voices[voice_idx]: ret.m_staffs[-1].m_voices[-1].append(elem['elem']) ret.create_shortcuts() return ret
def _get_new_bar_timepos(self): """ Return the timepos where the next bar will be added. """ if self.m_bars: return self.m_bars[-1].end() return Rat(0, 1)
def get_timelist(self): curpos = Rat(0, 1) retval = [] for timepos in sorted(self.m_tdict.keys()): if isinstance(self.m_tdict[timepos]['elem'][0], Rest): if retval[-1][0] == False: retval[-1][1] += self.m_tdict[timepos]['elem'][ 0].m_duration.get_rat_value() else: retval.append([ False, self.m_tdict[timepos]['elem'] [0].m_duration.get_rat_value() ]) elif self.m_tdict[timepos]['elem'][0].m_tieinfo == 'start': nlen = self.m_tdict[timepos]['elem'][ 0].m_duration.get_rat_value() elif self.m_tdict[timepos]['elem'][0].m_tieinfo == 'continue': nlen += self.m_tdict[timepos]['elem'][ 0].m_duration.get_rat_value() else: if self.m_tdict[timepos]['elem'][0].m_tieinfo == 'end': nlen += self.m_tdict[timepos]['elem'][ 0].m_duration.get_rat_value() else: nlen = self.m_tdict[timepos]['elem'][ 0].m_duration.get_rat_value() retval.append( [isinstance(self.m_tdict[timepos]['elem'][0], Note), nlen]) nlen = None return retval
def remove_trailing(self, voice, duration): """ Remove elements from the end of the bar, until their duration is a least 'duration' long. """ assert isinstance(duration, Rat) total = Rat(0, 1) while total < duration: total += self.pop_last_elem(voice)
def copy(self, parent): """ Return a copy of this Voice object. We make a copy of the dict and the m_length variable, but the dict revers to the same object. """ ret = Voice(parent) ret.m_length = Rat(self.m_length.m_num, self.m_length.m_den) ret.m_tdict = self.m_tdict.copy() return ret
def get_free_time(self, voice): """ Return the duration, as a Rat value, on the end of the bar consisting of Rests and Skips. """ d = Rat(0, 1) for timepos in reversed(voice.get_timeposes_of(self)): if not isinstance(voice.m_tdict[timepos]['elem'][0], (Skip, Rest)): break d += voice.m_tdict[timepos]['elem'][0].m_duration.get_rat_value() return d
def get_timelist(self): data = {} for staff_idx, staff in enumerate(self.m_staffs): for voice_idx, voice in enumerate(staff.m_voices): for timepos in voice.m_tdict: n = voice.m_tdict[timepos]['elem'][0] if isinstance(n, Note): if timepos not in data: data[timepos] = {'start': set(), 'end': set()} if n.m_tieinfo not in ('go', 'end'): data[timepos]['start'].add((staff_idx, voice_idx)) if n.m_tieinfo in (None, 'end'): endpos = timepos + n.m_duration.get_rat_value() if endpos not in data: data[endpos] = {'start': set(), 'end': set()} data[endpos]['end'].add((staff_idx, voice_idx)) retval = [] start_time = None v = sorted(data)[:] ppos = Rat(0, 1) voices = set() for idx, timepos in enumerate(v): if data[timepos]['start'] and data[timepos]['end']: for voice in data[timepos]['end']: voices.remove(voice) for voice in data[timepos]['start']: voices.add(voice) assert voices retval.append([True, timepos - ppos]) ppos = timepos elif data[timepos]['start']: if not voices: if (timepos != ppos): retval.append([False, timepos - ppos]) ppos = timepos else: retval.append([True, timepos - ppos]) ppos = timepos for voice in data[timepos]['start']: voices.add(voice) elif data[timepos]['end']: for voice in data[timepos]['end']: voices.remove(voice) if not voices: retval.append([True, timepos - ppos]) ppos = timepos return retval
def test_timelist(self): score = parse_to_score_object(r"\staff{c2 r4 r8 a8}" r"\staff{c4 c2 r8 g8}") self.assertEquals(score.get_timelist(), [ [True, Rat(1, 4)], [True, Rat(1, 2)], [False, Rat(1, 8)], [True, Rat(1, 8)], ]) score = parse_to_score_object(r"\staff{c4 d8 }" r"\staff{r8 e }") self.assertEquals(score.get_timelist(), [ [True, Rat(1, 8)], [True, Rat(1, 8)], [True, Rat(1, 8)], ]) score = parse_to_score_object(r"\staff{c4 r8 r8 d8 }" r"\staff{r8 e8 r8 r4 }") self.assertEquals(score.get_timelist(), [ [True, Rat(1, 8)], [True, Rat(1, 8)], [False, Rat(1, 4)], [True, Rat(1, 8)], ])
# if the lessonfile was invalid, m_P could be None if self.m_t.m_P and self.m_t.m_P.m_questions: if 'name' in self.m_t.m_P.get_question(): self.g_question_title.set_text(self.m_t.m_P.get_name()) else: self.g_question_title.set_text("") v = self.m_t.m_P.get_breakpoints() if v == []: # we display one button that will play the whole music if # there are not breakpoints in the music btn = self.create_pixmap_button() btn.connect('clicked', f, None, None) btn.show() self.g_partbox.pack_start(btn, True, True, 0) return tmp = [Rat(0, 1)] + v + [Rat(2**30, 1)] for i in range(len(tmp) - 1): btn = self.create_pixmap_button() btn.show() btn.connect('clicked', f, tmp[i], tmp[i + 1]) self.g_partbox.pack_start(btn, True, True, 0) # q_status is QSTATUS_NO if the question is invalid (from the lessonfile) if self.m_t.q_status == self.QSTATUS_NO: self.g_partbox.set_sensitive(False) else: self.g_partbox.set_sensitive(True) def display_start_of_music(self): """ Callers must catch exceptions. """
def __init__(self, parent): _StaffCommon.__init__(self, parent) self.set_clef("violin", Rat(0, 1))
def __init__(self, parent): HasParent.__init__(self, parent) # The timelen of the Voice self.m_length = Rat(0, 1) self.m_tdict = {}
def as_rat(self): return Rat(self.m_num, self.m_den)