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

Thread: OSC controller for Reaper digital audio workstation (DAW)

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

    OSC controller for Reaper digital audio workstation (DAW)

    One of the issues with most commercially available control surfaces is the fact that they use the Mackie HUI or MCU MIDI protocol for communication. These protocols were designed to work with the 31kb/s midi bandwidth and as a result are limited when it comes to (text) feedback. On a Mackie control I often find myself guessing as to what it is I'm looking at. Open Sound Control (OSC) has none of these limitations but unfortunately OSC support is still rare. Reaper is one of the few DAWs offering comprehensive OSC support. To explore the available OSC interface I build a basic OSC controller.

    Click image for larger version. 

Name:	Reaper_OSC_test.jpg 
Views:	76 
Size:	141.3 KB 
ID:	14737

    A Teensy 3.6 with a WIZ820_SD_ADAPTOR and WIZ850io module for wired ethernet, the connections are as in the example for the WIZ adapter. Two rotary encoders, one (with the small knob, indented) for selecting a parameter and one (big knob, not indented) for changing the value. A 4D Systems display showing the track number and name, plugin name, parameter name and parameter value as string and virtual knob display. The display also has vu meter showing the track volume. Not shown are five button for navigating tracks and plugins.
    The code only uses libraries that come with the Teensyduino installation. The 4D Systems VisiGenie library is not used, relevant parts are used directly in the sketch and asynchronous communication with a kind of display queue is implemented.
    During setup the controller sends the settings to Reaper and a 'refresh all control surfaces' action is triggered' which will cause Reaper to send all track data.
    Code:
      // send bundle with controller definition & state
      OSCBundle bndl;
      bndl.add("/device/track/count").add(NUM_OF_TRACKS);
      bndl.add("/device/send/count").add(NUM_OF_SENDS);
      bndl.add("/device/fx/count").add(NUM_OF_INSERTS);
      bndl.add("/device/fxparam/count").add(NUM_OF_PARAMETERS);
      bndl.add("/device/track/select").add(selectedTrack.number);
      bndl.add("/device/fx/select").add(selectedInsert.number);
      bndl.add("/device/fxparam/bank/select").add(selectedParameterBank);
    
      Udp.beginPacket(outIp, destPort);
      bndl.send(Udp);
      Udp.endPacket(); // mark the end of the OSC Packet
      bndl.empty(); // empty the bundle to free room for a new one
    
      // send 'reset all controllers' message so Reaper will send all track data
      sendOscMessage("/action",41743);
    The received data is parsed and stored in structs modelling the track structure (tracks, sends, inserts, parameters):

    Code:
    // struct containing OSC parameter data & values
    struct oscParameter{
      char  name[NAME_LENGTH];                            // parameter long name
      int   number;                                       // parameter number
      char  valueStr[VALUE_LENGTH];                       // formatted string value e.g. '350Hz'
      float value;                                        // normalised value
    };
    typedef struct oscParameter OscParameter;
    
    // struct for fx plugin
    struct trackInsert{
      char  name[NAME_LENGTH];                            // plugin long name
      int   number;                                       // plugin number
      bool  bypass;                                       // bypass status
      //OscParameter parameter[NUM_OF_PARAMETERS];        // plugin parameters
    };
    typedef struct trackInsert TrackInsert;
    
    // track send parameters
    struct trackSend{
      char  name[NAME_LENGTH];                            // send long name
      int   number;                                       // send number
      char  volumeStr[VALUE_LENGTH];                      // volume string    
      float volume;                                       // normalised volume
      char  panStr[VALUE_LENGTH];                         // panning string    
      float pan;                                          // normalised panning
    };
    typedef struct trackSend TrackSend;
    
    // struct containing Reaper track info & data
    struct track{
      char        name[NAME_LENGTH];                      // track name
      int         number;                                 // track number
      char        volumeStr[VALUE_LENGTH];                // volume string    
      float       volume;                                 // normalised volume
      char        panStr[VALUE_LENGTH];                   // panning string    
      float       pan;                                    // normalised panning
      bool        rec;                                    // record arm
      bool        mute;                                   // track mute
      bool        solo;                                   // track solo
      bool        monitor;                                // track monitor
      TrackSend   send[NUM_OF_SENDS];                     // track sends
      TrackInsert insert[NUM_OF_INSERTS];                 // track effects/instrument plugins
    };
    typedef struct track Track;
    Using these data the controller maintains the state of the tracks (for the number of tracks as defined in settings). This is a major improvement over HUI or MCU controllers which are essentially stateless and all functionality is implemented on the DAW. Maintaining state allows for context switching on the controller (e.g. select sends or pan for editing) without having to rely on the DAW to provide this function.
    The state of the plugin parameters is only only maintained for the selected plugin, this will be expanded to the selected track. Maintaining state for all tracks requires lots of memory at higher track counts so I didn't implement this.
    A print function is available to print the content of the structs to the serial monitor:

    Code:
    SELECTED TRACK PARAMETERS
    	Name		EMT Plate
    	Number		11
    	Volume		0.00dB
    	Pan		C
    	Record		0
    	Mute		0
    	Solo		0
    	Monitor		0
    
    	Send 1		Send 1		-inf dB		center
    	Send 2		Send 2		-inf dB		center
    	Send 3		Send 3		-inf dB		center
    	Send 4		Send 4		-inf dB		center
    	Send 5		                                		                		                
    	Send 6		                                		                		                
    	Send 7		                                		                		                
    	Send 8		                                		                		                
    
    	Insert 1	1	EMT 140
    	Insert 2	0	
    	Insert 3	0	
    	Insert 4	0	
    	Insert 5	0	
    	Insert 6	0	
    	Insert 7	0	
    	Insert 8	0	
    
    SELECTED INSERT PARAMETERS
    	Plate		B
    	Damping A	2.2 s
    	Damping B	2.4 s
    	Damping C	1.2 s
    	EQ		Out
    	LFreq		0.0000
    	LGain		-8.5 dB
    	HFreq		1.0000
    	HGain		0.0 dB
    	PreDly		0.0000
    	ModRate		0.0000
    	ModDepth	0.0000
    	Width		1.0000
    	WetSolo		Solo
    	Mix		0.2474
    	LowCut		M 90Hz
    
    
    TRACK OVERVIEW
    	1	-8.42dB		C	SEM		Oberheim SEM V		Lexicon
    	2	0.00dB		C	Marshall	Big Muff		Lexicon
    	3	0.00dB		C	Clean Guitar	ReaTuner		EMT Plate
    	4	0.00dB		C	Piano		ReaInsert (Cockos)	Lexicon
    	5	-9.72dB		C	Moog		ReaInsert (Cockos)	Lexicon
    	6	0.00dB		C	ARP		ReaInsert (Cockos)	Lexicon
    	7	-0.29dB		C	OB-6		ReaInsert (Cockos)	Send 1
    	8	0.00dB		C	Prophet		ReaInsert (Cockos)	Send 1
    	9	+1.13dB		C	Oppo					Lexicon
    	10	0.00dB		C	Lexicon		Lexicon 224		Send 1
    	11	0.00dB		C	EMT Plate	EMT 140			Send 1
    	12	0.00dB		C	Time Cube	Cooper Time Cube	Send 1
    	13	-inf dB		C	Track 13				Send 1
    	14	-inf dB		C	Track 14				Send 1
    	15	-inf dB		C	Track 15				Send 1
    	16	-inf dB		C	Track 16				Send 1
    Next up will be some more testing and figuring out how to best use the available functionality.

    Here's the complete sketch: OSC_Controller_20180902.zip

    Hopefully this helps a little to get people started with building OSC controllers.


    Suggestions for improving the code are welcome. I'm still learning C(++), just starting to get the hang of structs and pointers. Next up: classes

  2. #2
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    138
    A short video of the controller:


  3. #3
    I missed your posts when you published them, but thanks for the description and the code too. Very inspiring (and very useful too, since I'm looking to wrap my head around OSC...)

    In the last years I was more on building MIDI controllers ( one and two ) for live use with hardware instruments, but a day will come when I'll turn my attention on my Reaper-powerd-DAW ...

    Thank you!

    Stefano

  4. #4
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    138
    You're welcome, glad to hear that the code is of use to someone

    Here's an updated version of the code: OSC_Controller_20180923.zip

    I created a basic Reaper class to hold all the track data etc. The controller configuration is in Reaper.h:

    Code:
    #define NUM_OF_TRACKS     32    // number of tracks in a bank = number of tracks Reaper will send
    #define NUM_OF_PARAMETERS 48    // number of effect/instrument parameters in parameter bank = number of fx parameters Reaper will send
    #define NUM_OF_SENDS      8     // number of sends, linked to Reaper OSC config = the number of sends Reaper will send.
    #define NUM_OF_INSERTS    8     // number of fx inserts, linked to Reaper OSC config = the number of fx inserts Reaper will send.
    #define NAME_LENGTH       32    // length of string for name strings
    #define VALUE_LENGTH      16    // length of string for value strings
    The idea here is to get all track data and browse through pages on the controller so the controller can act completely independent from the focus (selected track) in Reaper. Something I want to try is to build a simple controller dedicated to a single plugin. The controller should scan all tracks for the occurrence of the plugin and present a list of track names to easily select the plugin for editing.

    PluginMap.h is a start of a parameter definition setup for plugins. Reaper currently does not send text feedback for continuous plugin parameters so I'll have to come up with a workaround for that. While I'm at it I'll also add some metadata that currently isn't available through OSC (or MIDI) like the step count of a parameter.

  5. #5
    Wow brilliant!

    I have to check your code!!


    Stefano

Posting Permissions

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