[Release] 1.0.0-beta7 (#119)
* ignore more varieties of build folder * deal with some warnings * FluidWaveform 'lineWidth' argument now also affects feature lines * add back the nmf-jit-classifier example * novelty interface change in example * resizable and layoutable guis (#83) * resizable and layoutable guis * FluidWaveform: rename 'win' to 'parent' * FluidWaveform/FluidPlotter: update help * FluidWaveform/Plotter: make views before forking This way views are immediately available upon creation, for example to be added to layouts. Views are still correctly updated with data from within the fork, whenever they are ready. * Thanks @elgiano! + a few small edits Co-authored-by: Ted Moore <ted@tedmooremusic.com> * slicers: add enums for algorithms (#86) * typo * FluidBufNMF class: add resynthMode argument * add PCA whitening parameter (#65) * add PCA whitening parameter * FluidPCA: Ensure whiten parameter is sent to kr query UGen Co-authored-by: Gerard <gerard@bumblebee.lan> Co-authored-by: weefuzzy <gungwho@gmail.com> * Enhance/optional message args (#77) * optional args: sc wrapper updates * optional args: KDTree try out * Enhance/choices param (#78) * NRTWrapper: Add choices param (long <-> bitset) * Update `FluidBufStats` with `select` control * BufStats class: Fix bitfield for `select` and warn on duplicate items * Update SpectralShape classes for new param * `PCA.sc`: add batch `inverseTranform` method * Wrapper: integer sign warnings * Enhance/max params (#93) * CMake: Centralise C++ version and set to 17 * Wrapper: handle new LongRuntimeMax param type * POC for new LongRuntimeMax param with MFCC numCoeffs * Wrapper: Make MSVC happy about constexpr lambda capture * All scalers: replace 'invert' parameter with `inverseTransform` messages * Wrapper: Work around 32 char limit for plugin commands If too long, remove vowels. Sorry. Better ideas welcome * fix #96 * typo * Update SC classes for new style `max<X>` parameters * SpectralShape SC class: maxFFT * RealTime wrapper: play it safer with output channel count This really relies on the SC class being correct, but then everything ultimately does... * RT FFT Object SC Classes: Provide maxFFTSize default * BufSTFT SC class: Add maxFFT (now needed due to core type change) * Add select param to Loudness and Pitch SC clases (#101) * Wrapper: workaround scsynth 32 char cmd length limit with extra dispatch layer also avoids need for formerly truncated plugin names in some cases * removed invert from scalers class definitions (#102) * Enhance/generate stubs (#104) * CMake: generate .cpp stubs * Remove old cpp stubs * Ensure correct MSVC runtime by default * CMake: invoke docs properly * CMake: Tidy up * CMake: Tidy up * CMake: typo * CI: Update nightly * CI: remove lingering references to docs job * CMake: belatedly add branch selection for flucoma deps upon which CI relies * CMake: Actually commit important code for best collaborative results * CMake: This file is now redundant, in fact * cmake: missing slash in install * bufnmf: added the maxFFTsize parameter in the server call * FluidStats: Change where output Array reshape happens to keep SynthDescLib happy * FluidBufNNDSVD: maxfftsize now needed in server call, or booooom fixes #161 * BufNMFCross: Needs MaxFFTSize * BufNNDSVD: Ensure activations buffer is queried at finish * FluidBufToKr ensure that numFrames is an int (not a float) * Added *(Buf)Feature objects to guide (and deleted old guide) NNDSVD --> NMFSeed in Guide fixed bad links in Guide * change interface and file name (#113) * hidden --> hiddenLayers in class definition (#114) * reordered some max<X> arguments * change interp to interpolation in nmfmorph class (#115) * Feature/skmeans (#66) * add PCA whitening parameter * add FluidSKMeans * SKMeans correction * added RT query * <fit>transform<point> -> <fit>encode<point> * added to overview Co-authored-by: Gerard <gerard@bumblebee.lan> Co-authored-by: tremblap <info@pierrealexandretremblay.com> * [CI] Update Release Workflow (#118) * cleanup nightly.yaml * use new release style * refactor release * remove workflow dispatch variablesnix
parent
229b46b11d
commit
8119849f91
@ -0,0 +1,48 @@
|
|||||||
|
FluidBufNMFSeed : FluidBufProcessor{
|
||||||
|
|
||||||
|
*kr { |source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw};
|
||||||
|
bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
|
||||||
|
activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
|
||||||
|
source = source.asUGenInput;
|
||||||
|
bases = bases.asUGenInput;
|
||||||
|
activations = activations.asUGenInput;
|
||||||
|
|
||||||
|
^FluidProxyUgen.kr1(\FluidBufNMFSeedTrigger, -1, source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize, fftSize, trig, blocking);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
*process { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw};
|
||||||
|
bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
|
||||||
|
activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
|
||||||
|
source = source.asUGenInput;
|
||||||
|
bases = bases.asUGenInput;
|
||||||
|
activations = activations.asUGenInput;
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [bases,activations]
|
||||||
|
).processList(
|
||||||
|
[source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize, fftSize, 0],freeWhenDone, action
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
*processBlocking { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufNMFSeed: Invalid source buffer".throw};
|
||||||
|
bases.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
|
||||||
|
activations.isNil.if {"FluidBufNMFSeed: Invalid bases buffer".throw};
|
||||||
|
source = source.asUGenInput;
|
||||||
|
bases = bases.asUGenInput;
|
||||||
|
activations = activations.asUGenInput;
|
||||||
|
|
||||||
|
^this.new(
|
||||||
|
server, nil, [bases,activations]
|
||||||
|
).processList(
|
||||||
|
[source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize, fftSize, 1],freeWhenDone, action
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FluidBufNMFSeedTrigger : FluidProxyUgen {}
|
||||||
@ -1,48 +0,0 @@
|
|||||||
FluidBufNNDSVD : FluidBufProcessor{
|
|
||||||
|
|
||||||
*kr { |source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, trig = 1, blocking = 0|
|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufNNDSVD: Invalid source buffer".throw};
|
|
||||||
bases.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw};
|
|
||||||
activations.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw};
|
|
||||||
source = source.asUGenInput;
|
|
||||||
bases = bases.asUGenInput;
|
|
||||||
activations = activations.asUGenInput;
|
|
||||||
|
|
||||||
^FluidProxyUgen.kr1(\FluidBufNNDSVDTrigger, -1, source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize, trig, blocking);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
*process { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufNNDSVD: Invalid source buffer".throw};
|
|
||||||
bases.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw};
|
|
||||||
activations.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw};
|
|
||||||
source = source.asUGenInput;
|
|
||||||
bases = bases.asUGenInput;
|
|
||||||
activations = activations.asUGenInput;
|
|
||||||
|
|
||||||
^this.new(
|
|
||||||
server, nil, [bases]
|
|
||||||
).processList(
|
|
||||||
[source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize,0],freeWhenDone, action
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
*processBlocking { |server, source, bases, activations, minComponents = 1, maxComponents = 200, coverage = 0.5, method = 0, windowSize = 1024, hopSize = -1, fftSize = -1, freeWhenDone = true, action|
|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufNNDSVD: Invalid source buffer".throw};
|
|
||||||
bases.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw};
|
|
||||||
activations.isNil.if {"FluidBufNNDSVD: Invalid bases buffer".throw};
|
|
||||||
source = source.asUGenInput;
|
|
||||||
bases = bases.asUGenInput;
|
|
||||||
activations = activations.asUGenInput;
|
|
||||||
|
|
||||||
^this.new(
|
|
||||||
server, nil, [bases]
|
|
||||||
).processList(
|
|
||||||
[source, bases, activations, minComponents, maxComponents, coverage, method, windowSize, hopSize, fftSize,1],freeWhenDone, action
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FluidBufNNDSVDTrigger : FluidProxyUgen {}
|
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
FluidSKMeans : FluidModelObject {
|
||||||
|
|
||||||
|
var clusters, threshold, maxiter;
|
||||||
|
|
||||||
|
*new {|server, numClusters = 4, encodingThreshold = 0.25, maxIter = 100|
|
||||||
|
^super.new(server,[numClusters,maxIter, encodingThreshold])
|
||||||
|
.numClusters_(numClusters)
|
||||||
|
.encodingThreshold_(encodingThreshold)
|
||||||
|
.maxIter_(maxIter);
|
||||||
|
}
|
||||||
|
|
||||||
|
numClusters_{|n| clusters = n.asInteger}
|
||||||
|
numClusters{ ^clusters }
|
||||||
|
|
||||||
|
encodingThreshold_{|t| threshold = t.asFloat}
|
||||||
|
encodingThreshold{ ^threshold }
|
||||||
|
|
||||||
|
maxIter_{|i| maxiter = i.asInteger}
|
||||||
|
maxIter{ ^maxiter }
|
||||||
|
|
||||||
|
|
||||||
|
prGetParams{^[this.id,this.numClusters, this.encodingThreshold, this.maxIter];}
|
||||||
|
|
||||||
|
fitMsg{ |dataSet| ^this.prMakeMsg(\fit,id,dataSet.id);}
|
||||||
|
|
||||||
|
fit{|dataSet, action|
|
||||||
|
actions[\fit] = [
|
||||||
|
numbers( FluidMessageResponse, _, this.numClusters ,_),
|
||||||
|
action
|
||||||
|
];
|
||||||
|
this.prSendMsg(this.fitMsg(dataSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
fitPredictMsg{|dataSet, labelSet|
|
||||||
|
^this.prMakeMsg(\fitPredict, id, dataSet.id, labelSet.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fitPredict{|dataSet, labelSet,action|
|
||||||
|
actions[\fitPredict] = [
|
||||||
|
numbers(FluidMessageResponse, _, this.numClusters, _),
|
||||||
|
action
|
||||||
|
];
|
||||||
|
this.prSendMsg(this.fitPredictMsg(dataSet,labelSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
predictMsg{|dataSet, labelSet|
|
||||||
|
^this.prMakeMsg(\predict, id, dataSet.id, labelSet.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
predict{ |dataSet, labelSet, action|
|
||||||
|
actions[\predict] = [
|
||||||
|
numbers(FluidMessageResponse, _, this.numClusters, _),
|
||||||
|
action
|
||||||
|
];
|
||||||
|
this.prSendMsg(this.predictMsg(dataSet,labelSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
predictPointMsg{|buffer|
|
||||||
|
^this.prMakeMsg(\predictPoint, id, this.prEncodeBuffer(buffer))
|
||||||
|
}
|
||||||
|
|
||||||
|
predictPoint { |buffer, action|
|
||||||
|
actions[\predictPoint] = [number(FluidMessageResponse,_,_),action];
|
||||||
|
this.prSendMsg(this.predictPointMsg(buffer))
|
||||||
|
}
|
||||||
|
|
||||||
|
fitEncodeMsg{|srcDataSet, dstDataSet|
|
||||||
|
^this.prMakeMsg(\fitEncode, id, srcDataSet.id, dstDataSet.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fitEncode{|srcDataSet, dstDataSet,action|
|
||||||
|
actions[\fitEncode] = [nil,action];
|
||||||
|
this.prSendMsg(this.fitEncodeMsg(srcDataSet,dstDataSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
encodeMsg{|srcDataSet, dstDataSet|
|
||||||
|
^this.prMakeMsg(\encode, id, srcDataSet.id, dstDataSet.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
encode{ |srcDataSet, dstDataSet, action|
|
||||||
|
actions[\encode] = [nil,action];
|
||||||
|
this.prSendMsg(this.encodeMsg(srcDataSet,dstDataSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
encodePointMsg{ |sourceBuffer, targetBuffer|
|
||||||
|
^this.prMakeMsg(\encodePoint, id,
|
||||||
|
this.prEncodeBuffer(sourceBuffer),
|
||||||
|
this.prEncodeBuffer(targetBuffer),
|
||||||
|
["/b_query", targetBuffer.asUGenInput]);
|
||||||
|
}
|
||||||
|
|
||||||
|
encodePoint { |sourceBuffer, targetBuffer, action|
|
||||||
|
actions[\encodePoint] = [nil,{action.value(targetBuffer)}];
|
||||||
|
this.prSendMsg(this.encodePointMsg(sourceBuffer, targetBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
getMeansMsg{|dataSet| ^this.prMakeMsg(\getMeans, id, dataSet.asUGenInput) }
|
||||||
|
|
||||||
|
getMeans{ |dataSet, action|
|
||||||
|
actions[\getMeans] = [nil, action];
|
||||||
|
this.prSendMsg(this.getMeansMsg(dataSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
setMeansMsg{|dataSet| ^this.prMakeMsg(\setMeans, id, dataSet.asUGenInput) }
|
||||||
|
|
||||||
|
setMeans{ |dataSet, action|
|
||||||
|
actions[\setMeans] = [nil, action];
|
||||||
|
this.prSendMsg(this.setMeansMsg(dataSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMsg{ ^this.prMakeMsg(\clear, id) }
|
||||||
|
|
||||||
|
clear{ |action|
|
||||||
|
actions[\clear] = [nil, action];
|
||||||
|
this.prSendMsg(this.clearMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
kr{|trig, inputBuffer,outputBuffer|
|
||||||
|
^FluidSKMeansQuery.kr(trig,
|
||||||
|
this,
|
||||||
|
this.prEncodeBuffer(inputBuffer),
|
||||||
|
this.prEncodeBuffer(outputBuffer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FluidSKMeansQuery : FluidRTMultiOutUGen {
|
||||||
|
|
||||||
|
*kr{ |trig, model,inputBuffer, outputBuffer |
|
||||||
|
^this.multiNew('control',trig, model.asUGenInput,inputBuffer.asUGenInput, outputBuffer.asUGenInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
init { arg ... theInputs;
|
||||||
|
inputs = theInputs;
|
||||||
|
^this.initOutputs(1, rate);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,202 @@
|
|||||||
|
// 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:
|
||||||
|
~classify_bases.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:
|
||||||
|
~activations.plot(numChannels:3)
|
||||||
|
|
||||||
|
/// code to execute first
|
||||||
|
(
|
||||||
|
var circle_buf = Buffer.alloc(s,s.sampleRate * 2); // b
|
||||||
|
var input_bus = Bus.audio(s,1); // g
|
||||||
|
var classifying = 0; // c
|
||||||
|
var cur_training_class = 0; // d
|
||||||
|
var train_base = Buffer.alloc(s, 65); // e
|
||||||
|
var activation_vals = [0.0,0.0,0.0]; // j
|
||||||
|
var thresholds = [0.5,0.5,0.5]; // k
|
||||||
|
var activations_disps;
|
||||||
|
var analysis_synth;
|
||||||
|
var osc_func;
|
||||||
|
var update_rout;
|
||||||
|
|
||||||
|
~classify_bases = Buffer.alloc(s, 65, 3); // f
|
||||||
|
~activations = Buffer.new(s);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
var head, head2, duration, audioin, halfdur, trig;
|
||||||
|
duration = BufFrames.kr(bufnum) / 2;
|
||||||
|
halfdur = duration / 2;
|
||||||
|
head = Phasor.ar(0,1,0,duration);
|
||||||
|
head2 = (head + halfdur) % duration;
|
||||||
|
|
||||||
|
// circular buffer writer
|
||||||
|
audioin = In.ar(input,1);
|
||||||
|
BufWr.ar(audioin,bufnum,head,0);
|
||||||
|
BufWr.ar(audioin,bufnum,head+duration,0);
|
||||||
|
trig = FluidAmpSlice.ar(audioin, 10, 1666, 2205, 2205, 12, 9, -47,4410, 85);
|
||||||
|
|
||||||
|
// cue the calculations via the language
|
||||||
|
SendReply.ar(trig, '/attack',head);
|
||||||
|
|
||||||
|
Out.ar(0,audioin);
|
||||||
|
}).add;
|
||||||
|
|
||||||
|
// drum sounds taken from original code by snappizz
|
||||||
|
// https://sccode.org/1-523
|
||||||
|
// produced further and humanised by PA
|
||||||
|
SynthDef(\fluidbd, {
|
||||||
|
|out = 0|
|
||||||
|
var body, bodyFreq, bodyAmp;
|
||||||
|
var pop, popFreq, popAmp;
|
||||||
|
var click, clickAmp;
|
||||||
|
var snd;
|
||||||
|
|
||||||
|
// body starts midrange, quickly drops down to low freqs, and trails off
|
||||||
|
bodyFreq = EnvGen.ar(Env([Rand(200,300), 120, Rand(45,49)], [0.035, Rand(0.07,0.1)], curve: \exp));
|
||||||
|
bodyAmp = EnvGen.ar(Env([0,Rand(0.8,1.3),1,0],[0.005,Rand(0.08,0.085),Rand(0.25,0.35)]), doneAction: 2);
|
||||||
|
body = SinOsc.ar(bodyFreq) * bodyAmp;
|
||||||
|
// pop sweeps over the midrange
|
||||||
|
popFreq = XLine.kr(Rand(700,800), Rand(250,270), Rand(0.018,0.02));
|
||||||
|
popAmp = EnvGen.ar(Env([0,Rand(0.8,1.3),1,0],[0.001,Rand(0.018,0.02),Rand(0.0008,0.0013)]));
|
||||||
|
pop = SinOsc.ar(popFreq) * popAmp;
|
||||||
|
// click is spectrally rich, covering the high-freq range
|
||||||
|
// you can use Formant, FM, noise, whatever
|
||||||
|
clickAmp = EnvGen.ar(Env.perc(0.001,Rand(0.008,0.012),Rand(0.07,0.12),-5));
|
||||||
|
click = RLPF.ar(VarSaw.ar(Rand(900,920),0,0.1), 4760, 0.50150150150) * clickAmp;
|
||||||
|
|
||||||
|
snd = body + pop + click;
|
||||||
|
snd = snd.tanh;
|
||||||
|
|
||||||
|
Out.ar(out, snd);
|
||||||
|
}).add;
|
||||||
|
|
||||||
|
SynthDef(\fluidsn, {
|
||||||
|
|out = 0|
|
||||||
|
var pop, popAmp, popFreq;
|
||||||
|
var noise, noiseAmp;
|
||||||
|
var click;
|
||||||
|
var snd;
|
||||||
|
|
||||||
|
// pop makes a click coming from very high frequencies
|
||||||
|
// slowing down a little and stopping in mid-to-low
|
||||||
|
popFreq = EnvGen.ar(Env([Rand(3210,3310), 410, Rand(150,170)], [0.005, Rand(0.008,0.012)], curve: \exp));
|
||||||
|
popAmp = EnvGen.ar(Env.perc(0.001, Rand(0.1,0.12), Rand(0.7,0.9),-5));
|
||||||
|
pop = SinOsc.ar(popFreq) * popAmp;
|
||||||
|
// bandpass-filtered white noise
|
||||||
|
noiseAmp = EnvGen.ar(Env.perc(0.001, Rand(0.13,0.15), Rand(1.2,1.5),-5), doneAction: 2);
|
||||||
|
noise = BPF.ar(WhiteNoise.ar, 810, 1.6) * noiseAmp;
|
||||||
|
|
||||||
|
click = Impulse.ar(0);
|
||||||
|
snd = (pop + click + noise) * 1.4;
|
||||||
|
|
||||||
|
Out.ar(out, snd);
|
||||||
|
}).add;
|
||||||
|
|
||||||
|
SynthDef(\fluidhh, {
|
||||||
|
|out = 0|
|
||||||
|
var click, clickAmp;
|
||||||
|
var noise, noiseAmp, noiseFreq;
|
||||||
|
|
||||||
|
// noise -> resonance -> expodec envelope
|
||||||
|
noiseAmp = EnvGen.ar(Env.perc(0.001, Rand(0.28,0.3), Rand(0.4,0.6), [-20,-15]), doneAction: 2);
|
||||||
|
noiseFreq = Rand(3900,4100);
|
||||||
|
noise = Mix(BPF.ar(ClipNoise.ar, [noiseFreq, noiseFreq+141], [0.12, 0.31], [2.0, 1.2])) * noiseAmp;
|
||||||
|
|
||||||
|
Out.ar(out, noise);
|
||||||
|
}).add;
|
||||||
|
|
||||||
|
// makes sure all the synthdefs are on the server
|
||||||
|
s.sync;
|
||||||
|
|
||||||
|
// instantiate the JIT-circular-buffer
|
||||||
|
analysis_synth = Synth(\JITcircular,[\bufnum, circle_buf, \input, input_bus]);
|
||||||
|
train_base.fill(0,65,0.1);
|
||||||
|
|
||||||
|
// instantiate the listener to cue the processing from the language side
|
||||||
|
osc_func = OSCFunc({ arg msg;
|
||||||
|
var head_pos = msg[3];
|
||||||
|
// when an attack happens
|
||||||
|
if (classifying == 0, {
|
||||||
|
// if in training mode, makes a single component nmf
|
||||||
|
FluidBufNMF.process(s, circle_buf, head_pos, 128, bases:train_base, 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, circle_buf, head_pos, 128, components:3, bases:~classify_bases, basesMode: 2, activations:~activations, windowSize: 128, action:{
|
||||||
|
// we are retrieving and comparing against the 2nd activation, because FFT processes are zero-padded on each sides, therefore the complete 128 samples are in the middle of the analysis.
|
||||||
|
~activations.getn(3,3,{|x|
|
||||||
|
activation_vals = x;
|
||||||
|
if (activation_vals[0] >= thresholds[0], {Synth(\fluidbd,[\out,1])});
|
||||||
|
if (activation_vals[1] >= thresholds[1], {Synth(\fluidsn,[\out,1])});
|
||||||
|
if (activation_vals[2] >= thresholds[2], {Synth(\fluidhh,[\out,1])});
|
||||||
|
defer{
|
||||||
|
activations_disps[0].string_("A:" ++ activation_vals[0].round(0.01));
|
||||||
|
activations_disps[1].string_("B:" ++ activation_vals[1].round(0.01));
|
||||||
|
activations_disps[2].string_("C:" ++ activation_vals[2].round(0.01));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}, '/attack', s.addr);
|
||||||
|
|
||||||
|
// make sure all the synths are instantiated
|
||||||
|
s.sync;
|
||||||
|
|
||||||
|
// GUI for control
|
||||||
|
{
|
||||||
|
var win = Window("Control", Rect(100,100,610,100)).front;
|
||||||
|
|
||||||
|
Button(win, Rect(10,10,80, 80)).states_([["bd",Color.black,Color.white]]).mouseDownAction_({Synth(\fluidbd, [\out, input_bus], analysis_synth, \addBefore)});
|
||||||
|
Button(win, Rect(100,10,80, 80)).states_([["sn",Color.black,Color.white]]).mouseDownAction_({Synth(\fluidsn, [\out, input_bus], analysis_synth, \addBefore)});
|
||||||
|
Button(win, Rect(190,10,80, 80)).states_([["hh",Color.black,Color.white]]).mouseDownAction_({Synth(\fluidhh, [\out, input_bus], analysis_synth,\addBefore)});
|
||||||
|
StaticText(win, Rect(280,7,85,25)).string_("Select").align_(\center);
|
||||||
|
PopUpMenu(win, Rect(280,32,85,25)).items_(["learn","classify"]).action_({|value|
|
||||||
|
classifying = value.value;
|
||||||
|
if(classifying == 0, {
|
||||||
|
train_base.fill(0,65,0.1)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
PopUpMenu(win, Rect(280,65,85,25)).items_(["classA","classB","classC"]).action_({|value|
|
||||||
|
cur_training_class = value.value;
|
||||||
|
train_base.fill(0,65,0.1);
|
||||||
|
});
|
||||||
|
Button(win, Rect(375,65,85,25)).states_([["transfer",Color.black,Color.white]]).mouseDownAction_({
|
||||||
|
if(classifying == 0, {
|
||||||
|
// if training
|
||||||
|
FluidBufCompose.process(s, train_base, numChans:1, destination:~classify_bases, destStartChan:cur_training_class);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
StaticText(win, Rect(470,7,75,25)).string_("Acts");
|
||||||
|
activations_disps = Array.fill(3, {arg i;
|
||||||
|
StaticText(win, Rect(470,((i+1) * 20 )+ 7,80,25));
|
||||||
|
});
|
||||||
|
StaticText(win, Rect(540,7,55,25)).string_("Thresh").align_(\center);
|
||||||
|
3.do {arg i;
|
||||||
|
TextField(win, Rect(540,((i+1) * 20 )+ 7,55,25)).string_("0.5").action_({|x| thresholds[i] = x.value.asFloat;});
|
||||||
|
};
|
||||||
|
|
||||||
|
win.onClose_({circle_buf.free;input_bus.free;osc_func.clear;analysis_synth.free;});
|
||||||
|
}.defer;
|
||||||
|
}.play;
|
||||||
|
)
|
||||||
|
|
||||||
|
// thanks to Ted Moore for the SC code cleaning and improvements!
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
makeSCWrapper<RTKDTreeQueryClient>("FluidKDTreeQuery",inTable);
|
||||||
|
makeSCWrapper<RTKMeansQueryClient>("FluidKMeansQuery",inTable);
|
||||||
|
makeSCWrapper<RTSKMeansQueryClient>("FluidSKMeansQuery",inTable);
|
||||||
|
makeSCWrapper<RTKNNClassifierQueryClient>("FluidKNNClassifierQuery",inTable);
|
||||||
|
makeSCWrapper<RTKNNRegressorQueryClient>("FluidKNNRegressorQuery",inTable);
|
||||||
|
makeSCWrapper<RTNormalizeQueryClient>("FluidNormalizeQuery",inTable);
|
||||||
|
makeSCWrapper<RTRobustScaleQueryClient>("FluidRobustScaleQuery",inTable);
|
||||||
|
makeSCWrapper<RTStandardizeQueryClient>("FluidStandardizeQuery",inTable);
|
||||||
|
makeSCWrapper<RTPCAQueryClient>("FluidPCAQuery",inTable);
|
||||||
|
makeSCWrapper<RTUMAPQueryClient>("FluidUMAPQuery",inTable);
|
||||||
|
makeSCWrapper<RTMLPRegressorQueryClient>("FluidMLPRegressorQuery",inTable);
|
||||||
|
makeSCWrapper<RTMLPClassifierQueryClient>("FluidMLPClassifierQuery",inTable);
|
||||||
@ -1,67 +0,0 @@
|
|||||||
s.reboot
|
|
||||||
////////////////////////////
|
|
||||||
// test for efficiency
|
|
||||||
|
|
||||||
(
|
|
||||||
b = Buffer.read(s,"../../release-packaging/AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav".resolveRelative);
|
|
||||||
c = Buffer.read(s,"../../release-packaging/AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav".resolveRelative);
|
|
||||||
d = Buffer.new(s);
|
|
||||||
)
|
|
||||||
|
|
||||||
(
|
|
||||||
// with basic params (basic summing of each full buffer in all dimensions)
|
|
||||||
Routine{
|
|
||||||
t = Main.elapsedTime;
|
|
||||||
FluidBufCompose.process(s, srcBufNumA: b.bufnum, srcBufNumB: c.bufnum, dstBufNum: d.bufnum);
|
|
||||||
s.sync;
|
|
||||||
(Main.elapsedTime - t).postln;
|
|
||||||
}.play;
|
|
||||||
)
|
|
||||||
|
|
||||||
d.query;
|
|
||||||
d.play;
|
|
||||||
d.plot
|
|
||||||
|
|
||||||
//constructing a mono buffer, with a quiet punch from the synth, with a choked piano resonance from the left channel
|
|
||||||
(
|
|
||||||
Routine{
|
|
||||||
t = Main.elapsedTime;
|
|
||||||
FluidBufCompose.process(s, srcBufNumA: b.bufnum, nFramesA: 9000, srcGainA: 0.5, srcBufNumB: c.bufnum, startAtB:30000, nFramesB:44100, nChansB:1, srcGainB:0.9, dstBufNum: d.bufnum);
|
|
||||||
s.sync;
|
|
||||||
(Main.elapsedTime - t).postln;
|
|
||||||
}.play;
|
|
||||||
)
|
|
||||||
|
|
||||||
//constructing a stereo buffer, with the end of the mono synth in both channels, with a piano resonance in swapped stereo
|
|
||||||
(
|
|
||||||
Routine{
|
|
||||||
t = Main.elapsedTime;
|
|
||||||
FluidBufCompose.process(s, srcBufNumA: b.bufnum, startAtA: 441000, nChansA: 2, srcGainA: 0.6, srcBufNumB: c.bufnum, nFramesB: 80000, startChanB: 1, nChansB: 2, srcGainB: 0.5, dstStartAtB: 22050, dstStartChanB: 0, dstBufNum: d.bufnum);
|
|
||||||
s.sync;
|
|
||||||
(Main.elapsedTime - t).postln;
|
|
||||||
}.play;
|
|
||||||
)
|
|
||||||
|
|
||||||
//constructing a one second buffer: the first second of each buffer, the mono synth on the right, the piano on the left
|
|
||||||
(
|
|
||||||
Routine{
|
|
||||||
t = Main.elapsedTime;
|
|
||||||
FluidBufCompose.process(s, srcBufNumA: b.bufnum, nFramesA: 44100, nChansA: 1, dstStartChanA: 1, srcBufNumB: c.bufnum, nFramesB:44100, nChansB:1, dstBufNum: d.bufnum);
|
|
||||||
s.sync;
|
|
||||||
(Main.elapsedTime - t).postln;
|
|
||||||
}.play;
|
|
||||||
)
|
|
||||||
|
|
||||||
// trying to grow a buffer on itself
|
|
||||||
e = Buffer.alloc(s,1,1);
|
|
||||||
(
|
|
||||||
Routine {
|
|
||||||
FluidBufCompose.process(s,srcBufNumA: b.bufnum, srcBufNumB: e.bufnum, dstBufNum: e.bufnum);
|
|
||||||
s.sync;
|
|
||||||
FluidBufCompose.process(s,srcBufNumA: c.bufnum, nChansA: 1, srcBufNumB: e.bufnum, dstBufNum: e.bufnum);
|
|
||||||
s.sync;
|
|
||||||
}.play;
|
|
||||||
)
|
|
||||||
|
|
||||||
e.plot
|
|
||||||
e.play
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
s.reboot
|
|
||||||
////////////////////////////
|
|
||||||
// test for efficiency
|
|
||||||
|
|
||||||
(
|
|
||||||
b = Buffer.read(s,"../../release-packaging/AudioFiles/Tremblay-AaS-SynthTwoVoices-M.wav".resolveRelative);
|
|
||||||
c = Buffer.new(s);
|
|
||||||
d = Buffer.new(s);
|
|
||||||
e = Buffer.new(s);
|
|
||||||
)
|
|
||||||
|
|
||||||
(
|
|
||||||
// with basic params
|
|
||||||
Routine{
|
|
||||||
t = Main.elapsedTime;
|
|
||||||
FluidBufHPSS.process(s, b.bufnum, harmBufNum: c.bufnum, percBufNum: d.bufnum);
|
|
||||||
s.sync;
|
|
||||||
(Main.elapsedTime - t).postln;
|
|
||||||
}.play
|
|
||||||
);
|
|
||||||
|
|
||||||
c.query;
|
|
||||||
c.play;
|
|
||||||
d.query;
|
|
||||||
d.play;
|
|
||||||
e.query;
|
|
||||||
e.play;
|
|
||||||
|
|
||||||
//nullsumming tests
|
|
||||||
{(PlayBuf.ar(1,c.bufnum))+(PlayBuf.ar(1,d.bufnum))+(-1*PlayBuf.ar(1,b.bufnum,doneAction:2))}.play
|
|
||||||
|
|
||||||
|
|
||||||
// with everything changed to make it much faster
|
|
||||||
(
|
|
||||||
Routine{
|
|
||||||
t = Main.elapsedTime;
|
|
||||||
FluidBufHPSS.process(s,b.bufnum, 44100, 44100, 0, 0, c.bufnum, d.bufnum, e.bufnum, 51, 31, 2); // need to change these for something sensible
|
|
||||||
s.sync;
|
|
||||||
(Main.elapsedTime - t).postln;
|
|
||||||
}.play
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
// owen's sexy example (The world's most expensive stereoizer)
|
|
||||||
|
|
||||||
(
|
|
||||||
{
|
|
||||||
var hpss = FluidHPSS.ar(PlayBuf.ar(1,b.bufnum,loop:1),modeFlag:2,hta1:SinOsc.kr(1.5,mul:20,add:20),hta2:SinOsc.kr(3,mul:25,add:25), pta1:SinOsc.kr(1.6,0,mul:30,add:30),pta2:SinOsc.kr(1.7,0,mul:17,add:24));
|
|
||||||
[hpss[2] + 0.5 * hpss[0], hpss[1] + 0.5 * hpss[0]];
|
|
||||||
}.play;
|
|
||||||
)
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue