-
Notifications
You must be signed in to change notification settings - Fork 3
/
midi.py
120 lines (100 loc) · 4.3 KB
/
midi.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
import pygame
import pandas as pd
import numpy as np
from midiutil.MidiFile import MIDIFile
def main_playmidi(csv_midi: pd.DataFrame):
"""
the main function that calls different functions that convert
the quantitive data back to midi and plays it.
parameters: dataframe of the quantitive data
output: playing the midi file
"""
csv_to_midi(csv_midi)
midi_file = 'midifile.mid'
freq = 44100 # audio CD quality
bitsize = -16 # unsigned 16 bit
channels = 2 # 1 is mono, 2 is stereo
buffer = 1024 # number of samples
pygame.mixer.init(freq, bitsize, channels, buffer)
pygame.mixer.music.set_volume(0.8)
# listen for interruptions
try:
# use the midi file you just saved
play_music(midi_file)
except KeyboardInterrupt:
# if user hits Ctrl/C then exit
# (works only in console mode)
pygame.mixer.music.fadeout(1000)
pygame.mixer.music.stop()
raise SystemExit
def csv_to_midi(csvdata):
"""
converts the data from the csv file into a midi file.
checks the time, duration and sound volume (velocity) of each note
and generates a new midi file containing this information
parameters: dataframe of the quantitive data
returns: a midi file
"""
midi_data = calculate_duration(csvdata)
track = 0
channel = 0
time = 0
tempo = 60 # In BPM - use 60 so that time is just in seconds
mymidi = MIDIFile(1) # One track, defaults to format 1 (tempo track is created
# automatically)
mymidi.addTempo(track, time, tempo)
for index, row in midi_data.iterrows():
note = int(row['note'])
volume = int(row['velocity'])
starttime = row['starttime_utc']
duration = row['duration']
mymidi.addNote(track, channel, note, starttime, duration, volume)
with open('midifile.mid', "wb") as output_file:
mymidi.writeFile(output_file)
def play_music(midi_file):
"""
loads and plays a midi file, using a package of playing music.
"""
clock = pygame.time.Clock()
try:
pygame.mixer.music.load(midi_file)
print("Music file %s loaded!" % midi_file)
except pygame.error:
print("File %s not found! (%s)" % (midi_file, pygame.get_error()))
return
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
# check if playback has finished
clock.tick(30)
def calculate_duration(csvdata: pd.DataFrame):
"""
allows the rearange the quantitive midi data;
in the original file, there are different rows to pressing and releasing the keyboard.
this function creates one row of data for each note played including the start time and duration.
it also converts the start time to UTC time, which is the only way a midi file can be generated.
parameters: dataframe with the original data
returns: new dataframe contaning the above-mentioned data.
"""
notes_data = pd.DataFrame(np.zeros(shape=[88, 5]), index=[np.arange(21, 109)],
columns=['starttime', 'starttime_utc', 'note', 'duration', 'velocity'])
new_data = pd.DataFrame(columns=['starttime', 'starttime_utc', 'note', 'duration', 'velocity'])
startsong = csvdata['timestamp'].iloc[0] - 0.001
for index, row in csvdata.iterrows():
note = row['note']
if row['action'] == 1:
if int(notes_data.loc[note, 'starttime_utc']) == 0:
notes_data.loc[note, 'starttime'] = row['time']
notes_data.loc[note, 'starttime_utc'] = row['timestamp'] - startsong
notes_data.loc[note, 'note'] = row['note']
notes_data.loc[note, 'velocity'] = row['velocity']
elif row['action'] == 2:
if int(notes_data.loc[note, 'starttime_utc'] != 0):
notes_data.loc[note, 'duration'] = row['timestamp'] - startsong - notes_data.loc[note, 'starttime_utc']
new_line = notes_data.loc[note]
new_data = new_data.append(new_line, ignore_index=True)
notes_data.loc[note, :] = np.zeros(shape=(1, 5))
new_data = new_data.sort_values('starttime').reindex()
return new_data
if __name__ == '__main__':
csvdata = pd.read_csv('test_data/session_1.csv')
main_playmidi(csvdata)