Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 2 of 2

Thread: OSC digital audio workstation controller (Reaper)

  1. #1
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    138

    OSC digital audio workstation controller (Reaper)

    Working on a Teensy based OSC (Open Sound Control) controller I have a question on how to handle messages mixed with bundles. All the examples are about either bundles or messages but not both. What would be a good solution for this?

    Reaper is sending both bundles and messages. This is what the serial monitor output looks like when treating the input as messages:

    Code:
    Receiving OSC Message...
    Adres = : #bundle
    Receiving OSC Message...
    /track/4/pan		Float   = : 0.48
    Adres = : /track/4/pan
    Receiving OSC Message...
    Adres = : #bundle
    Here's the code:

    Code:
    // OSC  test code
    
    #include <Bounce.h>
    #include <Encoder.h>
    #include <SPI.h>
    #include <Ethernet.h>
    #include <EthernetUdp.h>
    #include <OSCBundle.h>
    
    // ***** BUTTONS *****
    #define BUTTON_ONE_PIN 20
    #define BUTTON_TWO_PIN 21
    
    int             debounceTime=5;
    Bounce          buttonOne = Bounce(BUTTON_ONE_PIN,debounceTime);
    Bounce          buttonTwo = Bounce(BUTTON_TWO_PIN,debounceTime);
    elapsedMicros   sinceButtonCheck;                                   // timer for button check
    unsigned int    buttonCheckInterval=1000;                           // interval in microseconds for checking buttons
    // ***** END BUTTONS *****
    
    // ***** ENCODERS *****
    Encoder encoderOne(18,19);
    Encoder encoderTwo(22,23);
    long    currentEncoderOneValue=0;
    long    currentEncoderTwoValue=0;
    
    // you can find this written on the board of some Arduino Ethernets or shields; I couldn't find it but it does not matter :-)
    byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 
    
    //ports
    const unsigned int serverPort  = 8000;     // Reaper Out port
    const unsigned int destPort    = 9000;     // Reaper IN port
    
    //Create UDP message object
    EthernetUDP Udp;
    
    //the Teensy's IP
    IPAddress ip(192, 168, 178, 177);
    //destination IP
    IPAddress outIp(192,168,178,38);
    
    void setup(){
      Serial.begin(115200); 
      Serial.println("OSC test");
    
      pinMode(BUTTON_ONE_PIN, INPUT_PULLUP);
      pinMode(BUTTON_TWO_PIN, INPUT_PULLUP);
      //setup ethernet part
      Ethernet.begin(mac,ip);
      Udp.begin(serverPort);
    }
    
    void loop(){
      //process received messages
      OSCMsgReceive();
      //OSCBundleReceive();
      
      // check elapsedMicros and millis timers
      if (sinceButtonCheck >=buttonCheckInterval) {
        sinceButtonCheck = sinceButtonCheck - buttonCheckInterval;
        handleButtons();
        handleEncoders();
      }  
    } 
    void OSCBundleReceive(){
      OSCBundle bundleIN;
      int size;
      if( (size = Udp.parsePacket())>0) {
        while(size--) bundleIN.fill(Udp.read());
        if(!bundleIN.hasError()) {
          bundleIN.route("/track", showMessage);
          bundleIN.route("/fx", showMessage);
          bundleIN.route("/fxparam", showMessage);
        }
      }  
    }
    void OSCMsgReceive(){
      OSCMessage msgIN;
      int size; 
      char str[32];   
      if((size = Udp.parsePacket())>0){
        while(size--)
          msgIN.fill(Udp.read());
        if(!msgIN.hasError()){
          Serial.println("Receiving OSC Message...");
          msgIN.route("/track",showMessage);
    
        msgIN.getAddress(str);
        Serial.print("Adres = : ");
        Serial.println(str);
        }
      }
    }
    
    void showMessage(OSCMessage &msg, int addrOffset){
      char adresStr[48];    
      msg.getAddress(adresStr);
      Serial.print(adresStr);
      Serial.print("\t\t");
      if (msg.isInt(0)){        
        Serial.print("Int     = : ");
        Serial.println(msg.getInt(0));
      } 
      else if(msg.isFloat(0)){       
        Serial.print("Float   = : ");
        Serial.println(msg.getFloat(0));
      } 
      else if(msg.isBoolean(0)){       
        Serial.print("Boolean = : ");
        Serial.println(msg.isBoolean(0));
      }
      else if(msg.isString(0)){   
        char str[48]    ;
        msg.getString(0,str,48);
        Serial.print("String  = : ");
        Serial.println(str);
      }
    }
    
    void handleButtons(){
      if (buttonOne.update()) {
        if (buttonOne.fallingEdge()) {
         Serial.println("Button One pushed");
           
         OSCMessage msgOUT("/device/track/-");
         msgOUT.add(1.0);
        Udp.beginPacket(outIp, destPort);
        msgOUT.send(Udp); // send the bytes
        Udp.endPacket(); // mark the end of the OSC Packet
        msgOUT.empty(); // free space occupied by message
        }
      }
      if (buttonTwo.update()) {
        if (buttonTwo.fallingEdge()) {
         Serial.println("Button Two pushed");
         OSCMessage msgOUT("/device/track/+");
         msgOUT.add(1.0);
        Udp.beginPacket(outIp, destPort);
        msgOUT.send(Udp); // send the bytes
        Udp.endPacket(); // mark the end of the OSC Packet
        msgOUT.empty(); // free space occupied by message
        }
      }
    }
    
    void handleEncoders(){
      // long for new encoder value
      long newEncoderValue = 0;
      // Encoder One
      newEncoderValue=encoderOne.read();
      if (newEncoderValue!=currentEncoderOneValue){
        currentEncoderOneValue=newEncoderValue;
        Serial.println(currentEncoderOneValue);
      }
      // Encoder Two
      newEncoderValue=encoderTwo.read();
      if (newEncoderValue!=currentEncoderTwoValue){
        currentEncoderTwoValue=newEncoderValue;
        Serial.println(currentEncoderTwoValue);
      }
    }
    
    void funcValue(OSCMessage &msg, int addrOffset ){
    
      int value = msg.getFloat(0);
      OSCMessage msgOUT("/Fader/Value");
    
      Serial.print("Value = : ");
      Serial.println(value);
    
      msgOUT.add(value);
    
      Udp.beginPacket(Udp.remoteIP(), destPort);
      msgOUT.send(Udp); // send the bytes
      Udp.endPacket(); // mark the end of the OSC Packet
      msgOUT.empty(); // free space occupied by message
    }
    This is what my test setup looks like:

    Click image for larger version. 

