-
Notifications
You must be signed in to change notification settings - Fork 18
/
midi_functions.py
138 lines (109 loc) · 5.46 KB
/
midi_functions.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#Imports
from settings import *
import numpy as np
import midi_functions as mf
import _pickle as pickle
import os
import sys
import pretty_midi as pm
import mido
import operator
def programs_to_instrument_matrix(programs, instrument_attach_method, max_voices):
if instrument_attach_method == '1hot-instrument':
#very large, not recommended
instrument_feature_matrix = np.zeros((max_voices, 128))
for i, program in enumerate(programs):
instrument_feature_matrix[i, program] = 1
elif instrument_attach_method == '1hot-category':
#categories according to midi declaration, https://en.wikipedia.org/wiki/General_MIDI
#8 consecutive instruments make 1 category
instrument_feature_matrix = np.zeros((max_voices, 16))
for i, program in enumerate(programs):
instrument_feature_matrix[i, program//8] = 1
elif instrument_attach_method == 'khot-instrument':
#make a khot vector in log2 base for the instrument
#log2(128) = 7
instrument_feature_matrix = np.zeros((max_voices, 7))
for i, program in enumerate(programs):
p = program
for exponent in range(7):
if p % 2 == 0:
instrument_feature_matrix[i, exponent] = 1
p = p // 2
elif instrument_attach_method == 'khot-category':
#categories according to midi declaration, https://en.wikipedia.org/wiki/General_MIDI
#8 consecutive instruments make 1 category
#make a khot vector in log2 base for the category
#log2(16) = 4
instrument_feature_matrix = np.zeros((max_voices, 4))
for i, program in enumerate(programs):
p = program//8
for exponent in range(4):
if p % 2 == 1:
instrument_feature_matrix[i, exponent] = 1
p = p // 2
else:
print("Not implemented!")
return instrument_feature_matrix
def rolls_to_midi(pianoroll, programs, save_folder, filename, bpm, velocity_roll=None, held_notes_roll=None):
#bpm is in quarter notes, so scale accordingly
bpm = bpm * (SMALLEST_NOTE / 4)
pianoroll = np.pad(np.copy(pianoroll), ((0,0),(low_crop,num_notes-high_crop)), mode='constant', constant_values=0)
if not os.path.exists(save_folder):
os.makedirs(save_folder)
midi = pm.PrettyMIDI(initial_tempo=bpm, resolution=1000)
midi.time_signature_changes.append(pm.TimeSignature(4, 4, 0))
for voice, program in enumerate(programs):
current_instrument = pm.Instrument(program=program)
current_pianoroll = pianoroll[voice::len(programs),:]
if velocity_roll is not None:
current_velocity_roll = np.copy(velocity_roll[voice::len(programs)])
#during the training, the velocities were scaled to be in the range 0,1
#scale it back to the actual velocity numbers
current_velocity_roll[np.where(current_velocity_roll < velocity_threshold_such_that_it_is_a_played_note)] = 0
current_velocity_roll[np.where(current_velocity_roll >= velocity_threshold_such_that_it_is_a_played_note)] -= 0.5
current_velocity_roll /= (1.0 - velocity_threshold_such_that_it_is_a_played_note)
current_velocity_roll *= MAX_VELOCITY
if held_notes_roll is not None:
current_held_notes_roll = np.copy(held_notes_roll[voice::len(programs)])
tracker = []
start_times = dict()
velocities = dict()
for i, note_vector in enumerate(current_pianoroll):
notes = list(note_vector.nonzero()[0])
#
#notes that were just played and need to be removed from the tracker
removal_list = []
for note in tracker:
#determine if you still hold this note or not
hold_this_note = True
if held_notes_roll is not None:
hold_this_note = current_held_notes_roll[i] > 0.5
#it may happen that a note seems to be held but has switched to another channel
#in that case, play the note anyways
if note not in notes:
hold_this_note = False
else:
hold_this_note = note in notes and (i)% SMALLEST_NOTE is not 0
if hold_this_note:
#held note, don't play a new note
notes.remove(note)
else:
if velocity_roll is not None:
velocity = velocities[note]
if velocity > MAX_VELOCITY:
velocity = int(MAX_VELOCITY)
else:
velocity = 80
midi_note = pm.Note(velocity=velocity, pitch=note, start=(60/bpm)*start_times[note], end=(60/bpm)*i)
current_instrument.notes.append(midi_note)
removal_list.append(note)
for note in removal_list:
tracker.remove(note)
for note in notes:
tracker.append(note)
start_times[note]=i
if velocity_roll is not None:
velocities[note] = int(current_velocity_roll[i])
midi.instruments.append(current_instrument)
midi.write(save_folder + filename+'.mid')