This script implements a walking bass line. It uses a random number generator combined with knowledge of the chord progression to create a unique line for each measure.
The script makes use of the Calf FluidSynth LV2 plugin, the configuration for this plugin is defined in a separate class included here via the dofile directive:
dofile("fluidsynth.bip")
Define the class and member data:
class WalkingBass {
bass = FluidSynth(32)
rand = Math.Random()
Define a function to choose a random note from the given chord, loop until the note is a different pitch than the last note to avoid repeats.
function chordNote(chord, last) {
local ret = last
while(ret.pitch() == last.pitch()) {
local index = rand.integer(chord.size())
ret = chord.note(index)
}
return ret
}
Define a function to choose a leading note into the next chord.
function leadingNote(next, last) {
local root = next.note(0).pitch()
Loop until the chosen pitch is different than that of the last note.
local pitch = last.pitch()
while(pitch == last.pitch()) {
Choose an option for leading note at random:
switch(rand.integer(6)) {
Pitches a half step above or below the next root:
case 0:
pitch = root + 1
break
case 1:
pitch = root - 1
break
Pitches a full step above or below the next root:
case 2:
pitch = root + 2
break
case 3:
pitch = root - 2
break
Pitches a perfect fifth above or below the next root:
case 4:
pitch = root + 7
break
case 5:
pitch = root - 5
break
}
}
Create and return a MIDI note from the chosen pitch:
return Midi.Note(pitch, 127, 0.25)
}
Define the main method to schedule a measure of bass. This method takes four arguments:
function schedule(measure, power, chord, next) {
The chords as passed in are centered around middle C, transpose them two octaves lower so they contain bass notes.
chord.transpose(-24)
next.transpose(-24)
Calculate a velocity level based on the incoming power level, play the bass notes harder when the power is high and softer for lower power values:
local velocity = 64 + power / 2
Schedule the root note of the chord as the first quarter note in the measure.
local rootNote = chord.note(0)
rootNote.velocity(velocity)
bass.schedule(rootNote, measure)
Schedule a chord note as the second and third quarter notes in the measure.
local note2 = chordNote(chord, rootNote)
note2.velocity(velocity)
bass.schedule(note2, measure + 0.25)
local note3 = chordNote(chord, note2)
note3.velocity(velocity)
bass.schedule(note3, measure + 0.5)
Schedule a leading note to the next chord as the last quarter note in the measure.
local note4 = leadingNote(next, note3)
note4.velocity(velocity)
bass.schedule(note4, measure + 0.75)
}
A simple function to return an Audio.Source which allows the class to connect to a mixer etc.
function output() { return bass }
End of the class. See the main script for an example of using this class.
}
dofile("fluidsynth.bip")
class WalkingBass {
bass = FluidSynth(32)
rand = Math.Random()
function chordNote(chord, last) {
local ret = last
while(ret.pitch() == last.pitch()) {
local index = rand.integer(chord.size())
ret = chord.note(index)
}
return ret
}
function leadingNote(next, last) {
local root = next.note(0).pitch()
local pitch = last.pitch()
while(pitch == last.pitch()) {
switch(rand.integer(6)) {
case 0:
pitch = root + 1
break
case 1:
pitch = root - 1
break
case 2:
pitch = root + 2
break
case 3:
pitch = root - 2
break
case 4:
pitch = root + 7
break
case 5:
pitch = root - 5
break
}
}
return Midi.Note(pitch, 127, 0.25)
}
function schedule(measure, power, chord, next) {
chord.transpose(-24)
next.transpose(-24)
local velocity = 64 + power / 2
local rootNote = chord.note(0)
rootNote.velocity(velocity)
bass.schedule(rootNote, measure)
local note2 = chordNote(chord, rootNote)
note2.velocity(velocity)
bass.schedule(note2, measure + 0.25)
local note3 = chordNote(chord, note2)
note3.velocity(velocity)
bass.schedule(note3, measure + 0.5)
local note4 = leadingNote(next, note3)
note4.velocity(velocity)
bass.schedule(note4, measure + 0.75)
}
function output() { return bass }
}
This work is licensed under a
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.