How to loop through all selected tracks and repeat an action for each track
Here's a script that showcases how to loop through all the selected tracks, selecting each of them in turn and performing an action for every selected track, and then finally restoring the selection when done.
function doForAllSelectedTracks(action) {
var originallySelectedTrackNames = sf.ui.proTools.selectedTrackNames;
try {
sf.ui.proTools.selectedTrackHeaders.forEach(track => {
track.trackSelect();
action(track);
});
}
finally {
sf.ui.proTools.trackSelectByName({ names: originallySelectedTrackNames });
}
}
/**@param {AxPtTrackHeader} track */
function trackFunc(track) {
//Insert your code here
log(track.normalizedTrackName);
}
doForAllSelectedTracks(trackFunc);
Linked from:
- Chris Shaw @Chris_Shaw2020-11-01 00:36:00.849Z
Just out of curiosity, is there an advantage to this method?
I've been using this:var originallySelectedTrackNames = sf.ui.proTools.selectedTrackNames.slice() for (var i in originallySelectedTrackNames) { sf.ui.proTools.trackSelectByName({ names: [originallySelectedTrackNames[i]], }); //Your Code Here log(originallySelectedTrackNames[i]) } sf.ui.proTools.trackSelectByName({ names: originallySelectedTrackNames });
- Christian Scheuer @chrscheuer2020-11-01 12:34:25.101Z
The try/finally block that I'm using ensures that even if an error occurs, the original track will be selected again.
It also has the benefit of extracting the logic of actually enumerating through the array into a function, which cleans up the code and makes it more reusable.
It's generally a best practice to try to extract logic out into separate, reusable functions. This is how SoundFlow is coded internally as well. Reusing code ensures bug fixes can be applied across all uses of that code. It also helps make complex scripts more readable and maintainable, overall reducing the number of bugs that get created in the first place.- Christian Scheuer @chrscheuer2020-11-01 12:39:52.959Z
An important concept that we use in software design is that it's vital to "encapsulate complexity". In this case, the complexity is that you're just wanting to enumerate through some tracks, but you don't want that temporary state change (the difference in track selection) to leak out of that function. By wrapping it up in a function, we've encapsulated the complexity of dealing with any errors occurring while the temp state is in place.
Any callers of the function will now not have to deal with the complexity - they can just deal with what they were trying to do.
This concept is hugely important when writing complex apps. Always try to reduce complexity by encapsulating it in functions with small API surfaces.- Christian Scheuer @chrscheuer2020-11-01 12:45:26.512Z
Another thing I'd say about your code, is that looping with for..in over an array is not desirable.
See this quote from Mozillas Javascript documentation:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...inIf you do want to use a for loop on an array, instead use a for...of loop.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
- In reply tochrscheuer⬆:Chris Shaw @Chris_Shaw2020-11-01 15:22:18.360Z
Fantastic. Thanks for the thorough explanation. I’m learning this as I go and have been revisiting older scripts I’ve written to clean them up. That being said, using
for
loops is easier for me to read. I just have get used to doing it this way.- Christian Scheuer @chrscheuer2020-11-01 15:54:20.365Z
You can still use a for loop, but use for...of instead of for...in (for of also has the benefit of directly giving you the value), or use a regular for loop with indices.
- Christian Scheuer @chrscheuer2020-11-01 15:55:35.845Z
The for of loop would look like this:
var originallySelectedTrackNames = sf.ui.proTools.selectedTrackNames.slice(); for (var name of originallySelectedTrackNames) { sf.ui.proTools.trackSelectByName({ names: [name], }); //Your Code Here log(name); } sf.ui.proTools.trackSelectByName({ names: originallySelectedTrackNames });
Note that this doesn't cover the try/finally case, but just shows the specific way to use for...of instead of for...in
But yea, I'd still recommend the method I used in the original post, as it down the line will help make your scripts more stable.
- AIn reply tochrscheuer⬆:Alex Oldroyd @Alex_Oldroyd8
Hi Christian
How would you go about converting this to Logic Pro X?
Thanks
Alex
- Christian Scheuer @chrscheuer2022-04-09 18:17:01.082Z
Hi Alex,
Unfortunately, I don't know much about how Logic operates. It would be better to ask that as a new question in the How to section :)
- In reply tochrscheuer⬆:Mitch Willard @Mitch_Willard_the2nd
Hi @chrscheuer ,
When I use this code in other scripts, it seems to skip the 2nd selected track then apply the code to the next following track after the last selected.
eg.
If the selected tracks are:
Audio 1
Audio 2
Audio 3The action will apply the code either to:
Audio 1
Audio 3
Audio 4or in some cases:
Audio 1 twice
Audio 3Is it something I'm missing? or messing up? Here is just a basic code which is giving me the issue when applying, as oppose to sharing the entire code, which I can if need be.
function doForAllSelectedTracks(action) { /// Get Selected track names to variable globalState.selectedTracks = sf.ui.proTools.selectedTrackNames try { sf.ui.proTools.selectedTrackHeaders.forEach(track => { track.trackSelect(); action(track); }); } finally { sf.ui.proTools.trackSelectByName({ names: globalState.selectedTracks }); } }; /* bounce loop */ function bounceFunc() { sf.ui.proTools.menuClick({ menuPath: ['Edit', 'Consolidate Clip'], }) }; doForAllSelectedTracks(bounceFunc);
- Christian Scheuer @chrscheuer2022-04-29 09:47:11.263Z
Hi Mitch,
On the surface, this looks correct. Could you open a new issue with bit.ly/sfscripthelp so we can track this separately?
- Mitch Willard @Mitch_Willard
Will do @chrscheuer . Thanks
- TIn reply tochrscheuer⬆:Thomas Gloor @Thomas_Gloor
Hey @chrscheuer
Thank you for this. One little question. Would it be possible to have it work for every selected track EXCEPT the first one?Thanks!