-
-
Save emlyn/33f72346e8f6abb25dd34ad4e85ed4c9 to your computer and use it in GitHub Desktop.
# Guitar Strumming - by Emlyn | |
# This tries to work out the guitar (or ukulele etc.) fingering for arbitrary chords (and tuning). | |
# It seems to work reasonably well for basic chords, but is quite naive and probably makes many mistakes. | |
# Ideas, bug reports, fixes etc. gratefully received, just comment below, or tweet @emlyn77. | |
# Feel free to make use of this code as you like (with attribution if you feel like it, but you don't have to). | |
# Thanks to @Project_Hell_CK for fixing the tuning, and spotting that it gets chord(:f, :major) not quite right. | |
# 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 tonic, name, c.to_a.zip(tuning).map {|n, s| if n == :r then 'x' else (n - note(s)) end}.join, c | |
c | |
end | |
# Strum a chord with a certain delay between strings | |
define :strum do |c, d=0.1| | |
in_thread do | |
play_pattern_timed c.to_a.drop_while{|n| [nil,:r].include? n}, d | |
end | |
end | |
use_debug false | |
use_bpm 120 | |
chords = ring((guitar :a, :m), (guitar :c, :M), (guitar :d, :M), (ring :r, :r, 53, 57, 60, 65), | |
(guitar :a, :m), (guitar :c, :M), (guitar :e, :M), (guitar :e, '7')) | |
live_loop :guit do | |
with_fx :reverb, room: 0.95 do | |
with_fx :lpf, cutoff: 115 do | |
with_synth :pluck do | |
tick | |
"D.DU.UDU".split(//).each do |s| | |
if s == 'D' # Down stroke | |
strum chords.look, 0.05 | |
elsif s == 'U' # Up stroke | |
with_fx :level, amp: 0.5 do | |
strum chords.look.reverse, 0.03 | |
end | |
end | |
sleep 0.5 | |
end | |
end | |
end | |
end | |
end |
This is awesome. Thanks for helping me to learn. One minor issue was that there was one thread death (line 39) and the suggested fix in Sonic Pi v3.3.1 was to change the line from drop_last to drop_while. This worked. Thanks very much again. Now I have something to study to get to what I'd like to do.
Actually drop_while still killed the thread at line 39.
Hi @tsmcgrath, I'm glad you find it useful!
I'm not sure exactly what you mean about changing drop_last to drop_while - I don't see drop_last used anywhere.
In any case, you could simplify line 39 to just play_pattern_timed c, d
and it will work almost as well.
It will just delay the start slightly for chords that don't include the lowest strings, but if that is an issue, you could also change line 29 to c = c.drop(first_root)
so that the unplayed strings are not included at the start.
hi, I'm interested in this, but not sure I exactly understand what's being proposed, or the challenges!
Are you proposing an extension of the built-in chord library ?
Is it specifically a stringed-instrument library, with all the voiced notes, perhaps with alternate chords voicings
(if sonicpi can even differentiate)
thanks for the shares!
Hi @jonnyhotchkiss, thanks for your interest!
At the moment there is no plan to actually include this in Sonic Pi (although if Sam was interested in adding something based on this I'd be happy to help out).
But for now, you should be able to copy and paste the guitar.rb
file into a Sonic Pi buffer and play around with it (e.g. try changing the chords and strum pattern), and incorporate it into other pieces.
There are some problems with newer version of Sonic Pi with your code. You can't apply .drop_while
to a Sonic Pi scale. You need to convert the scale to a list first. so you need c.to_a.drop_while
which will work. Same applies for some other operations eg .zip
Thanks @rbnpi, I hadn't looked at this code for a while, but I've updated now it so it should work again.
OH I LOVE THAT DB YOU FOUND!
And yeah I agree with you. I rather it be algorithmically generated. It's proving to be tricky for every chord. I don't know enough music theory either to pull it off perfectly. I will let you know if I make progress.