Skip to content
This repository was archived by the owner on Nov 23, 2023. It is now read-only.

Commit 69863b5

Browse files
external MIDI synth, data fixes
1 parent 68b28a7 commit 69863b5

File tree

3 files changed

+101
-13
lines changed

3 files changed

+101
-13
lines changed

examples/notepredictor/generate.scd

Lines changed: 92 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,66 @@
1717
// TODO: pitch blacklist option
1818
// use case: forbid model from releasing a note held by the player
1919

20+
MIDIClient.init
21+
MIDIClient.destinations
22+
~m1 = MIDIOut.newByName("IAC Driver", "Bus 1");
23+
~m2 = MIDIOut.newByName("IAC Driver", "IAC Bus 2");
24+
/*
25+
~m1.noteOn(0, 60, 127)
26+
~m2.noteOn(0, 60, 127)
27+
~m1.noteOff(0, 60, 64)
28+
~m2.noteOff(0, 60, 64)*/
29+
30+
(
31+
~prog2portchan = { arg prog;
32+
case
33+
{prog==0}{"can't convert start token".postln}
34+
{prog<=128}{
35+
var group = (prog-1 /8).asInteger;
36+
var idx = (prog-1 %8).asInteger;
37+
var port = switch(group)
38+
{ 0}{(idx<6).if{~m1}{~m2}} //piano
39+
{ 1}{((idx<3)||(idx==5)).if{~m1}{~m2}} //chromatic perc
40+
{ 2}{(idx<4).if{~m1}{~m2}} //organ
41+
{ 3}{(idx<5).if{~m1}{~m2}} //guitar
42+
{ 4}{(idx<4).if{~m2}{~m1}} //bass
43+
{ 5}{(idx<5).if{~m1}{~m2}} //strings
44+
{ 6}{(idx<4).if{~m1}{~m2}} //ensemble
45+
{ 7}{(idx<3).if{~m1}{~m2}} //brass
46+
{ 8}{(idx<4).if{~m1}{~m2}} //reed
47+
{ 9}{(idx<3).if{~m1}{~m2}} //pipe
48+
{10}{(idx%2==0).if{~m1}{~m2}} //synth lead
49+
{11}{(idx<4).if{~m1}{~m2}} //synth pad
50+
{12}{~m1} //synth fx
51+
{13}{case //'ethnic'
52+
{idx<=3}{~m1}
53+
{idx==4}{~m2}
54+
{idx==5}{group=2;~m2}
55+
{idx==6}{group=5;~m1}
56+
{idx==7}{group=8;~m2}
57+
}
58+
{14}{~m1} //percussive
59+
{15}{~m1} //sound fx
60+
;
61+
// \melody.postln;
62+
(port:port, chan:group)
63+
}{(prog<=256)||(prog>=265)}{
64+
// \drum.postln;
65+
(port:~m2, chan:12)
66+
}{prog-257 < 8}{
67+
// \anon.postln;
68+
(port:~m1, chan:0)
69+
}
70+
};
71+
~release_all = {arg vel=0;
72+
[~m1, ~m2].do{arg port; 128.do{arg note; 16.do{arg chan; port.noteOff(chan, note, vel)}}}
73+
};
74+
)
75+
76+
/*(
77+
~prog2portchan.(257)[\chan]
78+
)*/
79+
2080
(
2181
~gui = false;
2282
MIDIIn.connectAll;
@@ -95,6 +155,7 @@ MIDIdef.noteOff(\input_off, {
95155
var t2 = Process.elapsedTime;
96156
var dt = t2-(t?(t2-~delay)); //time since last note
97157
var inst = ~player_inst;
158+
var port_chan = ~prog2portchan.(inst);
98159

99160
// cancel any pending predictions
100161
SystemClock.clear;
@@ -112,6 +173,8 @@ MIDIdef.noteOff(\input_off, {
112173
// release the previous note
113174
~synths[num]!?(_.release(0.05));
114175
~synths[num] = nil;
176+
// send MIDI
177+
port_chan[\port].noteOff(port_chan[\chan], num);
115178

116179
// post the current note
117180
[\player, dt, inst, num, 0].postln;
@@ -130,6 +193,8 @@ MIDIdef.noteOn(\input_on, {
130193
var t2 = Process.elapsedTime;
131194
var dt = t2-(t?(t2-~delay)); //time since last note
132195
var inst = ~player_inst;
196+
var port_chan = ~prog2portchan.(inst);
197+
133198

134199
// cancel any pending predictions
135200
SystemClock.clear;
@@ -145,11 +210,13 @@ MIDIdef.noteOn(\input_on, {
145210
~pending_predictions = ~pending_predictions+1;
146211

147212

148-
// release the previous note
213+
// release the previous note (if not properly released by noteoff)
149214
~synths[num]!?(_.release(0.05));
150215

151216
// play the current note
152217
~synths[num] = Synth(\pluck, [\freq, num.midicps, \vel, val/127]);//.release(1);
218+
// send MIDI
219+
port_chan[\port].noteOn(port_chan[\chan], num, val);
153220

154221
// post the current note
155222
[\player, dt, inst, num, val].postln;
@@ -170,9 +237,11 @@ OSCdef(\return, {
170237
var inst = msg[1]; // instrument of predicted note
171238
var pitch = msg[2]; // MIDI number of predicted note
172239
var dt = msg[3]; // time to predicted note
173-
var val = msg[4]; // velocity 0-127
240+
var vel = msg[4]; // velocity 0-127
174241
var end = msg[5];
175242
var step = msg[6];
243+
var port_chan = ~prog2portchan.(inst);
244+
176245

177246
// time-to-next note gets 'censored' by the model
178247
// when over a threshold, in this case 10 seconds,
@@ -199,6 +268,7 @@ OSCdef(\return, {
199268
// b.sendMsg("/predictor/reset");
200269
//release the last note
201270
~synths.do(_.release(1.0));
271+
~release_all.(127);
202272
// unset time so next note will have dt=0
203273
// t = nil;
204274
// \reset.postln
@@ -211,22 +281,30 @@ OSCdef(\return, {
211281
// ~pending_predictions.postln;
212282
// feed model its own prediction as input
213283
b.sendMsg("/predictor/predict",
214-
\inst, inst, \pitch, pitch, \time, dt_actual, \vel, val,
284+
\inst, inst, \pitch, pitch, \time, dt_actual, \vel, vel,
215285
\allow_start, false, \allow_end, true,
216-
\pitch_temp, 0.7, \rhythm_temp, 0.7, \timing_temp, 0.1,
217-
\min_time, ~delay*0, \max_time, 5,
286+
\instrument_temp, 1, \pitch_temp, 0.9, \rhythm_temp, 0.7, \timing_temp, 0.05,
287+
// \instrument_temp, 1, \pitch_temp, 1, \rhythm_temp, 1, \timing_temp, 1,
288+
\min_time, ~delay,
289+
\max_time, 5,
290+
218291
);
219292
~pending_predictions = ~pending_predictions+1;
220293

221294
// play the current note
222295
~synths[pitch]!?(_.release(0.05));
223-
(val > 0).if{
224-
~synths[pitch] = Synth(\pluck, [\freq, pitch.midicps, \vel, val/127])
296+
// send MIDI
297+
port_chan[\port].noteOff(port_chan[\chan], pitch, 64);
298+
(vel > 0).if{
299+
~synths[pitch] = Synth(\pluck, [
300+
\freq, pitch.midicps, \vel, vel/127]);
301+
// send MIDI
302+
port_chan[\port].noteOn(port_chan[\chan], pitch, vel)
225303
}{
226-
~synths[pitch] = nil
304+
~synths[pitch] = nil;
227305
};
228306
// post the current note
229-
[\model, step, dt, inst, pitch, val, end].postln;
307+
[\model, step, dt, inst, pitch, vel, end].postln;
230308
// mark the actual time of current note
231309
t = t2;
232310
~machine_t = t;
@@ -248,9 +326,12 @@ OSCdef(\return, {
248326
// send a note manually if you don't have a MIDI controller:
249327
SystemClock.clear;
250328
~synths.do(_.release(1.0));
329+
~release_all.(0);
251330
b.sendMsg("/predictor/reset");
252-
~player_inst = 83;
253-
{MIDIdef.all[\input_on].func.value(99, 60)}.defer(0.5);
331+
// ~player_inst = 272;
332+
// {MIDIdef.all[\input_on].func.value(99, 83)}.defer(0.5);
333+
~player_inst = 128.rand+1;
334+
{MIDIdef.all[\input_on].func.value(128.rand, 128.rand)}.defer(0.1);
254335
SystemClock.clear;
255336
)
256337
// b.sendMsg("/predictor/predict", \pitch, 70, \time, 0, \vel, 64);

notepredictor/notepredictor/data.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,13 @@ def __getitem__(self, idx):
9595
velocity.new_zeros((1,)),
9696
velocity,
9797
velocity.new_zeros((pad,))))
98-
# end signal: nonzero for last event
98+
# end signal: nonzero for last event + padding
9999
end = torch.zeros_like(program)
100-
end[-1] = 1
100+
end[-pad-1:] = 1
101+
102+
mask = torch.zeros_like(program)
103+
if pad > 0:
104+
mask[-pad:] = 1
101105

102106
# random slice
103107
i = random.randint(0, len(pitch)-self.batch_len)
@@ -106,8 +110,10 @@ def __getitem__(self, idx):
106110
time = time[i:i+self.batch_len]
107111
velocity = velocity[i:i+self.batch_len]
108112
end = end[i:i+self.batch_len]
113+
mask = mask[i:i+self.batch_len]
109114

110115
return {
116+
'mask':mask,
111117
'end':end,
112118
'instrument':program,
113119
'pitch':pitch,

notepredictor/scripts/train_notes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ def process_grad(self):
140140
return r
141141

142142
def get_loss_components(self, result):
143+
# TODO: masking
143144
return {
144145
'instrument_nll': -result['instrument_log_probs'].mean(),
145146
'pitch_nll': -result['pitch_log_probs'].mean(),

0 commit comments

Comments
 (0)