Bach's "Prelude in C major BMV 846 allegro"

Hi all

Just started learning Bach’s "Prelude in C major BMV 846 allegro on piano and wondered how to code it in Sonic Pi.

By doing this I learned to use “case”, “define” and “density”.

Kind regards
Relaxnow

Ps. would love to see/hear how you would use the Bach code to create something.

# Bach's "Prelude in C major BMV 846 allegro"
# Piano music sheet http://www.agatidiperinaldo.org/Bach%20-%20Preludio%201.pdf

# My performance of this code https://youtu.be/ea7TmW_tzwk

# Ideas:
# - Send midi to 0-coast
# - Create visuals from midi notes in Hydra or Improviz
# - Create dynamics/velocity on amp from note sheet: p, ff
# - "Use_random_seed Time.now.to_i"
# - Create a drum beat

# How to use "case" https://www.rubyguides.com/2015/10/ruby-case/


use_bpm 132  #112 original tempo on note sheet    #132 or 142 my tempo

live_loop :met do #Metronon
  sleep 1
end


# Song structure
# - Playing 2.times arpeggio most of the time
# - Last 2 bars only played once
# - Last bar are a chord

# Arpeggio
define :my_notes_play do |my_notes, my_pattern|
  
  #  use_synth :saw   # :saw :dsaw :dpulse :dtri    # change here
  
  case my_pattern
  when "repeat_two_times"
    2.times do
      8.times do
        play my_notes.tick, release: 0.25, cutoff: range(70,120,step: 2).mirror.tick(:a),
          pan: ring(0.25,-0.25).choose #if spread(9,16).tick
        sleep 0.5
      end
    end
  when "dont_repeat"
    16.times do
      play my_notes.tick, release: 0.25, cutoff: range(70,120,step: 2).mirror.tick(:a),
        pan: ring(0.25,-0.25).choose #if spread(9,16).tick
      sleep 0.5
    end
  when "play_my_chord"
    play_chord my_notes, release: 0.5*8, cutoff: range(70,120,step: 2).mirror.tick(:a),
      pan: ring(0.25,-0.25).choose #if spread(9,16).tick
    sleep 0.5*8
  end
end

# Bass half notes
define :my_notes_play_bass do |my_notes, my_pattern|
  case my_pattern
  when "repeat_two_times"
    #use_synth :saw
    2.times do
      play my_notes.take(1).tick, release: 6*0.25, cutoff: range(70,120,step: 2).mirror.tick(:b),
        pan: ring(0.25,-0.25).choose, amp: 0.4 #if spread(3,7).tick
      sleep 4
    end
  when "dont_repeat"
    #use_synth :saw
    2.times do
      play my_notes.take(1).tick, release: 6*0.25, cutoff: range(70,120,step: 2).mirror.tick(:b),
        pan: ring(0.25,-0.25).choose, amp: 0.4 #if spread(3,7).tick
      sleep 4
    end
  when "play_my_chord"
    play my_notes.take(1).tick, release: 6*0.25, cutoff: range(70,120,step: 2).mirror.tick(:b),
      pan: ring(0.25,-0.25).choose, amp: 0.4 #if spread(3,7).tick
    sleep 4
  end
end

# Arpeggio + bass halfnote + which pattern to play
define :my_inst do |my_notes, my_pattern|
  use_synth :saw   # :saw :dsaw :dpulse :dtri    # change here
  in_thread do
    my_notes_play my_notes, my_pattern
  end
  #use_synth :saw   # :saw :dsaw :dpulse :dtri    # change here
  my_notes_play_bass my_notes, my_pattern
end



