Working with OSC as variables


I started to learn to import OSC signals in to SonicPi.
As shown in the examples you just add the receiving osc message to something.

But is it possible to use the incoming osc messages as variables for adjusting loops that are already playing?

oscfoot = incomingoscsignal / 2 (I use the divider to show that I need certain osc signals to transform or map the data in the correct usable range)

and then use oscfoot thru ought the code in a buffer.


I don’t know the answer but I’ll be watching this space. It’s something I’d like to experiment with, too!

I dont know the answer either. but a wild guess says put the osc values into
a set: statement, in one loop, and in the loop you want to control use a get
statement to change the value… roughly like:
(syntax may be wrong, but the idea’s sound)

use_bpm 60

live_loop :get_value_from_osc do
  osc_value = incomingoscsignal / 2
  set :rate, osc_value
sleep 0.01

live_loop :rate_changer do
  get rate, :rate
  sample :ambi_choir, rate: rate, amp: 2
  sleep 1

Lets see how close I am to the final answer. :slight_smile:



here is a lengthy example, which might help and provide some ideas how to cope with incoming messages from different controllers or switches. The live_loop :simulate_a_rotary_controller does what it says: Acctually Sonic Pi sends OSC messages to itself in this example:

use_osc "", 9000

use_bpm 120

live_loop :simulate_a_rotary_controller do
  osc_send "", 4559, "/rotary/controller/0", (line 0, 1, inclusive: true, steps: 20).reflect.tick
  sleep 0.25

# A very useful function provided by Robin Newman
define :parse_osc do | path |
  v = get_event(path).to_s.split(",")[6]
  if v != nil
    return v[3..-2].split("/")
    return "Could not decipher osc path..."

live_loop :osc_device_watcher do
  m  = "/osc/**" # everything which comes in as osc ...
  data  = sync m
  seg   = parse_osc m
  puts "Seg contains these elements: #{seg}"
  puts "With 'seg' you can find out, which controller was used ..."
  puts "... and build contitional statements like the following ..."
  if seg[3] == "0" # if the controller 0 is being turned, change the volume from 'the_sample'
    puts "Simulated rotary controller sends:  #{data[0]}"
    # now store what's coming in into a variable
    set :vol_rotary_0, data[0]
    # and set the desired element via 'control' to this value during runtime
    control get(:the_sample), amp: get(:vol_rotary_0)

live_loop :amen_controlled_by_osc do
  s = sample :loop_amen, beat_stretch: 4, amp: get(:vol_rotary_0)
  set :the_sample, s
  sleep 4

Edit: Here is a short example by Robin.

pfieuw though nut to crack. I tried the short example and I think I got it working, think…

Going to digest on yours now! :slight_smile:

I already noticed my next issue with this… How to map my incoming data :slight_smile: -500 to +500 aren’t actually usable numbers… :smiley:

A question: From where is your data coming? What osc capable device are you using?

OSC data is coming from an ESP8266 with a accelerometer attached to it.

But dat you tell it. I could easily map the data in there! :smiley:

Yes, I thought so. Otherwise you can do things like (untested code sketch):

# map 0 - 1 to 0 - 500;
# in case of negative values e. g. from -500 - 500 it'll
# be a bit more complicated but basically you will map
# -500 - 0 to 0 - 0.5 and 1 - 500 to 0.51 - 1.0 or some similar way
set(:vol), 1 / 500.0 * data[0] 

@robin.newman Is it possible to describe you example even more :sweat_smile: because I’m trying to get values from sensors in with OSC and manipulate my effects or panning.

# A very useful function provided by Robin Newman

define :parse_osc do | path |
v = get_event(path).to_s.split(",")[6]
if v != nil
return v[3…-2].split("/")
return “Could not decipher osc path…”

I don’t get this at all. Does this look for any incoming OSC command?

I want to achieve something like

set pan
set fx
set sample2

Or do I need to set certain values in the live loops? Or I’m to tired an should just called it a day :stuck_out_tongue:

I decided to develop a program to illustrate what you want to do. Working well. Just doing final testing then I publish the details.
now published see here


use_cue_logging false

I was not aware of that. Might save some performance in my scripts… :slight_smile:

yes it can save a lot of clutter

I’m getting this in my cue log

/set/s #<SonicPi::SynthNode @id=23, @name=sonic-pi-basic_stereo_player @state=running>

and this is the code

For some reason suddenly OSC stopped working. I did work a few weeks ago though.

live_loop :foo do
  a, b, c = sync "/osc/trigger/prophet"
  synth :prophet, note: a, cutoff: b, sustain: c

and code from esp8266

