Added scaling and capping in midi_to_notes; removed notes_to_midi
Browse filesThe scaling and capping was done outside of midi_to_notes.
notes_to_midi was not necessary, since map_to_wav already did its job while being aware of the output format of the model.
- audio_methods.py +15 -36
audio_methods.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1 |
-
|
2 |
-
|
3 |
_SAMPLING_RATE = 16000 # Parameter to pass continuous signal to a discrete one
|
4 |
_INSTRUMENT_NAME = "Acoustic Grand Piano" # MIDI instrument used
|
5 |
_SCALING_FACTORS = pd.Series(
|
@@ -7,6 +6,9 @@ _SCALING_FACTORS = pd.Series(
|
|
7 |
) # Factors used to normalize song maps
|
8 |
|
9 |
def midi_to_notes(midi_file: str) -> pd.DataFrame:
|
|
|
|
|
|
|
10 |
pm = pretty_midi.PrettyMIDI(midi_file)
|
11 |
instrument = pm.instruments[0]
|
12 |
notes = collections.defaultdict(list)
|
@@ -15,45 +17,20 @@ def midi_to_notes(midi_file: str) -> pd.DataFrame:
|
|
15 |
sorted_notes = sorted(instrument.notes, key=lambda note: note.start)
|
16 |
prev_start = sorted_notes[0].start
|
17 |
|
|
|
18 |
for note in sorted_notes:
|
19 |
start = note.start
|
20 |
end = note.end
|
21 |
notes['pitch'].append(note.pitch)
|
22 |
-
notes['start'].append(start)
|
23 |
-
notes['end'].append(end)
|
24 |
notes['step'].append(start - prev_start)
|
25 |
notes['duration'].append(end - start)
|
26 |
prev_start = start
|
27 |
|
28 |
-
return pd.DataFrame({name: np.array(value) for name, value in notes.items()})
|
29 |
-
|
30 |
-
def notes_to_midi(
|
31 |
-
notes: pd.DataFrame,
|
32 |
-
out_file: str,
|
33 |
-
velocity: int = 100, # note loudness
|
34 |
-
) -> pretty_midi.PrettyMIDI:
|
35 |
-
|
36 |
-
pm = pretty_midi.PrettyMIDI()
|
37 |
-
instrument = pretty_midi.Instrument(
|
38 |
-
program=pretty_midi.instrument_name_to_program(
|
39 |
-
_INSTRUMENT_NAME))
|
40 |
-
|
41 |
-
prev_start = 0
|
42 |
-
for i, note in notes.iterrows():
|
43 |
-
start = float(prev_start + note['step'])
|
44 |
-
end = float(start + note['duration'])
|
45 |
-
note = pretty_midi.Note(
|
46 |
-
velocity=velocity,
|
47 |
-
pitch=int(note['pitch']),
|
48 |
-
start=start,
|
49 |
-
end=end,
|
50 |
-
)
|
51 |
-
instrument.notes.append(note)
|
52 |
-
prev_start = start
|
53 |
|
54 |
-
|
55 |
-
|
56 |
-
|
|
|
57 |
|
58 |
|
59 |
def display_audio(pm: pretty_midi.PrettyMIDI, seconds=120):
|
@@ -65,7 +42,9 @@ def display_audio(pm: pretty_midi.PrettyMIDI, seconds=120):
|
|
65 |
|
66 |
# Define function to convert song map to wav
|
67 |
|
68 |
-
def map_to_wav(song_map, out_file
|
|
|
|
|
69 |
contracted_map = tf.squeeze(song_map)
|
70 |
song_map_T = contracted_map.numpy().T
|
71 |
notes = pd.DataFrame(song_map_T, columns=["pitch", "step", "duration"]).mul(_SCALING_FACTORS, axis=1)
|
@@ -74,7 +53,7 @@ def map_to_wav(song_map, out_file, instrument_name, velocity=100):
|
|
74 |
pm = pretty_midi.PrettyMIDI()
|
75 |
instrument = pretty_midi.Instrument(
|
76 |
program=pretty_midi.instrument_name_to_program(
|
77 |
-
|
78 |
|
79 |
prev_start = 0
|
80 |
for i, note in notes.iterrows():
|
@@ -93,9 +72,9 @@ def map_to_wav(song_map, out_file, instrument_name, velocity=100):
|
|
93 |
pm.write(out_file)
|
94 |
return pm
|
95 |
|
96 |
-
def generate_and_display(out_file,
|
97 |
song_map = model.generate(z_sample)
|
98 |
display.display(imshow(tf.squeeze(song_map)[:,:50]))
|
99 |
-
wav = map_to_wav(song_map, out_file,
|
100 |
|
101 |
return display_audio(wav, seconds)
|
|
|
1 |
+
_CAP = 3501 # Cap for the number of notes
|
|
|
2 |
_SAMPLING_RATE = 16000 # Parameter to pass continuous signal to a discrete one
|
3 |
_INSTRUMENT_NAME = "Acoustic Grand Piano" # MIDI instrument used
|
4 |
_SCALING_FACTORS = pd.Series(
|
|
|
6 |
) # Factors used to normalize song maps
|
7 |
|
8 |
def midi_to_notes(midi_file: str) -> pd.DataFrame:
|
9 |
+
# Convert midi file to "song map" (dataframe where each note is broken
|
10 |
+
# into its components)
|
11 |
+
|
12 |
pm = pretty_midi.PrettyMIDI(midi_file)
|
13 |
instrument = pm.instruments[0]
|
14 |
notes = collections.defaultdict(list)
|
|
|
17 |
sorted_notes = sorted(instrument.notes, key=lambda note: note.start)
|
18 |
prev_start = sorted_notes[0].start
|
19 |
|
20 |
+
# Separate each individual note in pitch, step and duration
|
21 |
for note in sorted_notes:
|
22 |
start = note.start
|
23 |
end = note.end
|
24 |
notes['pitch'].append(note.pitch)
|
|
|
|
|
25 |
notes['step'].append(start - prev_start)
|
26 |
notes['duration'].append(end - start)
|
27 |
prev_start = start
|
28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
+
# Put notes in a dataframe
|
31 |
+
notes_df = pd.DataFrame({name: np.array(value) for name, value in notes.items()})
|
32 |
+
notes_df = notes_df[:_CAP] # Cap the song to match the model's architecture
|
33 |
+
return notes_df / _SCALING_FACTORS # Scale
|
34 |
|
35 |
|
36 |
def display_audio(pm: pretty_midi.PrettyMIDI, seconds=120):
|
|
|
42 |
|
43 |
# Define function to convert song map to wav
|
44 |
|
45 |
+
def map_to_wav(song_map: pd.DataFrame, out_file: str, velocity: int=100):
|
46 |
+
# Convert "song map" to midi file (reverse process with respect to midi_to_notes)
|
47 |
+
|
48 |
contracted_map = tf.squeeze(song_map)
|
49 |
song_map_T = contracted_map.numpy().T
|
50 |
notes = pd.DataFrame(song_map_T, columns=["pitch", "step", "duration"]).mul(_SCALING_FACTORS, axis=1)
|
|
|
53 |
pm = pretty_midi.PrettyMIDI()
|
54 |
instrument = pretty_midi.Instrument(
|
55 |
program=pretty_midi.instrument_name_to_program(
|
56 |
+
_INSTRUMENT_NAME))
|
57 |
|
58 |
prev_start = 0
|
59 |
for i, note in notes.iterrows():
|
|
|
72 |
pm.write(out_file)
|
73 |
return pm
|
74 |
|
75 |
+
def generate_and_display(out_file, model, z_sample=None, velocity=100, seconds=120):
|
76 |
song_map = model.generate(z_sample)
|
77 |
display.display(imshow(tf.squeeze(song_map)[:,:50]))
|
78 |
+
wav = map_to_wav(song_map, out_file, velocity)
|
79 |
|
80 |
return display_audio(wav, seconds)
|