python receive OSC message from Max

1.6k Views Asked by At

I am a fairly experienced python user, but I have no experience with OSC protocol, MAX and SuperCollider.

I am helping a friend in building a python code that should do the following:

  1. receive a message from MAX via OSC
  2. elaborate the message in python
  3. send the elaborated message to SuperCollider

What it is not clear to me is what concerns the 1 and 3 points.

PART 0

I import the following libraries

from pythonosc import dispatcher
from pythonosc import osc_server
from pythonosc import udp_client

PART 1

I tried to setup a simple server for point 1, since I am only receiving one string at a time from MAX:

def listen2Max(ip,port):
    '''
    set up server
    '''
    # dispatcher to receive message
    dispatcher = dispatcher.Dispatcher()
    dispatcher.map("/filter", print)
    # server to listen
    server = osc_server.ThreadingOSCUDPServer((ip,port), dispatcher)
    print("Serving on {}".format(server.server_address))
    server.serve_forever()

but i do not know what to return in this function, also I do not get the use of "/filter" in the map. Is it something that must be specified in MAX?

PART 2

This is actually my strong suit: I take the string message returned by my listen2Max function, do some py stuff and prepare a message called mymove to be sent to SuperCollider

PART 3

For point 3 I set up a simple client to communicate the string var mymove to SuperCollider

def talk2SC(ip,port,mymove):
    '''
    set up client
    '''
    client = udp_client.SimpleUDPClient(ip,port)
    client.send_message("/filter", mymove)

Should it work like that?

1

There are 1 best solutions below

0
brodegon On BEST ANSWER

This is the code that gets (at least part of) the job done.

Bear in mind that, in this case, Max sends strings to python. The messages I get are something like this: O-O e8g8 8 1

import argparse

from pythonosc import dispatcher 
from pythonosc import osc_server 
from pythonosc import udp_client

def main(path: str, *osc_arguments):
    msg = osc_arguments[-1]
    print("input message: {}".format(msg))
    msgOUT = msg+'whatever'
    # output
    print("output message: {}".format(msgOUT))
    ipOUT = osc_arguments[0][0]
    portOUT = osc_arguments[0][1]
    pathOUT= osc_arguments[0][2]
    talk2SC(ipOUT,portOUT,pathOUT,msgOUT)

def listen2Max(addrIN,addrOUT):
    '''
    set up server
    '''
    # input address
    ipIN   = addrIN[0]
    portIN = addrIN[1]
    pathIN = addrIN[2]
    # output address
    portOUT = addrOUT[0]
    pathOUT = addrOUT[1]
    # dispatcher to receive message
    disp = dispatcher.Dispatcher()
    disp.map(pathIN, main, ipIN, portOUT, pathOUT)
    # server to listen
    server = osc_server.ThreadingOSCUDPServer((ipIN,portIN), disp)
    print("Serving on {}".format(server.server_address))
    server.serve_forever()

def talk2SC(ip,port,path,mymove):
    '''
    set up client
    '''
    client = udp_client.SimpleUDPClient(ip,port)
    client.send_message(path, mymove)

if __name__ == "__main__":
    # generate parser
    parser = argparse.ArgumentParser(prog='scacchiOSC', formatter_class=argparse.RawDescriptionHelpFormatter, description='Interprete di messaggi OSC da Max\n')
    parser.add_argument("-II","--ipIN", type=str, default="127.0.0.1", help="The ip to listen on")
    parser.add_argument("-PI", "--portIN", type=int, default=5005, help="The port to listen on")
    parser.add_argument("-UI", "--uripathIN", type=str, default="/filter", help="MAX's URI path")
    parser.add_argument("-PO", "--portOUT", type=int, default=5006, help="The port to send messages to")
    parser.add_argument("-UO", "--uripathOUT", type=str, default="/filter", help="output URI path")
    args = parser.parse_args()
    # wrap up inputs
    outputAddress = [args.portOUT, args.uripathOUT]
    inputAddress = [args.ipIN, args.portIN, args.uripathIN]
    # listen to max
    listen2Max(inputAddress, outputAddress)

This code gets messages from a client like this:

from pythonosc import dispatcher
from pythonosc import osc_server
from pythonosc import udp_client
from typing import List, Any

def talk2SC(ip,port,path):
    '''
    set up client
    '''
    # maxmsg = 'Nxe6 f4e6'
    maxmsg = 'e4 e2e4'
    client = udp_client.SimpleUDPClient(ip,port)
    client.send_message(path, maxmsg)


if __name__ == '__main__':
    talk2SC("127.0.0.1",5005,"/filter")

and sends messages to a server like this:

from pythonosc import dispatcher
from pythonosc import osc_server
from pythonosc import udp_client
from typing import List, Any


def main(address: str, *osc_arguments):
    '''
    prende il messaggio di MAX e lo traduce aggiornando la scacchiera
    '''
    # read message
    msg = osc_arguments[0]
    # initialize board
    print(osc_arguments)
    # outputc

def listen2Max(ip,port,path):
    '''
    set up server
    '''
    # dispatcher to receive message
    disp = dispatcher.Dispatcher()
    handler = disp.map(path, main)
    # print(handler)
    # server to listen
    server = osc_server.ThreadingOSCUDPServer((ip,port), disp)
    print("Serving on {}".format(server.server_address))
    server.serve_forever()

if __name__ == '__main__':
    listen2Max("127.0.0.1",5006,"/chmove")

The tricky part is to make it communicate with Max. Luckily enough I found an answer here:

  1. format each Max message as /path "O-O e8g8 8 1"
  2. make python listen on the specified /path

If one wants python to listen to each and every path, one could change the default path in the parser item and use "*" as default. Then

  1. Max's messages could have whatever path
  2. in the parser one will have parser.add_argument("-UI", "--uripathIN", type=str, default="*", help="MAX's URI path")

Now, I just have to fix the SuperCollider part.