Walking Bass

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()

Choosing Notes

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, 1, 4)
  }


Scheduling

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, 0, 4)

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, 1, 4)

    local note3 = chordNote(chord, note2)
    note3.velocity(velocity)
    bass.schedule(note3, measure, 2, 4)

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, 3, 4)
  }

Audio Output

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.


}



Complete Script

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, 1, 4)
  }

  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, 0, 4)

    local note2 = chordNote(chord, rootNote)
    note2.velocity(velocity)
    bass.schedule(note2, measure, 1, 4)

    local note3 = chordNote(chord, note2)
    note3.velocity(velocity)
    bass.schedule(note3, measure, 2, 4)

    local note4 = leadingNote(next, note3)
    note4.velocity(velocity)
    bass.schedule(note4, measure, 3, 4)
  }

  function output() { return bass }

}


Tutorial IndexList of All Examples


Creative Commons License This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.