Help a newbie with OSC - IN

Hello. Please forgive a super newbie question.
I just starting my jorney with SonicPi, and i love it. But well. I just got my LK cotroller connected via UBRIDGE, and i can manage to play a synth with a “midi” keyoboard. Wow :slight_smile:
But i just can not understand how can i assign the OSC signal, to control the cutoff of a tb303, while its playing its fat riffs. Please anyone could show me how to do this simple thing?

I would like to also know, how can i adjust the volume of my loops being played, so i can maxe a simple mixer. I would really apprecaite your help. Soon i would like to make a simple looper and control ome sound tweaks, but i just need to learn basics first.

Hi @Kalitechnik,

you might want to have a look at my Live Looper to have some examples. Moreover especially @robin.newman has done a lot of stuff with Midi/OSC and wrote nice documentations on his blog and here on in_thread. Hope this will get you started.

Hi Robert
I think your controller is midi based, and it is probably easier to use midi to achieve what you want. You already have keyboard input playing which is great. If you have any rotary controls on your controller then these probably use midi control messages, where their output varies between 0 and 127 as the control is rotated. Sonic Pi can read these using midi control_change messages.
Below is a program modified from my simple polysynth program which allows you to play polyphonic tb303 inputs from a keyboard, and to control the cutoff setting BEFORE each note is played by rotating such a control. You can’t change the cutoff while the note is playing with this. The program could be modified to do this for a monophonic input, but it would be less easy to do for polyphonic input. The progam detects a keypress and starts a long :tb303 note of 10 seconds duration. IT keeps a record of which key was pressed, and stores a reference to the running synth. When the key is released it retreives that reference and kills the sounding note. I have added a live_loop at the top which polls a midi control (no 10) and uses the resulting input to set and store a cutoff value in the range 50-110. The current cutoff value is applied to each tb303 note as it starts.

If you want to try out the program, you will have to put in the name of your midi controller (in place of the oxygen 8 controller I used), and also change the midi control match number (I used control 10) to fit your set up.

#Program to give polyphonic :tb303 midi controlled synth
#cutoff can be set (BEFORE a note starts) by adjusting
#a rotary control using midi_cc I used control 10

use_synth :tb303
set :coVal,100
live_loop :getCutoff do
  use_real_time
  b = sync "/midi/usb_oxygen_8_v2/*/*/control_change"
  set :coVal,50+60/127.0*b[1] if b[0]==10 #adjust for actual control used
end


plist=[] #list to contains references to notes to be killed
ns=[] #array to store note playing references
nv=[0]*128 #array to store state of note for a particlar pitch 1=on, 0 = 0ff

128.times do |i|
  ns[i]=("n"+i.to_s).to_sym #set up array of symbols :n0 ...:n127
end
#puts ns #for testing

define :sv do |sym| #extract numeric value associated with symbol eg :n64 => 64
  return sym.to_s[1..-1].to_i
end
#puts sv(ns[64]) #for testing

live_loop :midi_piano_on do #this loop starts 10 second notes for spcified pitches and stores reference
  use_real_time
  note, on = sync "/midi/usb_oxygen_8_v2/*/*/note_on"#change to match your controller
  if on >0
    puts note,nv[note]
    if nv[note]==0 #check if new start for the note
      nv[note]=1 #mark note as started for this pitch
      #max duration of note set to 5 on next line. Can increase if you wish.
      x = play note, sustain: 10,cutoff: get(:coVal) #play note
      set ns[note],x #store reference in ns array
    end
  else
    
    if nv[note]==1 #check if this pitch is on
      nv[note]=0 #set this pitch off
      plist << get(ns[note]) #add note reference to the list plist
    end
  end
end

live_loop :notekill,auto_cue: false,delay: 0.25 do # this loop kills notes which have been released
  use_real_time
  if plist.length > 0 #check if notes to be killed
    k=plist.pop #get reference to the note to be killed from the plist
    control k,amp: 0,amp_slide: 0.02 #fade note out in 0.02 seconds
    sleep 0.02
    kill k #kill the note referred to in ns array
  end
  sleep 0.01
end

you can hear the program in operation here

https://drive.google.com/file/d/1D2nXJSYLPNiQwETuttZzoccVMUItU5iZ/view?usp=sharing

Hi @Kalitechnik,

