-
Notifications
You must be signed in to change notification settings - Fork 5
Transformations
Melody transformations alter the parsed melody. Transformations can be done to parsed melodies or used directly with zplay or zloop.
Transforming parsed melodies:
use_synth :piano
a = zparse "q 0 2 1 4"
b = a.inverse
c = a.retrograde
d = a.inverse.retrograde
e = a.transpose -3
zplay (a*2+b*2+c*2+d*2+b+e)*2
Alternatively numeric transformations can be done inline (not including augment, tonnetz etc.), for example:
zplay "(0 2 3 5)<inverse>(-2)" # Single inverse
zplay "(0 2 3 5)<inverse>(-2 1 4 2)" # Multiple inverses in row
z1 "(0 3 2 5)<retrograde> # Normal retrograde
z1 "(0 3 2 5)<retrograde>((0,3))" # Retrograde from random index
z1 "(e 0 3 6 8)<flex>(1 2 3)" # Stretch durations
z1 "(e 0 3 6 8)<stretch>(1 2 3)" # Add number of notes
zplay "(q 0 1 2 3)<fuse>(0 3 2 5)" # Creates one long melody by injecting intervals
Transformations can also be executed with cycle specific options using using cycle or by using lambdas:
z1 "q 0 1 2 3", cycle: {at: 2, harmonize: 3 } # 2 = every second
z2 "q 3 2 1 0", release:->(i){i%3} # i / first parameter is interpreted as the loop cycle
Alternatively a spesific range can be defined with the at parameter:
z1 "q 0 2 1 3",
cycle: [
{at: 6, range: 1..2, retrograde: true} # Apply for 1 to 2, where whole cycle length is 6
]
Transformations can also be stored to the next cycles of the loop using store: true:
z1 "q 0 1 e 2 3 4 5", store: true, swap:->(i){i}
There are three types of transformations: Order transformations, object transformations and loop transformations.
Order tranformations are always run before object tranformations. These type of transformations alter the whole or part of the melody before any notes are played. There can be multiple order transformations which are run in order from left to right.
Inverses the melody while keeping the chords in place. Use retrograde: true to inverse the whole melody.
Partial melody retrograde: [2,4] where first value is the start index and last valie is the end index.
Splitted melody retrograde: 3 where value is the number of parts the melody is splitted before the reverse.
zplay "i 1 2 vi%-2 3 4", chord_duration: 0, retrograde: true
zplay "0 1 2 3 4 5", retrograde: 1..2
zplay "0 1 2 3 4 5", retrograde: 2
Reverses melody including the chords.
zplay "i 1 2 vi%-2 3 4", chord_duration: 0, reverse: true
zplay "0 1 2 3", reverse: true
Swap places of degrees (and notes) in a melody. Swap in
zplay "0 1 2 3", swap: 1 # Creates 0 2 1 3
zplay "0 1 2 3", swap: [1,3] # Creates 1 0 2 3
z1 "0 1 2 3", store:true, swap: ->(i){i}
Creates mirror from the melody
zplay "0 1 2 3", mirror: true
Creates reflection from the melody
zplay "0 1 2 3", reflect: true
Rotates the melody
zplay "0 1 2 3", rotate: 1
zplay "0 1 2 3", rotate: 2
zplay "0 1 2 3", rotate: -1
z1 "0 1 2 3 4", store: true, rotate: 1 # Stores the rotation for the next cycle
Creates subset from the melody
zplay "0 1 2 3 4 5", subset: 3
zplay "0 1 2 3 4 5", subset: 1..2
In 1947 Slonimsky published the Thesaurus of Scales and Melodic Patterns, that became very influential source for artists like John Coltrane, Frank Zappa and Paul Grabowsky. In the book there are multitude of scales generated by procedures he called interpolation, ultrapolation, infrapolation, infra-interpolation, infra-ultapolation an infra-inter-ultrapolation. He also suffered from something that he self diagnosed as "sesquipedalian macropolysyllabification".
Easy way to get the basic idea of interpolation is to test out generating some scales in: https://slonimsky.netlify.app/
In ziffer interpolation can be done to parsed melodies:
# interpolate(number of nodes, division)
melody = zparse "1 -2 6 4", scale: :chromatic
zplay melody.interpolate(4,2)
Interpolation can also be done in ziffers syntax, which opens tons of ways to alternate the generated patterns. Syntax for the interpolation: (intervals)(division between nodes)
Examples:
zplay "(0 1 3 2)<3>(1)" # Single division for all nodes
zplay "(0 1 3 2)<4>(1 -1 0 2)" # Alternate division for different nodes
z1 "(0 2 <4 3 5>)<2>(1 <2 -2 0>)" # Cyclic interpolations
z1 "([2,3,4] (1,4))<2>((1,5) (3,5) {12})" # Very random interpolations
Fuses two melodies together by injecting intervals from the given melody
zplay "q 0 1", fuse: "q 1 2 3 4"
Zip will merge another melody to existing one. Zip works like a "zipper" and omits extra notes that cannot be "zipped".
zplay "0 1 2", zip: "3 4 5" # Plays 0 3 1 4 2 5
zplay "0 1", zip: "6 7 8" # Plays 0 6 1 7
Appends new notes to melody
zplay "q 0 1", append: "e 8 4" # Plays q 0 1 e 8 4
Prepends new notes to melody
zplay "q 0 1", prepend: "e 8 4" # Plays e 8 4 q 0 1
Randomizes the melody or part of the melody
zplay "q 0 1 2 3 4", shuffle: true
z1 "q 0 1 2 3 4", shuffle: 2..3
Drops a note or set of notes from a melody
zplay "q 0 1 2 3 4", drop: 2
zplay "q 0 1 2 3 4", drop: 0..3
Drops the last note from the melody
zplay "0 1 2 3 4", pop: true
z1 "0 1 2 3 4", store: true, pop: true # Drop the last every cycle and stops the loop
Removes first note from the melody
zplay "0 1 2 3 4", shift: true
z1 "0 1 2 3 4", store: true, shift: true # Remove the first every cycle and stops the loop
Pick notes from the melody with duplicates. The chances of getting more duplicates will be greater with long melodies. Same method as in Sonic Pi.
zplay "q 0 1 2 3 4", pick: 2
z1 "q 0 1 2 3 4", pick: 4
Streches the notes given amount of times. Same method as in Sonic Pi.
zplay "q 0 1 2", stretch: 2 # Plays 0 0 1 1 2 2
zplay "(q 0 1 2)<stretch>(2 3 4)" # Inline
Deals the notes in imaginary decks and plays from left to right
zplay "1 2 3 4", deal: 2 # Deals two "decks" 3 1 and 2 4
zplay "(1 2 3 4).d(2)" # Shorthand
print zparse "1 2 3 4", deal: 3 # Array of [[4,1],[2],[3]] + hash crap
z1 "(0..7)<deal>((2,6))" # Inline random dealing
Object transformations are always run after the order transformations. Ordering of the melody does not affect the result of object transformations (other that some notes might be missing after the order transformations). Object transformations are processed in order form left to right.
Transpose pitches within the given key and scale
z1 "q 0 3 6 8", transpose: -3 # Transpose all pitched by -3
z2 "q 0 3 6 8", transpose: [-3,-2,-1] # Transpose first pitch by -3, second by -2 ...
z3 "(q 0 3 6 8)<transpose>(3 4 2 1)" # Inline multiple transposes
Inverses pitches within the given key and scale
z1 "q 0 3 6 8", inverse: -1
z2 "q 0 3 6 8", inverse: [0,1-2,-1]
z3 "(q 0 3 6 8)<i>(3 -2 4 -1)" # Inline inverse with shorthand
Similar to transpose but applies the added value to whole sequence
z1 "q 0 3 6 8", add: -3
z2 "q 0 3 6 8", add: [1,2,-1]
Multiply the whole sequence
z1 "q 0 3 6 8", multiply: 2
z2 "q 0 3 6 8", multiply: [2,-1,3]
Intervallic augmentation will alter the melody by adding intervals to the degrees and parsing new note for that degree.
Example:
z1 "q .0 .0|q0 e1 q.2|2 e1 q2 e3|h.4|e7 7 7 4 4 4 2 2 2 0 0 0|q4 e3 q2 e1|h.0", augment: {
1=>"3",
2=>"#",
3=>->(){rrand_i 1,4},
4=>->(){rrand_i 1,2}
}
Note that augmenting is done by means of intervals, meaning 2=>1 will do nothing (1 = unison).
Harmonizes existing melody by creating chords from degrees using intervals, meaning 1=>1 will do nothing (1 = unison).
zplay "0 2 3 4", harmonize: 3
zplay "-3 -2 -1 0 1 2 3", harmonize: [3,5]
zplay "0 2 3 4", harmonize: [2,"#5","b4"]
zplay "0 2 3 4", harmonize: {1=>3,4=>5}
zplay "0 2 3 4", harmonize: {1=>[3,5],4=>[2,3,4]}
# Harmonize creates chords so you could also play arpeggios from that and invert those chords
zplay "G'e1 2 3' q 9 8 7 6 5 4 3 2 1 0", harmonize: [-3,-5], chord_invert: -2
Adds compound interval to the harmonization, added note is one octave up or down.
zplay "q 9 8 7 6 5 4 3 2 1 0", harmonize: 3, compound: 1
zplay "q 9 8 7 6 5 4 3 2 1 0", harmonize: -3, compound: 1
Flex will multiply or shorten the length of the melody
zplay "q 0 1 2 3"
zplay "q 0 1 2 3", flex: 1
zplay "q 0 1 2 3", flex: -0.5
z3 "(e 0 3 6 8)<flex>(1 2 3)" # Inline flex
Changes the note to rest from the given index (0-n) or array in loops
zplay "q 0 1 2 3", silence: 2
z1 "0 1 2 3", silence: [0,2,[0,2,3],3]
Loop transformations are type of transformations that are only done in loops. These type of transformations are done before order and object transformations.
Phase will force the loop to go out of sync causing the phasing effect when multiple loops are playing.
Example:
z1 "q 0 1 2 3"
z2 "q 0 1 2 3", sync: :z1, phase: 0.1 # Sleeps 0.1 before each cycle
Played melody or rhythm can be changed in a loop by defining a new pattern:
z1 "h K S [r K] S",
K: :drum_heavy_kick,
S: :drum_snare_soft,
cycle: [
{at: 2, pattern: "h K (S K) (r K) S"}
]
Create your own transformations using the define method. Order transformation method must have 2 parameters: array and loop cycle index. Object transform method must have 3 parameters: note object, loop cycle index, note index and melody length.
Example:
define :foo do |melody,loop_i|
print "foo"
print melody
print loop_i
return melody.reverse
end
define :bar do |ziff,loop_i,note_i,melody_length|
print "bar"
print ziff
print loop_i
print note_i
print melody_length
ziff[:note]+=5
return ziff
end
z1 "q 0 1 2", order_transform: :foo, object_transform: :bar
All transformations (and all other parameters, amp, release etc.) can be applied to notes using apply:
Apply to a single note for multiple times:
z1 "q 1 (1 5 2 0)~", synth: :rodeo, release: 0.1, apply: [
{
at: 1,
release: 3.0,
synth: :fm,
transpose: -1,
},
{
at: 1,
with_fx: :ixi_techno,
phase: 0.9
}
]
Apply to range of notes. Use lambas to randomize the values:
z2 "q 1 5 2 0", synth: :pluck, apply: [
{
at: 3..4,
transpose: ->(){rrand_i(-1,4)},
}
]
Apply can be combined with run effets (which affects all notes):
z3 "q 0 ^0 3 [2,5,7] 7", scale: :minor,
synth: :fm,
amp: 0.5,
cutoff: range(60,130,5).mirror,
res: 0.9,
release: 0.1,
run: {with_fx: :reverb, room: ->(){rand(1.0)}},
apply: {mod: 6, with_fx: :echo}
Some transformations can only be applied to chords
Tonnetz defines a tonal pitch space for triads. Ziffers tonnetz implementation is based on Oettingen/Riemann tonnetz with Neo-Riemannian PLR operations.
Transformations for triads are done using groups of one step transformations:
p (Parallel) r (Relative) l (Leading)
Transformations can be grouped as two step transformations:
pr and pl (Parallel) rl and rp (Relative) lr and lp (Leading)
And continue to 3, 4, ... transformations. Tonnetz and the transformations can be illustrated as a partial tonnetz where triads are represented as pitches in chromatic scale (O Tannenbaum, o Tannenbaum! Wie treu sind deine Blätter ...):
Examples:
# Other transformations than l,p,r are ignores, so for example "o" can be used to denote original chord
zplay "i", tonnetz: "o p o p p l l lpr lpr"
# Play transformations as a loop
z1 "i", tonnetz: "o p lr lrp"
# Store transformation for the next cycle using store: true
z1 "024", tonnetz: "pl r", store: true
# Arpeggios works also with the tonnetz transformations
z1 "@(q 0 1 2) 246", tonnetz: "pl rl r", store: true
# Do different transformations on different cycles of the loop
z1 "024", cycle: [
{at: 2, tonnetz: "lr" },
{at: 4, tonnetz: "plr"}
]
# Transformations can be applied to sequence of chords
z1 "i v vi", synth: :hollow, sustain: 0.8,
tonnetz: "l lpr lp lrplr lrplrplrpl"
------------------------------------ See more stuff from the menu --------------------------------------->