Communicating between Pure Data and MATLAB using OSC
Asked Answered
H

2

7

I'm trying to receive a message sent over OSC from Pure Data (or Max/MSP) to MATLAB.

  • I'm able to send OSC messages from Pure Data to Pure Data
  • I can send messages from MATLAB to MATLAB
  • I can even send messages from MATLAB to Pure Data
  • ...I'm really struggling to get Pure Data to talk back to MATLAB

Here's my code that sends messages from MATLAB (I'm using the oscmex protocol):

host = 'localhost'; % local host UDP address
sendPort = 3333; % UDP port number to send over
receivePort = 3333; % UDP port number to receive from
oscAddress = osc_new_address(host, sendPort); % open send address
oscServer = osc_new_server(receivePort); % open server

dataPacket = struct('path','/foo','tt','f','data',{num2cell([1.0])}); % create packet
osc_send(oscAddress, dataPacket); % write packet to OSC

oscMessage = osc_recv(oscServer, 0.1); % listen for packet on OSC

% check to see if anything is there...
if length(oscMessage) > 0
    fprintf('Found something!')
else
    fprintf('Failed to find anything')
end

osc_free_address(oscAddress);
osc_free_server(oscServer);

If I send using host 'localhost', everything works fine sending from MATLAB to MATLAB using the code above. If I set it to '127.0.0.1', MATLAB sends to Pure Data, but MATLAB then can't receive its own messages.

Now for the other end of things. Here's my Pure Data patch:

Pure Data patch image

Again, running the above patch alone successfully sends and receives messages through Pure Data.

The problem lies when I try to talk from one program to another. If I set things so that MATLAB is sending on port 3333 and Pure Data is receiving on 3333, and that Pure Data is sending on 2222 and MATLAB is receiving on 2222, I can make Pure Data receive if MATLAB's host is '127.0.0.1'. But, with '127.0.0.1', MATLAB can't send to itself.

In any case, no matter what I try, I'm unable to make Pure Data send to MATLAB, despite being able to get it to send to itself. I suspect it has something to do with the 'host' address.

My actual IPv4 address (found using 'ipconfig' of the MS command prompt) is completely different from 127.0.0.1, and using the value specified here doesn't seem to make things work any better.

I'm aware that I can't have more than one OSC server with the same port open at any one time and so my current attempt at a solution involves sending from MATLAB on one port, and sending from Pure Data on another, with only a single server open at one time on either port.

Note I'm also aware that I use /foo for messages from MATLAB and /test from Pure Data. However, my MATLAB code indiscriminately receives everything sent over OSC, so this makes no difference.

Any help getting PD to talk to MATLAB would be appreciated.

Update: Ive solved the 'localhost' issue and that doesn't seem to fix things (i had to add localhost to my Windows 'hosts' file). So, I may have been barking up the wrong tree by worrying about the localhost thing. But, I still can't get PD to talk to MATLAB.

Update #2: Amro has posted an elegant solution below and I still can't get MATLAB to receive messages from Pure Data. I've installed CloseTheDoor to monitor my UDP connections and notice that when MATLAB sets up a server, it uses the 'Interface' [::0], whereas PD sets uses 'Interface' 0.0.0.0. Since PureData is the one that successfully receives messages, perhaps I need to make MATLAB listen on 0.0.0.0 as well?

Hinch answered 13/6, 2013 at 0:4 Comment(11)
Did you try 'localhost' as the host name? This appears to be the name used with udpsend in your diagram, and the one that was working for MATLAB -> MATLAB communication. Not that it should make a difference, but worth a try...Storage
Yes, 'localhost' is the only host name (in MATLAB) that allows communication with MATLAB. However, only '127.0.0.1' allows communication from MATLAB to Pure Data. Neither (set in the Pure Data patch) allows communication from Pure Data to MATLABHinch
Have you tried to set the send port to another value (1 less) than the receive port? Also you could try to set the receiving host address to "0.0.0.0" which listens on any network interface not only the loopback. Also - what exactly do you call the "localhost issue"? Finally, is there any firewall involved?Grimsley
Thanks, I will try these when I get the chance. As for the 'localhost' issue, I mean the fact that originally I couldn't get MATLAB to talk to itself unless I used the host 'localhost', whereas I could talk to PD using '127.0.0.1' but not MATLAB itself. I believe I now have this working since changing my Windows 'hosts' file to define 'localhost' as '127.0.0.1'. In other words, I think the localhosts thing is a red herring here.Hinch
Sure - the localhost thing is another problem, because localhost just is the usual DNS name for 127.0.0.1. One or another should work (some software might use 127.0.0.1 as a name instead of IP so this could fail as well) - but if you've tried both there's another problem. I developed a software myself that talks to Pure Data via OSC - unfortunately the Pure Data side was not my part of the job so I cannot tell exactly if it behaves any special. But I know it can work ;-)Grimsley
Firewall is down; the problem persists! For some reason now I can't even get MATLAB to talk to MATLAB, and I have no idea what has changed to make that happen... This is a headache..Hinch
See update to question, explaining a nuance in the UDP connection interface I have uncovered.Hinch
@CaptainProg: what OS are you running? Are you sure a firewall is not interfering here? Please undo any changes you've made to hosts file. I've tested my code in R2013a running on WinXP 32-bit, and it works just fine... Note that the "0.0.0.0" address means that the server is listening on all interfaces. The double-colon address is IPv6 notation I think, could this be a IPv4 vs IPv6 issue? Usually ::1 is the same as 127.0.0.1, while ::0 means 0.0.0.0.Heartless
The OSC-MEX module uses liblo library. I think you should try to compile it yourself with IPv6 support explicitly enabled (looking at the code, there are a couple of #ifdef ENABLE_IPV6 macros in the source). As far as I can tell, the OSC-MEX binaries provided were compiled against liblo v0.22 if that makes a difference. btw you never mentioned the bitness of your OS and your MATLAB version that you running (32-bit vs 64-bit), that could be a factor.Heartless
Blimey. I once spent a solid two days trying to compile osc-mex with no success. Believe me - that's not a road I want to go down again. I even got to the point of contacting the author of osc-mex for support, and found that there were unresolved errors in the compilation files, and that there are layers upon layers of dependant libraries. I think that this is where I give up! I'll try your solution on another system though, to make sure it's not something else blocking my ports. By the by, I'm on 32-bit Win 7, MATLAB 2012a.Hinch
@CaptainProg: I managed to compile osc-mex from sources. See my recent edit :) Now I'm certain that the problem is related to IPv6, there's even a note on liblo page about it. So I would stick with IPv4 for now.. Perhaps you can temporarily disable it: informationweek.com/byte/personal-tech/…Heartless
H
5

