Hello again,
probably a combination of two different things: I’ve tried Ex 1f now with PV_BufRd / PV_BinBufRd and didn’t encounter any server crashes. Independent from this there’s a method missing preventing PV_BinGap and PV_BinRange from working within a chain, but this can easily be added (for variants with PV_BinGap see second block of code, replace in miSCellaneous_lib’s file PV_UGens.sc and recompile before playing examples 1 and 2).
Regarding the server crashes, check for following possible mistakes:
.) it needs an Array of different LocalBufs, so
{ LocalBuf(~windowSize) } ! ~maxOverlap
and not
LocalBuf(~windowSize) ! ~maxOverlap
.) In contrast to PV_BufRd PV_BinBufRd needs an FFT before
.) some type of multichannel mismatch (e.g. using a nested array where a flat one were needed)
For the sake of simplicity the examples below use 12 analysis buffers derived from a single buffer played at different rates.
// analysis adapted from PV_BufRd help file
// prepare 12 analysis buffers from one sound file played at 12 different rates
s.boot
(
~path = Platform.resourceDir +/+ "sounds/a11wlk01.wav";
~windowSize = 1024;
~hopSize = 0.25;
~soundfile = SoundFile.new(~path);
~soundfile.openRead;
~soundfile.close;
~rates = (1..12) * 0.1 + 0.4;
// need FFT buffers from different lengths
~recBufs = ~rates.collect { |rate, i|
Buffer.alloc(s, (~soundfile.duration / rate).calcPVRecSize(~windowSize, ~hopSize));
};
// one sound buffer is enough in this case
~soundBuf = Buffer.read(s, ~path);
// Hann window
~winType = 1;
)
// analysis SynthDef (in addition uses rate)
(
SynthDef(\pvrec_2, { arg recBuf, soundBufnum, rate = 1;
var in, chain, bufnum;
bufnum = LocalBuf.new(~windowSize);
Line.kr(1, 1, BufDur.kr(soundBufnum) / rate, doneAction: 2);
in = PlayBuf.ar(1, soundBufnum, rate * BufRateScale.kr(soundBufnum), loop: 0);
chain = FFT(bufnum, in, ~hopSize, ~winType);
chain = PV_RecordBuf(chain, recBuf, 0, 1, 0, ~hopSize, ~winType);
}).add;
)
// make analysis of same buffer played back at different speeds
(
~rates.do { |rate, i| Synth(\pvrec_2, [\recBuf, ~recBufs[i], \soundBufnum, ~soundBuf, \rate, rate]) }
)
// playback
(
SynthDef(\pvplay, { arg out = 0, recBuf = 1;
var in, chain, bufnum;
bufnum = LocalBuf.new(1024);
chain = PV_BufRd(bufnum, recBuf, MouseX.kr(0.0, 1.0));
Out.ar(out, IFFT(chain, 1).dup);
}).add;
)
// check with a specific rate
b = Synth("pvplay", [\out, 0, \recBuf, ~recBufs[10]]);
b.free;
// Ex. 1, Granulation with PV_BufRd
// check also with PV_BinGap uncommented
// therefore it needs adapted class definition
(
~maxOverlap = ~rates.size;
~audioBus = Bus.audio(s, ~maxOverlap);
// overlap only settable in SC versions >= 3.9
SynthDef(\gran_1f_a, { |out = 0, posLo = 0.1, posHi = 0.9,
posRateE = 0, posRateM = 1, overlap = 2, trigRate = 1,
panMax = 0.8, loBin = 10, hiBin = 10, amp = 1|
var sig, bpFreq, dUgen, bufDur, pos, posRate, playbuf, env, maxOverlap = ~maxOverlap;
var chains, fftBufs = { LocalBuf(~windowSize) } ! ~maxOverlap;
posRate = 10 ** posRateE * posRateM;
// phasor bounds must be between 0 and 1
pos = Phasor.ar(0, posRate * SampleDur.ir, posLo, posHi);
// multichannel trigger
env = DXEnvFan.ar(
Dseq((0..maxOverlap-1), inf),
trigRate.reciprocal,
size: maxOverlap,
maxWidth: maxOverlap,
width: (Main.versionAtLeast(3, 9)).if { overlap }{ 2 },
// option to avoid unwanted triggers
zeroThr: 0.002,
// take equalPower = 0 for non-squared sine envelopes
// more efficient with helper bus
equalPower: 0,
bus: ~audioBus
);
chains = PV_BufRd(fftBufs, ~recBufs, pos);
// check with PV_BinGap
// chains = PV_BinGap(chains, loBin, hiBin);
playbuf = IFFT(chains, ~winType);
// generate grains by multiplying with envelope
sig = playbuf * env;
// generate array of 12 stereo signals
sig = Pan2.ar(sig, Demand.ar(env, 0, Dseq([-1, 1], inf) * panMax));
// mix to out
Out.ar(0, Mix(sig) * amp)
}, metadata: (
specs: (
posLo: [0.01, 0.99, \lin, 0.01, 0],
posHi: [0.01, 0.99, \lin, 0.01, 0.5],
posRateE: [-3, 4, \lin, 1, -1],
posRateM: [0.1, 10, \exp, 0.01, 1.35],
trigRate: [1, 200, \lin, 0.01, 90],
overlap: [0.2, 12, \lin, 0.01, 7],
panMax: [0.0, 1, \lin, 0.005, 0.75],
loBin: [0, 200, \lin, 1, 5],
hiBin: [0, 200, \lin, 1, 12],
amp: [0.0, 3, \lin, 0.005, 1]
)
)).add;
)
(
\gran_1f_a.sVarGui.gui(
tryColumnNum: 1,
synthColorGroups: (0..9).clumps([4,2,1,2,1])
)
)
// Ex. 2, Granulation with PV_BinBufRd
// check also with PV_BinGap uncommented
// therefore it needs adapted class definition
(
~maxOverlap = ~rates.size;
~audioBus = Bus.audio(s, ~maxOverlap);
SynthDef(\gran_1f_b, { |out = 0, posLo = 0.1, posHi = 0.9,
posRateE = 0, posRateM = 1, overlap = 2, trigRate = 1,
panMax = 0.8, loBin = 10, hiBin = 10, clear = 0.0, amp = 1|
var sig, bpFreq, dUgen, bufDur, pos, posRate, playbuf, env, maxOverlap = ~maxOverlap;
var chains, fftBufs = { LocalBuf(~windowSize) } ! ~maxOverlap;
posRate = 10 ** posRateE * posRateM;
// phasor bounds must be between 0 and 1
pos = Phasor.ar(0, posRate * SampleDur.ir, posLo, posHi);
// multichannel trigger
env = DXEnvFan.ar(
Dseq((0..maxOverlap-1), inf),
trigRate.reciprocal,
size: maxOverlap,
maxWidth: maxOverlap,
width: (Main.versionAtLeast(3, 9)).if { overlap }{ 2 },
// option to avoid unwanted triggers
zeroThr: 0.002,
// take equalPower = 0 for non-squared sine envelopes
// more efficient with helper bus
equalPower: 0,
bus: ~audioBus
);
// need FFT before PV_BinBufRd !
chains = FFT(fftBufs, PlayBuf.ar(1, ~soundBuf, ~rates, loop: 1));
chains = PV_BinBufRd(chains, ~recBufs, pos, 0, 2, 10, clear);
// check with PV_BinGap
// chains = PV_BinGap(chains, loBin, hiBin);
playbuf = IFFT(chains, ~winType);
// generate grains by multiplying with envelope
sig = playbuf * env;
// generate array of ~maxOverlap stereo signals
sig = Pan2.ar(sig, Demand.ar(env, 0, Dseq([-1, 1], inf) * panMax));
// mix to out
Out.ar(0, Mix(sig) * amp)
}, metadata: (
specs: (
posLo: [0.01, 0.99, \lin, 0.01, 0],
posHi: [0.01, 0.99, \lin, 0.01, 0.5],
posRateE: [-3, 4, \lin, 1, -1],
posRateM: [0.1, 10, \exp, 0.01, 1.35],
trigRate: [1, 200, \lin, 0.01, 90],
overlap: [0.2, 12, \lin, 0.01, 7],
panMax: [0.0, 1, \lin, 0.005, 0.75],
clear: [0, 1, \lin, 1, 0],
loBin: [0, 200, \lin, 1, 5],
hiBin: [0, 200, \lin, 1, 12],
amp: [0.0, 3, \lin, 0.005, 1]
)
)).add;
)
(
\gran_1f_b.sVarGui.gui(
tryColumnNum: 1,
synthColorGroups: (0..10).clumps([4,2,1,1,2,1])
)
)
s.freqscope
Here the modified code for PV_UGens.sc within miSCellaneous, replace and recompile
PV_BinRange : PV_ChainUGen {
*new { |buffer, loBin, hiBin|
^this.multiNew(\control, buffer, loBin, hiBin)
}
*new1 { |rate, buffer, loBin, hiBin|
var wipe, bufSize, chain_clipped;
bufSize = buffer.miSC_getFFTbufSize;
chain_clipped = LocalBuf(bufSize);
chain_clipped = PV_Copy(buffer, chain_clipped);
wipe = loBin * 2 / bufSize;
chain_clipped = PV_BrickWall(chain_clipped, wipe);
wipe = hiBin * 2 / bufSize - 1;
chain_clipped = PV_BrickWall(chain_clipped, wipe);
^chain_clipped
}
}
PV_BinGap : PV_ChainUGen {
*new { |buffer, loBin, hiBin|
^this.multiNew(\control, buffer, loBin, hiBin)
}
*new1 { |rate, buffer, loBin, hiBin|
var wipe, bufSize, chain_gap_1, chain_gap_2;
bufSize = buffer.miSC_getFFTbufSize;
chain_gap_1 = LocalBuf(bufSize);
chain_gap_2 = LocalBuf(bufSize);
chain_gap_1 = PV_Copy(buffer, chain_gap_1);
chain_gap_2 = PV_Copy(buffer, chain_gap_2);
wipe = loBin - 1 * 2 / bufSize - 1;
chain_gap_1 = PV_BrickWall(chain_gap_1, wipe);
wipe = hiBin + 1 * 2 / bufSize;
chain_gap_2 = PV_BrickWall(chain_gap_2, wipe);
^PV_Add(chain_gap_1, chain_gap_2);
}
}
+ FFT {
miSC_getFFTbufSize {
^this.fftSize
}
}
+ PV_ChainUGen {
miSC_getFFTbufSize {
^this.inputs[0].miSC_getFFTbufSize
}
}
+ LocalBuf {
miSC_getFFTbufSize {
^this.inputs[1]
}
}