Skip to content

Enumerables

Miika Alonen edited this page Jun 24, 2023 · 14 revisions

Enumerables

Enumberables are something that can be calculated and played on the fly (which is more fun). Ziffers has some built in methods for calculating sequences such as continued fractions of Pi or triangular sums.

You can also create your own number sequences, so if you happen to know a mathematic formula for creating the perfect melody it is relatively easy to try it out :)

Playing number sequences

Number sequences are any number of numbers that creates a finite or infinite sequence.

Consider a number 369420. This number can be played sequantially as a melody or concurrently as a chord. It can also be played in any key or scale and added some rhythmic variation based on taste or some mathematical relations between the numbers. By default integers are played sequantially. To play integer as a chord use parse_chords: true parameter.

Then consider a sequence of numbers 6 18 54 162 486 1458 4374. These numbers again could be played in different ways. These sets of numbers can also be transformed, added, divided, etc. and the base of the numbers can be changed for example from 10 to 6. Number sequences can also be considered as a string that can be modified with rules.

Examples of combining rhythm with the number sequences:

zplay pi, rhythm: "s s e e"*2+"q e e"*2+"e s s e e"*2 # This will take some time to finish...
zplay reverse_sum(589865754), rhythm: {0=>"e", 1=>"s",2=>"s",3=>"q",4=>"s",5=>"e",6=>"e", 7=>"s",8=>"e",9=>"s"}

Enumerables can also be used as parallel loops. Here euler meets pi:

r_map = {0=>"e", 1=>"s",2=>"s",3=>"q",4=>"s",5=>"e",6=>"e", 7=>"s",8=>"e",9=>"s"}
z1 euler, rhythm: r_map , key: :c3, synth: :chiplead # Synth options can be modified on the fly
z2 pi, rhythm: r_map, key: :c3, synth: :chipbass

Or some very mathy jazz:

z1 bruijn, duration: ->(i){i%rrand_i(3,6)==0 ? 0.0625 : 0.125}, scale: :blues_major, synth: :tri, amp: 0.5, release: 0.1, attack: 0.1, decay: 0.1
z2 bruijn_walk, duration: 0.125, key: :c3, synth: :fm

Creating new enumerables

Custom enumerables can be created using Ruby's enumerations.

There couple of ways of creating enumerations. Using ranges and lazy enumerations:

# To infinity and beyond!
sums = (1..Float::INFINITY).lazy.collect {|x| (x*x)}
zplay sums, parse_chords: false, duration: 0.125

Or by creating enumerations using Enumeration class:

def awesomeness(n=20,variance=3)
  mem = [rrand_i(0,7)]
  Enumerator.new do |y|
    until mem.length>=n do
      mem = mem.zip(mem.map{|n| n+rrand_i(-variance, variance)}).flatten
      y << mem.join(" ")
    end
    print "Reached the end of enum!"
  end
end

# Loop cycles the finite enumerations
z1 awesomeness, rhythm: "e s s", release: 0.1

Integer sequences

There are many beautiful and intrigue integer sequences with musical properties out there. Best place to search for them is OEIS encyclopedia using hear keyword.

All sequence generators are implemented as ruby enumerators, which can be played directly using zplay or looped in various ways, for example:

print recaman.take(10)

e = recaman
10.times do
  print e.next
end

Here is a list of sequences that are implemented as enumerations:

fibonacci: Fibonacci

Sequence in OEIS: A000045

# Play fibonacci numbers as chords
zplay fibonacci, duration: 0.25

primes: Prime numbers

Sequence in OEIS: A000040

# Play primes as chords until 500
zplay primes(500), duration: 0.25

# Play primes as chords until memory runs out
zplay primes, duration: 0.25

Bruijn sequence

Cool self similar sequence. Sequence in OEIS: https://oeis.org/A000695

n = bruijn.take(30)
v = n.map {|n| n.to_s.split("") }.join(" ")
zplay v, duration: 0.25, synth: :piano

Bruijn walk sequence

Nice walking bass sequence based on bruijn. Sequence in OEIS: https://oeis.org/A059905

n = bruijn_walk.take(30)
v = n.map {|n| n.to_s.split("") }.join(" ")
zplay v, duration: 0.25, synth: :piano, octave: -2

Morse Thue sequence

Morse Thue sequence is propably the mostly used sequence in fractal music.

Sequence in OEIS: https://oeis.org/A010060

print thue_morse.take(100).to_a

Morse Thue sequence can be used to generate number sequences in any base (only defaults to binary):

melody =  thue_morse(6).take(100).to_a
rhythm = thue_morse(3).take(16).to_a

zplay melody, synth: :piano, scale: :egyptian, rhythm: rhythm

Alternatively sequence can be counted in binary and modded to spesified range using second parameter:

mt_scale = scalenator(thue_morse.take(100).to_a)
mt_melody =  thue_morse(2,7)
mt_rhythm = thue_morse(4).take(16).to_a

zplay mt_melody, scale: mt_scale, rhythm: mt_rhythm, synth: :dark_ambience, ring: 0.9, room: tweak(:sine,2,20,10).mirror, reverb_time: 100

Well ... still sounds like crap but maybe you can make something interesting out of it.

Van Eck's sequence

Numberphile video about the sequence: https://www.youtube.com/watch?v=etMJxB-igrc&t=422s Sequence in OEIS: http://oeis.org/A181391

# Getting first 100 from the sequence
n = vanecks.take(100)
print n

Playing sequence directly with some rhythm mapping:

use_bpm 200
z1 vanecks, rhythm: "qee", synth: :piano