Great to hear that you’ve just started your Sonic Pi journey and that you’re having fun. Welcome to our community and I really hope you stick around for a while.

When controlling Sonic Pi’s internal synths there’s two things to realise:

  1. All synths opts can be set at synth trigger time
  2. Many (but not all) opts can be controlled whilst the synth is running

Setting synth opts at trigger time is easy as you’ve likely already discovered:

synth :tb303, note: :e1, release: 0.3, cutoff: 70

If you’re happy only changing the initial trigger value, this is very easy to do via MIDI, simply call get to extract the most recently seen MIDI event using the path seen in the cue log. Something like this would work:

note, val = get("/midi/**/control_change")
synth :tb303, note: :e1, release: 0.1, cutoff: val

(This requires at least one control change message to have already been received)

You can then do this repeatedly if you wanted:

live_loop :acid_bass do
  note, val = get("/midi/**/control_change")
  synth :tb303, note: :e1, release: 0.1, cutoff: val
  sleep 0.125
end

Note that this approach will only change the trigger value, but once the synth has started, the value will not change (only the next synth hit will have the new value of the MIDI controller).

If you want to change a synth whilst it’s currently running you need to have a handle to it. Take a look at Section 7 of the built-in tutorial to find out more about this. If you’d like me to expand further than the tutorial, please let me know.

Thank you very much for a quick answer. I will play around and let you know about my progress. I am very pleased, to join the Sonic Pi community :slight_smile: And thank YOU Sam for creating such an interesting project. Finally i have found the tool, that might help me to compose a piece of music. Now i just need to learn the language, which is a great journey itself. I will have more questions coming for sure. See you later :slight_smile:

2 Likes

Hello again. So i spend many hours on trying to figure out few things:) Please do mind that this is my firs ever experience with coding. The is the best i could manage. I was so excited when i could start to control cutoff of tb303 with the CC number 10.

Then i wanted to add the drums and control the amplitude of sample: bd_house with a slider that
has CC number 12. But then two loops where controled when i moved every slider on my external controller. So i had to switch to MIDI 2 Channel. And than i was amazed again. I could control tb303, cut off and drums amplitude.

So my question is. How can i control parameters, and assign CC numbers, so when i will move the “knob” CC 10 - it will control the “value” of cutoff
“slider” CC 12 - it will control the volume (amplitude) of a drum sample.

Below is one of my first coding:)

live_loop :drums do
  slider1, value2 = get "/midi/ubridge/0/2/control_change"
  sample :bd_haus,  rate: 1, amp: value2/64.0 #amp range 0-2
  sleep 0.5
end

live_loop :motive do
  use_synth :dsaw
  use_random_seed 48 # i do not know how it works - but it makes magic
  motive = (scale :fs2, :minor_pentatonic).shuffle
  knob1, value1 = get("/midi/ubridge/0/1/control_change")  # knob1--> CC number 10
  play motive.tick, release: 1, cutoff: value1
  sleep 0.25
end

All control changes will trigger BOTH loops. You need to add a condition so that you only make changes if it is the control number you want. In the example below I store initial values for amp and cutoff value. After the get functions, check whether it is the correct control value 10 or 12. If so, reset the stored amp or cutoff value, then use these in the loop, and continue to use them until they are changed again.
By the way, fi you put three``` before and after you paste in some code here, then it will format it nicely for you.

set :lastampvalue,0
set :lastcutoffvalue,50 #starting values
live_loop :drums do
  slider1, value2 = get("/midi/*/*/*/control_change")
  puts slider1,value2 #this shows it responds to BOTH controls 10 AND 12
  if slider1 == 12 #only respond to control 12 so ONLY make changes if value is control 12
    set :lastampvalue,value2
  end
  sample :bd_haus, rate: 1, amp: get(:lastampvalue)/64.0 #amp range 0-2
  sleep 0.5
end

live_loop :motive do
  use_synth :dsaw
  use_random_seed 48 # i do not know how it works - but it makes magic
  motive = (scale :fs2, :minor_pentatonic).shuffle
  knob1, value1 = get("/midi/*/*/*/control_change") # knob1–> CC number 10
  if knob1==10
    set :lastcutoffvalue,value1# only adjust mvalue if control is 10
  end
  play motive.tick, release: 1, cutoff: get(:lastcutoffvalue) 
  sleep 0.2  
end
1 Like

Thank you for your help. Its working now. Let me try to build something more complex now. The first attempt to “4 Multitrack Recorder/Mixer/Player”. This is so exciting. I even registered myself on GitHub. LoL. “Look Mummy” - I am programmer :slight_smile:

1 Like

Hello everyone. Its been a few exciting days. I got my hands on TouchOSC app, and i could manage to connect it, and send OSC messages to SonicPi. I started to build a 303 Synth/Player. Created one loop playing one chord together with the drum loop. And i started to play with parameters of a synth. Oh my. I had a lot of fun just tweaking that one chord :slight_smile: So i decided to create a controller for all the opts of 303. And for the FX`s too. This is what i got after few hours of coding:)

