Hi! from Boulder, Colorado

I’m Spike, a coder, Ruby in fact, looking for a creative outlet (which seems to be a common refrain here).

I’m not sure when I first heard about Sonic Pi, but I’ve finally gotten around to playing with it and I’m having fun!

I believe you should alway bring something to share to a party, and to that end, here’s a little bass tab parser I’ve been working on, inspired by this Drum Tab Player.

The parsing is fragile, I don’t read music well nor play the base, so I’m pretty sure my bass map isn’t quite right, and I can’t really make it sounds like a bass yet, but it plays!

BASS_MAP = {
  #        0   1    2   3    4   5   6    7   8    9   10  11   12
  'G' => [:G1,:Af2,:A2,:Bf2,:B2,:C2,:Df2,:D2,:Ef2,:E2,:F2,:Gf2,:G2],
  'D' => [:D1,:Ef1,:E1,:F1,:Gf1,:G1,:Af2,:A2,:Bf2,:B2,:C2,:Df2,:D2],
  'A' => [:A1,:Bf1,:B1,:C1,:Df1,:D1,:Ef1,:E1,:F1,:Gf1,:G1,:Ff1,:A2],
  'E' => [:E1,:F1,:Fs1,:G1,:Af1,:A1,:Bf1,:B1,:C1,:Df1,:D1,:Ef1,:E1]
}

def tab_to_notes(tab_string)
  tab = tab_string.strip.split("\n") # Break up in to lines
  .map(&:chars) # Then in to characters
  .transpose # Then rotate 90 so we end up with something like:

  # [[ "G", "D", "A", "E", ">"]
  #  [ "—", "—", "3", "—", " "]
  #  [ "—", "—", "3", "—", " "]
  #  [ "—", "—", "1", "—", " "]
  # ]

  # That is, each beat is now it's own array.
  
  strings = tab.shift # Grab string label
  tab.shift # Remove space between label and tab, should be smarter
  
  tab.map do |beat|
    # Find a number or a "r" rest.
    index = beat.find_index {|n| n =~ /[r\d]/}
    next if index.nil? # Nothing found, probably just dashes
    string = strings[index]
    next :r if string == '>' # Rest
    fret = beat[index].to_i  # Convert the string into a number
    BASS_MAP[string][fret]   # And return the note
  end.compact
end

flashlight_tab = "
G ———————————————————————————————————————————————————————————————————————————————
D ———————————————————————————————————————————————————————————————————————————————
A ——3——3——1——1———————————————————————0——1——2—————————————————————————————————————
E ——————————————3——3——2——2——1—————1——————————————————————————————————————————————
>                              r                                                .
"

sna_tab = "
G|--------------------------------------------
D|------5-----------------5-------------------
A|-7--7---7--5-------7--7----7--5----5--------
E|-------------8--7----------------8----8--7~-
"

notes = tab_to_notes flashlight_tab

live_loop :bass do
  use_synth :piano
  notes.each do |note|
    play note, sustain: 0.2, amp: 0.3
    sleep 0.4
  end
2 Likes

Hi @spikex !

Thanks for sharing this piece of code.
It works great.

notice one end has to be added to your code, at the end :slight_smile: (copy / paste error)

well i use your code and something is weird into your map no ?
i suggest this :


BASS_MAP = {
  #        0   1    2   3    4   5   6    7   8    9   10  11   12
  'G' => [:G2,:Af2,:A2,:Bf2,:B2,:C3,:Df3,:D3,:Ef3,:E3,:F3,:Gf3,:G3],
  'D' => [:D2,:Ef2,:E2,:F2,:Gf2,:G2,:Af2,:A2,:Bf2,:B2,:C3,:Df3,:D3],
  'A' => [:A1,:Bf1,:B1,:C2,:Df2,:D2,:Ef2,:E2,:F2,:Gf2,:G2,:Ff2,:A2],
  'E' => [:E1,:F1,:Fs1,:G1,:Af1,:A1,:Bf1,:B1,:C2,:Df2,:D2,:Ef2,:E2]
}

Funny to learn that we can write :af2 which is equal to :ab2 (no doc for this ?)

play :ab2
sleep 1
play :af2
sleep 1

BASS_MAP = {
  #        0   1    2   3    4   5   6    7   8    9   10  11   12
  'G' => [:G2,:Af2,:A2,:Bf2,:B2,:C3,:Df3,:D3,:Ef3,:E3,:F3,:Gf3,:G3],
  'D' => [:D2,:Ef2,:E2,:F2,:Gf2,:G2,:Af2,:A2,:Bf2,:B2,:C3,:Df3,:D3],
  'A' => [:A1,:Bf1,:B1,:C2,:Df2,:D2,:Ef2,:E2,:F2,:Gf2,:G2,:Ff2,:A2],
  'E' => [:E1,:F1,:Fs1,:G1,:Af1,:A1,:Bf1,:B1,:C2,:Df2,:D2,:Ef2,:E2]
}

def tab_to_notes(tab_string)
  tab = tab_string.strip.split("\n") # Break up in to lines
  .map(&:chars) # Then in to characters
  .transpose # Then rotate 90 so we end up with something like:
  
  # [[ "G", "D", "A", "E", ">"]
  #  [ "—", "—", "3", "—", " "]
  #  [ "—", "—", "3", "—", " "]
  #  [ "—", "—", "1", "—", " "]
  # ]
  
  # That is, each beat is now it's own array.
  
  strings = tab.shift # Grab string label
  tab.shift # Remove space between label and tab, should be smarter
  
  tab.map do |beat|
    # Find a number or a "r" rest.
    index = beat.find_index {|n| n =~ /[r\d]/}
    next if index.nil? # Nothing found, probably just dashes
    string = strings[index]
    next :r if string == '>' # Rest
    fret = beat[index].to_i  # Convert the string into a number
    BASS_MAP[string][fret]   # And return the note
  end.compact
end

flashlight_tab = "
G ———————————————————————————————————————————————————————————————————————————————
D ———————————————————————————————————————————————————————————————————————————————
A ——3——31————1———————————————————————0——1——2—————————————————————————————————————
E ——————————————3——3——2——2——1—————1——————————————————————————————————————————————
>                              r                                                .
"

test_tab = "
G|-------------0-0
D|-----4-4-000--2-
A|----0-0---------
E|0000--------0---
"

notes = tab_to_notes test_tab

live_loop :bass do
  use_synth :fm
  use_synth_defaults attack: 0, decay: 0.15, sustain: 0.05, release:0.1, amp: 2
  use_octave +1
  notes.each do |note|
    play note
    sleep 0.25
  end
end

live_loop :hits do
  sample :drum_cymbal_pedal, amp: [1.5, 0.8, 1, 1.2].tick, attack: 0.1
  sleep 0.25
end

live_loop :bd do
  sample :drum_bass_hard
  sleep 0.5
end

live_loop :sn do
    sleep 3
    sample :elec_filt_snare
    sleep 1
  end

got a question how to get the 10,11, 12 th fret ? May i suggest to use c for 10 'd` for 11 and so on
to the 20th fret.

Thanks @nlb, that sounds really good! I appreciate the updated map.

I forgot that it was supposed to be b, not f for flat and, since it just worked, I didn’t notice.

You’re right, it should handle 10, 11, 12, and I know tabs sometimes use extra characters i.e 7~ where ~ means vibrato. I like the transpose as a simple way to break “beats” in their own array, but it clearly isn’t a general purpose solution. I have some other ideas I’m going to play with.

Thanks again!

1 Like