How to create a bassline

Here is a more complex example: a nicely harmonized 12-bar blues. I am using this principle in many songs to create harmonic bass lines that fit a given hamonization / sequence of chords.

When the progression of chords is given, then you can select the scale mode accordingly. Please search for ‘theory of harmonies and scales /or/ jazz scales’ in the internet, you will find plenty of information on that. Of course, you can also experiment with crazy sounding scales that do not meet the standard theory.

Once the scale for the bass line is determined, I randomly select the notes from the scale. However, not at equal randomness but overweighting the first, third and fifth tone (see knit and choose inside the :my_play_bass definition). Sometimes I had also use the seventh note. This is because the I - III - V - VII notes are making up the corresponding chord that meets the bass line. And that is what creates a hamonic bass line.

### 12 bar blues

use_debug false
use_random_seed 4
use_bpm 80

load_samples [:drum_cymbal_open, :drum_cymbal_closed, :perc_snap,
              :drum_heavy_kick, :drum_snare_hard]

###############
# control loop

my_chords = []
live_loop :control, auto_cue: false, delay: 0.2 do
  
  case tick
  when 0
    set :run, 1
    set :s_drums, 1
    set :drum_pattern, 3
  when 1
    set :drum_pattern, 1
    set :s_piano, 1
    set :s_bass, 1
    tick_reset(:bar)
  end
  
  case tick(:bar)%12
  when 5
    set :drum_pattern, 4
  when 6
    set :drum_pattern, 1
  when 11
    set :drum_pattern, 6
  when 0
    set :drum_pattern, 1 if look(:bar) > 0
  end
  
  # sync other loops
  cue :bar, look(:bar)
  sleep 4
end


##############
# definitions

define :p do |i|
  # drum kit definitions
  a = 1.0
  case i
  when 0
    sample :drum_heavy_kick, amp: a*2
  when 1
    sample :drum_snare_hard, amp: a
  when 2
    sample :drum_cymbal_closed, amp: a
  when 3
    sample :drum_cymbal_open, amp: a*0.7, attack: 0.01, sustain: 0.1, release: 0.5
  when 4
    sample :drum_splash_hard, amp: a*0.6, attack: 0.05, release: 1
  when 5
    sample :perc_snap, amp: a*0.4
  end
end

define :play_drums do |pattern|
  tick_reset
  16.times do |n|
    tick
    case pattern
    when 0
      # do nothing
    when 1
      #standard 4/4 rock
      p(0) if ("x-------x-----x-"[look]=="x")
      p(1) if ("----x-------x---"[look]=="x")
      p(2) if ("x-x---x-x-x-----"[look]=="x")
      p(3) if ("--------------x-"[look]=="x")
    when 2
      # soft splash
      p(4) if ("x---------------"[look]=="x")
    when 3
      # snap
      p(5) if ("x---x---x---x---"[look]=="x")
    when 4
      p(0) if ("x-------x-------"[look]=="x")
      p(1) if ("----x------x-x--"[look]=="x")
      p(2) if ("x-x---x-x-x-x-x-"[look]=="x")
      p(3) if ("----------------"[look]=="x")
    when 5
      p(1) if ("------------x-xx"[look]=="x")
    when 6
      p(0) if ("x-------x-------"[look]=="x")
      p(1) if ("----x-------x-xx"[look]=="x")
      p(2) if ("x-x---x-x-x-----"[look]=="x")
      p(4) if ("------------x---"[look]=="x")
    end
    sleep 1.0/4 if n < 15 # omit last sleep
  end
end

define :my_play_pattern do |m|
  tick_reset(:notes)
  max_t = m[:n].length - 1
  
  m[:n].each do |n|
    t = tick(:notes)
    rel = m[:rel] ? m[:rel][t] : nil
    sus = m[:sus] ? m[:sus][t] : nil
    play n, sustain: sus, release: rel
    sleep m[:t][t] if t < max_t # omit last sleep
  end
end

define :my_play_bass do |m|
  m[:n].each_with_index do |n, i|
    sca = scale n[0], n[1]
    if sca.length > 6
      note = (knit sca[0], 2, sca[1], 1, sca[2], 2,
              sca[4], 2, sca[6]-12, 1).choose
    else
      note = (knit sca[0], 2, sca[1], 1, sca[2], 2,
              sca[4], 2).choose
    end
    play note
    sleep 1 if i < 3 # omit last sleep
  end
end

