How to create a bassline

Hello, I’ve started to learn how to create songs with sonic pi for a few days, but there is one thing that I don’t understand with the notes in order to create a bassline. As a reference, I’m trying to do something like the bass guitar line from this song A Violent Yet Flammable World which starts at 11 seconds.

I’ve tried multiple things but I’m clearly missing something. Should I use ring() ? a series of chord() ?

How do you arrange the notes in your code so that the chords are played in this type of fashion?

Thanks,

Yann

Hi Yann,

As a start: what have you tried? it might give us a bit to work with if we understand the approaches you might have already attempted - seeing code examples is always helpful if you had any :slightly_smiling_face: (if you did have some you can share, don’t forget, you can give them nice syntax highlighting by adding a blank line before and after the code and putting three backticks ``` on these blank lines.)

Here is one way to code a bassline. But, there are definitely others which might serve your purpose better. To decide it would be could to know what you tried yet as @ethancrawford suggested.

Something like this might work:

live_loop :bass do
  l = [
    :gs4, :b4, :ds5,
    :gs4, :b4, :e5,
    :fs4, :b4, :ds5,
    :g4, :as4, :ds5,
  ]
  p = [0, 1, 2, 0, 2, 1]
  with_fx :lpf do
    at(line(0, 16), l.each_slice(3).to_a) do |bar|
      at [0, 0.75, 1.5, 2.5, 3, 3.5] do |_, i|
        synth :subpulse, note: bar[p[i]]-12, release: 1.25
        synth :saw, note: bar[p[i]]-24, attack: 0.5, release: 0.75, amp: 0.2
      end
    end
  end
  sleep 16
end
2 Likes

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

live_loop :bass do
l = [
:gs4, :b4, :ds5,
:gs4, :b4, :e5,
:fs4, :b4, :ds5,
:g4, :as4, :ds5,
]
p = [0, 1, 2, 0, 2, 1]
with_fx :lpf do
at(line(0, 16), l.each_slice(3).to_a) do |bar|
at [0, 0.75, 1.5, 2.5, 3, 3.5] do |_, i|
synth :subpulse, note: bar[p[i]]-12, release: 1.25
synth :saw, note: bar[p[i]]-24, attack: 0.5, release: 0.75, amp: 0.2
end
end
end
sleep 16
end

thanks for that sample of code, i love it

but i have some questions:

whats the purpose of p[]?
and why is the note “-12” or “-24” insted of taking different notes in l[]?

p[] is for changing the order of the 6 notes played in one bar?

thanks :smiley:

The subpulse and the saw play in different octaves, so they cannot play the same explicit note.

As for p[i], the order is the same every time, but the chord keeps changing.