Guitar backing track generator

Hi community!

This is a piece of code I wrote to generate, with few parameters, backing tracks played by the guitar.
You only need to instantiate the BackingTrack class. For example, here:

iggyPop_Passenger =
  BackingTrack.new(["Am", "F", "C", "G","Am", "F", "C", "E"],
                   ["x.UD.U"])

I specified the chord progression of the song (with the simple chiord notation used for example at ultimate-guitar) and the strumming pattern (the strum can be: up, down or stopped).

In this example:

nirvana_smells =
  BackingTrack.new(["F5","Bb5","Ab5","Db5"],
                   ["D..UD.xx","xxD.DUDU"],
                   160,
                   [:eb2, :ab2, :db3, :gb3, :bb3, :eb4]
                   )

it is specified also the bpm and the guitar tuning.

Here:

strokes_only_once =
  BackingTrack.new(["Dbm", "E", "B", "F#"],
                   [".DDUDUx","UxUxUD.D."],
                   118,
                   [:e2, :a2, :d3, :g3, :b3, :e4],
                   {"Dbm"=> "xxx999",
                    "E"=> "xxx997",
                    "B"=> "xxx879",
                    "F#"=> "xxx676"
                    })

there are chords customized for this particular song.

Let me know what do you think about this!

7 Likes

This is ingenious! The variations in stroke type make the :pluck synth sound much more like a guitar than it has any right to. Bravo.

2 Likes

This is so awesome!! Thanks for sharing!!
I figured adding the chord fingerings was really tedious - so I generated a bunch more fingerings if anyone wants them:

(code to generate is at bottom)

"Ab" => "466544",
"Abm" => "466444",
"Ab7" => "464574",
"Abmaj7" => "4x554x",
"Abm7" => "464444",
"Abdim" => "xx6434",
"A" => "x02220",
"Am" => "x02210",
"A7" => "x02020",
"Amaj7" => "x02224",
"Am7" => "x02010",
"Adim" => "x0121x",
"A#" => "x13331",
"A#m" => "x13321",
"A#7" => "x13131",
"A#maj7" => "x13231",
"A#m7" => "x13121",
"A#dim" => "x12320",
"Bb" => "x13331",
"Bbm" => "x13321",
"Bb7" => "x13131",
"Bbmaj7" => "x13231",
"Bbm7" => "x13121",
"Bbdim" => "x12320",
"B" => "x24442",
"Bm" => "x24432",
"B7" => "x24242",
"Bmaj7" => "x24342",
"Bm7" => "x20202",
"Bdim" => "x2343x",
"C" => "x32010",
"Cm" => "x35543",
"C7" => "x32310",
"Cmaj7" => "x35453",
"Cm7" => "x35343",
"Cdim" => "x3454x",
"C#" => "x43121",
"C#m" => "x46654",
"C#7" => "x46464",
"C#maj7" => "x46564",
"C#m7" => "x42100",
"C#dim" => "x42020",
"Db" => "x43121",
"Dbm" => "x46654",
"Db7" => "x46464",
"Dbmaj7" => "x46564",
"Dbm7" => "x42100",
"Dbdim" => "x42020",
"D" => "xx0232",
"Dm" => "xx0231",
"D7" => "xx0212",
"Dmaj7" => "xx0222",
"Dm7" => "xx0211",
"Ddim" => "xx0131",
"D#" => "x65343",
"D#m" => "xx1342",
"D#7" => "xx1323",
"D#maj7" => "xx1333",
"D#m7" => "xx1322",
"D#dim" => "x6787x",
"Eb" => "x65343",
"Ebm" => "xx1342",
"Eb7" => "xx1323",
"Ebmaj7" => "xx1333",
"Ebm7" => "xx1322",
"Ebdim" => "x6787x",
"E" => "022100",
"Em" => "022000",
"E7" => "020100",
"Emaj7" => "0x2444",
"Em7" => "020000",
"Edim" => "x7x986",
"F" => "133211",
"Fm" => "133111",
"F7" => "131241",
"Fmaj7" => "1x2210",
"Fm7" => "131111",
"Fdim" => "1x310x",
"F#" => "244322",
"F#m" => "244222",
"F#7" => "242352",
"F#maj7" => "2x332x",
"F#m7" => "242222",
"F#dim" => "133211",
"Gb" => "244322",
"Gbm" => "244222",
"Gb7" => "242352",
"Gbmaj7" => "2x332x",
"Gbm7" => "242222",
"Gbdim" => "xx4212",
"G" => "320033",
"Gm" => "355333",
"G7" => "320001",
"Gmaj7" => "3x443x",
"Gm7" => "353333",
"Gdim" => "xx5686",
"G#" => "466544",
"G#m" => "466444",
"G#7" => "464574",
"G#maj7" => "4x554x",
"G#m7" => "464444",
"G#dim" => "320033",

