Re:publica TEN – Hörbar programmieren mit Sonic Pi

Am 2. Mai hatte ich die Gelegenheit auf der Re:publica TEN Sonic Pi vorzustellen. Dies ist der Blogbeitrag zu Präsentation: mit Hintergrundinfos, dem Code-Beispielen und Links zu Sonic Pi.

Wer nicht programmieren kann, für den erscheint Code undurchsichtig und abstrakt. Tatsache ist, dass vieles von dem, was wir wissen, wünschen, hoffen und konsumieren seinen Niederschlag in Programmcode findet. Grund genug, sich nicht nur vorgegebenen Ergebnissen abzufinden, sondern selbst Hand anzulegen.

Sonic Pi wurde von Sam Aaron im Rahmen eines Projekts an der Cambridge University entwickelt. Es ging darum, das Curriculum an britischen Grundschulen umzusetzen. Dort steht nämlich, dass Schüler und Schülerinnen sich nicht nur oberflächlich mit digitaler Technologie beschäftigen, sondern einen tieferen Einblick erhalten sollten: Sie sollen das Programmieren lernen. Dabei hilft Sonic Pi.

Was ist Sonic Pi?

Trojanisches Pferd zu pädagogischen Zwecken: Sam Aaron berichtet, dass es in einer britischen Schulklasse voller 10-jähriger unmöglich ist, zwei zusammenhängende Sätze zu sprechen. Zunächst undenkbar, in so einer Atmosphäre etwas über Funktionen, Variablen oder noch kompliziertere Konzepte der Programmierung zu referieren. Das ist aber auch gar nicht nötig. Zwei Kommandos, nämlich play und sleep reichen aus, um die Kids ans spielen, arbeiten und verstehen zu bringen. Daraus ergibt sich das weitere. Sonic Pi ermöglicht ‚audible programming‘: Unmittelbares Feedback in Form von Tönen und Musik überführt die Abstraktheit des Codes in sinnliches Erleben (und wieder zurück).

Programmierumgebung: Auf der Basis von Ruby bietet Sonic Pi eine Vielzahl von Ausdrücken, mit denen Musik erzeugt, gesteuert und organisiert werden kann. Sonic Pi enthält einen Editor und eine Laufzeitumgebung, in das Geschriebene ausgeführt und verändert werden kann.

Synthesizer: Sonic Pi liefert aktuell 39 unterschiedliche Synthesizer-Sounds. Hinzu kommen eine Vielzahl von Filtern und Effekten, mit denen diese Palette noch erweitert werden kann. (Genauer gesagt, stellt nicht Sonic Pi diese Sounds zur Verfügung, sondern SuperCollider.)

Sequencer und Sampler: Mit Sonic Pi können musikalische Strukturen hergestellt und organisiert werden. Man kann unterschiedliche Klänge mehrstimmig ablaufen lassen, loopen oder auch abhängig vom Zufall auftauchen lassen. Sonic Pi versteht auch den Umgang mit Samples. 130 davon bringt es aktuell mit. Aber auch Klangdateien von der eigenen Festplatte können eingebunden werden (in .wav- oder .aiff-Format). Samples können u.a. gedehnt, gestaucht oder partiell abgespielt werden.

Musikalische Experimentierwerkstatt: Mit Sonic Pi kann man musikalische Experimente durchführen, Kompositionen nachbauen und verstehen und davon ausgehend eigenes entwerfen.

Live Coding-Instrument: Schließlich wird Sonic Pi zunehmend auch für das Live Coding eingesetzt. Statt D(isk) J(ockey) steht der C(ode) J(ockey) hinter seinem Laptop und kreiert Live-Musik, zu der getanzt wird. Sam Aaron beherrscht sein Intrument mittlerweile beeindruckend virtuos.

Code-Beispiele

