Bounce in Batch? Multiple Sessions?
Hello. Is there a way to use SF with the Finder to select a folder of PTX files one by one, open them, run the bounce to disk, etc? So you can then let an album's worth of songs bounce and walk away?
Linked from:
- Brett Ryan Stewart @Brett_Ryan_Stewart
just giving this one a bump :-)
- Chad Wahlbrink @Chad2022-01-10 16:25:12.995Z
@Brett_Ryan_Stewart - I have a macro in Keyboard Maestro that does just this! I am planning to convert this to Soundflow in the next week or so since I've just recently started truly understanding what I'm doing with javascripting. I believe it will be possible!
If someone beats me to it, that's okay too. <3
- Brett Ryan Stewart @Brett_Ryan_Stewart
That's awesome man! Will you be posting it on the store or in forums? I'd love to find out when you've got it working :-)
- In reply toBrett_Ryan_Stewart⬆:Chad Wahlbrink @Chad2022-01-10 19:22:55.764Z2022-01-11 23:39:53.882Z
Hey @Brett_Ryan_Stewart !
This script should do the job for you. This script is also on the store in my "CW Pro Tools Utilities" package.
Here's a video demo:
This script will open, bounce, and close a list of Pro Tools Session files from Finder. Simply select some PTX files (or Aliases) in Finder and run the macro. I suggest using alias files to bounce files from multiple projects. You can hold option and command while dragging PTX files (⌥⌘ + drag) to drag and drop files from multiple projects into a singular "Print" folder.
LIMITATIONS
- This script uses the session's name as the basis for naming your bounces. So you can name your project "Project Name MIX01_DRUMS", "Project Name MIX01_BASS", etc.
- Currently, this script uses the bounce/edit selection saved with the session. I could add an option to "recall memory location" with the selection at some point.
- Currently, this script will fail if you open a session with missing plug-ins or I/O. I can add this functionality and better error handling later.
- Currently, this script will use your most recent bounce settings. You can do a quick test bounce before saving your sessions if you need to change the bounce source, sample rate, destination, etc. I can add specific bounce setting functionality later.
// CW PT Bounce Multiple Pro Tools Sessions from Finder (Stems Bounce) // This script will open, bounce, and close a list of Pro Tools Session files from Finder. Simply select some PTX files (or Aliases) in Finder and run the macro. // I suggest using alias files to bounce files from multiple projects. // You can hold option and command while dragging PTX files (⌥⌘ + drag) to drag and drop files from multiple projects into a singular "Print" folder. // LIMITATIONS // - This script uses the session's name as the basis for naming your bounces. So you can name your project "Project Name MIX01_DRUMS", "Project Name MIX01_BASS", etc. // - Currently, this script uses the bounce/edit selection saved with the session. I could add an option to "recall memory location" with the selection at some point. // - Currently, this script will fail if you open a session with missing plug-ins or I/O. I can add this functionality and better error handling later. // - Currently, this script will use your most recent bounce settings. You can do a quick test bounce before saving your sessions // if you need to change the bounce source, sample rate, destination, etc. I can add specific bounce setting functionality later. // The way all good stories start sf.ui.finder.appActivateMainWindow(); sf.ui.finder.mainWindow.invalidate(); // Declare Variables for selected files const selectedFiles = sf.ui.finder.selectedPaths; // Declare more variables const dontSave = sf.ui.proTools.confirmationDialog.buttons.whoseTitle.is("Don't Save"); const bounceWin = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix"); const sessionOpen = sf.ui.proTools.invalidate().getMenuItem("File", "Close Session"); // Bounce Function function bounceFile(sessionFileName) { // Bounce Mix sf.ui.proTools.menuClick({ menuPath: ["File", "Bounce Mix..."], }); bounceWin.first.elementWaitFor(); // Place Text bounceWin.first.textFields.first.elementSetTextFieldWithAreaValue({ value: sessionFileName }); // Hit Bounce bounceWin.first.buttons.whoseTitle.is("Bounce").first.elementClick(); // Wait for Overwrite or bounce window to leave while (true) { if (!sf.ui.proTools.confirmationDialog.buttons.whoseTitle.is("Yes").first.exists && bounceWin.exists) { } else { break; } sf.wait({ intervalMs: 200 }) } if (sf.ui.proTools.confirmationDialog.buttons.whoseTitle.is("Yes").first.exists) { sf.ui.proTools.confirmationDialog.buttons.whoseTitle.is("Yes").first.elementClick(); } // Wait for bounce progress bar sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: "Appear", }); sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: 'Disappear', timeout: -1 }); } // The Print function function printStems(item) { // Open the first file sf.file.open({ path: item, applicationPath: "/Applications/Pro Tools.app", }); // Wait for ProTools sf.ui.proTools.appWaitForActive(); // Don't Save if (dontSave.invalidate().first.exists) { dontSave.invalidate().first.elementClick(); sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: "Disappear" }); } // Wait for Main Window while (!sf.ui.proTools.invalidate().hasValidUI) { sf.wait({ intervalMs: 200 }) } while (true) { if (!sf.ui.proTools.getMenuItem('File', 'Close Session').isEnabled && !sf.ui.proTools.confirmationDialog.exists) { } else { break; } sf.wait({ intervalMs: 500 }) } // Declare variables for current session name let sessionPath = sf.ui.proTools.mainWindow.sessionPath; let sessionFileName = sessionPath.split('/').slice(-1)[0]; let sessionTrim = sessionFileName.slice(0, sessionFileName.length - 4); bounceFile(sessionTrim); } // Run for each selected file in Finder selectedFiles.forEach(printStems); // Close last session and open finder sf.ui.proTools.menuClick({ menuPath: ["File", "Close Session"], }); dontSave.first.elementWaitFor({ waitType: "Appear", }); if (dontSave.invalidate().exists == true) { dontSave.invalidate().first.elementClick(); dontSave.invalidate().first.elementWaitFor({ waitType: "Disappear", }); sf.ui.proTools.mainWindow.elementWaitFor({ waitType: "Disappear", }); } // Activate Finder sf.ui.finder.appActivateMainWindow(); sf.ui.finder.mainWindow.invalidate(); // Notify Complete log("Your files have printed!");
Let me know if you run into any issues with this!
- Chad Wahlbrink @Chad2022-01-10 19:32:19.407Z
@Brett_Ryan_Stewart - also, note that pro tools needs to be open currently! Working on that aspect!
- Chad Wahlbrink @Chad2022-01-10 21:42:47.513Z
Scratch that - the script should now handle opening files with Pro Tools closed the same way it would if a session is already open. The original script is updated both here and in my package in the store.
- Chad Wahlbrink @Chad2022-01-10 21:40:41.913Z
Thanks, @Kitch! I'm standing on the shoulders of you, Christian, Chris Shaw, Andrew Scheps, and so many others on this forum. I've learned so much from you all digging on here lately. Thanks for all you do.
- Brett Ryan Stewart @Brett_Ryan_Stewart
@chadwahlbrink you are a badass! This is gonna be sooo helpful.
I'm having luck with it about half of the time. The other half I'm getting this error: Object reference not set to an instance of an objectNot sure what that means. Weird thing is, sometimes it works, sometimes it doesn't, with nothing else changing on my system UNLESS there is some background programs that might be interfering?
I've used bounce butler in the past and it would warn you to quit other apps. - In reply toChad⬆:Brett Ryan Stewart @Brett_Ryan_Stewart
Hey again @chadwahlbrink
So I can't be 100% but I tested it out a bunch of times, and it SEEMS like the only time I got that error i mentioned above was when I had a bunch of other apps open AND/OR when I was getting notifications on the Messages Mac app.
If that error is even indicitive that this could be the issue, I wonder if there's an optional prompt that could be added to the script that activates the Do Not Disturb function on Mac OS?- Chad Wahlbrink @Chad2022-01-11 00:31:06.238Z
Hey @Brett_Ryan_Stewart!
I'm working on that error.
The problem is hitting when waiting for pro tools to open the next session! I'll get it sorted. Still fairly new at JavaScript and hadn't used a while loop before.
// Wait for ProTools while (true) { if (sf.ui.proTools.mainWindow.exists != true) { } else { break; } sf.wait({ intervalMs: 500 }) }
- Brett Ryan Stewart @Brett_Ryan_Stewart
we're all rootin for you! Thanks for putting in the time, Chad!
- Chad Wahlbrink @Chad2022-01-11 03:41:19.785Z
Hey @Brett_Ryan_Stewart! Thanks for bearing with me. The script is updated above and on the store.
I believe the script should be more stable now! However, it only works correctly if Pro Tools is already open. I kept getting errors every time I tried to add code to handle waiting for Pro Tools to initially open. I'm sure @Kitch or others on here could help me flesh that bit out at some point!
Let me know if you still have issues with this as-is, though!
- Brett Ryan Stewart @Brett_Ryan_Stewart
@chadwahlbrink fine work dude! So far it seems to work consistently! I'll let you know if I hit any snags. Only thing I noticed thats different from the last script is that it doesn't automatically overwrite bounces with the same name like it did last go around. It prompts to ask if I want to overwrite.
But if that needed to be taken out to make it run smoothly that's a welcomed compromise :-) Thanks again man!- Chad Wahlbrink @Chad2022-01-11 19:11:44.683Z
Hey @Brett_Ryan_Stewart! I've got another update in the script above + on the store ✨✨
I FINALLY figured out a way to have the script work consistently if Pro Tools is closed when you launch the script from Finder. I also made it wait a bit longer for the "overwrite files" dialog. Please let me know if the script is breaking at that point for you!
- Brett Ryan Stewart @Brett_Ryan_Stewart
Thanks @chadwahlbrink !
Sadly this new script doesn't seem to work for me. I'm getting 2 different errors depending on how I run it.If I run it without PT already launched, I get this message:
TypeError: Cannot read property 'split' of null (CW PT Bounce Multiple Pro Tools Sessions from (and that all of the error I can see in that little notification that pops)If I run after PT is launched:
Element was not found or removed after waiting 2000ms (CW PT Bounce Multiple Pro Tools Sessions fromShould I mention I'm in Big Sur? Does that matter?
- Chad Wahlbrink @Chad2022-01-11 23:43:25.755Z
Man, you are right @Brett_Ryan_Stewart! I was initially testing on a very basic session with basically no plug-ins, which was working because the bounces were moving fast enough. However, testing with longer bounces, the script was kind of breaking down.
I updated the script & store it once more. It seems to be working for me now, but I'm hesitant to make promises! 🤪
If you have a chance to try this one out, I'd love to hear if it works. I'm kind of at my limit of time I can put into this script for the week, but if it's still not functional, I'd love to get @Kitch or @chrscheuer to give it a try.
Thanks all ✨✨✨
- Chad Wahlbrink @Chad2022-01-11 23:47:51.779Z
Also, @Brett_Ryan_Stewart - sometimes checking out the Log by hitting the "Log" button or using ⌃C in Soundflow can give more info on errors! Mainly, it can tell us what line of the code is failing.
- Brett Ryan Stewart @Brett_Ryan_Stewart
@chadwahlbrink Still no dice. Getting this in the log:
I tried it several times with PT still open, and the last time I quit PT to start it from scratch. Strangely it was working better in the earlier versions.
Just to clarify, other than paste the new code in to my existing command (or updating in the package) is there anything else I'd need to reboot or anything?Thanks for all you've put in to this man. I'm happpy to keep testing it as long as needed!
- In reply toChad⬆:Kitch Membery @Kitch2022-01-12 00:40:33.073Z
Super busy week behind the scenes here, but If I have some time over the weekend I'll take it for a spin. :-)
Rock on!
- Brett Ryan Stewart @Brett_Ryan_Stewart
appreciate all you do @Kitch !
- Chad Wahlbrink @Chad2022-01-12 02:13:03.663Z
Ah, so strange @Brett_Ryan_Stewart! I would love to get this fixed. I'll be on other projects the next few days, but I'm posting the OG script (with one small addition) below just in case that actually works better for you. No pressure to keep trying these different versions, but it's definitely helpful to have the feedback on what is working on other systems.
Honestly, this original script is working really well with this "dummy" session I've been trying on my laptop just now. It's possible I tried to get too fancy when I was trying to get it not to crash for the instance of launching the script while Pro Tools being closed.
This is the OG script with only this small section added at line 71 - which was the real break through for making it work:
while (!sf.ui.proTools.invalidate().hasValidUI) { sf.wait({ intervalMs: 200 }) }
Here's the script ✨
// CW PT Bounce Multiple Pro Tools Sessions from Finder (Stems Bounce) // This script will open, bounce, and close a list of Pro Tools Session files from Finder. Simply select some PTX files (or Aliases) in Finder and run the macro. // I suggest using alias files to bounce files from multiple projects. // You can hold option and command while dragging PTX files (⌥⌘ + drag) to drag and drop files from multiple projects into a singular "Print" folder. // LIMITATIONS // - This script uses the session's name as the basis for naming your bounces. So you can name your project "Project Name MIX01_DRUMS", "Project Name MIX01_BASS", etc. // - Currently, this script uses the bounce/edit selection saved with the session. I could add an option to "recall memory location" with the selection at some point. // - Currently, this script will fail if you open a session with missing plug-ins or I/O. I can add this functionality and better error handling later. // - Currently, this script will use your most recent bounce settings. You can do a quick test bounce before saving your sessions // if you need to change the bounce source, sample rate, destination, etc. I can add specific bounce setting functionality later. // The way all good stories start sf.ui.finder.appActivateMainWindow(); sf.ui.finder.mainWindow.invalidate(); // Declare Variables for selected files const selectedFiles = sf.ui.finder.selectedPaths; // Declare more variables var dontSave = sf.ui.proTools.windows.first.buttons.whoseTitle.is("Don't Save").first; var bounceWin = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix"); var bounceProgress = sf.ui.proTools.children.whoseRole.is("AXMenuBar").whoseTitle.is("").first.children.whoseRole.is("AXMenuBarItem").first; // Bounce Function function bounceFile(sessionFileName) { // Bounce SFX Stem sf.ui.proTools.menuClick({ menuPath: ["File", "Bounce Mix..."], }); bounceWin.first.elementWaitFor(); bounceWin.first.textFields.first.elementSetTextFieldWithAreaValue({ value: sessionFileName }); bounceWin.first.buttons.whoseTitle.is("Bounce").first.elementClick(); if (sf.ui.proTools.confirmationDialog.buttons.whoseTitle.is("Yes").first.exists) { sf.ui.proTools.confirmationDialog.buttons.whoseTitle.is("Yes").first.elementClick(); } bounceWin.first.elementWaitFor({ waitType: "Disappear", timeout: -1 }); sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: 'Disappear', timeout: -1 }); } // The Print function function printStems(item) { // Open the first file sf.file.open({ path: item, applicationPath: "/Applications/Pro Tools.app", }); // Wait for ProTools sf.ui.proTools.appWaitForActive(); // Don't Save dontSave.elementWaitFor({ waitType: "Appear", failIfNotFound: false }); // If a session is currently open and there is confirmation Dialog, hit don't save if (dontSave.exists == true) { dontSave.elementClick(); dontSave.elementWaitFor({ waitType: "Disappear", }); } // Wait for Main Window while (!sf.ui.proTools.invalidate().hasValidUI) { sf.wait({ intervalMs: 200 }) } sf.ui.proTools.mainWindow.elementWaitFor({ waitType: 'Appear', timeout: -1}); // Declare variables for current session name const sessionPath = sf.ui.proTools.mainWindow.sessionPath; const sessionFileName = sessionPath.split('/').slice(-1)[0]; var sessionTrim = sessionFileName.slice(0, sessionFileName.length - 4); bounceFile(sessionTrim); } // Run for each selected file in Finder selectedFiles.forEach(printStems); // Close last session and open finder sf.ui.proTools.menuClick({ menuPath: ["File", "Close Session"], }); dontSave.elementWaitFor({ waitType: "Appear", }); if (dontSave.exists == true) { dontSave.elementClick(); dontSave.elementWaitFor({ waitType: "Disappear", }); sf.ui.proTools.mainWindow.elementWaitFor({ waitType: "Disappear", }); } // Activate Finder sf.ui.finder.appActivateMainWindow(); sf.ui.finder.mainWindow.invalidate(); // Notify Complete log("Your files have printed!");
- Brett Ryan Stewart @Brett_Ryan_Stewart
Bravo dude. I think this one is much closer to a fully functioning script.
It ran MOST of the time without a hitch.
I got one error one time about an element missing, but then I ran it again (using 3 test sessions with very little tracks and only selecting about 30 seconds of the track to print) and it worked fine.One thing I noticed that was a funny little hiccup: I hit cancel on the session load window after hitting bounce command and then I couldn't get the command to work at all until i restarted SF.
And the other thing that i believe you're already aware is that it got hung up on the load window about missing busses. I used @Christopher_Barnett 's Barnett Public Shortcuts "Detailed Reports" killer when loading the sessions that had that prompt and that message never came back and the bounce script ran fine after that. Thanks Christopher!
So I'd say park it for now. It's mostly functional and awesome so thank you, and lmk when you're ready to try out more stuff. I'm here for it!
- Chad Wahlbrink @Chad2022-01-12 04:56:16.243Z
That's great, man! Glad it's mostly working.
Yes, I'd say this script will inevitably grow and get more usable with time! I appreciate you testing it for me.
I had dialed in a keyboard maestro version of this that has bounced hundreds and hundreds of stems. But I'd love to get this script to be just as useable!
I'll look into adding options to kill missing I/O and missing plugins pop-ups on load. A few other ideas as well. If you install the package I have in store, I'll make sure to keep that up to date.
Finally, yes, sometimes you have to reset the Soundflow UI or cancel a script to get it to fire again. There are some built-in Soundflow commands to reset the Soundflow engine, which can come in handy!
- Brett Ryan Stewart @Brett_Ryan_Stewart
2 last questions since you mentioned:
Is there a ways to have SF auto update any installed packages? I was looking for that option.
and secondly, where are these SF Engine reset commands? That'd be great!- Chad Wahlbrink @Chad2022-01-12 05:07:39.613Z
↑ These are the system commands! They are in the Soundflow System Folder from the built-in actions.
↑ There is no "auto-update" (yet), but you can always check for available updates by clicking "show available updates" in the store.
- Brett Ryan Stewart @Brett_Ryan_Stewart
ah super helpful! Thanks a milion @chadwahlbrink
- In reply toChad⬆:Kitch Membery @Kitch2022-01-12 19:39:12.852Z
Hi @chadwahlbrink,
Below is a reusable function I created for dismissing dialog windows;
function dismissDialog({ dialogText, buttonName }) { const dlg = sf.ui.proTools.confirmationDialog; //Wait 100ms for dialog box to appear dlg.elementWaitFor({ timeout: 100, pollingInterval: 10, onError: "Continue", }); if ( dlg.children.whoseRole.is("AXStaticText").whoseValue.contains(dialogText) .first.exists ) { dlg.buttons.whoseTitle.is(buttonName).first.elementClick(); dlg.elementWaitFor({ waitType: "Disappear", timeout: 100, pollingInterval: 10, onError: "Continue", }); } }
When calling the function use a function call as follows;
dismissDialog({ dialogText: "Exact Text that appears in the Dialog goes here", buttonName: "The name of the button you want to press to dismiss the dialog", });
You can call the function multiple times, just update the
dialogText
&buttonName
properties in each, and have them in the order that the dialog windows appear :-)I've not had a chance to fully test it but it should work. Let me know if it does not.
Rock on!
- Chad Wahlbrink @Chad2022-01-13 18:10:05.444Z
Kitch! That's brilliant. Thanks for sharing that bit of code. Hopefully, I can implement the "dismiss I/O" and "dismiss missing plugins" dialogs over the weekend!
Thanks 🏄♂️✨
- Brett Ryan Stewart @Brett_Ryan_Stewart
Hey @chadwahlbrink I see there's an update 1.0.29. Does this include Kitch's code? Ready for me to test drive it?
- In reply toChad⬆:Kitch Membery @Kitch2022-01-16 03:30:54.124Z
Hi @chadwahlbrink & @Brett_Ryan_Stewart
Did a little refactor for you, let me know how it runs? Have not really tested it thoroughly though, so fingers crossed.
Rock on!
/** * @param {Object} obj * @param {string} obj.dialogText * @param {string} obj.buttonName */ function dismissDialog({ dialogText, buttonName }) { const dlg = sf.ui.proTools.confirmationDialog; //Wait 100ms for dialog box to appear dlg.elementWaitFor({ timeout: 100, pollingInterval: 10, onError: "Continue", }); if (dlg.children.whoseRole.is("AXStaticText").whoseValue.contains(dialogText).first.exists) { dlg.buttons.whoseTitle.is(buttonName).first.elementClick(); dlg.elementWaitFor({ waitType: "Disappear", timeout: 100, pollingInterval: 10, onError: "Continue", }); } } /** * @param {Object} obj * @param {"Skip All"|"Manually Find & Relink"|"Automatically Find & Relink"} obj.radioButton */ function dismissMissingFilesDialog({ radioButton }) { const dlg = sf.ui.proTools.windows.whoseTitle.is("Missing Files").first; //Wait for dialog box to appear dlg.elementWaitFor({ timeout: 300, pollingInterval: 10, onError: "Continue", }); if (dlg.exists) { dlg.radioButtons.whoseTitle.is(radioButton).first.elementClick(); dlg.buttons.whoseTitle.is("Ok").first.elementClick(); dlg.elementWaitFor({ waitType: "Disappear", timeout: 100, pollingInterval: 10, onError: "Continue", }); } } /** * @param {string} sessionName */ function bounceSession(sessionName) { const bounceWin = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix"); log(`Bounceing mix: ${sessionName}`); // Bounce SFX Stem sf.ui.proTools.menuClick({ menuPath: ["File", "Bounce Mix..."], }); bounceWin.first.elementWaitFor(); bounceWin.first.textFields.first.elementSetTextFieldWithAreaValue({ value: sessionName }); bounceWin.first.buttons.whoseTitle.is("Bounce").first.elementClick(); dismissDialog({ dialogText: "Do you want to replace it?", buttonName: "Yes", }); bounceWin.first.elementWaitFor({ waitType: "Disappear", timeout: -1 }); sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: 'Disappear', timeout: -1 }); log(`Finished bounceing mix: ${sessionName}`); closeProToolsSessionWithoutSaving(); } function closeProToolsSessionWithoutSaving() { sf.ui.proTools.menuClick({ menuPath: ["File", "Close Session"], }); dismissDialog({ dialogText: "Save changes to ", buttonName: "Don't Save", }); } function main() { sf.ui.finder.appActivate(); sf.ui.finder.mainWindow.invalidate(); // Filter selected finder paths for Pro Tools Sessions const selectedProToolsSessionPaths = sf.ui.finder.selectedPaths.filter(path => path.endsWith(".ptx")); // Bail out if no Pro Tools sessions are selected if (selectedProToolsSessionPaths.length <= 0) { alert(`No Pro Tools sessions were selected. Please select Pro Tools session(s) in the the finder and try again`); throw 0 } // Check to see if Pro Tools session is currently open const hasOpenSession = sf.ui.proTools.mainWindow.invalidate().sessionPath !== null; // If Pro Tools has a session open, ask to "Save" / "Don't Save" or "Cancel" if (hasOpenSession) { const toSaveSession = sf.interaction.displayDialog({ buttons: ["Save", "Don't Save", "Cancel"], defaultButton: "Cancel", cancelButton: "Cancel", title: "Proceed...", prompt: "There is currently a session open, would you like to save it?", }).button; switch (toSaveSession) { case "Save": //Save Session sf.ui.proTools.menuClick({ menuPath: ["File", "Save"] }); break case "Don't Save": closeProToolsSessionWithoutSaving(); break }; } // Run for each selected Session path in Finder selectedProToolsSessionPaths.forEach(sessionPath => { // Open protools session sf.file.open({ path: sessionPath, applicationPath: "/Applications/Pro Tools.app", }); // Wait for ProTools sf.ui.proTools.appWaitForActive(); // Dismiss missing files..."" dialog if it appears dismissMissingFilesDialog({ radioButton: "Skip All", }); sf.ui.proTools.appActivateMainWindow({ pollingInterval: 100, retryTimeout: 20000 }, `Session did not load`); // Extract the session Name let sessionName = sessionPath.split('/').slice(-1)[0].split('.').slice(0, -1).join("."); bounceSession(sessionName); }); sf.ui.finder.appActivateMainWindow(); alert("Your files have printed!"); } main();
- Chad Wahlbrink @Chad2022-01-16 14:34:40.873Z
This is rad, @Kitch ! You are saving me some work. I appreciate you taking the time to try it out and tinker with it.
I'll give this new code a try in the next few days.
@Brett_Stewart - I haven't implemented Kitch's code into the store version yet! The store version is still exactly as I had left it! I'm working on a robust Bounce Window template at the moment that I hope to merge with this script.
- Brett Ryan Stewart @Brett_Ryan_Stewart
hell yea @Kitch and @chadwahlbrink ! Thank you both. Look forward to being your guinea pig!
- Chad Wahlbrink @Chad2022-01-16 20:07:15.285Z2022-01-25 14:21:43.477Z
Hey @Kitch & @Brett_Ryan_Stewart!
First, I want to thank you again, @Kitch. This refactoring is BEAUTIFUL. Thank you for taking the time. The extra prompt to "save the session" or "close without saving" at the beginning of the script is a nice touch! I also love the extra "logging" of each printed file. How cool.
Here is a slightly tweaked version that I'm finding handles dismissing "missing I/O," "missing AAX plug-ins," and "missing file windows" well. Eventually, I'd like to make this a "template" style script that can allow you to choose whether to dismiss those alerts or stop the script.
I had to tweak and move some things around to get it to work, but I left the majority of the functions as you defined them, Kitch!
@Brett_Ryan_Stewart, this new version is published to my package in the store. I'm working on a new package that will likely adopt this script and add more functionality! I'll keep you in the loop on that progress.
(@Kitch, if you'd prefer me not to use your code for my store releases, that is understandable, and you can reach out to me regarding that!)
// CW PT Bounce Multiple Pro Tools Sessions from Finder (Stems Bounce) // This script will open, bounce, and close a list of Pro Tools Session files from Finder. Select some PTX files (or Aliases) in Finder and run the macro. // I suggest using alias files to bounce files from multiple projects. // You can hold option and command while dragging PTX files (⌥⌘ + drag) to drag and drop files from multiple projects into a singular "Print" folder. // LIMITATIONS // - This script uses the session's name as the basis for naming your bounces. So you can name your project "Project Name MIX01_DRUMS", "Project Name MIX01_BASS", etc. // - Currently, this script uses the bounce/edit selection saved with the session. I could add an option to "recall memory location" with the selection at some point. // - Currently, this script will automatically dismiss missing I/O, missing AAX plug-ins, and missing file windows. I can add the option to break the script if these errors are encountered. // - Currently, this script will use your most recent bounce settings. You can do a quick test bounce before saving your sessions // if you need to change the bounce source, sample rate, destination, etc. I can add specific bounce setting functionality later. // // Shout out to Kitch Membery for the beautiful refactoring help on this! THANK YOU, KITCH! // // Also, shout out to Bret Ryan Stewart for the nudge to get this going and also for being a beta tester. THANK YOU, Bret! // Functions // Function - Dismiss Dialog (Generic) /** * @param {Object} obj * @param {string} obj.dialogText * @param {string} obj.buttonName */ function dismissDialog({ dialogText, buttonName }) { const dlg = sf.ui.proTools.confirmationDialog; //Wait 100ms for dialog box to appear dlg.elementWaitFor({ timeout: 100, pollingInterval: 10, onError: "Continue", }); if (dlg.children.whoseRole.is("AXStaticText").whoseValue.contains(dialogText).first.exists) { dlg.buttons.whoseTitle.is(buttonName).first.elementClick(); dlg.elementWaitFor({ waitType: "Disappear", timeout: 100, pollingInterval: 10, onError: "Continue", }); } } // Function - Dismiss Missing Files Dialog /** * @param {Object} obj * @param {"Skip All"|"Manually Find & Relink"|"Automatically Find & Relink"} obj.radioButton} */ function dismissMissingFilesDialog({ radioButton }) { const dlg = sf.ui.proTools.windows.whoseTitle.is("Missing Files").first; //Wait for dialog box to appear dlg.elementWaitFor({ timeout: 300, pollingInterval: 10, onError: "Continue", }); if (dlg.exists) { dlg.radioButtons.whoseTitle.is(radioButton).first.elementClick(); dlg.buttons.whoseTitle.is("Ok").first.elementClick(); dlg.elementWaitFor({ waitType: "Disappear", timeout: 100, pollingInterval: 10, onError: "Continue", }); } } // Function - Bounce Session /** * @param {string} sessionName */ function bounceSession(sessionName) { const bounceWin = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix"); sf.ui.proTools.mainWindow.elementWaitFor(); // Bounce File sf.ui.proTools.menuClick({ menuPath: ["File", "Bounce Mix..."], }); bounceWin.first.elementWaitFor(); bounceWin.first.textFields.first.elementSetTextFieldWithAreaValue({ value: sessionName }); bounceWin.first.buttons.whoseTitle.is("Bounce").first.elementClick(); log(`Bouncing mix: ${sessionName}`); dismissDialog({ dialogText: "Do you want to replace it?", buttonName: "Yes", }); bounceWin.first.elementWaitFor({ waitType: "Disappear", timeout: -1 }); sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: 'Disappear', timeout: -1 }); log(`Finished bouncing mix: ${sessionName}`); closeProToolsSessionWithoutSaving(); } // Function - Close Pro Tools Session Without Saving function closeProToolsSessionWithoutSaving() { sf.ui.proTools.menuClick({ menuPath: ["File", "Close Session"], }); dismissDialog({ dialogText: "Save changes to ", buttonName: "Don't Save", }); } // Function - Main function main() { sf.ui.finder.appActivate(); sf.ui.finder.mainWindow.invalidate(); // Filter selected finder paths for Pro Tools Sessions const selectedProToolsSessionPaths = sf.ui.finder.selectedPaths.filter(path => path.endsWith(".ptx")); // Bail out if no Pro Tools sessions are selected if (selectedProToolsSessionPaths.length <= 0) { alert(`No Pro Tools sessions were selected. Please select Pro Tools session(s) in the the finder and try again`); throw 0 } // Check to see if Pro Tools session is currently open const hasOpenSession = sf.ui.proTools.mainWindow.invalidate().sessionPath !== null; // If Pro Tools has a session open, ask to "Save" / "Don't Save" or "Cancel" if (hasOpenSession) { const toSaveSession = sf.interaction.displayDialog({ buttons: ["Save", "Don't Save", "Cancel"], defaultButton: "Cancel", cancelButton: "Cancel", title: "Proceed...", prompt: "There is currently a session open, would you like to save it?", }).button; switch (toSaveSession) { case "Save": //Save Session sf.ui.proTools.menuClick({ menuPath: ["File", "Save"] }); break case "Don't Save": closeProToolsSessionWithoutSaving(); break }; } // Run for each selected Session path in Finder selectedProToolsSessionPaths.forEach(sessionPath => { // Open protools session sf.file.open({ path: sessionPath, applicationPath: "/Applications/Pro Tools.app", }); // Wait for ProTools sf.ui.proTools.appWaitForActive(); while (!sf.ui.proTools.invalidate().hasValidUI) { sf.wait({ intervalMs: 200 }) } while (sf.ui.proTools.invalidate().windows.filter(w => w.title.value.match(/^(Edit:|Mix:)/)).length.valueOf() == 0) { // Ignore Missing I/O if (sf.ui.proTools.windows.whoseTitle.is("Session Notes").first.exists) { sf.ui.proTools.windows.whoseTitle.is("Session Notes").first.buttons.whoseTitle.is("No").first.elementClick(); sf.ui.proTools.windows.whoseTitle.is("Session Notes").first.buttons.whoseTitle.is("No").first.elementWaitFor({ waitType: "Disappear" }); } // Ignore Missing Plug-ins if (sf.ui.proTools.windows.whoseTitle.is("Missing AAX Plug-ins").first.exists) { sf.ui.proTools.windows.whoseTitle.is("Missing AAX Plug-ins").first.buttons.whoseTitle.is("No").first.elementClick(); sf.ui.proTools.windows.whoseTitle.is("Missing AAX Plug-ins").first.buttons.whoseTitle.is("No").first.elementWaitFor({ waitType: "Disappear" }); } // Dismiss missing files..."" dialog if it appears dismissMissingFilesDialog({ radioButton: "Skip All", }); sf.wait({ intervalMs: 200 }) } // Extract the session Name let sessionName = sessionPath.split('/').slice(-1)[0].split('.').slice(0, -1).join("."); bounceSession(sessionName); }); sf.ui.finder.appActivateMainWindow(); alert("Your files have printed!"); } main();
- Brett Ryan Stewart @Brett_Ryan_Stewart
Hey Chad!
So it ran smoothly about half the time.
Seems like every other time it threw this error...- Chad Wahlbrink @Chad2022-01-16 21:08:05.660Z
Interesting, @Brett_Ryan_Stewart , are you using the updated store version or did you copy and paste the code from here into your own script?
Line 74 is blank on my code, so that makes things harder for me to debug for you.
- Chad Wahlbrink @Chad2022-01-16 21:15:17.404Z
It's currenty working really consistently for me. I just installed the store version on my laptop with different I/O and less plugins and it's working great. So, unfortunately, I'll need more info or need to test more on my own another time to find a fix for ya.
(Note that I also left the pre-kitch code in tact at the top of this post and the code here: Bounce in Batch? Multiple Sessions? #post-35 and on the store is the latest with Kitch's edits utilized.)
- Brett Ryan Stewart @Brett_Ryan_Stewart
I had use the store update. Should I not have?
- Chad Wahlbrink @Chad2022-01-16 21:49:00.631Z
The store update should work great for updating. I wonder if you could try uninstalling that package and reinstalling it?
I'm just curious how line 74 could throw an error if there isn't code there. 🤔
regardless, thanks for trying it!
- Brett Ryan Stewart @Brett_Ryan_Stewart
oh derp.... I was running the old command. Brain fart. I just pasted in the new script and BOOM working every time! And @Kitch 's add of that Save The Session prompt is aces. You guys rule!
- Chad Wahlbrink @Chad2022-01-16 23:02:47.633Z
Ah! I'm so glad, @Brett_Ryan_Stewart.
Yeah, this new refactor from @Kitch really pushed it over the top.
I have more plans for this script coming 🔜 ✨🔮✨
- In reply toChad⬆:Kitch Membery @Kitch2022-01-16 23:12:29.221Z
You're welcome, mate :-) Feel free to add my code to the store release. @chadwahlbrink :-)
- In reply toChad⬆:Chad Wahlbrink @Chad2022-01-25 14:26:06.366Z
I updated the script above (Bounce in Batch? Multiple Sessions? #post-35) and on the store to reflect a more robust way to wait for the edit/mix window to load when loading the next session in the queue. I was using the script for my first real-world project and it was breaking down a bit. I'm hoping this works more consistently for all!
@Kitch - I had a great time chatting with you yesterday and the bit of code you turned me onto from this post really unlocked things for me - Pro Tools Main window isFocused issue #post-4
I'm using a slightly modified version to check the length of the array of filtered windows. The while loop now looks like this:
while (sf.ui.proTools.invalidate().windows.filter(w => w.title.value.match(/^(Edit:|Mix:)/)).length.valueOf() == 0)
- Brett Ryan Stewart @Brett_Ryan_Stewart
hey @chadwahlbrink and @Kitch . Sorry I disappeared for a bit. Had some gear go down and was knee deep in getting operational again. Just now getting back in to the Flow, and with V5 no less! Gonna try out this new script asap and report back! Thank you!
- Chad Wahlbrink @Chad2022-01-29 04:55:29.047Z
Great, @Brett_Ryan_Stewart!
I just used the script to print stems for another song last night. The only hiccup was that my screen fell asleep! When I woke up the display, the script picked back up where it left off 😂
I've got an update that can fix that by temporarily disabling the computer sleep settings. I'll get that posted to the store version in the next few days. But otherwise, the script seems to run great for me!
- Brett Ryan Stewart @Brett_Ryan_Stewart
Works like a champ!
- In reply toChad⬆:Brett Ryan Stewart @Brett_Ryan_Stewart
Hey @chadwahlbrink !
So this suddently stopped working. First time I've used it since updating SF to 5.0.6
Basically two different things happened.
If I created aliases for the PTX files, when i selected them and ran the command, i got the message that No Pro Tools Sessions were selected....So then I copied the PTX files themselves to a folder, and hit Bounce command, and it opened up the first session but then never went further than opening it.
I closed the session, and tried the command on the original PTX file within the song folder, and it just stopped responding altogether.
I tried it on several different files. Any thoughts?- Chad Wahlbrink @Chad2022-02-09 20:55:26.901Z
Hey @Brett_Ryan_Stewart!
Thank you for bringing this to my attention.
The SoundFlow App Store version of the script has been updated and expanded. It's also been renamed "Bounce in Batch," although it's still within my "CW Pro Tools Utilities" package.
A few things have been updated:
- I have added functionality to override system sleep settings so that the screen will remain active for all of your bounces! This setting will revert after the script completes.
- I have made the script into a "Template" style script. Templating will allow me to add more functionality to the script.
- The first "feature" of the new Template style script is defining links for different notifications. Follow the instructions here: How to get push notifications from soundflow using IFTTT. This feature allows you to add multiple notifications via the IFTTT mobile app for an error in the bounce process or a notification when the entire script has been completed.
- I have tweaked the code a bit to be less picky about what files are selected. The script previously was trying to make sure you only selected .ptx files, but I'm turning that off and having it work on any selected files for now. I think that will fix the issue with Aliases for now and I can find a more "robust" fix for that later.
I believe this script should be running smoother as a whole. I tested it on a batch of 18 sessions today. It's still a work-in-progress and will continue to evolve! As always, I appreciate your patience and willingness to try it!
- Chad Wahlbrink @Chad2022-02-09 20:56:09.283Z2022-02-09 21:13:41.136Z
Also, let me know if the notification link creation is too confusing! I'm planning to make a little demo video about that process.
NOTE - you don't have to put a link into the script for it to work. It should ignore that functionality if you leave those blank.
Thanks!
- In reply toChad⬆:Brett Ryan Stewart @Brett_Ryan_Stewart
Chad this is so kickass! Testing now, and gonna try to setup the notifications thing. Question, should I still "make editable copy" of the Bounce In Batch" ? Or do I need to make a copy of the Default Preset and put it in My Presets?
- In reply toChad⬆:Brett Ryan Stewart @Brett_Ryan_Stewart
Hey @chadwahlbrink , ok reporting back. First off, thanks for the shoutout in the code :-) i'm officially plugged in to the matrix!
So having a few issues, most important of which is it's getting hung up on the bounce screen. Here's a SR to show you what happens. As you'll see, I cancel out of both windows, at which point the script tries to keep going to the next PTX alias, and so I stop running all commands.
https://www.dropbox.com/s/ozwqg8iv406s5ti/Screen Recording 2022-02-09 at 4.12.43 PM.mov?dl=0The other trouble I'm having is with IFTTT. Followed steps meticulously and can't get the action to run. But that's for later.... :-)
- Chad Wahlbrink @Chad2022-02-09 22:38:06.532Z
Hey @Brett_Ryan_Stewart!
This makes sense, the script doesn't handle the MP3 details window yet, I'll have to add that functionality. Did rendering to MP3 work previously?
I'll have some time this weekend to add MP3 support.
Here's a demo of how to use the notifications. You would just do this process twice (creating two applets on IFTTT) and then using your created {event} in the URL for the template.
https://maker.ifttt.com/trigger/{event}/with/key/FollowedByYourUniqueKey
- Brett Ryan Stewart @Brett_Ryan_Stewart
Oh gotcha. Ok I put all the sessions back to WAV. I'll report back.
Thank you for the IFTTT walkthrough! Still can't get it to send a notification, and I've got notifications for the IFTTT app turned on on my phone, and am logged in to the same account that created the applet.
I should be able to test this by pasting in the URL and clicking "run command," shouldn't I?- Chad Wahlbrink @Chad2022-02-09 23:21:48.338Z
https://maker.ifttt.com/trigger/{event}/with/key/FollowedByYourUniqueKey
Yes, you should be able to drop this link in a web browser and run it to test it (with {event} and "FollowedByYourUniqueKey" replaced accordingly). If you still have issues, we could set up a quick zoom call to get it working.
Also, I just pushed an update of the script to the app store that should handle MP3 exports just fine.
- Brett Ryan Stewart @Brett_Ryan_Stewart
oh kickass thanks man! Yea i just pasted in my URL to a browser. It's saying "congratulations you just fired scripterror event" but no notifications on my phone. Guessing the issue is on my phone's end.
I'll take you up on that zoom call if I can't get it figured out :-)And testing the MP3 option now. Will report back.
- In reply toChad⬆:Brett Ryan Stewart @Brett_Ryan_Stewart
actually dumb question: If I update your package, will that also update the editable copy I moved to my default package?
- Chad Wahlbrink @Chad2022-02-09 23:36:44.471Z
You would have to make a new "editable copy" of the updated package version of the script in that scenario! I know that's annoying. However, you can just use the package version and make your own "personal presets." Those should work even with the app store updates.
Are you on iOS or Android? I would check your app-level notification settings for IFTTT.
I'd focus on just getting the "completion link" to work first. But yes, let me know how it goes, and we can hop on zoom if need be.
- In reply toChad⬆:Jack Green @Jack_Green
Hey Kitch and Chad,
Having a small issue with this failing to dismiss the session notes sporadically. Works sometime fails maybe 20%? I think it's a wait issue as its failing before the box has appeared.
Error is: TypeError: Cannot read property 'match' of null
(Setup Multiple Sessions line 395)Line 395 in my script is
while (sf.ui.proTools.invalidate().windows.filter(w => w.title.value.match(/^(Edit:|Mix:)/)).length.valueOf() == 0) {
Full function I think this is still a straight copy and paste aside from the function at the end.
function main() { sf.ui.finder.appActivate(); sf.ui.finder.mainWindow.invalidate(); // Filter selected finder paths for Pro Tools Sessions const selectedProToolsSessionPaths = sf.ui.finder.selectedPaths.filter(path => path.endsWith(".ptx")); // Bail out if no Pro Tools sessions are selected if (selectedProToolsSessionPaths.length <= 0) { alert(`No Pro Tools sessions were selected. Please select Pro Tools session(s) in the the finder and try again`); throw 0 } // Check to see if Pro Tools session is currently open const hasOpenSession = sf.ui.proTools.mainWindow.invalidate().sessionPath !== null; // If Pro Tools has a session open, ask to "Save" / "Don't Save" or "Cancel" if (hasOpenSession) { const toSaveSession = sf.interaction.displayDialog({ buttons: ["Save", "Don't Save", "Cancel"], defaultButton: "Cancel", cancelButton: "Cancel", title: "Proceed...", prompt: "There is currently a session open, would you like to save it?", }).button; switch (toSaveSession) { case "Save": //Save Session sf.ui.proTools.menuClick({ menuPath: ["File", "Save"] }); break case "Don't Save": closeProToolsSessionWithoutSaving(); break }; } // Run for each selected Session path in Finder selectedProToolsSessionPaths.forEach(sessionPath => { // Open protools session sf.file.open({ path: sessionPath, applicationPath: "/Applications/Pro Tools.app", }); // Wait for ProTools sf.ui.proTools.appWaitForActive(); while (!sf.ui.proTools.invalidate().hasValidUI) { sf.wait({ intervalMs: 200 }) } while (sf.ui.proTools.invalidate().windows.filter(w => w.title.value.match(/^(Edit:|Mix:)/)).length.valueOf() == 0) { // Ignore Missing I/O if (sf.ui.proTools.windows.whoseTitle.is("Session Notes").first.exists) { sf.ui.proTools.windows.whoseTitle.is("Session Notes").first.buttons.whoseTitle.is("No").first.elementClick(); sf.ui.proTools.windows.whoseTitle.is("Session Notes").first.buttons.whoseTitle.is("No").first.elementWaitFor({ waitType: "Disappear" }); } // Ignore Missing Plug-ins if (sf.ui.proTools.windows.whoseTitle.is("Missing AAX Plug-ins").first.exists) { sf.ui.proTools.windows.whoseTitle.is("Missing AAX Plug-ins").first.buttons.whoseTitle.is("No").first.elementClick(); sf.ui.proTools.windows.whoseTitle.is("Missing AAX Plug-ins").first.buttons.whoseTitle.is("No").first.elementWaitFor({ waitType: "Disappear" }); } } // Dismiss missing files..."" dialog if it appears dismissMissingFilesDialog({ radioButton: "Skip All", }); sf.ui.proTools.appActivateMainWindow({ pollingInterval: 100, retryTimeout: 20000 }, `Session did not load`); // Extract the session Name let sessionName = sessionPath.split('/').slice(-1)[0].split('.').slice(0, -1).join("."); setupSession(); }); sf.ui.finder.appActivateMainWindow(); alert("Sessions SETUP!"); } main();
I have copied all the other functions and as I said it works most of the time. Im testing on a group of 4 sessions currently 3 small 1 quite large. First attempt failed on the 2nd small session, second attempt failed on the Large session after completing the 3 small sessions. (If that makes any sense!)
Thanks in advance!
Jack
- Chad Wahlbrink @Chad2023-04-19 15:39:50.741Z
Hey @Jack_Green!
Thanks for reaching out.
I am no longer updating my 'bounce in batch' script. The support for it became unwieldy, and I have been pointing everyone towards using Bounce Factory by Andrew Scheps for their bouncing needs.
I apologize for any inconvenience. If it's functional for you in its current state, feel free to augment it to suit your needs!- Jack Green @Jack_Green
Thanks for that. I'll see what I can sort. Not using it as a bounce in batch using it as a setup session in batch!
- In reply toChad⬆:Jack Green @Jack_Green
Just want to reply and say thank you this is amazing.