"""Midi creator module""" import math from collections import Counter import librosa import numpy as np import pretty_midi from modules.Pitcher.pitcher import get_frequencies_with_high_confidence from modules.Ultrastar.ultrastar_converter import ( get_end_time_from_ultrastar, get_start_time_from_ultrastar, ultrastar_note_to_midi_note, ) from modules.console_colors import ( ULTRASINGER_HEAD, red_highlighted, ) from modules.Ultrastar.ultrastar_txt import UltrastarTxtValue from modules.Pitcher.pitched_data import PitchedData def convert_ultrastar_to_midi_instrument(ultrastar_class: UltrastarTxtValue) -> object: """Converts an Ultrastar data to a midi instrument""" print(f"{ULTRASINGER_HEAD} Creating midi instrument from Ultrastar txt") instrument = pretty_midi.Instrument(program=0) velocity = 100 for i in enumerate(ultrastar_class.words): pos = i[0] start_time = get_start_time_from_ultrastar(ultrastar_class, pos) end_time = get_end_time_from_ultrastar(ultrastar_class, pos) pitch = ultrastar_note_to_midi_note(int(ultrastar_class.pitches[pos])) note = pretty_midi.Note(velocity, pitch, start_time, end_time) instrument.notes.append(note) return instrument def instruments_to_midi(instruments: list[object], bpm: float, midi_output: str) -> None: """Write instruments to midi file""" print(f"{ULTRASINGER_HEAD} Creating midi file -> {midi_output}") midi_data = pretty_midi.PrettyMIDI(initial_tempo=bpm) for instrument in instruments: midi_data.instruments.append(instrument) midi_data.write(midi_output) class MidiCreator: """Docstring""" def convert_frequencies_to_notes(frequency: [str]) -> list[list[str]]: """Converts frequencies to notes""" notes = [] for freq in frequency: notes.append(librosa.hz_to_note(float(freq))) return notes def most_frequent(array: [str]) -> list[tuple[str, int]]: """Get most frequent item in array""" return Counter(array).most_common(1) def find_nearest_index(array: list[float], value: float) -> int: """Nearest index in array""" idx = np.searchsorted(array, value, side="left") if idx > 0 and ( idx == len(array) or math.fabs(value - array[idx - 1]) < math.fabs(value - array[idx]) ): return idx - 1 return idx def create_midi_notes_from_pitched_data(start_times: list[float], end_times: list[float], pitched_data: PitchedData) -> list[str]: """Create midi notes from pitched data""" print(f"{ULTRASINGER_HEAD} Creating midi notes from pitched data") midi_notes = [] for i in enumerate(start_times): pos = i[0] start_time = start_times[pos] end_time = end_times[pos] note = create_midi_note_from_pitched_data( start_time, end_time, pitched_data ) midi_notes.append(note) # todo: Progress? # print(filename + " f: " + str(mean)) return midi_notes def create_midi_note_from_pitched_data(start_time: float, end_time: float, pitched_data: PitchedData) -> str: """Create midi note from pitched data""" start = find_nearest_index(pitched_data.times, start_time) end = find_nearest_index(pitched_data.times, end_time) if start == end: freqs = [pitched_data.frequencies[start]] confs = [pitched_data.confidence[start]] else: freqs = pitched_data.frequencies[start:end] confs = pitched_data.confidence[start:end] conf_f = get_frequencies_with_high_confidence(freqs, confs) notes = convert_frequencies_to_notes(conf_f) note = most_frequent(notes)[0][0] return note