Hey folks, I’ve been working on creating generative music compositions in Sonic Pi with the hopes of creating a generative “album” of “tracks” that play indefinitely. I’ve been using Sonic Pi to generate MIDI data, that is then paired up with a DAW (in my case Ableton) to synthesize the sound. You can find an example of one of these compositions here.
Now that I have the basic process down (tutorials coming soon!) I’ve started making more complex compositions. Right now, for instance I’m working on a track that has five instruments with different degrees of variability, MIDI CCs that act as LFOs, and this thing I’m calling a “phrase controller.” In most linear music, you’ll have different sections of a track that play in a particular order (intro, bridge, hook, etc), but with generative music, you don’t want the track to necessarily play the same sections over and over again in the same order, so I’m using this phrase controller to determine what parts are playing and with what variables at any given time.
I think the ideas behind what I’m creating are really interesting, and I’m really digging the sounds. However, its become obvious that the code is majorly overly complex and bloated. This has become particularly apparent given that when in Phrase 3, drum beats have started skipping and jumping. I’d love any tips and tricks you have on simplifying and optimizing the code!
# LUPIN TULPA
/
Beginning code:
- set BPM of track
- create :tick to sync the rest of the live_loops
/
use_bpm 88
live_loop :tick do
sleep 1
end
/
Define instruments:
- use parameters in function that can be controlled by "phrase_controller"
- set midi channels
/
define :choir do
use_midi_defaults channel: 1
sus = (knit 4, 2, 8, 1, 16, 1).choose
##| sus = 1
midi (scale :e3, :major, num_octaves: 1).choose, sustain: sus*3
sleep sus
midi (scale :e3, :major, num_octaves: 1).choose, sustain: sus*2
sleep sus
midi (scale :e3, :major, num_octaves: 1).choose, sustain: sus
sleep sus
end
define :bass do |sus|
use_midi_defaults channel: 2
bass_note = [:e1, :g1, :a1, :g1].tick
midi bass_note, sustain: sus
sleep sus
midi bass_note, sustain: sus
sleep sus
midi :es1, sustain: sus
sleep sus
midi bass_note, sustain: sus
sleep sus
end
define :guit do |prob1, prob2, prob3, speed|
sus = (knit 0.25, 2, 0.5, 1).choose
##| sus = 1
if one_in(prob1)
midi (scale :e3, :major, num_octaves: 1).choose, sustain: sus*speed, channel: 3
sleep sus*speed
midi (scale :e4, :major, num_octaves: 1).choose, sustain: sus*speed, channel: 3
sleep sus*speed
midi (scale :e3, :major, num_octaves: 1).choose, sustain: sus*speed, channel: 3
sleep sus*speed
midi (scale :e3, :major, num_octaves: 1).choose, sustain: sus*speed, channel: 3
sleep sus*speed
end
if one_in(prob2)
midi (scale :e3, :major, num_octaves: 1).choose, sustain: sus*speed, channel: 4
sleep sus*speed
midi (scale :e4, :major, num_octaves: 1).choose, sustain: sus*speed, channel: 4
sleep sus*speed
midi (scale :e3, :major, num_octaves: 1).choose, sustain: sus*speed, channel: 4
sleep sus*speed
midi (scale :e3, :major, num_octaves: 1).choose, sustain: sus*speed, channel: 4
sleep sus*speed
end
if one_in(prob3)
midi (scale :e3, :major, num_octaves: 2).choose, sustain: sus*speed, channel: 3
midi (scale :e3, :major, num_octaves: 2).choose, sustain: sus*speed, channel: 4
sleep sus*2*speed
midi (scale :e4, :major, num_octaves: 2).choose, sustain: sus*speed, channel: 3
midi (scale :e4, :major, num_octaves: 2).choose, sustain: sus*speed, channel: 4
sleep sus*2*speed
midi (scale :e3, :major, num_octaves: 2).choose, sustain: sus*speed, channel: 3
midi (scale :e3, :major, num_octaves: 2).choose, sustain: sus*speed, channel: 4
sleep sus*2*speed
midi (scale :e3, :major, num_octaves: 2).choose, sustain: sus*speed, channel: 3
midi (scale :e3, :major, num_octaves: 2).choose, sustain: sus*speed, channel: 4
sleep sus*2*speed
end
sleep 0.25
end
define :drum1 do
use_midi_defaults channel: 5, sustain: 0.25
midi :g2
sleep 2
midi :g2
sleep 0.25
midi :f2
sleep 1.75
end
define :drum2 do
use_midi_defaults channel: 5, sustain: 0.25
midi :g2
sleep 0.5
midi :d2
sleep 1.5
midi :g2
sleep 0.25
midi :f2
sleep 1.75
end
define :drum3 do
use_midi_defaults channel: 5, sustain: 0.25
midi :g2
sleep 0.5
midi :d2
sleep 1.5
midi :g2
sleep 0.25
midi :f2
sleep 0.5
midi :gs2
sleep 0.25
midi :a2
sleep 0.25
midi :d2
sleep 0.75
end
define :drum4 do
use_midi_defaults channel: 5, sustain: 0.25
midi :g2
sleep 0.5
midi :d2
sleep 0.5
midi :gs2
sleep 1
midi :g2
sleep 0.25
midi :f2
sleep 0.5
midi :gs2
sleep 0.25
midi :a2
sleep 0.25
midi :d2
sleep 0.75
midi :g2
sleep 0.5
midi :d2
sleep 0.75
midi :e2
sleep 0.75
midi :g2
sleep 0.25
midi :f2
sleep 0.5
midi :gs2
sleep 0.25
midi :a2
sleep 0.25
midi :d2
sleep 0.25
midi :b2
sleep 0.5
end
define :drum5 do
use_midi_defaults channel: 5, sustain: 0.25
midi :g2
sleep 0.5
midi :d2
sleep 0.5
midi :gs2
sleep 0.25
midi :e2
sleep 0.5
midi :b1
sleep 0.25
midi :g2
sleep 0.25
midi :f2
sleep 0.25
midi :cs3
sleep 0.25
midi :gs2
sleep 0.25
midi :a2
sleep 0.25
midi :d2
midi :gs2
sleep 0.75
midi :g2
sleep 0.5
midi :d2
sleep 0.5
midi :gs2
sleep 0.25
midi :e2
sleep 0.5
midi :b2
sleep 0.25
midi :g2
sleep 0.25
midi :f2
sleep 0.5
midi :gs2
sleep 0.25
midi :a2
sleep 0.25
midi :d2
sleep 0.25
midi :b2
sleep 0.5
end
define :drum6 do
use_midi_defaults channel: 5, sustain: 0.25
#1
midi :g2
sleep 0.5
midi :d2
sleep 0.5
#2
midi :gs2
sleep 0.25
midi :e2
sleep 0.5
midi :b1
sleep 0.25
#3
midi :g2
sleep 0.25
midi :f2
sleep 0.25
midi :cs3
sleep 0.25
midi :gs2
sleep 0.25
#4
midi :a2
sleep 0.25
midi :d2
midi :gs2
sleep 0.25
midi :b1
sleep 0.5
#5
midi :g2
sleep 0.5
midi :d2
sleep 0.5
#6
midi :gs2
midi :as2
sleep 0.25
midi :e2
sleep 0.5
midi :b2
sleep 0.25
#7
midi :g2
sleep 0.25
midi :f2
sleep 0.25
midi :cs3
sleep 0.25
midi :gs2
sleep 0.25
#8
midi :a2
midi :d2
sleep 0.25
midi :gs2
midi :d2
sleep 0.25
midi :b2
midi :d2
sleep 0.5
end
live_loop :cc_tuner do
stop
cc10 = (line 70, 50, steps: 100).mirror.tick
midi_cc 20, cc10, channel: 1
sleep 1
end
live_loop :cc_control, sync: :tick do
##| stop
cc3 = (line 10, 30, steps: 40).mirror.tick
midi_cc 13, cc3, channel: 1
cc4 = (line 0, 127, steps: 200).mirror.tick
midi_cc 14, cc4, channel: 1
cc5 = (line 127, 0, steps: 200).mirror.tick
midi_cc 15, cc5, channel: 1
cc6 = (line 50, 70, steps: 100).mirror.tick
midi_cc 16, cc6, channel: 1
cc7 = (line 70, 50, steps: 100).mirror.tick
midi_cc 17, cc7, channel: 1
sleep 1
end
/
Phrase controller:
- Select which phrase will play
- Determine how many times a phrase will play before selecting next phrase
- Create live_loops that use phrase variable to determine the changes in the
/
live_loop :phrase_control, sync: :tick do
##| stop
phrase = 3
repeats = 12
if phrase == -3 then
sleep 1
end
if phrase == -2 then
sleep 1
end
if phrase == -1 then
sleep 1
end
if phrase == 0 then
##| repeats = 4
cc1 = (line 50, 90, steps: 400).mirror.tick
midi_cc 11, cc1, channel: 1
cc2 = (line 90, 70, steps: 400).mirror.tick
midi_cc 12, cc2, channel: 1
cc8 = (line 30, 60, steps: 100).mirror.tick
midi_cc 18, cc8, channel: 1
cc9 = (line 60, 90, steps: 100).mirror.tick
midi_cc 19, cc9, channel: 1
cc10 = (line 50, 100, steps: 200).mirror.tick
midi_cc 20, cc10, channel: 1
sleep 1
end
if phrase == 1 then
##| repeats = 4
cc1 = (line 50, 70, steps: 400).mirror.tick
midi_cc 11, cc1, channel: 1
cc2 = (line 90, 70, steps: 400).mirror.tick
midi_cc 12, cc2, channel: 1
cc8 = (line 40, 80, steps: 50).mirror.tick
midi_cc 18, cc8, channel: 1
cc9 = (line 40, 70, steps: 100).mirror.tick
midi_cc 19, cc9, channel: 1
cc10 = (line 50, 80, steps: 200).mirror.tick
midi_cc 20, cc10, channel: 1
sleep 1
end
if phrase == 2 then
##| repeats = 4
/
cc1 = (line 30, 50, steps: 400).mirror.tick
midi_cc 11, cc1, channel: 1
cc2 = (line 90, 70, steps: 400).mirror.tick
midi_cc 12, cc2, channel: 1
/
cc8 = (line 80, 100, steps: 50).mirror.tick
midi_cc 18, cc8, channel: 1
cc9 = (line 40, 90, steps: 100).mirror.tick
midi_cc 19, cc9, channel: 1
/
cc10 = (line 50, 80, steps: 200).mirror.tick
midi_cc 20, cc10, channel: 1/
sleep 1
end
if phrase == 3 then
##| repeats = 4
sleep 1
end
repeats.times do
##| stop
live_loop :choir_phrase, sync: :tick do
if phrase == -3 then
choir
end
if phrase == -2 then
choir
end
if phrase == -1 then
choir
end
if phrase == 0
choir
end
if phrase == 1 then
choir
end
if phrase == 2 then
choir
end
if phrase == 3 then
choir
end
end
live_loop :bass_phrase, sync: :tick do
if phrase == -3 then
bass 4
end
if phrase == -2 then
bass 4
end
if phrase == -1 then
bass 4
end
if phrase == 0
bass 2
end
if phrase == 1 then
bass 4
end
if phrase == 2 then
bass 4
end
if phrase == 3 then
bass 4
end
end
live_loop :guit_phrase, sync: :tick do
if phrase == -3 then
guit 2, 2, 1, (knit 1, 2, 2, 1).choose
end
if phrase == -2 then
guit 1, 1, 4, 2
if one_in(2)
sleep (knit 4, 1, 8, 2, 16, 1).choose
end
end
if phrase == -1 then
guit 4, 0, 0, (knit 4, 1, 8, 4).choose
if one_in(1)
sleep (knit 4, 1, 8, 2, 16, 4).choose
end
end
if phrase == 0
sleep 1
end
if phrase == 1 then
sleep 1
end
if phrase == 2 then
sleep 1
end
if phrase == 3 then
sleep 1
end
end
live_loop :drum_phrase do
if phrase == -3 then
sleep 1
end
if phrase == -2 then
sleep 1
end
if phrase == -1 then
sleep 1
end
if phrase == 0 then
drum1
end
if phrase == 1 then
drum2
end
if phrase == 2 then
drum3
##| drum4
end
if phrase == 3 then
drum5
drum6
end
end
end
if phrase == -3 then
phrase = (knit -3, 4, -2, 1).choose
end
if phrase == -2 then
phrase = (knit -3, 1, -2, 4, -1, 1).choose
end
if phrase == -1 then
phrase = (knit -2, 1, -1, 4, 0, 1).choose
end
if phrase == 0 then
phrase = (knit -1, 1, 0, 4, 1, 1).choose
end
if phrase == 1 then
phrase = (knit 0, 1, 1, 4, 2, 1).choose
end
if phrase == 2 then
phrase = (knit 1, 1, 2, 4, 3, 1).choose
end
if phrase == 3 then
phrase = (knit 2, 1, 3, 4).choose
end
print "phrase", phrase
##| sleep 1
end
I’ve also noticed that the phrase changes invariably after every tick rather than sticking to one phrase according to the “repeats” variable. I figure this is because of the placement of the following code, but I could be wrong.
if phrase == -3 then
phrase = (knit -3, 4, -2, 1).choose
end
if phrase == -2 then
phrase = (knit -3, 1, -2, 4, -1, 1).choose
end
if phrase == -1 then
phrase = (knit -2, 1, -1, 4, 0, 1).choose
end
if phrase == 0 then
phrase = (knit -1, 1, 0, 4, 1, 1).choose
end
if phrase == 1 then
phrase = (knit 0, 1, 1, 4, 2, 1).choose
end
if phrase == 2 then
phrase = (knit 1, 1, 2, 4, 3, 1).choose
end
if phrase == 3 then
phrase = (knit 2, 1, 3, 4).choose
end