# Notes
with_fx :reverb, room: 0.8 do
  
  live_loop :BMV_inst, sync: :met do
    
    # stop
    #bar 1-4
    puts "Bar 1  ------"
    notes = [:c3, :e3, :g3, :c4, :e4, :g3, :c4, :e4]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 2"
    notes = [:c3, :d3, :a3, :d4, :f4, :a3, :d4, :f4]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 3"
    notes = [:b2, :d3, :g3, :d4, :f4, :g3, :d4, :f4]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 4"
    notes = [:c3, :e3, :g3, :c4, :e4, :g3, :c4, :e4]
    my_inst notes, "repeat_two_times"
    
    
    #bar 5-8
    puts "Bar 5  ------"
    notes = [:c3, :e3, :a3, :e4, :a4, :a3, :e4, :a4]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 6"
    notes = [:c3, :d3, :fs3, :a3, :d4, :fs3, :a3, :d4]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 7"
    notes = [:b2, :d3, :g3, :d4, :g4, :g3, :d4, :g4]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 8"
    notes = [:b2, :c3, :e3, :g3, :c4, :e3, :g3, :c4]
    my_inst notes, "repeat_two_times"
    
    
    #bar 9-12
    puts "Bar 9 ------"
    notes = [:a2, :c3, :e3, :g3, :c4, :e3, :g3, :c4]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 10"
    notes = [:d2, :a2, :d3, :fs3, :c4, :d3, :fs3, :c4]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 11"
    notes = [:g2, :b2, :d3, :g3, :b3, :d3, :g3, :b3]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 12"
    notes = [:g2, :bb2, :e3, :g3, :cs4,  :e3, :g3, :cs4]
    my_inst notes, "repeat_two_times"
    
    
    #bar 13-16
    puts "Bar 13 ------"
    notes = [:f2, :a2, :d3, :a3, :d4, :d3, :a3, :d4]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 14"
    notes = [:f2, :gs2, :d3, :f3, :b3, :d3, :f3, :b3]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 15"
    notes = [:e2, :g2, :c3, :g3, :c4, :c3, :g3, :c4]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 16"
    notes = [:e2, :f2, :a2, :c3, :f3, :a2, :c3, :f3]
    my_inst notes, "repeat_two_times"
    
    
    #bar 17-20
    puts "Bar 17 ------"
    notes = [:d2, :f2, :a2, :c3, :f3, :a2, :c3, :f3,]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 18"
    notes = [:g1, :d2, :g2, :b2, :f3, :g2, :b2, :f3,]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 19"
    notes = [:c2, :e2, :g2, :c3, :e3, :g2, :c3, :e3]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 20"
    notes = [:c2, :g2, :bb2, :c3, :e3, :bb2, :c3, :e3]
    my_inst notes, "repeat_two_times"
    
    
    #bar 21-24
    puts "Bar 21 ------"
    notes = [:f1, :f2, :a2, :c3, :e3, :a2, :c3, :e3]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 22"
    notes = [:fs1, :c2, :a2, :c3, :ds3, :a2, :c3, :ds3]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 23"
    notes = [:g1, :ds2, :b2, :c3, :ds3, :b2, :c3, :ds3]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 24"
    notes = [:gs1, :f2, :b2, :c3, :d3, :b2, :c3, :d3]
    my_inst notes, "repeat_two_times"
    
    
    #bar 25-28
    puts "Bar 25 ------"
    notes = [:g1, :f2, :g2, :b2, :d3, :g2, :b2, :d3]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 26"
    notes = [:g1, :e2, :g2, :c3, :e3, :g2, :c3, :e3]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 27"
    notes = [:g1, :d2, :g2, :c3, :f3, :g2, :c3, :f3]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 28"
    notes = [:g1, :d2, :g2, :b2, :f3, :g2, :b2, :f3]
    my_inst notes, "repeat_two_times"
    
    
    #bar 29-32
    puts "Bar 29 ------"
    notes = [:g1, :ds2, :a2, :c3, :fs3, :a2, :c3, :fs3]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 30"
    notes = [:g1, :e2, :g2, :c3, :g3, :g2, :c3, :g3]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 31"
    notes = [:g1, :d2, :g2, :c3, :f3, :g2, :c3, :f3]
    my_inst notes, "repeat_two_times"
    
    puts "Bar 32"
    notes = [:g1, :d2, :g2, :b2, :f3, :g2, :b2, :f3]
    my_inst notes, "repeat_two_times"
    
    
    
    #bar 33-36
    puts "Bar 33 ------"
    notes = [:c1, :c2, :g2, :bb2, :e3, :g2, :bb2, :e3]
    my_inst notes, "repeat_two_times"  # pattern "1" => repeating notes two times
    
    # Ending
    puts "Bar 34"
    notes = [:c1,:c2,:f2,:a2, :c3,:f3,:c3,:a2, :c3,:a2,:f2,:a2, :f2,:d2,:f2,:d2]  # pattern "2" => not repeating
    my_inst notes, "dont_repeat"
    
    puts "Bar 35"
    notes = [:c1,:b1,:g3,:b3, :d4,:f4,:d4,:b3, :d3,:b3,:g3,:b3, :d3,:f3,:e3,:d3]  # pattern "2" => not repeating
    my_inst notes, "dont_repeat"
    
    puts "Bar 36"
    notes = [:c1, :c2, :e3, :g3, :c4]  # pattern "3" => all notes played as a chord
    my_inst notes, "play_my_chord"
    
    sleep 4
    
    # Waiting before starting from begining
    sleep 8
  end
  