Here’s the code how I generated these (haven’t checked that they’re all the fingering’s one might want, but it’s a good start:

2 Likes

This is brilliant! I also had a look at getting Sonic Pi to play guitar chords a while ago, but focussed more on trying to generate the chord fingerings algorithmically (so that it should work with any tuning, or for other stringed instruments like the ukulele).

I’m not much of a guitar player, aside from having learned to strum a few chords, and I’m not aware of the intricacies of exactly how chord fingerings are decided. Therefore my code is quite naive, but it does seem to do reasonably well for the basic chords.

My code is here, or the main part that works out the chord fingerings is:

# Next note higher or equal to base note n, that is in the chord c
define :next_note do |n, c|
  # Make sure n is a number
  n = note(n)
  # Get distances to each note in chord, add smallest to base note
  n + (c.map {|x| (note(x) - n) % 12}).min
end

ukulele = [:g, :c, :e, :a]
guitar_standard = [:e2, :a2, :d3, :g3, :b3, :e4]

# Return ring representing the chord chrd, as played on a guitar with given tuning
define :guitar do |tonic, name, tuning=guitar_standard|
  chrd = (chord tonic, name)
  # For each string, get the next higher note that is in the chord
  c = tuning.map {|n| next_note(n, chrd)}
  # We want the lowest note to be the root of the chord
  root = note(chrd[0])
  first_root = c.take_while {|n| (n - root) % 12 != 0}.count
  # Drop up to half the lowest strings to make that the case if possible
  if first_root > 0 and first_root < tuning.count / 2
    c = (ring :r) * first_root + c.drop(first_root)
  end
  # Display chord fingering
  #puts c.zip(tuning).map {|n, s| if n == :r then 'x' else (n - note(s)) end}.join, c
  c
end

Maybe both bits of code could be combined to allow creating backing tracks with arbitrary chords and/or tunings, without having to find out the fingerings in advance.

2 Likes

@emlyn – Hey - that’s pretty clever!

Update:
On the gist itself – @Alb85 pointed out a mistake I had made. There’s some discussion on the gist itself, but posting here to note I updated my original post to:

  1. Have a new list of chord fingerings
  2. Points to an updated gist
2 Likes

hi @strickinato

super idea ! Well done !

I just add this code just for newbie to paste / cut into sonic pi and then have fun

use_debug false

class BackingTrack
  
  attr_accessor :chord_progression, :strumming_pattern, :bpm, :tuning, :guitar_chords
  
  def initialize(chord_progression,
                 strumming_pattern = ["D.DU.U.D",".DU.DU.."],
                 bpm=200,
                 tuning = [:e2, :a2, :d3, :g3, :b3, :e4],
                 guitar_chords= { "C" => "x32010",
                                  "G" => "320033",
                                  "A" => "x03330",
                                  "E" => "022100",
                                  "Am" => "x02210",
                                  "F" => "133211",
                                  "B" => "x24442",
                                  "D" => "xx0232",
                                  "F5"=> "133xxx",
                                  "F#"=> "244322",
                                  "Bb5"=> "688xxx",
                                  "Ab5"=> "466xxx",
                                  "Db5"=> "x466xxx"
                                  })
    
    @chord_progression = chord_progression
    @strumming_pattern   = strumming_pattern
    @bpm   = bpm
    @tuning   = tuning
    @guitar_chords = guitar_chords
  end
  
end


simple_track =
  BackingTrack.new(["G","D","C","C"])

iggyPop_Passenger =
  BackingTrack.new(["Am", "F", "C", "G","Am", "F", "C", "E"],
                   ["x.UD.U"])
  
nirvana_smells =
  BackingTrack.new(["F5","Bb5","Ab5","Db5"],
                   ["D..UD.xx","xxD.DUDU"],
                   160,
                   [:eb2, :ab2, :db3, :gb3, :bb3, :eb4]
                   )
  
strokes_only_once =
  BackingTrack.new(["Dbm", "E", "B", "F#"],
                   [".DDUDUx","UxUxUD.D."],
                   118,
                   [:e2, :a2, :d3, :g3, :b3, :e4],
                   {"Dbm"=> "xxx999",
                    "E"=> "xxx997",
                    "B"=> "xxx879",
                    "F#"=> "xxx676"
                    })
  

live_loop :guitar do
  play_backing_track iggyPop_Passenger
end


define :chord_name_to_notes do |chord_name, guitar_chords, tuning|
  
  guitar_pattern = guitar_chords[chord_name]
  notes = []
  i = 0
  guitar_pattern.split(//).each do |curr_string|
    if curr_string!='x'
      notes.push(tuning[i] + curr_string.to_i)
    end
    i=i+1
  end
  notes
end

define :single_stroke do |notes, stroke|
  if stroke == 'D' # Down stroke
    time = 0.03
    rel = 1.6
  elsif stroke == 'U' # Up stroke
    time = 0.03
    rel = 1.6
    notes=notes.reverse.take(3)
  elsif stroke == 'x' # Down stroke
    time = 0.001
    rel = 0.2
  end
  
  if stroke != '.'
    in_thread do
      play_pattern_timed notes, time, release: rel
    end
  end
  
end

define :play_strumming do |notes,strumming_pattern|
  strumming_pattern.look.split(//).each do |stroke|
    single_stroke notes, stroke
    sleep 0.5
  end
end

define :play_backing_track do |backing_track|
  use_bpm backing_track.bpm
  with_fx :reverb do
    with_fx :lpf, cutoff: 115 do
      with_synth :pluck do
        tick
        notes = chord_name_to_notes backing_track.chord_progression.look, backing_track.guitar_chords, backing_track.tuning
        play_strumming notes, backing_track.strumming_pattern
      end
    end
  end
end





# Next note higher or equal to base note n, that is in the chord c
define :next_note do |n, c|
  # Make sure n is a number
  n = note(n)
  # Get distances to each note in chord, add smallest to base note
  n + (c.map {|x| (note(x) - n) % 12}).min
end

ukulele = [:g, :c, :e, :a]
guitar_standard = [:e2, :a2, :d3, :g3, :b3, :e4]

Edit : remove the guitar function

just to point out that sometimes as i tried to change the name of chords to see if it’s interactive

Here i change the first chord of iggyPop tune, Dm

i got this error

If i stop and relaunch the same error message.

why ?

Up to now only the following chords are defined:

guitar_chords= { "C" => "x32010",
                                  "G" => "320033",
                                  "A" => "x03330",
                                  "E" => "022100",
                                  "Am" => "x02210",
                                  "F" => "133211",
                                  "B" => "x24442",
                                  "D" => "xx0232",
                                  "F5"=> "133xxx",
                                  "F#"=> "244322",
                                  "Bb5"=> "688xxx",
                                  "Ab5"=> "466xxx",
                                  "Db5"=> "x466xxx"
                                  })

In the next days I’ll add additional chords.
You can enrich the list by yourself in the initialize method of the BackingTrack class.

ok sorry :slight_smile: i didn’t read the code

Also, I see that you (@nlb) pasted my guitar function onto the end of the code, I don’t know if you were expecting it to used here? That won’t work at the moment, as the backing track generator would have to be modified to use it (maybe as a backup when a chord is not explicitly defined). I don’t have time to look into that now, but maybe later when I have some time I will look into integrating them.

@emlyn i removed your guitar function :slight_smile: in my post

1 Like

just add some chords (from discussion generate_fingers_for_generator.rb · GitHub) to have fun with next chords to be added by @Alb85

EDIT : i repaired the chords list and have added a “midi mode” to send to a vst host in example. Set mode_midi to true or to false.

EDIT 02 : trying with Download Free Acoustic guitar plugin: Revitar 2 by CutterMusic, an others Free Acoustic Guitar Simulation VSTs [2022!] - The Home Recordings

use_debug false

use_midi_defaults port: "lp-01_3" # adjust to your midi port

## test your midi vst communication
comment do
  midi :c
  stop
end

# to send midi to a vst host set to true / false if you want spi to play
mode_midi =true

class BackingTrack
  
  attr_accessor :chord_progression, :strumming_pattern, :bpm, :tuning, :guitar_chords
  
  def initialize(chord_progression,
                 strumming_pattern = ["D.DU.U.D",".DU.DU.."],
                 bpm=200,
                 tuning = [:e2, :a2, :d3, :g3, :b3, :e4],
                 guitar_chords= { "C" => "x32010",
                                  
                                  
                                  
                                  "G" => "320033",
                                  "E" => "022100",
                                  "F" => "133211",
                                  "B" => "x24442",
                                  "D" => "xx0232",
                                  "F5"=> "133xxx",
                                  "F#"=> "244322",
                                  "Bb5"=> "688xxx",
                                  "Db5"=> "x466xxx",
                                  
                                  
                                  "Ab" => "466544",
                                  "Ab5"=> "466xxx",
                                  "Abm" => "466444",
                                  "Ab7" => "464574",
                                  "Abmaj7" => "4x554x",
                                  "Abm7" => "464444",
                                  "Abdim" => "xx6434",
                                  "A" => "x02220",
                                  "Am" => "x02210",
                                  "A7" => "x02020",
                                  "Amaj7" => "x02224",
                                  "Am7" => "x02010",
                                  "Adim" => "x0121x",
                                  "A#" => "x13331",
                                  "A#m" => "x13321",
                                  "A#7" => "x13131",
                                  "A#maj7" => "x13231",
                                  "A#m7" => "x13121",
                                  "A#dim" => "x12320",
                                  "Bb" => "x13331",
                                  "Bbm" => "x13321",
                                  "Bb7" => "x13131",
                                  "Bbmaj7" => "x13231",
                                  "Bbm7" => "x13121",
                                  "Bbdim" => "x12320",
                                  "B" => "x24442",
                                  "Bm" => "x24432",
                                  "B7" => "x24242",
                                  "Bmaj7" => "x24342",
                                  "Bm7" => "x20202",
                                  "Bdim" => "x2343x",
                                  "C" => "x32010",
                                  "Cm" => "x35543",
                                  "C7" => "x32310",
                                  "Cmaj7" => "x35453",
                                  "Cm7" => "x35343",
                                  "Cdim" => "x3454x",
                                  "C#" => "x43121",
                                  "C#m" => "x46654",
                                  "C#7" => "x46464",
                                  "C#maj7" => "x46564",
                                  "C#m7" => "x42100",
                                  "C#dim" => "x42020",
                                  "Db" => "x43121",
                                  "Dbm" => "x46654",
                                  "Db7" => "x46464",
                                  "Dbmaj7" => "x46564",
                                  "Dbm7" => "x42100",
                                  "Dbdim" => "x42020",
                                  "D" => "xx0232",
                                  "Dm" => "xx0231",
                                  "D7" => "xx0212",
                                  "Dmaj7" => "xx0222",
                                  "Dm7" => "xx0211",
                                  "Ddim" => "xx0131",
                                  "D#" => "x65343",
                                  "D#m" => "xx1342",
                                  "D#7" => "xx1323",
                                  "D#maj7" => "xx1333",
                                  "D#m7" => "xx1322",
                                  "D#dim" => "x6787x",
                                  "Eb" => "x65343",
                                  "Ebm" => "xx1342",
                                  "Eb7" => "xx1323",
                                  "Ebmaj7" => "xx1333",
                                  "Ebm7" => "xx1322",
                                  "Ebdim" => "x6787x",
                                  "E" => "022100",
                                  "Em" => "022000",
                                  "E7" => "020100",
                                  "Emaj7" => "0x2444",
                                  "Em7" => "020000",
                                  "Edim" => "x7x986",
                                  "F" => "133211",
                                  "Fm" => "133111",
                                  "F7" => "131241",
                                  "Fmaj7" => "1x2210",
                                  "Fm7" => "131111",
                                  "Fdim" => "1x310x",
                                  "F#" => "244322",
                                  "F#m" => "244222",
                                  "F#7" => "242352",
                                  "F#maj7" => "2x332x",
                                  "F#m7" => "242222",
                                  "F#dim" => "133211",
                                  "Gb" => "244322",
                                  "Gbm" => "244222",
                                  "Gb7" => "242352",
                                  "Gbmaj7" => "2x332x",
                                  "Gbm7" => "242222",
                                  "Gbdim" => "xx4212",
                                  "G" => "320033",
                                  "Gm" => "355333",
                                  "G7" => "320001",
                                  "Gmaj7" => "3x443x",
                                  "Gm7" => "353333",
                                  "Gdim" => "xx5686",
                                  "G#" => "466544",
                                  "G#m" => "466444",
                                  "G#7" => "464574",
                                  "G#maj7" => "4x554x",
                                  "G#m7" => "464444",
                                  "G#dim" => "320033"
                                  })
    
    @chord_progression = chord_progression
    @strumming_pattern   = strumming_pattern
    @bpm   = bpm
    @tuning   = tuning
    @guitar_chords = guitar_chords
  end
  
end


cool =
  BackingTrack.new(["Dm","Dm","C","Am7"],
                   ["UxUx.UxD"],
                   110
                   )
  

iggyPop_Passenger =
  BackingTrack.new(["Am", "F", "C", "G","Am", "F", "C", "E"],
                   ["x.UD.U"])
  
nirvana_smells =
  BackingTrack.new(["F5","Bb5","Ab5","Db5"],
                   ["D..UD.xx","xxD.DUDU"],
                   160,
                   [:eb2, :ab2, :db3, :gb3, :bb3, :eb4]
                   )
  
strokes_only_once =
  BackingTrack.new(["Dbm", "E", "B", "F#"],
                   [".DDUDUx","UxUxUD.D."],
                   118,
                   [:e2, :a2, :d3, :g3, :b3, :e4],
                   {"Dbm"=> "xxx999",
                    "E"=> "xxx997",
                    "B"=> "xxx879",
                    "F#"=> "xxx676"
                    })
  



live_loop :guitar do
  play_backing_track nirvana_smells
end



define :chord_name_to_notes do |chord_name, guitar_chords, tuning|
  
  guitar_pattern = guitar_chords[chord_name]
  notes = []
  i = 0
  guitar_pattern.split(//).each do |curr_string|
    if curr_string!='x'
      notes.push(tuning[i] + curr_string.to_i)
    end
    i=i+1
  end
  notes
end

define :single_stroke do |notes, stroke|
  
  if stroke == 'D' # Down stroke
    time = 0.03
    rel = 1.6
  elsif stroke == 'U' # Up stroke
    time = 0.03
    rel = 1.6
    notes=notes.reverse.take(3)
  elsif stroke == 'x' # Down stroke
    time = 0.001
    rel = 0.2
  end
  
  
  if stroke != '.'
    in_thread do
      if (!mode_midi)
        play_pattern_timed notes, time, release: rel
      elsif
        notes.length.times do
          midi notes.tick('n'), sustain: rel, channel: 3
          sleep time
        end
      end # end if stroke
    end
  end
end

define :play_strumming do |notes,strumming_pattern|
  strumming_pattern.look.split(//).each do |stroke|
    single_stroke notes, stroke
    sleep 0.5
  end
end

define :play_backing_track do |backing_track|
  use_bpm backing_track.bpm
  with_fx :reverb do
    with_fx :lpf, cutoff: 110 do
      with_synth :pluck do
        tick
        notes = chord_name_to_notes backing_track.chord_progression.look, backing_track.guitar_chords, backing_track.tuning
        play_strumming notes, backing_track.strumming_pattern
      end
    end
  end
end





# Next note higher or equal to base note n, that is in the chord c
define :next_note do |n, c|
  # Make sure n is a number
  n = note(n)
  # Get distances to each note in chord, add smallest to base note
  n + (c.map {|x| (note(x) - n) % 12}).min
end

ukulele = [:g, :c, :e, :a]
guitar_standard = [:e2, :a2, :d3, :g3, :b3, :e4]
1 Like

So, I had a go at merging everything in here together: now it will prefer a chord fingering passed in to the backing track if available, otherwise if it’s in the list of standard chords (using the list generated by @strickinato) and using standard tuning it will use that, otherwise it will fall back to generating it with my code.

I removed chords from the standard list that are generated the same by my code, in order to keep the size down. I was actually surprised by how many don’t match - I might be able to use these examples to improve my algorithm.

I also removed the BackingTrack class, and just passed the parameters into a function. This was mainly because with it included, the code became too long to run in a single buffer. You can still combine all track parameters into a hash object if you want to keep them together (as I’ve done with the Strokes example).

The way the tuning works is slightly different than before - now it will adjust the fingering to still try to generate the named chord, so the Nirvana example had to be tweaked a bit - for this I added a transpose option to shift the chords up or down a specified number of semitones.

I also added lower-case u and d strokes, which are the same as U and D, but just a bit quieter, so you can more easily add some basic dynamics.

Oh, and I added one more example track.

One thing I found quite fun, was to take one of the example tracks, and just add a tuning: ukulele_tuning parameter, and it converts it into a ukulele version!

Here’s the code:

guitar_tuning = [:e2, :a2, :d3, :g3, :b3, :e4]
ukulele_tuning = [:g, :c, :e, :a]

guitar_chords = {
  guitar_tuning => {
    "F5"=> "133xxx",
    "Bb5"=> "688xxx",
    "Db5"=> "x466xx",
    
    "Ab" => "466544",
    "Ab5"=> "466xxx",
    "Abm" => "466444",
    "Ab7" => "464574",
    "Abmaj7" => "4x554x",
    "Abm7" => "464444",
    "Abdim" => "xx6434",
    "Amaj7" => "x02224",
    "Adim" => "x0121x",
    "A#" => "x13331",
    "A#7" => "x13131",
    "A#maj7" => "x13231",
    "Bb" => "x13331",
    "Bb7" => "x13131",
    "Bbmaj7" => "x13231",
    "B" => "x24442",
    "Bm" => "x24432",
    "B7" => "x24242",
    "Bmaj7" => "x24342",
    "Bm7" => "x20202",
    "Bdim" => "x2343x",
    "Cm" => "x35543",
    "C7" => "x32310",
    "Cmaj7" => "x35453",
    "Cm7" => "x35343",
    "Cdim" => "x3454x",
    "C#m" => "x46654",
    "C#7" => "x46464",
    "C#maj7" => "x46564",
    "C#m7" => "x42100",
    "Dbm" => "x46654",
    "Db7" => "x46464",
    "Dbmaj7" => "x46564",
    "Dbm7" => "x42100",
    "Ddim" => "xx0131",
    "D#" => "x65343",
    "D#7" => "xx1323",
    "D#maj7" => "xx1333",
    "D#dim" => "x6787x",
    "Eb" => "x65343",
    "Eb7" => "xx1323",
    "Ebdim" => "x6787x",
    "Emaj7" => "0x2444",
    "Edim" => "x7x986",
    "F" => "133211",
    "F7" => "131241",
    "Fmaj7" => "1x2210",
    "Fdim" => "1x310x",
    "F#" => "244322",
    "F#m" => "244222",
    "F#7" => "242352",
    "F#maj7" => "2x332x",
    "F#m7" => "242222",
    "F#dim" => "133211",
    "Gb" => "244322",
    "Gbm" => "244222",
    "Gb7" => "242352",
    "Gbmaj7" => "2x332x",
    "Gbm7" => "242222",
    "Gbdim" => "xx4212",
    "G" => "320033",
    "Gm" => "355333",
    "G7" => "320001",
    "Gmaj7" => "3x443x",
    "Gm7" => "353333",
    "Gdim" => "xx5686",
    "G#" => "466544",
    "G#m" => "466444",
    "G#7" => "464574",
    "G#maj7" => "4x554x",
    "G#m7" => "464444",
    "G#dim" => "320033",
}}

define :next_note do |n, c|
  # Make sure n is a number
  n = note(n)
  # Get distances to each note in chord, add smallest to base note
  n + (c.map {|x| (note(x) - n) % 12}).min
end

# Return ring representing the chord chrd, as played on a guitar with given tuning
define :guitar_chord do |tonic, name, tuning=guitar_tuning|
  chrd = (chord tonic, name)
  # For each string, get the next higher note that is in the chord
  c = tuning.map {|n| next_note(n, chrd)}
  # We want the lowest note to be the root of the chord
  root = note(chrd[0])
  first_root = c.take_while {|n| (n - root) % 12 != 0}.count
  # Drop up to half the lowest strings to make that the case if possible
  if first_root > 0 and first_root < tuning.count / 2
    c = (ring :r) * first_root + c.drop(first_root)
  end
  c
end

define :split_chord do |guitar_pattern, tuning|
  guitar_pattern.split(//).zip(tuning).map{|curr_string, t|
    curr_string=='x' ? nil : t + curr_string.to_i
  }
end

define :chord_notes do |chord_name, fingerings, tuning|
  tuning ||= guitar_tuning
  if fingerings && fingerings[chord_name]
    notes = split_chord(fingerings[chord_name], tuning)
  elsif guitar_chords[tuning] and guitar_chords[tuning][chord_name]
    notes = split_chord(guitar_chords[tuning][chord_name], tuning)
  else
    m = /([A-G][b#]?)(.*)/.match(chord_name)
    tonic = m[1].sub('#', 's')
    name = m[2].empty? ? "M" : m[2].sub('maj7', 'major7')
    notes = guitar_chord tonic, name, tuning
  end
  notes.filter{|n| ![nil,:r].include? n}.ring
end

define :single_stroke do |notes, stroke, midi|
  amp = 1
  time = 0.03
  rel = 1.6
  if stroke == 'D' # Down stroke
  elsif stroke == 'd' # Down stroke (quieter)
    amp = 0.7
  elsif stroke == 'U' # Up stroke
    notes=notes.reverse.take(3)
  elsif stroke == 'u' # Up stroke (quieter)
    notes=notes.reverse.take(3)
    amp = 0.7
  elsif stroke == 'x' # Down stroke
    time = 0.001
    rel = 0.2
  elsif stroke == '.'
  else
    puts "Unrecognosed stroke:" + stroke
    stop
  end
  
  if stroke != '.'
    in_thread do
      if midi
        notes.each do |n|
          midi n, sustain: rel, channel: 3, vel: amp * 127
          sleep time
        end
      else
        play_pattern_timed notes, time, release: rel, amp: amp
      end # end if stroke
    end
  end
end

define :play_strumming do |notes,strumming_pattern,midi|
  strumming_pattern.split(//).each do |stroke|
    single_stroke notes, stroke, midi
    sleep 0.5
  end
end

define :play_backing_track do |chords: [], pattern: ["D.DU.U.D",".DU.DU.."], bpm: nil, tuning: nil, fingerings: nil, transpose: 0, midi: false|
  with_transpose transpose do
    with_bpm bpm || current_bpm do
      with_fx :reverb do
        with_fx :lpf, cutoff: 110 do
          with_synth :pluck do
            tick :backing_track
            notes = chord_notes chords.look(:backing_track), fingerings, tuning
            play_strumming notes, pattern.look(:backing_track), midi
          end
        end
      end
    end
  end
end


live_loop :guitar do
  # Uncomment one of the tracks below:
  
  # Simple track
  #play_backing_track(chords: ["G","D","C","C"], bpm: 140)
  
  # Nirvana: Smells Like Teen Spirit
  #play_backing_track(chords: ["F5","Bb5","Ab5","Db5"], pattern: ["D..UD.xx","xxD.DUDU"], bpm: 160, transpose: -1)
  
  # Iggy Pop: The Passenger
  #play_backing_track(chords: ["Am", "F", "C", "G","Am", "F", "C", "E"], pattern: ["x.UD.U"], bpm: 200)
  
  # The Strokes: You Only Live Once
  track = {chords: ["Dbm", "E", "B", "F#"],
           pattern: [".DDUDUx","UxUxUD.D."],
           bpm: 118,
           fingerings: {"Dbm" => "xxx999",
                        "E" => "xxx997",
                        "B" => "xxx879",
                        "F#" => "xxx676"}}
  #play_backing_track(**track)
  
  # Oasis: Wonderwall
  #play_backing_track(chords: ["Em7", "G", "Dsus4", "A7sus4"],
  #                   pattern: ['D.d.D.dU', 'dUD.D.du', 'DUD.D.d', 'U.U.uduDu'] * 5 + ['D.d.D.dU', 'dUD.D.du', 'DUD.D.d', 'U.U.uduD.'],
  #                   bpm: 136)
end

2 Likes

hi @emlyn

a typo :slight_smile: .

We need to uncomment play_backing_track

image

Cheers

Oops sorry, I should have made that clearer. There are several example tracks in there all commented out, you have to uncomment one of them to play it.

1 Like