Sonic Pi enthält ein hervorragendes Tutorial. Deshalb spare ich es mir an dieser Stelle, die Beispiele aus der Präsentation ausführlich zu kommentieren. Meine Empfehlung: Sonic Pi herunterladen und installieren, das Tutorial im Programm aufrufen (Shortcut: Alt-i) ansehen und dann gleich anfangen. Noch ein kleiner Hinweis: Das deutschprachige Tutorial ist im Moment nicht ganz auf dem neuesten Stand (vermutlich jedoch in der nächsten Version 2.11). Wer also des englischen mächtig ist, kann die englischen Seiten online lesen.

Die Code-Beispiele enthalten teilweise mehrere Abschnitte, die nicht unbedingt parallel gespielt werden können. Aus diesem Grund sind Teile auskommentiert. Das geschieht in Sonic Pi u.a. mit folgendem Befehl:

comment do
  # Was hier steht, wird nicht berücksichtigt, wenn man auf 'Run' klickt.
end

uncomment do
  # Dieses hier aber schon.
end

Also, falls man nichts oder zuviel hört, bitte nach „comment“ oder „uncomment“ suchen.

Play & Sleep

Mit diesen beiden Kommandos kann man schon sehr viel anfangen. Das nachfolgende Beispiel benutzt weiter unten auch noch den Befehl „play_pattern_timed“, eine Kombination aus „play“ und „sleep“; wenn man zwei musikalische Abschnitte gleichzeitig hören möchte, dann hilft der Befehl „in_thread“:

# Beethoven: Ode an die Freude
use_bpm 200

uncomment do
  play :e
  sleep 1
  play :e
  sleep 1
  play :f
  sleep 1
  play :g
  sleep 1
  play :g
  sleep 1
  play :f
  sleep 1
  play :e
  sleep 1
  play :d
  sleep 1
  play :c
  sleep 1
  play :c
  sleep 1
  play :d
  sleep 1
  play :e
  sleep 1
  play :e
  sleep 1.5
  play :d
  sleep 0.5
  play :d
  sleep 2
  
  play :e
  sleep 1
  play :e
  sleep 1
  play :f
  sleep 1
  play :g
  sleep 1
  play :g
  sleep 1
  play :f
  sleep 1
  play :e
  sleep 1
  play :d
  sleep 1
  play :c
  sleep 1
  play :c
  sleep 1
  play :d
  sleep 1
  play :e
  sleep 1
  play :d
  sleep 1.5
  play :c
  sleep 0.5
  play :c
  sleep 2
end

comment do
  in_thread do
    play_pattern_timed [:e, :e, :f, :g, :g, :f, :e, :d, :c, :c, :d, :e], 1
    play_pattern_timed [:e, :d, :d],[1.5, 0.5, 2]
    play_pattern_timed [:e, :e, :f, :g, :g, :f, :e, :d, :c, :c, :d, :e], 1
    play_pattern_timed [:d, :c, :c],[1.5, 0.5, 2]
    
    play_pattern_timed [:d, :d, :e, :c, :d], 1
    play_pattern_timed [:e, :f], 0.5
    play_pattern_timed [:e, :c, :d], 1
    play_pattern_timed [:e, :f], 0.5
    play_pattern_timed [:e, :d, :c, :d, :g3, :e], [1, 1, 1, 1, 1, 2]
    play_pattern_timed [:e, :f, :g, :g, :f, :e, :d, :c, :c, :d, :e], 1
    play_pattern_timed [:d, :c, :c],[1.5, 0.5, 2]
  end
  
  play_pattern_timed [:c3, :g2, :c3, :g2], 4, release: 5, amp: 0.5
  play_pattern_timed [:c3, :g2, :c3], 4, release: 5, amp: 0.5
  play_pattern_timed [:g2, :c3], 2, release: 3, amp: 0.5
  play_pattern_timed [:g2, :g2, :g2], 4, release: 5, amp: 0.5
  play_pattern_timed [:g2, :g2], [2, 2], release: 5, amp: 0.5
  play_pattern_timed [:c3, :g2, :c3], 4, release: 5, amp: 0.5
  play_pattern_timed [:g2, :c3], 2, release: 3, amp: 0.5
