Merge branch 'master' into clients/inter_client_comms
commit
c84a81a3bf
@ -0,0 +1,29 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2017-2019 University of Huddersfield
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
FluidAmpGate : UGen {
|
||||||
|
*ar { arg in = 0, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, maxSize = 88200;
|
||||||
|
^this.multiNew('audio', in.asAudioRateInput(this), rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize)
|
||||||
|
}
|
||||||
|
checkInputs {
|
||||||
|
if(inputs.at(12).rate != 'scalar') {
|
||||||
|
^(": maxSize cannot be modulated.");
|
||||||
|
};
|
||||||
|
^this.checkValidInputs;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,9 @@
|
|||||||
FluidAmpSlice : UGen {
|
FluidAmpSlice : UGen {
|
||||||
*ar { arg in = 0, absRampUp = 10, absRampDown = 10, absThreshOn = -90, absThreshOff = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, relRampUp = 1, relRampDown = 1, relThreshOn = 144, relThreshOff = -144, highPassFreq = 85, maxSize = 88200;
|
*ar { arg in = 0, fastRampUp = 1, fastRampDown = 1, slowRampUp = 100, slowRampDown = 100, onThreshold = -144, offThreshold = -144, floor = -144, minSliceLength = 2, highPassFreq = 85;
|
||||||
^this.multiNew('audio', in.asAudioRateInput(this), absRampUp, absRampDown, absThreshOn, absThreshOff, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, relRampUp, relRampDown, relThreshOn, relThreshOff, highPassFreq, maxSize, 0)
|
|
||||||
|
^this.multiNew('audio', in.asAudioRateInput(this), fastRampUp, fastRampDown, slowRampUp, slowRampDown, onThreshold, offThreshold, floor, minSliceLength, highPassFreq)
|
||||||
}
|
}
|
||||||
checkInputs {
|
checkInputs {
|
||||||
if(inputs.at(16).rate != 'scalar') {
|
|
||||||
^(": maxSize cannot be modulated.");
|
|
||||||
};
|
|
||||||
^this.checkValidInputs;
|
^this.checkValidInputs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,37 @@
|
|||||||
|
FluidBufAmpGate : UGen {
|
||||||
|
|
||||||
|
*new1 { |rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, doneAction = 0, blocking|
|
||||||
|
var maxSize = max(minLengthAbove + lookBack, max(minLengthBelow,lookAhead));
|
||||||
|
|
||||||
|
source = source.asUGenInput;
|
||||||
|
indices = indices.asUGenInput;
|
||||||
|
|
||||||
|
source.isNil.if {"FluidBufAmpSlice: Invalid source buffer".throw};
|
||||||
|
indices.isNil.if {"FluidBufAmpSlice: Invalid features buffer".throw};
|
||||||
|
|
||||||
|
^super.new1(rate, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, maxSize, doneAction, blocking);
|
||||||
|
}
|
||||||
|
|
||||||
|
*kr { |source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, doneAction = 0|
|
||||||
|
|
||||||
|
^this.multiNew(\control, source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq, doneAction,blocking:0);
|
||||||
|
}
|
||||||
|
|
||||||
|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, action |
|
||||||
|
|
||||||
|
^FluidNRTProcess.new(
|
||||||
|
server, this, action, [indices]
|
||||||
|
).process(
|
||||||
|
source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, indices, rampUp = 10, rampDown = 10, onThreshold = -90, offThreshold = -90, minSliceLength = 1, minSilenceLength = 1, minLengthAbove = 1, minLengthBelow = 1, lookBack = 0, lookAhead = 0, highPassFreq = 85, action|
|
||||||
|
|
||||||
|
^FluidNRTProcess.new(
|
||||||
|
server, this, action, [indices], blocking: 1
|
||||||
|
).process(
|
||||||
|
source, startFrame, numFrames, startChan, numChans, indices, rampUp, rampDown, onThreshold, offThreshold, minSliceLength, minSilenceLength, minLengthAbove, minLengthBelow, lookBack, lookAhead, highPassFreq
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,47 +1,47 @@
|
|||||||
FluidBufNMF : UGen {
|
FluidBufNMF : UGen {
|
||||||
|
|
||||||
*new1 {|rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, doneAction = 0, blocking = 0|
|
*new1 {|rate, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, doneAction = 0, blocking = 0|
|
||||||
|
|
||||||
source = source.asUGenInput;
|
source = source.asUGenInput;
|
||||||
destination = destination.asUGenInput;
|
resynth = resynth.asUGenInput;
|
||||||
bases = bases.asUGenInput;
|
bases = bases.asUGenInput;
|
||||||
activations = activations.asUGenInput;
|
activations = activations.asUGenInput;
|
||||||
|
|
||||||
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
|
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
|
||||||
|
|
||||||
destination = destination ? -1;
|
resynth = resynth ? -1;
|
||||||
bases = bases ? -1;
|
bases = bases ? -1;
|
||||||
activations = activations ? -1;
|
activations = activations ? -1;
|
||||||
|
|
||||||
^super.new1(rate,source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, doneAction, blocking);
|
^super.new1(rate,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, doneAction, blocking);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, doneAction = 0|
|
*kr {|source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth, bases, basesMode = 0, activations, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, doneAction = 0|
|
||||||
^this.multiNew(\control,source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, doneAction);
|
^this.multiNew(\control,source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components, iterations, windowSize, hopSize, fftSize, doneAction);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action|
|
*process { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
|
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
|
||||||
|
|
||||||
^FluidNRTProcess.new(
|
^FluidNRTProcess.new(
|
||||||
server, this, action, [destination, bases, activations].select{|x| x!= -1}
|
server, this, action, [resynth, bases, activations].select{|x| x!= -1}
|
||||||
).process(
|
).process(
|
||||||
source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed
|
source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, destination = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action|
|
*processBlocking { |server, source, startFrame = 0, numFrames = -1, startChan = 0, numChans = -1, resynth = -1, bases = -1, basesMode = 0, activations = -1, actMode = 0, components = 1, iterations = 100, windowSize = 1024, hopSize = -1, fftSize = -1, windowType = 0, randomSeed = -1, action|
|
||||||
|
|
||||||
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
|
source.isNil.if {"FluidBufNMF: Invalid source buffer".throw};
|
||||||
|
|
||||||
^FluidNRTProcess.new(
|
^FluidNRTProcess.new(
|
||||||
server, this, action, [destination, bases, activations].select{|x| x!= -1},blocking: 1
|
server, this, action, [resynth, bases, activations].select{|x| x!= -1},blocking: 1
|
||||||
).process(
|
).process(
|
||||||
source, startFrame, numFrames, startChan, numChans, destination, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed
|
source, startFrame, numFrames, startChan, numChans, resynth, bases, basesMode, activations, actMode, components,iterations, windowSize, hopSize, fftSize, windowType, randomSeed
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,100 @@
|
|||||||
|
(
|
||||||
|
var win, soundFileView, freqSscope,loadButton, loopButton;
|
||||||
|
var harmSlider, percSlider, mixSlider;
|
||||||
|
var soundFile, buffer;
|
||||||
|
var synthDef, synth;
|
||||||
|
var makeSynthDef;
|
||||||
|
|
||||||
|
Font.default = Font("Monaco", 16);
|
||||||
|
buffer = Buffer.new;
|
||||||
|
win = Window.new("HPSS", Rect(200,200,800,450)).background_(Color.gray);
|
||||||
|
|
||||||
|
soundFileView = SoundFileView.new(win)
|
||||||
|
.gridOn_(false)
|
||||||
|
.waveColors_([Color.white]);
|
||||||
|
|
||||||
|
loadButton = Button(win, Rect(0, 0, 100, 100))
|
||||||
|
.minHeight_(150)
|
||||||
|
.states_([["Load", Color.grey, Color.grey(0.8)]]);
|
||||||
|
|
||||||
|
loopButton = Button(win, Rect(0, 0, 100, 100))
|
||||||
|
.minHeight_(150)
|
||||||
|
.states_(
|
||||||
|
[["Play", Color.grey, Color.grey(0.8)],
|
||||||
|
["Stop", Color.grey, Color.grey(0.2)]]
|
||||||
|
);
|
||||||
|
|
||||||
|
harmSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
percSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
mixSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
freqSscope = FreqScopeView(win, server:Server.default);
|
||||||
|
freqSscope.active_(true);
|
||||||
|
|
||||||
|
loadButton.action_{
|
||||||
|
FileDialog({ |path|
|
||||||
|
soundFile = SoundFile.new;
|
||||||
|
soundFile.openRead(path[0]);
|
||||||
|
buffer = Buffer.read(Server.default, path[0]);
|
||||||
|
soundFileView.soundfile = soundFile;
|
||||||
|
soundFileView.read(0, soundFile.numFrames);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
loopButton.action_{|but|
|
||||||
|
if(but.value == 1, {
|
||||||
|
synth = Synth(\hpssExtractionDemo, [\buffer, buffer.bufnum]);
|
||||||
|
mixSlider.action.value(mixSlider);
|
||||||
|
},{
|
||||||
|
synth.free;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
mixSlider.action_{|slider|
|
||||||
|
synth.set(\bal, ControlSpec(0, 1).map(slider.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
makeSynthDef = {
|
||||||
|
|
||||||
|
synthDef = SynthDef(\hpssExtractionDemo,
|
||||||
|
{|buffer, bal = 0.5|
|
||||||
|
var player, fhpss, mix;
|
||||||
|
var harmSize = (2 * ControlSpec(1, 100, step:1).map(harmSlider.value)) - 1;
|
||||||
|
var percSize = (2 * ControlSpec(1,100, step:1).map(percSlider.value)) - 1;
|
||||||
|
player = PlayBuf.ar(1, buffer, loop:1);
|
||||||
|
fhpss = FluidHPSS.ar(in: player, harmFilterSize: harmSize, percFilterSize: percSize, maskingMode: 1, harmThreshFreq1: 0.1, harmThreshAmp1: 0, harmThreshFreq2: 0.5, harmThreshAmp2: 0, percThreshFreq1: 0.1, percThreshAmp1: 0, percThreshFreq2: 0.5, percThreshAmp2: 0, windowSize: 1024, hopSize: 256, fftSize: -1);
|
||||||
|
|
||||||
|
mix =(bal * fhpss[0]) + ((1 - bal) * fhpss[1]);
|
||||||
|
Out.ar(0,Pan2.ar(mix));
|
||||||
|
}
|
||||||
|
).add;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
win.layout_(
|
||||||
|
VLayout(
|
||||||
|
[
|
||||||
|
HLayout(
|
||||||
|
[loadButton, stretch:1],
|
||||||
|
[soundFileView, stretch:5]
|
||||||
|
), stretch:2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
HLayout(
|
||||||
|
[loopButton, stretch:1],
|
||||||
|
[VLayout(
|
||||||
|
HLayout(StaticText(win).string_("H Size ").minWidth_(100), harmSlider),
|
||||||
|
HLayout(StaticText(win).string_("P Size").minWidth_(100), percSlider),
|
||||||
|
HLayout(StaticText(win).string_("Mix").minWidth_(100), mixSlider)
|
||||||
|
), stretch:5]
|
||||||
|
), stretch:2
|
||||||
|
],
|
||||||
|
[freqSscope, stretch:2]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
makeSynthDef.value;
|
||||||
|
win.front;
|
||||||
|
)
|
||||||
@ -0,0 +1,111 @@
|
|||||||
|
(
|
||||||
|
var server;
|
||||||
|
var win, soundFileView, loadButton, loopButton;
|
||||||
|
var sliders;
|
||||||
|
var soundFile, audioBuffer, destBuffer;
|
||||||
|
var synthDef, synth;
|
||||||
|
var sl1, sl2, sl3, sl4;
|
||||||
|
|
||||||
|
server = Server.default;
|
||||||
|
Font.default = Font("Monaco", 16);
|
||||||
|
|
||||||
|
audioBuffer = Buffer.new;
|
||||||
|
destBuffer = Buffer.new;
|
||||||
|
|
||||||
|
|
||||||
|
synthDef = SynthDef(\nmfDemo,{|bufnum, a1, a2, a3, a4|
|
||||||
|
var p = PlayBuf.ar(4, bufnum, loop:1);
|
||||||
|
var mix = (a1*p[0]) + (a2 * p[1]) + (a3*p[2]) + (a4*p[3]);
|
||||||
|
Out.ar(0, Pan2.ar(mix));
|
||||||
|
}).add;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
win = Window.new("NMF4",
|
||||||
|
Rect(200,200,800,450)).background_(Color.gray);
|
||||||
|
|
||||||
|
soundFileView = SoundFileView.new(win)
|
||||||
|
.gridOn_(false)
|
||||||
|
.waveColors_([Color.white]);
|
||||||
|
|
||||||
|
loadButton = Button(win, Rect(0, 0, 100, 100))
|
||||||
|
.minHeight_(150)
|
||||||
|
.states_([["Load", Color.grey, Color.grey(0.8)],
|
||||||
|
["Wait", Color.grey, Color.grey(0.2)]]
|
||||||
|
);
|
||||||
|
|
||||||
|
loopButton = Button(win, Rect(0, 0, 100, 100))
|
||||||
|
.minHeight_(150)
|
||||||
|
.states_(
|
||||||
|
[["Play", Color.grey, Color.grey(0.8)],
|
||||||
|
["Stop", Color.grey, Color.grey(0.2)]]
|
||||||
|
);
|
||||||
|
|
||||||
|
sliders = Array.fill(4, {|i|
|
||||||
|
var s = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
s.action_{
|
||||||
|
var sym = ("a"++(i+1)).asSymbol;
|
||||||
|
synth.set(sym, ControlSpec(0, 1).map(s.value));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
loadButton.action_{
|
||||||
|
FileDialog({ |path|
|
||||||
|
soundFile = SoundFile.new;
|
||||||
|
soundFile.openRead(path[0]);
|
||||||
|
soundFileView.soundfile = soundFile;
|
||||||
|
soundFileView.read(0, soundFile.numFrames);
|
||||||
|
Routine{
|
||||||
|
audioBuffer = Buffer.read(server, path[0]);
|
||||||
|
server.sync;
|
||||||
|
FluidBufNMF.process(server,
|
||||||
|
audioBuffer.bufnum,resynth:destBuffer.bufnum, components:4
|
||||||
|
);
|
||||||
|
server.sync;
|
||||||
|
destBuffer.query;
|
||||||
|
server.sync;
|
||||||
|
{loadButton.value_(0)}.defer;
|
||||||
|
}.play;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
loopButton.action_{|but|
|
||||||
|
var a1 = ControlSpec(0, 1).map(sliders[0].value);
|
||||||
|
var a2 = ControlSpec(0, 1).map(sliders[1].value);
|
||||||
|
var a3 = ControlSpec(0, 1).map(sliders[2].value);
|
||||||
|
var a4 = ControlSpec(0, 1).map(sliders[3].value);
|
||||||
|
|
||||||
|
if(but.value == 1, {
|
||||||
|
synth = Synth(\nmfDemo,
|
||||||
|
[\bufnum, destBuffer.bufnum, \a1, a1, \a2, a2, \a3, a3, \a4, a4]);
|
||||||
|
},{
|
||||||
|
synth.free;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
win.layout_(
|
||||||
|
VLayout(
|
||||||
|
[
|
||||||
|
HLayout(
|
||||||
|
[loadButton, stretch:1],
|
||||||
|
[soundFileView, stretch:5]
|
||||||
|
), stretch:2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
HLayout(
|
||||||
|
[loopButton, stretch:1],
|
||||||
|
[VLayout(
|
||||||
|
HLayout(StaticText(win).string_("source 1 ").minWidth_(100), sliders[0]),
|
||||||
|
HLayout(StaticText(win).string_("source 2 ").minWidth_(100), sliders[1]),
|
||||||
|
HLayout(StaticText(win).string_("source 3 ").minWidth_(100), sliders[2]),
|
||||||
|
HLayout(StaticText(win).string_("source 4 ").minWidth_(100), sliders[3])
|
||||||
|
), stretch:5]
|
||||||
|
), stretch:2
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
win.front;
|
||||||
|
)
|
||||||
@ -0,0 +1,150 @@
|
|||||||
|
(
|
||||||
|
var server;
|
||||||
|
var win, soundFileView,loadButton, processButton;
|
||||||
|
var ksSlider, thSlider;
|
||||||
|
var soundFile, audioBuffer, slicesBuffer, slicesArray;
|
||||||
|
var addSelections, playFunc, stopFunc;
|
||||||
|
var synthDef, synth;
|
||||||
|
var synths;
|
||||||
|
|
||||||
|
var playing, currentSelection, colors, prevColor;
|
||||||
|
var qwerty = "1234567890qwertyuiopasdfghjklzxcvbnm";
|
||||||
|
playing = Array.fill(qwerty.size, {false});
|
||||||
|
server = Server.default;
|
||||||
|
Font.default = Font("Monaco", 16);
|
||||||
|
|
||||||
|
audioBuffer = Buffer.new;
|
||||||
|
slicesBuffer = Buffer.new;
|
||||||
|
|
||||||
|
colors = Array.fill(qwerty.size, {Color.rand});
|
||||||
|
synths = Array.fill(qwerty.size, {nil});
|
||||||
|
|
||||||
|
synthDef = SynthDef(\noveltySegDemo,{|buf, start, end|
|
||||||
|
Out.ar(0, BufRd.ar(1, buf, Phasor.ar(1, 1, start, end)));
|
||||||
|
}).add;
|
||||||
|
|
||||||
|
playFunc = {|index|
|
||||||
|
var dur;
|
||||||
|
currentSelection = index;
|
||||||
|
if(playing[index].not){
|
||||||
|
synths[index] = Synth(\noveltySegDemo,
|
||||||
|
[\buf, audioBuffer.bufnum,
|
||||||
|
\start, slicesArray[index],
|
||||||
|
\end, slicesArray[index+1]
|
||||||
|
]);
|
||||||
|
playing[index] = true;
|
||||||
|
};
|
||||||
|
soundFileView.setSelectionColor(currentSelection, Color.white);
|
||||||
|
};
|
||||||
|
|
||||||
|
stopFunc = {|index| synths[index].free; playing[index] = false;
|
||||||
|
soundFileView.setSelectionColor(
|
||||||
|
index, colors[index]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
win = Window.new("NoveltySegmentation",
|
||||||
|
Rect(200,200,800,450)).background_(Color.gray);
|
||||||
|
|
||||||
|
win.view.keyDownAction_{|view, char, modifiers, unicode, keycode, key|
|
||||||
|
var num = qwerty.indexOf(char);
|
||||||
|
if (num.notNil&& slicesArray.notNil){
|
||||||
|
playFunc.value(num);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
win.view.keyUpAction_{|view, char|
|
||||||
|
var num = qwerty.indexOf(char);
|
||||||
|
if(num.notNil){
|
||||||
|
stopFunc.value(num);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
soundFileView = SoundFileView.new(win)
|
||||||
|
.gridOn_(false)
|
||||||
|
.waveColors_([Color.white]);
|
||||||
|
|
||||||
|
loadButton = Button(win, Rect(0, 0, 100, 100))
|
||||||
|
.minHeight_(150)
|
||||||
|
.states_([["Load", Color.grey, Color.grey(0.8)]]);
|
||||||
|
|
||||||
|
processButton = Button(win, Rect(0, 0, 100, 100))
|
||||||
|
.minHeight_(150)
|
||||||
|
.states_(
|
||||||
|
[["Process", Color.grey, Color.grey(0.8)],
|
||||||
|
["Wait", Color.grey, Color.grey(0.2)]]
|
||||||
|
);
|
||||||
|
|
||||||
|
ksSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
thSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
|
||||||
|
|
||||||
|
loadButton.action_{
|
||||||
|
FileDialog({ |path|
|
||||||
|
soundFile = SoundFile.new;
|
||||||
|
soundFile.openRead(path[0]);
|
||||||
|
audioBuffer = Buffer.read(server, path[0]);
|
||||||
|
soundFileView.soundfile = soundFile;
|
||||||
|
soundFileView.read(0, soundFile.numFrames);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
processButton.action_{|but|
|
||||||
|
var ks = 2*(ControlSpec(2, 100, step:1).map(ksSlider.value)) - 1;
|
||||||
|
var th = ControlSpec(0, 1).map(thSlider.value);
|
||||||
|
if(but.value == 1, {
|
||||||
|
Routine{
|
||||||
|
FluidBufNoveltySlice.process(
|
||||||
|
server,
|
||||||
|
source:audioBuffer.bufnum,
|
||||||
|
indices:slicesBuffer.bufnum,
|
||||||
|
kernelSize:ks,
|
||||||
|
threshold: th
|
||||||
|
);
|
||||||
|
server.sync;
|
||||||
|
slicesBuffer.loadToFloatArray(action:{|arr|
|
||||||
|
slicesArray = arr;
|
||||||
|
{ processButton.value_(0);
|
||||||
|
addSelections.value(slicesArray)
|
||||||
|
}.defer;
|
||||||
|
|
||||||
|
});
|
||||||
|
}.play;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
addSelections = {|array|
|
||||||
|
var nSegments = min(array.size, soundFileView.selections.size) - 1;
|
||||||
|
soundFileView.selections.do({|sel, i| soundFileView.selectNone(i)});
|
||||||
|
nSegments.do({|i|
|
||||||
|
soundFileView.setSelectionStart(i, array[i]);
|
||||||
|
soundFileView.setSelectionSize(i, array[i+1] - array[i]);
|
||||||
|
soundFileView.setSelectionColor(i, colors[i]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
win.layout_(
|
||||||
|
VLayout(
|
||||||
|
[
|
||||||
|
HLayout(
|
||||||
|
[loadButton, stretch:1],
|
||||||
|
[soundFileView, stretch:5]
|
||||||
|
), stretch:2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
HLayout(
|
||||||
|
[processButton, stretch:1],
|
||||||
|
[VLayout(
|
||||||
|
HLayout(StaticText(win).string_("Kernel ").minWidth_(100), ksSlider),
|
||||||
|
HLayout(StaticText(win).string_(" Threshold").minWidth_(100), thSlider)
|
||||||
|
), stretch:5]
|
||||||
|
), stretch:2
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
win.front;
|
||||||
|
)
|
||||||
@ -0,0 +1,107 @@
|
|||||||
|
(
|
||||||
|
var win, soundFileView, freqSscope,loadButton, loopButton;
|
||||||
|
var thresholdSlider, lenSlider, mixSlider;
|
||||||
|
var soundFile, buffer;
|
||||||
|
var synthDef, synth;
|
||||||
|
|
||||||
|
Font.default = Font("Monaco", 16);
|
||||||
|
buffer = Buffer.new;
|
||||||
|
win = Window.new("SineExtraction",
|
||||||
|
Rect(200,200,800,450)).background_(Color.gray);
|
||||||
|
|
||||||
|
soundFileView = SoundFileView.new(win)
|
||||||
|
.gridOn_(false)
|
||||||
|
.waveColors_([Color.white]);
|
||||||
|
|
||||||
|
loadButton = Button(win, Rect(0, 0, 100, 100))
|
||||||
|
.minHeight_(150)
|
||||||
|
.states_([["Load", Color.grey, Color.grey(0.8)]]);
|
||||||
|
|
||||||
|
loopButton = Button(win, Rect(0, 0, 100, 100))
|
||||||
|
.minHeight_(150)
|
||||||
|
.states_(
|
||||||
|
[["Play", Color.grey, Color.grey(0.8)],
|
||||||
|
["Stop", Color.grey, Color.grey(0.2)]]
|
||||||
|
);
|
||||||
|
|
||||||
|
thresholdSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
lenSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
mixSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
freqSscope = FreqScopeView(win, server:Server.default);
|
||||||
|
freqSscope.active_(true);
|
||||||
|
|
||||||
|
loadButton.action_{
|
||||||
|
FileDialog({ |path|
|
||||||
|
soundFile = SoundFile.new;
|
||||||
|
soundFile.openRead(path[0]);
|
||||||
|
buffer = Buffer.read(Server.default, path[0]);
|
||||||
|
soundFileView.soundfile = soundFile;
|
||||||
|
soundFileView.read(0, soundFile.numFrames);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
loopButton.action_{|but|
|
||||||
|
if(but.value == 1, {
|
||||||
|
synth = Synth(\sineExtractionDemo, [\buffer, buffer.bufnum]);
|
||||||
|
mixSlider.action.value(mixSlider);
|
||||||
|
thresholdSlider.action.value(thresholdSlider);
|
||||||
|
lenSlider.action.value(lenSlider);
|
||||||
|
},{
|
||||||
|
synth.free;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
mixSlider.action_{|slider|
|
||||||
|
synth.set(\bal, ControlSpec(0, 1).map(slider.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
thresholdSlider.action_{|slider|
|
||||||
|
synth.set(\threshold, ControlSpec(-144, 0).map(slider.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
lenSlider.action_{|slider|
|
||||||
|
synth.set(\minLength, ControlSpec(0, 30).map(slider.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
synthDef = SynthDef(\sineExtractionDemo,
|
||||||
|
{|buffer, threshold = 0.9, minLength = 15, bal = 0.5|
|
||||||
|
var player, fse, mix;
|
||||||
|
player = PlayBuf.ar(1, buffer, loop:1);
|
||||||
|
fse = FluidSines.ar(in: player, bandwidth: 76,
|
||||||
|
detectionThreshold: threshold, minTrackLen: minLength,
|
||||||
|
windowSize: 2048,
|
||||||
|
hopSize: 512, fftSize: 8192
|
||||||
|
);
|
||||||
|
mix =(bal * fse[0]) + ((1 - bal) * fse[1]);
|
||||||
|
Out.ar(0,Pan2.ar(mix));
|
||||||
|
}
|
||||||
|
).add;
|
||||||
|
|
||||||
|
win.layout_(
|
||||||
|
VLayout(
|
||||||
|
[
|
||||||
|
HLayout(
|
||||||
|
[loadButton, stretch:1],
|
||||||
|
[soundFileView, stretch:5]
|
||||||
|
), stretch:2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
HLayout(
|
||||||
|
[loopButton, stretch:1],
|
||||||
|
[VLayout(
|
||||||
|
HLayout(StaticText(win).string_("Threshold ").minWidth_(100), thresholdSlider),
|
||||||
|
HLayout(StaticText(win).string_("Min Length").minWidth_(100), lenSlider),
|
||||||
|
HLayout(StaticText(win).string_("Mix").minWidth_(100), mixSlider)
|
||||||
|
), stretch:5]
|
||||||
|
), stretch:2
|
||||||
|
],
|
||||||
|
[freqSscope, stretch:2]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
win.front;
|
||||||
|
)
|
||||||
@ -0,0 +1,103 @@
|
|||||||
|
(
|
||||||
|
var win, soundFileView, freqSscope,loadButton, loopButton;
|
||||||
|
var fwSlider, bwSlider, mixSlider;
|
||||||
|
var soundFile, buffer;
|
||||||
|
var synthDef, synth;
|
||||||
|
|
||||||
|
Font.default = Font("Monaco", 16);
|
||||||
|
buffer = Buffer.new;
|
||||||
|
win = Window.new("TransientExtraction",
|
||||||
|
Rect(200,200,800,450)).background_(Color.gray);
|
||||||
|
|
||||||
|
soundFileView = SoundFileView.new(win)
|
||||||
|
.gridOn_(false)
|
||||||
|
.waveColors_([Color.white]);
|
||||||
|
|
||||||
|
loadButton = Button(win, Rect(0, 0, 100, 100))
|
||||||
|
.minHeight_(150)
|
||||||
|
.states_([["Load", Color.grey, Color.grey(0.8)]]);
|
||||||
|
|
||||||
|
loopButton = Button(win, Rect(0, 0, 100, 100))
|
||||||
|
.minHeight_(150)
|
||||||
|
.states_(
|
||||||
|
[["Play", Color.grey, Color.grey(0.8)],
|
||||||
|
["Stop", Color.grey, Color.grey(0.2)]]
|
||||||
|
);
|
||||||
|
|
||||||
|
fwSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
bwSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
mixSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
freqSscope = FreqScopeView(win, server:Server.default);
|
||||||
|
freqSscope.active_(true);
|
||||||
|
|
||||||
|
loadButton.action_{
|
||||||
|
FileDialog({ |path|
|
||||||
|
soundFile = SoundFile.new;
|
||||||
|
soundFile.openRead(path[0]);
|
||||||
|
buffer = Buffer.read(Server.default, path[0]);
|
||||||
|
soundFileView.soundfile = soundFile;
|
||||||
|
soundFileView.read(0, soundFile.numFrames);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
loopButton.action_{|but|
|
||||||
|
if(but.value == 1, {
|
||||||
|
synth = Synth(\transientExtractionDemo, [\buffer, buffer.bufnum]);
|
||||||
|
mixSlider.action.value(mixSlider);
|
||||||
|
fwSlider.action.value(fwSlider);
|
||||||
|
bwSlider.action.value(bwSlider);
|
||||||
|
},{
|
||||||
|
synth.free;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
mixSlider.action_{|slider|
|
||||||
|
synth.set(\bal, ControlSpec(0, 1).map(slider.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
fwSlider.action_{|slider|
|
||||||
|
synth.set(\fw, ControlSpec(0.0001, 3, \exp).map(slider.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
bwSlider.action_{|slider|
|
||||||
|
synth.set(\bw, ControlSpec(0.0001, 3, \exp).map(slider.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
synthDef = SynthDef(\transientExtractionDemo,
|
||||||
|
{|buffer, fw = 3, bw = 1, bal = 0.5|
|
||||||
|
var player, fte, mix;
|
||||||
|
player = PlayBuf.ar(1, buffer, loop:1);
|
||||||
|
fte = FluidTransients.ar(in: player, threshFwd:fw, threshBack:bw, clumpLength:256);
|
||||||
|
mix =(bal * fte[0]) + ((1 - bal) * fte[1]);
|
||||||
|
Out.ar(0,Pan2.ar(mix));
|
||||||
|
}
|
||||||
|
).add;
|
||||||
|
|
||||||
|
win.layout_(
|
||||||
|
VLayout(
|
||||||
|
[
|
||||||
|
HLayout(
|
||||||
|
[loadButton, stretch:1],
|
||||||
|
[soundFileView, stretch:5]
|
||||||
|
), stretch:2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
HLayout(
|
||||||
|
[loopButton, stretch:1],
|
||||||
|
[VLayout(
|
||||||
|
HLayout(StaticText(win).string_("Forward Th ").minWidth_(100), fwSlider),
|
||||||
|
HLayout(StaticText(win).string_("Backward Th").minWidth_(100), bwSlider),
|
||||||
|
HLayout(StaticText(win).string_("Mix").minWidth_(100), mixSlider)
|
||||||
|
), stretch:5]
|
||||||
|
), stretch:2
|
||||||
|
],
|
||||||
|
[freqSscope, stretch:2]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
win.front;
|
||||||
|
)
|
||||||
@ -0,0 +1,148 @@
|
|||||||
|
(
|
||||||
|
var server;
|
||||||
|
var win, soundFileView,loadButton, processButton;
|
||||||
|
var fwSlider, bwSlider, debounceSlider;
|
||||||
|
var soundFile, audioBuffer, slicesBuffer, slicesArray;
|
||||||
|
var addSelections, playFunc, stopFunc;
|
||||||
|
var synthDef, synth;
|
||||||
|
|
||||||
|
var playing, currentSelection, colors, prevColor;
|
||||||
|
var qwerty = "1234567890qwertyuiopasdfghjklzxcvbnm";
|
||||||
|
|
||||||
|
playing = false;
|
||||||
|
server = Server.default;
|
||||||
|
Font.default = Font("Monaco", 16);
|
||||||
|
|
||||||
|
audioBuffer = Buffer.new;
|
||||||
|
slicesBuffer = Buffer.new;
|
||||||
|
|
||||||
|
colors = Array.fill(64, {Color.rand});
|
||||||
|
|
||||||
|
synthDef = SynthDef(\transientSegDemo,{|buf, start, end|
|
||||||
|
Out.ar(0, BufRd.ar(1, buf, Phasor.ar(1, 1, start, end)));
|
||||||
|
}).add;
|
||||||
|
|
||||||
|
playFunc = {|index|
|
||||||
|
var dur;
|
||||||
|
currentSelection = index;
|
||||||
|
if(playing.not){
|
||||||
|
synth = Synth(\transientSegDemo,
|
||||||
|
[\buf, audioBuffer.bufnum,
|
||||||
|
\start, slicesArray[index],
|
||||||
|
\end, slicesArray[index+1]
|
||||||
|
]);
|
||||||
|
playing = true;
|
||||||
|
};
|
||||||
|
soundFileView.setSelectionColor(currentSelection, Color.white);
|
||||||
|
};
|
||||||
|
|
||||||
|
stopFunc = {synth.free; playing = false;
|
||||||
|
soundFileView.setSelectionColor(currentSelection, colors[currentSelection]);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
win = Window.new("TransientSegmentation",
|
||||||
|
Rect(200,200,800,450)).background_(Color.gray);
|
||||||
|
|
||||||
|
win.view.keyDownAction_{|view, char, modifiers, unicode, keycode, key|
|
||||||
|
var num = qwerty.indexOf(char);
|
||||||
|
if(num.notNil && slicesArray.notNil){
|
||||||
|
playFunc.value(num);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
win.view.keyUpAction_{stopFunc.value;};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
soundFileView = SoundFileView.new(win)
|
||||||
|
.gridOn_(false)
|
||||||
|
.waveColors_([Color.white]);
|
||||||
|
|
||||||
|
loadButton = Button(win, Rect(0, 0, 100, 100))
|
||||||
|
.minHeight_(150)
|
||||||
|
.states_([["Load", Color.grey, Color.grey(0.8)]]);
|
||||||
|
|
||||||
|
processButton = Button(win, Rect(0, 0, 100, 100))
|
||||||
|
.minHeight_(150)
|
||||||
|
.states_(
|
||||||
|
[["Process", Color.grey, Color.grey(0.8)],
|
||||||
|
["Wait", Color.grey, Color.grey(0.2)]]
|
||||||
|
);
|
||||||
|
|
||||||
|
fwSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
bwSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
debounceSlider = Slider(win, Rect(0, 0, 100, 10)).value_(0.5);
|
||||||
|
|
||||||
|
loadButton.action_{
|
||||||
|
FileDialog({ |path|
|
||||||
|
soundFile = SoundFile.new;
|
||||||
|
soundFile.openRead(path[0]);
|
||||||
|
audioBuffer = Buffer.read(server, path[0]);
|
||||||
|
soundFileView.soundfile = soundFile;
|
||||||
|
soundFileView.read(0, soundFile.numFrames);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
processButton.action_{|but|
|
||||||
|
var fw = ControlSpec(0.0001, 3, \exp).map(fwSlider.value);
|
||||||
|
var bw = ControlSpec(0.0001, 3, \exp).map(bwSlider.value);
|
||||||
|
var db = ControlSpec(1, 4410).map(debounceSlider.value);
|
||||||
|
if(but.value == 1, {
|
||||||
|
Routine{
|
||||||
|
FluidBufTransientSlice.process(
|
||||||
|
server,
|
||||||
|
source:audioBuffer.bufnum,
|
||||||
|
indices:slicesBuffer.bufnum,
|
||||||
|
threshFwd: fw,
|
||||||
|
threshBack: bw,
|
||||||
|
clumpLength:db
|
||||||
|
);
|
||||||
|
server.sync;
|
||||||
|
slicesBuffer.loadToFloatArray(action:{|arr|
|
||||||
|
slicesArray = arr;
|
||||||
|
{ processButton.value_(0);
|
||||||
|
addSelections.value(slicesArray)
|
||||||
|
}.defer;
|
||||||
|
|
||||||
|
});
|
||||||
|
}.play;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
addSelections = {|array|
|
||||||
|
var nSegments = min(array.size, soundFileView.selections.size) - 1;
|
||||||
|
soundFileView.selections.do({|sel, i| soundFileView.selectNone(i)});
|
||||||
|
nSegments.do({|i|
|
||||||
|
soundFileView.setSelectionStart(i, array[i]);
|
||||||
|
soundFileView.setSelectionSize(i, array[i+1] - array[i]);
|
||||||
|
soundFileView.setSelectionColor(i, colors[i]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
win.layout_(
|
||||||
|
VLayout(
|
||||||
|
[
|
||||||
|
HLayout(
|
||||||
|
[loadButton, stretch:1],
|
||||||
|
[soundFileView, stretch:5]
|
||||||
|
), stretch:2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
HLayout(
|
||||||
|
[processButton, stretch:1],
|
||||||
|
[VLayout(
|
||||||
|
HLayout(StaticText(win).string_("Forward Th ").minWidth_(100), fwSlider),
|
||||||
|
HLayout(StaticText(win).string_("Backward Th").minWidth_(100), bwSlider),
|
||||||
|
HLayout(StaticText(win).string_("Debounce").minWidth_(100), debounceSlider)
|
||||||
|
), stretch:5]
|
||||||
|
), stretch:2
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
win.front;
|
||||||
|
)
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
// A complex example of using composition as an Mid-Side FIR filtering process
|
||||||
|
|
||||||
|
// load a stereo buffer and initialise the many destinations
|
||||||
|
(
|
||||||
|
b = Buffer.read(s,File.realpath(FluidBufCompose.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav");
|
||||||
|
c = Buffer.new(s);
|
||||||
|
d = Buffer.new(s);
|
||||||
|
e = Buffer.new(s);
|
||||||
|
f = Buffer.new(s);
|
||||||
|
)
|
||||||
|
|
||||||
|
// encode the mid (in c) and the side (in d)
|
||||||
|
(
|
||||||
|
FluidBufCompose.process(s,b, numChans: 1, gain: -3.0.dbamp, destination: c);
|
||||||
|
FluidBufCompose.process(s,b, numChans: 1, gain: -3.0.dbamp, destination: d);
|
||||||
|
FluidBufCompose.process(s,b, numChans: 1, gain: -3.0.dbamp, startChan: 1, destination: c, destGain: 1.0);
|
||||||
|
FluidBufCompose.process(s,b, numChans: 1, gain: -3.0.dbamp * -1.0, startChan: 1, destination: d, destGain: 1.0);
|
||||||
|
)
|
||||||
|
|
||||||
|
// (optional) compare auraly the stereo with the MS
|
||||||
|
b.play;
|
||||||
|
{PlayBuf.ar(1,[c,d])}.play;
|
||||||
|
|
||||||
|
// The geeky bit: copy the side (buffer d) on itself with specific amplitudes and delays, in effect applying a FIR filter through expensive convolution
|
||||||
|
|
||||||
|
// Important: do either of the 3 options below
|
||||||
|
|
||||||
|
// option 1: apply a high pass on the side, with a cutoff of nyquist / 4
|
||||||
|
e.free; e = Buffer.new(s);
|
||||||
|
(
|
||||||
|
[1.0, -1.0].do({ arg x,y;
|
||||||
|
FluidBufCompose.process(s, d, gain: x, destStartFrame: y, destination: e, destGain: 1.0);
|
||||||
|
});
|
||||||
|
)
|
||||||
|
|
||||||
|
// option 2: apply a high pass on the side, with a cutoff of nyquist / 10
|
||||||
|
e.free; e = Buffer.new(s);
|
||||||
|
(
|
||||||
|
[0.8, -0.32, -0.24, -0.16, -0.08].do({ arg x,y;
|
||||||
|
FluidBufCompose.process(s, d, gain: x, destStartFrame: y, destination: e, destGain: 1.0);
|
||||||
|
});
|
||||||
|
)
|
||||||
|
|
||||||
|
// option 3: apply a high pass on the side, with a cutoff of nyquist / 100
|
||||||
|
e.free; e = Buffer.new(s);
|
||||||
|
(
|
||||||
|
[0.982494, -0.066859, -0.064358, -0.061897, -0.059477, -0.057098, -0.054761, -0.052466, -0.050215, -0.048007, -0.045843, -0.043724, -0.041649, -0.03962, -0.037636, -0.035697, -0.033805, -0.031959, -0.030159, -0.028406, -0.026699, -0.025038, -0.023425, -0.021857, -0.020337].do({ arg x,y;
|
||||||
|
FluidBufCompose.process(s, d, gain: x, destStartFrame: y, destination: e, destGain: 1.0);
|
||||||
|
});
|
||||||
|
)
|
||||||
|
|
||||||
|
// play the high-passed side buffer
|
||||||
|
e.play;
|
||||||
|
// if you want to try the other filters, do not forget to clear the destination buffer since it will add programmatically onto itself and would not create the expected frequency response
|
||||||
|
|
||||||
|
// decode the MS back to stereo
|
||||||
|
(
|
||||||
|
FluidBufCompose.process(s,c, numChans: 2, gain: -3.0.dbamp, destination: f);
|
||||||
|
FluidBufCompose.process(s,e, gain: -3.0.dbamp, destination: f, destGain: 1.0);
|
||||||
|
FluidBufCompose.process(s,e, gain: -3.0.dbamp * -1.0, destination: f, destStartChan: 1, destGain: 1.0);
|
||||||
|
)
|
||||||
|
|
||||||
|
// play the MS processed version
|
||||||
|
f.play;
|
||||||
|
|
||||||
|
// compare with the original
|
||||||
|
b.play;
|
||||||
@ -0,0 +1,100 @@
|
|||||||
|
// (re)set the source buffers
|
||||||
|
(
|
||||||
|
~low = Buffer.sendCollection(s, (Signal.sineFill(4410, Array.fill(3,0) ++ 1)));
|
||||||
|
~mid = Buffer.sendCollection(s, (Signal.sineFill(4410, Array.fill(12,0) ++ 1)));
|
||||||
|
~high = Buffer.sendCollection(s, (Signal.sineFill(4410, Array.fill(48,0) ++ 1)));
|
||||||
|
~piano = Buffer.read(s,File.realpath(FluidBufCompose.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav",0,8820);
|
||||||
|
)
|
||||||
|
|
||||||
|
// draw the buffers to see what happened
|
||||||
|
(
|
||||||
|
~low.plot;
|
||||||
|
~mid.plot;
|
||||||
|
~high.plot;
|
||||||
|
~piano.plot;
|
||||||
|
)
|
||||||
|
|
||||||
|
// define the concatenation macro
|
||||||
|
(
|
||||||
|
~concat = {
|
||||||
|
arg x;
|
||||||
|
if(x.class != Array,
|
||||||
|
{
|
||||||
|
"Error - Needs an array as argument".postln;
|
||||||
|
}, {
|
||||||
|
Routine{
|
||||||
|
for (1,x.size - 1, {
|
||||||
|
arg i;
|
||||||
|
FluidBufCompose.process(s,x[i],destination:x[0], destStartFrame:x[0].numFrames);
|
||||||
|
});
|
||||||
|
"Done!".postln;
|
||||||
|
}.play;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// test various combinations of concatenation
|
||||||
|
~concat.value([~low,~mid])
|
||||||
|
~concat.value([~mid,~low,~high])
|
||||||
|
~concat.value([~mid,~piano,~low])
|
||||||
|
~concat.value([~mid,~piano])
|
||||||
|
|
||||||
|
// check the buffers for the results
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
// define the merging macro
|
||||||
|
(
|
||||||
|
~merge = {
|
||||||
|
arg x;
|
||||||
|
if(x.class != Array,
|
||||||
|
{
|
||||||
|
"Error - Needs an array as argument".postln;
|
||||||
|
}, {
|
||||||
|
Routine{
|
||||||
|
for (1,x.size - 1, {
|
||||||
|
arg i;
|
||||||
|
FluidBufCompose.process(s,x[i],destination:x[0],destGain:1);
|
||||||
|
});
|
||||||
|
"Done!".postln;
|
||||||
|
}.play;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// test various combinations of merging
|
||||||
|
~merge.value([~low,~mid])
|
||||||
|
~merge.value([~mid,~low,~high])
|
||||||
|
~merge.value([~mid,~piano,~low])
|
||||||
|
~merge.value([~mid,~piano])
|
||||||
|
|
||||||
|
// check the buffers for the results
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
// define the stacking macro
|
||||||
|
(
|
||||||
|
~stack = {
|
||||||
|
arg x;
|
||||||
|
if(x.class != Array,
|
||||||
|
{
|
||||||
|
"Error - Needs an array as argument".postln;
|
||||||
|
}, {
|
||||||
|
Routine{
|
||||||
|
for (1,x.size - 1, {
|
||||||
|
arg i;
|
||||||
|
FluidBufCompose.process(s,x[i],destination:x[0], destStartChan:x[0].numChannels);
|
||||||
|
});
|
||||||
|
"Done!".postln;
|
||||||
|
}.play;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// test various combinations of stacking
|
||||||
|
~stack.value([~low,~mid])
|
||||||
|
~stack.value([~mid,~low,~high])
|
||||||
|
~stack.value([~mid,~piano,~low])
|
||||||
|
~stack.value([~mid,~piano])
|
||||||
|
|
||||||
|
// check the buffers for the results
|
||||||
@ -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 improvments!
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
// using NMF, splitting a small portion, then associating components to targets, then thresholding on these target's activations to find objects.
|
||||||
|
|
||||||
|
//set some buffers
|
||||||
|
(
|
||||||
|
b = Buffer.read(s,File.realpath(FluidNMFMatch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-BaB-SoundscapeGolcarWithDog.wav");
|
||||||
|
c = Buffer.new(s);
|
||||||
|
x = Buffer.new(s);
|
||||||
|
e = Buffer.new(s);
|
||||||
|
)
|
||||||
|
|
||||||
|
// train where all objects are present
|
||||||
|
(
|
||||||
|
Routine {
|
||||||
|
FluidBufNMF.process(s,b,130000,150000,0,1, c, x, components:10);
|
||||||
|
c.query;
|
||||||
|
}.play;
|
||||||
|
)
|
||||||
|
|
||||||
|
// wait for the query to print
|
||||||
|
// then find a component for each item you want to find. You could also sum them. Try to find a component with a good object-to-rest ratio
|
||||||
|
(
|
||||||
|
~dog =1;
|
||||||
|
{PlayBuf.ar(10,c)[~dog]}.play
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
~bird = 3;
|
||||||
|
{PlayBuf.ar(10,c)[~bird]}.play
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// copy at least one other component to a third filter, a sort of left-over channel
|
||||||
|
(
|
||||||
|
Routine{
|
||||||
|
FluidBufCompose.process(s, x, startChan:~dog, numChans: 1, destination: e);
|
||||||
|
FluidBufCompose.process(s, x, startChan:~bird, numChans: 1, destStartChan: 1, destination: e, destGain:1);
|
||||||
|
(0..9).removeAll([~dog,~bird]).do({|chan|FluidBufCompose.process(s,x, startChan:chan, numChans: 1, destStartChan: 2, destination: e, destGain:1)});
|
||||||
|
e.query;
|
||||||
|
}.play;
|
||||||
|
)
|
||||||
|
e.plot;
|
||||||
|
|
||||||
|
//using this trained basis we can then see the activation... (wait for 5 seconds before it prints!)
|
||||||
|
(
|
||||||
|
{
|
||||||
|
var source, blips;
|
||||||
|
//read the source
|
||||||
|
source = PlayBuf.ar(2, b);
|
||||||
|
blips = FluidNMFMatch.kr(source.sum,e,3);
|
||||||
|
}.plot(5);
|
||||||
|
)
|
||||||
|
|
||||||
|
// ...and use some threshold to 'find' objects...
|
||||||
|
(
|
||||||
|
{
|
||||||
|
var source, blips;
|
||||||
|
//read the source
|
||||||
|
source = PlayBuf.ar(2, b);
|
||||||
|
blips = Schmidt.kr(FluidNMFMatch.kr(source.sum,e,3),0.5,[10,1,1000]);
|
||||||
|
}.plot(5);
|
||||||
|
)
|
||||||
|
|
||||||
|
// ...and use these to sonify them
|
||||||
|
(
|
||||||
|
{
|
||||||
|
var source, blips, dogs, birds;
|
||||||
|
//read the source
|
||||||
|
source = PlayBuf.ar(2, b);
|
||||||
|
blips = Schmidt.kr(FluidNMFMatch.kr(source.sum,e,3),0.5,[10,1,1000]);
|
||||||
|
dogs = SinOsc.ar(100,0,Lag.kr(blips[0],0.05,0.15));
|
||||||
|
birds = SinOsc.ar(1000,0,Lag.kr(blips[1],0.05,0.05));
|
||||||
|
[dogs, birds] + source;
|
||||||
|
}.play;
|
||||||
|
)
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
// Using an 88-components piano base to do polyphonic pitch tracking
|
||||||
|
|
||||||
|
//load in the sound in and a pretrained basis
|
||||||
|
(
|
||||||
|
b = Buffer.read(s,File.realpath(FluidNMFMatch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Tremblay-SA-UprightPianoPedalWide.wav");
|
||||||
|
c = Buffer.read(s,File.realpath(FluidNMFMatch.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/filters/piano-dicts.wav");
|
||||||
|
)
|
||||||
|
b.play
|
||||||
|
c.query
|
||||||
|
|
||||||
|
//use the pretrained bases to compute activations of each notes to drive the amplitude of a resynth
|
||||||
|
(
|
||||||
|
{
|
||||||
|
var source, resynth;
|
||||||
|
source = PlayBuf.ar(2, b,loop:1).sum;
|
||||||
|
resynth = SinOsc.ar((21..108).midicps, 0, FluidNMFMatch.kr(source,c,88,10,4096).madd(0.002)).sum;
|
||||||
|
[source, resynth]
|
||||||
|
}.play
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
//now sample and hold the same stream to get notes identified, played and sent back via osc
|
||||||
|
(
|
||||||
|
{
|
||||||
|
var source, resynth, chain, trig, acts;
|
||||||
|
source = PlayBuf.ar(2,b,loop:1).sum;
|
||||||
|
|
||||||
|
// built in attack detection, delayed until the stable part of the sound
|
||||||
|
chain = FFT(LocalBuf(256), source);
|
||||||
|
trig = TDelay.kr(Onsets.kr(chain, 0.5),0.1);
|
||||||
|
|
||||||
|
// samples and holds activation values that are scaled and capped, in effect thresholding them
|
||||||
|
acts = Latch.kr(FluidNMFMatch.kr(source,c,88,10,4096).linlin(15,20,0,0.1),trig);
|
||||||
|
|
||||||
|
// resynths as in the previous example, with the values sent back to the language
|
||||||
|
resynth = SinOsc.ar((21..108).midicps, 0, acts).sum;
|
||||||
|
SendReply.kr(trig, '/activations', acts);
|
||||||
|
[source, resynth]
|
||||||
|
// [source, T2A.ar(trig)]
|
||||||
|
// resynth
|
||||||
|
}.play
|
||||||
|
)
|
||||||
|
|
||||||
|
// define a receiver for the activations
|
||||||
|
(
|
||||||
|
OSCdef(\listener, {|msg|
|
||||||
|
var data = msg[3..];
|
||||||
|
// removes the silent and spits out the indicies as midinote number
|
||||||
|
data.collect({arg item, i; if (item > 0.01, {i + 21})}).reject({arg item; item.isNil}).postln;
|
||||||
|
}, '/activations');
|
||||||
|
)
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
(
|
||||||
|
b = Buffer.read(s,File.realpath(FluidBufNoveltySlice.class.filenameSymbol).dirname.withTrailingSlash ++ "../AudioFiles/Nicol-LoopE-M.wav");
|
||||||
|
c = Buffer.new(s);
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
// with basic params
|
||||||
|
Routine{
|
||||||
|
var startTime, target, tolerance, startThresh, prevThresh, curThresh, curVal, prevVal, iters, maxIters, dVal, dThresh;
|
||||||
|
startTime = Main.elapsedTime;
|
||||||
|
prevThresh = 0.1; //initial threshold (between 0.00001 and 0.99999
|
||||||
|
target = 10; //number of slices desired
|
||||||
|
tolerance = 0; // the acceptable error in the number of slices yield
|
||||||
|
maxIters = 100; //max number of iterations acceptable
|
||||||
|
|
||||||
|
//makes a first iteration
|
||||||
|
FluidBufNoveltySlice.process(s,b, indices: c, threshold:prevThresh,action:{|x|prevVal = x.numFrames});
|
||||||
|
//makes a second iteration
|
||||||
|
if ( (prevVal < target), {
|
||||||
|
curThresh = (prevThresh * 0.5).max(0.000001);
|
||||||
|
}, {
|
||||||
|
curThresh = (prevThresh * 2).min(0.999999);
|
||||||
|
});
|
||||||
|
FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh,action:{|x|curVal = x.numFrames});
|
||||||
|
|
||||||
|
//makes further iterations until the result is achieved, or the maximum of acceptable iterations is reached
|
||||||
|
iters = 2;
|
||||||
|
while ( {
|
||||||
|
(iters < maxIters) && ((curVal - target).abs > tolerance)
|
||||||
|
}, {
|
||||||
|
iters = iters + 1;
|
||||||
|
dVal = curVal - prevVal;
|
||||||
|
dThresh = curThresh - prevThresh;
|
||||||
|
|
||||||
|
prevThresh = curThresh;
|
||||||
|
prevVal = curVal;
|
||||||
|
|
||||||
|
if ( (dVal == 0), {
|
||||||
|
//if we have not change results between the last 2 passes, make the variation of threshold bigger
|
||||||
|
curThresh = (dThresh + curThresh).min(0.999999).max(0.000001);
|
||||||
|
},{
|
||||||
|
//if we have
|
||||||
|
curThresh = (((dThresh / dVal) * (target - curVal)) + curThresh).min(0.999999).max(0.000001);
|
||||||
|
});
|
||||||
|
FluidBufNoveltySlice.process(s,b, indices: c, threshold:curThresh,action:{|x|curVal = x.numFrames});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
//depending on the outcome, gives the right info back
|
||||||
|
|
||||||
|
if ( iters >= maxIters, {
|
||||||
|
// failed
|
||||||
|
"Failed to find a suitable threshold in % seconds.\n".postf((Main.elapsedTime - startTime).round(0.01));
|
||||||
|
}, {
|
||||||
|
// succeeded
|
||||||
|
"Found % as a suitable threshold for % slices in % seconds and % iterations.\n".postf(curThresh, curVal, (Main.elapsedTime - startTime).round(0.01), iters);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}.play
|
||||||
|
)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue