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

Bounce Mix - Select Multi Mix Sources

By Nicolai Linck @NLinck
    2023-03-14 16:31:13.020Z

    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

    Feedback ZIP

    Solved in post #15, click to view
    • 20 replies

    There are 20 replies. Estimated reading time: 23 minutes

    1. N
      Nicolai Linck @NLinck
        2023-03-25 08:19:19.132Z

        Anyone?

        1. I 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

          1. or are you trying to add you bounce sources in addition to what may already be there?

        2. In reply toNLinck:
          Chris Shaw @Chris_Shaw2023-03-30 21:00:22.171Z2023-04-01 01:22:49.397Z

          Try this:
          This will add/delete the number of sources needed (determined by the mixSources array) then set all of the source popups.

          To use different sources just add or remove the paths in the mixSources array.
          //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
              });
          })
          
          1. If 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]
            
          2. N
            In reply toNLinck:
            Nicolai Linck @NLinck
              2023-04-24 14:31:21.540Z

              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!

              1. NNicolai Linck @NLinck
                  2025-04-28 09:01:24.811Z

                  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!!

                  1. NNicolai Linck @NLinck
                      2025-12-11 10:55:16.507Z

                      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.

                      1. NNicolai Linck @NLinck
                          2025-12-11 10:55:38.855Z

                          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 menu

                          11.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

                          1. NNicolai Linck @NLinck
                              2025-12-11 10:56:31.514Z

                              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
                                  });
                              })
                              
                              
                              1. NNicolai Linck @NLinck
                                  2025-12-11 11:01:51.915Z

                                  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]
                                  
                                  1. @NLinck,
                                    Which version of Pro Tools and SF are you using?

                                    1. NNicolai Linck @NLinck
                                        2025-12-16 08:17:24.496Z

                                        @Chris_Shaw im on PT 25.10.0 and SF 6.0.9

                                        1. Give 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()
                                          });
                                          
                                          Reply1 LikeSolution
                                          1. NNicolai Linck @NLinck
                                              2025-12-17 09:03:04.781Z

                                              I just updated as well and this just works so fine and fast! Whats the difference here?

                                              1. The 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

                                                1. NNicolai Linck @NLinck
                                                    2025-12-18 08:34:22.374Z

                                                    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!

                                                    1. Just copy the script and remove or add sources to the mix sources list.
                                                      The script will automatically adjust the bounce window sources.

                                                      1. NNicolai Linck @NLinck
                                                          2025-12-18 16:37:56.223Z

                                                          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!!

                                                          1. Chris Shaw @Chris_Shaw2025-12-18 17:33:16.332Z2025-12-18 17:59:42.744Z

                                                            Here's another version that should do this.
                                                            Populate the mixPaths array with all of the paths that are possibly needed.
                                                            Edit the defaultPaths array with the paths that are to be used as defaults (Just duplicate the sourcePaths and 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()
                                                            });