Strudel.cheatsheet

Livecoding музики у браузері · TidalCycles на JS · strudel.cc · клік по коду = копія

sound / note "mini-notation" .scale / .chord .lpf .room .gain Ctrl+Enter

Transport / темп

керування циклом · лейни
setcps(0.5)
cycles per second — темп; дефолт 0.5
setcps(0.5)
setcpm(120/4)
cycles per minute (зручно від BPM)
setcpm(120/4)
$: …
анонімний лейн; кілька $ грають разом (stack)
$: s("bd*4") $: s("~ hh")
d1: … / d2: …
іменований лейн; той самий label — останній виграє
d1: s("bd*4")
hush()
зупинити все; .hush() у ланцюзі глушить лейн
hush()

Sound / джерела

семпли, ноти, банки
sound("bd hh sd") / s
тригерить семпли за іменем
s("bd hh sd hh")
note("c e g")
висота нотами (літери + октава)
note("c4 e4 g4").s("piano")
n("0 2 4")
індекс: у семпл-набір або в scale
n("0 2 4").sound("casio")
":" — варіант
вибір семпла з набору (з 0)
s("casio:1 casio:2")
bank("RolandTR909")
драм-машина як банк семплів
s("bd sd").bank("RolandTR808")
samples("github:user/repo")
підвантажити зовнішній семпл-пак
samples('github:tidalcycles/dirt-samples')

Банки звуків

що реально є в цьому Flok-рантаймі
Звідки цей список: ця збірка Flok на старті викликає registerSynthSounds(), registerSoundfonts(), registerZZFXSounds() і підтягує шість семпл-банків з strudel.b-cdn.net: tidal-drum-machines, piano, Dirt-Samples, EmuSP12, vcsl, mridangam. Голі імена (bd, sd, hh…) беруться з EmuSP12 — це дефолтний кіт. Щоб взяти той самий звук з іншої машини — .bank("…").
Драм-машини · tidal-drum-machines · 71 банк, 683 семпли
назващо цеприклад
bdbass drum / бочкаs("bd*4")
sdsnare drum / малийs("bd sd")
rimrimshot, сухий клац по ободуs("bd rim sd rim")
cpclap / плескітs("bd ~ cp ~")
hhclosed hi-hat / закритий хетs("hh*8")
ohopen hi-hat / відкритий хетs("hh oh hh oh")
lt / mt / htтом low / mid / highs("lt mt ht ~")
crcrash-тарілкаs("cr ~ ~ ~").gain(.7)
rdride-тарілкаs("rd*8").gain(.5)
cbcowbell / ковбелs("~ cb ~ cb")
shshaker / шейкерs("sh*16").gain(.4)
tbtambourine / бубонs("~ tb ~ tb")
percрізна перкусія банкуs("perc*4").bank("RolandTR707")
miscусе інше, що не влізло в категоріїs("misc:2")
fxшумові вставки (є лише у кількох банках)s("fx").bank("RolandTR727")
Банки та їхні скорочення: повне ім’я — RolandTR808, RolandTR909, AkaiMPC60, LinnLM1, OberheimDMX, KorgMinipops, CasioRZ1, YamahaRX5… Ця збірка додатково викликає aliasBank(), тому працюють і короткі аліаси без вендора: .bank("TR808"), .bank("TR909"), .bank("MPC60"), .bank("LM1"), .bank("DMX"). Регістр не важливий.
Мелодика та синтез · registerSynthSounds()
назващо цеприклад
triangle / triтрикутник — дефолт, якщо s() не заданоnote("c e g").s("tri")
square / sqrмеандр, порожнистий і язикатийnote("c2 g2").s("square")
sawtooth / sawпилка — база для ліда й басаnote("c2").s("saw").lpf(600)
sine / sinсинус — чистий тон, добрий сабnote("c1").s("sine")
supersawрозстроєний стек пилок; .unison/.detunenote("c3").s("supersaw").detune(.4)
whiteбілий шум — рівний по всьому спектруs("white").decay(.05)
pinkрожевий шум — м’якший, -3 дБ/октs("pink*8").gain(.4)
brownкоричневий шум — глухий, низовийs("brown").lpf(400)
crackleтріск вінілу; керується .densitys("crackle").density(20)
Soundfonts · registerSoundfonts() · 129 GM-інструментів

Усі з префіксом gm_, граються через note(). Ключові родини:

