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

Commit 2896db4

Browse files
SC example; tweak time distribution; time data augmentation; docstrings
1 parent 8c91bd4 commit 2896db4

7 files changed

Lines changed: 304 additions & 89 deletions

File tree

clients/midi-duet.scd

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
(
2+
MIDIIn.connectAll;
3+
b = NetAddr.new("127.0.0.1", 9999);
4+
// Server.default.options.inDevice_("Built-in Microph");
5+
// Server.default.options.outDevice_("Built-in Output");
6+
Server.default.options.inDevice_("mic-buds");
7+
Server.default.options.outDevice_("mic-buds");
8+
s.boot;
9+
k = MIDIKeyboard.new(bounds: Rect(0, 0, 500, 100), octaves:11, startnote:0)
10+
)
11+
12+
(
13+
SynthDef(\pluck, {
14+
var signal = Saw.ar(\freq.kr, 0.2) * EnvGate.new(1);
15+
Out.ar([0,1], signal);
16+
}).add
17+
)
18+
19+
// measure round-trip latency
20+
(
21+
OSCdef(\return, {
22+
arg msg, time, addr, recvPort;
23+
(Process.elapsedTime - t).postln;
24+
}, '/prediction', nil);
25+
t = Process.elapsedTime;
26+
b.sendMsg("/predictor/predict", 60+12.rand, 0);
27+
)
28+
29+
// set the delay for more precise timing
30+
~delay = 0.015;
31+
32+
// NetAddr.localAddr // retrieve the current IP and port
33+
// thisProcess.openPorts; // list all open ports
34+
35+
// duet with the model
36+
// feeds the model's predictions back to it as well as player input
37+
(
38+
t = Process.elapsedTime;
39+
b.sendMsg("/predictor/reset");
40+
41+
// MIDI from controller
42+
MIDIdef.noteOn(\input, {
43+
arg val, num, chan, src;
44+
var t2 = Process.elapsedTime;
45+
var dt = t2-t; //time since last note
46+
47+
// cancel any pending predictions
48+
SystemClock.clear;
49+
50+
//get a new prediction in light of current note
51+
b.sendMsg("/predictor/predict", num, dt);
52+
53+
// release the previous note
54+
y.release(1.0);
55+
56+
// play the current note
57+
y = Synth(\pluck, [\freq, num.midicps]);//.release(1);
58+
59+
// post the current note
60+
[\player, dt, num].postln;
61+
62+
// mark time of current note
63+
t = t2;
64+
});
65+
66+
67+
// OSC return from python
68+
OSCdef(\return, {
69+
arg msg, time, addr, recvPort;
70+
var num = msg[1]; // MIDI number of predicted note
71+
var dt = msg[2]; // time to predicted note
72+
73+
// time-to-next note gets 'censored' by the model
74+
// when over a threshold, in this case 10 seconds,
75+
// meaning it just predicts 10s rather than a any longer time
76+
var censor = dt==10.0;
77+
78+
censor.if{
79+
// if the predicted time is > 10 seconds, don't schedule it, just stop.
80+
\censor.postln; y.release(3.0)
81+
}{
82+
// schedule the predicted note
83+
SystemClock.sched(dt-~delay, {
84+
(num==129).if{
85+
// 129 is the 'stop token', meaning 'end-of-performance'
86+
// in this case don't schedule a note, and reset the model
87+
b.sendMsg("/predictor/reset");
88+
//release the last note
89+
y.release(1.0);
90+
\reset.postln
91+
}{
92+
// cancel any pending predictions (there shouldn't be any, but might
93+
// be if there was a lot of fast MIDI input)
94+
SystemClock.clear;
95+
// feed model its own prediction as input
96+
b.sendMsg("/predictor/predict", num, dt);
97+
// release the previous note
98+
(dt<1e-2).if{
99+
// if the time delay is very small, slowly release to play a chord
100+
y.release(1.0)
101+
}{
102+
// otherwise release fast to play a melody
103+
y.release(0.1)
104+
};
105+
// play the current note
106+
y = Synth(\pluck, [\freq, num.midicps]);//.release(1);
107+
// post the current note
108+
[\model, dt, num].postln;
109+
// mark the time of current note
110+
t = Process.elapsedTime;
111+
// crudely draw note on piano GUI
112+
AppClock.sched(0,{k.keyDown(num)});
113+
AppClock.sched(0.2,{k.keyUp(num)});
114+
}
115+
})};
116+
117+
}, '/prediction', nil);
118+
)
119+
120+
// b.sendMsg("/predictor/predict", 70, 0.1);
121+
122+
// load another model
123+
// b.sendMsg("/predictor/load", "/path/to/checkpoint");

0 commit comments

Comments
 (0)