No internet connection
  1. Home
  2. How to

Toggle A/B Switch in ADPTR MetricAB

By Håkan Lundberg @Hakan_Lundberg
    2025-10-23 10:28:13.258Z2025-10-23 10:40:28.438Z

    Is there a more direct way of doing this now that the new Soundflow v6 / Pro Tools integration is here?

    The old one script, as far as I can tell, works by Soundflow opening the plugin and clicking the button, which works but not always, at least for me.

    A more accurate version would simply be awesome!

    • 16 replies

    There are 16 replies. Estimated reading time: 20 minutes

    1. Chad Wahlbrink @Chad2025-10-23 18:22:47.070Z2025-10-31 21:46:14.110Z

      Hi, @Hakan_Lundberg,

      Try this out. Set the track name at the top.

      You should be able to run this from anywhere in a session or even from a different app 🏄‍♂️

      let trackName = 'METERS'
      let pluginName = 'ADPTR MetricAB'
      
      function swapMetricAB() {
          // Show Plugin
          let pluginIndex = sf.ui.proTools.sfx.invalidate().trackGetByName({ name: trackName }).track.insertButtons.findIndex(x => x.value.value.startsWith(pluginName));
      
          // Show Insert
          sf.ui.proTools.sfx.trackGetByName({ name: trackName }).track.trackInsertToggleShow({ insertNumber: pluginIndex + 1, targetValue: "Enable" })
      
          // Get the window of the audiosuite plugin window
          var pluginView = sf.ui.proTools.sfx.windows.whoseTitle.is("Plug-in: ADPTR MetricAB").first;
      
          // Wait for Plugin Window
          pluginView.elementWaitFor();
      
          // Get the plugin window's frame for the relative position data. 
          var pluginViewFrame = pluginView.frame;
      
          // Get the ui element at the global scope based on the position of the plugin windows frame. 
          var pluginElement = sf.ui.root.getElementAtPosition(pluginViewFrame.x + 144, pluginViewFrame.y + 491);
      
          // Click "A/B"
          pluginElement.elementClick();
      
          // Log A or B
          pluginElement.value.value === "Off" ? log('A') : log('B');
      
          // Show Insert
          sf.ui.proTools.sfx.trackGetByName({ name: trackName }).track.trackInsertToggleShow({ insertNumber: pluginIndex + 1, targetValue: "Disable" })
      
      }
      
      swapMetricAB();
      
      1. Ben Rubin @Ben_Rubin
          2025-10-23 18:57:11.047Z2025-10-23 21:11:35.045Z

          Thanks for this, @Chad!

          Works great. For anyone else like me whose print track name changes all the time, here's what you can replace so you can choose your track by Input Name.

          Just replace

          let trackName = 'METERS'
          

          with

          sf.ui.proTools.trackGotoByInputName({
              inputName: "<YOUR PRINT TRACK INPUT NAME>",
          });
          
          let trackName = sf.ui.proTools.selectedTrackNames[0];
          
          1. In reply toChad:
            Håkan Lundberg @Hakan_Lundberg
              2025-10-28 05:49:05.047Z

              @Chad
              Unfortunately I can't make it work.
              I just get an error:
              Trigger Metric AB Button failed
              Insert Number must be in the range 1..10 (Trigger
              Metric AB Button: Line 12)

              1. Chad Wahlbrink @Chad2025-10-28 16:42:55.555Z

                Thanks, @Hakan_Lundberg,

                I've updated the script above to add an invalidation to line 6:

                let pluginIndex = sf.ui.proTools.sfx.invalidate().trackGetByName({ name: trackName }).track.insertButtons.findIndex(x => x.value.value.startsWith(pluginName));
                

                This seems to make the script more stable for me.

                Let me know if that update helps or if you are still hitting the same issue.

                Best,
                Chad

                1. In reply toChad:
                  Håkan Lundberg @Hakan_Lundberg
                    2025-10-30 14:23:15.977Z2025-10-31 06:33:28.509Z

                    @Chad
                    It worked brilliantly for a while but suddenly it stopped working again and I'm getting an error:

                    Trigger Metric AB Button Failed
                    Could not find section UI element.
                    AxPtTrackinsertSendButton with expression

                    1. Chad Wahlbrink @Chad2025-10-31 20:16:43.375Z

                      Hi, @Hakan_Lundberg,

                      Thanks for the update! Is this still giving you an issue?

                      It seems to be working on my machine, but I'm happy to explore the issue further with you.

                      Does restating SoundFlow help?

                      Can you please use the “Macro and Script Help” Workflow to create a new post for this? That would allow our team to review the logs and better help you.

                      Best,
                      Chad

                      1. Håkan Lundberg @Hakan_Lundberg
                          2025-10-31 21:45:33.632Z

                          Suddenly your old script started working again and I have absolutely no idea why.

                          I have tried it maybe 30 times, failing every time. When I tried your newest script it also failed - returning error TypeError: Cannot read property 'insertButtons' of null.

                          I then switched back to the old script and - voila!

                          In the old script I replaced the line:

                          sf.ui.proTools.sfx.trackGetByName({ name: trackName }).track.trackInsertToggleShow({ insertNumber: pluginIndex + 1, targetValue: "Disable" })
                          

                          with

                          sf.ui.proTools.sfx.trackGetByName({ name: trackName }).track.trackInsertToggleShow({ insertNumber: pluginIndex + 1, targetValue: "Enable" }) 
                          

                          in order to keep the window open, or is there a better way?

                          There is also a log display every time I use it. Can that be disabled?

                          1. Chad Wahlbrink @Chad2025-10-31 21:49:33.209Z

                            Hi, @Hakan_Lundberg,

                            Let me know if this new version performs any better:

                        • In reply toHakan_Lundberg:
                          Chad Wahlbrink @Chad2025-10-31 21:47:03.679Z2025-11-14 02:42:26.301Z

                          Hi, @Hakan_Lundberg,

                          I think I may have figured out what was going wrong here! If it's left open, there were some scenarios where it would start failing.

                          I've made the script more robust and also added the option to click different buttons in MetricAB like "Playback", "Spectrum", "Correlation", "Stereo Image", "Dynamics", and "Loudness"

                          Like this:

                          clickMetricAB({ trackName: "MIX", buttonName: "Loudness", openClose: "LeaveOpen" });
                          

                          OR set open and close status like this:

                          clickMetricAB({ trackName: "MIX", buttonName: "swapAB", openClose: "OpenAndClose" });
                          

                          Here's the full updated script:

                          
                          let lookUp = {
                              "swapAB": {
                                  x: 144,
                                  y: 491
                              },
                              "Playback": {
                                  x: 347,
                                  y: 120
                              },
                              "Spectrum": {
                                  x: 460,
                                  y: 120
                              },
                              "Correlation": {
                                  x: 563,
                                  y: 120
                              },
                              "Stereo Image": {
                                  x: 665,
                                  y: 120
                              },
                              "Dynamics": {
                                  x: 770,
                                  y: 120
                              },
                              "Loudness": {
                                  x: 878,
                                  y: 120
                              },
                          }
                          
                          /**
                           * clickMetricAB
                           * @param {Object} params - params for function
                           * @param {string} params.trackName - Track Name
                           * @param {'swapAB'|'Playback'|'Spectrum'|'Correlation'|'Stereo Image'|'Dynamics'|'Loudness'} [params.buttonName='swapAB'] - Button To Click
                           * @param {'OpenAndClose'|'LeaveOpen'} [params.openClose='OpenAndClose'] - Open and Close Behavior
                           */
                          function clickMetricAB({ trackName, buttonName = 'swapAB', openClose = 'OpenAndClose' }) {
                          
                              const PLUGIN_NAME = 'ADPTR MetricAB';
                          
                              if (!trackName) {
                                  throw new Error('trackName is required');
                              }
                          
                              var pluginView = sf.ui.proTools.sfx.windows.whoseTitle.is(`Plug-in: ${PLUGIN_NAME}`).first;
                          
                              // Show Plugin
                              let pluginIndex = sf.ui.proTools.sfx.invalidate().trackGetByName({ name: trackName }).track.insertButtons.findIndex(x => x.value.value.startsWith(PLUGIN_NAME));
                          
                              if (pluginView.exists) {
                                  sf.ui.proTools.appActivate();
                                  pluginView.elementRaise();
                              } else {
                                  // Show Insert
                                  sf.ui.proTools.sfx.trackGetByName({ name: trackName }).track.trackInsertToggleShow({ insertNumber: pluginIndex + 1, targetValue: "Enable" });
                                  // Wait for Plugin Window
                                  pluginView.elementWaitFor();
                          
                                  sf.ui.proTools.sfx.windows.whoseTitle.is(`Plug-in: ${PLUGIN_NAME}`).first.buttons.whoseTitle.is("Target button").first.elementClick();
                              }
                          
                              // Get the plugin window's frame for the relative position data. 
                              var pluginViewFrame = pluginView.frame;
                          
                              // Get the ui element at the global scope based on the position of the plugin windows frame. 
                              var pluginElement = sf.ui.root.getElementAtPosition(pluginViewFrame.x + lookUp[buttonName].x, pluginViewFrame.y + lookUp[buttonName].y);
                          
                              pluginElement.elementWaitFor();
                          
                              // Click
                              pluginElement.elementClick();
                          
                              // if (buttonName === "swapAB") {
                              //     // Log A or B
                              //     pluginElement.value.value === "Off" ? log('A') : log('B');
                              // }
                          
                              if (openClose === "OpenAndClose") {
                                  // Hide Insert
                                  sf.ui.proTools.sfx.trackGetByName({ name: trackName }).track.trackInsertToggleShow({ insertNumber: pluginIndex + 1, targetValue: "Disable" })
                              }
                          }
                          
                          clickMetricAB({ trackName: "MIX", buttonName: "swapAB", openClose: "LeaveOpen" });
                          
                          
                          1. Chad Wahlbrink @Chad2025-10-31 21:50:05.316Z

                            I've commented out the logging bit on this one.

                            1. Håkan Lundberg @Hakan_Lundberg
                                2025-10-31 22:09:07.743Z2025-10-31 22:20:10.830Z

                                Thank you sooo much!

                                It really works now and having the different views accessible is simply a blessing.
                                An amazing solution that will make reference mixing a breeze for me.

                                Can't thank you enough! :-)

                              • In reply toChad:
                                Håkan Lundberg @Hakan_Lundberg
                                  2025-11-06 13:01:07.431Z

                                  Hi @Chad
                                  unfortunately it mysteriously stopped working again.

                                  I get the error:
                                  Metric AB Swap AB failed
                                  Couldn't locate the UI for
                                  AxPtTrackInsertSendButton with expression

                                  Any idea what is going on?

                                  1. Chad Wahlbrink @Chad2025-11-06 15:10:04.221Z

                                    Hi, @Hakan_Lundberg,

                                    Very strange. When that happens, does restarting SoundFlow fix the issue?

                                    I think I would need you to send a report using the “Macro and Script Help” Workflow so I can check the logs.

                                    A screen recording shared via a Dropbox/Google Drive link could also help illuminate what is going on. But I would start by filing a report with the “Macro and Script Help” Workflow so your recent log files are available to our team for closer review.

                                    1. Håkan Lundberg @Hakan_Lundberg
                                        2025-11-12 14:44:05.737Z

                                        Update:

                                        I noticed something significant.

                                        If I open the plugin myself and leave it open - then the button works!

                                        I don't know if something changed in the script but ultimately I would like the script to open the plugin with the target button disabled (if not open).

                                        It's not a biggie, I can of course open the plugin myself if the script isn't able to, but as I recall, the script opened the plugin with target mode disabled - but suddenly didn't for some reason.

                                        1. Chad Wahlbrink @Chad2025-11-14 02:43:11.906Z

                                          Hi, @Hakan_Lundberg,

                                          I updated the script above:
                                          Toggle A/B Switch in ADPTR MetricAB #post-14

                                          I added line 62 to disable the target button when the script opens the plugin:

                                          sf.ui.proTools.sfx.windows.whoseTitle.is(`Plug-in: ${PLUGIN_NAME}`).first.buttons.whoseTitle.is("Target button").first.elementClick();
                                          
                          2. A
                            In reply toHakan_Lundberg:
                            Adam Lilienfeldt @Adam_Lilienfeldt
                              2025-11-15 10:04:03.733Z

                              Hi all!
                              I had a small issue with the plugin not closing after switching between A and B, even when the parameter was set to OpenAndClose. I was able to fix it by changing line 83 to:

                              sf.ui.proTools.sfx.trackGetByName({ name: trackName }).track.trackInsertToggleShow({ insertNumber: pluginIndex + 1, /*targetValue: "Disable" */ }).window.windowClose();
                              
                              

                              I've also added the functionality that if the plugin is already open when the script is run, it will keep it open after switching A and B. If it's not already open, it will close it once done switching.

                              Here's my updated script in case anyone will find it useful:

                              let lookUp = {
                                  "swapAB": {
                                      x: 144,
                                      y: 491
                                  },
                                  "Playback": {
                                      x: 347,
                                      y: 120
                                  },
                                  "Spectrum": {
                                      x: 460,
                                      y: 120
                                  },
                                  "Correlation": {
                                      x: 563,
                                      y: 120
                                  },
                                  "Stereo Image": {
                                      x: 665,
                                      y: 120
                                  },
                                  "Dynamics": {
                                      x: 770,
                                      y: 120
                                  },
                                  "Loudness": {
                                      x: 878,
                                      y: 120
                                  },
                              }
                              
                              /**
                               * clickMetricAB
                               * @param {Object} params - params for function
                               * @param {string} params.trackName - Track Name
                               * @param {'swapAB'|'Playback'|'Spectrum'|'Correlation'|'Stereo Image'|'Dynamics'|'Loudness'} [params.buttonName='swapAB'] - Button To Click
                               * @param {'OpenAndClose'|'LeaveOpen'} [params.openClose='OpenAndClose'] - Open and Close Behavior
                               */
                              function clickMetricAB({ trackName, buttonName = 'swapAB', openClose = 'OpenAndClose' }) {
                              
                                  const PLUGIN_NAME = 'ADPTR MetricAB';
                              
                                  if (!trackName) {
                                      throw new Error('trackName is required');
                                  }
                              
                                  var pluginView = sf.ui.proTools.sfx.windows.whoseTitle.is(`Plug-in: ${PLUGIN_NAME}`).first;
                              
                                  var isPluginOpen = pluginView.exists;
                                  
                              
                                  // Show Plugin
                                  let pluginIndex = sf.ui.proTools.sfx.invalidate().trackGetByName({ name: trackName }).track.insertButtons.findIndex(x => x.value.value.startsWith(PLUGIN_NAME));
                              
                                  if (pluginView.exists) {
                                      sf.ui.proTools.appActivate();
                                      pluginView.elementRaise();
                                  } else {
                                      // Show Insert
                                      sf.ui.proTools.sfx.trackGetByName({ name: trackName }).track.trackInsertToggleShow({ insertNumber: pluginIndex + 1, targetValue: "Enable" });
                                      // Wait for Plugin Window
                                      pluginView.elementWaitFor();
                              
                                      sf.ui.proTools.sfx.windows.whoseTitle.is(`Plug-in: ${PLUGIN_NAME}`).first.buttons.whoseTitle.is("Target button").first.elementClick();
                                  }
                              
                                  // Get the plugin window's frame for the relative position data. 
                                  var pluginViewFrame = pluginView.frame;
                              
                                  // Get the ui element at the global scope based on the position of the plugin windows frame. 
                                  var pluginElement = sf.ui.root.getElementAtPosition(pluginViewFrame.x + lookUp[buttonName].x, pluginViewFrame.y + lookUp[buttonName].y);
                              
                                  pluginElement.elementWaitFor();
                              
                                  // Click
                                  pluginElement.elementClick();
                              
                                  if (buttonName === "swapAB") {
                                      //     // Log A or B
                                      pluginElement.value.value === "Off" ? log('A') : log('B');
                                  }
                              
                                  if (openClose === "OpenAndClose" && !isPluginOpen) {
                                      // Hide Insert
                                      sf.ui.proTools.sfx.trackGetByName({ name: trackName }).track.trackInsertToggleShow({ insertNumber: pluginIndex + 1, /*targetValue: "Disable" */ }).window.windowClose();
                                  }
                              }
                              
                              clickMetricAB({ trackName: "Master out", buttonName: "swapAB", openClose: "OpenAndClose" });