Inventory sequence

Video about the sequence in Numberphile: https://www.youtube.com/watch?v=rBU9E-ZOZAI Sequence in OEIS: http://oeis.org/A342585

# Getting first 100 from the sequence
n = inventory.take(100)
print n

Playing sequence directly with some rhythm mapping:

use_bpm 170
z1 inventory, rhythm: {0=>"s",1=>"e",2=>"e.",2=>"q"}, synth: :chiplead

recaman: Recaman

# Play recaman sequence
zplay recaman, duration: 0.25, synth: :hollow, release: 2.0

hoffQ: Hofstadter Q-sequence

Sequence in OEIS: A005185

zplay hoffQ, duration: 0.25, parse_chords: false, scale: :minor, rhythm: "eeqq"*2+"h q q"*2+"q e e q q"*2

sterns: Sterns sequence

Sequence in OEIS: A002487

zplay sterns, duration: 0.25, scale: :harmonic_minor

dress: Dress sequences

Sequence in OEIS: A001316

zplay dress, synth: :chipbass, key: :d3, scale: :minor_pentatonic, rhythm: "eeqh"

frac2: Remove all factors of 2 from n

Sequence in OEIS: A000265

zplay frac2, duration: 0.25

binrev(base=2): Reverse binary digits

Sequence in OEIS: A030101

Optional parameter base, default=2.

zplay binrev, duration: 0.25

reverse_sum(n,base=10): Reverse sum of n in base x

Reverse sum of some numbers creates symmetric patterns. Idea from MathPages.

Optional parameter base, default=10

zplay reverse_sum(313), synth: :fm, parse_chords: false, rhythm: "q q h"

collatz(n): Collatz sequence

Collatz conjecture will create a number sequence that will eventually lead to 1.

zplay (collatz 987654321), parse_chords: false, rhythm: "qqeeee"

triangular: Triangular numbers sequence

Sequence in OEIS: A000217

# Plays chords from triangular number sequence
zplay triangular, duration: 0.25

quadratic: Quadratic function

# Play values of y as chords
z1 quadratic(1,100,1,-10,20), duration: 0.2

aprog: Arithmetic progression

Create your own Arithemitc progression.

zplay aprog(1,4,5), duration: 0.25
print "Done!"

# This one goes forever
zplay aprog(1,4), duration: 0.25

gprog: Geometric progression

Create your own Geometric progression.

zplay gprog(3,4,5), duration: 0.25
print "Done!"

# This one goes forever
zplay gprog(3,4), duration: 0.25

Continued fractions

Infinite Continued fraction is a number sequence like pi or square root of 2. Continued fractions can be calculated using for example general infinite continued fraction algorithm, which was used for enumerables pi, phi and euler.

pi: Pi

Everybody loves π. Now you can listen to it forever (until your computer crashes).

Sequence in OEIS: A000796

z1 pi, rhythm: [0.125,0.125,0.25], synth: :winwood_lead, release: ->(){rrand(0.01,0.5)}, res: 0.1, attack: ->(){rrand(0.01,0.5)}

phi: Phi (Golden ratio)

Sequence in OEIS: A001622

z1 phi, rhythm: "eeqq"*2+"h q q"*2+"q e e q q"*2

euler: Euler's number (e)

Sequence in OEIS: A001113

z1 euler, duration: 0.25, synth: :organ_tonewheel, amp: 0.5, tierce: 3.0, fundamental: 2.0, larigot: 2.0

Elementary cellular automaton

Cellular automata is implemented as enumerations where indexes of dead or live cells are interpeted as pitches in the given scale.

First generation can be inputted as a boolean array, boolean string and also a integer or string that is interpreted as binary.

There are 255 rules for elementary automata, which can be changed as a second parameter (random between 1-255 if omitted). See wiki for behaviour of different rules:

# First parameter: Starting generation, Second parameter: Rule number
zplay (live_cells spread(3,7), 23), rhythm: "qee" # First generation: "1010100"
zplay (live_cells 235, 23), rhythm: "qeeh" # First generation: 11101011"
zplay (dead_cells "001010001010", 23), rhythm: "qeeh" # First generation: "1010001010"
zplay (dead_cells "quuquubaba", 23), rhythm: "qeeh" # First generation: "011100010..."

Cellular automata can also output booleans with boolean_automata method. These booleans can be interpreted as melody and rhythm in various ways, for example using bools_to_seq method:

# First param is turned into binary, second param is the rule number
a = boolean_cells "Answer to Life, the Universe and Everything", 42

s = a.take(1000)
b = bools_to_seq(s)

zplay b, rhythm: b, synth: :hollow, sustain: 0.25, scale: :mixolydian

Markov chain

There are two type of markov chains implemented. Markov generator and markov analyzer.

Markov generator interprets integers (or hex strings) as markov chains. Input is interpreted as infinite chain, for example: 123 = 1 -> 2 -> 3 -> 1. More transitions between numbers will cause higher propability of changes, for example: 12344444444324, will repeat 4 more than the other numbers:

zplay (markov_generator 12345), duration: 0.25
zplay markov_generator("034A34F3562G3"), rhythm: "hqq"*2+"qqeeee"*2

Markov analyzer can create chains from existing melody:

m = zparse "q. 0 0 | q0 e1 q.2 | q2 e1 q2 e3 | h.4 | e 7 7 7 4 4 4 2 2 2 0 0 0 | q4 e3 q2 e1 | h. 0 "

zplay markov_analyzer m, 2, 0 # Paremeters: parsed melody, chain order, starting index