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.
Linked from:
- Chris Shaw @Chris_Shaw2022-11-14 18:19:02.526Z
This should do it.
Just change thedisplayPath1
anddisplayPath2
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 })
- In reply toBergatron_Music⬆:Bergatron Music @Bergatron_Music
Wow! Thank you Chris!
- RIn reply toBergatron_Music⬆:Renan Carvalho @Renan_Carvalho
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 = muteChris Shaw @Chris_Shaw2023-05-11 15:55:19.062Z
this will cycle through whichever displays you want instead of every menu item.
Just add/delete the views you want in thedisplayPaths
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 });
- DDan Powe @Dan_Powe6
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 });
- SIn reply toBergatron_Music⬆:Sreejesh Nair @Sreejesh_Nair
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 });
- DDan Powe @Dan_Powe6
That's absolutely fantastic, Sreejesh! Thanks so much for your help and explanation 😊
- SSreejesh Nair @Sreejesh_Nair
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 }); }
Dav3 @D_av_3
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']];
- SSreejesh Nair @Sreejesh_Nair
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.
- SIn reply toBergatron_Music⬆:Sreejesh Nair @Sreejesh_Nair
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 });