end






# ----------  Drum sketch to Bach ---------- #

my_drum_on = false #true/false

with_fx :reverb, room: rrand(0.3,0.9) do
  live_loop :drums, sync: :met do
    #stop
    sample :bd_haus, cutoff: 90, amp: 1.5, on: my_drum_on
    sleep 1
    sample :bd_haus, cutoff: 70, amp: 2.5, pan: ring(0.25, 0,-0.25).choose,
      on: my_drum_on if one_in(4)
    sleep 0.5
    sample :bd_haus, cutoff: 70, amp: 2.5, pan: ring(0.25, 0,-0.25).choose,
      on: my_drum_on if one_in(4)
    sleep 0.5
    sample :sn_generic, cutoff: ring(100,105,110,115,120,125,130).mirror.tick(:asd),
      amp: 0.5, finish: rrand(0.1,0.5), on: my_drum_on  if one_in(2)
    sleep 0.5
    sample :sn_generic, cutoff: ring(100,105,110,115,120,125,130).mirror.tick(:asd),
      amp: 0.5, finish: rrand(0.1,0.3), on: my_drum_on  if one_in(1)
    sleep 0.5*2
    sample :bd_haus, cutoff: 70, amp: 2.5, pan: ring(0.25, 0,-0.25).choose,
      on: my_drum_on if one_in(4)
    sleep 0.5
  end
end

# Sketch idea from density hihats from Mister Bomb
# - Sonic Pi Tutorial - Making Trap Hi Hats with Density function
# - https://youtu.be/3Fii-M0I1FA?t=543
define :hats do |d|
 # stop
  density d do
    sample :drum_cymbal_closed, on: my_drum_on, amp: 0.75,
      pan: ring(-0.25,0.25).choose, finish: rrand(0.02,0.08)
    sleep 1
  end
end
live_loop :hats, sync: :met do
  #hats (ring 1,2,3,4,5,4,3,2,1).tick
  #hats (ring 1,2,1).choose
  hats (ring 2,2,2,2,4).choose
  #hats rrand_i(1,4)
end




# Other idea #
# ---- Pop structure ---- # - change random_seed to fit this - #
#### Intro 4
#### #### #### #### Verse 16
#### (pre) 4
#### #### Chorus 8
#### #### Verse 8
#### (pre) 4
#### #### Chorus 8
#### #### Bridge 8
#### #### Chorus 8

# (knit Intro,4, Verse,16, Pre,4, Chorus,8, Verse,8, Pre,4, Chorus,8, Bridge,8,Chorus,8).tick


8 Likes

This is fantastic! Lovely work! The drums are really fun too :smile:

I did a fun experiment with the same piece a couple years ago:

######################################################################
# Infinite C Major Prelude from Bach's Well Tempered Clavier, book I #
######################################################################

use_random_seed 1004
use_bpm 65

# This complicated data structure encapsulates the chord voicings and
# harmonic transitions in this piece. Each entry maps a chord quality
# to a list of three or four lists of two elements each, one for each
# inversion: first a chord voicing for that inversion, expressed as a
# "figured bass" (i.e., intervals in semitones above the bass note,
# NOT the root), and second a list of chords it can move to, expressed
# as [bass movement in semitones, quality, inversion].