end

Samples & live_loop

Mit Samples kann man sich in Sonic Pi stundenlang vergnügen. Spätestens hier sollte man die „live_loop“ kennenlernen. Sie ermöglicht u.a., dass man einen Sound/Sample verändert, ohne dass dieser beim erneuten Klick auf „Run“ vervielfacht wird. „Live Loops“ sind auch die Basis für das Live Coding: Geht etwas schief beim Coden, dann läuft der Sound weiter (bis auf die Schleife, bei der man den Fehler gemacht hat). Das ist besonders dann von Vorteil, wenn man vor sich einige hundert Leute hat, die weitertanzen möchten.

Hier einige Beispiele zum Thema „Samples“, die ich in der Präsentation gezeigt habe:

uncomment do
  live_loop :amen do
    sample :loop_amen, rate: 1
    sleep sample_duration(:loop_amen, rate: 1)
  end
end

# Hip-Hop
comment do
  live_loop :amen do
    sample :loop_amen, rate: 0.75
    sleep sample_duration(:loop_amen, rate: 0.75)
  end
end

# Jungle
comment do
  live_loop :amen do
    sample :loop_amen, rate: 1.25
    sleep sample_duration(:loop_amen, rate: 1.25)
  end
end

# Backwards
comment do
  live_loop :amen do
    sample :loop_amen, rate: -1
    sleep sample_duration(:loop_amen, rate: 1)
  end
end

# Samples nur teilweise spielen
comment do
  live_loop :sample do
    sample :ambi_glass_rub #, start: 0.3, finish: 0.6, rate: [0.25, 0.75, 1.25].choose
    sleep 0.5
  end
end

Clapping Music von Steve Reich

William Denton hat die „Clapping Music“ von Steve Reich in Sonic Pi umgesetzt. Solche Experimente kann man gut als Basis für eigene Versuche verwenden:

# https://www.miskatonic.org/2015/01/12/clapping-music-on-sonic-pi/
# Steve Reich's 'Clapping Music' on Sonic Pi by William Denton

uncomment do
  use_bpm 400
  
  load_sample :drum_tom_lo_soft
  load_sample :drum_tom_mid_soft
  
  clapping = (ring 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0)
  
  
  13.times do |cycle|
    puts "Zyklus: #{cycle}"
    1.times do
      12.times do
        sample :drum_tom_lo_soft, rate: 2, pan: -0.5, amp: 1.25 if clapping.tick == 1
        second = look + cycle
        sample :drum_tom_mid_soft, rate: 2, pan:  0.5 if clapping[second] == 1
        sleep 1
      end
    end
  end
end

# Clapping Variation, by MB

comment do
  use_bpm 500
  
  clapping = (ring 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0)
  
  use_synth :fm
  use_synth_defaults divisor: 0.05 , depth: 1, attack: 0, sustain: 0, release: 1
  
  
  13.times do |cycle|
    2.times do |reps|
      with_fx :reverb, room: 0.5, mix: 0.25 do
        12.times do
          play :c3, pan: -1, amp: 1.25 if clapping.tick == 1
          second = look + cycle
          play :f4, pan:  -0.5, amp: 1.25 if clapping[second] == 1
          third = look + (cycle * 2)
          play :g5, pan:  -0.25, amp: 1 if clapping[third] == 1
          fourth = look + (cycle * 3)
          play :bb3, pan:  0, amp: 1 if clapping[fourth] == 1
          fifth = look + (cycle * 4)
          play :d4, pan:  0.5, amp: 0.75 if clapping[fifth] == 1
          sixth = look + (cycle * 5)
          play :g5, pan: 1, amp: 0.5 if clapping[sixth] == 1
          sleep 1
        end
      end
    end
  end
end

Drums

Ein einfaches Beispiel für ein Drum-Pattern, Bass, einen Hintergrundakkord und einer pentatonischen Melodie, die per Zufall variiert wird:

