No internet connection
  1. Home
  2. How to

Delete between markers

By Chris Testa @Chris_Testa
    2024-10-30 20:11:56.914Z

    I posted this as a follow up to someone else's question that was pretty similar but I think it got buried. If there is a post out there with this already I can certainly work off that. Thanks

    "Anyone know how to apply this to deleting selections and then moving on to the next selection? For example, in most of my shows I want to make sure there is no audio within the blacks between the act breaks. I usually have "break 1", "ACT 2", "break 2", "ACT 3" markers etc. Ideally I'd like to make a selection of tracks and have the selection go to "break 1" then select until "ACT 2" then delete, then move on to "break 2" and make the selection from marker "break2" to marker "ACT3" and so on....

    • 17 replies

    There are 17 replies. Estimated reading time: 18 minutes

    1. D
      danielkassulke @danielkassulke
        2024-10-30 21:24:01.678Z

        Hi @Chris_Testa , try this one, courtesy of @Kitch, tweaking by me :

        const startMemoryLocationKeyword = "ACT";
        const endMemoryLocationKeyword = "BREAK";
        
        sf.ui.proTools.memoryLocationsWindow.invalidate();
        
        function isAudioTrack(track) {
            return track.title.value.indexOf('Audio Track') >= 0;
        }
        
        function selectAllAudioTracks() {
            var tracksToSelect = sf.ui.proTools.visibleTrackHeaders.filter(isAudioTrack);
        
            sf.ui.proTools.trackSelectByName({
                names: tracksToSelect.map(t => t.normalizedTrackName),
                deselectOthers: true,
            });
        }
        
        function executeSelectAudioTracks() {
            sf.ui.proTools.appActivateMainWindow();
        
            selectAllAudioTracks();
        }
        
        function validateMemoryLocationArray(locations, startMemoryLocationKeyword, endMemoryLocationKeyword) {
            const locationNames = locations.map(m => m.name);
        
            if (locationNames.length < 2) {
                throw `There must be at least one pair of "${startMemoryLocationKeyword}" and "${endMemoryLocationKeyword}" markers.`;
            }
        
            if (!locationNames[0].includes(startMemoryLocationKeyword)) {
                throw `The first location name must contain "${startMemoryLocationKeyword}".`;
            }
        
            for (let i = 1; i < locationNames.length; i++) {
                const expectedKeyword = i % 2 === 1 ? endMemoryLocationKeyword : startMemoryLocationKeyword;
                if (!locationNames[i].includes(expectedKeyword)) {
                    throw `Location at index ${i} must contain '${expectedKeyword}'.`;
                }
            }
        
            return true;
        }
        
        function selectAndProcessSection({ startMemoryLocation, endMemoryLocation, isDeadSpace }) {
            let errorMessage = "";
        
            if (!startMemoryLocation) {
                errorMessage += `Start memory location containing "${startMemoryLocationKeyword}" not found. `;
            }
        
            if (!endMemoryLocation) {
                errorMessage += `End memory location containing "${endMemoryLocationKeyword}" not found.`;
            }
        
            if (errorMessage) {
                throw new Error(errorMessage.trim());
            }
        
            executeSelectAudioTracks();
        
            const inTime = startMemoryLocation.startTime;
            const outTime = endMemoryLocation.startTime;
        
            sf.app.proTools.invalidate();
            sf.app.proTools.setTimelineSelection({ inTime, outTime });
        
            // clear timeline selection if between "BREAK" and "ACT"
            if (isDeadSpace) {
                sf.ui.proTools.menuClick({
                    menuPath: ["Edit", "Clear"],
                });
            }
        }
        
        function main() {
            sf.ui.proTools.appActivateMainWindow();
            sf.ui.proTools.invalidate();
            sf.app.proTools.invalidate();
        
            const memoryLocations = sf.app.proTools.memoryLocations.allItems;
        
            const filteredMemoryLocations = memoryLocations.filter(ml => {
                const name = ml.name;
                return name.includes(startMemoryLocationKeyword) || name.includes(endMemoryLocationKeyword);
            });
        
            validateMemoryLocationArray(filteredMemoryLocations, startMemoryLocationKeyword, endMemoryLocationKeyword);
        
            for (let i = 0; i < filteredMemoryLocations.length - 1; i++) {
                let startMemoryLocation = filteredMemoryLocations[i];
                let endMemoryLocation = filteredMemoryLocations[i + 1];
        
                const isDeadSpace = startMemoryLocation.name.includes(endMemoryLocationKeyword) &&
                                    endMemoryLocation.name.includes(startMemoryLocationKeyword);
        
                selectAndProcessSection({
                    startMemoryLocation,
                    endMemoryLocation,
                    isDeadSpace,
                });
            }
        }
        
        main();
        

        Script will filter out non-alphabetical characters from the memory locations, if your labelling conventions look like mine, i.e. "1. ACT 1", "2. BREAK 1" etc. The script will also ignore any markers between BREAK and ACT that don't have those words in them. Note that it's currently operating on all audio tracks - you might want to tweak this, but i figured that dead air is dead air. Tested on 2024.10, SF V5.9.0

        1. CChris Testa @Chris_Testa
            2024-10-30 22:01:15.413Z

            Amazing. Thank you. Trying out now. I'll let you know. thanks.

            1. In reply todanielkassulke:
              CChris Testa @Chris_Testa
                2024-10-30 22:13:33.245Z

                So I got an error. I switched the "BREAK" to "break" and still the same error. I've included the error and what my Markers list looks like. This is baked into my template so it won't really change.

                1. Ddanielkassulke @danielkassulke
                    2024-10-30 22:41:48.529Z2024-10-30 23:25:13.701Z

                    OK, slight rethink:

                    const startMemoryLocationKeyword = "ACT";
                    const endMemoryLocationKeyword = "BREAK";
                    
                    sf.ui.proTools.memoryLocationsWindow.invalidate();
                    
                    function isAudioTrack(track) {
                        return track.title.value.indexOf('Audio Track') >= 0;
                    }
                    
                    function selectAllAudioTracks() {
                        var tracksToSelect = sf.ui.proTools.visibleTrackHeaders.filter(isAudioTrack);
                    
                        sf.ui.proTools.trackSelectByName({
                            names: tracksToSelect.map(t => t.normalizedTrackName),
                            deselectOthers: true,
                        });
                    }
                    
                    function executeSelectAudioTracks() {
                        sf.ui.proTools.appActivateMainWindow();
                        selectAllAudioTracks();
                    }
                    
                    function selectAndProcessSection({ startMemoryLocation, endMemoryLocation }) {
                        executeSelectAudioTracks();
                    
                        const inTime = startMemoryLocation.MainCounterValue;
                        const outTime = endMemoryLocation.MainCounterValue;
                    
                        sf.app.proTools.invalidate();
                        sf.app.proTools.setTimelineSelection({ inTime, outTime });
                    
                        sf.ui.proTools.menuClick({
                            menuPath: ["Edit", "Clear"],
                        });
                    }
                    
                    function main() {
                        sf.ui.proTools.appActivateMainWindow();
                        sf.ui.proTools.invalidate();
                        sf.app.proTools.invalidate();
                    
                        // Fetch and retrieve all memory locations
                        const memoryLocationsData = sf.proTools.memoryLocationsFetch().collection['List'];
                    
                        const actRegex = /\bact\b/i;
                        const breakRegex = /\bbreak\b/i;
                    
                        const filteredMemoryLocations = memoryLocationsData
                            .filter(ml => actRegex.test(ml.Name) || breakRegex.test(ml.Name))
                            .sort((a, b) => parseInt(a.MainCounterValue) - parseInt(b.MainCounterValue));
                    
                        let processing = false;
                    
                        for (let i = 0; i < filteredMemoryLocations.length - 1; i++) {
                            let breakMarker = filteredMemoryLocations[i];
                    
                            if (!processing && actRegex.test(breakMarker.Name)) {
                                processing = true;
                            }
                    
                            if (processing && breakRegex.test(breakMarker.Name)) {
                                for (let j = i + 1; j < filteredMemoryLocations.length; j++) {
                                    let actMarker = filteredMemoryLocations[j];
                    
                                    if (actRegex.test(actMarker.Name)) {
                                        selectAndProcessSection({
                                            startMemoryLocation: breakMarker,
                                            endMemoryLocation: actMarker,
                                        });
                    
                                        i = j;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    
                    main();
                    
                    1. CChris Testa @Chris_Testa
                        2024-10-30 23:12:48.498Z

                        ok now nothing happens. No trigger, no error. Humm. not sure. Do I need to modify something on my end?

                        1. Ddanielkassulke @danielkassulke
                            2024-10-30 23:25:30.990Z

                            edited above - suspect it might have been case-sensitivity that was throwing it off

                            1. CChris Testa @Chris_Testa
                                2024-10-30 23:31:51.137Z

                                No. Still nothing. It's weird, no error. It's like it doesn't fire. I tried running directly from SF Editor and then added to my deck and still nothing. Not sure.

                                1. Ddanielkassulke @danielkassulke
                                    2024-10-30 23:35:37.521Z

                                    What version of PT / SF are you running?

                                    1. CChris Testa @Chris_Testa
                                        2024-10-30 23:36:57.071Z

                                        PT 2024.3 and SF 5.9

                                        1. Ddanielkassulke @danielkassulke
                                            2024-10-30 23:41:11.551Z

                                            Ahhhh ok. Gotcha. 2024.6 Changed a fair bit of stuff in the memory location window, so all of my memory location scripts are optimised to handle 2024.6+. I won't be able to test backwards compatibility, but give this script a test drive when you have updated PT. Not to tell you how to cook your own BBQ, but you could download PT 2024.6 and rename the app to "Pro Tools 2024.6" and test it alongside 2024.3 if you were interested.

                                            1. CChris Testa @Chris_Testa
                                                2024-10-30 23:42:36.517Z

                                                ok. Yeah I was having some HDX issues with 2024.6 so I moved back. Thinking of going straight to 2024.10. Thank you again and I will.

                                                1. In reply todanielkassulke:
                                                  CChris Testa @Chris_Testa
                                                    2024-10-31 17:32:04.945Z

                                                    Ok so I updated this morning to 2024.10 and it works perfectly. Thank you again. chris

                                                    1. Ddanielkassulke @danielkassulke
                                                        2024-10-31 22:42:42.298Z

                                                        No probs! I'll share one with you that automatically creates those act/break markers too - almost finished it, but have a couple of problems to solve first.

                                                        1. In reply toChris_Testa:
                                                          Ddanielkassulke @danielkassulke
                                                            2024-11-01 06:23:40.023Z
                                                            const pT = sf.ui.proTools;
                                                            pT.appActivateMainWindow();
                                                            pT.mainWindow.invalidate();
                                                            
                                                            function isAudioTrack(track) {
                                                                return track.title.value.indexOf('Audio Track') >= 0;
                                                            }
                                                            
                                                            function selectAllAudioTracks() {
                                                                const tracksToSelect = sf.ui.proTools.visibleTrackHeaders.filter(isAudioTrack);
                                                                sf.ui.proTools.trackSelectByName({
                                                                    names: tracksToSelect.map(t => t.normalizedTrackName),
                                                                    deselectOthers: true,
                                                                });
                                                            }
                                                            
                                                            function getMemoryLocations() {
                                                                sf.app.proTools.memoryLocations.invalidate();
                                                                return sf.app.proTools.memoryLocations.allItems.map(memLoc => ({
                                                                    number: memLoc.number,
                                                                    name: memLoc.name
                                                                }));
                                                            }
                                                            
                                                            function newMemoryLocation(name, startTime, markerNumber) {
                                                                const locationMarkerName = `${markerNumber}. ${name}`;
                                                                sf.app.proTools.createMemoryLocation({
                                                                    name: locationMarkerName,
                                                                    startTime: startTime.toString(),
                                                                    colorIndex: 16
                                                                });
                                                            }
                                                            
                                                            function newSelectionMarker(name, inTime, outTime) {
                                                                selectAllAudioTracks();
                                                                sf.app.proTools.setTimelineSelection({
                                                                    inTime: inTime.toString(),
                                                                    outTime: outTime.toString()
                                                                });
                                                                sf.keyboard.press({ keys: "numpad enter" });
                                                            
                                                                const memLocWin = sf.ui.proTools.newMemoryLocationDialog.elementWaitFor().element;
                                                                memLocWin.textFields.first.elementSetTextFieldWithAreaValue({ value: name });
                                                                memLocWin.radioButtons.whoseTitle.is("Selection").first.checkboxSet({ targetValue: "Enable" });
                                                                memLocWin.buttons.whoseTitle.is("OK").first.elementClick();
                                                                memLocWin.elementWaitFor({ waitType: "Disappear" });
                                                            }
                                                            
                                                            function getOverallInOutTimes() {
                                                                selectAllAudioTracks();
                                                                sf.keyboard.press({ keys: "left" });
                                                                pT.menuClick({
                                                                    menuPath: ["Edit", "Select All"]
                                                                });
                                                                const timelineSelection = sf.app.proTools.getTimelineSelection();
                                                                return {
                                                                    inTime: parseFloat(timelineSelection.inTime),
                                                                    outTime: parseFloat(timelineSelection.outTime)
                                                                };
                                                            }
                                                            
                                                            function getSegmentBoundaries() {
                                                                const { inTime: overallInTime, outTime: overallOutTime } = getOverallInOutTimes();
                                                                const selectedClips = sf.app.proTools.getSelectedClipInfo().Clips.map(clip => ({
                                                                    startTime: parseFloat(clip.startTime),
                                                                    endTime: parseFloat(clip.endTime)
                                                                })).sort((a, b) => a.startTime - b.startTime);
                                                            
                                                                const currentSampleRate = sf.app.proTools.getSessionSampleRate().sampleRate;
                                                                const sampleRateNumber = Number(currentSampleRate.match(/\d+/)[0]);
                                                                const gapThresholdSamples = 30 * sampleRateNumber;
                                                            
                                                                const segments = [];
                                                                let segmentStartTime = selectedClips[0].startTime;
                                                                let segmentEndTime = selectedClips[0].endTime;
                                                            
                                                                for (let i = 1; i < selectedClips.length; i++) {
                                                                    const currentClip = selectedClips[i];
                                                                    const gap = currentClip.startTime - segmentEndTime;
                                                            
                                                                    if (gap > gapThresholdSamples) {
                                                                        segments.push({ startTime: segmentStartTime, endTime: segmentEndTime });
                                                                        segmentStartTime = currentClip.startTime;
                                                                    }
                                                            
                                                                    segmentEndTime = Math.max(segmentEndTime, currentClip.endTime);
                                                                }
                                                                segments.push({ startTime: segmentStartTime, endTime: segmentEndTime });
                                                            
                                                                segments[0].startTime = Math.min(segments[0].startTime, overallInTime);
                                                                segments[segments.length - 1].endTime = Math.max(segments[segments.length - 1].endTime, overallOutTime);
                                                            
                                                                return segments;
                                                            }
                                                            
                                                            function createMarkers() {
                                                                const segments = getSegmentBoundaries();
                                                                const startMarkerNumber = getMemoryLocations().reduce((max, mem) => Math.max(max, mem.number), 0) + 1;
                                                                let markerNumber = startMarkerNumber;
                                                            
                                                                segments.forEach((segment, index) => {
                                                                    newMemoryLocation(`ACT ${index + 1}`, segment.startTime, markerNumber);
                                                                    markerNumber += 1;
                                                            
                                                                    if (index < segments.length - 1) {
                                                                        newMemoryLocation(`BREAK ${index + 1}`, segment.endTime, markerNumber);
                                                                        markerNumber += 1;
                                                                    }
                                                                });
                                                            
                                                                segments.forEach((segment, index) => {
                                                                    newSelectionMarker(`ACT ${index + 1}`, segment.startTime, segment.endTime);
                                                                });
                                                            }
                                                            
                                                            createMarkers();
                                                            

                                                            Hey @Chris_Testa Give this a spin next time you want to create those markers automatically. This script will:

                                                            • Create ACT location markers
                                                            • Create BREAK location markers
                                                            • Create ACT selection markers

                                                            Any part of your timeline containing no clips for more than 30 seconds will be considered a break. You can adjust this on line 71 const gapThresholdSamples.

                                                            A couple of things to be mindful of - clips on inactive audio tracks will be considered part of the timeline, so might throw the timeline selection off. Also, if your audio tracks are in closed folders this script will fail.

                                                            1. CChris Testa @Chris_Testa
                                                                2024-11-01 17:46:13.991Z

                                                                That's really interesting. I might be able to use that on a REF mix to lay in markers then bring everything else in. Are you mixing TV?

                                                                1. Ddanielkassulke @danielkassulke
                                                                    2024-11-06 08:41:13.216Z

                                                                    Not full time, though will do a few TV gigs per year. Enough that I became acutely aware of how much time I was wasting without scaffolding my workflow with soundflow

                                                                    1. CChris Testa @Chris_Testa
                                                                        2024-11-06 19:53:45.742Z

                                                                        Right. SF is essential for post now. So many repetitive tasks. Every day I'm adding something else. Thank you again for your help with this one.