`#include <HMC5883L.h>

//#include <Adafruit_HMC5883_U.h>
#if defined(ESP8266)
#include <ESP8266WiFi.h>        // Include the Wi-Fi library
#include <WiFi.h>
#include <WiFiUdp.h>            // UDP library
#include <OSCMessage.h>         // OSC library
#include <Wire.h>

HMC5883L compass;

const char* ssid     = "*";         // The SSID (name) of the Wi-Fi network you want to connect to
const char* password = "*";     // The password of the Wi-Fi network

WiFiUDP Udp; // A UDP instance to let us send and receive packets over UDP
//const IPAddress outIp(192,168,0,234); // remote IP of your computer
//const IPAddress outIp(172,18,96,53); // remote IP of your computer @kask
const IPAddress outIp(192,168,0,190); // remote IP of your computer @kask

const unsigned int outPort = 4559; // remote port to receive OSC
const unsigned int localPort = 8888; // local port to listen for OSC packets (actually not used for sending)

void setup() {
  Serial.begin(115200);         // Start the Serial communication to send messages to the computer
  WiFi.begin(ssid, password);             // Connect to the network
  Serial.print("Connecting to ");
  Serial.print(ssid); Serial.println(" ...");

  int i = 0;
  while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect
    Serial.print(++i); Serial.print(' ');

  Serial.println("Connection established!");  
  Serial.print("IP address:\t");
  Serial.println(WiFi.localIP());         // Send the IP address of the ESP8266 to the computer

Serial.println("Starting UDP");
Serial.print("Local port: ");
#ifdef ESP32

// Initialize HMC5883L
Serial.println("Initialize HMC5883L");
while (!compass.begin())
    Serial.println("Could not find a valid HMC5883L sensor, check wiring!");



void checkSettings()
  Serial.print("Selected range: ");
  switch (compass.getRange())
    case HMC5883L_RANGE_0_88GA: Serial.println("0.88 Ga"); break;
    case HMC5883L_RANGE_1_3GA:  Serial.println("1.3 Ga"); break;
    case HMC5883L_RANGE_1_9GA:  Serial.println("1.9 Ga"); break;
    case HMC5883L_RANGE_2_5GA:  Serial.println("2.5 Ga"); break;
    case HMC5883L_RANGE_4GA:    Serial.println("4 Ga"); break;
    case HMC5883L_RANGE_4_7GA:  Serial.println("4.7 Ga"); break;
    case HMC5883L_RANGE_5_6GA:  Serial.println("5.6 Ga"); break;
    case HMC5883L_RANGE_8_1GA:  Serial.println("8.1 Ga"); break;
    default: Serial.println("Bad range!");
  Serial.print("Selected Measurement Mode: ");
  switch (compass.getMeasurementMode())
    case HMC5883L_IDLE: Serial.println("Idle mode"); break;
    case HMC5883L_SINGLE:  Serial.println("Single-Measurement"); break;
    case HMC5883L_CONTINOUS:  Serial.println("Continuous-Measurement"); break;
    default: Serial.println("Bad mode!");

  Serial.print("Selected Data Rate: ");
  switch (compass.getDataRate())
    case HMC5883L_DATARATE_0_75_HZ: Serial.println("0.75 Hz"); break;
    case HMC5883L_DATARATE_1_5HZ:  Serial.println("1.5 Hz"); break;
    case HMC5883L_DATARATE_3HZ:  Serial.println("3 Hz"); break;
    case HMC5883L_DATARATE_7_5HZ: Serial.println("7.5 Hz"); break;
    case HMC5883L_DATARATE_15HZ:  Serial.println("15 Hz"); break;
    case HMC5883L_DATARATE_30HZ: Serial.println("30 Hz"); break;
    case HMC5883L_DATARATE_75HZ:  Serial.println("75 Hz"); break;
    default: Serial.println("Bad data rate!");
  Serial.print("Selected number of samples: ");
  switch (compass.getSamples())
    case HMC5883L_SAMPLES_1: Serial.println("1"); break;
    case HMC5883L_SAMPLES_2: Serial.println("2"); break;
    case HMC5883L_SAMPLES_4: Serial.println("4"); break;
    case HMC5883L_SAMPLES_8: Serial.println("8"); break;
    default: Serial.println("Bad number of samples!");


void loop() { 
  Vector raw = compass.readRaw();
  Vector norm = compass.readNormalize();

  int Xmap = norm.XAxis;
  float XmapDecimal = (map(norm.XAxis, -500, 600, 0, 1000));
  float XmapDecimalCorrect = XmapDecimal / 1000;
  //Serial.print(" Xraw = ");
   //Serial.print(" XRaw is now =");
// Serial.print(XmapRound);
  //Serial.print(" Yraw = "); 
  //Serial.print(" Zraw = ");
  Serial.print(" Xnorm = ");
  //Serial.print(" Ynorm = ");
  //Serial.print(" ZNorm = ");
  OSCMessage msg("/trigger/prophet");

 // OSCMessage msg2("/test/amen");
   // msg.add(raw.YAxis);
    //msg.add(raw.ZAxis);    Udp.beginPacket(outIp, outPort);

Occasionally I get the OSC system not working. It is sometimes after my computer has gone to sleep with SP running. It fails to work when you wake it up again. A restart of SP solves this.
I assume you have receive remote OSC messages ticked in the IO prefs.
Also, you havn’t switched from Sonic PI 3.1 to 3.2dev? If so the port moves to 4560 not 4559 and you need to use /osc*/trigger/prophet to receive the osc cue, inserting a * where shown.

Restarted SP!
Receive remote check!
Should I go to dev? I’m still 3.1.

Still doesn’t work

Are OSC messages working locally? Try the other example I posted for you
Have a look at the log files, particularly erlang.log You could build 3.2dev but you need to be aware of the changes I have pointed out (using sync “/osc*/…” instead of sync “/osc/…” and using port 4560 instead of 4559.

Ok very stupid and neglectant of me! I commented something out of my Arduino code and with the screen resized I didn’t notice it… I’m looking at your new example!! Thanks!!!