Accessing a track send window (previously "Toggle Automation Safe button for a send?")
It looks like I posted in the wrong topic initially. Here's a consolidated version of my question.
I'm trying to write a script to apply changes to all sends that are assigned across selected (or all) tracks. For example, Automation Safe, Pre/Post, Follow Main Pan, Invert Pan, etc.
I need to test whether the floating window for a particular send, or any send for that matter, is displayed. However, merely testing that condition is causing an error.
for example, if I do this:
if(sf.ui.proTools.mainTrackOutputWindow.exists) {
log("window exists");
} else {
log("window does not exist");
}
I get:
11.01.2024 12:57:12.11 <info> [Backend]: Could not get UIElement for AxPtMainTrackOutputWindow: Could not find floating window for track output
[LOG] bar
and if I try:
if(sf.ui.proTools.sendMixerWindow.exists) {
log("window exists");
} else {
log("window does not exist");
}
I get:
11.01.2024 12:59:27.01 <info> [Backend]: Could not get UIElement for AxPtSendMixerWindow: Could not find floating send window
[LOG] bar
If I try another floating window, such as the Markers or Automation windows, this code works as expected.
I've looked for UI elements on a track itself (sf.ui.proTools.selectedTrack) but come up with nothing there either.
I also want to check if a particular send is assigned at all, and I can't figure out how to do that either.
It's unfortunate that there is only the ability to toggle, rather than to specify whether to open or close the send window.
I think I'm starting to understand why Andrew Scheps' BounceFactory saves the initial window state, and then closes all windows :)
Thank you in advance for any support, I'm an experienced programmer but I find the documentation for this API to be very difficult to understand.
- Chris Shaw @Chris_Shaw2024-01-12 19:35:04.951Z2024-01-12 20:53:34.080Z
Here's a script that will close all open send windows:
sf.ui.proTools.appActivate() let floatingWindows = sf.ui.proTools.floatingWindows; let sendWindows = floatingWindows.filter(win => win.buttons.whoseTitle.is("Output View selector").whoseValue.startsWith("send").exists) // Close all send windows sendWindows.forEach(window => window.windowClose())
All output windows are floating windows so the only way to determine if an output window is a send is to put them all into an array and test each one to see if the "Output View selector" button value starts with "send". You can use the same method to check if an output window is set to a specific send (a, b, c, etc.).
To understand the value/state of the automation safe button, open a send window and try this
let automationSafeBtn = sf.ui.proTools.mainTrackOutputWindow.buttons.whoseTitle.is("Automation Safe").first; log(automationSafeBtn.value)
This outputs this object when safe is off:
{ "Value": "", "IntValue": 0, "DoubleValue": 0, "Name": "AXValue", "Parent": "{\"title\":\"Automation Safe\",\"role\":\"AXButton\",\"fullRole\":\"AXButton:AXToggle\"}", "FriendlyNodeName": "AxStringOrIntProperty", "SupportsAutoUpdate": false }
and this value when safe is on:
{ "Value": "Selected", "IntValue": 0, "DoubleValue": 0, "Name": "AXValue", "Parent": "{\"title\":\"Automation Safe\",\"role\":\"AXButton\",\"fullRole\":\"AXButton:AXToggle\"}", "FriendlyNodeName": "AxStringOrIntProperty", "SupportsAutoUpdate": false }
With this info you can determine if safe is on with this:
let automationSafeBtn = sf.ui.proTools.mainTrackOutputWindow.buttons.whoseTitle.is("Automation Safe").first; let isSafeOn = automationSafeBtn.value.value == "Selected"; log(isSafeOn)
Which you can use to toggle the button on if it is currently off:
let automationSafeBtn = sf.ui.proTools.mainTrackOutputWindow.buttons.whoseTitle.is("Automation Safe").first; //Get safe status let isSafeOn = automationSafeBtn.value.value == "Selected"; / //If safe is off click to turn on if (!isSafeOn) automationSafeBtn.elementClick();
Then you can use something like this to set all open send windows' buttons to a specific value:
sf.ui.proTools.appActivate(); sf.ui.proTools.mainWindow.invalidate(); let selectedTracks = sf.ui.proTools.selectedTrackHeaders //Change the following variables for desired results let auxSend = 1; let buttonToSet = "Pre/Post fader"; const turnButtonOn = true; //use false to turn off /* Send Button Names - possible values: "Pre/Post fader" - on/off "Automation Safe" - "Selected" / "" "Link Panner to Main" - "Selected" / "" "Inverse Pan" - "Selected" /"" */ function closeAllOutputWindows() { //Close any existing outputWindows let existingFloatingWindows = sf.ui.proTools.floatingWindows; existingFloatingWindows.allItems.forEach(win => win.windowClose()) } //Determine new button state value depending on button let newButtonState if (buttonToSet == "Pre/Post fader") { newButtonState = turnButtonOn ? "on" : ""; } else { newButtonState = turnButtonOn ? "Selected" : ""; } closeAllOutputWindows() //Cycle through selected tracks selectedTracks.forEach(track => { track.trackSendToggleShow({ sendNumber: auxSend, onError: "Continue" }) //Get All floating windows let floatingWindows = sf.ui.proTools.floatingWindows; //Filter only Send windows let sendWindows = floatingWindows.filter(win => win.buttons.whoseTitle.is("Output View selector").whoseValue.startsWith("send").exists) // Set button for each send widow sendWindows.forEach(sendWin => { let sendWindowBtns = sendWin.buttons; let button = sendWindowBtns.whoseTitle.is(buttonToSet).first; //Check if button exist and click if necessary if (button.exists) { if (button.value.invalidate().value !== newButtonState) { button.elementClick() } } }) }) //Close any remaining output windows closeAllOutputWindows()
Chris Shaw @Chris_Shaw2024-01-12 20:45:57.685Z
The script will attempt to open the desired send on each track. If it doesn't exist it would normally throw an error but I've set
onError
toContinue
so it will just move on to the next track.
- RIn reply toRyan_Gildea⬆:Ryan Gildea @Ryan_Gildea
Thanks, Chris, for going through the effort to write that for me! I basically figured it out on my own but it's cool to see how you approached it. Basically the same but my code is a little harder to read lol. I also wanted it to work for any number of tracks I selected. Also I handled some additional edge cases.
If anyone's interested here it is.
sf.ui.proTools.requireMainWindow(); sf.ui.proTools.appActivateMainWindow(); const sendSettingsFromProps = { "Automation Safe": event.props.automationSafe, "Link Panner to Main": event.props.linkPannerToMain, "Pre/Post fader": event.props.preOrPostSend, "Link": event.props.linkStereoPanners, "Inverse Pan": event.props.inversePan, } const selectedTracks = sf.ui.proTools.selectedTrackHeaders; let tracksCount = 0, sendsCount = 0; const closeSends = () => { let floatingWindows = sf.ui.proTools.floatingWindows; let sendWindows = floatingWindows.filter(win => win.buttons.whoseTitle.is("Output View selector").whoseValue.startsWith("send").exists) // Close all send windows sendWindows.forEach(window => window.windowClose()) }; closeSends(); const disallowedTrackTypes = ["Basic Folder Track", "VCA Track", "Master Fader"] selectedTracks.forEach(track => { tracksCount++; log(`++++ Processing Track: ${track.normalizedTrackName} ++++`); if (disallowedTrackTypes.filter(trackType => track.title.value.includes(trackType)).length == 0) { for (let i = 1; i < 11; i++) { log(`---- Processing Send ${i} ----`); sendsCount++; if (track.sendSelectorButtons[i - 1].value.invalidate().value !== "unassigned") { // it's assigned to a send let result = track.trackSendToggleShow({ sendNumber: i, onError: "Continue" }) if (result.success) { // we can show the output window for (const [elementName, desiredValue] of Object.entries(sendSettingsFromProps)) { let button = sf.ui.proTools.mainTrackOutputWindow.buttons.whoseTitle.is(elementName).first; if (button.exists) { let val = button.value.invalidate().value.replace("inactive", "").trim(); // allow setting inactive sends too if (val != desiredValue) { log(` setting ${elementName} to "${desiredValue}" (was: "${val}")`); button.elementClick(); } } } } } } } }); log(`Total Tracks Processed: ${tracksCount}`); log(`Total Sends Procesed: ${sendsCount}`); closeSends();```