top of page

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.

Description
Recording
Score

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

bottom of page