OSC messages and dynamic live_loop creation

Hi,
I write this post looking for some help.

But before that, I would like to say thanks to all members of this great community.
I really like this idea of using code as a creative tool.

I have learned a lot from all of you, in particular reading Martin’s , @robin.newman’s and samaaron’s posts in this forum.

CONTEXT:
Recently I have started a mini project that involves Sonic Pi. I have created an interface with Html and javascript that communicates with Sonic Pi via OSC. That works just fine. The project is a drum machine. You can select between all the sonic pi’s percussion instruments and add it to your drum machine.

Here is an image of my app:

WHAT I HAVE SO FAR :
On my js application, each time a user adds a new instrument, Sonic pi receives a new OSC message. I parse the address and get the name of the instrument using a function called parse_sync_address created by robin.newman.

MY PROBLEM :
I don’t know how to CREATE A LIVE_LOOP only and only if I receive a new OSC message indicating the new instrument to add.
I’ve read this: Create live_loops dynamically with a for loop
but this is different to what I want to achieve because the iteration is defined in advance with all the needed elements.

If anyone could point me on the right direction I will finish this
little project and I will make it available to everyone in github.

Thank you!!!

1 Like

Hi @RiTor,

I would create all live_loops in advance an run these constantly. Otherwise you 1. would have to reevaluate the code to start a loop and 2. you probably would have some hard work to ensure the synchronisation of the different loops.

I have done some projects quite similar to yours (e. g. this one); the code is quite old and messy. Currently I am working again on a Monome application and will release a working version soon (it is running with much better performance with SP 3.2.x now :wink: ).

2 Likes

can you give an example of the live loop you want to create. Also do you want to alter the live loop once it has started playing, for example its content, or to stop it again.

1 Like

You could define a number of ‘instruments’ in advance and change the sample and pattern with OSC.
Then have them all running in one (live_)loop.

Something like this:

#Changed with OSC messages:
sample_0 = :drum_kick_hard
pattern_0 = (bools 1,0,0,0,1,0,0,0)
#...
sample_32 = :drum_kick_hard
pattern_32 = (bools 1,0,0,0,1,0,0,0)

#This part just keeps running:
loop do
  sample sample_0 if pattern_0.look
  #...
   sample sample_32 if pattern_32.look
end

That’s what I did when I experimented with Processing->OSC->SP to make similar projects.

2 Likes

Hi @robin.newman
Thanks for your reply.

This is the idea of the code I intend to produce:

live_loop :get_input do
  use_real_time
  sync "/osc*/instrument/**"
  input_inst = parse_sync_address "/osc*/instrument/**"
  set :current_action, input_inst.slice(2, 1)[0]
  set :instrument_name, input_inst.slice(3, 1)[0]
end

# HOW  CAN I MAKE THIS DYNAMICALLY ?
if get[:current_action] == "remove_instrument"
  # Remove a live loop called like the :instrument_name
  # I know i can just set the volume to zero, but
  # I would like to kill the loop in order to
  # waste as less ressources as possible
end

# HOW  CAN I MAKE THIS DYNAMICALLY
if get[:current_action] == "add_instrument"
  # Add a live loop like the one below.
  # of course this is not working right now because it
  # executes only each time I click play and not
  # dynamically
  live_loop "play_#{get[:instrument]}" do
    16.times do |i|
      # Along with the sample, I use some logic
      # to get the current pattern,
      # but for the purposes of brevity
      # I don't include it
      sample get[:instrument], amp: 0.8
      sleep 0.25
    end
  end
end

As you can see I would like to do two things:

  • Create a live_loop each time an add_instrument message is received
  • Kill or stop a loop each time a remove_instrument message is received

I know I can make this thing in a simpler manner using the advise of @Davids-Fiddle and @Martin, but I’m curious about how I could do it dynamically.

Thanks!!

1 Like

Thank you for your help @Davids-Fiddle,
I figure it out how to do this like you propose.
Anyways, I’m curious about how to achieve the same result in
a dynamic way (I think I can learn some interesting things in the process).

@Martin , as I said to Davids I have took in consideration the
idea of defining in advance the live loops. Anyway, I asked robin his advice too.
I took a look of your projects and I’ve been really surprised while watching the monome application video. That’s really cool!!
I tried to read the readme in your github but I’m not quite sure I understand well enough how this thing working. I will take a closer look and try to understand it better.

I really appreciate the work you guys do sharing your knowledge.
That includes @Davids-Fiddle too, I also learned things from his videos :slight_smile:

2 Likes

Ok I’ll take a look at this and get back to you.

2 Likes

Maybe this (or other ideas in the same thread) could be useful to you? Programmatically Creating Live Loops On The Fly

My goal was to create a solution where you could arbitrarily run (or stop) a piece of code within a live_loop and it works well enough for my needs.

1 Like

here is some code which will define live_loops for you and stop them when asked too.
It should work for any built in sample (provided there duration is short enough for the timings in your specimen live loop.
If you want the live loops all completely different, you can define a separate defLoop function for each one you expect to use. The basic principle should work though.
In the example, I’m sending all the osc input from sonic-pi rather than from an external source.

use_osc "localhost",4560
use_debug false



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 :getdata do
  use_real_time
  sync "/osc*/instrument/**"
  input_inst = parse_sync_address "/osc*/instrument/**"
  puts input_inst
  set :current_action, input_inst.slice(2, 1)[0]
  set :instrument_name, input_inst.slice(3, 1)[0]
  inst= get(:instrument_name)
  if get(:current_action)=="add_instrument"
    puts inst
    defLoop inst #create and start live_loop
  end
  if get(:current_action) == "remove_instrument"
    
    set ((inst+"k").to_sym),1
  end
end


define :defLoop do |name| #function to define and start live loop
  live_loop  ("ll"+name).to_sym do
    16.times do |i|
      sample name.to_sym
      #whatever else you like in here
      sleep 0.25
      #stop section for loop
      if get((name+"k").to_sym)==1
        set ((name+"k").to_sym),0
        stop
      end
    end
  end
end


#trial run
osc "/instrument/add_instrument/bd_haus"
sleep 4
osc "/instrument/add_instrument/drum_cymbal_hard"
sleep 4
osc "/instrument/remove_instrument/drum_cymbal_hard"
sleep 4
osc "/instrument/remove_instrument/bd_haus"
2 Likes

@robin.newman It works perfectly!! Thank you. I will continue to work on this small project and I’ll share it with all of you when it’s done.

Thanks to everyone for helping me. I 'll be around here reading you and maybe, someday I will be able to help others :slight_smile:

if you share your project, you will for sure help lot of us !
see you soon !

3 Likes