No internet connection
  1. Home
  2. How to

copying and pasting various info / learning java

By Joseph Faison @Joseph_Faison
    2023-07-01 15:38:49.910Z

    Hi, so I've been using Soundflow via macros and am now trying to learn a little javascript for some session prep tasks I can't find macros for. One thing that I'm struggling with is figuring out ways to copy and paste various things such as a track's output to a newly created folder, or to copy a selected track's name and then rename a newly created track with that name + a suffix.

    Ultimately what I'm trying to write a script for is something that will:
    Copy the name (X) and output (Y) of a selected track
    create two new aux sends.
    put those auxes into a new routing folder
    name the folder (X) + "FX"
    route the folder to original output (Y)

    Thanks!
    Joseph

    • 9 replies
    1. @Joseph_Faison, welcome to SoundFlow, and congratulations on starting your scripting journey!

      I can get you started by helping you with your first request.

      This is how you get a selected track's name and output and store it in a variable:

      const selectedTrack = sf.ui.proTools.selectedTrack;
      const selectedTrackName = selectedTrack.normalizedTrackName;
      const selectedTrackOutputName = selectedTrack.outputPathButton.value.value;
      

      Now you can use those variables later in your script, for example, when you get to the point of naming the Routing Folder, you can do this:

      sf.ui.proTools.windows.whoseTitle.is("Move To New Folder").first
          .textFields.whoseTitle.is("Track name").first
          .elementSetTextFieldWithAreaValue({
              value: selectedTrackName + " FX"
          });
      

      Hopefully, this gets you going to the next step.

      1. JJoseph Faison @Joseph_Faison
          2023-07-02 20:33:27.823Z

          thank you!

        • F
          In reply toJoseph_Faison:
          Forrester Savell @Forrester_Savell
            2023-07-02 23:28:51.947Z

            Hi Joseph

            I've had a go at creating a script for you. Let me know if this works for you and does what you're after?

            sf.ui.proTools.appActivateMainWindow()
            sf.ui.proTools.invalidate()
            
            //Get selected Tracks
            const selectedTracks = sf.ui.proTools.selectedTrackNames
            
            let firstTrack = selectedTracks[0];
            let folderName = firstTrack + " FX";
            
            // define track output selector
            const audioOutput = sf.ui.proTools.selectedTrack.groups.whoseTitle.is("Audio IO")
            const trackOutput = audioOutput.first.popupButtons.allItems[1]
            
            // get ouput of first track
            var outputPaths = trackOutput.popupMenuFetchAllItems().menuItems
            const outputPath = outputPaths.map((mi, i) => ({ menuItem: mi, index: i })).filter(m => m.menuItem.element.isMenuChecked)[0].menuItem.path;
            
            sf.ui.proTools.appActivateMainWindow();
            
            function createAuxes() {
                sf.ui.proTools.menuClick({
                    menuPath: ['Track', 'New...']
                });
            
                const newTrackWin = sf.ui.proTools.windows.whoseTitle.is("New Tracks").first
            
                newTrackWin.elementWaitFor();
            
                const numberField = newTrackWin.textFields.whoseTitle.is('Number of new tracks').first;
            
                const numberOfTracks = '2';
            
                if (numberField.value.invalidate().value.trim() !== numberOfTracks) {
                    numberField.elementClick();
            
                    sf.keyboard.type({ text: numberOfTracks });
            
                    var i = 0;
                    while (numberField.value.invalidate().value !== numberOfTracks) {
                        sf.wait({ intervalMs: 30 });
                    };
                }
            
                newTrackWin.popupButtons.whoseDescription.is("Track format").first.popupMenuSelect({
                    menuPath: ["Stereo"]
                });
            
                newTrackWin.popupButtons.whoseDescription.is("Track type").first.popupMenuSelect({
                    menuPath: ["Aux Input"]
                });
            
                newTrackWin.textFields.whoseTitle.is("Track Name").first.elementClick();
            
                newTrackWin.textFields.whoseTitle.is("Track Name").first.elementSetTextFieldWithAreaValue({
                    value: 'FX Aux',
                });
                newTrackWin.buttons.whoseTitle.is("Create").first.elementClick();
            
            
            };
            
            function createFolder() {
                sf.ui.proTools.menuClick({
                    menuPath: ["Track", "Move to New Folder..."],
                });
            
                const newFolderWin = sf.ui.proTools.windows.whoseTitle.is("Move To New Folder").first
            
                newFolderWin.elementWaitFor()
            
                newFolderWin.popupButtons.first.popupMenuSelect({
                    menuPath: ["Routing Folder"],
                });
            
                newFolderWin.checkBoxes.whoseTitle.is("Route Tracks to New Folder").first.checkboxSet({
                    targetValue: "Enable"
                });
            
                newFolderWin.textFields.whoseTitle.is("Track Name").first.elementSetTextFieldWithAreaValue({
                    value: folderName,
                });
                newFolderWin.popupButtons.whoseDescription.is("Track format").first.popupMenuSelect({
                    menuPath: ["Stereo"]
                });
                newFolderWin.buttons.whoseTitle.is("Create").first.elementClick();
            };
            
            createAuxes();
            
            createFolder();
            
            sf.ui.proTools.appActivateMainWindow();
            
            sf.ui.proTools.trackSelectByName({ names: [folderName] });
            
            sf.ui.proTools.selectedTrack.groups.whoseTitle.is("Audio IO").first.
                popupButtons.whoseTitle.contains("Audio Output").first.popupMenuSelect({
                    menuPath: outputPath,
                });
            
            1. FForrester Savell @Forrester_Savell
                2023-07-03 00:21:26.498Z

                Hey @raphaelsepulveda
                Would you be able to critique my script? I can see we've used different ways to store track names and outputs. Is there anywhere else I can trim things down and make them more efficient?
                I have noticed that my script seems to lag between the createFolder function and the folder selection.

                1. JJoseph Faison @Joseph_Faison
                    2023-07-03 01:01:22.729Z

                    Wow thanks Forrester! This is working well. The way you are defining the output seems to be working better, I'm hitting a snag doing it as

                    selectedTrack.outputPathButton.value.value
                    

                    as when I'm routing to vocal(stereo) it logs it as vox (which is how pro tools displays). Not having that issue here.

                    1. @Joseph_Faison, @Forrester_Savell really came through for you. Love to see it!

                      Yes, if you look a the reply I left for Forrester, we took different approaches on how to manage the output assignments. I usually grab the name of the output first, in the most un-intrusive way, and figure out the path later when assigning the output to the track.

                      Here's what I mean, this is just the main function of the refactored version I left for Forrester but with output assignment being managed the way I described earlier:

                      function main() {
                          sf.ui.proTools.appActivateMainWindow();
                          sf.ui.proTools.mainWindow.invalidate();
                      
                          //Get selected Tracks
                          const selectedTrack = sf.ui.proTools.selectedTrack;
                          const selectedTrackName = selectedTrack.normalizedTrackName;
                          const selectedTrackOutputName = selectedTrack.outputPathButton.value.value;
                      
                          createAuxes({
                              numOfAuxes: 2,
                              auxName: "FX Aux"
                          });
                      
                          createFolder({
                              folderName: selectedTrackName + " FX",
                              isSelectFolderOnly: true
                          });
                      
                          // Assign output on selected track
                          sf.ui.proTools.selectedTrack.groups.whoseTitle.is("Audio IO").first
                              .popupButtons.whoseTitle.contains("Audio Output").first
                              .popupMenuSelect({
                                  menuSelector: menuItems => menuItems.find(menuItems => {
                                      const path = menuItems.path;
                                      return path[path.length - 1].includes(selectedTrackOutputName);
                                  })
                              });
                      }
                      
                      main();
                      

                      The main difference is that this approach doesn't need to open the output popup menu of the first selected track at the beginning of the script just to get the path and then close it again, and therefore it looks more transparent when running. On the flip side, my approach as it is right now could behave unexpectedly if the session has busses similarly named, while Forrester's grabs the exact path the original track had.

                      Hope that makes sense!

                    2. Raphael Sepulveda @raphaelsepulveda2023-07-03 16:47:16.438Z2023-07-03 22:21:43.011Z

                      @Forrester_Savell, that's awesome! You really came through for Joseph on this one. Very cool!

                      I'm happy to review this for you. I went through it quickly and here are the things that stood out:

                      • Avoid using var.
                        • Use const most of the time, if the contents of the variable are not going to change. If they are going to change, then use let.
                      • Function declarations
                        • Place all function declarations at the top of the script
                        • No need for a semicolon at the end of a function declaration
                      • Use a main function
                        • This is the function where the important parts of the script happen, usually in a concise way. This has several benefits and the main one is that it increases readability and relieves, what coders call, cognitive load. When you scan down the main function, you should be able to quickly understand what it is doing without getting into the details (check the refactored version below to see what I mean).
                      • Invalidate the mainWindow after creating a new track with a script
                        • This is to refresh SoundFlow's cache and have it recognize the new tracks. This is especially important if you're doing something to the newly created track within the runtime of the script.
                      • Avoid global variables
                        • Pass them to the functions directly instead
                        • In your script, this happened in createFolder() which was referring to folderName, located outside of its scope.
                        • This is one of the rules of a paradigm called Functional Programming (FP for short), which is cool cause it avoids all kinds of trouble. I don't follow FP all of the time but it's good to consider.
                        • This also makes your functions modular and can be reused in other scripts

                      Those are the main things!

                      To answer some of your other questions:

                      • Yeah, the way we stored track names and outputs was different but not too different. In my approach, I wanted to extract data from just the first selected track, while you fetched the names of all the selected tracks and then chose the first one from there. As for the output, I think you did awesome by grabbing the path directly from the track. I usually just grab the name and then figure out the path later while assigning it to the track—example of this in another reply I'm leaving in response to Joseph.
                      • Yes, that lag you're talking about is PT figuring out the routing behind the scenes. If you do the steps manually and try to do something immediately after, like select other tracks, or change edit selection, you'll see PT is super sluggish for a few seconds. SoundFlow waits patiently until PT is ready to be automated again before proceeding to select the folder.

                      After all that been said, here's how I'd refactor your script:

                      /** @param {{ numOfAuxes: number, auxName: string }} args */
                      function createAuxes({ numOfAuxes, auxName }) {
                          const newTrackWin = sf.ui.proTools.windows.whoseTitle.is("New Tracks").first;
                          const numberField = newTrackWin.textFields.whoseTitle.is('Number of new tracks').first;
                      
                          sf.ui.proTools.menuClick({ menuPath: ['Track', 'New...'] });
                          newTrackWin.elementWaitFor();
                      
                          if (numberField.value.invalidate().value.trim() !== String(numOfAuxes)) {
                              numberField.elementClick();
                      
                              sf.keyboard.type({ text: String(numOfAuxes) });
                      
                              var i = 0;
                              while (numberField.value.invalidate().value !== String(numOfAuxes)) {
                                  sf.wait({ intervalMs: 30 });
                              }
                          }
                      
                          newTrackWin.popupButtons.whoseDescription.is("Track format").first.popupMenuSelect({
                              menuPath: ["Stereo"]
                          });
                      
                          newTrackWin.popupButtons.whoseDescription.is("Track type").first.popupMenuSelect({
                              menuPath: ["Aux Input"]
                          });
                      
                          newTrackWin.textFields.whoseTitle.is("Track Name").first.elementSetTextFieldWithAreaValue({
                              value: auxName,
                          });
                      
                          newTrackWin.buttons.whoseTitle.is("Create").first.elementClick();
                          newTrackWin.elementWaitFor({ waitType: "Disappear" });
                      
                          sf.ui.proTools.mainWindow.invalidate();
                      }
                      
                      /** @param {{ folderName: string, isSelectFolderOnly: boolean }} args */
                      function createFolder({ folderName, isSelectFolderOnly }) {
                          const newFolderWin = sf.ui.proTools.windows.whoseTitle.is("Move To New Folder").first;
                      
                          sf.ui.proTools.menuClick({
                              menuPath: ["Track", "Move to New Folder..."],
                          });
                      
                          newFolderWin.elementWaitFor()
                      
                          newFolderWin.popupButtons.first.popupMenuSelect({
                              menuPath: ["Routing Folder"],
                          });
                      
                          newFolderWin.checkBoxes.whoseTitle.is("Route Tracks to New Folder").first.checkboxSet({
                              targetValue: "Enable"
                          });
                      
                          newFolderWin.textFields.whoseTitle.is("Track Name").first.elementSetTextFieldWithAreaValue({
                              value: folderName,
                          });
                          newFolderWin.popupButtons.whoseDescription.is("Track format").first.popupMenuSelect({
                              menuPath: ["Stereo"]
                          });
                          newFolderWin.buttons.whoseTitle.is("Create").first.elementClick();
                          newFolderWin.elementWaitFor({ waitType: "Disappear" });
                      
                          sf.ui.proTools.mainWindow.invalidate();
                      
                          if (isSelectFolderOnly) {
                              sf.ui.proTools.trackSelectByName({ names: [folderName] });
                          }
                      }
                      
                      function getSelectedTrackOutputPath() {
                          // define track output selector
                          const trackOutput = sf.ui.proTools.selectedTrack.groups.whoseTitle.is("Audio IO").first
                              .popupButtons.allItems[1];
                      
                          const outputPaths = trackOutput.popupMenuFetchAllItems().menuItems;
                          const outputPath = outputPaths
                              .map((mi, i) => ({ menuItem: mi, index: i }))
                              .filter(m => m.menuItem.element.isMenuChecked)[0].menuItem.path;
                      
                          sf.ui.proTools.appActivateMainWindow();
                      
                          return outputPath;
                      }
                      
                      function main() {
                          sf.ui.proTools.appActivateMainWindow();
                          sf.ui.proTools.mainWindow.invalidate();
                      
                          //Get selected Tracks
                          const selectedTrackName = sf.ui.proTools.selectedTrackNames[0];
                          const outputPath = getSelectedTrackOutputPath();
                      
                          createAuxes({
                              numOfAuxes: 2,
                              auxName: "FX Aux"
                          });
                      
                          createFolder({
                              folderName: selectedTrackName + " FX",
                              isSelectFolderOnly: true
                          });
                      
                          // Assign output on selected track
                          sf.ui.proTools.selectedTrack.groups.whoseTitle.is("Audio IO").first
                              .popupButtons.whoseTitle.contains("Audio Output").first
                              .popupMenuSelect({
                                  menuPath: outputPath,
                              });
                      }
                      
                      main();
                      
                      1. FForrester Savell @Forrester_Savell
                          2023-07-03 22:12:48.979Z

                          Wow @raphaelsepulveda this is immensely helpful, thanks for taking the time to write all that out. That is a great list of tips I didn't pickup watching hours of Javascript video. I have a bit of refactoring to do in my other scripts!

                          1. Glad that was helpful. You're doing great!