|
| 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