#Let the show begins - 01-04-2018 - Warsaw, Poland
#Hi My "CodeName" is "Kalitechnik" and this is ...
#my first attempt to program instruments in SonicPI
#Instruments will be controlled by Touch OSC via Wi-Fi (So Cool!)
#See you soon on GitHub, help and contribution will be appreciated.

#I wish to build the instrument ("Rythm Pi") for playing live that will consist of two.
#A "Drum" - Section and the "Bass" - Section

#So here i start with two loops - RythmPi and BassPi
#First i start to work with a "Bass Pi" instrument.
#Lets see what i can do:)

set :pan, 0
set :amp, 1

set :attack, 0
set :attack_level, 1
set :decay, 0
set :decay_level, 0
set :sustain, 0
set :sustain_level, 1
set :release, 1
set :release_level, 1

set :cutoff, 30
set :cutoff_slide, 0

set :cutoff_attack, 0
set :cutoff_decay, 0
set :cutoff_sustain, 0
set :cutoff_release, 1
set :resonance, 0

set :reverb_mix, 0
set :room, 0.6
set :damp, 0.5

set :echo_mix, 0
set :phase, 0.25
set :echo_decay, 2

set :lpf_mix, 0
set :lpf_cutoff, 0

set :hpf_mix, 0
set :hpf_cutoff, 0



#Main Controllers
live_loop :amp do
  use_real_time
  b = sync "/osc/main_amp"
  puts "amp", b[0]
  set :amp, b[0]
end

live_loop :pan do
  use_real_time
  b = sync "/osc/pan"
  puts "pan", b[0]
  set :pan, b[0]
end

#ADSR Amplitude Envelope Control
live_loop :attack do
  use_real_time
  b = sync "/osc/attack"
  puts "attack", b[0]
  set :attack, b[0]
end
live_loop :attack_level do
  use_real_time
  b = sync "/osc/attack_level"
  puts "attack_level", b[0]
  set :attack_level, b[0]
end

live_loop :decay do
  use_real_time
  b = sync "/osc/decay"
  puts "decay", b[0]
  set :decay, b[0]
end

live_loop :decay_level do
  use_real_time
  b = sync "/osc/decay_level"
  puts "decay_level", b[0]
  set :decay_level, b[0]
end

live_loop :sustain do
  use_real_time
  b = sync "/osc/sustain"
  puts "sustain", b[0]
  set :sustain, b[0]
end

live_loop :sustain_level do
  use_real_time
  b = sync "/osc/sustain_level"
  puts "sustain_level", b[0]
  set :sustain_level, b[0]
end


live_loop :release do
  use_real_time
  b = sync "/osc/release"
  puts "release", b[0]
  set :release, b[0]
end

live_loop :release_level do
  use_real_time
  b = sync "/osc/release_level"
  puts "release_level", b[0]
  set :release_level, b[0]
end

#CutOff Filter Control
live_loop :cutoff do
  use_real_time
  b = sync "/osc/cutoff"
  puts "cutoff", b[0]
  set :cutoff, b[0]
end

live_loop :cutoff_slide do
  use_real_time
  b = sync "/osc/cutoff_slide"
  puts "cutoff_slide", b[0]
  set :cutoff_slide, b[0]
end

live_loop :cutoff_attack do
  use_real_time
  b = sync "/osc/cutoff_attack"
  puts "cutoff_attack", b[0]
  set :cutoff_attack, b[0]
end

live_loop :cutoff_decay do
  use_real_time
  b = sync "/osc/cutoff_decay"
  puts "cutoff_decay", b[0]
  set :cutoff_decay, b[0]