Let me start by saying that I've never used PureData or OSC before, and I just duplicated the graph/patch you've shown to create the server/client.

1) server in PureData, client in MATLAB:

First lets create the server in PureData:

server_PD: pastebin.com/hwJzxri2

Now here is a simple client implemented as a GUI in MATLAB:

function example_osc_client()
    handles = createGUI();
    osc = [];

    function h = createGUI()
        h.fig = figure('Menubar','none', 'Resize','off', ...
            'CloseRequestFcn',@onClose, ...
            'Name','OSC Client', 'Position',[100 100 220 140]);
        movegui(h.fig, 'center')
        h.conn = uicontrol('Style','pushbutton', 'String','Connect', ...
            'Callback',{@onClick,'connect'}, ...
            'Parent',h.fig, 'Position',[20 20 80 20]);
        h.disconn = uicontrol('Style','pushbutton', 'String','Disconnect', ...
            'Callback',{@onClick,'disconnect'}, ...
            'Parent',h.fig, 'Position',[120 20 80 20]);
        h.slid = uicontrol('Style','slider', 'Callback',@onSlide, ...
            'Min',-10, 'Max',10, 'Value',0, ...
            'Parent',h.fig, 'Position',[30 60 160 20]);
        h.txt = uicontrol('Style','text', 'String','0.0', ...
            'Parent',h.fig, 'Position',[80 100 60 20]);
        set([h.slid;h.disconn], 'Enable','off');
        drawnow
    end

    function onClick(~,~,action)
        switch lower(action)
            case 'connect'
                osc = osc_new_address('127.0.0.1', 2222);
                set(handles.conn, 'Enable','off')
                set(handles.disconn, 'Enable','on')
                set(handles.slid, 'Enable','on')
            case 'disconnect'
                osc_free_address(osc); osc = [];
                set(handles.conn, 'Enable','on')
                set(handles.disconn, 'Enable','off')
                set(handles.slid, 'Enable','off')
        end
        drawnow
    end

    function onSlide(~,~)
        if isempty(osc), return; end
        val = single(get(handles.slid,'Value'));
        m = struct('path','/test', 'tt','f', 'data',{{val}});
        osc_send(osc, m);
        set(handles.txt, 'String',num2str(val))
        drawnow
    end

    function onClose(~,~)
        if ~isempty(osc)
            osc_free_address(osc);
        end
        delete(handles.fig);
    end
end

client_MATLAB

As you move the slider, messages are sent to the server (using OSC-MEX interface), and the values are displayed in the PureData model.

While testing this, I noticed that double type was not supported, as I saw the following message in the PD log window:

unpackOSC: PrintTypeTaggedArgs: [A 64-bit float] not implemented

So it was necessary to either manually cast values as single or explicitly specify the hint type in the structure passed to osc_send OSC-MEX function:

val = single(1);
m = struct('path','/test', 'tt','f', 'data',{{val}});
osc_send(osc, m);

2) server in MATLAB, client in PureData:

Similarly we create the client in PureData:

client_PD: pastebin.com/P1KcM00m

Again, here is the server implemented as a MATLAB GUI:

