Spaces:
Sleeping
Sleeping
Upload midi_creator.py
Browse files- src/modules/midi_creator.py +124 -0
src/modules/midi_creator.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Midi creator module"""
|
| 2 |
+
|
| 3 |
+
import math
|
| 4 |
+
from collections import Counter
|
| 5 |
+
|
| 6 |
+
import librosa
|
| 7 |
+
import numpy as np
|
| 8 |
+
import pretty_midi
|
| 9 |
+
|
| 10 |
+
from modules.Pitcher.pitcher import get_frequencies_with_high_confidence
|
| 11 |
+
from modules.Ultrastar.ultrastar_converter import (
|
| 12 |
+
get_end_time_from_ultrastar,
|
| 13 |
+
get_start_time_from_ultrastar,
|
| 14 |
+
ultrastar_note_to_midi_note,
|
| 15 |
+
)
|
| 16 |
+
from modules.console_colors import (
|
| 17 |
+
ULTRASINGER_HEAD,
|
| 18 |
+
red_highlighted,
|
| 19 |
+
)
|
| 20 |
+
from modules.Ultrastar.ultrastar_txt import UltrastarTxtValue
|
| 21 |
+
from modules.Pitcher.pitched_data import PitchedData
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def convert_ultrastar_to_midi_instrument(ultrastar_class: UltrastarTxtValue) -> object:
|
| 25 |
+
"""Converts an Ultrastar data to a midi instrument"""
|
| 26 |
+
|
| 27 |
+
print(f"{ULTRASINGER_HEAD} Creating midi instrument from Ultrastar txt")
|
| 28 |
+
|
| 29 |
+
instrument = pretty_midi.Instrument(program=0)
|
| 30 |
+
velocity = 100
|
| 31 |
+
|
| 32 |
+
for i in enumerate(ultrastar_class.words):
|
| 33 |
+
pos = i[0]
|
| 34 |
+
start_time = get_start_time_from_ultrastar(ultrastar_class, pos)
|
| 35 |
+
end_time = get_end_time_from_ultrastar(ultrastar_class, pos)
|
| 36 |
+
pitch = ultrastar_note_to_midi_note(int(ultrastar_class.pitches[pos]))
|
| 37 |
+
|
| 38 |
+
note = pretty_midi.Note(velocity, pitch, start_time, end_time)
|
| 39 |
+
instrument.notes.append(note)
|
| 40 |
+
|
| 41 |
+
return instrument
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def instruments_to_midi(instruments: list[object], bpm: float, midi_output: str) -> None:
|
| 45 |
+
"""Write instruments to midi file"""
|
| 46 |
+
|
| 47 |
+
print(f"{ULTRASINGER_HEAD} Creating midi file -> {midi_output}")
|
| 48 |
+
|
| 49 |
+
midi_data = pretty_midi.PrettyMIDI(initial_tempo=bpm)
|
| 50 |
+
for instrument in instruments:
|
| 51 |
+
midi_data.instruments.append(instrument)
|
| 52 |
+
midi_data.write(midi_output)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
class MidiCreator:
|
| 56 |
+
"""Docstring"""
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def convert_frequencies_to_notes(frequency: [str]) -> list[list[str]]:
|
| 60 |
+
"""Converts frequencies to notes"""
|
| 61 |
+
notes = []
|
| 62 |
+
for freq in frequency:
|
| 63 |
+
notes.append(librosa.hz_to_note(float(freq)))
|
| 64 |
+
return notes
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def most_frequent(array: [str]) -> list[tuple[str, int]]:
|
| 68 |
+
"""Get most frequent item in array"""
|
| 69 |
+
return Counter(array).most_common(1)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def find_nearest_index(array: list[float], value: float) -> int:
|
| 73 |
+
"""Nearest index in array"""
|
| 74 |
+
idx = np.searchsorted(array, value, side="left")
|
| 75 |
+
if idx > 0 and (
|
| 76 |
+
idx == len(array)
|
| 77 |
+
or math.fabs(value - array[idx - 1]) < math.fabs(value - array[idx])
|
| 78 |
+
):
|
| 79 |
+
return idx - 1
|
| 80 |
+
|
| 81 |
+
return idx
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def create_midi_notes_from_pitched_data(start_times: list[float], end_times: list[float], pitched_data: PitchedData) -> list[str]:
|
| 85 |
+
"""Create midi notes from pitched data"""
|
| 86 |
+
print(f"{ULTRASINGER_HEAD} Creating midi notes from pitched data")
|
| 87 |
+
|
| 88 |
+
midi_notes = []
|
| 89 |
+
|
| 90 |
+
for i in enumerate(start_times):
|
| 91 |
+
pos = i[0]
|
| 92 |
+
start_time = start_times[pos]
|
| 93 |
+
end_time = end_times[pos]
|
| 94 |
+
|
| 95 |
+
note = create_midi_note_from_pitched_data(
|
| 96 |
+
start_time, end_time, pitched_data
|
| 97 |
+
)
|
| 98 |
+
|
| 99 |
+
midi_notes.append(note)
|
| 100 |
+
# todo: Progress?
|
| 101 |
+
# print(filename + " f: " + str(mean))
|
| 102 |
+
return midi_notes
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def create_midi_note_from_pitched_data(start_time: float, end_time: float, pitched_data: PitchedData) -> str:
|
| 106 |
+
"""Create midi note from pitched data"""
|
| 107 |
+
|
| 108 |
+
start = find_nearest_index(pitched_data.times, start_time)
|
| 109 |
+
end = find_nearest_index(pitched_data.times, end_time)
|
| 110 |
+
|
| 111 |
+
if start == end:
|
| 112 |
+
freqs = [pitched_data.frequencies[start]]
|
| 113 |
+
confs = [pitched_data.confidence[start]]
|
| 114 |
+
else:
|
| 115 |
+
freqs = pitched_data.frequencies[start:end]
|
| 116 |
+
confs = pitched_data.confidence[start:end]
|
| 117 |
+
|
| 118 |
+
conf_f = get_frequencies_with_high_confidence(freqs, confs)
|
| 119 |
+
|
| 120 |
+
notes = convert_frequencies_to_notes(conf_f)
|
| 121 |
+
|
| 122 |
+
note = most_frequent(notes)[0][0]
|
| 123 |
+
|
| 124 |
+
return note
|