end

live_loop :cutoff_sustain do
  use_real_time
  b = sync "/osc/cutoff_sustain"
  puts "cutoff_sustain", b[0]
  set :cutoff_sustain, b[0]
end

live_loop :cutoff_release do
  use_real_time
  b = sync "/osc/cutoff_release"
  puts "cutoff_release", b[0]
  set :cutoff_release, b[0]
end

live_loop :resonance do
  use_real_time
  b = sync "/osc/resonance"
  puts "resonance", b[0]
  set :resonance, b[0]
end

#Reverb Control
live_loop :reverb_mix do
  use_real_time
  b = sync "/osc/reverb_mix"
  puts "reverb_mix", b[0]
  set :reverb_mix, b[0]
end

live_loop :room do
  use_real_time
  b = sync "/osc/room"
  puts "room", b[0]
  set :room, b[0]
end

live_loop :damp do
  use_real_time
  b = sync "/osc/damp"
  puts "damp", b[0]
  set :damp, b[0]
end

#Echo Control
live_loop :echo_mix do
  use_real_time
  b = sync "/osc/echo_mix"
  puts "echo_mix", b[0]
  set :echo_mix, b[0]
end

live_loop :phase do
  use_real_time
  b = sync "/osc/phase"
  puts "phase", b[0]
  set :phase, b[0]
end

live_loop :echo_decay do
  use_real_time
  b = sync "/osc/echo_decay"
  puts "echo_decay", b[0]
  set :echo_decay, b[0]
end

live_loop :pi303 do
  sync :drums
  with_fx :echo do |echo|
    control echo, mix: get(:echo_mix), phase: get(:phase), decay: get(:echo_decay)
    with_fx :reverb do |reverb|
      control reverb, mix: get(:reverb_mix), room: get(:room), damp: get(:damp)
      use_synth :tb303
      play chord(:Cs3, :m7), amp: get(:amp), pan: get(:pan), attack: get(:attack), attack_level: get(:attack_level), decay: get(:decay),
        decay_level: get(:decay_level), sustain: get(:sustain), sustain_level: get(:sustain_level), release: get(:release),
        release_level: get(:release_level), cutoff: get(:cutoff), cutoff_slide: get(:cutoff_slide), res: get(:resonance),
        cutoff_attack: get(:cutoff_attack), cutoff_decay: get(:cutoff_decay), cutoff_sustain: get(:cutoff_sustain),
        cutoff_release: get(:cutoff_release)
      sleep 0.5
    end
  end
end

live_loop :drums do
  sample :bd_ada
  sleep 0.25
  sample :bd_pure, amp: 0.6
  sleep 0.25
end

Now i am stucked on few things, so i would like to ask you for some more help.
I wish to add few things, namely…

  1. [WAVE] - It has 3 positions - wave:(0,1,2) - how can i assign a knob to it? Lets say i am creating the osc knob on Touch , i set the range (0,2). What would be the code to read and interpret the OSC values, to 0, 1 and 2 and change the :wave [0,1,2]? The name of knob is “/osc/wave”

  2. [ENV] - same goes for env_curve: [1,2,3,4,5,6,7]. I am creating a knob on Touch for this and give the name to it “env_curve”. How to set it and get it all up, to make changes on a synth?

  3. [START] - i wish too press a button “/osc/start” and start my loop goin. And when i press the button again, i wish the loop to stop playing. (i imagine this as a “mute” option on a mixer later).
    So yes, please help me to have that option.

  4. [NOTE] - Right now i set a loop to play wth a Cs chord, but… I wish to have a selector on my panel. So before i START a synth, and of course while its running, i wish to select a note, or chord that will be playing. For this i wish to use both midi and osc. I feel that i know how to use a Midi note on, so my question will be regarding the OSC and “buttons”.

  5. [CHORDS] - I have a “/osc/buttons/1/1”, and “/osc/buttons/1/2” — Lets call a first one “tonic”, and second one “subdominant”. When i press TONIC i wish a synth to play a chord (Cs3, minor), and when i press SUBDOMINANT a chord (Fs3, minor). Please, help me with this one too.

OK. Thank you for your help and support. I just registered on GitHub and started to publish there my project. There will be a TouchOSC file there too. It looks like that right now.

