ZeroXBufWr writes zero crossing analysis from signals to buffers
Part of: miSCellaneous
Inherits from: UGen
ZeroXBufWr analyses zero crossings from an input signal and writes the signal and the zero crossing indices to buffers. It is intended to be used with ZeroXBufRd and TZeroXBufRd, see these help files for more examples.
NOTE: Often it pays to adjust zero crossings in the sound buffer effectively to 0, that way sawtooth-like interpolation artefacts can be avoided. See Ex. 2 and Ex. 7 in ZeroXBufRd help..
NOTE: For avoiding too long half wavesets it can be useful to apply LeakDC resp. a high pass filter before analysis.
NOTE: For full functionality at least SC 3.7 is recommended (adjustZeroXs set to 2 doesn't work in 3.6)
CREDITS: Thanks to Tommaso Settimi for an inspiring discussion, which gave me a nudge to tackle these classes.
See also: ZeroXBufRd, TZeroXBufWr, DX suite, DXMix, DXMixIn, DXEnvFan, DXEnvFanOut, DXFanOut, Buffer Granulation, Live Granulation, PbindFx, kitchen studies
Creation / Class Methods
*ar (in, sndBuf, zeroXBuf, startWithZeroX = 0, adjustZeroXs = 1, doneAction = 0)
in - Signal to be analysed, size must correspond to sndBuf and zeroXBuf.
sndBuf - Buffer or SequenceableCollection of Buffers to write signals to,
size must correspond to in and zeroXBuf, writing can be disabled with writeSndBuf.
The length of sndBuf determines the trigger for the doneAction.
zeroXBuf - Buffer or SequenceableCollection of Buffers to write anaysis data to,
size must correspond to in and sndBuf.
startWithZeroX - Number 0 or 1 or SequenceableCollection of such,
determining whether the first sample should be regarded as zero crossing.
Defaults to 0.
adjustZeroXs - One of the Numbers -1, 0, 1, 2 or a SequenceableCollection of such.
-1: indicate all zeroXs, dont't write sound buffer
0: indicate all zeroXs, write sound buffer
1: indicate all zeroXs, write sound buffer, set to 0 there
2: indicate zeroXs with a minimum distance of 2, set to 0 there
Actions 1 and 2 can lead to smoother half wavesets, see examples.
Defaults to 0.
doneAction - Done action to be performed after the duration of the longest buffer of sndBuf.
Defaults to 0.
Examples
// See the ZeroXBufRd and TZeroXBufRd help files for more examples
Ex.1) Basic usage
// prepare two short buffers for audio and zero crossing data
(
b = Buffer.alloc(s, 256);
z = Buffer.alloc(s, 256);
)
// analyse a short snippet of random noise
(
{
var src = LFDNoise3.ar(5000);
ZeroXBufWr.ar(src, b, z, startWithZeroX: 0, doneAction: 2);
Silent.ar
}.play
)
// plot buffer, most likely it doesn't start with 0
b.plot
// zero crossings
z.loadToFloatArray(action: { |b| b.postln })
// clear buffers
(
b.zero;
z.zero;
)
// example with SinOsc
// other than you might expect SinOsc doesn't start with 0
// for analysis you might want to regard the value at index 0 as zero crossing
// this can be done with the flag startWithZeroX:
(
{
var src = SinOsc.ar(500);
ZeroXBufWr.ar(src, b, z, startWithZeroX: 1, doneAction: 2);
Silent.ar
}.play
)
// plot buffer, you see that it doesn't start with 0
b.plot
// zero crossings including start
z.loadToFloatArray(action: { |b| b.postln })
Ex.2) Adjusting zero crossings
(
b = Buffer.alloc(s, 128);
z = Buffer.alloc(s, 128);
)
s.scope
// fill buffer
(
{
var src = LFPar.ar(700);
ZeroXBufWr.ar(src, b, z, startWithZeroX: 1, doneAction: 2);
Silent.ar
}.play
)
// playing this repeated half waveset at slow rate shows that the x axis is crossed
// as the buffer's value at the zero crossing isn't exactly 0
x = { ZeroXBufRd.ar(b, z, nil, 1, rate: 0.2) * 0.1 }.play
// this can be circumvented by two strategies:
// setting to zeros from the language
// can be done while running
b.adjustZeroXs(z)
x.release
// alternatively writing can be done with the flag 'adjustZeroXs' set to 1:
(
{
var src = LFPar.ar(700);
ZeroXBufWr.ar(src, b, z, startWithZeroX: 1, adjustZeroXs: 1, doneAction: 2);
Silent.ar
}.play
)
x = { ZeroXBufRd.ar(b, z, nil, 1, rate: 0.2) * 0.1 }.play
x.release
// If flag 'adjustZeroXs' is set to 2, this defines the minimum distance of detected zero crossings,
// these positions in the buffer are also set to 0.
// This option can make sense in the case of sources with many fast sign switchings.
// here the resulting buffer can end up with sequences of zeros ...
(
{
var seq = Drand([1, 1, 2, 3], inf);
var src = Duty.ar(SampleDur.ir * seq, 0, Dwhite(0.1, 1) * Dseq([-1, 1], inf));
ZeroXBufWr.ar(src, b, z, startWithZeroX: 1, adjustZeroXs: 1, doneAction: 2);
Silent.ar
}.play
)
b.plot
// ... whereas here we have a continuous sequence of at least minimal half wavesets
(
{
var seq = Drand([1, 1, 2, 3], inf);
var src = Duty.ar(SampleDur.ir * seq, 0, Dwhite(0.1, 1) * Dseq([-1, 1], inf));
ZeroXBufWr.ar(src, b, z, startWithZeroX: 1, adjustZeroXs: 2, doneAction: 2);
Silent.ar
}.play
)
b.plot
// This can especially make a difference with ZeroXBufRd, as the latter works with
// a minimum half waveset length of 2 samples