GIRAFFE CUP (2020)
My wife once received a ceramic mug with a giraffe theme along the upper rim as a gift. The cup is from The Animal Project, an organization that makes products using curated artwork draw by persons with special needs. The artist for the giraffe cup is named Jun Yi. If you hit the cup with a plastic mallet, it sounds lovely. I recorded a brief improvisation on the giraffe cup and then sliced up the recording into components, most as short as a single hit. With these samples, I coded a short work with a pulsing kick drum - made from several layers of giraffe cup samples - and a rhythmic layer that shifts between a sense of closeness and distance. Twelve different tempos are used and, when combined with different kick drum speeds and rhythmic patterns, a vibrant and unpredictable world comes into being.
SCORE
GiraffeCup = "/GiraffeCup/Output"
set_recording_bit_depth! 24
use_random_seed 123
=begin
This function defines a kick drum sound that results from layers of samples of the giraffe cup. It takes 3 arguments: pan, rate multiple, and kick amplitude multiple. The giraffeKick is one layer of activity in each groove (see below). It gets repitched through the rate value and brought forward or backward in the texture through the amplitude value.
=end
define :giraffeKick do |p,rm,kam|
sample GiraffeCup, 235, rate: 1.33*rm, attack:0, decay: 0.25, decay_level: 0.0,
sustain: 0, amp: 0.6*kam, pan: p
sample GiraffeCup, 236, rate: 0.235*rm, attack:0, decay: 0.11, decay_level: 0.0,
sustain: 0, amp: 0.6*kam, pan: p
sample GiraffeCup, 233, rate: 0.33*rm, attack:0, decay: 0.16, decay_level: 0.0,
sustain: 0, amp: 0.7*kam, pan: p
sample GiraffeCup, 229, rate: 0.0675*rm, attack:0, decay: 0.3, decay_level: 0.0,
sustain: 0, sustain_level: 0.0*rm, amp: 1*kam, lpf: 60, pan: p
sample GiraffeCup, 228, rate: 0.02*rm, attack:0, decay: 0.12, decay_level: 0.0,
sustain: 0, sustain_level: 0.0, amp: 3*kam, lpf: 60, pan: p
sample GiraffeCup, 227, rate: 0.027*rm, attack:0, decay: 0.25, decay_level: 0.0,
sustain: 0, sustain_level: 0.0, amp: 3*kam, lpf: 48, pan: p
end
=begin
There are 4 grooves in this piece. They differ in only a few ways, but these differences are significant. The following values are unique in each: base, random seed, rand_i size for sample selection, the values in the speed array. The base is an offset value added to the value determined by the rand_i in the samp variable. For instance, the samp variable in groove1 is rand_i(45) + base, where base is 213. The effective sample selection range, therefore, is 213-257. The different random seed values not only help to distinguish the grooves from each other but to reconfirm the character of each groove.
Each groove has a series of rep_vals, that is, repetition values. This is a multi-dimensional array, wherein the first value of each subarray determine the number of repetitions for the giraffeCup samples and the other determines (with the help of a speed value) the number of repetitions of the kick.
=end
groove1_rep_vals = [[44,8],[37,7],[33,6],[28,5],[22,4],[14,3],[10,2],[5,1]]
define :groove1 do |a,r,pr,s,vs,es,ep,ed,rm,kam|
base = 213
use_random_seed 190
low_rate = 1.0 - r
high_rate = 1.0 + r
panRange = if pr == 0
[-0.1, 0.1]
else
[-0.4, 0.4]
end
kickPan = if pr == 0
-0.1
else
0.1
end
# in_thread is used to execute code blocks simultaneously. This allows the GiraffeCup sample
# playback to occur at the same time as the giraffKick playback.
in_thread do
with_fx :reverb, mix: vs do
with_fx :echo, mix: es, phase: ep, decay: ed do
a[0].times do
samp = rand_i(45) + base
sample GiraffeCup, samp, amp: 2, rate: rrand(low_rate,high_rate),
pan: rrand(panRange[0],panRange[1])
wait choose([0.125, 0.25])
end
end
end
end
len = a[1]
speed = [0.125,0.25,0.333,0.5,1.0][s]
reps = len / speed
with_fx :bitcrusher, amp: 2, bits: 16, sample_rate: 22000 do
with_fx :distortion, mix: 0.7, distort: 0.5 do
reps.to_i.times do
giraffeKick(kickPan,rm,kam)
wait speed
end
end
end
end
groove2_rep_vals = [[38,8],[36,7],[32,6],[28,5],[23,4],[16,3],[11,2],[7,1]]
define :groove2 do |a,r,pr,s,vs,es,ep,ed,rm,kam|
use_random_seed 475
base = 144
low_rate = 1.0 - r
high_rate = 1.0 + r
panRange = if pr == 0
[-0.1, 0.1]
else
[-0.4, 0.4]
end
kickPan = if pr == 0
-0.1
else
0.1
end
in_thread do
with_fx :reverb, mix: vs do
with_fx :echo, mix: es, phase: ep, decay: ed do
a[0].times do
samp = rand_i(25) + base
sample GiraffeCup, samp, amp: 1.5, rate: rrand(low_rate,high_rate),
pan: rrand(panRange[0],panRange[1])
wait choose([0.125, 0.25])
end
end
end
end
len = a[1]
speed = [0.0675,0.25,0.333,0.5,1.0][s]
reps = len / speed
with_fx :bitcrusher, amp: 2, bits: 16, sample_rate: 22000 do
with_fx :distortion, mix: 0.7, distort: 0.5 do
reps.to_i.times do
giraffeKick(kickPan,rm,kam)
wait speed
end
end
end
end
groove3_rep_vals = [[42,8],[38,7],[32,6],[28,5],[22,4],[15,3],[10,2],[5,1]]
define :groove3 do |a,r,pr,s,vs,es,ep,ed,rm,kam|
base = 50
use_random_seed 225
low_rate = 1.0 - r
high_rate = 1.0 + r
panRange = if pr == 0
[-0.1, 0.1]
else
[-0.4, 0.4]
end
kickPan = if pr == 0
-0.1
else
0.1
end
in_thread do
with_fx :reverb, mix: vs do
with_fx :echo, mix: es, phase: ep, decay: ed do
a[0].times do
samp = rand_i(25) + base
sample GiraffeCup, samp, amp: 1.5, rate: rrand(low_rate,high_rate),
pan: rrand(panRange[0],panRange[1])
wait choose([0.125, 0.25])
end
end
end
end
len = a[1]
speed = [0.0675,0.25,0.333,0.5,1.0][s]
reps = len / speed
with_fx :bitcrusher, amp: 2, bits: 16, sample_rate: 22000 do
with_fx :distortion, mix: 0.7, distort: 0.5 do
reps.to_i.times do
giraffeKick(kickPan,rm,kam)
wait speed
end
end
end
end
groove4_rep_vals = [[42,8],[37,7],[32,6],[28,5],[21,4],[16,3],[10,2],[5,1]]
define :groove4 do |a,r,pr,s,vs,es,ep,ed,rm,kam|
use_random_seed 225
base = 92
low_rate = 1.0 - r
high_rate = 1.0 + r
panRange = if pr == 0
[-0.1, 0.1]
else
[-0.4, 0.4]
end
kickPan = if pr == 0
-0.1
else
0.1
end
in_thread do
with_fx :reverb, mix: vs do
with_fx :echo, mix: es, phase: ep, decay: ed do
a[0].times do
samp = rand_i(35) + base
sample GiraffeCup, samp, amp: 1.5, rate: rrand(low_rate,high_rate),
pan: rrand(panRange[0],panRange[1])
wait choose([0.125, 0.25])
end
end
end
end
len = a[1]
speed = [0.125,0.25,0.333,0.5,1.0][s]
reps = len / speed
kickType = [0,1].choose
with_fx :bitcrusher, amp: 2, bits: 16, sample_rate: 22000 do
with_fx :distortion, mix: 0.7, distort: 0.5 do
reps.to_i.times do
if kickType == 0
sample :bd_haus, amp: 0.5*kam, pan: kickPan
else
giraffeKick(kickPan,rm,kam)
end
wait speed
end
end
end
end
# The kickSpeeds values are ticked through and used as indices for a particular groove's
# speed array in each section.
kickSpeeds = [2, 3, 1, 0, 2, 3, 3, 3, 3, 3, 2, 1, 2, 1, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 2, 3, 1, 0, 2, 2, 1, 2, 1, 2, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3,
2, 3, 1, 0, 2, 2, 3, 1, 0, 2, 2, 3, 1, 0, 2, 2, 3, 1, 0, 2, 2, 3, 1, 0, 2]
# This array is all permutations of 4 values [0,1,2,3]. It is ticked through with each
# iteration of tempo_lcg (see below) and used to select the groove for that particularly section.
four_permutations = [0, 1, 2, 3, 0, 1, 3, 2, 0, 2, 1, 3, 0, 2, 3, 1, 0, 3, 2, 1, 0,
3, 1, 2, 1, 0, 2, 3, 1, 0, 3, 2, 1, 2, 0, 3, 1, 2, 3, 0, 1, 3,
0, 2, 1, 3, 2, 0, 2, 0, 1, 3, 2, 0, 3, 1, 2, 1, 0, 3, 2, 1, 3,
0, 2, 3, 0, 1, 2, 3, 1, 0, 3, 0, 1, 2, 3, 0, 2, 1, 3, 1, 0, 2,
3, 1, 2, 0, 3, 2, 0, 1, 3, 2, 1, 0]
# The rates array is used by the groove functions to define a minimum and maximum value for
# sample playback rate. It is what causes the repitching of the giraffe cup samples.
rates = [0.0,0.01,0.02,0.03,0.05,0.08,0.13,0.21,0.34]
​
# The rate_lcg array functions as indices to access values in the rates array.
# It is generated using a Mod 9 LCG that cycles through multiplier values from 1 to 8,
# seed values from 0 to 7, and increment values from 0 to 7.
# Note that the result is not the typically desired random permutation of all values from 0-8.
# This is an example of using an LCG in the "wrong" way to get a correct (desired) experience.
rate_lcg = [0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 7, 6, 4, 0, 1, 3, 7, 6, 8, 8, 8, 8, 8, 8,
8, 8, 8, 6, 0, 3, 6, 0, 3, 6, 0, 3, 6, 7, 3, 1, 0, 4, 6, 7, 3, 8, 8, 8,
8, 8, 8, 8, 8, 8, 3, 0, 6, 3, 0, 6, 3, 0, 6, 0, 7, 0, 7, 0, 7, 0, 7, 0]
# The tempo_lcg array is generated from a Mod 12 LCG cycling through increment values from 1 to 11
# with a multiplier value of 1 and a seed value of 11.
tempo_lcg = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 3, 5, 7, 9, 11, 1, 3, 5, 7, 9,
11, 2, 5, 8, 11, 2, 5, 8, 11, 2, 5, 8, 11, 3, 7, 11, 3, 7, 11, 3, 7, 11,
3, 7, 11, 4, 9, 2, 7, 0, 5, 10, 3, 8, 1, 6, 11, 5, 11, 5, 11, 5, 11, 5,
11, 5, 11, 5, 11, 6, 1, 8, 3, 10, 5, 0, 7, 2, 9, 4, 11, 7, 3, 11, 7, 3,
11, 7, 3, 11, 7, 3, 11, 8, 5, 2, 11, 8, 5, 2, 11, 8, 5, 2, 11, 9, 7, 5,
3, 1, 11, 9, 7, 5, 3, 1, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 11]
# The lengths_lcg is generated from a Mod 7 LCG cycling through seed values from 0 to 6 and
# increment values of [2,3,4,5,6,0,1]. The multiplier is fixed at 1.
lengths_lcg = [2, 4, 6, 0, 2, 4, 6, 0, 0, 3, 2, 5, 4, 7, 6, 1, 6, 2, 6, 2, 6, 2, 6, 2,
4, 1, 2, 7, 0, 5, 6, 3, 2, 0, 6, 4, 2, 0, 6, 4, 0, 7, 2, 1, 4, 3, 6, 5,
6, 6, 6, 6, 6, 6, 6, 6, 4, 5, 2, 3, 0, 1, 6, 7]
# These bpm values are logarithmically related by the 12th square of 2, the same value used to
# determine frequencies in the equal tempered tuning system.
bpms = [60.0, 63.54, 67.29, 71.26, 75.46, 79.92, 84.63, 89.62, 94.91, 100.5, 106.44, 112.72]
# The kick_pitch_rate_multiples array is ticked through, updated with each new tempo_lcg value
# (see below). They serve as index values to rate_mult array within the main block of the score.
kick_pitch_rate_multiples = [6, 2, 8, 4, 0, 6, 2, 8, 4, 0, 9, 5, 7, 1, 9, 5, 7, 1, 9, 5, 4,
0, 8, 2, 4, 0, 8, 2, 4, 0, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 7, 3, 9, 5, 1, 7, 3, 9, 5, 4, 0, 2,
6, 4, 0, 2, 6, 4, 0, 9, 5, 3, 7, 9, 5, 3, 7, 9, 5, 6, 8, 6, 8,
6, 8, 6, 8, 6, 8]
###SCORE####
=begin
This random seed value is important since it controls the FX levels, something very noticeable in the piece. Most significant is that different values will apply the heavy reverb to different sections than those heard in the recording, fundamentally changing our sense of structure in the piece.
=end
use_random_seed 103
=begin
The piece is generated by iterating though the tempo_lcg array. Each value serves as an index to the bpms array, in effect, changing the tempo on each iteration. It also determines the length of the piece and influences the form since each of its 144 values cause updates in other parameters that shape the character of each of these 144 sections.
=end
tempo_lcg.each do |t|
# This block first updates all of the arguments needed for a groove function, then determines
# which groove function is to be used for the current section and runs the groove with all of
# the updated arguments.
length_order = lengths_lcg.tick(:lens1)
temp_rate = rates[rate_lcg.tick(:ratePerms1)]
temp_groove = four_permutations.tick(:perms)
send_val = [0,1,2].choose
verb_send = if send_val == 2
rrand(0.5, 0.7)
elsif send_val == 1
rrand(0.2, 0.35)
else
0.0
end
echo_send = [0.0,0.05,0.1,0.15,0.25,0.4].choose
echo_phase = [0.125,0.25,0.33,0.4,0.5].choose
echo_decay = [0.5, 1.0, 1.5, 2.5, 4.0, 6.5].choose
ks = kickSpeeds.tick(:kspeeds)
rate_mult = [0.7, 0.82, 0.9, 1.0, 1.1, 1.21, 1.33, 1.44, 1.57, 2.3][kick_pitch_rate_multiples.tick(:kprm)]
kick_amp_mult = if ks == 0
0.8
else
0.8 - verb_send
end
kick_pan = [0,1]
use_bpm bpms[t]
case temp_groove
when 0
temp_times = groove1_rep_vals[length_order]
groove1(temp_times, temp_rate, kick_pan.tick(:kpan), ks, verb_send, echo_send, echo_phase,
echo_decay, rate_mult, kick_amp_mult)
when 1
temp_times = groove2_rep_vals[length_order]
groove2(temp_times, temp_rate, kick_pan.tick(:kpan), ks, verb_send, echo_send, echo_phase,
echo_decay, rate_mult, kick_amp_mult)
when 2
temp_times = groove3_rep_vals[length_order]
groove3(temp_times, temp_rate, kick_pan.tick(:kpan), ks, verb_send, echo_send, echo_phase,
echo_decay, rate_mult, kick_amp_mult)
when 3
temp_times = groove4_rep_vals[length_order]
groove4(temp_times, temp_rate, kick_pan.tick(:kpan), ks, verb_send, echo_send, echo_phase,
echo_decay, rate_mult, kick_amp_mult)
end
end