Toggle A/B Switch in ADPTR MetricAB
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!
Linked from:
Chad Wahlbrink @Chad2025-10-23 18:22:47.070Z2025-10-31 21:46:14.110ZHi, @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();
Ben Rubin @Ben_RubinThanks 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];- In reply toChad⬆:
Håkan Lundberg @Hakan_Lundberg@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)
Chad Wahlbrink @Chad2025-10-28 16:42:55.555ZThanks, @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- In reply toChad⬆:
Håkan Lundberg @Hakan_Lundberg@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
Chad Wahlbrink @Chad2025-10-31 20:16:43.375ZHi, @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
Håkan Lundberg @Hakan_LundbergSuddenly 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?
Chad Wahlbrink @Chad2025-10-31 21:49:33.209ZHi, @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.301ZHi, @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" });
Chad Wahlbrink @Chad2025-10-31 21:50:05.316ZI've commented out the logging bit on this one.
Håkan Lundberg @Hakan_LundbergThank 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_LundbergHi @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?
Chad Wahlbrink @Chad2025-11-06 15:10:04.221ZHi, @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.
Håkan Lundberg @Hakan_LundbergUpdate:
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.
Chad Wahlbrink @Chad2025-11-14 02:43:11.906ZHi, @Hakan_Lundberg,
I updated the script above:
Toggle A/B Switch in ADPTR MetricAB #post-14I 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();
- AIn reply toHakan_Lundberg⬆:Adam Lilienfeldt @Adam_Lilienfeldt
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" });