Utils methods

Module containing utils methods than can be used outside of tokenization.

miditok.utils.compute_ticks_per_bar(time_sig: TimeSignatureFactory(), time_division: int) int

Compute the number of ticks in a bar.

The number of ticks per bar depends on the time signature.

Parameters:
  • time_sig – time signature object.

  • time_division – MIDI time division in ticks/quarter.

Returns:

MIDI bar resolution, in ticks/bar

miditok.utils.compute_ticks_per_beat(time_sig_denominator: int, time_division: int) int

Compute the number of ticks in a beat at a given time signature.

Parameters:
  • time_sig_denominator – time signature denominator.

  • time_division – MIDI time division in ticks/quarter.

Returns:

number of ticks per beat at the given time signature.

miditok.utils.convert_ids_tensors_to_list(ids) list[int] | list[list[int]]

Convert a PyTorch, Tensorflow Tensor or numpy array to a list of integers.

This method works with Jax too. It is recursive and will convert nested Tensors/arrays.

Parameters:

ids – ids sequence to convert.

Returns:

the input, as a list of integers

miditok.utils.detect_chords(notes: NoteTickList, ticks_per_beat: np.array, chord_maps: dict[str, Sequence[int]], program: int | None = None, specify_root_note: bool = True, beat_res: int = 4, onset_offset: int = 1, unknown_chords_num_notes_range: tuple[int, int] | None = None, simul_notes_limit: int = 10) list[Event]

Detect chords in a list of notes.

Make sure to sort notes by start time then pitch before: notes.sort(key=lambda x: (x.start, x.pitch)). On very large tracks with high note density this method can be slow. If you plan to use it with the Maestro or GiantMIDI datasets, it can take up to hundreds of seconds per MIDI depending on your cpu. This method works by iterating over each note, find if it played with other notes, and if it forms a chord from the chord maps. It does not consider chord inversion..

Parameters:
  • notes – notes to analyse (sorted by starting time, them pitch).

  • ticks_per_beat – array indicating the number of ticks per beat per time signature denominator section. The numbers of ticks per beat depend on the time signatures of the MIDI being parsed. The array has a shape (N,2), for N changes of ticks per beat, and the second dimension representing the end tick of each section and the number of ticks per beat respectively.

  • chord_maps – list of chord maps, to be given as a dictionary where keys are chord qualities (e.g. “maj”) and values pitch maps as tuples of integers (e.g. (0, 4, 7)). You can use miditok.constants.CHORD_MAPS as an example.

  • program – program of the track of the notes. Used to specify the program when creating the Event object. (default: None)

  • specify_root_note – the root note of each chord will be specified in Events/tokens. Tokens will look as “Chord_C:maj”. (default: True)

  • beat_res – beat resolution, i.e. number of samples per beat (default 4).

  • onset_offset – maximum offset (in samples) ∈ N separating notes starts to consider them starting at the same time / onset (default is 1).

  • unknown_chords_num_notes_range – range of number of notes to represent unknown chords. If you want to represent chords that does not match any combination in chord_maps, use this argument. Leave None to not represent unknown chords. (default: None)

  • simul_notes_limit – number of simultaneous notes being processed when looking for a chord this parameter allows to speed up the chord detection, and must be >= 5 (default 10).

Returns:

the detected chords as Event objects.

miditok.utils.fix_offsets_overlapping_notes(notes: NoteTickList) None

Reduce the durations of overlapping notes.

By applying this method, when a note starts while it is already being played, the previous note will end. Before running this method make sure the notes are sorted by start then pitch then end values: notes.sort(key=lambda x: (x.start, x.pitch, x.end)).

Parameters:

notes – notes to fix.

miditok.utils.get_bars_ticks(midi: ScoreFactory()) list[int]

Compute the ticks of the bars of a MIDI.

Note: When encountering multiple time signature messages at a same tick, we this method will automatically consider the last one (coming in the list). Other software can proceed differently. Logic Pro, for example, uses the first one. I haven’t found documentation or recommendations for this specific situation. It might be better to use the first one and discard the others.

Parameters:

midi – MIDI to analyze.

Returns:

