235 lines
5.7 KiB
Racket
235 lines
5.7 KiB
Racket
#lang scribble/manual
|
||
@(require (for-label racket))
|
||
|
||
@title{LocalBuf}
|
||
Allocate a buffer local to the synth@section{categories}
|
||
UGens>Buffer
|
||
@section{related}
|
||
Classes/Buffer, Classes/SetBuf, Classes/ClearBuf
|
||
|
||
@section{classmethods}
|
||
|
||
@section{private}
|
||
categories, new1
|
||
|
||
@section{method}
|
||
new
|
||
Allocate a local buffer
|
||
@section{argument}
|
||
numFrames
|
||
number of frames (default: 1)
|
||
@section{argument}
|
||
numChannels
|
||
number of channels for multiple channel buffers (default: 1)
|
||
@section{returns}
|
||
a new buffer – the ugen outputs its buffer number and can thus be used in any other ugen that requires a buffer number input.
|
||
|
||
@section{method}
|
||
newFrom
|
||
Allocates a new buffer from a given list of values
|
||
@section{argument}
|
||
list
|
||
The list may be two-dimensional for numChannels > 1.
|
||
It is then reshaped into the buffer's current format by flattening.
|
||
@section{returns}
|
||
a new buffer
|
||
@section{discussion}
|
||
|
||
Since newFrom is called by the as message, one may thus convert an array to a LocalBuf:
|
||
|
||
@racketblock[
|
||
[1, 2, 3].as(LocalBuf)
|
||
::
|
||
|
||
]
|
||
@section{instancemethods}
|
||
|
||
@section{method}
|
||
set
|
||
set the buffer slots with a list of values.
|
||
@section{discussion}
|
||
|
||
If list is smaller than numFrames, it will only set
|
||
part of the buffer. The list may be two-dimensional for numChannels > 1.
|
||
offset is the starting index (default: 0)
|
||
@section{warning}
|
||
|
||
SynthDef permits a maximum of 65536 (2**16) unique constant values in one definition. A very large array of distinct values can corrupt the SynthDef's binary format. If you need a large buffer to be pre-initialized with signal data, it is strongly recommended to use link::Classes/Buffer:: instead.
|
||
::
|
||
|
||
If the buffer is large but holds a smaller number of unique values, e.g.
|
||
@racketblock[Array.fill(88200, { #[0, 0.25, 0.5, 0.75, 1.0].choose })::, this is no problem. SynthDef compacts the large array for the list of constants.
|
||
|
||
]
|
||
|
||
@racketblock[
|
||
SynthDef(\bigLocalBuf, {
|
||
LocalBuf(88200).set(Array.fill(88200, { #[0, 0.25, 0.5, 0.75, 1.0].choose }))
|
||
}).add;
|
||
|
||
SynthDescLib.at(\bigLocalBuf).constants;
|
||
// prints: FloatArray[ 1, 88200, 0, 0.75, 0.5, 0.25 ]
|
||
::
|
||
|
||
]
|
||
@section{method}
|
||
clear
|
||
set the buffer slot to zero.
|
||
@section{discussion}
|
||
|
||
This is important when randomly accessing buffer slots
|
||
(e.g. with a BufRd) or not overwriting them. Clear is not an efficient real time operation
|
||
for larger buffers, so it should be only used when really needed - but then it is essential:
|
||
a LocalBuf is "created" in each new synth, and it may reuse old space. So if an older
|
||
synth has already ended, this part of memory may be the same as the new synth's.
|
||
|
||
@section{examples}
|
||
|
||
|
||
@racketblock[
|
||
// example: FFT
|
||
|
||
(
|
||
{
|
||
var in, chain;
|
||
in = WhiteNoise.ar(0.1.dup);
|
||
chain = FFT({LocalBuf(2048, 1)}.dup, in);
|
||
chain = PV_BrickWall(chain, SinOsc.kr([0.1, 0.11]));
|
||
IFFT(chain) // inverse FFT
|
||
}.play;
|
||
)
|
||
|
||
// spawn some FFT based synths:
|
||
(
|
||
SynthDef(\fftgrain, { |out, sustain = 1, rate = 0.2|
|
||
var in, chain;
|
||
in = WhiteNoise.ar(0.1).dup;
|
||
chain = FFT({LocalBuf(128, 1)}.dup, in);
|
||
chain = PV_BrickWall(chain,
|
||
SinOsc.kr(rate * XLine.kr(1, 15 * [1, 1.6], sustain), Rand(0, pi))
|
||
);
|
||
Out.ar(out, IFFT(chain) * XLine.kr(1, 0.001, sustain, doneAction: Done.freeSelf)) // inverse FFT
|
||
}).add;
|
||
)
|
||
|
||
(
|
||
Pbind(
|
||
\instrument, \fftgrain,
|
||
\rate, Pwhite().linexp(0, 1, 0.01, 300),
|
||
\legato, Pwhite(1, 3.0, inf),
|
||
\dur, Prand([0.2, 1, 1.2], inf)
|
||
).play
|
||
)
|
||
|
||
// IndexL
|
||
(
|
||
{
|
||
var buf = LocalBuf.newFrom((0..5).scramble);
|
||
var freq = IndexL.kr(buf, MouseX.kr(0, BufFrames.kr(buf))).poll * 100 + 40;
|
||
Saw.ar(freq * [1, 1.1]) * 0.1
|
||
}.play;
|
||
)
|
||
|
||
// DetectIndex
|
||
(
|
||
{
|
||
var buf1 = LocalBuf.newFrom((0..5).scramble);
|
||
var buf2 = LocalBuf.newFrom((0..5).scramble - 1);
|
||
var buf3 = LocalBuf.newFrom((0..5).scramble + 1);
|
||
var index = DetectIndex.kr([buf1, buf2], SinOsc.kr([0.85, 0.8], 0, 6).trunc).poll;
|
||
var freq = IndexL.kr([buf2, buf3], index).poll * 40 + 40;
|
||
Saw.ar(freq) * 0.1
|
||
}.play;
|
||
)
|
||
|
||
|
||
// DegreeToKey
|
||
// modal space
|
||
// mouse x controls discrete pitch in dorian mode
|
||
(
|
||
play({
|
||
var mix;
|
||
|
||
mix =
|
||
|
||
// lead tone
|
||
SinOsc.ar(
|
||
(
|
||
DegreeToKey.kr(
|
||
[0, 2, 3.2, 5, 7, 9, 10].as(LocalBuf),
|
||
MouseX.kr(0, 15), // mouse indexes into scale
|
||
12, // 12 notes per octave
|
||
1, // mul = 1
|
||
72 // offset by 72 notes
|
||
).poll
|
||
+ LFNoise1.kr([3,3], 0.04) // add some low freq stereo detuning
|
||
).midicps, // convert midi notes to hertz
|
||
0,
|
||
0.1)
|
||
|
||
// drone 5ths
|
||
+ RLPF.ar(LFPulse.ar([48,55].midicps, 0.15),
|
||
SinOsc.kr(0.1, 0, 10, 72).midicps, 0.1, 0.1);
|
||
|
||
// add some 70's euro-space-rock echo
|
||
CombN.ar(mix, 0.31, 0.31, 2, 1, mix)
|
||
})
|
||
)
|
||
|
||
// Osc
|
||
(
|
||
{
|
||
var buf;
|
||
var list = Wavetable.sineFill(512, 1.0 / [1, 10, 3, 10, 5, 6, 10]);
|
||
// list.plot;
|
||
buf = LocalBuf.newFrom(list);
|
||
Osc.ar(buf,
|
||
XLine.kr(2000, 200 + {30.0.rand}.dup, 10) + SinOsc.ar(Line.kr(2, 300, 10),
|
||
0, 100)
|
||
) * 0.1;
|
||
}.play;
|
||
)
|
||
|
||
// see how not clearing the buffer accesses old data:
|
||
// slowly overwrite data with noise
|
||
(
|
||
{
|
||
var buf = LocalBuf(2048, 2);
|
||
BufWr.ar(WhiteNoise.ar(1.dup), buf, LFNoise0.ar(530).range(0, BufFrames.kr(buf)));
|
||
PlayBuf.ar(2, buf, MouseX.kr(1, 2), loop: 1) * 0.1
|
||
}.play
|
||
)
|
||
|
||
// avoid this (unless you like the glitch) by clearing buffer first:
|
||
(
|
||
{
|
||
var buf = LocalBuf(2048, 2).clear;
|
||
BufWr.ar(WhiteNoise.ar(1.dup), buf, LFNoise0.ar(530).range(0, BufFrames.kr(buf)));
|
||
PlayBuf.ar(2, buf, MouseX.kr(1, 2), loop: 1) * 0.1
|
||
}.play
|
||
)
|
||
|
||
|
||
// BufCombC stereo (needs no clearing, because delay is filled by ugen)
|
||
(
|
||
{
|
||
var z = Decay.ar(Dust.ar(1.dup, 0.1), 0.3, WhiteNoise.ar);
|
||
BufCombC.ar(LocalBuf(SampleRate.ir, 2), z, XLine.kr(0.0001, 0.01, 20), 0.2);
|
||
}.play
|
||
)
|
||
|
||
// multichannel test
|
||
(
|
||
{
|
||
var in, chain, n = 4;
|
||
in = WhiteNoise.ar(0.1.dup(n));
|
||
chain = FFT({LocalBuf(2048, 1)}.dup(n), in);
|
||
chain = PV_BrickWall(chain, LFNoise2.kr(2.dup(n)));
|
||
Splay.ar(IFFT(chain)) // inverse FFT
|
||
}.play;
|
||
)
|
||
::
|
||
|
||
]
|
||
|
||
|