# Start with a very basic drum rhythm as base for a simple tune

use_bpm 125

# 00: The Hihat
live_loop :hihat do
  use_synth :cnoise
  use_synth_defaults release: 0.15
  with_fx :hpf, cutoff: 120 do
    play :c
    sleep 0.25
  end
end

# 01: This loop triggers a heart beat every bar
# It is initially synchronized with the :hihat
live_loop :bar, sync: :hihat do
  sleep 4
end

# 02: This is the kick drum
live_loop :kick, sync: :bar do
  with_fx :lpf, cutoff: 110 do
    sample :bd_haus
    sleep 0.25
    sample :bd_haus
    sleep 1.5
    sample :bd_haus
    sleep 0.25
    sample :bd_haus
    sleep 2
  end
end

# 03: The snare drum
live_loop :snare, sync: :bar do
  sleep 1
  with_fx :reverb, room: 0.5, mix: 0.5 do
    sample :drum_snare_hard, amp: 0.25
  end
  sleep 1
end

# 04: The bass
bassline = (ring\
            :c3, :c3, :r, :r,
            :r, :c3, :r, :c3,
            :c3, :r, :r, :r,
            :r, :r, :r, :r,
            :bb2, :bb2, :r, :r,
            :r, :bb2, :r, :bb2,
            :bb2, :r, :r, :r,
            :r, :eb3, :r, :bb2)

live_loop :bass, sync: :bar do
  use_synth :fm
  use_synth_defaults divisor: 2, depth: 2, release: 0.25
  play bassline.tick
  sleep 0.25
end

# 05: A very simple chord pattern
live_loop :chords, sync: :bar do
  use_synth :blade
  play chord(:f4, :sus4), release: 3, amp: 0.5
  sleep 4
end

# 06: A random melody from a pentatonic scale
live_loop :melody, sync: :bar do
  with_fx :reverb, room: 0.5, mix: 0.5 do
    8.times do
      play scale(:c5, :minor_pentatonic, num_octaves: 2).choose, amp: 0.25, release: 0.25
      sleep [0.25, 0.5, 0.75].choose
    end
  end
end

Tiny Hommage to early Detroit Techno

Schließlich ein schon aufwändigeres Beispiel. Um die einzelnen „live_loops“ zu hören, muss man im ‚Mixer‘ bei jeweiligen Instrument statt einer „0“ eine „1“ eingeben. Die „live_loop : synth2“ wird eingeblendet. Es dauert also eine Weile, bis man etwas hört:

# Re:publica 2016
# Early Detroit Techno-like Tune with Sonic Pi
# Inspired by "Particle Shower" by The Martian

use_bpm 125
set_sched_ahead_time! 0.5
use_debug false

# Very simple Mixer -------------------------------------------------

bass = 0
kick = 0
kick_plus = 0
shaker = 0
cabasa = 0
hihat = 0
tom = 0
xylo_lo = 0
xylo_hi = 0
cowbell = 0
snare = 0
synth1 = 0 # funky
synth2 = 0 # climb to the top
synth3 = 0 # background
melody = 0 # sweet bell

synth2_vol = (range 0, 0.75, step: 0.05).ramp
#synth2_vol = (ring 0.75)

# Timer Loops -------------------------------------------------------
live_loop :half_beat do
  sleep 0.5
end

live_loop :beat4 do
  # stop
  sync :half_beat
  sleep 4
end

live_loop :beat16 do
  sync :half_beat
  # sample :elec_beep, amp: 0.75
  sleep 16
end

# Bass Loop --------------------------------------------------------

bs = 0.5  # sound
bp = 0.095 # percussion

bass_rel = (ring\
            bs, bs,
            0, bp, bp, bs,
            0, bs, bs,
            0, bp, bp, bp)

bass_dur = (ring\
            0.5, 0.5,
            0.25, 0.25, 0.25, 0.25,
            0.25, 0.5, 0.25,
            0.25, 0.25, 0.25, 0.25)