function example_osc_server()
    handles = createGUI();
    osc = [];

    function h = createGUI()
        h.fig = figure('Menubar','none', 'Resize','off', ...
            'CloseRequestFcn',@onClose, ...
            'Name','OSC Server', 'Position',[100 100 220 140]);
        movegui(h.fig, 'center')
        h.start = uicontrol('Style','pushbutton', 'String','Start', ...
            'Callback',{@onClick,'start'}, ...
            'Parent',h.fig, 'Position',[20 20 80 20]);
        h.stop = uicontrol('Style','pushbutton', 'String','Stop', ...
            'Callback',{@onClick,'stop'}, ...
            'Parent',h.fig, 'Position',[120 20 80 20]);
        h.txt = uicontrol('Style','text', 'String','', ...
            'Parent',h.fig, 'Position',[60 80 100 20]);
        set(h.stop, 'Enable','off');
        drawnow expose

        h.timer = timer('TimerFcn',@receive, 'BusyMode','drop', ...
            'ExecutionMode','fixedRate', 'Period',0.11);
    end

    function onClick(~,~,action)
        switch lower(action)
            case 'start'
                set(handles.start, 'Enable','off')
                set(handles.stop, 'Enable','on')
                osc = osc_new_server(2222);
                start(handles.timer);
            case 'stop'
                set(handles.start, 'Enable','on')
                set(handles.stop, 'Enable','off')
                osc_free_server(osc); osc = [];
                stop(handles.timer);
        end
        drawnow expose
    end

    function receive(~,~)
        if isempty(osc), return; end
        m = osc_recv(osc, 0.1);
        if isempty(m), return; end
        set(handles.txt, 'String',num2str(m{1}.data{1}))
        drawnow expose
    end

    function onClose(~,~)
        if ~isempty(osc)
            osc_free_server(osc);
        end
        stop(handles.timer); delete(handles.timer);
        delete(handles.fig);
        clear handles osc
    end
end

server_MATLAB

The server part was a bit trickier in MATLAB. The idea is that we don't want MATLAB to block indefinitely waiting for messages. So I created a timer which executes every 0.11 second. Inside the timer function we try to receive message in a blocking manner but with a timeout of 0.1 sec. This way both the GUI and MATLAB IDE stay responsive.


3) other combinations:

Using the above solutions, you could also open both client and server in PureData, or both client and server in MATLAB. It should work either way.

Finally I should say that it made no difference whether I'm using the hostname as localhost or specified the IP address directly 127.0.0.1.

HTH


EDIT:

I managed to compile the OSC-MEX package myself, here are the steps. First download osc-mex sources and its dependencies. This includes: liblo sources, pthreads-win32 binaries, premake4 executable.

1) We start by building the liblo library:

  • Copy "premake4.exe" into the "build" directory, and run: premake4 --platform=x32 vs2010
  • open the generated "liblo.sln" solution file in VS2010. Select the "liblo" project and go to "Project > Properties". Add the include folder containing pthreads header files in the "Additional Include Directories" field. Similarly add the lib folder for the linker, and specify pthreadVC2.lib as additional dependency.
  • Select the "ReleaseLib" Win32 target and build the project. This should create the final target: lib\ReleaseLib\liblo.lib

Note that by default, IPv6 support is disabled in liblo because OSC applications like Pd have problems with IPv6. If you still want to enable it, add the following line to config.h file:

#define ENABLE_IPV6 1

2) Next we compile the MEX-functions in MATLAB:

  • Go to the folder containing the C-sources of the MEX-functions
  • copy liblo.lib from the previous step into this directory. Also copy pthreadVC2.lib from the pthreads library.
  • compile each function using:

    mex -largeArrayDims -I../path/to/liblo-0.27 xxxxxx.c pthreadVC2.lib liblo.lib -lwsock32 -lws2_32 -liphlpapi
    
  • You should end up with six *.mexw32 files for each of the xxxxxx.c source files

  • Finally copy the pthreads DLL into this same folder: pthreadVC2.dll

To save you some troubles, here are the compiled MEX-files built on WinXP 32-bit and Win8 64-bit both using VS2010. Here are the sources if you would like to compile it yourself (simply build the solution in VS2010, then run osc_make.m in MATLAB)

Heartless answered 16/6, 2013 at 17:56 Comment(2)
Amro, thanks very much for your efforts with this. Unfortunately the problem persists: even with your 'server in MATLAB, client in PD' example, no data makes it through to MATLAB. Thus I continue to try to find a solution... Currently wondering if something else is blocking the connection (firewall is off).Hinch
See update to question, explaining a nuance in the UDP connection interface I have uncovered.Hinch
G
0

localhost is an alias for 127.0.0.1; they really are the same IP-address. so if matlab only receives something if it is sending to localhost but not if sending to 127.0.0.1, they probably have a buggy implementation of OSC.

as long as you have [udpreceive 2222] in your patch, Pd will block the port UDP/2222, and matlab will not be able to receive anything on that port.

so the simple solution is: remove [udpreceive 2222] before creating the matlab server using osc_new_server(2222);

Geronimo answered 13/6, 2013 at 9:57 Comment(1)
Thanks; yes I have tried this - I'm aware that I can't have more than one OSC server with the same port open at any one time and so my current attempt at a solution involves sending from MATLAB on one port, and sending from Pure Data on another, with only a single server open at one time on either port.Hinch

© 2022 - 2024 — McMap. All rights reserved.