HI there
I had a play with the problems you list and attach possible answers to most of them.
I used a test TouchOSC template shown below.

The top section is a rotary slider address /test/wave
This is surrounded by three LEDS addresses /test/led0 to /test/led2 These are lit by my program to indicate the current value of the waveopt.
Underneath these in the middle of the screen is a two way push switch with 1 column and two rows.
The two switch array has address ‘/test/chord’ with the two individual switches at /test/chord/1/1 and
/test/chord/1/2 These are used to control which of the two chords Cs3:minor and Fs3:minor should be played.
The bottom section is a single toggle switch address /test/mute which is used to mute a running live_loop.

Your question 1 I use the rotary and decode whereabout in its range it is. values <0.3 select 0 for the waveopt, values from 0.3 to 0.6 select 1 and values over 0.6 select value 2. These values are saved and then applied via use_merge_synth_defaults commands where required.

Your question 2 could be answered in a similar fashion although I haven’t implemented it.

Your question 3 I am not sure if you just want to switch the volume on or off, or to sync the start of your loop as well.
If the former then I have given an example of using an fx :level effect to control the volume of an embedded live loop. You could have more than one live loop command inside the fx :level wrapper and they would all be effected.
If you want to sync the start of the live loop as well, then some of the code from my loop_controller program which is described here http://in-thread.sonic-pi.net/t/osc-loop-machine-makeymakey/850/20?u=robin.newman might help

Your question 4 I thinkI need more detail to know how to help here.

Your question 5 I detect which of the two linked pushbuttons /test/chord have been pushed and then play the appropriate chord.

My code is shown below

use_osc "172.20.10.5",9000 #address of TouchOSC pad
set :waveopt,0 #initialise waveopt setting
osc "/test/led0",1 #initialise feedback leds
osc "/test/led1",0
osc "/test/led2",0
osc "/test/wave",0.2
live_loop :getwaveopt do #live loop to update waveopt setting
  use_real_time
  b= sync "/osc/test/wave"
  if b[0] < 0.3
    set :waveopt,0
    osc "/test/led0",1
    osc "/test/led1",0
    osc "/test/led2",0
  elsif b[0] <0.6
    set :waveopt,1
    osc "/test/led0",0
    osc "/test/led1",1
    osc "/test/led2",0
  else
    set :waveopt,2
    osc "/test/led0",0
    osc "/test/led1",0
    osc "/test/led2",1
  end
end

define:parse_sync_address do |address| # used to decode wild cards
  v= get_event(address).to_s.split(",")[6]
  if v != nil
    return v[3..-2].split("/")
  else
    return ["error"]
  end
end

use_synth :tb303

live_loop :selectchord do #select one of two chords to play
  use_real_time
  b= sync "/osc/test/chord/1/*" # * is either 1 or 2
  if b[0]==1
    switch=parse_sync_address("/osc/test/chord/1/*")[4].to_i
   #the parse function gives output like  ["osc", "test", "chord", "1", "2"]
   #I then select the 5th entry using [4] and convert to integer from a string
    puts switch
    use_merged_synth_defaults wave: get(:waveopt)
    play_chord (chord :Cs3,:minor),release: 0.4,amp: 1 if switch==1
    play_chord (chord :Fs3,:minor),release: 0.4,amp: 1 if switch==2
  end
end

#next section uses fx_level to control volume in a live loop
with_fx :level, amp: 0 do |v|
  set :val,v #store reference to fx command
  live_loop :getVol do
    use_real_time
    b= sync "/osc/test/mute" #get state of mute toggle switch
    levelVol=b[0]
    puts levelVol
    control get(:val),amp: levelVol,amp_slide: 0.1 #adjust level
  end
  
  live_loop :controlledVolume do
    n=(chord :Cs3,:minor).tick
    use_merged_synth_defaults wave: get(:waveopt)
    play n,amp: 0.7,release: 0.2 #amp setting is modified by enveloping level value
    sleep 0.2
  end
end
1 Like

Hi there again. Thank you for your kind help. So i could manage to set the ENV and WAVE opts. Also i could manage to START my loop, and stop it. For this i used this code:

set :startpib303, 0

live_loop :startpib303 do
  use_real_time
  b = sync "/osc/button1"
  puts "start"
  set :startpib303, b[0]
end



