You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bmSClib/DSP/examples/spectral_freeze_scatter.scd

105 lines
5.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

(
// Пример: spectral freeze + grain scatter.
// Концепция из README.org дуэта 24_09_2026 (концепция №1).
//
// Два независимых словаря:
// - fft_freeze — удержание magnitude, фазовый джиттер (PV_Freeze + PV_PhaseShift).
// - grain_scatter — WarpZ по кольцевому буферу, scatter позиции.
//
// Оба словаря идут через makeBufferProcessingNdef — это стандартный
// путь для Fx/сэмплер-словарей в bmSClib: func получает audio-сигнал
// первым аргументом (| buf |), обёртка делает SynthDef.wrap(func, [buf])
// и кладёт результат в Ndef.
//
// Входной звук подаётся через Bus — это стандартный паттерн для Fx,
// потому что makeBufferProcessingNdef передаёт \buf как OSC-параметр
// (control-rate число = номер bus), а не как UGen.
s.options.numBuffers = 1024; // FFT-буферы + ringbuffer для scatter
s.waitForBoot({
// ---- Распределение буферов -------------------------------------
// FFT: явное распределение, как в шаблоне make-scd.el.
~fftBuf = Buffer.alloc(s, 1024, 2); // stereo FFT, 1024 сэмплов
// Кольцевой буфер для grain_scatter: 2 секунды, стерео.
~ringBuf = Buffer.alloc(s, s.sampleRate * 2.0, 2);
// ---- Входной bus ------------------------------------------------
// В реальном концерте — звук от Letov или печатной машинки.
// Здесь — SoundIn.ar как stand-in, пишется в ~inBus отдельным
// синтом. Ndef читает этот bus по номеру через \buf.kr.
~inBus = Bus.audio(s, 2);
{ Out.ar(~inBus, SoundIn.ar([0, 1])) }.play;
// ---- Загрузка словарей ------------------------------------------
~freezeDict = BmDspAlgs.getDict("spectral/fft_freeze.scd");
~scatterDict = BmDspAlgs.getDict("spectral/grain_scatter.scd");
// ---- Режим 1: только freeze --------------------------------------
// makeBufferProcessingNdef(name, buf) — создаёт Ndef(\name) с
// func, обёрнутой через SynthDef.wrap(func, prependArgs: [buf]).
// buf — это audio-сигнал; здесь передаём номер bus.
~freezeDict.makeBufferProcessingNdef(\freezeOnly, ~inBus);
Ndef(\freezeOnly).set(\fftBufnum, ~fftBuf.bufnum);
Ndef(\freezeOnly).play;
// Управление freeze-педалью: gate=1 триггерит freeze, gate=0
// отпускает. Удобно вешать на MIDI CC или OSC.
Ndef(\freezeOnly).addSpec(\freezeGate, [0, 1, \lin, 1, 0]);
Ndef(\freezeOnly).set(\freezeGate, 0);
// ---- Режим 2: только scatter -------------------------------------
~scatterDict.makeBufferProcessingNdef(\scatterOnly, ~inBus);
Ndef(\scatterOnly).set(\bufnum, ~ringBuf.bufnum);
Ndef(\scatterOnly).play;
// ---- Режим 3: оба последовательно -------------------------------
// freeze → scatter: frozen-сигнал проходит через WarpZ.
// Ndef-цепочка: Ndef(\freezeOnly) пишет на свой bus, scatter
// читает с него.
~freezeBus = Bus.audio(s, 2);
Ndef(\freezeOnly)[0] = { Out.ar(~freezeBus, Ndef(\freezeOnly).bus.asMap) };
~scatterDict.makeBufferProcessingNdef(\freezeAndScatter, ~freezeBus);
Ndef(\freezeAndScatter).set(\bufnum, ~ringBuf.bufnum);
Ndef(\freezeAndScatter).play;
// ---- Педали ------------------------------------------------------
// Управление через OSC / MIDI / HID. Ndef.set принимает числа,
// для live-управления — .map(\param, <CC>.kr).
Ndef(\freezeOnly).addSpec(\phaseJitter, [0, 1, \lin, 0.01, 0.1]);
Ndef(\freezeOnly).set(\phaseJitter, 0.1);
Ndef(\scatterOnly).addSpec(\positionJitter, [0, 1, \lin, 0.01, 0.3]);
Ndef(\scatterOnly).set(\positionJitter, 0.3);
});
// ---- Использование в перформансе -------------------------------------
// В живом концерте:
//
// 1. freezeOnly: установить \freezeGate в 1, чтобы "заморозить"
// текущий вход. phaseJitter через педаль добавляет "дыхание"
// frozen-спектра.
//
// 2. На тихой паузе (Letov молчит) — включить freeze, и звук
// "дышит" сам по себе. Снять freeze — вернуться к реальному
// входу.
//
// 3. Когда Letov играет активно — scatterOnly поверх, с
// разбросом позиции через .map(\positionJitter, <CC>.kr).
//
// 4. freezeAndScatter: сначала freeze, затем scatter по
// frozen-сигналу. Получается "зависший" звук, который
// разбивается на зёрна — главный спецэффект скетча.
//
// ---- Остановка -------------------------------------------------------
// Ndef(\freezeOnly).stop;
// Ndef(\scatterOnly).stop;
// Ndef(\freezeAndScatter).stop;
// ~fftBuf.free;
// ~ringBuf.free;
// ~inBus.free;
// ~freezeBus.free;
)