Title
Bounce Mix - Select Multi Mix Sources
What do you expect to happen when you run the script/macro?
This script opens up det Bounce window, ads 3 mix sources and selects the physical outputs: DX, FX and MX BED. It all works great.. But
Are you seeing an error?
What happens when you run this script?
If multible mix sources are selected when opening the window the script will just add more to the existing count.
Is there a way to detect the count? remove the sources in a smart way so that you dont get an error if the count is unkoown?
How were you running this script?
I clicked the "Run Script" or "Run Macro" button in SoundFlow
How important is this issue to you?
3
Details
{
"inputExpected": "This script opens up det Bounce window, ads 3 mix sources and selects the physical outputs: DX, FX and MX BED. \nIt all works great.. But\n",
"inputIsError": false,
"inputWhatHappens": "If multible mix sources are selected when opening the window the script will just add more to the existing count. \n\nIs there a way to detect the count? remove the sources in a smart way so that you dont get an error if the count is unkoown?",
"inputHowRun": {
"key": "-MpfwYA4I6GGlXgvp5j1",
"title": "I clicked the \"Run Script\" or \"Run Macro\" button in SoundFlow"
},
"inputImportance": 3,
"inputTitle": "Bounce Mix - Select Multi Mix Sources"
}
Source
sf.ui.proTools.appActivateMainWindow();
sf.ui.proTools.getMenuItem('File', 'Bounce Mix...').elementClick();
//Wait for 'Bounce Mix' window
let bounceWin = sf.ui.proTools.dialogWaitForManual({
dialogTitle: 'Bounce Mix'
}).dialog;
// CLICK TO ADD SOOURCES
const mixSourceButtonTitle = 'DX BED (7.1.2)';
const mixSourcePath = ["physical output", "DX BED (7.1.2)"];
const bounceDlg = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first;
//Mix Source - Popup Menu
if (bounceDlg.popupButtons.allItems[1].value.invalidate().value !== mixSourceButtonTitle) {
bounceDlg.popupButtons.allItems[1].popupMenuSelect({
menuSelector: items => items.filter(item =>
item.path[0].endsWith(mixSourcePath[0]) &&
item.path[1].endsWith(mixSourcePath[1]))[0]
});
}
sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first.buttons.whoseTitle.is("Add row").first.mouseClickElement({
anchor: "TopLeft",
});
const mixSourceButtonTitle = 'FX BED (7.1.2)';
const mixSourcePath = ["physical output", "FX BED (7.1.2)"];
const bounceDlg = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first;
//Mix Source - Popup Menu
if (bounceDlg.popupButtons.allItems[2].value.invalidate().value !== mixSourceButtonTitle) {
bounceDlg.popupButtons.allItems[2].popupMenuSelect({
menuSelector: items => items.filter(item =>
item.path[0].endsWith(mixSourcePath[0]) &&
item.path[1].endsWith(mixSourcePath[1]))[0]
});
}
sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first.buttons.whoseTitle.is("Add row").first.mouseClickElement({
anchor: "TopLeft",
});
const mixSourceButtonTitle = 'MX BED (7.1.2)';
const mixSourcePath = ["physical output", "MX BED (7.1.2)"];
const bounceDlg = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first;
//Mix Source - Popup Menu
if (bounceDlg.popupButtons.allItems[3].value.invalidate().value !== mixSourceButtonTitle) {
bounceDlg.popupButtons.allItems[3].popupMenuSelect({
menuSelector: items => items.filter(item =>
item.path[0].endsWith(mixSourcePath[0]) &&
item.path[1].endsWith(mixSourcePath[1]))[0]
});
}
Links
User UID: QO1zQljHOKXAG1PpilXlugc9h0d2
Feedback Key: sffeedback:QO1zQljHOKXAG1PpilXlugc9h0d2:-NQVxXGIKLzqArBErbhx
- NNicolai Linck @NLinck
Anyone?
Chris Shaw @Chris_Shaw2023-03-30 19:08:21.389ZI think the easiest way to do this would be to store the bounce window settings in one of the 1-5 preset buttons or or save the settings and select them from the top settings menu. Then just write a short script to select them
Chris Shaw @Chris_Shaw2023-03-30 19:10:51.731Zor are you trying to add you bounce sources in addition to what may already be there?
In reply toNLinck⬆:Chris Shaw @Chris_Shaw2023-03-30 21:00:22.171Z2023-04-01 01:22:49.397ZTry this:
This will add/delete the number of sources needed (determined by themixSourcesarray) then set all of the source popups.To use different sources just add or remove the paths in the
mixSourcesarray.
//CS////Paths to desired sources const mixSources = [ ["physical output", "DX BED (7.1.2)"], ["physical output", "FX BED (7.1.2)"], ["physical output", "MX BED (7.1.2)"], ]; sf.ui.proTools.appActivateMainWindow(); // Define Bounce Window const bounceWindow = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first // Open bounce Window sf.ui.proTools.getMenuItem('File', 'Bounce Mix...').elementClick(); // Wait for Bounce Window bounceWindow.elementWaitFor() // Count the number of move row buttons // This will determine how many sources exist in the bounce window let existingSources = bounceWindow.buttons.whoseTitle.is("Move row").allItems.length; // If no Move Row buttons exist then there is only one existing source if (existingSources == 0) existingSources = 1 //Determine which button to press to add or remove sources (rows) const addOrRemove = existingSources < mixSources.length ? "Add " : "Remove " // Define button to press const addOrRemoveButton = bounceWindow.buttons.whoseTitle.is(addOrRemove + "row").first // Determine how many sources to add or delete // If number is negative (remove rows) then ensure resulting number is positive via Math.abs const addOrDeleteCount = Math.abs(mixSources.length - existingSources) // Add or remove rows addOrRemoveButton.mouseClickElement({ clickCount: addOrDeleteCount }); // Set bounce sources mixSources.forEach((path, index) => { bounceWindow.popupButtons.allItems[index + 1].popupMenuSelect({ menuPath: path }); })
Chris Shaw @Chris_Shaw2023-04-01 01:29:50.332ZIf this doesn't work then you'll probably have to use the menu filtering you have in your script. If that's the case, replace line 44 in my script with :
menuSelector: items => items.filter(item => item.path[0].endsWith(mixSourcePath[0]) && item.path[1].endsWith(mixSourcePath[1]))[0]
- NIn reply toNLinck⬆:Nicolai Linck @NLinck
Hi Chris. I have been very busy so i haven't had time before now to try out your code. It works like a charm. Thank you!
- NNicolai Linck @NLinck
Hi again Chris.
Im having issues with line 48. The script is working well for the most parts, but its throwing an error saying:
Could not open popup menu (Bounce Video test Copy: Line 48)
Popup menu was not found
Popup window was not found after waiting 2000 ms
Sometimes its stopping on the first Mix Source, sometimes almost the last.The code is here:
//Paths to desired sources const mixSources = [ ["bus", "2.0 Master (Stereo)"], ["bus", "5.1 Master (5.1)"], ["bus", "Dia Stem 2.0 (Stereo)"], ["bus", "Dia Stem 5.1 (5.1)"], ["bus", "FX Stem 2.0 (Stereo)"], ["bus", "FX Stem 5.1 (5.1)"], ["bus", "Music Stem 2.0 (Stereo)"], ["bus", "Music Stem 5.1 (5.1)"], ]; sf.ui.proTools.appActivateMainWindow(); // Define Bounce Window const bounceWindow = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first // Open bounce Window sf.ui.proTools.getMenuItem('File', 'Bounce Mix...').elementClick(); // Wait for Bounce Window bounceWindow.elementWaitFor() // Count the number of move row buttons // This will determine how many sources exist in the bounce window let existingSources = bounceWindow.buttons.whoseTitle.is("Move row").allItems.length; // If no Move Row buttons exist then there is only one existing source if (existingSources == 0) existingSources = 1 //Determine which button to press to add or remove sources (rows) const addOrRemove = existingSources < mixSources.length ? "Add " : "Remove " // Define button to press const addOrRemoveButton = bounceWindow.buttons.whoseTitle.is(addOrRemove + "row").first // Determine how many sources to add or delete // If number is negative (remove rows) then ensure resulting number is positive via Math.abs const addOrDeleteCount = Math.abs(mixSources.length - existingSources) // Add or remove rows addOrRemoveButton.mouseClickElement({ clickCount: addOrDeleteCount }); // Set bounce sources mixSources.forEach((path, index) => { bounceWindow.popupButtons.allItems[index + 1].popupMenuSelect({ menuPath: path }); })Thanks in advance!!
- NNicolai Linck @NLinck
Hi @Chris_Shaw. I know this is getting a bit old, but would be awesome to getting the script to work. It seems to break at the Set Bounce Sources in the end of the code.
- NNicolai Linck @NLinck
Im getting this error:
11.12.2025 11:50:18.56 [Backend]: Logging error in action (01) WaitForPopupMenuAction: Popup window was not found after waiting 2000 ms
Logging error in action (01) OpenPopupMenuFromElementAction: Popup menu was not found
Logging error in action (01) PopupMenuSelectAction: Could not open popup menu11.12.2025 11:50:18.57 [Backend]: !! Command Error: Add 5.1 Master [user:default:cmj095xcl0003o37zj1lkg42x]:
Could not open popup menu (Add 5.1 Master: Line 50)
Popup menu was not found
Popup window was not found after waiting 2000 ms- NNicolai Linck @NLinck
My script is this:
sf.ui.proTools.appActivateMainWindow(); //Paths to desired sources const mixSources = [ ["bus", "2.0 Master (Stereo)"], ["bus", "5.1 Master (5.1)"], ["bus", "DX Stem 2.0 (Stereo)"], ["bus", "FX Stem 2.0 (Stereo)"], ["bus", "MX Stem 2.0 (Stereo)"], ["bus", "DX Stem 5.1 (5.1)"], ["bus", "FX Stem 5.1 (5.1)"], ["bus", "MX Stem 5.1 (5.1)"], ]; sf.ui.proTools.appActivateMainWindow(); // Define Bounce Window const bounceWindow = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first // Open bounce Window sf.ui.proTools.getMenuItem('File', 'Bounce Mix...').elementClick(); // Wait for Bounce Window bounceWindow.elementWaitFor() // Count the number of move row buttons // This will determine how many sources exist in the bounce window let existingSources = bounceWindow.buttons.whoseTitle.is("Move row").allItems.length; // If no Move Row buttons exist then there is only one existing source if (existingSources == 0) existingSources = 1 //Determine which button to press to add or remove sources (rows) const addOrRemove = existingSources < mixSources.length ? "Add " : "Remove " // Define button to press const addOrRemoveButton = bounceWindow.buttons.whoseTitle.is(addOrRemove + "row").first // Determine how many sources to add or delete // If number is negative (remove rows) then ensure resulting number is positive via Math.abs const addOrDeleteCount = Math.abs(mixSources.length - existingSources) // Add or remove rows addOrRemoveButton.mouseClickElement({ clickCount: addOrDeleteCount }); // Set bounce sources mixSources.forEach((path, index) => { bounceWindow.popupButtons.allItems[index + 1].popupMenuSelect({ menuPath: path }); })- NNicolai Linck @NLinck
I also tried with the other code you posted but that's not working as well:
menuSelector: items => items.filter(item => item.path[0].endsWith(mixSourcePath[0]) && item.path[1].endsWith(mixSourcePath[1]))[0]
Chris Shaw @Chris_Shaw2025-12-15 20:39:00.038Z@NLinck,
Which version of Pro Tools and SF are you using?- NNicolai Linck @NLinck
@Chris_Shaw im on PT 25.10.0 and SF 6.0.9
Chris Shaw @Chris_Shaw2025-12-16 21:59:28.177ZGive this a try.
I'm running it on PT 2025.12 which was just released.if (sf.ui.useSfx && !globalState.OPP_disableSfx) sf.ui.useSfx(); sf.ui.proTools.appActivateMainWindow(); // Paths to desired sources const mixSources = [ ["bus", "2.0 Master (Stereo)"], ["bus", "5.1 Master (5.1)"], ["bus", "DX Stem 2.0 (Stereo)"], ["bus", "FX Stem 2.0 (Stereo)"], ["bus", "MX Stem 2.0 (Stereo)"], ["bus", "DX Stem 5.1 (5.1)"], ["bus", "FX Stem 5.1 (5.1)"], ["bus", "MX Stem 5.1 (5.1)"], ]; // Open bounce Window sf.ui.proTools.getMenuItem('File', 'Bounce Mix...').elementClick(); // Define Bounce Window const bounceWindow = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first // Wait for Bounce Window bounceWindow.elementWaitFor() bounceWindow.invalidate() // Count the number of move row buttons // This will determine how many sources exist in the bounce window let numberOdMoveRowButtons = bounceWindow.buttons.whoseTitle.is("Move row").allItems.length; // If no Move Row buttons exist then there is only one existing source let existingSources = numberOdMoveRowButtons == 0 ? 1 : numberOdMoveRowButtons //Determine which button to press to add or remove sources (rows) const addOrRemove = existingSources < mixSources.length ? "Add " : "Remove " // Define button to press const addOrRemoveButton = bounceWindow.buttons.whoseTitle.is(addOrRemove + "row").first // Determine how many sources to add or delete // If number is negative (remove rows) then ensure resulting number is positive via Math.abs const addOrDeleteCount = Math.abs(mixSources.length - existingSources) // Add or remove rows if (addOrDeleteCount > 0) { addOrRemoveButton.mouseClickElement({ clickCount: addOrDeleteCount, method: "MouseSimulation" }); } // Set bounce sources mixSources.forEach((sourcePath, index) => { bounceWindow.popupButtons.allItems[index + 1].invalidate(); // log(sourcePath) if (bounceWindow.popupButtons.allItems[index + 1].value.invalidate().value == sourcePath[1]) { return }; bounceWindow.popupButtons.allItems[index + 1].popupMenuSelect({ // menuPath: sourcePath, menuSelector: items => items.find(i => i.path[1] && i.path[1].trim().includes(sourcePath[1]) ), }); sf.waitFor({ callback: () => bounceWindow.popupButtons.allItems[index + 1].value.value = sourcePath[1] }) bounceWindow.windowFocusClick() });- NNicolai Linck @NLinck
I just updated as well and this just works so fine and fast! Whats the difference here?
Chris Shaw @Chris_Shaw2025-12-17 17:21:30.925ZThe script takes advantage of SF6's SFX integration with Pro Tools. The first line in the script turns on these features. With SFX, PT2025.10+ no longer needs to open popup menus to change their values (as well as other features).
More info can be found here:
https://soundflow.org/docs/scripting/adapting-scripts-for-sfx- NNicolai Linck @NLinck
It’s working really well. I’ve run it many times now, trying to make the script fail, but it’s rock solid!
I’d like to add some more bounce features to my surface. Is it possible to create a script that only adds a single mix source, for example “2.0 Master”? If other mix sources are present, it shouldn’t affect them, only add the new source.
As a reverse function, would it also be possible to have a script that only removes the mix source “2.0 Master”?
All the best, and thanks for your help!
Chris Shaw @Chris_Shaw2025-12-18 14:59:34.332ZJust copy the script and remove or add sources to the mix sources list.
The script will automatically adjust the bounce window sources.- NNicolai Linck @NLinck
Yes, I did that, and it’s really easy to set up.
However, sometimes I need to add an extra source that isn’t on the list, such as “Additional_DX_Stem” or “FO_Stem”, for projects that require them. It would be really nice to have the ability to simply add these sources instead of having to build scripts for every possible mix source.Also, if I create a copy with only “Additional_DX_Stem”, it removes the mix sources that are already present instead of adding it.
And if it’s possible, being able to remove just a specific mix source without affecting the others would be totally awesome!
All the best and thanks for all the help!!
Chris Shaw @Chris_Shaw2025-12-18 17:33:16.332Z2025-12-18 17:59:42.744ZHere's another version that should do this.
Populate themixPathsarray with all of the paths that are possibly needed.
Edit thedefaultPathsarray with the paths that are to be used as defaults (Just duplicate thesourcePathsand comment off the paths that are not defaults).
You can then select/deselect whatever paths to be used for the bounce with a selection window://Paths to desired sources const mixPaths = [ ["bus", "2.0 Master (Stereo)"], ["bus", "5.1 Master (5.1)"], ["bus", "DX Stem 2.0 (Stereo)"], ["bus", "FX Stem 2.0 (Stereo)"], ["bus", "MX Stem 2.0 (Stereo)"], ["bus", "DX Stem 5.1 (5.1)"], ["bus", "FX Stem 5.1 (5.1)"], ["bus", "MX Stem 5.1 (5.1)"], ]; const defaultPaths = [ ["bus", "2.0 Master (Stereo)"], ["bus", "5.1 Master (5.1)"], ["bus", "DX Stem 2.0 (Stereo)"], ["bus", "FX Stem 2.0 (Stereo)"], ["bus", "MX Stem 2.0 (Stereo)"], // ["bus", "DX Stem 5.1 (5.1)"], // ["bus", "FX Stem 5.1 (5.1)"], ] // ============================================ // M A I N // ============================================ if (sf.ui.useSfx && !globalState.OPP_disableSfx) sf.ui.useSfx(); sf.ui.proTools.appActivateMainWindow(); const mixSources = sf.interaction.selectFromList({ title: "Select Source", items: mixPaths.map(p => p.join(", ")), prompt: "Select Mix sources:\n\ncmd-click to select mutiple sources\nshift-click to select a range of sources", allowMultipleSelections: true, defaultItems: defaultPaths.map(p => p.join(", ")) }).list.map(s => s.split(", ")) sf.ui.proTools.appActivateMainWindow(); // Open bounce Window sf.ui.proTools.getMenuItem('File', 'Bounce Mix...').elementClick(); // Define Bounce Window const bounceWindow = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first // Wait for Bounce Window bounceWindow.elementWaitFor() bounceWindow.invalidate() // Count the number of move row buttons // This will determine how many sources exist in the bounce window let numberOdMoveRowButtons = bounceWindow.buttons.whoseTitle.is("Move row").allItems.length; // If no Move Row buttons exist then there is only one existing source let existingSources = numberOdMoveRowButtons == 0 ? 1 : numberOdMoveRowButtons //Determine which button to press to add or remove sources (rows) const addOrRemove = existingSources < mixSources.length ? "Add " : "Remove " // Define button to press const addOrRemoveButton = bounceWindow.buttons.whoseTitle.is(addOrRemove + "row").first // Determine how many sources to add or delete // If number is negative (remove rows) then ensure resulting number is positive via Math.abs const addOrDeleteCount = Math.abs(mixSources.length - existingSources) // Add or remove rows if (addOrDeleteCount > 0) { addOrRemoveButton.mouseClickElement({ clickCount: addOrDeleteCount, method: "MouseSimulation" }); } // Set bounce sources mixSources.forEach((sourcePath, index) => { bounceWindow.popupButtons.allItems[index + 1].invalidate(); // log(sourcePath) if (bounceWindow.popupButtons.allItems[index + 1].value.invalidate().value == sourcePath[1]) { return }; bounceWindow.popupButtons.allItems[index + 1].popupMenuSelect({ // menuPath: sourcePath, menuSelector: items => items.find(i => i.path[1] && i.path[1].trim().includes(sourcePath[1]) ), }); sf.waitFor({ callback: () => bounceWindow.popupButtons.allItems[index + 1].value.value = sourcePath[1] }) bounceWindow.windowFocusClick() });