live_loop :play do
  with_fx :lpf do |lpf|
    control lpf, mix: get(:lpf_mix), cutoff: get(:lpf_cutoff)
    with_fx :hpf do |hpf|
      control hpf, mix: get(:hpf_mix), cutoff: get(:hpf_cutoff)
      with_fx :echo do |echo|
        control echo, mix: get(:echo_mix), phase: get(:phase), decay: get(:echo_decay)
        with_fx :reverb do |reverb|
          control reverb, mix: get(:reverb_mix), room: get(:room), damp: get(:damp)
          use_synth :tb303
          play chord(:Fs3, :m)  if get(:startpib303)==1
          sleep 1
        end
      end
    end
  end
end

Maybe there is more simple way to do this, but for now its ok.
I have also made some changes in OSC template. You can find it on my Github.
https://github.com/kalitechnik/PiB-303/blob/master/PiB303-TouchOSC.touchosc
Looks like this right now.

And here comes next question. I could not understand how to manage and assing multiple buttons, so i need help on this one:

I have created the multipush button called “/osc/beats”. It has 4 buttons. I need to assing it to SLEEP, so i can choose the beats [0.125, 0.25, 0.5, 1]. Please can you help me on this one, and explain again how to use the multiple buttons, and the parse function. Its a little bit difficult for me to understand. Thank you very much for your assistance.

My whole code looking like this right now:

#Hi My "CodeName" is "Kalitechnik" and this is ...
#my first attempt to program instruments in SonicPI
#Instruments will be controlled by Touch OSC via Wi-Fi (So Cool!)
#See you soon on GitHub, help and contribution will be appreciated.

#I wish to build the instrument ("Rythm Pi") for playing live that will consist of two.
#A "Drum" - Section and the "Bass" - Section

#So here i start with two loops - RythmPi and BassPi
#First i start to work with a "Bass Pi" instrument.
#Lets see what i can do:)

set :pan, 0
set :amp, 1
set :pitch, 0

set :attack, 0
set :attack_level, 1
set :decay, 0
set :decay_level, 0
set :sustain, 0
set :sustain_level, 1
set :release, 1
set :release_level, 1

set :env_curve, 1
set :wave, 0

set :cutoff, 30
set :cutoff_slide, 0

set :cutoff_attack, 0
set :cutoff_decay, 0
set :cutoff_sustain, 0
set :cutoff_release, 1
set :resonance, 0

set :reverb_mix, 0
set :room, 0.6
set :damp, 0.5

set :echo_mix, 0
set :phase, 0.25
set :echo_decay, 2

set :lpf_mix, 0
set :lpf_cutoff, 100

set :hpf_mix, 0
set :hpf_cutoff, 100

set :beats, 1




#Main Controllers
live_loop :amp do
  use_real_time
  b = sync "/osc/main_amp"
  puts "amp", b[0]
  set :amp, b[0]
end

live_loop :pan do
  use_real_time
  b = sync "/osc/pan"
  puts "pan", b[0]
  set :pan, b[0]
end

live_loop :pitch do
  use_real_time
  b = sync "/osc/pitch"
  puts "pitch", b[0]
  set :pitch, b[0]
end




#ADSR Amplitude Envelope Control
live_loop :attack do
  use_real_time
  b = sync "/osc/attack"
  puts "attack", b[0]
  set :attack, b[0]
end
live_loop :attack_level do
  use_real_time
  b = sync "/osc/attack_level"
  puts "attack_level", b[0]
  set :attack_level, b[0]
end

live_loop :decay do
  use_real_time
  b = sync "/osc/decay"
  puts "decay", b[0]
  set :decay, b[0]
end

live_loop :decay_level do
  use_real_time
  b = sync "/osc/decay_level"
  puts "decay_level", b[0]
  set :decay_level, b[0]
end

live_loop :sustain do
  use_real_time
  b = sync "/osc/sustain"
  puts "sustain", b[0]
  set :sustain, b[0]
end

live_loop :sustain_level do
  use_real_time
  b = sync "/osc/sustain_level"
  puts "sustain_level", b[0]
  set :sustain_level, b[0]
end


live_loop :release do
  use_real_time
  b = sync "/osc/release"
  puts "release", b[0]
  set :release, b[0]
end

live_loop :release_level do
  use_real_time
  b = sync "/osc/release_level"
  puts "release_level", b[0]
  set :release_level, b[0]
