No internet connection
  1. Home
  2. How to
  3. Pro Tools

Pro Tools Script: Bounce Regions Specified by Memory Locations

By jpdharing @jpdharing
    2025-01-29 19:15:14.112Z

    Hi, all.

    I'm writing a script for a session that would contain a bunch of different songs/tracks in a full sequence where I need to bounce all of them individually. The different songs are always marked at their start by a memory location named after the title of the song, and always extend until the next memory location, marking the beginning of the next.

    I'm trying to make a script that moves to my first marker, extends the region to the next, and bounces, setting the file name to "the name of the memory location + [bounce type]", and filling in the bounce window with the desired settings. This would repeat as many times as there are memory locations, minus 1, to handle the inclusion of an "END" marker. For example, the first 1644 WAV bounce in a session where the first track is titled "trackName1" would be named "trackName1 1644 WAV". This would handle in a similar fashion to adding MP3's, by titling those files as "trackName1 MP3-REFERENCE" in the MP3 window.

    Most of the script functions as intended, but I can't figure out how to extract and store information from the memory locations window. I need the total number of memory locations, which is used to determine how many times to loop, as well as the names of all the memory locations. I want to loop the bounce function while iterating through and referencing an array of memory location names, so the process is entirely automated.

    Disorganized, but potentially relevant info: Currently, referencing the "AXRow" children in the memory locations window returns an array of size 2, while using "AXColumn" returns 12. I can tell that the AXColumns are the cells that contain the labels for the columns like "#", "Marker Name", etc. I found that the "Marker Names" column is reference by "children.whoseRole.is("AXCell").allItems[2]". Not sure how helpful that is to know, but it's info I found trying to figure this out, so I figured I'd add it. I can't find any documentation on "AXCell" or "AXStaticText", but I suspect the answer could be there.

    Any and all help would be greatly appreciated. I'm relatively new to JavaScript, but I've been programming for a while. Thanks in advance!

    sf.keyboard.press({
        keys: "return",
    });
    
    sf.wait();
    
    sf.ui.proTools.memoryLocationsShowWindow();
    sf.ui.proTools.memoryLocationsWindow.elementWaitFor();
    
    // Get the memory locations table
    const memoryLocations = sf.ui.proTools.memoryLocationsWindow.tables
        .whoseTitle.is("Memory Locations")
        .first.invalidate()
        .children.whoseRole.is("AXRow");
    
    // Extract memory location names
    const memoryLocationNames = memoryLocations.map(l =>
        l.children.whoseRole.is("AXCell").allItems[2].children.whoseRole.is("AXStaticText").first.title.value.replace("Selected. ", "")
    );
    
    // Get the total number of memory locations
    const totalMemoryLocations = memoryLocationNames.length;
    
    // Loop through for all memory locations except the 'END'
    for (let i = 0; i < totalMemoryLocations; i++) {
    
        sf.wait();
    
        // Execute the code block for each memory location, using i for the numpad key
        sf.keyboard.press({
            keys: `numpad comma, numpad ${i}, numpad comma`, // Dynamically presses numpad I
        });
    
        sf.wait();
    
        // Calling command "Extend Selection to Next Marker"
        sf.soundflow.runCommand({
            commandId: 'user:ckp49i4j60000a2100yfwywgf:ckou1qcq5007ozv10n96bgqcj',
            props: {}
        });
    
        bounceSettings1644(memoryLocationNames[i]);
    
    }
    
    function bounceSettings1644(fileName) {
        // Call the command to open the Bounce Mix window
        sf.soundflow.runCommand({
            commandId: 'user:ckp49i4j60000a2100yfwywgf:cktcnf0ra000x9r109i8wen1u#cktcms2j4000h9r103zqxnmg5',
            props: {}
        });
    
        //Wait for 'Bounce Mix' window
        let bounceWin = sf.ui.proTools.dialogWaitForManual({
            dialogTitle: 'Bounce Mix'
        }).dialog;
    
        const safeFileName = fileName || "No Track Name"; // Default name if fileName is undefined/null
    
        sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.textFields.first.elementSetTextFieldWithAreaValue(
            { value: safeFileName.trim() + " 1644 WAV(CD)" }
        );
    
        //Set File Type
        if (bounceWin.popupButtons.first.value.invalidate().value != 'WAV (BWF)')
            bounceWin.popupButtons.first.popupMenuSelect({ menuPath: ['WAV (BWF)'] });
    
        //AUDIO PANEL
        const audioPanel = bounceWin.groups.whoseTitle.is('Audio').first;
    
        //Popup Buttons
        const popupButtons = audioPanel.children.filter(e => e.fullRole == "AXPopUpButton").slice(-4);
    
        const compressionType = popupButtons[0];
        const formatBtn = popupButtons[1];
        const bitDepthBtn = popupButtons[2];
        const sampleRateBtn = popupButtons[3];
    
        //Compression Type
        if (compressionType.value.invalidate().value != 'PCM (Uncompressed)')
            compressionType.popupMenuSelect({ menuPath: ['PCM (Uncompressed)'] });
    
        //File Format
        if (formatBtn.value.invalidate().value != 'Interleaved')
            formatBtn.popupMenuSelect({ menuPath: ['Interleaved'] });
    
        //Bit Depth
        if (bitDepthBtn.value.invalidate().value != '16 Bit')
            bitDepthBtn.popupMenuSelect({ menuPath: ['16 Bit'] });
    
        //Sample Rate
        if (sampleRateBtn.value.invalidate().value != '44.1 kHz')
            sampleRateBtn.popupMenuSelect({ menuPath: ['44.1 kHz'] });
    
        //Enable MP3
        audioPanel.checkBoxes.whoseTitle.is('Add MP3').first.checkboxSet({
            targetValue: 'Enable'
        });
    
        sf.wait();
    
        sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first.buttons.whoseTitle.is("Bounce").first.elementClick();
    
        sf.ui.proTools.windows.whoseTitle.is('MP3').first.textFields.first.elementSetTextFieldWithAreaValue(
            { value: safeFileName.trim() + " MP3-REFERENCE" }
        );
    
        sf.wait();
    
        sf.ui.proTools.windows.whoseTitle.is("MP3").first.buttons.whoseTitle.is("OK").first.elementClick();
    
        // Wait for no modals before continuing with the script
        sf.ui.proTools.waitForNoModals();
    
    }
    
    Solved in post #5, click to view
    • 6 replies
    1. D
      danielkassulke @danielkassulke
        2025-01-29 22:08:48.693Z

        Hi @jpdharing,

        I have a few scripts that do versions of this. For example, I have a "Bounce nearest selection region" that lets you select sample rate and bit depth from a drop down list, and appends that to the bounce name. Both of these scripts bounce selection-type markers that contain a string, in my case "BNC". It seems to me like you would get a lot of mileage out of those scripts, but they would rely on selection-type markers rather than location-type markers. Would it fit with your workflow to rely on selection markers?

        1. Jjpdharing @jpdharing
            2025-01-29 23:07:33.799Z

            It's possible that something like that could work. I can always modify, however, I do work at a facility that standardizes certain things like this, so I'm not perfectly sure at the moment. My issue isn't so much in changing the text field itself or appending, that I have working. My issue is with referencing the names of my markers so I can store them into a map/variable, and referencing the total number of markers. Are you referencing the names of your markers in your scripts?

            1. Ddanielkassulke @danielkassulke
                2025-01-30 00:39:39.571Z

                Can you provide a screenshot showing how you would typically label these markers in your session? If you could show a couple of these in your screenshot that would be ace.

                1. Jjpdharing @jpdharing
                    2025-02-03 18:27:59.499Z

                    Actually, was given a solution on this thread that works for what I'm doing, I really appreciate your input though! I started looking into selection markers after you mentioned them and I've already found a few applications for myself, so, again, thanks for helping out!

              • In reply tojpdharing:
                Sreejesh Nair @Sreejesh_Nair
                  2025-01-30 02:48:03.972Z2025-01-30 03:38:25.847Z

                  This will give you marker name, the marker number, start time and the total count of markers in the session using the Pro Tools SDK. You can then use these values to modify your script.

                  const markerNameList = sf.app.proTools.memoryLocations.invalidate().allItems.map(m => ({
                      name: m.name,
                      number: m.number,
                      startTime: m.startTime
                  }));
                  
                  // Marker names
                  const markerNames = markerNameList.map(marker => marker.name);
                  
                  // Marker numbers
                  const markerNumbers = markerNameList.map(marker => marker.number);
                  
                  // Marker start
                  const markerStart = markerNameList.map(marker => marker.startTime);
                  
                  //Total Marker Count
                  const markerCount = markerNameList.length;
                  

                  The following code will go through the marker in order of start time, in case the markers are not created that way, and make selections. You can insert your bounce code within that. You can use the SDK methods for this.

                  const markerNameList = sf.app.proTools.memoryLocations.invalidate().allItems.map(m => ({
                      name: m.name,
                      number: m.number,
                      startTime: m.startTime
                  }));
                  
                  // Sort markerNameList by startTime
                  const markerInTimelineOrder = markerNameList
                      .slice()
                      .sort((a, b) => parseInt(a.startTime, 10) - parseInt(b.startTime, 10)) // Convert startTime to number and sort
                      .map(marker => marker.startTime); // Store sorted names
                  
                  //Make selections and loop till final location
                  for (let i = 0; i < markerInTimelineOrder.length - 1; i++) {
                      const startMarker = markerInTimelineOrder[i];
                      const endMarker = markerInTimelineOrder[i + 1];
                  
                      sf.app.proTools.invalidate();
                      sf.app.proTools.setTimelineSelection({ inTime: startMarker, outTime: endMarker });
                  
                  //Put your bounce code here
                  
                  }
                  
                  Reply1 LikeSolution
                  1. Jjpdharing @jpdharing
                      2025-02-03 18:23:41.612Z

                      This is perfect, I can definitely make this work with what I've got. Thank you so much!