236 lines
5.7 KiB
Text
236 lines
5.7 KiB
Text
|
#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;
|
|||
|
)
|
|||
|
::
|
|||
|
|
|||
|
]
|
|||
|
|
|||
|
|