end

#CutOff Filter Control
live_loop :cutoff do
  use_real_time
  b = sync "/osc/cutoff"
  puts "cutoff", b[0]
  set :cutoff, b[0]
end

live_loop :cutoff_slide do
  use_real_time
  b = sync "/osc/cutoff_slide"
  puts "cutoff_slide", b[0]
  set :cutoff_slide, b[0]
end

live_loop :cutoff_attack do
  use_real_time
  b = sync "/osc/cutoff_attack"
  puts "cutoff_attack", b[0]
  set :cutoff_attack, b[0]
end

live_loop :cutoff_decay do
  use_real_time
  b = sync "/osc/cutoff_decay"
  puts "cutoff_decay", b[0]
  set :cutoff_decay, b[0]
end

live_loop :cutoff_sustain do
  use_real_time
  b = sync "/osc/cutoff_sustain"
  puts "cutoff_sustain", b[0]
  set :cutoff_sustain, b[0]
end

live_loop :cutoff_release do
  use_real_time
  b = sync "/osc/cutoff_release"
  puts "cutoff_release", b[0]
  set :cutoff_release, b[0]
end

live_loop :resonance do
  use_real_time
  b = sync "/osc/resonance"
  puts "resonance", b[0]
  set :resonance, b[0]
end

#LowPass and HighPass Filters Cutoff
live_loop :lpf_mix do
  use_real_time
  b = sync "/osc/lpf_mix"
  puts "lpf_mix", b[0]
  set :lpf_mix, b[0]
end

live_loop :lpf_cutoff do
  use_real_time
  b = sync "/osc/lpf_cutoff"
  puts "/osc/lpf_cutoff", b[0]
  set :lpf_cutoff, b[0]
end

live_loop :hpf_mix do
  use_real_time
  b = sync "/osc/hpf_mix"
  puts "hpf_mix", b[0]
  set :hpf_mix, b[0]
end

live_loop :hpf_cutoff do
  use_real_time
  b = sync "/osc/hpf_cutoff"
  puts "hpf_cutoff", b[0]
  set :hpf_cutoff, b[0]
end

#Reverb Controlls
live_loop :reverb_mix do
  use_real_time
  b = sync "/osc/reverb_mix"
  puts "reverb_mix", b[0]
  set :reverb_mix, b[0]
end

live_loop :room do
  use_real_time
  b = sync "/osc/room"
  puts "room", b[0]
  set :room, b[0]
end

live_loop :damp do
  use_real_time
  b = sync "/osc/damp"
  puts "damp", b[0]
  set :damp, b[0]
end



#Echo Controlls
live_loop :echo_mix do
  use_real_time
  b = sync "/osc/echo_mix"
  puts "echo_mix", b[0]
  set :echo_mix, b[0]
end

live_loop :phase do
  use_real_time
  b = sync "/osc/phase"
  puts "phase", b[0]
  set :phase, b[0]
end

live_loop :echo_decay do
  use_real_time
  b = sync "/osc/echo_decay"
  puts "echo_decay", b[0]
  set :echo_decay, b[0]
end


#Wave and Envelope Curve
live_loop :wave do
  use_real_time
  b = sync "/osc/wave"
  if b[0] < 1
    set :wave,0
  elsif b[0] < 2
    set :wave,1
  elsif b[0] < 3.1
    set :wave,2
  end
end

live_loop :env_curve do
  use_real_time
  b = sync "/osc/env_curve"
  if b[0] < 1
    set :env_curve,1
  elsif b[0] < 2
    set :env_curve,2
  elsif b[0] < 3
    set :env_curve,3
  elsif b[0] < 4
    set :env_curve,4
  elsif b[0] < 5
    set :env_curve,4
  elsif b[0] < 6
    set :env_curve,6
  elsif b[0] < 7.1
    set :env_curve,7
  end
end

#Starting PiB303
set :startpib303, 0

live_loop :startpib303 do
  use_real_time
  b = sync "/osc/button1"
  puts "start"
  set :startpib303, b[0]
end



