def test_midi_export_anacrusis(self): part = score.Part("id") # 1 div is 1 quarter part.set_quarter_duration(0, 1) # 4/4 at t=0 part.add(score.TimeSignature(4, 4), 0) # ANACRUSIS # quarter note from t=0 to t=1 part.add(score.Note("c", 4), 0, 1) # incomplete measure from t=0 to t=1 part.add(score.Measure(), 0, 1) # whole note from t=1 to t=5 part.add(score.Note("c", 4), 1, 5) # add missing measures score.add_measures(part) # print(part.pretty()) mid = export_and_read(part, anacrusis_behavior="shift") t = 0 for msg in mid.tracks[0]: t += msg.time if msg.type == "note_on": assert t == 0 break mid = export_and_read(part, anacrusis_behavior="pad_bar") t = 0 for msg in mid.tracks[0]: t += msg.time if msg.type == "note_on": assert t == 3, f"Incorrect time of first note on: {t} (should be 3)" break
def make_measure(xml_measure): measure = score.Measure() # try: # measure.number = int(xml_measure.attrib['number']) # except: # LOGGER.warn('No number attribute found for measure') measure.number = get_value_from_attribute(xml_measure, 'number', int) return measure
def test_export_import_pprint(self): # create a part part1 = score.Part('My Part') # create contents divs = 10 ts = score.TimeSignature(3, 4) page1 = score.Page(1) system1 = score.System(1) measure1 = score.Measure(number=1) note1 = score.Note(step='A', octave=4, voice=1, staff=1) rest1 = score.Rest(voice=1, staff=1) note2 = score.Note(step='C', octave=5, alter=-1, voice=2, staff=1) # and add the contents to the part: part1.set_quarter_duration(0, divs) part1.add(ts, 0) part1.add(measure1, 0, 30) part1.add(page1, 0) part1.add(system1, 0) part1.add(note1, 0, 15) part1.add(rest1, 15, 30) part1.add(note2, 0, 30) score.set_end_times(part1) # pretty print the part pstring1 = part1.pretty() with TemporaryFile() as f: # save part to musicxml save_musicxml(part1, f) f.flush() f.seek(0) # load part from musicxml part2 = load_musicxml(f) # pretty print saved/loaded part: pstring2 = part2.pretty() # test pretty printed strings for equality equal = pstring1 == pstring2 if not equal: show_diff(pstring1, pstring2) msg = 'Exported and imported score does not yield identical pretty printed representations' self.assertTrue(equal, msg)
def test_times_2(self): # 6/8 anacrusis part = score.Part("id") # 2 divs is 1 quarter part.set_quarter_duration(0, 2) part.add(score.TimeSignature(6, 8), 0) # ANACRUSIS part.add(score.Note("c", 4), 0, 3) part.add(score.Measure(), 0, 3) part.add(score.Note("c", 4), 3, 9) score.add_measures(part) time_pairs = [(-3, -1.5), (0, 0), (6, 3)] test_time_pairs(part, time_pairs)
def test_times_1(self): # 4/4 anacrusis part = score.Part("id") # 1 div is 1 quarter part.set_quarter_duration(0, 1) # 4/4 at t=0 part.add(score.TimeSignature(4, 4), 0) # ANACRUSIS # quarter note from t=0 to t=1 part.add(score.Note("c", 4), 0, 1) # incomplete measure from t=0 to t=1 part.add(score.Measure(), 0, 1) # whole note from t=1 to t=5 part.add(score.Note("c", 4), 1, 5) # add missing measures score.add_measures(part) time_pairs = [(-1, -1), (0, 0), (4, 4)] test_time_pairs(part, time_pairs)
def part_from_matchfile(mf): part = score.Part('P1', mf.info('piece')) # snotes = sorted(mf.snotes, key=attrgetter('OnsetInBeats')) snotes = sort_snotes(mf.snotes) divs = np.lcm.reduce(np.unique([note.Offset.denominator * (note.Offset.tuple_div or 1) for note in snotes])) part.set_quarter_duration(0, divs) min_time = snotes[0].OnsetInBeats # sorted by OnsetInBeats max_time = max(n.OffsetInBeats for n in snotes) ts = mf.time_signatures beats_map, beat_type_map, min_time_q, max_time_q = make_timesig_maps(ts, max_time) bars = np.unique([n.Bar for n in snotes]) t = min_time t = t * 4 / beat_type_map(min_time_q) offset = t # bar map: bar_number-> start in quarters bar_times = {} for b0, b1 in iter_current_next(bars, start=0): bar_times.setdefault(b1, t) if t < 0: t = 0 else: # multiply by diff between consecutive bar numbers n_bars = b1 - b0 if t <= max_time_q: t += (n_bars * 4 * beats_map(t)) / beat_type_map(t) for note in snotes: # start of bar in quarter units bar_start = bar_times[note.Bar] # offset within bar in quarter units bar_offset = (note.Beat - 1) * 4 / beat_type_map(bar_start) # offset within beat in quarter units beat_offset = (4 * note.Offset.numerator / (note.Offset.denominator * (note.Offset.tuple_div or 1))) # # anacrusis if bar_start < 0: # in case of anacrusis we set the bar_start to -bar_duration (in # quarters) so that the below calculation is correct bar_start = - beats_map(bar_start) * 4 / beat_type_map(bar_start) # note onset in divs onset_divs = int(divs * (bar_start + bar_offset + beat_offset - offset)) # print(note.Anchor, onset_divs, bar_start, bar_offset, beat_offset, offset) articulations = set() if 'staccato' in note.ScoreAttributesList: articulations.add('staccato') if 'accent' in note.ScoreAttributesList: articulations.add('accent') # dictionary with keyword args with which the Note (or GraceNote) will be instantiated note_attributes = dict(step=note.NoteName, octave=note.Octave, alter=note.Modifier, id=note.Anchor, articulations=articulations) staff_nr = next((a[-1] for a in note.ScoreAttributesList if a.startswith('staff')), None) try: note_attributes['staff'] = int(staff_nr) except (TypeError, ValueError): # no staff attribute, or staff attribute does not end with a number note_attributes['staff'] = None note_attributes['voice'] = next((int(a) for a in note.ScoreAttributesList if NUMBER_PAT.match(a)), None) # get rid of this if as soon as we have a way to iterate over the # duration components. For now we have to treat the cases simple # and compound durations separately. if note.Duration.add_components: prev_part_note = None for i, (num, den, tuple_div) in enumerate(note.Duration.add_components): # when we add multiple notes that are tied, the first note will # get the original note id, and subsequent notes will get a # derived note id (by appending, 'a', 'b', 'c',...) if i > 0: # tnote_id = 'n{}_{}'.format(note.Anchor, i) note_attributes['id'] = score.make_tied_note_id(note_attributes['id']) part_note = score.Note(**note_attributes) duration_divs = int(divs * 4 * num / (den * (tuple_div or 1))) assert duration_divs > 0 offset_divs = onset_divs + duration_divs part.add(part_note, onset_divs, offset_divs) if prev_part_note: prev_part_note.tie_next = part_note part_note.tie_prev = prev_part_note prev_part_note = part_note onset_divs = offset_divs else: num = note.Duration.numerator den = note.Duration.denominator tuple_div = note.Duration.tuple_div duration_divs = int(divs * 4 * num / (den * (tuple_div or 1))) offset_divs = onset_divs + duration_divs # notes with duration 0, are also treated as grace notes, even if # they do not have a 'grace' score attribute if ('grace' in note.ScoreAttributesList or note.Duration.numerator == 0): part_note = score.GraceNote('appoggiatura', **note_attributes) else: part_note = score.Note(**note_attributes) part.add(part_note, onset_divs, offset_divs) # add time signatures for (ts_beat_time, ts_bar, (ts_beats, ts_beat_type)) in ts: bar_start_divs = int(divs * (bar_times[ts_bar] - offset)) # in quarters part.add(score.TimeSignature(ts_beats, ts_beat_type), bar_start_divs) # add key signatures for (ks_beat_time, ks_bar, keys) in mf.key_signatures: if len(keys) > 1: # there are multple equivalent keys, so we check which one is most # likely according to the key estimator est_keys = estimate_key(notes_to_notearray(part.notes_tied), return_sorted_keys=True) idx = [est_keys.index(key) if key in est_keys else np.inf for key in keys] key_name = keys[np.argmin(idx)] else: key_name = keys[0] fifths, mode = key_name_to_fifths_mode(key_name) part.add(score.KeySignature(fifths, mode), 0) add_staffs(part) # add_clefs(part) # add incomplete measure if necessary if offset < 0: part.add(score.Measure(number=1), 0, int(-offset * divs)) # add the rest of the measures automatically score.add_measures(part) # print(part.pretty()) score.tie_notes(part) score.find_tuplets(part) if not all([n.voice for n in part.notes_tied]): # print('notes without voice detected') add_voices(part) return part