def _handle_note(e, position, part, ongoing, prev_note): # prev_note is used when the current note has a <chord/> tag # get some common features of element if available duration = get_value_from_tag(e, 'duration', int) or 0 # elements may have an explicit temporal offset # offset = get_value_from_tag(e, 'offset', int) or 0 staff = get_value_from_tag(e, 'staff', int) or None voice = get_value_from_tag(e, 'voice', int) or None note_id = get_value_from_attribute(e, 'id', str) symbolic_duration = {} dur_type = get_value_from_tag(e, 'type', str) if dur_type: symbolic_duration['type'] = dur_type dots = len(e.findall('dot')) if dots: symbolic_duration['dots'] = dots actual_notes = get_value_from_tag(e, 'time-modification/actual-notes', int) if actual_notes: symbolic_duration['actual_notes'] = actual_notes normal_notes = get_value_from_tag(e, 'time-modification/normal-notes', int) if normal_notes: symbolic_duration['normal_notes'] = normal_notes chord = e.find('chord') if chord is not None: # this note starts at the same position as the previous note, and has # same duration assert prev_note is not None position = prev_note.start.t articulations_e = e.find('notations/articulations') if articulations_e is not None: articulations = get_articulations(articulations_e) else: articulations = {} pitch = e.find('pitch') if pitch is not None: step = get_value_from_tag(pitch, 'step', str) alter = get_value_from_tag(pitch, 'alter', int) octave = get_value_from_tag(pitch, 'octave', int) grace = e.find('grace') if grace is not None: grace_type, steal_proportion = get_grace_info(grace) note = score.GraceNote(grace_type, step, octave, alter, note_id, voice=voice, staff=staff, symbolic_duration=symbolic_duration, articulations=articulations, steal_proportion=steal_proportion) if (isinstance(prev_note, score.GraceNote) and prev_note.voice == voice): note.grace_prev = prev_note else: note = score.Note(step, octave, alter, note_id, voice=voice, staff=staff, symbolic_duration=symbolic_duration, articulations=articulations) if (isinstance(prev_note, score.GraceNote) and prev_note.voice == voice): prev_note.grace_next = note else: # note element is a rest note = score.Rest(note_id, voice=voice, staff=staff, symbolic_duration=symbolic_duration, articulations=articulations) part.add(note, position, position+duration) ties = e.findall('tie') if len(ties) > 0: tie_key = ('tie', getattr(note, 'midi_pitch', 'rest')) tie_types = set(tie.attrib['type'] for tie in ties) if 'stop' in tie_types: tie_prev = ongoing.get(tie_key, None) if tie_prev: note.tie_prev = tie_prev tie_prev.tie_next = note del ongoing[tie_key] if 'start' in tie_types: ongoing[tie_key] = note notations = e.find('notations') if notations is not None: if notations.find('fermata') is not None: fermata = score.Fermata(note) part.add(fermata, position) note.fermata = fermata starting_slurs, stopping_slurs = handle_slurs(notations, ongoing, note, position) for slur in starting_slurs: part.add(slur, position) for slur in stopping_slurs: part.add(slur, end=position+duration) starting_tups, stopping_tups = handle_tuplets(notations, ongoing, note) for tup in starting_tups: part.add(tup, position) for tup in stopping_tups: part.add(tup, end=position+duration) new_position = position + duration return new_position, note
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
def create_part(ticks, notes, spellings, voices, note_ids, time_sigs, key_sigs, part_id=None, part_name=None): LOGGER.debug('create_part') part = score.Part(part_id, part_name=part_name) part.set_quarter_duration(0, ticks) clef = score.Clef(number=1, **estimate_clef_properties([pitch for _, pitch, _ in notes])) part.add(clef, 0) for t, name in key_sigs: fifths, mode = key_name_to_fifths_mode(name) part.add(score.KeySignature(fifths, mode), t) LOGGER.debug('add notes') for (onset, pitch, duration), (step, alter, octave), voice, note_id in zip(notes, spellings, voices, note_ids): if duration > 0: note = score.Note(step, octave, alter, voice=int(voice or 0), id=note_id, symbolic_duration=estimate_symbolic_duration(duration, ticks)) else: note = score.GraceNote('appoggiatura', step, octave, alter, voice=int(voice or 0), id=note_id, symbolic_duration=dict(type='quarter')) part.add(note, onset, onset+duration) if not time_sigs: warnings.warn('No time signatures found, assuming 4/4') time_sigs = [(0, 4, 4)] time_sigs = np.array(time_sigs, dtype=np.int) # for convenience we add the end times for each time signature ts_end_times = np.r_[time_sigs[1:, 0], np.iinfo(np.int).max] time_sigs = np.column_stack((time_sigs, ts_end_times)) LOGGER.debug('add time sigs and measures') for ts_start, num, den, ts_end in time_sigs: time_sig = score.TimeSignature(num.item(), den.item()) part.add(time_sig, ts_start.item()) score.add_measures(part) # this is the old way to add measures. Since part comes from MIDI we # only have a single global divs value, which makes add it easier to compute # measure durations: # measure_counter = 1 # # we call item() on numpy numbers to get the value in the equivalent python type # for ts_start, num, den, ts_end in time_sigs: # time_sig = score.TimeSignature(num.item(), den.item()) # part.add(time_sig, ts_start.item()) # measure_duration = (num.item() * ticks * 4) // den.item() # measure_start_limit = min(ts_end.item(), part.last_point.t) # for m_start in range(ts_start, measure_start_limit, measure_duration): # measure = score.Measure(number=measure_counter) # m_end = min(m_start+measure_duration, ts_end) # part.add(measure, m_start, m_end) # measure_counter += 1 # if np.isinf(ts_end): # ts_end = m_end LOGGER.debug('tie notes') # tie notes where necessary (across measure boundaries, and within measures # notes with compound duration) score.tie_notes(part) LOGGER.debug('find tuplets') # apply simplistic tuplet finding heuristic score.find_tuplets(part) LOGGER.debug('done create_part') return part
def _handle_note(e, position, part, ongoing, prev_note, doc_order): # get some common features of element if available duration = get_value_from_tag(e, "duration", int) or 0 # elements may have an explicit temporal offset # offset = get_value_from_tag(e, 'offset', int) or 0 staff = get_value_from_tag(e, "staff", int) or None voice = get_value_from_tag(e, "voice", int) or None # add support of uppercase "ID" tags note_id = (get_value_from_attribute(e, "id", str) if get_value_from_attribute(e, "id", str) else get_value_from_attribute(e, "ID", str)) symbolic_duration = {} dur_type = get_value_from_tag(e, "type", str) if dur_type: symbolic_duration["type"] = dur_type dots = len(e.findall("dot")) if dots: symbolic_duration["dots"] = dots actual_notes = get_value_from_tag(e, "time-modification/actual-notes", int) if actual_notes: symbolic_duration["actual_notes"] = actual_notes normal_notes = get_value_from_tag(e, "time-modification/normal-notes", int) if normal_notes: symbolic_duration["normal_notes"] = normal_notes chord = e.find("chord") if chord is not None: # this note starts at the same position as the previous note, and has # same duration assert prev_note is not None position = prev_note.start.t articulations_e = e.find("notations/articulations") if articulations_e is not None: articulations = get_articulations(articulations_e) else: articulations = {} pitch = e.find("pitch") if pitch is not None: step = get_value_from_tag(pitch, "step", str) alter = get_value_from_tag(pitch, "alter", int) octave = get_value_from_tag(pitch, "octave", int) grace = e.find("grace") if grace is not None: grace_type, steal_proportion = get_grace_info(grace) note = score.GraceNote( grace_type=grace_type, step=step, octave=octave, alter=alter, id=note_id, voice=voice, staff=staff, symbolic_duration=symbolic_duration, articulations=articulations, steal_proportion=steal_proportion, doc_order=doc_order, ) if isinstance(prev_note, score.GraceNote) and prev_note.voice == voice: note.grace_prev = prev_note else: note = score.Note( step=step, octave=octave, alter=alter, id=note_id, voice=voice, staff=staff, symbolic_duration=symbolic_duration, articulations=articulations, doc_order=doc_order, ) if isinstance(prev_note, score.GraceNote) and prev_note.voice == voice: prev_note.grace_next = note else: # note element is a rest note = score.Rest( id=note_id, voice=voice, staff=staff, symbolic_duration=symbolic_duration, articulations=articulations, doc_order=doc_order, ) part.add(note, position, position + duration) ties = e.findall("tie") if len(ties) > 0: tie_key = ("tie", getattr(note, "midi_pitch", "rest")) tie_types = set(tie.attrib["type"] for tie in ties) if "stop" in tie_types: tie_prev = ongoing.get(tie_key, None) if tie_prev: note.tie_prev = tie_prev tie_prev.tie_next = note del ongoing[tie_key] if "start" in tie_types: ongoing[tie_key] = note notations = e.find("notations") if notations is not None: if notations.find("fermata") is not None: fermata = score.Fermata(note) part.add(fermata, position) note.fermata = fermata starting_slurs, stopping_slurs = handle_slurs(notations, ongoing, note, position) for slur in starting_slurs: part.add(slur, position) for slur in stopping_slurs: part.add(slur, end=position + duration) starting_tups, stopping_tups = handle_tuplets(notations, ongoing, note) for tup in starting_tups: part.add(tup, position) for tup in stopping_tups: part.add(tup, end=position + duration) new_position = position + duration return new_position, note