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