No internet connection
  1. Home
  2. Macro and Script Help

Accessing a track send window (previously "Toggle Automation Safe button for a send?")

By Ryan Gildea @Ryan_Gildea
    2024-01-11 20:02:46.254Z

    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.

    • 3 replies
    1. 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()
      
      1. 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 to Continue so it will just move on to the next track.

      2. R
        In reply toRyan_Gildea:
        Ryan Gildea @Ryan_Gildea
          2024-01-14 16:50:13.791Z

          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();```