live_loop :play do
  with_fx :lpf do |lpf|
    control lpf, mix: get(:lpf_mix), cutoff: get(:lpf_cutoff)
    with_fx :hpf do |hpf|
      control hpf, mix: get(:hpf_mix), cutoff: get(:hpf_cutoff)
      with_fx :echo do |echo|
        control echo, mix: get(:echo_mix), phase: get(:phase), decay: get(:echo_decay)
        with_fx :reverb do |reverb|
          control reverb, mix: get(:reverb_mix), room: get(:room), damp: get(:damp)
          use_synth :tb303
          play chord(:Fs3, :m), amp: get(:amp), pan: get(:pan), attack: get(:attack), attack_level: get(:attack_level), decay: get(:decay),
            decay_level: get(:decay_level), sustain: get(:sustain), sustain_level: get(:sustain_level), release: get(:release),
            release_level: get(:release_level), cutoff: get(:cutoff), cutoff_slide: get(:cutoff_slide), res: get(:resonance),
            cutoff_attack: get(:cutoff_attack), cutoff_decay: get(:cutoff_decay), cutoff_sustain: get(:cutoff_sustain),
            cutoff_release: get(:cutoff_release), pitch: get(:pitch), wave: get(:wave), env_curve: get(:env_curve) if get(:startpib303)==1
          sleep 1
        end
      end
    end
  end
end
~~~

Here is sone detail on how to deal with multiway switches. I used two sets of toggle switches.
The first was called multi15 and had 5 columns but only 1 row of switches. ie the settings on the initial multiway toggle switch were set to 1 and 5. These elements are addressed by /test/multi15/1/1 up to /test/multi1/5/1 In each case a switch returns 1 when on and 0 when 0ff. I ticked the box exclusive on the template, which means that when you press any switch to turn it on any other switch is turned off. It is therefore important to test ONLY from switch on states, so that your loop does NOT respond to the switch being turned off.

The second array of multitoggles I called multi51. This had 5 rows but only one column of switches, ie 5 switches arranged vertically one below the next. These are addressed by /test/multi51/1/1 up to /test/multi51/1/5.

(In order to use just one live loop for each of these switches, I use a wild card * in the address position which changes. Thus in the first case I test for a match to
“/osc/test/multi15//1" The * will match any digit, in this case in the range 1…5
In the second case I test for a match to "/osc/test/multi51/1/
” where the * will match the digits 1…5

The problem then is how do you ascertain WHICH switch has been chosen. This is where my function parse_sync_address comes in.
This uses an undocumented function in Sonic Pi named get_event. This returns a lot of information on a detected event.
In the program below I print the full information returned by the get_event function when a switch change is detected.
eg
when "/osc/test/multi15/*/1" is matched
#<SonicPi::CueEvent:[[1523049098.4831412, 0, #<SonicPi::ThreadId [-1]>, 0, 0.0, 60.0], "/osc/test/multi15/1/1", [1.0]]

What the parse_sync_function does is to extract the matched address part of this information, ie "/osc/test/multi15/1/1"
It then uses the Ruby split function to convert this into a list of strings, with one element for each section of the address eg.
["osc", "test", "multi15", "1", "1"]
This is what the function returns. By inspecting the element corresponding to the wild card * we can see which switch was pressed. In this case it will be element 3. (they number from 0)

In the case of the multi51 switch array it will be element 4 as the wild card is in that position eg
"/osc/test/multi51/1/*"

The ouchOSC template used to test out the code is shown below

The code I used with this is shwon below

define:parse_sync_address do |address|
  v= get_event(address).to_s.split(",")[6]
  if v != nil
    return v[3..-2].split("/")
  else
    return ["error"]
  end
end


live_loop :multi15 do #1 row 5 columns ie horizontal 5 way switch
  use_real_time
  b= sync  "/osc/test/multi15/*/1"
  if b[0]==1 #only respond to switch on
    puts get_event "/osc/test/multi15/*/1"
    res = parse_sync_address "/osc/test/multi15/*/1"
    puts res
    switch = res[3]
    puts "horizontal switch #{switch} selected"
  end
end


live_loop :multi51 do #5 rows 1 column ie vertical 5 way switch
  use_real_time
  b= sync "/osc/test/multi51/1/*"
  if b[0]==1 #only respond to switch on
    puts get_event "/osc/test/multi51/1/*"
    res = parse_sync_address "/osc/test/multi51/1/*"
    puts res
    switch = res[4]
    puts "vertical switch #{switch} selected"
  end
end

# note #{number} lets you print number in the middle of a string.
2 Likes