Skip to content

Generative

Miika Alonen edited this page Dec 10, 2022 · 28 revisions

Generative syntax

Generative syntax is a way of defining aleatory melodies (which is a fancy word for random or semirandom melodies). ? is a random note, (x,y) is a random note between x and y, [a,b c,d]* will randomnly pick one from the list, for example:

z1 "[: q ? ? ? 0 ? ? ? (1,2) [6,7] (1,2) [6,7] (1,2) [6,7] (1,2) [6,[7 2,8,9 3,11]] :]"

Random syntax can also be nested which will eventually melt your brain:

z1 "([1,4,5,3],[(6,7),(6,[8,10,11]),(6,[9,10,39])])" # This is a loop of a single note

As you might have noticed, everything can be nested, meaning that you can put random numbers inside your random numbers (yo dawg) and create pretty crazy (and mostly completely useless) combinations:

zplay "(1,(2,[4,5,(6,9),10]))" # Plays a random note 1 between 2 between 4 or 5 or 6 between 9 ... and so on.

To repeat the process again (like the teletubbies would want) you can use repeated sets like so:

zplay "(: (1,(2,[4,5,(6,9),10])) :4)"

Lists are also pretty nifty for doing all sorts of things, for example:

z1 "q (0 1 2 3)+(1,3)" # Adding random stuff to set

zplay "q (0 1 2 3)+(-1 1 -1 1)" # Cartesian sum

zplay "q (1 2 3 4)*(-1 1 -1 1)" # Cartesian product

z1 "q (1 2 3 4)+[1 3, 2 2, 3 1]" # Cartesian sum with a random set

zplay "(1 2 3)<>(4 5 3)" # Zip two lists

Random note

Use ? for random number between 0-11

z1 "q 0 ? 1 ? 2 ? 3 ?"

Random decimal (Between 1.0 and 0.01)

z1 "% ? % ?" # Can be used as random duration
z1 "{%>0.5?0:4} 3" # or in conditional statements

Totally random note lengths would sound well ... totally random, but since we often long for some sort of continuity, the random durations generated with the % are random within the measure length, which can be altered using measure_length parameter. By default measure length is 1.0.

Example of two loops using random durations and different measure length:


z0 "h 2 % [1 4 3, 2 4, 3 2 4 6, 1]| q 2 3 % 9 3", synth: :dtri, release: 0.1, attack: 0.1, sustain: 0.1, cutoff: 20

z1 "h 3 2 % 3 [? 3, 3] | h 4 % ? ? 3", synth: :bass_foundation, octave: -2, measure_length: 2.0

It is completely optional to use the measure length. If the measures are full, all random relative durations will be 0.

Random integer between x and y

Random syntax (x,y) creates random integers between x and y.

zplay "(1,5)" # Random number between 1 and 5

zplay "(: (1,4) :4)" # Random number between 1 and 4 - 4 times

zplay "([2,4,6],[7,8,12])" # Generates random number between numbers picked from a spesific set of numbers

Random decimal

Random decimals can be used to define random note lengths or parameter values:

# Random note length between 0.1 and 0.25
z1 "(0.1,0.25) 1"
# Set release to random between 0.1-2.0 and note length between 0.1-1.0
z2 "R<(0.1,2.0)> (0.1,1.0) 0..3"
# Random slide length
z3 "q ~<(0.01,1.0)>0123"

Lists

Ziffers lists are ordered sequences of pitch classes denoted using ( ) characters. Lists can be created, modified and combined with other lists using operators.

List operators

