-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrecorder.py
137 lines (107 loc) · 4.56 KB
/
recorder.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
import os
from shutil import copyfile
import subprocess
import time
from mido import MidiFile
from multiprocessing import Process
import music21
import sys
class MIDIRecorder:
playing = False
recording = False
looping = False
Factor = 1
playid = 0
ntrack = 0
ntrackcurrent = 0
mididir = None
midiplayport = None
midirecordport = None
maxtracks = None
looptweak = None
scores = None
playingprocess = None
loopprocess = None
# mididir - directory midi files are saved and transformed in
# midiplayport -
# midirecordport -
def __init__(self, mididir, midiplayport, midirecordport, maxtracks, looptweak):
# TODO: assert mididir is a valid path, and the
# ports are described by valid strings
self.mididir = mididir
self.midiplayport = midiplayport
self.midirecordport = midirecordport
self.maxtracks = maxtracks
self.looptweak = looptweak
self.scores = [None]*maxtracks
# Return the path to the file we care about
def _currentfile(self, basefile):
if basefile:
return os.path.join(self.mididir, "track-" + str(self.ntrack) + ".mid")
else:
return os.path.join(self.mididir, "output.mid")
def _run(self, command):
return subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid)
def _kill(self, process):
try:
os.killpg(os.getpgid(process.pid), signal=SIGTERM)
except:
pass
def _playfile(self):
playingprocess = self._run("aplaymidi -p " + self.midiplayport + " " + self._currentfile(False))
def _stopfile(self):
self._kill(playingprocess)
playingprocess = None
def _recordfile(self):
playingprocess = self._run("arecordmidi -p " + self.midirecordport + " " + self._currentfile(True))
# Generate the score
score[self.ntrack] = music21.converter.parse(self._currentfile(True))
newscore.write("midi", self._currentfile(False))
def _sleepandcall(self, t):
while True:
time.sleep(t)
self._playfile()
def _getmidilength(self, basefile):
return MidiFile(self._currentfile(basefile)).length
def _loopfile(self):
timetosleep = self._getmidilength(False) - self.looptweak
self.loopprocess = Process(target=self._sleepandcall, args=(timetosleep,))
self.loopprocess.start()
def _stoploopfile(self):
self.loopprocess.terminate()
self._stopfile()
def toggleplay(self):
if self.playing:
self._stopfile()
self.playing = False
self.ntrack = self.ntrackcurr
elif not self.recording and not self.looping:
self._playfile()
self.playing = True
def togglerecord(self):
if self.recording:
self._stopfile()
self.recording = False
self.ntrack = self.ntrackcurr
elif not self.playing and not self.looping:
self._recordfile()
self.recording = True
def toggleloop(self):
if self.looping:
self._stoploopfile()
self.looping = False
self.ntrack = self.ntrackcurr
elif not self.playing and not self.recording:
self._loopfile()
self.looping = True
def changetrack(self, ntrack):
assert(ntrack => 0 and ntrack < self.maxtracks)
if not self.playing and not self.recording and not self.looping:
self.ntrack = ntrack
self.ntrackcurr = ntrack
def changespeed(self, factor):
if scores == None:
print("Can't import empty score!")
sys.exit(1)
self.scores[self.ntrack].scaleOffsets(factor).scaleDurations(factor)
newscore.write("midi", self._currentfile(False))