bass_amp = (ring\
            1.25, 1,
            0, 1, 1, 1,
            0, 1, 1,
            0, 1.5, 1, 1)

live_loop :bass do
  stop if bass == 0
  sync :beat4
  
  use_synth :fm
  #use_synth_defaults divisor: 1, depth: 2, attack: 0, sustain: 0, cutoff: 120
  use_synth_defaults divisor: 1, attack: 0, sustain: 0, amp: 1, cutoff: 130,  depth: 3
  
  #with_fx :bpf, centre: 30, res: 0.25 do
  13.times do
    play :f1, release: bass_rel.tick, amp: bass_amp.look
    sleep bass_dur.look
  end
  #end
end

# Kick ----------------------------------------------------------------
live_loop :kick do
  stop if kick == 0
  sync :beat4
  
  with_fx :lpf, cutoff: 40 do
    4.times do
      sample :bd_gas, rate: 1, amp: 3
      sleep 1
    end
  end
end

live_loop :kick_plus do
  stop if kick_plus == 0
  sync :beat4
  
  with_fx :lpf, cutoff: 40, mix: 1 do
    4.times do
      sample :bd_zum, rate: 1, amp: 1
      sleep 1
    end
  end
end

# Pedal Hihat --------------------------------------------------------------

live_loop :hihat do
  stop if hihat == 0
  sync :beat4
  4.times do
    sleep 0.5
    sample :drum_cymbal_open, start: 0.025, finish: 0.15, rate: 1.1, pan: -0.3, amp: 0.35
    sleep 0.5
  end
end

# Shaker --------------------------------------------------------------------
live_loop :shaker do
  stop if shaker == 0
  sync :beat4
  use_synth :cnoise
  use_synth_defaults attack: 0, sustain: 4, release: 0, amp: 5
  with_fx :slicer, phase: 0.25, pulse_width: 0.35 do
    with_fx :hpf, cutoff: 130 do
      play 60
    end
    sleep 4
  end
end
# Tambourine ---------------------------------------------------------------
live_loop :cabasa do
  stop if cabasa == 0
  sync :beat4
  
  use_synth :noise
  use_synth_defaults attack: 0.0, decay: 0.02, release: 0.05, pan: 0.75, amp: 2
  
  with_fx :rhpf, cutoff: 123, res: 0.75, reps: 16 do
    if (spread 11, 16).tick then
      play :c1
    end
    sleep 0.25
  end
end

# Tom -----------------------------------------------------------------

tom_ptn = (ring 5 * 0.25, 2 * 0.25, 5 * 0.25, 2 * 0.25, 2 * 0.25)
#tom_rate = (ring 1.8, 1.8, 1.8, 1.8, 1.8, 1.8, 1.8, 1.8, 2.05, 1.8) # values for fuzz tom
tom_rate = (ring 2, 2, 2, 2, 2, 2, 2, 2, 2.25, 2)


live_loop :tom do
  stop if tom == 0
  sync :beat16
  with_fx :reverb, room: 0.5 do
    20.times do # pattern for 4 bars
      sample :elec_bong, start: 0, rate: tom_rate.look, amp: 1
      #sample :elec_fuzz_tom, start: 0.1, finish: 1, rate: tom_rate.look, amp: 0.75
      sleep tom_ptn.tick
    end
  end
end

# Cowbell ------------------------------------------------------------
cowbell_ptn = (ring\
               1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0,
               1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
               0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0,
               1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0)

cowbell_ptn2 = (ring\
                1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0,
                1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1,
                0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
                1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0)

live_loop :cowbell do
  stop if cowbell == 0
  sync :beat16
  
  with_fx :reverb, room: 0.25, mix: 0.5 do
    64.times do
      if cowbell_ptn.tick > 0 then
        sample :elec_bell, rate: 2.04, pan: 0.75, amp: cowbell_ptn.look, amp: 0.75
      end
      if cowbell_ptn2.look > 0 then
        #sample :elec_bell, rate: 4.08, pan: 0.75, amp: cowbell_ptn2.look * 0.75
        sample :elec_bell, rate: 2.74, pan: 0.75, amp: cowbell_ptn2.look * 0.75
      end
      sleep 0.25
    end
  end