list of ticks for each bar.

miditok.utils.get_midi_programs(midi: ScoreFactory()) list[tuple[int, bool]]

Return the list of programs of the tracks of a MIDI.

Parameters:

midi – the MIDI object to extract tracks programs

Returns:

the list of track programs, as a list of tuples (program, is_drum)

miditok.utils.get_midi_ticks_per_beat(midi: ScoreFactory()) ndarray

Compute the portions of numbers of ticks in a beat in a MIDI.

The method returns a numpy array of shape (N,2), for N ticks-per-beat changes, and the second dimension corresponding to the ending tick and the number of ticks per beat of the portion.

Parameters:

midi – MIDI to analyze.

Returns:

ticks per beat values as a numpy array.

miditok.utils.merge_same_program_tracks(tracks: list[Track] | TrackTickList, effects: bool = True) None

Merge tracks having the same program number.

Parameters:
  • tracks – list of tracks.

  • effects – will also merge effects, i.e. control changes, sustain pedals and pitch bends. (default: True)

miditok.utils.merge_tracks(tracks: list[Track] | TrackTickList | Score, effects: bool = True) Track

Merge several symusic.Tracks.

The notes (and optionally effects) will be concatenated and sorted by time. All the tracks will be merged into the first Track of the list.

Parameters:
  • tracks – list of tracks to merge, or symusic.Score object.

  • effects – will also merge effects, i.e. control changes, sustain pedals and pitch bends. (default: True)

Returns:

the merged track.

miditok.utils.merge_tracks_per_class(midi: ScoreFactory(), classes_to_merge: list[int] | None = None, new_program_per_class: dict[int, int] | None = None, max_num_of_tracks_per_inst_class: dict[int, int] | None = None, valid_programs: list[int] | None = None, filter_pitches: bool = True) None

Merge symusic.Tracks per MIDI program class.

Example, a list of tracks / programs [0, 3, 8, 10, 11, 24, 25, 44, 47] will become [0, 8, 24, 25, 40] if classes_to_merge is [0, 1, 5]. The classes are in miditok.constants.INSTRUMENT_CLASSES.

Note: programs of drum tracks will be set to -1.

Parameters:
  • midi – MIDI object to merge tracks

  • classes_to_merge – instrument classes to merge, to give as list of indexes (see miditok.constants.INSTRUMENT_CLASSES). Give None to merge nothing, the function will still remove non-valid programs/tracks if given. (default: None)

  • new_program_per_class – new program of the final merged tracks, to be given per instrument class as a dict {class_id: program}. (default: None)

  • max_num_of_tracks_per_inst_class – max number of tracks per instrument class, if the limit is exceeded for one class only the tracks with the maximum notes will be kept, give None for no limit. (default: None)

  • valid_programs – valid program ids to keep, others will be deleted, give None for keep all programs. (default None)

  • filter_pitches – after merging, will remove notes whose pitches are out the recommended range defined by the GM2 specs. (default: True)

miditok.utils.num_bar_pos(seq: Sequence[int], bar_token_id: int, position_tokens_ids: Sequence[int]) tuple[int, int]

Return the number of bars and the last position of a sequence of tokens.

This method is compatible with tokenizations representing time with Bar and Position tokens, such as miditok.REMI.

Parameters:
  • seq – sequence of tokens

  • bar_token_id – the bar token value

  • position_tokens_ids – position tokens values

Returns:

the current bar, current position within the bar, current pitches played at this position, and if a chord token has been predicted at this position.

miditok.utils.remove_duplicated_notes(notes: NoteTickList | dict[str, ndarray], consider_duration: bool = False) None

Remove (inplace) duplicated notes, i.e. with the same pitch and onset time.

This can be done either from a symusic.NoteTickList, or a note structure of arrays (symusic.NoteTickList.numpy()). The velocities are ignored in this method. The notes need to be sorted by time, then pitch, and duration if consider_duration is True: notes.sort(key=lambda x: (x.start, x.pitch, x.duration)).

Parameters:
  • notes – notes to analyse.

  • consider_duration – if given True, the method will also consider the durations of the notes when detecting identical ones. (default: False)