Monday, August 05, 2019

Set up OSC in Unity to send and receive data.

To test my code I send OSC data from a small python script on my Mac across the local network. I have to open a port in the Windows firewall before I can receive the message.

Here is the C# script:

using System.Collections;
using UnityEngine;

/// <summary>
/// This class uses https://github.com/thomasfredericks/UnityOSC
/// </summary>
public class PythonOSC : MonoBehaviour
{

    [SerializeField]
    private OSC _osc;
    
    void Start()
    {
        _osc.SetAddressHandler("/hello", OnHello); //simple 'hello world' test path
        _osc.SetAllMessageHandler(OnAnyMessage);        //handle any message
        StartCoroutine(DelaySendMessage());     //Test sending a message
    }

    /// <summary>
    /// Will send an OSC message to address "/hello" with 
    /// a single float parameter after 2 seconds
    /// </summary>
    /// <returns></returns>
    private IEnumerator DelaySendMessage()
    {
        yield return new WaitForSeconds(2);
        var msg = new OscMessage {address = "/hello"};
        msg.values.Add(0.1f);
        _osc.Send(msg);
    }
    
    /// <summary>
    /// Will handle all OSC messages arriving on the incoming port 
    /// </summary>
    /// <param name="oscm"></param>
    private void OnAnyMessage(OscMessage oscm)
    {
        Debug.Log("any message received");
    }

    /// <summary>
    /// Handler for the "/hello" address
    /// Parse the float from the message
    /// </summary>
    /// <param name="oscm"></param>
    private void OnHello(OscMessage oscm)
    {
        var str = oscm.GetFloat(0);
        Debug.Log($"Said Hello {str}");
    }

}

And the python script:

"""Small example OSC client
This program sends 10 random values between 0.0 and 1.0 to the /filter address,
waiting for 1 seconds between each value.
"""
import argparse
import random
import time

from pythonosc import osc_message_builder
from pythonosc import udp_client


if __name__ == "__main__":
  parser = argparse.ArgumentParser()
  parser.add_argument("--ip", default="localhost",
      help="The ip of the OSC server")
  parser.add_argument("--port", type=int, default=3001,
      help="The port the OSC server is listening on")
  args = parser.parse_args()

  client = udp_client.SimpleUDPClient(args.ip, args.port)

  for x in range(10):
    val = random.random();
    print(args.ip, args.port,val);
    client.send_message("/hello", val)
    time.sleep(1)

I also create a receiver in ChucK to handle the value sent from Unity:

//osc.ck

// create our OSC receiver
OscRecv oscin;

3001 => oscin.port;

// start listening (launch thread)
oscin.listen();

// ..or listen to all messages
//oscin.listenAll();

// create an address in the receiver 
// and store it in a new variable.
oscin.event("/hello,f") @=> OscEvent HelloEvent; 

while ( true ){
    
    HelloEvent => now;
    
    while ( HelloEvent.nextMsg() != 0 ){
        HelloEvent.getFloat() => eventBus.hrmEvent.val;

        eventBus.hrmEvent.broadcast();
    }
    
}

This completes the testing for OSC messaging to and from Unity. I’ll deal with sending OSC over wifi from the Arduino Feather later..