end

# xylo: lo ------------------------------------------------------------

live_loop :xylo_lo do
  stop if xylo_lo == 0
  sync :beat4
  
  use_synth :fm
  use_synth_defaults divisor: 0, depth: 1, attack: 0, sustain: 0, release: 0.25, pan: 0.3, amp: 0.5
  
  with_fx :reverb, room: 0.5, mix: 0.5 do
    4.times do
      sleep 0.75
      play :eb4, attack: 0.25
      sleep 0.25
      play :g4, release: 0.25
      sleep 0.25
      play :f4, release: 0.25
      sleep 0.75
    end
  end
end


# Xylo: hi ------------------------------------------------------------

xylo_hi_nte = (ring :c5, :eb5, :c5, :c5, :c5, :eb5, :d5, :eb5)

xylo_hi_ptn = (ring\
               2 * 0.25,
               3 * 0.25,
               2 * 0.25,
               2 * 0.25,
               1 * 0.25,
               2 * 0.25,
               2 * 0.25,
               2 * 0.25)

live_loop :xylo_hi do
  stop if xylo_hi == 0
  sync :beat4
  
  use_synth :fm
  use_synth_defaults divisor: 0, depth: 1, attack: 0, sustain: 0, release: 0.25, amp: 0.5, pan: 0.3
  
  with_fx :reverb, room: 0.5, mix: 0.5 do
    32.times do
      play xylo_hi_nte.tick
      sleep xylo_hi_ptn.look
    end
  end
end

# Snare ---------------------------------------------------------------

live_loop :snare do
  stop if snare == 0
  sync :beat4
  
  ##| with_fx :echo, phase: [0.25, 0.5, 0.75].choose, decay: 8, mix: 0.5 do
    with_fx :reverb, room: 0.5, mix: 0.25 do
      2.times do
        sleep 1
        #sample :drum_snare_hard, rate: 2, pan: -0.3, amp: 1
        sample :elec_hi_snare, finish: 0.75, rate: 0.75, pan: -0.3, amp: 1.0
        sleep 1
      end
    end
  ##| end
end

# Synth 1: funky ------------------------------------------------

c1 = chord_invert(chord(:c2, 'm7'), 2)
c2 = chord_invert(chord(:c2, 'm7'), 1)
c3 = chord_invert(chord(:c2, 'm7+9'), 1)

c1 = chord_invert(chord(:c3, 'm7'), 2)
c2 = chord_invert(chord(:c3, 'm7'), 1)
c3 = chord_invert(chord(:c3, 'm7+9'), 1)

s1_chrds = (ring\
            c1, 0, c2, 0,
            0, c1, 0, c1,
            0, c2, 0, c3,
            0, c1, c2, c3,
            0, 0, c1, 0,
            0, c1, 0, c1,
            0, 0, c1, c1,
            0, c1, c2, c3)

live_loop :synth1 do
  stop if synth1 == 0
  sync :beat16
  
  # organ like
  # use_synth :fm
  # use_synth_defaults divisor: 0.5, depth: 2, attack: 0, sustain: 0, release: 0.25, pan: -0.5, amp: 1.5
  
  # clavinet like
  use_synth :pluck
  use_synth_defaults release: 0.25, noise_amp: 1, coef: 0.5, amp: 5
  
  with_fx :reverb, room: 0.25, mix: 0.25 do
    128.times do
      if s1_chrds.tick != 0 then
        play s1_chrds.look
      end
      sleep 0.25
    end
  end
end

# Synth 2: climb to the top -------------------------------------------------------

