JiT-NMF-Classifier now working

nix
Pierre Alexandre Tremblay 6 years ago
parent 80b95e33fd
commit 57f431a349

@ -1,7 +1,30 @@
s.reboot // using nmf in 'real-time' as a classifier
// how it works: a circular buffer is recording and attacks trigger the process
// if in learning mode, it does a one component nmf which makes an approximation of the base. 3 of those will be copied in 3 different positions of our final 3-component base
// in in guessing mode, it does a thres component nmf from the trained bases and yields the 3 activation peaks, on which it thresholds resynth
//how to use:
// 1. start the server
// 2. select between parenthesis below and execute. You should get a window with 3 pads (bd sn hh) and various menus
// 3. train the 3 classes:
// 3.1 select the learn option
// 3.2 select which class you want to train
// 3.3 play the sound you want to associate with that class a few times (the left audio channel is the source)
// 3.4 click the transfer button
// 3.5 repeat (3.2-3.4) for the other 2 classes.
// 3.x you can observe the 3 bases here:
f.plot(numChannels:3)
// 4. classify
// 4.1 select the classify option
// 4.2 press a pad and look at the activation
// 4.3 tweak the thresholds and enjoy the resynthesis. (the right audio channel is the detected class where classA is a bd sound)
// 4.x you can observe the 3 activations here:
h.plot(numChannels:3)
//a circular buffer is doing a fake real time and attacks trigger an analysis
/// code to execute first
( (
b = Buffer.alloc(s,s.sampleRate * 2); b = Buffer.alloc(s,s.sampleRate * 2);
g = Bus.audio(s,1); g = Bus.audio(s,1);
@ -11,8 +34,10 @@ e = Buffer.alloc(s, 65);
f = Buffer.alloc(s, 65, 3); f = Buffer.alloc(s, 65, 3);
h = Buffer.alloc(s, 65, 3); h = Buffer.alloc(s, 65, 3);
j = [0.0,0.0,0.0]; j = [0.0,0.0,0.0];
k = [0.0,0.0,0.0]; k = [0.5,0.5,0.5];
// the circular buffer with triggered actions sending the location of the head at the attack
Routine {
SynthDef(\JITcircular,{arg bufnum = 0, input = 0, env = 0; SynthDef(\JITcircular,{arg bufnum = 0, input = 0, env = 0;
var head, head2, duration, audioin, halfdur, trig; var head, head2, duration, audioin, halfdur, trig;
duration = BufFrames.kr(bufnum) / 2; duration = BufFrames.kr(bufnum) / 2;
@ -34,8 +59,7 @@ SynthDef(\JITcircular,{arg bufnum = 0, input = 0, env = 0;
// drum sounds taken from original code by snappizz // drum sounds taken from original code by snappizz
// https://sccode.org/1-523 // https://sccode.org/1-523
// produced further and randomised by PA // produced further and humanised by PA
SynthDef(\fluidbd, { SynthDef(\fluidbd, {
|out = 0| |out = 0|
var body, bodyFreq, bodyAmp; var body, bodyFreq, bodyAmp;
@ -96,19 +120,21 @@ SynthDef(\fluidhh, {
Out.ar(out, noise); Out.ar(out, noise);
}).add; }).add;
)
( // makes sure all the synthdefs are on the server
s.sync;
// instantiate the JIT-circular-buffer // instantiate the JIT-circular-buffer
x = Synth(\JITcircular,[\bufnum, b.bufnum, \input, g.index]); x = Synth(\JITcircular,[\bufnum, b.bufnum, \input, g.index]);
e.fill(0,65,0.1); e.fill(0,65,0.1);
// instantiate the listener to cue the processing from the language side // instantiate the listener to cue the processing from the language side
r = OSCFunc({ arg msg; r = OSCFunc({ arg msg;
if (c == 0, { if (c == 0, {
// if in training mode, makes a single component nmf
FluidBufNMF.process(s, b, msg[3], 128, bases:e, basesMode: 1, windowSize: 128); FluidBufNMF.process(s, b, msg[3], 128, bases:e, basesMode: 1, windowSize: 128);
}, { }, {
// if in classifying mode, makes a 3 component nmf from the pretrained bases and compares the activations with the set thresholds
FluidBufNMF.process(s, b, msg[3], 128, components:3, bases:f, basesMode: 2, activations:h, windowSize: 128, action:{ FluidBufNMF.process(s, b, msg[3], 128, components:3, bases:f, basesMode: 2, activations:h, windowSize: 128, action:{
h.getn(3,3,{|x| h.getn(3,3,{|x|
j = x; j = x;
@ -120,10 +146,12 @@ r = OSCFunc({ arg msg;
); );
}); });
}, '/attack', s.addr); }, '/attack', s.addr);
)
// stop it all // make sure all the synths are instantiated
( s.sync;
// GUI for control
{
w = Window("Control", Rect(100,100,590,100)).front; w = Window("Control", Rect(100,100,590,100)).front;
Button(w, Rect(10,10,80, 80)).states_([["bd",Color.black,Color.white]]).mouseDownAction_({Synth(\fluidbd, [\out, g.index], x, \addBefore)}); Button(w, Rect(10,10,80, 80)).states_([["bd",Color.black,Color.white]]).mouseDownAction_({Synth(\fluidbd, [\out, g.index], x, \addBefore)});
@ -139,13 +167,15 @@ l = Array.fill(3, {arg i;
}); });
StaticText(w, Rect(520,7,55,25)).string_("Thresh").align_(\center); StaticText(w, Rect(520,7,55,25)).string_("Thresh").align_(\center);
3.do {arg i; 3.do {arg i;
TextField(w, Rect(520,((i+1) * 20 )+ 7,55,25)).action_({|x| k[i] = x.value.asFloat;}); TextField(w, Rect(520,((i+1) * 20 )+ 7,55,25)).string_("0.5").action_({|x| k[i] = x.value.asFloat;});
}); };
w.onClose_({b.free;g.free;r.clear;x.free; y.free;q.stop;}); w.onClose_({b.free;g.free;r.clear;x.free; y.free;q.stop;});
) }.defer;
( s.sync;
// updates the activations
q = Routine { q = Routine {
{ {
{ {
@ -156,9 +186,5 @@ q = Routine {
0.1.wait; 0.1.wait;
}.loop; }.loop;
}.play; }.play;
}.play;
) )
e.getn(0,65,{|x|x.postln;})
f.getn(0,65 * 3,{|x|x.postln;})
f.plot(numChannels:3)
h.plot(numChannels:3)

Loading…
Cancel
Save