harmonies = (map major: [
               #        [root-position voicing],  [ => vi6],      [or => ii42],    [or => V7/IV]
               [ [4, 7, 12, 16], [ [0, :minor, 1], [0, :minor7, 3], [0, :dom7, 0], [-5, :dim7, 0] ] ],
               [ [3, 8, 15, 20], [ [0, :major7, 3] ] ],     # 1st inv
               [ [9, 12, 17, 21] ]                          # 2nd
             ],
             major7: [
               [ [7, 11, 12, 16], [ [1, :dim7, 0] ] ],      # root
               [ [3, 8, 15, 19] ],                          # 1st
               [ [12, 16, 17, 21] ],                        # 2nd
               [ [1, 5, 8, 13], [ [-2, :minor7, 0] ] ]      # 3rd
             ],
             minor: [
               [ [3, 7, 12, 15], [ [0, :dim7, 0] ] ],       # root
               [ [4, 9, 16, 21], [ [0, :dim7, 0] ] ],       # 1st
               [ [8, 12, 17, 20] ]                          # 2nd
             ],
             minor7: [
               [ [3, 7, 10, 15], [ [-7, :dom7, 0] ] ],      # root
               [ [4, 9, 16, 19] ],                          # 1st
               [ [12, 15, 17, 20] ],                        # 2nd
               [ [2, 9, 14, 17], [ [-1, :dom7, 1] ] ]       # 3rd
             ],
             dom7: [
               [ [7, 12, 16, 22], [ [5, :major, 0], [5, :major7, 0] ] ], # root
               [ [3, 8, 15, 18], [ [1, :major, 0], [-1, :dom7, 3] ] ],   # 1st
               [ [5, 9, 12, 15] ],                                       # 2nd
               [ [2, 6, 9, 14], [ [-1, :major, 1] ] ]                    # 3rd
             ],
             dim7: [
               [ [3, 9, 12, 18], [ [1, :major, 0], [1, :minor, 0],
                                   [-1, :dom7, 0], [2, :dim7, 0]] ]
             ]
             # only did root position of dim7 thanks to enharmonic equivalence,
             # so this Bach-bot has no qualms about modulation
             )

define :arpeggiate do | bass, quality, inversion |
  use_synth_defaults amp: 0.8
  dynamicwave = (line 0.2, 0.8, steps: 40).mirror
  # build and subside over several bars to simulate sensitive phrasing
  # this controls amp and decay
  2.times do
    tick_reset
    play bass, decay: 1.5, amp: dynamicwave.tick(:crescendotick) + 0.2, pan: -0.5
    sleep (ring 0.3, 0.25).tick(:accenttick)
    # a slight pause to emphasize the downbeat of each new harmony
    7.times do
      play bass + harmonies[quality][inversion][0][
      (ring 0, 1, 2, 3, 1, 2, 3,).tick],
        # the arpeggiation pattern, not including the first note
        amp: (line 0.2, -0.2, steps: 7).tick(:arptick) + dynamicwave.tick(:crescendotick),
        decay: dynamicwave.look(:crescendotick) / 2,
        pan: (line -0.3, 0.5, steps: 7).look(:arptick)
      # sweep from left to right, unrealistically
      sleep 0.25
    end
  end
end

define :next_chord do | bass, quality, inversion |
  vec = harmonies[quality][inversion][1].choose
  result = [bass + vec[0], vec[1], vec[2]]
  if bass + vec[0] > :f4.to_i
    vec[0] -= 12
  elsif bass + vec[0] < :c2.to_i
    vec[0] += 12
  end
  return bass + vec[0], vec[1], vec[2]
end

### piece starts here
use_synth :piano
use_synth_defaults decay: 0.5

starting_chords = (ring [:c4, :major, 0], [:c4, :minor7, 3])
current_chord = starting_chords.tick

with_fx :gverb, room: 50, mix: 0.5 do
  2.times do
    arpeggiate(*current_chord)
    current_chord = starting_chords.tick
  end
  loop do
    current_chord = next_chord(*current_chord)
    arpeggiate(*current_chord)
  end
end
3 Likes

So cool. Ty for sharing the code. I was wondering how to do this with random_seed and chords.

1 Like

Thanks! There must be simpler ways than what I came up with. There are much harder ways that would sound more Bach-like—e.g., with machine learning and smarter “expressivity” systems—but I like that Sonic Pi pushes me to keep the code relatively legible and compact.

Can you elaborate on your idea with the “pop structure” at the bottom? I can’t quite figure it out, but it sounds interesting!

1 Like

The structure is from this video https://youtu.be/UcmrgTNE9Cs?t=94
It’s not connected to the Bach code. It was just for me to remember the idea.

Idea - using a different random_seed number in each part of the structure: intro, verse, pre, chorus

Perhaps using the idea of Markov chains for beginners instead of random_seed

2 Likes

Ah, got it! Yeah, go for it!

  1. generate a billion pop songs
  2. upload to Spotify
  3. retire early
1 Like

It’s more like - when I write my own stuff I tend to only do part A and getting stuck on part B, C and D :slight_smile:

I would like to be able to jam like Steevio or a773 and I guess random_seed are the key to doing that combined with spread()+random_seed

a773

And as a newbie I still need to learn how to create chords like in your code.