live_loop :synth2_climb do
  
  stop if synth2 == 0
  sync :beat16
  
  vol = synth2_vol.tick
  
  use_synth :tri
  use_synth :fm
  use_synth_defaults release: 0.25
  
  with_fx :flanger do
    8.times do
      sleep 0.75
      play :eb5, amp: 0.4 * vol, pan: -0.6
      play :c5, amp: 0.15 * vol, pan: -0.3
      sleep 0.25
      play :g5, amp: 0.4 * vol, pan: -0.3
      play :eb5, amp: 0.15 * vol, pan: -0.1
      sleep 0.25
      play :f5, amp: 0.4 * vol, pan: -0.1
      play :d5, amp: 0.15 * vol, pan: 0
      sleep 0.25
      play :a5, sustain: 0.25, release: 0.5, amp: 0.3 * vol, pan: 0.2
      play :f5, sustain: 0.25, release: 0.5, amp: 0.15 * vol, pan: 0.4
      sleep 0.5
    end
  end
end


live_loop :synth2_top do
  
  stop if synth2 == 0
  sync :beat16
  
  vol = synth2_vol.tick
  
  use_synth :tri
  use_synth :fm
  
  with_fx :flanger do
    8.times do
      sleep 0.5
      play :c6, sustain: 0.25, release: 0.15, amp: 0.5 * vol, pan: 0.5
      play :eb6, sustain: 0.25, release: 0.15, amp: 0.25 * vol, pan: 0.75
      sleep 1.25
      play :c6, sustain: 0.25, release: 0.25, amp: 0.5 * vol, pan: -0.8
      play :eb6, sustain: 0.25, release: 0.5, amp: 0.15 * vol, pan: -0.8
      sleep 0.25
    end
  end
end

# Synth 3: background -----------------------------------------------------

synth3_inv = (ring 0, 4, 3, 4, 0)

live_loop :synth3 do
  
  stop if synth3 == 0
  sync :beat16
  
  use_synth :blade
  use_synth_defaults vibrato_depth: 0.05, vibrato_rate: 0.5
  
  #use_synth :dpulse
  #use_synth_defaults amp: 0.25, detune: 0.1, pulse_width: 0.5, dpulse_width: 0.99, cutoff: 130
  
  with_fx :reverb, room: 0.5, mix: 0.5 do
    play chord_invert((ring :f5, :a5, :bb5, :c5, :eb5), synth3_inv.tick), attack: 3, sustain: 5, release: 5
    sleep 16
  end
end


# Melody: Sweet Bell ------------------------------------------------------

live_loop :melody do
  
  stop if melody == 0
  sync :beat16
  
  use_synth :beep
  use_synth_defaults attack: 0, sustain: 0.25, release: 0.75, pan: 0.5, amp: 0.5
  
  with_fx :reverb, room: 0.5, mix: 0.5 do
    play :f6, sustain: 0.5, release: 0.5, amp: 0.8, pan: -1
    sleep 0.5 * 3
    play :f5, release: 0.75, pan: 0.5
    sleep 0.5 * 4
    play :c6, release: 0.3, pan: -0.5
    sleep 0.5 * 2
    play :eb6, release: 0.75, pan: 1
    sleep 0.5 * 7
    play :c6, release: 0.3, pan: -0.1
    sleep 0.5 * 4
    play :a5, sustain: 0.1, release: 0.1, pan: -1
    sleep 0.5 * 1
    play :bb5, sustain: 0.1, release: 0.1, pan: 1
    sleep 0.5 * 1
    play :c6, sustain: 0.1, release: 0.1, pan: 0
    sleep 0.5 * 1
    play :f5, sustain: 0.75, release: 0.1, pan: 0.5
    sleep 0.5 * 9
  end
end

Links:

Website http://sonic-pi.net/
Github https://github.com/samaaron/sonic-pi
Google Groups https://groups.google.com/forum/#!forum/sonic-pi
Live Coding-Channel (Sam Aaron) https://www.livecoding.tv/samaaron/
Github (Leuphana Universität) https://github.com/mbutz/sonicpi-leuphana-ws1516
Support Sonic Pi https://www.patreon.com/samaaron