my_chords = (ring
             #1
             {n: (knit chord(:A2, :dom7, invert: 2), 4), t: [1, 1, 1, 1]},
             {n: (knit chord(:D3, :dom7, invert: 1), 2, chord(:Eb3, :dim7, invert: 1), 2), t: [1, 1, 1, 1]},
             {n: (knit chord(:A2, :dom7, invert: 2), 4), t: [1, 1, 1, 1]},
             {n: (knit chord(:E3, :m7, invert: 1), 2, chord(:A2, :dom7, invert: 2), 2), t: [1, 1, 1, 1]},
             #2
             {n: (knit chord(:D3, :dom7, invert: 1), 4), t: [1, 1, 1, 1]},
             {n: (knit chord(:Eb3, :dim7), 4), t: [1, 1, 1, 1]},
             #3
             {n: (knit chord(:A2, :dom7, invert: 2), 2, chord(:B2, :m7, invert: 0), 2), t: [1, 1, 1, 1]},
             {n: (knit chord(:Db3, :m7, invert: 0), 2, chord(:C3, :m7, invert: 0), 2), t: [1, 1, 1, 1]},
             {n: (knit chord(:B2, :m7, invert: 0), 4), t: [1, 1, 1, 1]},
             {n: (knit chord(:E3, :dom7, invert: 0), 4), t: [1, 1, 1, 1]},
             #4
             {n: (knit chord(:A2, :dom7, invert: 2), 4), t: [1, 1, 1, 1]},
             {n: (knit chord(:B2, :m7, invert: 0), 2, chord(:E3, :dom7, invert: 0), 2), t: [1, 1, 1, 1]},
             )

my_bass_line = (ring
                #1
                {n: (knit [:A2, :blues_major], 4)},
                {n: (knit [:D3, :mixolydian], 2, [:Eb3, :diminished2], 2)},
                {n: (knit [:A2, :blues_major], 4)},
                {n: (knit [:E3, :minor_pentatonic], 2, [:A3, :blues_major], 2)},
                #2
                {n: (knit [:D3, :mixolydian], 4)},
                {n: (knit [:Eb3, :diminished2], 4)},
                #3
                {n: (knit [:A2, :blues_major], 2, [:B2, :dorian], 2)},
                {n: (knit [:Db3, :minor_pentatonic], 2, [:C3, :blues_minor], 2)},
                {n: (knit [:B2, :dorian], 4)},
                {n: (knit [:E3, :augmented], 4)},
                #4
                {n: (knit [:A2, :blues_major], 4)},
                {n: (knit [:B2, :blues_minor], 2, [:E3, :augmented], 2)},
                )


###################
# instrument loops

# drum loop
with_fx :reverb, mix: 0.4, room: 0.7 do
  live_loop :my_drums, auto_cue: false, sync: :s_drums do
    sync :bar
    play_drums(get :drum_pattern)
    stop if (get :run)  == 0
  end
end

# bass loop
with_synth :fm do
  with_synth_defaults amp: 0.5, attack: 0.01, sustain: 0.9, release: 0.1, divisor: 2 do
    live_loop :my_bass, auto_cue: false, sync: :s_bass do
      m = my_bass_line.tick
      rep = m[:rep] ? m[:rep] : 1
      rep.times do
        sync :bar
        my_play_bass(m)
      end
      stop if (get :run)  == 0
    end
  end
end

# piano loop
with_fx :echo, phase: 0.25, mix: 0.15, decay: 1 do
  with_synth :piano do
    with_synth_defaults amp: 1.8, hard: 0.6, release: 1.2 do
      
      live_loop :my_piano, auto_cue: false, sync: :s_piano do
        m = my_chords.tick
        rep = m[:rep] ? m[:rep] : 1
        rep.times do
          sync :bar
          my_play_pattern(m)
        end
        stop if (get :run)  == 0
      end
    end
  end
end

And here a simple example showing the same principle:

use_debug false
use_bpm 80
st = 3.0

chords = [chord(:F2, :major7),
          chord(:G2, :minor7),
          chord(:C3, :dom7),
          chord(:F2, :major7),
          chord(:D3, :minor7),
          chord(:C3, :major7),
          chord(:G2, :dom7, inverse: 1),
          chord(:C3, :major7),
          ]

with_synth :piano do
  with_synth_defaults hard: 0.6, sustain: st, release: 1.0, amp: 1.6 do
    live_loop :piano, auto_cue: false do
      ch = chords.tick
      puts note_info(ch[0])
      tick_set :x, 0
      12.times do |i|
        play ch if ("x-x----x--x-"[tick(:x)]=="x")
        sleep st/12
      end
    end
  end
end



scales = [scale(:F2, :ionian),
          scale(:G2, :dorian),
          scale(:C3, :mixolydian),
          scale(:F2, :ionian),
          scale(:D3, :dorian),
          scale(:C3, :ionian),
          scale(:G3, :mixolydian),
          scale(:C3, :ionian),
          ]

num_notes = 6
with_synth :fm do
  with_synth_defaults attack: 0.01, sustain: st/num_notes-0.1, release: 0.1, divisor: 2 do
    live_loop :bass, auto_cue: false do
      sca = scales.tick
      notes = (knit sca[0], 2, sca[1], 1, sca[2], 2, sca[4], 2, sca[6]-12, 1).pick(num_notes)
      notes.each do |n|
        play n
        sleep st/num_notes
      end
    end
  end
end
2 Likes