( // Пример: 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, .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, .kr). // // 4. freezeAndScatter: сначала freeze, затем scatter по // frozen-сигналу. Получается "зависший" звук, который // разбивается на зёрна — главный спецэффект скетча. // // ---- Остановка ------------------------------------------------------- // Ndef(\freezeOnly).stop; // Ndef(\scatterOnly).stop; // Ndef(\freezeAndScatter).stop; // ~fftBuf.free; // ~ringBuf.free; // ~inBus.free; // ~freezeBus.free; )