v0.5 is a rather significant set of changes. I still wouldn't consider it 1.x...
I gave up on anything but the toggleTrackEnable{N}.lua approach. Right now, as I'm working on multiple computers, I have my scripts on a shared drive - so long as everything is in the same folder, the same magic in the toggleTrackEnable{N}.lua files works...
So here's the new toggleTrackEnable.lua - now with comments as it's rather specific to the use I'm putting it to.
{pathTo...}\Scripts\toggleTrackEnable.lua:
Code:
--[[
/**
* ReaScript Name: toggleTrackEnable
* Description: Toggle Track Enable
* Instructions: Install this in user reaper scripts.
* Author: pljones
* Licence: GPL v3
* Version: 0.5
*/
]]
function toggleTrackEnable()
is_new,name,sec,cmd,rel,res,val = reaper.get_action_context()
if not(is_new) then
return
end
-- val is just boolean but we want -1, not the 1 that's passed by OSC
if not(val == 0) then val = -1 end
-- the only way to get info from the OSC input trigger... ugh, ugh, ugh...
-- but it works more or less
local tkNo = name:match([[toggleTrackEnable(%d*)%.lua$]])
if (tkNo == nil) then
return
end
-- OSC tkNo from above starts at 1
-- Reaper GetTrack is zero based
-- The ReaperOSC
-- TRACK_SELECT b/track/@/onOff
-- mapping also maps OSC @=1 to Reaper GetTrack=0
-- Unfortunately, because of the extra "Sound source parent SEND" Reaper track,
-- in my project file, I need to do some magic...
-- OSC tkNo 1 needs to map to Reaper GetTrack 1.
-- Hence, the OSC mapping will have touched the wrong track, so turn it off.
local track = reaper.GetTrack(0, tkNo - 1)
if (track == nil) then
return
end
reaper.SetTrackSelected(track, 0)
-- And now, get the intended track
track = reaper.GetTrack(0, tkNo)
-- if this track is not a sound source (see below), ignore the request
if not(canToggle(track)) then
return
end
_toggleTrackEnableN(tkNo, track)
end
--[[
A track can be toggled if it has MIDI In for All inputs.
That's just how life is in my project file...
]]
function canToggle(track)
local recInput = reaper.GetMediaTrackInfo_Value(track, "I_RECINPUT")
-- Is this a MIDI track?
if recInput & 4096 == 0 then
return false
end
-- Is this receiving all inputs?
return recInput & 31 == 0
end
function _toggleTrackEnableN(tkNo, track)
-- local isTkFxEnabled = reaper.GetMediaTrackInfo_Value(track, "I_FXEN")
-- reaper.ShowConsoleMsg("track " .. tkNo .. "; setting " .. val .. "; isTkFxEnabled " .. isTkFxEnabled .. "\n")
local srTk = 0
local search = reaper.GetTrack(0, srTk)
local srVal = 0
while not(search == nil) do
if canToggle(search) then
-- For the target track, apply the OSC val, otherwise, turn off
if ((0 + srTk) == (0 + tkNo)) then srVal = val else srVal = 0 end
reaper.SetMediaTrackInfo_Value(search, "I_FXEN", srVal)
-- Trigger the ReaperOSC mapping for feedback
-- See notes above for why the mess is needed...
reaper.SetTrackSelected(reaper.GetTrack(0, srTk - 1), srVal)
end
srTk = srTk + 1
search = reaper.GetTrack(0, srTk)
end
end
Then, place multiple copies of the following (the "1" being the first - the "1" is important as it's used as the track number in the above) in the same folder as the main script. The script content is identical in each copy. (This hasn't changed since v0.1, I think.)
{pathTo...}\Scripts\toggleTrackEnable1.lua:
Code:
dofile(debug.getinfo(1,'S').source:match[[^@?(.*[\/])[^\/]-$]] .. 'toggleTrackEnable.lua')
reaper.defer(toggleTrackEnable)
Now... The reason for that "off by one" malarkey in the main script is because I had 80-odd copies of toggleTrackEnable{N}.lua already mapped up as actions and didn't want to have to remap everything. Of course, when I moved them to the shared folder, I had to remap everything (though a quick edit/replace-all sorted that) and maybe I could have fixed the issue there... Oh well...
So, bind each toggleTrackEnable{N}.lua to your controller - I'm sending "/track/{N}/onOff {val}" from mine.
v0.4 and later include support for feedback using the ReaperOSC mapping file and track selection. Create (and activate) a mapping with this content:
Code:
DEVICE_TRACK_COUNT 100
# Have a toggle button that sends this:
TRACK_SELECT b/track/@/onOff
Remember, this is a bi-directional mapping. So if you change the selected track in Reaper, Reaper emits the OSC command. But it does
not run the script! So all of a sudden, your controller is out of sync with Reaper...
0.5 brings a big new feature. I've added two more buttons to my TouchOSC controller (I'll attach that, too). These let me toggle what I'm hearing between incoming audio from ReaNINJAM and Jamulus. That's why I've now got the "Sound source parent SEND" track mentioned in the main script above. All my sound sources route through that, that gets send to two output (ReaNINJAM outgoing and ReaRoute output to Jamulus) tracks; the input from these services each has its own track (ReaNINJAM input being direct from the output track as it's a "thru" plugin; and Jamulus ReaRoute audio in) which then route to the Master out. It's this final step - the send to Master - that's controlled.
Simple, huh?
Code:
--[[
/**
* ReaScript Name: enableOnlineService
* Description: Switches between two online services for monitoring purposes.
* Instructions:
* # Install this in user reaper scripts
* # Ensure /{service}/onOff is bound to enableService-{service}.lua (because there's no way to look up the shortcut).
* # Ensure online service track receive exists, called {service} RECEIVE (and no others like that).
* Currently there is no feedback as it's not possible to send OSC from a script (and I can't think how to fake it here).
* Author: pljones
* Licence: GPL v3
* Version: 0.2
*/
]]
-- Put these two lines into enableService-{service}.lua for each {service}:
-- dofile(debug.getinfo(1,'S').source:match[[^@?(.*[\/])[^\/]-$]] .. 'enableOnlineService.lua')
-- reaper.defer(enableService)
function initService()
if not(initServiceDone == nil) then
return
end
hwMidiOut = nil
SVC_ENABLE = 110 -- CC no for service controls
local wantedMIDIOutputName = "TouchOSC Bridge"
local numMIDIOutputs = reaper.GetNumMIDIOutputs()
local midiOut = 0
while midiOut < numMIDIOutputs
do
retval, nameout = reaper.GetMIDIOutputName(midiOut, "")
if not(nameout == nil) and nameout == wantedMIDIOutputName then
hwMidiOut = midiOut
midiOut = numMIDIOutputs
end
midiOut = midiOut + 1
end
-- reaper.ShowConsoleMsg("initService: " .. wantedMIDIOutputName .. " " .. hwMidiOut .. "\n")
initServiceDone = true
end
function enableService()
is_new,name,sec,cmd,rel,res,val = reaper.get_action_context()
if not(is_new) then
return
end
local service = name:match("^.+enableService%-?(.+)%.lua$")
if (service == nil) then
return
end
_enableService(service)
end
function _enableService(service)
-- reaper.ShowConsoleMsg("service " .. service .. "; val " .. val .. "\n")
local serviceNo = 0
local srTk = 0
local search = reaper.GetTrack(0, srTk)
while(not (search == nil))
do
local retVal, searchName = reaper.GetSetMediaTrackInfo_String(search, "P_NAME", "", false)
if (not(searchName == nil)) then
local receiveName = searchName:match("^(.+) RECEIVE$")
if (not(receiveName == nil)) then
-- it's a service receive
local ret
if (service == receiveName) then
-- this is track we are looking for
--[[
Previously this side simply set the value "on" (1) for both commands
Now it reads and toggles
]]
local val = reaper.GetMediaTrackInfo_Value(search, "B_MAINSEND")
if not(val == 0) then val = 0 else val = 1 end
ret = reaper.SetMediaTrackInfo_Value(search, "B_MAINSEND", val)
reaper.StuffMIDIMessage(16 + hwMidiOut, 0xBF, SVC_ENABLE + serviceNo, val)
--[[
else
-- it's a service receive but a different one
ret = reaper.SetMediaTrackInfo_Value(search, "B_MAINSEND", 0)
reaper.StuffMIDIMessage(16 + hwMidiOut, 0xBF, SVC_ENABLE + serviceNo, 0)
]]
end
serviceNo = serviceNo + 1
end
end
srTk = srTk + 1
search = reaper.GetTrack(0, srTk)
end
end
initService()
Yay for StuffMIDIMessage! This is what's known as a "nasty hack". The buttons send OSC and are not MIDI-enabled. Instead, I have an LED overlay that is MIDI-enabled. We also ensure this goes out the TouchOSC Bridge. The script relies on track names to find the desired input (i.e. "{service} RECEIVE" track is the one) and sets the track main send on or off appropriately. As I only want one in, it turns any other "... RECEIVE" tracks off.
The new buttons are down the bottom right on every tab, like this:
And that's created through the ns_kit7+NIAR+AD-muxed v4.touchosc file (I had to rename it to add .ZIP to allow it to be attached, so rename it back to remove the extra .ZIP before trying to open it in TouchOSC Editor).
I'd make the RPP available but it's 32Mb and probably not much use, directly... Instead,
JamulusReaNINJAM.rpp is a mini demo that works with the scripts and the TouchOSC layout. You can add additional sound source tracks under the "Sound source parent SEND" and it should keep working (up to however many mapped OSC toggles you have).
Change Log
0.5 / 0.2: Minor update to the receive toggles not to be mutually exclusive any more - proved more harm than good in use
0.5: Significant rewrite plus enableOnlineService.lua script, Touch OSC mapping and sample Reaper project file
0.4: Add track selection to trigger OSC feedback (as there's no API for it directly)
0.3: Fix off by one error
0.2: Loop over all tracks and disable them if not the target track
0.1: Initial version