No internet connection
  1. Home
  2. How to

Playlists and Waveform Toggle

By Bergatron Music @Bergatron_Music
    2022-11-13 19:39:24.142Z

    Hey SF.
    Is there a way to set the track view to toggle between two different view options, like waveform and playlists? Right now I have a macro for each view, but it would be even more efficient if I had one macro to toggle between the two. Any help would be greatly appreciated.

    Solved in post #2, click to view
    • 11 replies

    There are 11 replies. Estimated reading time: 17 minutes

    1. This should do it.
      Just change the displayPath1 and displayPath2 values to the settings you want.

      // Define display values
      const displayPath1 = ["waveform"]
      const displayPath2 = ["playlists"]
      
      sf.ui.proTools.appActivate();
      
      //Get current display
      const currentDisplay = sf.ui.proTools.selectedTrack.displaySelectorButton.value.invalidate().value;
      
      // check current display value to determine new display value
      const newDisplay = (displayPath1[displayPath1.length - 1] == currentDisplay) ? displayPath2 : displayPath1;
      
      // change display
      sf.ui.proTools.selectedTrack.trackDisplaySelect({ displayPath: newDisplay, selectForAllSelectedTracks: true })
      
      
      ReplySolution
      1. Bergatron Music @Bergatron_Music
          2022-11-14 19:29:30.370Z

          Wow! Thank you Chris!

          1. R
            Renan Carvalho @Renan_Carvalho
              2023-05-10 22:43:16.816Z

              Hey Chris, is there any way to set with a single click, every time I press the shortcut for this this script changes the view option?
              example: pressed T = Waveform, pressed again = Volume, pressed again = mute

              1. this will cycle through whichever displays you want instead of every menu item.
                Just add/delete the views you want in the displayPaths array to customize it to what you want.

                // Define display values
                const displayPaths = ["waveform", "playlists", "volume", "mute"];
                
                sf.ui.proTools.appActivate();
                
                //Get current display
                const currentDisplay = sf.ui.proTools.selectedTrack.displaySelectorButton.value.invalidate().value;
                
                // check current display value to determine new display value
                let newDisplayIndex = (displayPaths.indexOf(currentDisplay)) + 1;
                if (newDisplayIndex >= displayPaths.length) newDisplayIndex = 0;
                
                // change display
                sf.ui.proTools.selectedTrack.trackDisplaySelect({
                    displayPath: [displayPaths[newDisplayIndex]],
                    selectForAllSelectedTracks: true
                });
                
                1. DDan Powe @Dan_Powe6
                    2024-01-06 21:52:20.512Z

                    Hey Chris! I'm trying to toggle between volume and volume trim. I've based it off of this and it only ever selects volume. Any chance you have any suggestions as to where I'm going wrong? I should note, I'm trying this on folder tracks as that's where I'd ideally use it. Using Pro Tools 2023.12.

                    // Define display values
                    const displayPaths = ["volume", "volume trim"];
                    
                    sf.ui.proTools.appActivate();
                    
                    //Get current display
                    const currentDisplay = sf.ui.proTools.selectedTrack.displaySelectorButton.value.invalidate().value;
                    
                    // check current display value to determine new display value
                    let newDisplayIndex = (displayPaths.indexOf(currentDisplay)) + 1;
                    if (newDisplayIndex >= displayPaths.length) newDisplayIndex = 0;
                    
                    // change display
                    sf.ui.proTools.selectedTrack.trackDisplaySelect({
                        displayPath: [displayPaths[newDisplayIndex]],
                        selectForAllSelectedTracks: true
                    });
                    
                2. S
                  Sreejesh Nair @Sreejesh_Nair
                    2024-01-07 06:53:04.676Z

                    This needs a slight modification to work correctly since the current display will always cause the index to be the same value. I implemented a search of the current display to see if it loosely matches the value in the displayPaths. If so, we can shift the display path to show the lane of the next one in the array. This way, even if you have volume and volume trim, it will shift to the next. And as per the original logic, if the value is >= to the displayPaths.length, it will shift it to 0.

                    // Define display values
                    const displayPaths = ["waveform", "volume", "volume trim"];
                    
                    sf.ui.proTools.appActivate();
                    
                    // Get current display
                    const currentDisplay = sf.ui.proTools.selectedTrack.displaySelectorButton.value.invalidate().value;
                    
                    // Function to find loosely matching index
                    function findLooseMatchIndex(array, target) {
                        for (let i = 0; i < array.length; i++) {
                            if (target.includes(array[i]) || array[i].includes(target)) {
                                return i;
                            }
                        }
                        return -1; // Return -1 if no loose match is found
                    }
                    
                    // Check current display value to determine new display value
                    let matchedIndex = findLooseMatchIndex(displayPaths, currentDisplay);
                    let newDisplayIndex = matchedIndex === -1 ? 0 : matchedIndex + 1;
                    
                    // Loop back to the first index if it exceeds the array length
                    if (newDisplayIndex >= displayPaths.length) newDisplayIndex = 0;
                    
                    // Change display
                    sf.ui.proTools.selectedTrack.trackDisplaySelect({
                        displayPath: [displayPaths[newDisplayIndex]],
                        selectForAllSelectedTracks: true
                    });
                    
                    1. DDan Powe @Dan_Powe6
                        2024-01-07 10:26:02.564Z

                        That's absolutely fantastic, Sreejesh! Thanks so much for your help and explanation 😊

                        1. SSreejesh Nair @Sreejesh_Nair
                            2024-01-07 10:55:26.413Z2024-01-07 11:58:23.597Z

                            A robust method would be to set the track heights to medium and then do an exact match. I only used loose match quickly because it may give you an error on Aux tracks to being set to Volume only since the match is the same for Volume or Volume trim if you are doing a loose match. Here is an example code that also factors a correction if the cycled lane isn't found like say waveform on an aux track.

                            // Define display values
                            const displayPaths = ["waveform", "mute", "volume", "volume trim"];
                            
                            sf.ui.proTools.appActivate();
                            
                            // Get current display
                            const currentDisplay = sf.ui.proTools.selectedTrack.displaySelectorButton.value.invalidate().value;
                            
                            //Set track height to medium
                            var f = sf.ui.proTools.selectedTrack.frame;
                            var popupMenu = sf.ui.proTools.selectedTrack.popupMenuOpenFromElement({
                                relativePosition: { x: f.w - 10, y: 5 },
                            }).popupMenu;
                            popupMenu.menuClickPopupMenu({
                                menuPath: ["medium"]
                            });
                            
                            // Function to find exact matching index
                            function findExactMatchIndex(array, target) {
                                for (let i = 0; i < array.length; i++) {
                                    if (array[i] === target) {
                                        return i;
                                    }
                                }
                                return -1; // Return -1 if no exact match is found
                            }
                            
                            // Check current display value to determine new display value
                            let matchedIndex = findExactMatchIndex(displayPaths, currentDisplay);
                            let newDisplayIndex = matchedIndex === -1 ? 0 : matchedIndex + 1;
                            
                            // Loop back to the first index if it exceeds the array length
                            if (newDisplayIndex === displayPaths.length) newDisplayIndex = 0;
                            
                            // Change display
                            try {
                                sf.ui.proTools.selectedTrack.trackDisplaySelect({
                                    displayPath: [displayPaths[newDisplayIndex]],
                                    selectForAllSelectedTracks: true
                                });
                            
                            } catch (error) {   
                                sf.ui.proTools.selectedTrack.trackDisplaySelect({
                                    displayPath: [displayPaths[newDisplayIndex + 1]],
                                    selectForAllSelectedTracks: true
                                });
                            }
                            
                            1. Dav3 @D_av_3
                                2024-01-08 14:27:21.511Z

                                cool thanks! if I wanted to insert send A level instead of trim would I have to write this on line 2?

                                
                                const displayPaths = ["waveform", "mute", "volume", ['*(snd a)*', 'level']];
                                
                                1. SSreejesh Nair @Sreejesh_Nair
                                    2024-01-08 20:57:06.274Z2024-01-08 21:27:52.280Z

                                    No. The sends require a different way because of the wildcard method. And if you want to skip to the next send (say if send a wasnt present, shift to send b and so on), then a bit more complicated compare function as below is needed. Thats because the displayPath is shown as snd a level, while our array has to be "(snd a)", "level" (with a wildcard). The below code will help you with a starting point.

                                    const displayPaths = [
                                        ["waveform"],
                                        ["mute"],
                                        ["volume"],
                                        ["volume trim"],
                                        ["*(snd a)*", "level"],
                                        ["*(snd b)*", "level"],
                                        ["*(snd c)*", "level"],
                                    ];
                                    
                                    sf.ui.proTools.appActivate();
                                    
                                    const currentDisplay = sf.ui.proTools.selectedTrack.displaySelectorButton.value.invalidate().value;
                                    
                                    var f = sf.ui.proTools.selectedTrack.frame;
                                    var popupMenu = sf.ui.proTools.selectedTrack.popupMenuOpenFromElement({
                                        relativePosition: { x: f.w - 10, y: 5 },
                                    }).popupMenu;
                                    popupMenu.menuClickPopupMenu({
                                        menuPath: ["medium"]
                                    });
                                    
                                    function formatForComparison(str) {
                                        return str.replace(/[\*\(\)]/g, '').trim();
                                    }
                                    
                                    function findExactMatchIndex(array, target) {
                                        const formattedTarget = formatForComparison(target);
                                    
                                        for (let i = 0; i < array.length; i++) {
                                            const arrayString = Array.isArray(array[i]) ? array[i].join(' ') : array[i];
                                            if (formatForComparison(arrayString) === formattedTarget) {
                                                return i;
                                            }
                                        }
                                        return -1;
                                    }
                                    
                                    let newDisplayIndex = (findExactMatchIndex(displayPaths, currentDisplay) + 1) % displayPaths.length;
                                    
                                    for (let attempts = 0; attempts < displayPaths.length; attempts++) {
                                        try {
                                            sf.ui.proTools.selectedTrack.trackDisplaySelect({
                                                displayPath: displayPaths[newDisplayIndex],
                                                selectForAllSelectedTracks: true,
                                                useWildcards: true
                                            });
                                            break;
                                        } catch (error) {
                                            newDisplayIndex = (newDisplayIndex + 1) % displayPaths.length;
                                        }
                                    }
                                    

                                    note, that if your tracks have different sends, it will only use the first track to check for the availability of the send. Iterating through all the tracks is a more complex and resource intensive method.

                            2. S
                              Sreejesh Nair @Sreejesh_Nair
                                2024-01-09 15:04:48.400Z2024-01-09 18:33:05.957Z

                                Here is a code that will cycle through all the displaypaths we have including sends. If a particular send isnt found on one track using the logic that the send button value of the first track of the selected tracks will not be equal to unassigned, it will check the rest of the tracks in the selection to see if this is the case and then show all the send lanes on the available tracks. It uses the globalState method to store the last value of the displayPaths index it used. Hope it is useful.

                                const combinedPaths = ["waveform", "mute", "volume", "volume trim", '*(snd a)*', '*(snd b)*', '*(snd c)*', '*(snd d)*', '*(snd e)*', '*(snd f)*', '*(snd g)*', '*(snd h)*', '*(snd i)*', '*(snd j)*'];
                                
                                // Function to determine if the current path is a send lane
                                function isSendLane(path) {
                                    return path.includes('*(snd ');
                                }
                                
                                const originalTrackNames = sf.ui.proTools.selectedTrackNames;
                                sf.ui.proTools.appActivateMainWindow();
                                
                                // Get the current index from global state, defaulting to 0 if not set
                                let currentIndex = globalState.combinedPathIndex !== undefined ? globalState.combinedPathIndex : 0;
                                let pathDisplayed = false;
                                let sendButtonIndex = globalState.sendButtonValue !== undefined ? globalState.sendButtonValue : 0;
                                let sendLaneCheckDone = false;
                                
                                while (!pathDisplayed && originalTrackNames.length > 0) {
                                    const currentPath = combinedPaths[currentIndex];
                                
                                    if (isSendLane(currentPath)) {
                                        let trackFound = false;
                                        
                                        for (let i = 0; i < originalTrackNames.length; i++) {
                                            const trackNameToCheck = originalTrackNames.slice(i);
                                            try {
                                                var sendButtonValue = sf.ui.proTools.trackGetByName({ name: trackNameToCheck[0] }).track.sendButtons[sendButtonIndex].value.value;
                                                if (sendButtonValue !== "unassigned") {
                                                    sf.ui.proTools.trackSelectByName({ names: trackNameToCheck });
                                                    sf.ui.proTools.selectedTrack.trackDisplaySelect({
                                                        displayPath: [currentPath, "level"],
                                                        selectForAllSelectedTracks: true,
                                                        useWildcards: true
                                                    });
                                                    pathDisplayed = true;
                                                    trackFound = true;
                                                    break; // Exit the for loop if a valid send button is found
                                                }
                                            } catch (error) {
                                                // Handle error or continue to the next track
                                            }
                                        }
                                
                                        if (!trackFound) {
                                            sendButtonIndex = (sendButtonIndex + 1) % 9; // Increment sendButtonIndex if no track with the send is found
                                        } else {
                                            sendButtonIndex = (sendButtonIndex + 1) % 9; // Increment sendButtonIndex for the next run after a valid send is found
                                        }
                                    } else {
                                        // Not a send lane
                                        sf.ui.proTools.trackSelectByName({ names: originalTrackNames });
                                        sf.ui.proTools.selectedTrack.trackDisplaySelect({
                                            displayPath: [currentPath],
                                            selectForAllSelectedTracks: true,
                                            useWildcards: true
                                        });
                                        pathDisplayed = true;
                                        sendButtonIndex = 0; // Reset sendButtonIndex if not a send lane
                                    }
                                
                                    if (!pathDisplayed || pathDisplayed) {
                                        currentIndex = (currentIndex + 1) % combinedPaths.length;
                                    }
                                }
                                
                                
                                // Update global state for the next run
                                globalState.sendButtonValue = sendButtonIndex; // Increment or reset sendButtonIndex
                                globalState.combinedPathIndex = currentIndex;
                                sf.ui.proTools.trackSelectByName({ names: originalTrackNames });