назващо цеприклад
gm_piano · gm_epiano1 · gm_epiano2клавішні: акустика та електропіаноnote("c4 e4 g4").s("gm_epiano1")
gm_acoustic_bass · gm_synth_bass_1баси — акустичний і синтетичнийnote("c2 eb2 g2").s("gm_synth_bass_1")
gm_pad_warm · gm_pad_halo · gm_pad_sweepпади для підкладкиnote("c3 g3").s("gm_pad_warm").room(.8)
gm_lead_1_square · gm_lead_2_sawtoothліди GMnote("c4 g4").s("gm_lead_2_sawtooth")
gm_string_ensemble_1 · gm_violin · gm_celloструнніnote("c3 e3 g3").s("gm_string_ensemble_1")
gm_flute · gm_clarinet · gm_alto_saxдерев’яні духовіnote("c5 d5 e5").s("gm_flute")
gm_trumpet · gm_trombone · gm_brass_sectionмідні духовіnote("c4 e4").s("gm_brass_section")
gm_marimba · gm_vibraphone · gm_kalimbaмелодична перкусіяnote("c4 e4 g4 b4").s("gm_kalimba")
gm_church_organ · gm_drawbar_organорганиnote("c3 g3 c4").s("gm_church_organ")
gm_fx_atmosphere · gm_fx_crystal · gm_fx_rainFX-текстури GMnote("c4").s("gm_fx_crystal").slow(2)
gm_sitar · gm_koto · gm_banjo · gm_shakuhachiетнічніnote("c4 d4 f4").s("gm_koto")
ZZFX · registerZZFXSounds() · 8-бітний чарактер
назващо цеприклад
zzfxZZFX-двигун 8-біт саунд-ефектівs("zzfx").note("c3")
z_sineZZFX-синусnote("c3 e3").s("z_sine")
z_sawtoothZZFX-пилкаnote("c2").s("z_sawtooth")
z_triangleZZFX-трикутникnote("c3").s("z_triangle")
z_squareZZFX-меандрnote("c3").s("z_square")
z_tanтангенс — брудний, різкийnote("c2").s("z_tan")
z_noiseZZFX-шумs("z_noise").decay(.1)
Додаткові банки, які вантажить саме ця збірка
назващо цеприклад
pianoмультисемпльоване акустичне піано (окремий банк)note("c4 e4 g4").s("piano")
casioDirt-Samples: лоуфай Casio, 3 семплиs("casio:0 casio:1")
crowDirt-Samples: крики воронs("crow:1")
eastDirt-Samples: східна перкусіяs("east*4")
insectDirt-Samples: комахиs("insect:0")
jazzDirt-Samples: джазовий китs("jazz:2 jazz:4")
metalDirt-Samples: металевий брязкітs("metal*8").gain(.4)
numbersDirt-Samples: озвучені цифри 0–8s("numbers:3")
spaceDirt-Samples: космічні текстуриs("space:5").slow(2)
windDirt-Samples: вітерs("wind").slow(4)
vcslVersilian CSL — 128 імен: agogo, bongo, cajon, cabasa, balafon, anvil, belltrees("bongo*4")
mridangam_*13 штрихів індійського барабана: _dhi _dhin _dhum _ka _ki _na _nam _ta _tha _thom _ardha _chaapu _gumkis("mridangam_ta mridangam_dhi")

Як подивитись список у рантаймі

без вгадувань
У Flok немає вкладки sounds, як на strudel.cc. Але soundMap є в скоупі Strudel-панелі — отже, список можна вивести в консоль браузера (F12) прямо з коду.
// усі імена звуків, алфавітно (дивись у консоль)
console.log(Object.keys(soundMap.get()).sort())
// скільки всього й тільки банки TR-серії
const all = Object.keys(soundMap.get()) console.log(all.length) console.log(all.filter(n => n.startsWith("rolandtr")))
// чи є конкретний звук
console.log(!!soundMap.get()["gm_pad_warm"])

Свої семпли

samples() · github · strudel.json · власний CDN
// цілий репозиторій з GitHub (бере strudel.json у корені)
await samples('github:tidalcycles/dirt-samples') $: s("bd*4, ~ cp")
// пряме посилання на strudel.json
await samples('https://mandrock-files.duckdns.org/flok/media/strudel.json')
// об’єкт + базовий URL другим аргументом
await samples({ kick: 'kick.wav', snare: ['sn1.wav', 'sn2.wav'] }, 'https://mandrock-files.duckdns.org/flok/media/') $: s("kick snare:1")
// свій кіт як банк — через префікс імені
await samples({ mandrock_bd: 'bd.wav', mandrock_sd: 'sd.wav' }, 'https://mandrock-files.duckdns.org/flok/media/') $: s("bd sd").bank("mandrock")
Про свій хостинг: клади файли в /root/cdn/flok/media/ на VPS — вони віддаються з https://mandrock-files.duckdns.org/flok/media/…. Семпли — це fetch + decodeAudioData, тож для чужого орігіну (а Flok живе на flok.mandrock-files.duckdns.org, це інший орігін) потрібен заголовок Access-Control-Allow-Origin. Для /flok/media/ він уже налаштований — nginx віддає Access-Control-Allow-Origin: *, тож семпли звідти підхоплюються без бубна. Кластимеш деінде на /root/cdn/ — заголовка не буде, і decodeAudioData впаде: див. hydra.html → Відео з YouTube / Vimeo, там той самий сюжет із боку відео.
strudel.cc/learn/samples →

