Introduction
interface.
Out-of-the-Box Functionality
Out of the box, REAPER supports a limited set of functions via the OSC interface, which are defined in .ReaperOSC files located in ~/Library/Application\ Support/REAPER/OSC/ on MacOS. You can modify the address patterns of OSC messages REAPER will react to and call pre-defined functions, but you can't define your own functions directly.
Defining and Calling Custom Actions
Fortunately, REAPER supports calling custom actions via /action/_COMMAND_ID OSC messages.
Below, I'll demonstrate the setup. Please note that I'll be modifying some of REAPER's configuration files directly, as certain modifications via REAPER itself are not possible or are very unhandy. Always backup your files before making any modifications to avoid losing your work or breaking your existing setup.
Custom actions are stored in ~/Library/Application\ Support/REAPER/reaper-kb.ini on MacOS. The file format description can be found documentation. _MY_COMPANY_TEST_SIMPLE_OSC (note the underscore) is the Command ID of the test_simple_osc.lua script, which should be placed in the ~/Library/Application\ Support/REAPER/Scripts/MyCompany/ directory.
Content of test_simple_osc.lua:
reaper.ShowMessageBox("This is a simple script that can be invoked via OSC", "Info", 0)
After manually editing files, REAPER must be restarted for changes to take effect!
To verify the script and reaper-kb.ini are correct, run REAPER, and from the Actions dialog (Shift + ?), find the MyCompany: test simple OSC trigger action and run it. You should see a message box with the text "This is a simple script that can be invoked via OSC".
Now you can send an OSC message to trigger the custom action. For testing purposes, you can use
Passing Arguments to Custom Actions
The above method works well for many use cases, but it has one downside: it's impossible to pass any arguments when calling actions. If the variance of possible argument values is low, one can create multiple actions. For example, to enable or disable a certain option: /action/_ENABLE_OPTION, /action/_DISABLE_OPTION. This is fine, but such an approach is not suitable for passing numeric or string arguments to an action.
Is there a way to pass an argument to an action? Yes, as REAPER supports binding OSC messages to trigger actions. When an action is invoked this way, a single (first) OSC message argument of string or float type can be retrieved from the script.
To start, make sure binding OSC messages to actions is allowed: In REAPER, navigate to Preferences (Command + ,), then the Control/OSC/web section. Select the existing OSC control from the list and press Edit.
Check the Allow binding messages to REAPER actions and FX learn option and press Ok.
Create osc.lua in ~/Library/Application\ Support/REAPER/Scripts/MyCompany/ with the following content:
-- osc.lua
-- Utility functions to get and parse OSC message and argument from REAPER action context
local osc = {}
function osc.parse(context)
local msg = {}
-- Extract the message
msg.address = context:match("^osc:/([^:[]+)")
if msg.address == nil then
return nil
end
-- Extract float or string value
local value_type, value = context:match(":([fs])=([^%]]+)")
if value_type == "f" then
msg.arg = tonumber(value)
elseif value_type == "s" then
msg.arg = value
end
return msg
end
function osc.get()
local is_new, name, sec, cmd, rel, res, val, ctx = reaper.get_action_context()
if ctx == nil or ctx == '' then
return nil
end
return osc.parse(ctx)
end
return osc
Next to it, create test_osc_with_arg.lua:
-- Retrieve the directory of the current script.
local script_path = debug.getinfo(1, "S").source:match("@?(.*/)")
-- Set the package path to include the other scripts in the directory
package.path = package.path .. ';' .. script_path .. '?.lua'
-- Require the osc module
local osc = require('osc')
local msg = osc.get()
if msg then
reaper.ShowMessageBox("OSC address: " .. msg.address .. ", argument: " .. (msg.arg and msg.arg or "(nil)"), "Info", 0)
else
reaper.ShowMessageBox("Invalid or no OSC message", "Error", 0)
end
Add the following line to ~/Library/Application\ Support/REAPER/reaper-kb.ini:
SCR 4 0 MY_COMPANY_TEST_OSC_WITH_ARG "MyCompany: test OSC trigger with argument" "MyCompany/test_osc_with_arg.lua"
Finally, the newly created action can be mapped to OSC messages. This can be done in two ways:
- By editing the
~/Library/Application\ Support/REAPER/reaper-osc-actions.inifile; - Or by creating a shortcut from the
Actionsdialog by listening for an incoming OSC message.
I prefer the former method. Add the following line to ~/Library/Application\ Support/REAPER/reaper-osc-actions.ini:
"/test_message_with_arg" 0 0 _MY_COMPANY_TEST_OSC_WITH_ARG
This instructs REAPER to invoke the _MY_COMPANY_TEST_OSC_WITH_ARG action when a "/test_message_with_arg" OSC message is received.
Restart REAPER for changes to take effect.
To test that everything works, from the Terminal run: sendosc localhost 8000 /test_message_with_arg s "test string". This should invoke the test_osc_with_arg.lua script in REAPER and display a message box with the text "OSC address: test_message_with_arg, argument: test string".
Running sendosc localhost 8000 /test_message_with_arg f 123.456 will display the message "OSC address: test_message_with_arg, argument: 123.456001".
Running sendosc localhost 8000 /test_message_with_arg will display the message "OSC address: test_message_with_arg, argument: (nil)".
Limitations
REAPER has the following limitations when it comes to working with OSC messages:
- Scripts can only receive the first argument of the OSC message. As a workaround, a string argument can be used to encapsulate any number of arguments.
- Only string and float argument types are supported. Again, a string can be used to hold any required datatype that can be represented as a string.
- Sending OSC messages from scripts is not supported. As a workaround,
sendoscor a similar utility can be invoked usingos.execute(), or third-party REAPER extensions can be used to accomplish this task.
Conclusion
REAPER is a powerful DAW offering scripting and extensible external control capabilities, which makes it suitable for use as part of various automation applications, including DAW plugin test automation frameworks. Unfortunately, its minimalistic documentation makes feature discovery problematic and requires additional effort to make things work. Some basic functionality is still missing and requires workarounds.
SOCIAL SHARE CARD GENERATOR