Thinking about control structures

Inspired by @Eli 's excellent dub example Curating a Sonic Pi Music Playlist I’ve been thinking about control structures for live work.

On the one hand I like the idea of all the control being local within each loop, as that’s the simplest. On the other hand it’d be nice to work up a SPi pattern as a multi-song, multi-voice instrument, with lots of EZ to use control structures at the top.

Oscillating between the two extremes, today I’m thinking of a compromise - in the script below I’ve got a couple of hashes: ‘control’ you can set an integer value for some voices, and then I use dice in the loop to choose how often they appear, 0 is all the time, 6 is never. I like the results.

Then ‘volume’ scales the volume of a few of the voices.

What do you think?

#Milton Lumpky

#
#120bpm
#4/4

control = {"hihat"  => 0,
           "chick"  => 3,
           "snare"  => 6,
           "kick"   => 3,
           "chord"  => 0,
           "bass"   => 0,
           "sample" => 4}

volume = {"chord"  => 0.1,
          "bass"   => 1.0,
          "sample" => 0.2}

set_mixer_control! limiter_bypass: 1
use_bpm 120

d = "~/Music/Sonic-Pi/milton lumpky/samples/"
load_samples

with_fx :reverb, mix: 0.2, room: 0.6 do |reverb|
  with_fx :echo, phase: 0.75, mix: 0.2, decay: 5.0 do |echo|
    
    
    live_loop "main" do
      
      if tick(:fx_start)==0 then
        set :reverb, reverb
        set :echo, echo
      end
      control get[:reverb], mix: 0.2
      control get[:echo], mix: 0.2
      
      c1 = [:d4, :f4, :a4]
      c3 = [:f4, :a4, :c4]
      c5 = [:a3, :c4, :e4]
      chord_seq = [c1,c1,c5,c5, c1,c1,c3,c5]
      
      b1 = [:d4, :d4, :f4, :a3]
      b3 = [:f4, :f4, :f4, :f4]
      b5 = [:a4, :a4, :a4, :a4]
      bass_seq = [b1,b1,b5,b5, b1,b1,b3,b5]
      
      time_warp 4-1.0/32 do
        set :currentchord, chord_seq.tick(:seq)
        set :currentbass, bass_seq.look(:seq)
        set :barnumber, tick(:barnumber)
      end
      sleep 4.0
      
      cue "bar"
      
      if tick(:bar4)>=4 then
        tick_reset :bar4
        cue "bar4"
      end
      
    end
    
    
    live_loop "sync" do
      sync "/cue/bar"
      time_warp 16-rt(0.07) do
        in_thread do
          use_osc "192.168.1.50", 4561
          osc "/pulse", 49
        end
      end
      sleep 8
    end
    
    live_loop "sample" do
      sync "/cue/bar"
      if dice(6)>control["sample"] then
        #m=1.0+Math::sin(vt/6.1)
        #a=0.01 + 0.05*m
        a=0.1
        a=a*volume["sample"]
        sample d, "milton lumpky sample 1.flac", amp: a, attack: 0.5
        sleep 0
      end
    end
    
    
    live_loop "chord" do
      sync "/cue/bar"
      a = 0.3
      a=a*volume["chord"]
      if dice(6)>control["chord"] then
        use_synth :sine
        in_thread do
          8.times do
            play_chord get[:currentchord], amp: a, attack: 0.1, release: 0.1
            sleep 1.0/2
          end
        end
      end
    end
    
    live_loop "bass" do
      sync "/cue/bar"
      a = 0.1
      a = a*volume["bass"]
      pattern = [0,2,2,1,1,2,1,1,1,1,1,1,1,1,1,1]
      tick_reset
      if dice(6)>control["bass"] then
        in_thread do
          use_synth  :fm
          8.times do
            n = get[:currentbass].tick
            sleep pattern.tick/2.0
            s = play n-12, amp: a, release: 0.3
          end
        end
      end
    end
    
    live_loop "hihat" do
      sync "/cue/bar"
      a=0.1
      if dice(6)>control["hihat"] then
        in_thread do
          accent = [1.0,0.6,0.6,0.6].ring
          16.times do
            sample :drum_cymbal_closed, amp: a*accent.tick
            sleep 1.0/2
          end
        end
      end
    end
    
    live_loop "chick" do
      sync "/cue/bar"
      a=0.2
      if dice(6)>control["chick"] then
        in_thread do
          2.times do
            sleep 1
            sample :drum_cymbal_pedal, amp: a
            sleep 1
          end
        end
      end
    end
    
    live_loop "kick" do
      sync "/cue/bar"
      a=0.3
      if dice(6)>control["kick"] then
        in_thread do
          4.times do
            sample :bd_fat, amp: a
            sleep 1
          end
        end
      end
    end
    
    live_loop "snare" do
      sync "/cue/bar"
      a=0.2
      if dice(6)>control["snare"] then
        in_thread do
          1.times do
            x=0.1
            sleep 1-x*2.0
            sample :drum_snare_soft, amp: a/5
            sleep x
            sample :drum_snare_soft, amp: a/5
            sleep x
            sample :drum_snare_soft, amp: a
            sleep 4
          end
        end
      end
    end
    
    
  end #echo
end #reverb
















Well obviously, I’m all for it. :slight_smile:

However - take it a step further, have
a midi key/vol box, and write your code
so a button switches on/off an effect,
with a pot for vol or freq or both…

OR…

There’s been some great work on OSC from
phones and laptops, to do similar. Robin (Newman)
esp. is a great source for neat OSC toys.

Eli…

Yes, I thought about that - then thought I wouldn’t, as it’s getting away from the SPi code idea. I went through the same thing with Reaper and VCV Rack, and I’ve gone back to native on-screen use. I don’t know if I’ll even keep the ‘controls at the top’ idea. See how it feels.

I thought OSC on my phone was a neat thing and wrote a load of code for that, until I found during a rehearsal it didn’t work for some fiddly techie reason.

Further thoughts. With the pattern above I can have an optional startup programmed loop, which is a useful thing as I can set intro that going while attending to something else - then switch it back to manual at some point. I’m sure that can be adapted to an outro too.

live_loop "program" do
  sync "/cue/bar"
  if true then
    n = tick("program")
    if n < 4 then
      c = {"hihat"  => 0,
           "chick"  => 6,
           "snare"  => 0,
           "kick"   => 6,
           "chord"  => 6,
           "bass"   => 6,
           "sample" => 6}
    elsif n < 8 then
      c = {"hihat"  => 0,
           "chick"  => 0,
           "snare"  => 0,
           "kick"   => 0,
           "chord"  => 6,
           "bass"   => 6,
           "sample" => 6}
    else
      c = {"hihat"  => 0,
           "chick"  => 0,
           "snare"  => 6,
           "kick"   => 6,
           "chord"  => 0,
           "bass"   => 0,
           "sample" => 3}
    end
    control = c
  end
end
1 Like

I have yet to settle on a definite structure, but have often felt that a similar approach would be helpful for my needs also :slight_smile:

I’ve been trying this for a bit, and it does seem to be helpful for live work. Although, reading a little more about Ruby I feel I should use symbols. Not that I fully understand what they are - I see they are created and many are already defined e.g. :C4 etc but can’t seem to create them myself, in Spi.

Anyway, that’s for another topic. but for now it’s like this… It’s easy to change in the moment, and/or copying to another block below so I can keep the original

control = {
  :sample1 => 0,
  :sample2 => 0,
  :sample3 => 0,
  :bass    => 0,
  :hihat   => 0,
  :perc1   => 3,
  :perc2   => 3,
  :kick    => 4
}