i just finished taking an online mix class presented by Philip Weinrobe and his use of Sound Flow to setup and route folders for mix prep in Pro Tools inspired me to purchase Sound Flow! i'm not super great at coding or anything but i think i could figure it out. Was hoping there were already similar commands set up that i could implement. selected tracks into routing folders with output pre-routed, etc.
Any help pointing me in the right direction would be much appreciated. thanks in advance.
Chad Wahlbrink @Chad2025-12-10 02:12:54.478ZHi, @Nate_Pyfer!
We love @Philip_weinrobe. He's the best. I'm so glad you did his School of Song class! I did the same class a few years back, and it was life-changing.
Okay, diving into Mix Prep can be one of the more complicated things to tackle when you are first getting into SoundFlow, but it's super fun and rewarding as well!
The script Phil shows off in class uses code from this forum and the Store. Specifically, you'll want to check out @raphaelsepulveda's great package called Raphael Sepulveda Utilities and @Forrester_Savell's great Improved Strip Silence Package.
The process I would suggest:
- Go through a session, organize tracks by "food groups" (BASS, DRUMS, GTR...).
- Color each food group according to your desires.
- Create a group for each food group.
- Save a track preset for each food group, recalling only the track color and group.
This will allow you to use Raphael's excellent command "RS_Recall Track Preset" for each food group track preset for selected tracks:
You can add these commands to a deck if you'd like, or assign Keyboard Triggers, whatever you fancy.
I typically run the Improved Strip Silence script around this point and strip silence all the audio tracks in the session.
Next, I would create some macros like the following:
"RS_Move to New Folder" > "RS_Route Selected Tracks to Output"
You can add these commands to your deck and apply them manually, OR you can copy the command IDs and group names to the script shared here:
This script will "automate" the session prep process, like you probably saw in Phil's class.
Here's a video walking through these steps:
- NNate Pyfer @Nate_Pyfer
Dude….. wow man. Thank you! Sincerely. I hope others that are coming here from Philips classes will see this. Incredibly thorough walkthrough! 🙏🏻
Chad Wahlbrink @Chad2025-12-10 03:11:15.560ZHappy to help!!
- DIn reply toNate_Pyfer⬆:Dion Stewart @Dion_Stewart
I recently signed up for SoundFlow. One of the main reasons was to automate session prep.
@Chad - that video was exactly what I needed to get started. I'm using a mix template based on Michael Brauer's mix template and all the audio tracks go into routing folders. Automating placement of tracks inside those folders is a huge time saver. Thank you!
And @Nate_Pyfer - thanks for asking the question.
Great stuff! Cheers
- DIn reply toNate_Pyfer⬆:Dion Stewart @Dion_Stewart
@Chad & @Nate_Pyfer Hi guys.
I made some modifications to the script @Chad references in the video above. I think it's a bit easier to use now. There's a single table at the top of the script that has the group name and the command ID of the command you want to run for that group. That's the only part of the code you need to change.
I'm just getting my feet wet here and these are small changes, but I thought I'd share them. I'd welcome your feedback.
Thanks
// Put All Names of Groups you want to run a command for in the object lookup table below // group name, colon, then Command ID followed by a comma as in the examples below const groupCommands = { "KICK PREP": "user:cmk5x7hxy0001iy691ovc25a0:cmk7m1z0d0001mc6902aalqwd", "SN PREP": "user:cmk5x7hxy0001iy691ovc25a0:cmk7rvl6d000dd369anewukxe", }; // You should not have to touch anything below this line let groupNamesFromTable = Object.keys(groupCommands), groupsInSessionToProcess = getTrackGroups().filter(group => groupNamesFromTable.includes(group.name)); if (groupsInSessionToProcess.length == 0) { throw "No Matching Groups Were Found In Session"; } groupsInSessionToProcess.forEach(group => { try { // select the tracks in the group and run the command on the selected tracks selectTracksInGroup(group); sf.soundflow.runCommand({ commandId: groupCommands[group.name] }); } catch (err) { throw err; } }) ///////////////////////////////////////////////////////////////////////////////////////////////////// // Function - Get Track Group Props function getTrackGroups() { // Declare Group List const groupList = sf.ui.proTools.mainWindow.tables.whoseTitle.is("Group List").first; const groupRows = groupList.childrenByRole("AXRow"); sf.ui.proTools.groupsEnsureGroupListIsVisible(); // Map Groups const groupsList = groupRows.allItems.map(group => { const groupSelectionBtn = group.childrenByRole('AXCell').first.buttons.first; const groupTitleBtnName = group.childrenByRole('AXCell').allItems[1].buttons.first.value.invalidate().value; return { name: groupTitleBtnName.split(' - ')[1], groupID: groupTitleBtnName.split(' - ')[0].split(' ').slice(-1)[0], groupSelectionBtn: groupSelectionBtn, } }); return groupsList; }; ///////////////////////////////////////////////////////////////////////////////////////////////////// function selectTracksInGroup(group) { // Collect session group information let currentGroupsInSession = getTrackGroups(); // Showing all tracks to make sure the tracks in the group are ready to be processed sf.ui.proTools.mainWindow.trackListPopupButton.popupMenuSelect({ menuPath: ["Show All Tracks"] }); // Find the desired group and select its tracks currentGroupsInSession.find(sessionGroup => sessionGroup.name === group.name).groupSelectionBtn.elementClick(); let selectedTracks = sf.ui.proTools.selectedTracks; // Ensure those tracks are visible so you can start processing them. ensureAnAudioTrackIsSelectedAndVisible(selectedTracks); // Uncomment if you want to restore previously shown tracks sf.ui.proTools.mainWindow.trackListPopupButton.popupMenuSelect({ menuPath: ["Restore Previously Shown Tracks"] }); } /// Borrowing from Raphael Sepulveda https://forum.soundflow.org/-6034#post-9 to make sure tracks are in view /** * @param {object} arg * @param {AxPtTrackHeader[]} [arg.tracks] - Optional. If provided gets top most from those tracks. */ function determineTopMostEditTrack({ tracks } = {}) { function getTopMostTrack(tracks) { return tracks.filter(h => h.frame.y >= editTimeLineTopY)[0]; } sf.ui.proTools.mainWindow.invalidate(); const editTimeLineTopY = sf.ui.proTools.mainWindow.timelineFocusButton.frame.y; return getTopMostTrack(tracks || sf.ui.proTools.visibleTrackHeaders); }; /** @param {AxPtTrackHeader} track */ function isAudioTrack(track) { return track.title.value.endsWith('Audio Track '); } function ensureAnAudioTrackIsSelectedAndVisible(audioTracks) { if (!sf.ui.proTools.selectedTrackCount || !isAudioTrack(sf.ui.proTools.selectedTrack)) { determineTopMostEditTrack({ tracks: audioTracks }).trackScrollToView(); } else { sf.ui.proTools.selectedTrack.trackScrollToView(); } }