Mini-notation

мова всередині лапок — серце Strudel
"a b c"
секвенція — вміщується в 1 цикл
s("bd hh sd hh")
* / /
швидше (повтор) / повільніше
s("bd*2 hh/2")
[ ]
під-поділ: група в одному кроці
s("bd [hh hh] sd hh")
< >
чергування — по одному елементу за цикл
s("bd <hh sd>")
, (кома)
stack — паралельні шари в одних лапках
s("bd*4, ~ hh ~ hh")
! / @
!n — реплікація; @n — подовження (вага)
s("bd!3 sd@2")
? — ймовірність
? = 50% пропуск; ?0.3 — 30%
s("hh*8?")
~ — пауза
тиша на крок
s("bd ~ sd ~")
(k,n,off) — euclid
евклідів ритм: k ударів на n кроків
s("bd(3,8)")
"0 .. 7"
скорочення діапазону
n("0 .. 7").scale("C:minor")

Pitch / гармонія

ноти, лади, акорди
note("c4 e4 g4")
абсолютні ноти
note("c4 eb4 g4").s("sawtooth")
.scale("D4:minor")
n() інтерпретується як ступені ладу
n("0 2 4 6").scale("D4:dorian")
chord("<C^7 Dm9>").voicing()
акордові символи → голосоведення
chord("<C^7 Dm9>").voicing().s("piano")
.add(note(12))
транспозиція (тут +октава)
note("c e g").add(note("<0 7>"))
.dict('ireal')
словник акордів для chord()
chord("Cm9").dict('ireal').voicing()

Structure / комбінування

шари, послідовності, ритм-маски
stack(a, b, c)
накласти патерни один на одного
stack(s("bd*4"), s("~ hh ~ hh"))
seq / cat(a, b)
seq — за цикл; cat — по циклу на кожен
cat(s("bd*4"), s("sd*4"))
.struct("x ~ x x")
ритм-маска: де x — грає
note("c").struct("x ~ x x")
.mask("<1 0>")
гейт увімк/вимк по патерну
s("hh*8").mask("<1 1 1 0>")
.segment(4)
семплувати неперервний сигнал у N подій
saw.segment(8).note()

Transforms

функції над патерном
.fast(2) / .slow(2)
пришвидшити / сповільнити
s("bd sd").fast(2)
.rev
розвернути патерн задом наперед
n("0 1 2 3").rev
.jux(rev)
функція лише в правий канал; juxBy(.5,fn)
s("bd sd hh cp").jux(rev)
.iter(4)
зсувати старт патерна щоцикл
n("0 1 2 3").iter(4)
.every(3, fast(2))
умовно кожні n циклів
s("bd*4").every(3, fast(2))
.off(1/8, x=>x.speed(2))
зсунута копія поверх оригіналу
s("bd cp").off(1/8, x=>x.speed(2))
.ply(2)
повторити кожну подію n разів
s("bd sd").ply(2)
.chop(4) / .striate(4)
гранулярна нарізка семпла
s("break").chop(8)
.sometimesBy(.4, fn)
застосувати з імовірністю; rarely/often
n("0 2 4").sometimesBy(.3, x=>x.add(12))
.degradeBy(.3)
випадково викидати частину подій
s("hh*16").degradeBy(.2)
.chunk(4, fn)
застосувати fn до рухомого фрагмента
n("0 .. 7").chunk(4, x=>x.add(12))
.superimpose(fn)
накласти оброблену копію поверх
note("c e g").superimpose(x=>x.add(7))

Effects