Name:	Teensy-OSC-test.jpg 
Views:	14 
Size:	82.1 KB 
ID:	14547

    Two rotary encoders I had from a previous experiment, hardware debounced as suggested in the Bourns datasheet. The ethernet is handled by a wiz850io on the adapter board. On the hardware side everything is working perfectly. Now I have to figure out the software side of it.
    This screenshot gives an overview of how things fit together (OSC treated as bundle):

    Click image for larger version. 

Name:	Reaper_OSC_Response.jpg 
Views:	13 
Size:	165.3 KB 
ID:	14548

    The serial monitor shows the Reaper response after the left encoder button has been pressed making track 5 the selected track. All track parameters are sent complete with readable insert names and all. Even the effect parameter names are complete and not some cryptic six character summary like with the Mackie MCU or HUI protocols. A nice benefit of using OSC communication in stead of MIDI is the fact that Reaper does not complain about missing midi devices when compiling and uploading the code to the Teensy but the most important part is the level of detail in the messages. What I've seen so far opens up a whole new range of possibilities.

  2. #2
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    138
    I was able to solve this myself, here's what I came up with:
    Code:
    void OSCReceive(){
      OSCBundle   bundleIN;
      OSCMessage  msgIN;
      int size;
      if( (size = Udp.parsePacket())>0) {
        byte udpData[size];
        for (int i=0;i<size;i++) udpData[i]=Udp.read();
        // if data begins with # it is a bundle
        if (udpData[0]==35){
          bundleIN.fill(udpData,size);
          if(!bundleIN.hasError()) {
            Serial.println("Receiving OSC Bundle...");
            bundleIN.route("/track", showMessage);
            bundleIN.route("/fx", showMessage);
            bundleIN.route("/fxparam", showMessage);
          }
        }
        // if data begins with / it is a message
        else if (udpData[0]==47){
          msgIN.fill(udpData,size);
          if(!msgIN.hasError()){
            Serial.println("Receiving OSC Message...");
            msgIN.route("/track",showMessage);
            msgIN.route("/fx",showMessage);
            msgIN.route("/fxparam",showMessage);
          }
        }
      }
    }
    Example output:
    Code:
    OSC test
    Receiving OSC Bundle...
    /track/4/pan		Float   = : 0.47
    /track/4/pan/str		String  = : 6%L
    Receiving OSC Bundle...
    /track/4/pan		Float   = : 0.46
    /track/4/pan/str		String  = : 7%L
    Receiving OSC Bundle...
    /track/4/pan		Float   = : 0.46
    /track/4/pan/str		String  = : 8%L
    Receiving OSC Message...
    /track/4/pan		Float   = : 0.46
    So far, so good

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •