Files
Cagire/docs/control_flow.md
2026-02-16 01:22:40 +01:00

3.9 KiB

Control Flow

A drum pattern that plays the same sound on every step is not very interesting. You want kicks on the downbeats, snares on the backbeats, hats filling the gaps. Control flow is how you make decisions inside a step.

if / else / then

The simplest branch. Push a condition, then if:

step 4 mod 0 = if kick s . then

Every fourth step gets a kick. The rest do nothing. Add else for a two-way split:

step 2 mod 0 = if
  kick s 0.8 gain .
else
  hat s 0.3 gain .
then

These are compiler syntax -- you won't find them in the dictionary. Think nothing of it.

? and !?

When you already have a quotation, ? executes it if the condition is truthy:

{ snare s . } coin ?

!? is the opposite -- executes when falsy:

{ hat s 0.2 gain . } coin !?

These pair well with chance, prob, and the other probability words:

{ rim s . } fill ?              ;; rim only during fills
{ 0.5 verb } 0.3 chance ?      ;; occasional reverb wash

ifelse

Two quotations, one condition. The true branch comes first:

{ kick s . } { hat s . } step 2 mod 0 = ifelse

Reads naturally: "kick or hat, depending on whether it's an even step."

{ c3 note } { c4 note } coin ifelse
saw s 0.6 gain .                ;; bass or lead, coin flip

pick

Choose the nth option from a list of quotations:

{ kick s . } { snare s . } { hat s . } step 3 mod pick

Step 0 plays kick, step 1 plays snare, step 2 plays hat. The index is 0-based.

{ c4 } { e4 } { g4 } { b4 } step 4 mod pick
note sine s 0.5 decay .

Four notes cycling through a major seventh chord, one per step.

case / of / endof / endcase

For matching a value against several options. Cleaner than a chain of ifs when you have more than two branches:

step 8 mod case
  0 of kick s . endof
  4 of snare s . endof
endcase

Steps 0 and 4 get sounds. Everything else falls through to endcase and nothing happens.

A fuller pattern:

step 8 mod case
  0 of kick s 0.9 gain .    endof
  2 of hat s 0.3 gain .     endof
  4 of snare s 0.7 gain .   endof
  6 of hat s 0.3 gain .     endof
  hat s 0.15 gain .
endcase

The last line before endcase is the default -- it runs when no of matched. Here it gives unmatched steps a ghost hat.

The of value can be any expression:

step 16 mod case
  0         of kick s .               endof
  3 1 +     of snare s .              endof
  2 4 *     of kick s . snare s .     endof
endcase

times

Repeat a quotation n times. @i holds the current iteration (starting from 0):

4 { @i 4 / at hat s . } times   ;; four hats, evenly spaced

Build chords:

3 { c4 @i 4 * + note } times
sine s 0.4 gain 0.5 verb .      ;; c4, e4, g#4

Subdivide and accent:

8 {
  @i 8 / at
  @i 4 mod 0 = if 0.7 else 0.2 then gain
  hat s .
} times

Eight hats per step. Every fourth one louder.

Putting It Together

A basic drum pattern using case:

step 8 mod case
  0 of kick s .                     endof
  2 of { hat s . } often            endof
  4 of snare s .                    endof
  6 of { rim s . } sometimes        endof
  { hat s 0.15 gain . } coin ?
endcase

Kicks and snares on the strong beats. Hats and rims show up probabilistically. The default sprinkles ghost hats.

A melodic step that picks a scale degree and adds micro-timing:

{ c4 } { d4 } { e4 } { g4 } { a4 } step 5 mod pick
note

step 3 mod 0 = if
  0 0.33 0.66 at          ;; triplet feel on every third step
then

saw s 0.4 gain 0.3 decay 0.2 verb .

A times loop paired with case for a drum machine in one step:

4 {
  @i case
    0 of kick s .                  endof
    1 of hat s 0.3 gain .          endof
    2 of snare s .                 endof
    3 of { rim s . } 0.5 chance    endof
  endcase
  @i 4 / at
} times

Four voices, four sub-positions, one step.