Hello everyone,
I’m a beginner on sonic pi, this seem to be a very interesting and fun approach of making music / coding mixture. I would like to share my current looper project : it is a live looping device intended to be reactive and avoid complicated trigs and live fails by an explicit way of performing musical actions (basically one button for one action) and eliminated non musical actions (as well as erase or tap tempo). It comes from experiencing live looping with acoustic duet project (me on the guitar + singer). So it is voice looping oriented as it is trigged with a pad.
In fact I’ve already coded a proto version on ableton live + m4l (link) but it is not suitable for live performance.
So here is my code so far, it trigs standard sonic pi samples for the moment in order to simulate loops (to avoid having to really continuously loop during code constuction) :
define :initmidi1 do |r11, s11, p11, r12, s12, p12, r13, s13, p13|
midi_note_on 0, velocity = r11 if r11!=99
midi_note_on 2, velocity = s11 if s11!=99
midi_note_on 4, velocity = p11 if p11!=99
midi_note_on 12, velocity = r12 if r12!=99
midi_note_on 14, velocity = s12 if s12!=99
midi_note_on 16, velocity = p12 if p12!=99
midi_note_on 24, velocity = r13 if r13!=99
midi_note_on 26, velocity = s13 if s13!=99
midi_note_on 28, velocity = p13 if p13!=99
end
define :initvar1 do |r11, s11, p11, r12, s12, p12, r13, s13, p13|
set :rec11, r11
set :stop11, s11
set :play11, p11
set :rec12, r12
set :stop12, s12
set :play12, p12
set :rec13, r13
set :stop13, s13
set :play13, p13
cue :ready if r11!=1 and s11!=1 and s12!=1 and s13!=1
end
define :timer1 do |t|
set :duration1, t - get(:rec11_t)
end
define :clumsyloop1 do |n|
if (get(:play11) == 1 or get(:rec12) == 1) and get(:_c1) == n
sample :ambi_lunar_land
sleep get(:duration1)
if get(:rec12) == 1 and get(:_c1) == n
set :autorec13, 1
cue :auto
stop
end
if get(:stop12) == 1 and get(:_c1) == n
set :autoplay11, 1
cue :auto
stop
end
else
if (get(:play12) == 1 or get(:rec13) == 1) and get(:_c1) == n
sample :ambi_lunar_land
sample :ambi_glass_hum if get(:stop12) == 0
sleep get(:duration1)
if get(:rec13) == 1 and get(:_c1) == n
set :autoplay13, 1
cue :auto
stop
end
if get(:stop13) == 1 and get(:_c1) == n
set :autoplay11, 1 if get(:stop12) == 1
set :autoplay12, 1 if get(:stop12) == 0
cue :auto
stop
end
else
if get(:play13) == 1 and get(:_c1) == n
sample :ambi_lunar_land
sample :ambi_glass_hum if get(:stop12) == 0
sample :ambi_dark_woosh if get(:stop13) == 0
sleep get(:duration1)
else
stop
end
end
end
end
initmidi1 0, 0, 0, 0, 0, 0, 0, 0, 0
initvar1 0, 0, 0, 0, 0, 0, 0, 0, 0
set :duration1, 0
set :_c1, 0
set :autorec13, 0
set :autolpay11, 0
set :autolpay12, 0
set :autolpay13, 0
live_loop :manualtrig1 do # Trig with touch osc midi pad
use_real_time
note, velocity = sync "/midi:samsung_android_samsung_android_midi_1_28_0:1/note_on"
if note == 0 and get(:rec11) == 0 # rec11 double click ignored ======== looper1 master loop
sample_free_all
sample :ambi_choir
set :rec11_t, Time.now.to_f # t0 for :duration1 further calculation
initvar1 1, 0, 0, 0, 0, 0, 0, 0, 0
initmidi1 99,0, 0, 0, 60, 0, 0, 60, 0
end
if note == 2 and get(:stop11) == 0# stop11
sample_free_all
timer1 Time.now.to_f if get(:rec11) == 1 # means that previous action = rec11
initvar1 0, 1, 0, 0, 0, 0, 0, 0, 0
initmidi1 0, 99, 0, 0, 60, 0, 0, 60, 0
end
if note == 4 # play11
sample_free_all
timer1 Time.now.to_f if get(:rec11) == 1
initvar1 0, 0, 1, 0, 0, 0, 0, 0, 0
initmidi1 0, 0, 99, 0, 60, 0, 0, 60, 0
end
if note == 12 and get(:rec12) == 0 # rec12 ======== looper1 first overdub
sample_free_all
timer1 Time.now.to_f if get(:rec11) == 1
initvar1 0, 0, 0, 1, 0, 0, 0, 0, 0
initmidi1 0, 0, 60, 99, 0, 0, 0, 60, 0
end
if note == 14 # stop12
sample_free :ambi_glass_hum
set :rec12, 0
set :stop12, 1
initmidi1 99, 99, 99, 0, 99, 0, 99, 99, 99
end
if note == 16 # play12
sample_free_all
initvar1 0, 0, 0, 0, 0, 1, 0, 0, 0
initmidi1 0, 0, 60, 0, 0, 99, 0, 60, 0
end
if note == 24 and get(:rec13) == 0 # rec13 ======== looper1 second overdub
sample_free_all
initvar1 0, 0, 0, 0, 0, 0, 1, 0, 0
initmidi1 0, 0, 60, 0, 0, 60, 99, 0, 0
end
if note == 26 # stop13
sample_free :ambi_dark_woosh
set :rec13, 0
set :stop13, 1
initmidi1 99, 99, 99, 99, 99, 99, 0, 99, 0
end
if note == 28 # play13
sample_free_all
initvar1 0, 0, 0, 0, 0, 0, 0, 0, 1
initmidi1 0, 0, 60, 0, 0, 60, 0, 0, 99
end
end
live_loop :autotrig1 do
use_real_time
sync "/cue/auto"
sleep 0.03 # stabilization delay
if get(:autorec13) == 1
sample_free_all
initvar1 0, 0, 0, 0, 0, 0, 1, 0, 0
set :autorec13, 0
initmidi1 0, 0, 60, 0, 0, 60, 127, 0, 0
end
if get(:autoplay11) == 1
sample_free_all
initvar1 0, 0, 1, 0, 0, 0, 0, 0, 0
set :autoplay11, 0
initmidi1 0, 0, 127, 0, 60, 0, 0, 60, 0
end
if get(:autoplay12) == 1
sample_free_all
initvar1 0, 0, 0, 0, 0, 1, 0, 0, 0
set :autoplay12, 0
initmidi1 0, 0, 60, 0, 0, 127, 0, 60, 0
end
if get(:autoplay13) == 1
sample_free_all
initvar1 0, 0, 0, 0, 0, 0, 0, 0, 1
set :autoplay13, 0
initmidi1 0, 0, 60, 0, 0, 60, 0, 0, 127
end
end
live_loop :timemanagement1 do
use_real_time
sync "/cue/ready"
if get(:stop11) == 1 or get(:rec11) == 1 # stop11 and rec11 kill the live_loop
sample_free_all
stop
end
if get(:play11) == 1 or get(:play12) == 1 or get(:play13) == 1 or get(:rec12) == 1 or get(:rec13) == 1 # floating counter to avoid waiting till end of sleep time
set :_c1, get(:_c1) + 1
if get(:_c1) == 11
set :_c1, 1
end
end
live_loop :loop101 do
clumsyloop1 1
end
live_loop :loop102 do
clumsyloop1 2
end
live_loop :loop103 do
clumsyloop1 3
end
live_loop :loop104 do
clumsyloop1 4
end
live_loop :loop105 do
clumsyloop1 5
end
live_loop :loop106 do
clumsyloop1 6
end
live_loop :loop107 do
clumsyloop1 7
end
live_loop :loop108 do
clumsyloop1 8
end
live_loop :loop109 do
clumsyloop1 9
end
live_loop :loop110 do
clumsyloop1 10
end
end
Some explanation of main blocks :
define :initmidi1 do |r11, s11, p11, r12, s12, p12, r13, s13, p13|
# Midi ctrl init and enlightening feedback
end
define :initvar1 do |r11, s11, p11, r12, s12, p12, r13, s13, p13|
# Status var init
end
define :timer1 do |t|
# Measure of main loop duration
end
define :clumsyloop1 do |n|
# Sample/loop parallel plays depending on situation
end
live_loop :manualtrig1 do # Trig with touch osc midi pad
# Midi pad to trig actions 3x3 pad with below midi notes (C D E of different octaves) :
# [ 24 ] [ 26 ] [ 28 ] => [ rec13 ] [ stop13 ] [ play13 ] overdub2
# [ 12 ] [ 14 ] [ 16 ] => [ rec12 ] [ stop12 ] [ play12 ] overdub1
# [ 00 ] [ 02 ] [ 04 ] => [ rec11 ] [ stop11 ] [ play11 ] main loop
end
live_loop :autotrig1 do
# Automatic actions
end
live_loop :timemanagement1 do
# Making loops alive
end
One of trickiest part for me was finding how to keep reactivity and avoid waiting for end of sleep time. It is the aim of :timemamagement1
live loop : it increments a floating counter that skip current sample and make it possible to perform on the fly actions, by bypassing current sample sleep in a way. It works but there has to be a smarter way of doing it…
To do list before having a real functioning version :
- To find how to automatically connect and configure soundcard on boot
- Replace sonic pi sample by real live looping
- Optimize
:timemanagement1
an:clumsyloop1
sections - Duplicate code (two parallel loopers)
Thanks for reading