Set operations include all basic computational operators including *, +, -, **, /, ^, %, |, &, <<, >>. See [ruby's operators](https://www.tutorialspoint.com/ruby/ruby_operators.htm for more details.

Examples:

zplay "(0 1 2 3)+3" # Add 3 to each in a set

zplay "(0 1 2 3)+(1,6)" # Add a random number to each in a set

zplay "(0 1 2 3)<<(0,3)" # Add a random number to each in a set

zplay "(0 1 2 3)<<(1,3)" # Do a binary left switch using random number

zplay "(1 2 3)+(2 4 6)" # Do cartesian sum with two sets

zplay "((1 2 3)*(2 4 6))%7" # Do cartesian product with two sets and use mod 7

Additionally there are support for different array operations, such as: <> zip(ish) <*> pitch multiplication <+> product <&> intersection <|> union <-> difference <1-n> interpolation

Examples:

zplay "(1 2 3)<>(3 6 3 2)" # Zip to sets, behaves like a ring if there are odd number of elements
zplay "(q e)<>(3 6 3 2)" # Also works for note lengths or octaves etc.
zplay "(1 2 3)<+>(3 6 3)" # Product of two sets
zplay "(1 2 3)<*>(3 6 3)" # Two sets interval multiplication
zplay "(1 2 3)<&>(3 6 3)" # Intersection of two sets
zplay "(1 2 3)<|>(3 6 3)" # Union of two sets
zplay "(1 2 3)<->(3 6 3)" # Difference of two sets
zplay "(1 3 2 4)<4>(1 -2 3)" # Shorthand for scale interpolation

List methods

! Unique: Create unique set

& Combine: Interpret integers as chords

$ Separate: Interpret integers as sequences

zplay "(0 1 1 2)!" # ! Unique set: 0 1 2

z1 "q ((-3..4){(3x+2)(3x^3)})$" # $ Splits generated integers 1432 -> 1 4 3 2

z1 "q ((-3..4){(3x+2)(3x^3)})&" # $ Splits generated integers 1432 -> 1 4 3 2

z1 "q ((-3..4){(3x+2)(3x^3)})$!" # Unique set and reflect can be combined

zplay "((1000,5000))&" # Generates a chord: 1243

zplay "(1,30) ((1,30))$ ((1,30))&" # Generates: {23} 2 3 14"

zplay "(((10,50),(100,1000)))&" # Generates random chord between random numbers

zplay "(((10,50),(100,1000)))$" # Generates random list from random numbers

zplay "10..12" # Sequence: {10} {11} {12}

zplay "(10..12)&" # Sequence: 10 11 12

zplay "(10..12)$" # Sequence: 1 0 1 1 1 2

zplay "q (100..200+8)&" # Sequence of chords: 100 108 116 124 132 140 148 156 164 172 180 188 196

List transformations

In addition to list methods some transformations can also be called inside Ziffers syntax (For full list of transformations see Transformations-page).

Shorthands defined for some transformations:

Reflect set (aka reflect) Mirror set (aka mirror) Inverse pcs (aka inverse) Rotate set (aka rotate) Stretch (aka stretch) Swap (aka swap) Deal (aka deal)

(index,items)

Get permutation with index, second parameter optional
z1 "q (1 2 3 4)<r>" # Reflect set. Nice for loops
z1 "q (1 2 3 4)<m>" # Similar to reflect, but mirrors completely
z1 "q (1 2 3 4)<i>" # Inverses pcs
z1 "q (1 2 3 4)<i>(3)" # Inverse transpose by 3
z1 "(q 0 1 2 3)<i>(0 -2 5 6)" # Cycle inversions
z1 "q (1 2 3 4)<rot>" # Rotates set by 1
z1 "q (1 2 3 4)<rot>((1,4))" # Rotates set randomly
z1 "q (1 2 3 4)<d>((1,4))" # Deal randomly
z1 "q (1 2 3 4)<p>(3,5)" # 3 note permutation index 5
z1 "q (1 2 3 4)<p>(3,(0,5))" # 3 note permutation from random index
z1 "(q 0 1 2 3)<p>(0 2 5 6)" # Cycle different permutations
z1 "(i iva)@(q 0 1 2)<p>((0,7),2)" # Create arpeggios with random permutations size 2

Sequences

Create sequences of pitches using s..e notation. Alternatively sequences can also be parsed as splitted integers s;;e or chords s::e.

zplay "1..7" # Sequence: 1 2 3 4 5 6 7

zplay "q 0..(1,7)" # Random sequence from 0 to random

Arithmetic sequences

zplay "0..6+2" # Sequence: 0 2 4 6

zplay "0..6+4" # Sequence: 0 4

zplay "0..6*4" # Sequence: 0 4 8 12 16 20

zplay "0..4**2" # Geometric sequence: 1 2 4 8

zplay "-3..4*3" # Sequence: -3 0 3 6

# Random arithmetic sequences
z1 "a (0..3*(1,7)) h ((0,2)..2*(1,9)) a (0..3*(-7,-5))", synth: :piano, key: :c4, scale: :minor

# Real fun starts when the generated sequences are summed together as sets (sometimes called Slonimsky sets).
zplay "q (0..4*3)+(2..-5*2)"

Sequences, random numbers and random picks can be modified as lists using ( ) syntax:

zplay "(0..7)" # Populate list

zplay "(1..7)~" # Sequence in random order: "2135764"

zplay "(1..7)~3" # Sequence from 1 to 7 take random 3: "152"

zplay "(1..9+2)?3" # Can be combined with take random

zplay "(1 3 4 6 7)~2" # Take different random 2: "3 6"

zplay "(1 3 4 6 7)?2" # Take random 2: "3 3"

z1 "(q. e e q)<>(: (1,4)..[5,6,7] :3)~" # Create random range between randomized values and add note lengths by zipping

Assign values to variables

Values can be assigned to control characters. This is a way of creating repeating randomized patterns and shortening the notation. Assigned values can also be reused within new lists.

zplay "A=(1..3 {(1,3)+2}) q A A A" # Repeats the assigned list
zplay "A=(3 2 (1,5) 3) B=(? (1,3) 3) q A B A B" # Repeat two separate lists
z1 "A=(1,4) B=? C=[4,5,6] A B A C B A B C B A" # Assign different random values and play as pattern
z1 "q A=(0 2 1 4) B=(5 4 3 2) (A B)+[1,2,3,4]" # Assign lists and reuse in lists for arithmetic operations

Access and reverse list using index or ranges

List values can be accessed using index values or ranges:

zplay "q (1..3)[2]" # Play value from index 2
zplay "q (1..3)[1001]" # Wait what? Lists act like rings!
zplay "q (1..3)~[2,6]" # Randomize and take 6 starting from index 2
zplay "q (1..6)[(1,4)]" # Play value from random index
zplay "q (0..7)[0..4]" # Populate list from 0 to 7 but only take first 4
zplay "q (0..7)[3..]" # Take from index 3 to the end
zplay "q (0..7)[..3]" # Take from end to the index 3
zplay "q (0..7)[-2..3]" # Take from -2 to 3
zplay "q A=(0..4) A (A)[0..3] (A)[..0]" # Assing list to value and modify it multiple times
z1 "q (0..7)[(-2,3)..5]" # Or use any other random syntax inside accessors which makes things more interesting

Same works for ziffers "native" arrays also:

a = zparse "q 0..6"
zplay a+a[0..4]+a[0..3]+a[..1]

Lists and set theory

As you might have already noticed Ziffer's lists are not sets in a a set theory sense. If you really want to create a set (meaning no same pitch classes), you can use different set operators.

Note lengths can be used with the traditional set operations, but two pitch classes with different length are considered as equal, for example:

zplay "(1 2 q3)<|>(2 6 e3)" # Union of two sets with note lengths

To change the behaviour use Ziffers.set_eql_keys method to define which keys are compared, for example:

Ziffers.set_eql_keys([:pc,:duration]) # Currently only global definition possible

zplay "(1 2 q3)<|>(2 6 e3)" # Union of two sets with different result

Alternative you can use to_pc_set method to get rid of extra notes from the set:

not_a_set = zparse "q (0 1 2 3 0 1 2 3)*(1 3 2)", scale: :chromatic
pc_set = not_a_set.to_pc_set # Transform to pitch class set
zplay pc_set

List note lengths

Use zip to set note lengths for the list:

zplay "(q e)<>(1..4)" # With note lengths: "q 2 e 2 q 3 e 4"

zplay "(q e)<>(1 2 3 4 5)" # With note lengths: "q 1 e 2 q 3 e 4 q 5"

zplay "(q e q)<>(1..9+2)+1*2" # Note lengths can be combined with any operations in a set

Scales for lists

Scales can be changed within the lists

zplay "<minor>(q 0 2 3 4 5 1) <major>(q 0 2 3 4 5 1)" # Set scales for different lists
zplay "<a>(q 0 4 3 2) <c>(q 0 4 3 2) <f>(q 0 4 3 2)" # Use shorthands for modes
zplay "L<mixolydian> 0 2 3 4 <minor>3 4 2 <major>(2 3 4 9)" # Too many ways to do the same thing ...

Looping lists

(: :) = do n-times

zplay "(: 1..3 :)" # Create sequence 2 times

zplay "(: 1..3 (4,6) :3)~ # Create sequence add random number and randomize order 3 times

List functions

Lists can be transformed using syntax inspired by polynomial functions. Each term of the polynomial function is added at the end of the set, for example: (n..n){(2x^2)(x+2)}. The x in the function (0 1 2){2x} is evaluated using numbers in the list (0 1 2). The n in the function (3 2 1){n*x} is the index in the list and would be evaluated as (0*3 1*2 2*1).

Examples:

zplay "q (0 1 2){2x-1}" # Polynomial with one term
zplay "q (0 1 2){(2x-1)(2x^2-4)}" # Polynomial with two terms
zplay "q (0 1 2){(2x)-(3x)}" # Polynomial sets substracted
z1 "q (0 1 2){((1,2)x-1)(x-[4,1,-1])}" # Random polynomial functions
z1 "q ( (1,4) ){(2x-1)+(2x-(4,6))+(x-(1,4))}" # Multiple additive polynomial functions
zplay "(0..10){x-(1,7)}" # Random polynomial function applied to range
z1 "(3..0){(n*x)+n}" # Function using the list index

Conditional functions

Set's can also be transformed conditionally using alternating functions

Examples:

zplay "((1,5)){x<2?4}" # Simple function returning value based on condition
zplay "(: (1,6) :5){x>3?x+10}" # Simple function doing addition based on condition
zplay "(0..20){x>7?x-(1,7):x+2}" # Minus random values based on condition
zplay "(0..10){x>3?x-(1,4):x+(1,4)}" # Minus or add ...
z1 "q(1..10){x%10==0?(0,3):{x%5==0?(2,4):{x%3==0?(6,7):(3,5)}}}" # Multiple recursive conditional statements in function

Conditional statements

Conditional statements can be used to create random melodies. Conditions can use %, (1,5) or [1,3,4] syntax to compare the result to some value.

Examples:

z1 "1 {%>0.5? e 3 1}" # Simple conditional statement returning one value or nothing
z1 "{(1,4)>3?0:3}" # If random number is > 3 then 0 else 3
z1 "2 {[1,3,2]==1?4:5}" # Random values based on comparison on random array
z1 "{%<0.25? q 0 2 4 3 : q 2 4 e 3 5 3 1}" # Alternate patterns
z1 "(: {%>0.5?0:2} :10)" # Repeat statements -> "0 2 0 0 0 2 0 2"
z1 "{%>0.5 ? q 3 0 : {%<0.25 ? e 6 1 : e 0 5}}" # Loop recursive conditional statements
z1 "{%<0.25? q 0 2 4 3 : q (0 1 2 3){x%2==0?x+1:x-2} }" # Conditional function inside conditional

Post note

Ok ... thats it. Now go crazy with the combinations ...

Maybe something minorly disturbing:

z1 "q ((1..4*(2,5))%6*(1 -2 -1 (-1,2) -2 3))<qee>", scale: :minor
z2 "(036 468)*[1,-2,3,4]", scale: :minor

Or some atonal mayhem:

z1 "(q e e)<>((((1,10)..(15,25))~*([2 3 1, -1 2 3, 5 3 2]))%24)", scale: :chromatic
z2 "q (0 31 2 46)*(2 [-1,-2,3] (3,4) (1,4))", scale: :chromatic

This generative syntax / live code golfing thing is still evolving so leave feedback or post issues if you run into some problems.