Ziffers is a numbered musical notation (aka. Ziffersystem) that makes composing generative melodies easier and faster for any key or scale.
Writing and playing melodies will be as simple as:
zplay "q4e11q21034", key: "f", scale: "major"
or
zplay "|:44332233:|.4h4", key: :c, scale: :chromatic
Just copy the source and run it in a free buffer or use run_file command to include the ziffers.rb file. Some features like lsystem or zpreparse also requires ziffer utils to run.
Ziffers is a numbered notation for music, meaning you write melodies using numbers that represent notes in some scale. Ziffers parses custom "ASCII" notation with zparse method and produces array of hash objects that contains parameters which can be played using zplay method. You can also use zparams method to use produced notes in any other method.
Notes are marked as numbers 1-9 representing the position in used scale. Default key is :c and scale :major making the numbers 1=C, 2=D, 3=E .. and so forth. If some scale does not have certain degree, for example 8 in major scale, it is transposed automatically meaning 8 is 1 in higher octave.
Ziffers is a single character language, which means that every degree that is higher than 9 needs to be transposed to the next octave.
To create higher notes you can use ^ which makes the note go one octave go higher, for example ^1 is same as 8 in major scale.
Use _ to change the octave one step lower.
Octave change is sticky, meaning it affects all of the notes that comes after the ^/_ character
Using negative degrees is other way to play lower notes. Consider scale as number line from negative to positive, for example: -987654321+123456789. In most cases it is more intuitive to use -2 than _6. You can also use lower numbers using eval syntax, for example: =-12
In some cases it might be easier to escape the digits that require more characters than one. To do this you can use the = character.
= character evaluates the characters between the = and the next empty space:
zplay "=-10 =12 =24"
zplay "=1*1 =2*2 =3*3 =4*4"
You can also use ruby string interpolation in combination with the = character:
# Plays degrees [0, 0, 2, 3, 4, 6, 6, 9, 8, 12, 10, 15, 12, 18, 14, 21, 16, 24, 18, 27]
10.times do |n|
zplay "=#{n*2} =#{n*3} "
end
Default note length is quarter note, meaning 0.25 sleep after the note plays. Note length characters are sticky, so you only have to type note length when you need to change the following note lengths.
For example note lengths in Blue bird song can be defined using characters w and q for Whole and Quarter notes:
zplay("5353 5653 4242 4542 5353 5653 w5 q5432 w1")
- m = Max = 8/1 = 8 beats
- l = Long = 4/1 = 4 beats
- d = Double whole = 2/1 = 2 beats
- w = Whole = 1/1 = 1 beat
- h = Half = 1/2 = 0.5 beat
- q = Quarter = 1/4 = 0.25 beat
- e = Eighth = 1/8 = 0.125 beat
- s = Sixteenth = 1/16 = 0.0625 beat
- t = Thirty-second = 1/32 = 0.03125 beat
- f = Sixty-fourth = 1/64 = 0.015625 beat
Same note lengths can also be defined using different escape notations:
Decimals:
zplay("5353 5653 4242 4542 5353 5653 Z1 5 Z0.25 5432 Z1 1")
Fractions
zplay("1/4 5353 5653 4242 4542 5353 5653 4/4 5 1/4 5432 4/4 1")
Note that only 1/1*n works. No support fractions where numerator is bigger than 9, eg. 12/64
Default note length can also be changed via parameter, for example:
# Plays short efg notes for 1 note per beat.
zplay("123",{release:0.5, sleep: 1})
. for dotted notes. First dot increases the duration of the basic note by half of its original value. Second dot half of the half, third dot half of the half of the half ... and so on. For example dots added to Whole note "w." will change the duration to 1.5, second dot "w.." to 1.75, third dot to 1.875.
You can also use notation based on note names to parse melody the ziffers notation. In order to use note names you have to use Z or fractions to define note lengths.
Include ziffer utils to use zpreparse-function to parse note names to degrees:
print zpreparse "1/4 cdefg - abg", :e
# Prints "1/4 67123 - 453"
Use zplay with note names by defining the parsekey. Ziffers utils must be included. These two play exactly same melody:
zplay("|:1/4 1231:|:34 2/4 5:|@:1/8 5654 1/4 31:|:1 -5+ 2/4 1:@|", key: :e)
zplay("|:1/4 cdec:|:ef 2/4 g:|@:1/8 gagf 1/4 ec:|:c -g+ 2/4 c:@|", parsekey: :c, key: :e)
Use 0 to create silence in melodies. 0 can be combined with note length, meaning it will sleep the length of the 0 note.
- & is flat
- # is sharp
Sharps and flats are not sticky so you have to use it every time before the note number. For example in key of C: #1 = C#
| Bars are just nice
Use : as basic repeat, for example in Frere Jacques:
# Repeat every bar
zplay("|: 1231 :|: 34w5 :|: q5654h31 :|: 1_5^w1 :|")
Use ; in repeats for alternative sections, like "|: 123 ; 432 ; 543 :|".
Alternative endings in ievan polkka:
zplay("|:q1e11q12| q3113 |;q2_77^2 |q31h1;q.5e4q32|q31h1:|"\
"|:q5e55q43|q2_77^2|;q4e44q32|q3113;q4e44q32|q31h1:|", :g, :minor)
Use @ ... @ to repeat multiple sections. For example this play row row row your boat 2 times and then the last section 2 times at the end:
zplay("|:.1.1|1q2h.3|3q2h3q4|w.5|@q888555333111|5q4h3q2|w.1:@|")
Use * to start from beginning and continue until first *. If & is at the end it works like D.C, otherwise the song will continue after the & character. For example:
zplay("| 7162 *| 6354 |*")
< Volume up > Volume down
Default volume or amp is 1. Volume characters changes the amp 0.25 by default. You can also change the amp treshold, for example:
#Changes amp treshold to 0.5, meaning first note is played at amp 1.5, second 2, and last 1.5
zplay("<1<2>1",:d,:minor,0.5,0.5)
Capitalized letters are used as control characters, to change properties of following degrees/notes.
List of control characters:
- A = :amp
- E = :env_curve
- C = :attack
- P = :pan
- D = :decay
- S = :sustain
- R = :release
- Z = :sleep
- X = :chordSleep
- T = :pitch
- K = :key
Use P1, P1 or P-1
~ starts and ends the slide. Remeber to add slide speed or/and space after the first slide marker.
For example:
zplay "~ 123456789"
# Note slide set to 0.4
zplay("~0.4 12321 ")
(TODO: release and sleep not working after the note_slide)
With randomization you can create random or semirandom melodies, for example:
zplay " |: ???0???q(1,2)[6,7](1,2)[6,7] (1,2)[6,7](1,2)[6,7] :| "
Use ? for random degree between 1-7
Use (1,5) for random numer between 1 and 5. (1,7) is same as ?.
(1,4*4) = create 4 random numbers between 1 and 4: "2341"
(3000,4000;qeee*4) = create 4 random numbers between 3000 and 4000: "q3e532"
(1..7) = create sequence: "1234567"
(1..9+2) = create sequence using step: "13579"
(1..7~) = create random sequence: "1324657".
(1..7?3) = take 3 from random sequence: "152"
(1..3;qe) = create sequence with note lengths: "q2e23"
Use [q1,e2345,h3] for randomly selected lengths and degree/degrees from the array.
It is also possible to combine other random syntax: [(1,3),(1..5,2)]
Chords can be used within the notation using roman numerals: i ii iii iv v vi vii or by custom chord syntax {1,3,5}
Chords can be customized with ^ like: vi^dim. See Sonic Pi:s chord_names list for supported chord names.
Chords can also be inverted using % char, for example %1 to invert all following chords up by one.
You can also create melodies by playing chord arpeggios using G character and chords. You can use subset of ziffers notation to denote chord notes and note lengths.
Examples:
zplay "G1231 i ii iii iv v vi vii"
zplay "Gq12e^3212 |: i^7 :3||: %-1 iv^dim7 :3|", key: :d4, scale: :mixolydian
Plays degrees in some key and scale. Function plays the hash array from zparse or parses the string or array first. You should consider using preparsed melody if you are using zplay inside live_loop.
You can use zplay with strings, integers and arrays, for example:
zplay 1 # Plays in :c :major
zplay [1,2,3], key: "f", sleep: 0.25
zplay "w1h2q3"
zplay [[1,1],[2,0.5],[3,0.25]] # Is same as w1h2q3
Run examples in buffer to see various ways to use zplay.
Plays midi notes using space separated midi notation
zmidi "|: q 53 53 53 57 h 60 q 53 53 ; h 55 q 60 60 h 57 q 53 53 ; q 55 55 57 55 w 53 :|"
You can also use zplay with samples to create new "synths", for example:
zplay("554e56 12323456 q 334e56 e75645343", {sample: :guit_e_fifths, start: 0.2, finish: 0.25, amp: 3})
The heart of ziffers. Parses string and returns hashmap that contains parameters to play single note
For example:
print zparse "1,2", key: :d
Prints:
[{:key=>:d, :scale=>:major, :release=>0.5, :sleep=>0.25, :pitch=>0.0, :amp=>1, :pan=>0, :amp_step=>0.5, :note_slide=>0.5, :control=>nil, :skip=>false, :pitch_slide=>0.25, :degree=>1, :note=>62}, {:key=>:d, :scale=>:major, :release=>0.5, :sleep=>0.25, :pitch=>0.0, :amp=>1, :pan=>0, :amp_step=>0.5, :note_slide=>0.5, :control=>nil, :skip=>false, :pitch_slide=>0.25, :degree=>2, :note=>64}]
Parses integer degree arrays to hash arrays
print zarray [1,2]
# [{:key=>:c, :scale=>:major, :release=>1.0, :sleep=>0.25, :pitch=>0.0, :amp=>1, :pan=>0, :amp_step=>0.5, :note_slide=>0.5, :control=>nil, :skip=>false, :pitch_slide=>0.25, :note=>60}, {:key=>:c, :scale=>:major, :release=>1.0, :sleep=>0.25, :pitch=>0.0, :amp=>1, :pan=>0, :amp_step=>0.5, :note_slide=>0.5, :control=>nil, :skip=>false, :pitch_slide=>0.25, :note=>62}]
zparse(n, opts)
- n = ascii notation as presented in the first section
Helper that creates array from hash, for example:
n = zparse("1,2")
print zparams(n,:note)
# Prints [60,62]
Ziffers is meant to provide easy way to write and experiment with melodies. By zparams you can easily use ziffers with other Sonic Pi methods such as play_pattern_timed. When using standard methods, remember to combine pitch to notes by transposing the arrays.
Example of using Ziffers with default Sonic Pi methods:
ievanpolka = \
"|:q1e11q12|q3113;q2_77^2|q31h1;q.5e4q32|q31h1:|"\
"|:q5e55q43|q2_77^2;q4e44q32|q3113;q4e44q32|q31h1:|"
n = zparse(ievanpolka,{key:"C", scale:"minor"})
notes = zparams(n, :note)
pitch = zparams(n, :pitch)
notes = [notes,pitch].transpose.map {|x| x.reduce(:+)}
durations = zparams(n, :sleep)
play_pattern_timed notes, durations
Look under examples to see other ways to use zparse and other Sonic Pi projects, such as Markov chains.
Ziffers can be used to generate fractal melodies using L-system based approach. Include ziffer utils to use rules and gen parameters. Define transformation rules as hash object and use gen to define amount of generations the rules are run against.
Matched values are defined as hash keys and replacements as hash value. For example:
zplay "1", rules: {"1"=>"13","3"=>"6431"}, gen: 3
Key can be string or regular expression that represents the string to be matched and replaced. You can feed back the matched string with $ or ${1-9} if using regexp groups. Use single quotes to evaluate the calculations:
zplay "1", rules: {/(3)1/=>"q'$1+1'1'$1+2'",/[1-7]/=>"e313"}, gen: 4
sleep 2
zplay "123", rules: {/[1-9]/=>"'$*1' [e,q] '$*2'"}, gen: 4
Use 0.4%= syntax in the beginning of the hash value to define chance of replacement. This way you can vary the replaced matches when using alternative random seeds.
This example has 20% chance to tranform any number between 1 and 7 to six numbers randomly chosen from numbers between 1 and 9.
zplay "1234", rules: {/[1-7]/=>"0.2%=(1..9,6)"}, gen: 4
Use regular expression lookbehind and lookahead syntax. For example this would match 3 only if it is between 1 and 2: /(?<=1)3(?=2)/
Write rules that change the input in a way that the end result does not grow. This way you can predictable or unpredictable loops that are somewhat similar to cellular automata.
Example of using lsystem directly to produce a random loop from the different generations:
use_synth :chipbass
n = lsystem("12e3456",{"1"=>"[2,4]","2"=>"[1,5]","3"=>"5","4"=>"3","5"=>"[1,3]"},10).ring
live_loop :p do
sample :bd_tek
zplay n.tick
end