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
|