звукова обробка · single-use
.gain(.8) / .pan(.5)
гучність / стерео-панорама
s("hh*8").gain("[.25 1]*4")
.lpf / .hpf / .bpf
фільтри (cutoff Гц); .lpq — резонанс
note("c2 e2 g2").lpf(800).lpq(8)
.room(.5) / .size
реверб та його розмір
s("cp").room(.6).size(4)
.delay(".5:.125:.6")
затримка = time:feedback:mix
s("rim").delay(".5:.125:.6")
.crush(4)
біткрашер 1..16 (менше = грубіше)
s("bd*4").crush("<16 8 4>")
.distort("2:.5") / .shape
дисторшн / вейв-шейпінг (обережно, гучно)
s("bd").distort("2:.4")
.vowel("<a e i o>")
формантний (голосний) фільтр
note("c e g").s("saw").vowel("<a e>")
.speed(2)
швидкість відтворення семпла (питч)
s("break").speed("<1 1.5 2>")
.attack .decay .sustain .release
ADSR-обвідна амплітуди
note("c e g").attack(.01).release(.3)
.lpenv(4) .lpa .lpd .lps
обвідна фільтра (є hp*/bp* аналоги)
note("c2").lpf(300).lpenv(4).lpa(.02)

Signals / модуляція

неперервні джерела для будь-якого параметра
sine · saw · tri · square · cosine
неперервні осцилятори 0..1
note("c e g").lpf(sine.range(200,2000))
perlin · rand · irand(n)
плавний шум / рандом / цілий рандом
s("hh*8").gain(rand.range(.3,1))
.range(lo, hi)
перемапити сигнал у діапазон
saw.range(100,1000)
signal.slow(4) / .fast
розтягнути/стиснути сигнал у часі
sine.slow(8).range(0,1)
slider(500,100,2000)
UI-повзунок прямо в коді
note("c").lpf(slider(500,100,2000))

Готові патчі

встав і запусти
// базовий біт
setcps(0.5) $: s("bd*4, ~ cp ~ cp") $: s("hh*8").gain("[.25 1]*4")
// кислотна лінія
note("<c2 eb2 g2 bb1>*8") .s("sawtooth") .lpf(sine.range(300,1800).slow(4)) .lpq(8).distort("1.5:.4")
// мелодія по ладу
n("0 <2 4> 5 7 [9 7] 4 2 0") .scale("D4:minor") .s("triangle").room(.4) .jux(rev)
// евклід + офсет
$: s("bd(3,8)").bank("RolandTR909") $: s("hh(7,8)").gain(.5) $: s("cp(2,8,1)").room(.3)
// акордова подушка
chord("<Cm9 Ab^7 Eb^7 G7b9>/2") .dict('ireal').voicing() .s("sawtooth").lpf(700) .lpa(.5).lpenv(4).room(.6)
// пробний ambient-скелет
note("c3 g3 e4 b4".slow(2)) .s("gm_pad_warm") .room(.8).size(6) .lpf(perlin.range(400,1600).slow(6))

flok.cc · Hydra ↔ Strudel

спільний livecoding-джем звук + візуал
flok.cc — браузерний спільний редактор із панелей-таргетів (strudel, hydra, p5, sclang, mercury…), до 8 слотів. Таргет міняється у лівому верхньому куті слота; Command → Add Pane / Configure (таргети через кому, напр. strudel, hydra). Нічого ставити не треба.
// Hydra ВСЕРЕДИНІ Strudel — initHydra() зверху
await initHydra() osc(10, 0.1, 1.2).kaleid(4).out() $: s("bd*4, ~ cp").bank("RolandTR909")
// аудіо-реактив з мікрофона (detectAudio)
await initHydra({ detectAudio: true }) osc(30) .modulate(noise(3), ()=>a.fft[0]) .out()
// H() — патерн Strudel як вхід у Hydra (в такт)
await initHydra() osc(20) .rotate(H("<0 0.5 1>")) .out()
// feedStrudel — піаноролл/scope як відео-джерело s0
await initHydra({ feedStrudel: true }) src(s0).kaleid(4).modulate(noise(2)).out() $: note("c e g b").s("piano")
// flok: у hydra-панелі P() бере mini-патерн Strudel
// hydra-таргет у flok, синхронно з часом Strudel shape(4) .scale(() => P("<1 2 3>")) .out()
// p5.js теж працює в hydra-таргеті flok
// hydra-таргет p5.background(0) p5.fill(255) p5.circle(p5.width/2, p5.height/2, 200)
Нюанс fft у flok: через те, як налаштована analyze-нода, після того як Hydra почала брати a.fft, кожну Strudel-панель треба один раз переевалити вручну (Ctrl+Enter) — інакше дані спектра не підхопляться.
strudel.cc/learn/hydra →

Гарячі клавіші

REPL strudel.cc / flok
Ctrl+Enterзапустити / оновити
Ctrl+.зупинити (hush)
Ctrl+Shift+.стоп (альт.)
Ctrl+/закоментувати рядок
Shift+Enterновий рядок без запуску