Stem Bouncing In Pro Tools
Hi,
I'm looking to find or develop a script that can automate the process of print stems for Pro Tools. FTR I'm a music mixer so it's a little more specific to that. I have a few workflows that achieve this without SoundFlow successfully (still somewhat tedious and laborious) but I'm almost certain judging by exploring Andrew Scheps Melodyne script among others that this could be very well possible.
In a nutshell the process in my mind would be as follows:
*user likely to select all audio tracks to stem out
- Creates "Stems" subfolder within session folder
- Starting from the top, solo's one track at a time (ensuring X-OR solo mode is engaged)
- Opens "bounce to disk" prompt
- Selects Mix Buss output (i.e. Output 1/2, MON LR, etc - whatever variant)
- Copies the label of the TRACK NAME to Filename in the Bounce to disk prompt
- Ensures desired format/bit depth/sample etc etc
- Selects Offline mode.
- Bounces offline to newly created "Stems" folder
- Moves to next active audio track, repeating the process
Obviously very typical stem printing stuff and is the proper way to do it.
This script seemed to be off to a promising start:
https://forum.soundflow.org/-1242/how-to-bounce-each-track-automatic
A few issues with this script that I wonder if someone could help.
-
This one hides all tracks and then solos the first track. I understand the script and why this was easy to implement, but seeing how Andrew's Melodyne script can just move down one track at a time to complete his process, I'm hopeful that this could also be applied to solos?
-
I'm not sure if this process is repetitive in the way I'm hoping, I keep running into errors (typically something around line 110 about timing out) so I don't know...
Ideally I would love to sit down and get my hands dirty with some code myself, but already today after having spent a few hours poking around I really think it's best I seek help from the great community here as I need to focus on my projects atm.
Thanks in advance!!
- Kitch Membery @Kitch2020-10-12 10:12:57.873Z2020-10-12 20:47:52.073Z
Here is a script I whiped up that should do what you want :-) It needs a bit of refactoring but it works as is.
Note: You will have to change the output settings in the following lines to suit your pro tools output settings;
if (sf.ui.proTools.windows.whoseTitle.is('Bounce').first.popupButtons.first.value.invalidate().value != 'Line 1-2 (Stereo)') { sf.ui.proTools.windows.whoseTitle.is('Bounce').first.popupButtons.first.popupMenuSelect({ menuPath: ["output", "Line 1-2 (Stereo) -> MacBook Pro Speakers 1-2"], useWildcards:true, }); }
And the update the name of the stems folder in this line.
let stemFolder = createStemFolder('My Stems Folder');
Here is the script;
var sessionPath = sf.ui.proTools.mainWindow.invalidate().sessionPath; var sessionDir = sessionPath.split('/').slice(0, -1).join('/'); var sessionName = sessionPath.split('/').slice(-1)[0].split('.').slice(0, -1).join('.'); function createStemFolder(newDirectory) { const newFolder = `${sessionDir}/Bounced Files/${newDirectory}`; if (!sf.file.directoryExists({ path: newFolder }).exists) { sf.file.directoryCreate({ path: newFolder }); } sf.file.directoryCreate({ path: newFolder, }); return newFolder; } function bounceStem(stemName, firstRun) { sf.ui.proTools.menuClick({ menuPath: ["File", "Bounce to", "Disk..."], }); let bounceDlg = sf.ui.proTools.dialogWaitForManual({ dialogTitle: 'Bounce' }).dialog; sf.ui.proTools.windows.whoseTitle.is('Bounce').waitFor(); let stemFolder = createStemFolder('My Stems Folder'); if (firstRun === true) { sf.ui.proTools.windows.whoseTitle.is('Bounce').first.buttons.whoseTitle.is('Choose...').first.elementClick(); const openWin = sf.ui.proTools.windows.whoseTitle.is('Open').first; openWin.elementWaitFor(); sf.keyboard.press({ keys: "slash" }); openWin.sheets.first.comboBoxes.whoseValue.is('/').first.elementWaitFor(); openWin.sheets.first.comboBoxes.first.elementSetTextFieldWithAreaValue({ value: stemFolder, }); openWin.sheets.first.buttons.whoseTitle.is('Go').first.elementClick(); openWin.sheets.first.comboBoxes.first.elementWaitFor({ waitType: 'Disappear' }); openWin.getElement('AXDefaultButton').elementClick(); openWin.elementWaitFor({ waitType: 'Disappear' }); } const bounceCheckboxes = sf.ui.proTools.windows.whoseTitle.is('Bounce').first.checkBoxes; bounceCheckboxes.whoseTitle.is('Add MP3').first.checkboxSet({ targetValue: "Disable", }); let popupButtons = bounceDlg.getElements("AXChildren").filter(function (e) { return e.fullRole == "AXPopUpButton" }).slice(-4); let fileTypeBtn = popupButtons[0]; let formatBtn = popupButtons[1]; let bitDepthBtn = popupButtons[2]; let sampleRateBtn = popupButtons[3]; //Set Output if (sf.ui.proTools.windows.whoseTitle.is('Bounce').first.popupButtons.first.value.invalidate().value != 'Line 1-2 (Stereo)') { sf.ui.proTools.windows.whoseTitle.is('Bounce').first.popupButtons.first.popupMenuSelect({ menuPath: ["output", "Line 1-2 (Stereo) -> MacBook Pro Speakers 1-2"], useWildcards: true, }); } //File Type if (fileTypeBtn.value.invalidate().value != 'WAV') fileTypeBtn.popupMenuSelect({ menuPath: ['WAV'] }); //File Format if (formatBtn.value.value != 'Interleaved') formatBtn.popupMenuSelect({ menuPath: ['Interleaved'] }); //Bit Depth if (bitDepthBtn.value.invalidate().value != '24 Bit') bitDepthBtn.popupMenuSelect({ menuPath: ['24 Bit'] }); //Sample Rate if (sampleRateBtn.value.invalidate().value != '48 kHz') sampleRateBtn.popupMenuSelect({ menuPath: ['48 kHz'] }); //File Name sf.ui.proTools.windows.whoseTitle.is('Bounce').first.textFields.whoseTitle.is('').first.elementSetTextFieldWithAreaValue({ value: stemName, }); //Media Composer Compatability bounceCheckboxes.whoseTitle.is('Enforce Media Composer Compatibility').first.checkboxSet({ targetValue: 'Disable', }); //Import After Bounce bounceCheckboxes.whoseTitle.is('Import After Bounce').first.checkboxSet({ targetValue: 'Disable', }); //Add to iTunes bounceCheckboxes.whoseTitle.is('Add To iTunes Library').first.checkboxSet({ targetValue: 'Disable', }); //Offline Bounce bounceCheckboxes.whoseTitle.is('Offline').first.checkboxSet({ targetValue: 'Enable', }); sf.wait({ intervalMs: 200 }); sf.ui.proTools.windows.whoseTitle.is('Bounce').first.buttons.whoseTitle.is('Bounce').first.elementClick(); sf.ui.proTools.waitForNoModals({ timeout: 100 }); while (sf.ui.frontmostApp.title.value !== "Pro Tools") { sf.wait({ intervalMs: 500 }); } } function soloModeXOr() { sf.ui.proTools.menuClick({ menuPath: ["Options", "Solo Mode", "X-OR (Cancels Previous Solo)"], targetValue: "Enable", }); } function main() { sf.ui.proTools.appActivateMainWindow(); sf.ui.proTools.mainWindow.invalidate(); soloModeXOr(); let firstLoop = true; sf.ui.proTools.selectedTracks.trackHeaders.slice().map(track => { track.trackSetSolo({ targetValue: "Enable", }); let stemName = track.title.value; bounceStem(stemName, firstLoop); firstLoop = false; }); } main();
Hope that helps :-)
Rock onKitch Membery @Kitch2020-10-12 20:21:11.101Z2020-10-12 20:49:17.008Z
I've updated the script... Should work now, but needs some error handling.
I'll refactor the script sometime this week and maybe add the ability to create a new incrimented stem folder if a stem folder exists.
Let me knoow if you have any other additions that would be useful. This is a great candidate for a Command Template!
- TTristan Hoogland @Tristan
Great stuff Kitch!
Feeling a lot more on its way now. I'm able to get it to work in a lot more scenarios than before, but as I make more tracks active in the session I'm now running into the following:
"Popup window was not found after waiting 2000ms, Line 23)This error is also presented when I interrupt the process, not sure if that provides any more clues.
Absolutely! There are plenty of variations I can think of that I need to do on a daily basis (instrumentals, acapellas, etc). Once this one is feeling solid we can throw a handful more in there too. Was looking at some code for importing the audio files after all printed to the bottom of the session, maybe even pack into a Tracks Folder. Also a version where we could print the stems, import them to the bottom of the session then "hide and make inactive" all the original tracks so you effectively end up with a clean "stems-only" session that could be Saved As to a new PT file. That might be a bit risky for some, as I'm sure one would ideally like to check the stems have all printed properly before removing original tracks, but if it's a tight script then that might be worth attempting.
Eagerly jumping between mixes and this script today!
- TTristan Hoogland @Tristan
Here are two videos:
#1 - working with 2 of the drum auxes above inactive.
https://www.dropbox.com/s/vlrykzjtfpy4s94/PT STEMS 1 - working.MOV?dl=0#2 - then I make those 2 drum auxes active and the 2000ms error is presented.
https://www.dropbox.com/s/jho7fl7xcxnts8t/PT STEMS 2 - working timeout 2000ms error.MOV?dl=0 - In reply toTristan⬆:
Kitch Membery @Kitch2020-10-12 21:21:00.021Z
Great Ideas!
The error makes total sence. I just thought of another way to fix the issue it that I think will be bullet proof. Will let you know how I go. I have a busy day today but will get onto it some time in the next few days :-)
- TTristan Hoogland @Tristan
Kitch, you are my hero. A true inspiration.
click to show
- TIn reply toTristan⬆:Tristan Hoogland @Tristan
Thanks so much for throwing this together/sharing so quickly Kitch!
Unfortunately in an already mixed track I am experiencing a similar issue with "Track Already Exists" at line 122. I created a basic session and it seemed to run through fine there, except it's still only bouncing out the first track and then appears the script is complete. I was expecting/hoping it'd then move down to the follow audio track, solo that print that, etc etc. Is it working as intended on your end?
The bouncing out/creating folder stuff is amazing already though!
Kitch Membery @Kitch2020-10-12 18:05:29.242Z
You are welcome mate!
.... Check in the newly created stems folder to see if there is a file in there that has the same as the first track you have selected as you will only get that error if there is already a file in there with that name. Then try running it again.
Here is a video example of it working on my system;
Let me know if that works.
Rock on!- FFrancesco Donadello @Francesco_Donadello
Hello Kitch, I'm new to Soundflow but this "automated stem bounce" script is really what I'm looking for. Thanks for posting it. however, I've tried it on my system and it's giving me an error message on line 42:
openWin.sheets.first.comboBoxes.whoseValue.is('/').first.elementWaitFor();
saying " Element was not found or removed after waiting 2000 ms "
it's like he can not copy or find the newly created directory name in the comboBox.
I don't know Java at all, so forgive me if I'm talking nonsense. i'm sure you understand the problem.
thanks for any help!Kitch Membery @Kitch2020-10-25 19:37:04.377Z
I'll take a look at the script sometime this week and see if I work it out. :-)
- TIn reply toTristan⬆:Tristan Hoogland @Tristan
Kitch! So damn close mate.
I stupidly forgot to select the tracks I wanted to bounce out, so seeing your demonstration solved that problem.
On a simple, fresh session like yours it's definitely working as intended. I can even repeat the process on that session over and over and not run into any errors like I am on my existing sessions.
Here's the error I'm getting:
https://www.dropbox.com/s/t0gtjq8awus20r4/IMG_3361.MOV?dl=0"Failed - Tracka already exists, line 122". It bounces out that file, but then just completely stops.
I've checked the folders for duplicates/existing files, created new/unique folder names in the script, etc and it still comes up on that first track no matter what. I tried making inactive all my tracks and just trying two tracks, but still the same error. Anything I can do my end to help shed some light? I might go through and check out if it's a plugin issue or something after my mixing today.
Man, so excited for something like this. You have no idea how much I've died for something like this!
Kitch Membery @Kitch2020-10-12 19:20:11.154Z
I've been meaning to create this script for quite some time so when you posted the request I jumped on it. :-)
Try one thing for me. I missed a semicolon on the end of one of my lines of code can you change line
throw "Track already exists"
to
throw "Track already exists";
If that does not work I'll take a look and see if that section of my script can be improved (I think the way I aproached this section was a little hacky). To be fair t'was 4:00am when I finished it. Haha.
Kitch Membery @Kitch2020-10-12 19:31:25.138Z
I can reproduce the error here now. I'll take a look at it this evening and see what I can do :-)
- TTristan Hoogland @Tristan
Thanks Kitch!
Yeah I just tried a few different iterations now, it seems a bit plugin sensitive on my end. When I started introducing certain plugins from various manufacturers it would present the error, (i.e. VMR with intersampling on, but worked ok with OS ON, some Waves plugs seemed to be OK, softube not working at all, nor UAD, nor a lot of others).
Sounds like you're onto it anyway! I'm exploring a few other ideas and additions myself to add on top of your script. Thanks again Kitch, absolute legend!!
Kitch Membery @Kitch2020-10-12 20:04:10.999Z
Yoh have to be an Australian... I feel it in my waters?
- TTristan Hoogland @Tristan
You know your bees wax, Kitch? Why don't you mind it!
- TTristan Hoogland @Tristan
;)
Kitch Membery @Kitch2020-10-12 20:07:36.389Z
I'm a Sydney boy! Are you in Australia? :-)
- TIn reply toTristan⬆:Tristan Hoogland @Tristan
Hey team,
Managed to make a few quick adjustments to the script. Still a few more I'd like to add, but for now it's working quite well.
Updated:
Maximize all collapsed panels
Disable "import after bounce"
Suspends groups**I'm sure other users have worked out that the script won't ignore groups when bouncing. I'd say in 99% of cases I want to bounce files individually so I don't end up with multiple prints of the same thing plus having other tracks print with other tracks. If this isn't a desirable function for you personally, remove code from lines 131-137
Hope this helps everybody! I should preface by saying this is working in PT 2020.11 and has not been tested in PT 2020.12 and I have no desire to move up at this point.
var sessionPath = sf.ui.proTools.mainWindow.invalidate().sessionPath; var sessionDirectory = sessionPath.split('/').slice(0, -1).join('/'); var sessionName = sessionPath.split('/').slice(-1)[0].split('.').slice(0, -1).join('.'); function makeNewDirectory({ templatePath }) { const parentDirectory = templatePath.split('/').slice(0, -1).join('/'); const directoryTemplateName = templatePath.split('/').slice(-1)[0]; for (let i = 1; ; i++) { let directoryName = `${directoryTemplateName}${i}`; let directoryPath = parentDirectory + '/' + directoryName; if (!sf.file.directoryExists({ path: directoryPath }).exists) { sf.file.directoryCreate({ path: directoryPath }); return directoryPath; } } } function bounceStem(stemName, directory, versionNumber, firstRun) { sf.ui.proTools.menuClick({ menuPath: ["File", "Bounce Mix..."], }); let bounceDlg = sf.ui.proTools.dialogWaitForManual({ dialogTitle: 'Bounce Mix' }).dialog; sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').waitFor(); let stemFolder = directory; if (firstRun === true) { sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Location').first.radioButtons.whoseTitle.is('Directory:').first.elementClick(); sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Location').first.buttons.whoseTitle.is('Choose...').first.elementClick(); const openWin = sf.ui.proTools.windows.whoseTitle.is('Open').first; openWin.elementWaitFor(); sf.keyboard.press({ keys: "slash" }); openWin.sheets.first.comboBoxes.whoseValue.is('/').first.elementWaitFor(); openWin.sheets.first.comboBoxes.first.elementSetTextFieldWithAreaValue({ value: stemFolder, }); openWin.sheets.first.buttons.whoseTitle.is('Go').first.elementClick(); openWin.sheets.first.comboBoxes.first.elementWaitFor({ waitType: 'Disappear' }); openWin.getElement('AXDefaultButton').elementClick(); openWin.elementWaitFor({ waitType: 'Disappear' }); } function maximiseBounceWindow() { const bounceWin = sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first; bounceWin.groups.forEach(g => { if (g.frame.h === 22) { if (g.buttons.whoseTitle.is('Collapser').first.exists) { g.buttons.whoseTitle.is('Collapser').first.elementClick(); } } }) } //Maximise 'Bounce Mix' Window maximiseBounceWindow(); const bounceCheckboxes = sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.checkBoxes; sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Audio').first.checkBoxes.whoseTitle.is('Add MP3').first.checkboxSet({ targetValue: "Disable", }); let popupButtons = bounceDlg.getElements("AXChildren").filter(function (e) { return e.fullRole == "AXPopUpButton" }).slice(-4); let fileTypeBtn = popupButtons[0]; let formatBtn = popupButtons[1]; let bitDepthBtn = popupButtons[2]; let sampleRateBtn = popupButtons[3]; //File Name sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.textFields.whoseTitle.is('').first.elementSetTextFieldWithAreaValue({ value: stemName + versionNumber, }); sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Location').first.checkBoxes.whoseTitle.is('Import After Bounce').first.checkboxSet({ targetValue: "Disable", }); //Offline Bounce bounceCheckboxes.whoseTitle.is('Offline').first.checkboxSet({ targetValue: 'Enable', }); sf.wait({ intervalMs: 200 }); sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.buttons.whoseTitle.is('Bounce').first.elementClick(); //Wait for bounce to finish (Wait for bounce dialog to appear, then to disappear) sf.ui.proTools.confirmationDialog.elementWaitFor({ timeout: 100000 }); sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: 'Disappear', timeout: -1 }); //-1 is endless timeout (cancel by Ctrl+Shift+Esc) while (sf.ui.frontmostApp.title.value !== "Pro Tools") { sf.wait({ intervalMs: 500 }); } } function soloModeXOr() { sf.ui.proTools.menuClick({ menuPath: ["Options", "Solo Mode", "X-OR (Cancels Previous Solo)"], targetValue: "Enable", }); } function main() { sf.ui.proTools.appActivateMainWindow(); sf.ui.proTools.mainWindow.invalidate(); const newDirectory = makeNewDirectory({ templatePath: `${sessionDirectory}/Bounced Files/${sessionName}_STEMS V` }); const versionNumber = '_STEM_' + newDirectory.split(" ").slice(-1); sf.ui.proTools.groupsEnsureGroupListIsVisible(); var groupListPopup = sf.ui.proTools.groupsOpenListPopupMenu().popupMenu; groupListPopup.menuClickPopupMenu({ menuPath: ["Suspend All Groups"], targetValue: 'Enable' }); sf.ui.proTools.appActivateMainWindow(); soloModeXOr(); let firstLoop = true; sf.ui.proTools.selectedTracks.trackHeaders.slice().map(track => { track.trackSetSolo({ targetValue: "Enable", }); let stemName = track.title.value.split(" - ")[0]; bounceStem(stemName, newDirectory, versionNumber, firstLoop); firstLoop = false; }); sf.system.exec({ commandLine: `open "${newDirectory}"` }); } main();
- JJonas Jalhay @Jonas_Jalhay
Hey Tristan,
Just wanted to report that I've tried your latest script and it's working perfectly in PT 2020.12
Thanks again for the hard work, such an amazing script for people who need to print "proper" stems on a daily basis.
Best,
Jonas
- TIn reply toTristan⬆:Tristan Hoogland @Tristan
Oh great stuff @Jonas_Jalhay !!
This might have instilled a little more confidence to upgrade. The multiple sessions thing with bounce butler is pretty cool, that would be handy...
I'm right there with you, it's such a tedious process to do, but when you're working at a high level you just have no option but to do it properly.
I still have a few more things I'd like to include with the script, but this does get us to 90% mark. The main one for me is finding a way to print the lead vocal DRY and FX separately. Currently I print all my instrument stems with the script, then manually finish off the lead vocals by alternating the mute on the fx bus + dry lead vocal bus. That's getting pretty specific to my workflow, though and might need help from old blackbelt @Kitch to get that included, though I have a bit of downtime today and tomorrow so I might try my luck at implementing it.
- JJonas Jalhay @Jonas_Jalhay
Oh right I didn't think of that, that is something that is easily done with Bounce Butler as you can mute your FXs bus (while soloing your lead vocal) and do the same thing in reverse for your FXs only stem.
I guess you could "unsolo safe" your FXs bus so it can print the dry only vocal as part of the batch. If only Protools would allow to solo FXs bus like other DAWs do it could work... I know that feature has been asked by PT users for a long time, I'm not sure why it has never been implemented, I get why as physically you shouldn't be able to but surely it's not that hard to add it as a feature.
Are you able to do your instrumental, TV mix and acapella with the script easily as well?
- TTristan Hoogland @Tristan
unsolo safeing the fx buss is an interesting consideration, I've been focusing harder on mutes and not even thinking of that as a function. My current routing has the BVs usually sharing the FX routing as the lead so that introduces another variable for consideration as I prefer to print the BV's WITH the FX attached to the stem. I know myself enough to know that when I'm mixing I just don't care about this stuff so I know it'll bite me in the ass when I stem out!
I haven't tried getting it to do the inst, aca and TV yet. Unless you're incredibly diligent with routing during mix the final routing of tracks always needs some careful review before printing and as you know that's not really the time consuming part of printing deliverables. Of course, definitely possible, but low on the priority list for me at this point in time.
- JJonas Jalhay @Jonas_Jalhay
Gotcha, yeah it's tricky to find something that will translate to everyone's template or workflow but some of the main outline should work and you're right printing fully split out stems is definitely the part that sucks your soul away and I'm glad I never have to sit thru it ever again.
Looking forward to seeing how the script evolves over time but it's pretty damn good already.
- TTristan Hoogland @Tristan
Absolutely. My printing life has certainly become a lot easier in the past few months and I'm pretty happy about that! Very excited myself to keep evolving it though. Stay tuned!
- TIn reply toTristan⬆:Tristan Hoogland @Tristan
Hey @Kitch
A question for you - is there a way to recall the session set up parameters, log them and apply to the bounce mix window? Currently I'm needing to have individual 44.1/48/88.2/96k scripts, but ideally it'd be great to not have to manually account for this when bouncing stems. I've worked out the logging part of course, but getting it to apply it to the bounce mix dialog is seeming a little trickier, potentially even unlikely?
Kitch Membery @Kitch2021-02-18 18:52:03.589Z
Legend,
That should be doable. Will see what I can do in the next couple of days. :-)
Rock on
- TTristan Hoogland @Tristan
Legend right back @ u~
- In reply toTristan⬆:
Kitch Membery @Kitch2021-02-18 23:10:41.719Z
Mate!
This will get the sample rate and bit depth from the Session Setup window and return it as an object that you can pass into the
bounceStem
function.Let me know if you get stuck :-)
function getSessionSampleRateAndBitDepth() { //Activate Pro Tools Main Window sf.ui.proTools.appActivateMainWindow(); //Session Setup Window Variable const win = sf.ui.proTools.windows.whoseTitle.is('Session Setup').first; //If Session Setup Window is not open, open it. if (!win.exists) { sf.ui.proTools.menuClick({ menuPath: ["Setup", "Session"], }); } //Wait for Session Setup Window to appear win.elementWaitFor({}, `Could not find the Session Setup Window`); let sampleRate = sf.ui.proTools.windows.whoseTitle.is("Session Setup").first.groups.whoseTitle.is("Session Format").first.children.whoseRole.is("AXStaticText").allItems[2].value.invalidate().value; let bitDepth = sf.ui.proTools.windows.whoseTitle.is("Session Setup").first.groups.whoseTitle.is("Session Format").first.popupButtons.allItems[1].value.invalidate().value; //Close Session Setup Window sf.ui.proTools.menuClick({ menuPath: ["Setup", "Session"], }); //Wait for Session Setup Window to disappear win.elementWaitFor({ waitType: "Disappear", }); let sessionInfo = { sampleRate: sampleRate, bitDepth: bitDepth, } return sessionInfo; } let sessionInfo = getSessionSampleRateAndBitDepth(); log(sessionInfo); //This will log Session Info Object log(sessionInfo.sampleRate); //This will log the Session Sample Rate log(sessionInfo.bitDepth); //This will log the Session Bit Depth
- TTristan Hoogland @Tristan
Huge stuff K! Amazing for getting onto this so quickly. I'll put it through its paces tomorrow and report back!
- In reply toKitch⬆:TTristan Hoogland @Tristan
Heya Kitch! Long time no speaky. Hope all is well in your neck of the woods.
Now that the ProTools accessibility menu thing has been fixed (I silently noticed this!) I'm getting deep into soundflow stuff and updating and creating various scripts.
With this script - I still never quite nailed the recall as an object function to implement the session setup window parameters and apply them to the bounce dialog (i.e. so the bit depth and sample rate print the same as the session settings). Seeing as I've not used the object function before I'm a little lost about how I implement it. Would you possibly be able to amend my main script (pasting below) with how you would go about this? Thanks so much!
var sessionPath = sf.ui.proTools.mainWindow.invalidate().sessionPath; var sessionDirectory = sessionPath.split('/').slice(0, -1).join('/'); var sessionName = sessionPath.split('/').slice(-1)[0].split('.').slice(0, -1).join('.'); function makeNewDirectory({ templatePath }) { const parentDirectory = templatePath.split('/').slice(0, -1).join('/'); const directoryTemplateName = templatePath.split('/').slice(-1)[0]; for (let i = 1; ; i++) { let directoryName = `${directoryTemplateName}${i}`; let directoryPath = parentDirectory + '/' + directoryName; if (!sf.file.directoryExists({ path: directoryPath }).exists) { sf.file.directoryCreate({ path: directoryPath }); return directoryPath; } } } function bounceStem(stemName, directory, versionNumber, firstRun) { sf.ui.proTools.menuClick({ menuPath: ["File", "Bounce Mix..."], }); let bounceDlg = sf.ui.proTools.dialogWaitForManual({ dialogTitle: 'Bounce Mix' }).dialog; sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').waitFor(); let stemFolder = directory; if (firstRun === true) { sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Location').first.radioButtons.whoseTitle.is('Directory:').first.elementClick(); sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Location').first.buttons.whoseTitle.is('Choose...').first.elementClick(); const openWin = sf.ui.proTools.windows.whoseTitle.is('Open').first; openWin.elementWaitFor(); sf.keyboard.press({ keys: "slash" }); openWin.sheets.first.comboBoxes.whoseValue.is('/').first.elementWaitFor(); openWin.sheets.first.comboBoxes.first.elementSetTextFieldWithAreaValue({ value: stemFolder, }); openWin.sheets.first.buttons.whoseTitle.is('Go').first.elementClick(); openWin.sheets.first.comboBoxes.first.elementWaitFor({ waitType: 'Disappear' }); const openButton = sf.ui.proTools.windows.whoseTitle.is("Open").first.buttons.whoseTitle.is("Open").first; openButton.elementWaitFor(); openButton.elementClick(); openWin.elementWaitFor({ waitType: 'Disappear' }); } function maximiseBounceWindow() { const bounceWin = sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first; bounceWin.groups.forEach(g => { if (g.frame.h === 22) { if (g.buttons.whoseTitle.is('Collapser').first.exists) { g.buttons.whoseTitle.is('Collapser').first.elementClick(); } } }) } //Maximise 'Bounce Mix' Window maximiseBounceWindow(); const bounceCheckboxes = sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.checkBoxes; sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Audio').first.checkBoxes.whoseTitle.is('Add MP3').first.checkboxSet({ targetValue: "Disable", }); let popupButtons = bounceDlg.getElements("AXChildren").filter(function (e) { return e.fullRole == "AXPopUpButton" }).slice(-4); let fileTypeBtn = popupButtons[0]; let formatBtn = popupButtons[1]; let bitDepthBtn = popupButtons[2]; let sampleRateBtn = popupButtons[3]; //File Name sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.textFields.whoseTitle.is('').first.elementSetTextFieldWithAreaValue({ value: stemName + versionNumber, }); sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Location').first.checkBoxes.whoseTitle.is('Import After Bounce').first.checkboxSet({ targetValue: "Disable", }); // Mix Source - Set Parameters const mixSourceButtonTitle = '13-14 (Stereo)'; const mixSourcePath = ["output", "13-14 (Stereo) -> 13-14"]; const bounceDlg = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first; //Mix Source - Popup Menu if (bounceDlg.popupButtons.allItems[1].value.invalidate().value !== mixSourceButtonTitle) { bounceDlg.popupButtons.allItems[1].popupMenuSelect({ menuSelector: items => items.filter(item => item.path[0].endsWith(mixSourcePath[0]) && item.path[1].endsWith(mixSourcePath[1]))[0] }); } //Offline Bounce bounceCheckboxes.whoseTitle.is('Offline').first.checkboxSet({ targetValue: 'Enable', }); sf.wait({ intervalMs: 200 }); sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.buttons.whoseTitle.is('Bounce').first.elementClick(); //Wait for bounce to finish (Wait for bounce dialog to appear, then to disappear) sf.ui.proTools.confirmationDialog.elementWaitFor({ timeout: 100000 }); sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: 'Disappear', timeout: -1 }); //-1 is endless timeout (cancel by Ctrl+Shift+Esc) while (sf.ui.frontmostApp.title.value !== "Pro Tools") { sf.wait({ intervalMs: 500 }); } } //Enable Track Number sf.ui.proTools.menuClick({ menuPath: ["View", "Track Number"], targetValue: "Enable", }); function soloModeXOr() { sf.ui.proTools.menuClick({ menuPath: ["Options", "Solo Mode", "X-OR (Cancels Previous Solo)"], targetValue: "Enable", }); } function main() { sf.ui.proTools.appActivateMainWindow(); sf.ui.proTools.mainWindow.invalidate(); const newDirectory = makeNewDirectory({ templatePath: `${sessionDirectory}/Bounced Files/${sessionName}_STEMS V` }); const versionNumber = '_STEM_' + newDirectory.split(" ").slice(-1); sf.ui.proTools.groupsEnsureGroupListIsVisible(); var groupListPopup = sf.ui.proTools.groupsOpenListPopupMenu().popupMenu; groupListPopup.menuClickPopupMenu({ menuPath: ["Suspend All Groups"], targetValue: 'Enable' }); sf.ui.proTools.appActivateMainWindow(); soloModeXOr(); let firstLoop = true; sf.ui.proTools.selectedTracks.trackHeaders.slice().map(track => { track.trackSetSolo({ targetValue: "Enable", }); let stemName = track.title.value.split(" - ")[0]; bounceStem(stemName, newDirectory, versionNumber, firstLoop); firstLoop = false; }); sf.system.exec({ commandLine: `open "${newDirectory}"` }); } main(); //Disable Track Number sf.ui.proTools.menuClick({ menuPath: ["View", "Track Number"], targetValue: "Disable", });
Kitch Membery @Kitch2022-09-08 18:40:08.532Z
It may take me a while to get around to this but hopefully I can find some time over the weekend to take a look. What OS and Pro Tools version are you using?
If you decide to give it a try, the best way to do what you are after is to write a function to collect the bit depth and sample rate, and call that function prior to opening the Bounce Window;
Something like this should do that.
function getSessionSampleRateAndBitDepth() { sf.ui.proTools.appActivateMainWindow({}, `Could not activate Pro Tools main window`); const win = sf.ui.proTools.windows.whoseTitle.is('Session Setup').first; if (!win.exists) { sf.ui.proTools.menuClick({ menuPath: ["Setup", "Session"], }, `Could not click the "Setup", "Session" menu item (Open).`); } win.elementWaitFor({}, `Could not find the Session Setup Window`); const sessionFormatPanel = win.groups.whoseTitle.is("Session Format").first let sampleRate = sessionFormatPanel.children.whoseRole.is("AXStaticText").allItems[2].value.invalidate().value; let bitDepth = sessionFormatPanel.popupButtons.allItems[1].value.invalidate().value; //Close Session Setup Window sf.ui.proTools.menuClick({ menuPath: ["Setup", "Session"] }, `Could not click the "Setup", "Session" menu item (Close).`); win.elementWaitFor({ waitType: "Disappear", }, `The session setup window could not be closed.`); let sessionSettings = { sampleRate: sampleRate, bitDepth: bitDepth, } return sessionSettings; } //Add the following lines at the start of your main function after Pro Tools's main window has been activated and invalidated. const sessionSettings = getSessionSampleRateAndBitDepth(); const sampleRate = sessionSettings.sampleRate; const bitDepth = sessionSettings.bitDepth;
You can then use these
sampleRate
&bitDepth
variables to open and select the Sample Rate and Bit Depth popup menus in the bounce window using thepopupMenuSelect
method.On a side note... If you are bouncing a truckload of mixes with variations, I highly recommend you check out Andrew Schep's "Bounce Factory" package in the store. It was not available back when we were working on this last, so I figured it's worth pointing out.
Rock on!
- TTristan Hoogland @Tristan
Thanks Kitch! I appreciate you helping out. I'll have a stab at implementing this on my own.
click to show
- JIn reply toTristan⬆:Jonas Jalhay @Jonas_Jalhay
Hey guys, tweaked the latest script to work in the latest PT update (2021.6)
Hope that helps anyone!
var sessionPath = sf.ui.proTools.mainWindow.invalidate().sessionPath; var sessionDirectory = sessionPath.split('/').slice(0, -1).join('/'); var sessionName = sessionPath.split('/').slice(-1)[0].split('.').slice(0, -1).join('.'); function makeNewDirectory({ templatePath }) { const parentDirectory = templatePath.split('/').slice(0, -1).join('/'); const directoryTemplateName = templatePath.split('/').slice(-1)[0]; for (let i = 1; ; i++) { let directoryName = `${directoryTemplateName}${i}`; let directoryPath = parentDirectory + '/' + directoryName; if (!sf.file.directoryExists({ path: directoryPath }).exists) { sf.file.directoryCreate({ path: directoryPath }); return directoryPath; } } } function bounceStem(stemName, directory, versionNumber, firstRun) { sf.ui.proTools.menuClick({ menuPath: ["File", "Bounce Mix..."], }); let bounceDlg = sf.ui.proTools.dialogWaitForManual({ dialogTitle: 'Bounce Mix' }).dialog; sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').waitFor(); let stemFolder = directory; if (firstRun === true) { sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Location').first.radioButtons.whoseTitle.is('Directory:').first.elementClick(); sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Location').first.buttons.whoseTitle.is('Choose...').first.elementClick(); const openWin = sf.ui.proTools.windows.whoseTitle.is('Open').first; openWin.elementWaitFor(); sf.keyboard.press({ keys: "slash" }); openWin.sheets.first.comboBoxes.whoseValue.is('/').first.elementWaitFor(); openWin.sheets.first.comboBoxes.first.elementSetTextFieldWithAreaValue({ value: stemFolder, }); openWin.sheets.first.buttons.whoseTitle.is('Go').first.elementClick(); openWin.sheets.first.comboBoxes.first.elementWaitFor({ waitType: 'Disappear' }); bounceStem sf.ui.proTools.windows.whoseTitle.is("Open").first.buttons.whoseTitle.is("Open").first.elementClick(); } function maximiseBounceWindow() { const bounceWin = sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first; bounceWin.groups.forEach(g => { if (g.frame.h === 22) { if (g.buttons.whoseTitle.is('Collapser').first.exists) { g.buttons.whoseTitle.is('Collapser').first.elementClick(); } } }) } //Maximise 'Bounce Mix' Window maximiseBounceWindow(); const bounceCheckboxes = sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.checkBoxes; sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Audio').first.checkBoxes.whoseTitle.is('Add MP3').first.checkboxSet({ targetValue: "Disable", }); let popupButtons = bounceDlg.getElements("AXChildren").filter(function (e) { return e.fullRole == "AXPopUpButton" }).slice(-4); let fileTypeBtn = popupButtons[0]; let formatBtn = popupButtons[1]; let bitDepthBtn = popupButtons[2]; let sampleRateBtn = popupButtons[3]; //File Name sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.textFields.whoseTitle.is('').first.elementSetTextFieldWithAreaValue({ value: stemName + versionNumber, }); sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Location').first.checkBoxes.whoseTitle.is('Import After Bounce').first.checkboxSet({ targetValue: "Disable", }); //Offline Bounce bounceCheckboxes.whoseTitle.is('Offline').first.checkboxSet({ targetValue: 'Enable', }); sf.wait({ intervalMs: 200 }); sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.buttons.whoseTitle.is('Bounce').first.elementClick(); //Wait for bounce to finish (Wait for bounce dialog to appear, then to disappear) sf.ui.proTools.confirmationDialog.elementWaitFor({ timeout: 100000 }); sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: 'Disappear', timeout: -1 }); //-1 is endless timeout (cancel by Ctrl+Shift+Esc) while (sf.ui.frontmostApp.title.value !== "Pro Tools") { sf.wait({ intervalMs: 500 }); } } function soloModeXOr() { sf.ui.proTools.menuClick({ menuPath: ["Options", "Solo Mode", "X-OR (Cancels Previous Solo)"], targetValue: "Enable", }); } function main() { sf.ui.proTools.appActivateMainWindow(); sf.ui.proTools.mainWindow.invalidate(); const newDirectory = makeNewDirectory({ templatePath: `${sessionDirectory}/Bounced Files/${sessionName}_Stems V` }); const versionNumber = '_Stem_' + newDirectory.split(" ").slice(-1); sf.ui.proTools.groupsEnsureGroupListIsVisible(); var groupListPopup = sf.ui.proTools.groupsOpenListPopupMenu().popupMenu; groupListPopup.menuClickPopupMenu({ menuPath: ["Suspend All Groups"], targetValue: 'Enable' }); sf.ui.proTools.appActivateMainWindow(); soloModeXOr(); let firstLoop = true; sf.ui.proTools.selectedTracks.trackHeaders.slice().map(track => { track.trackSetSolo({ targetValue: "Enable", }); let stemName = track.title.value.split(" - ")[0]; bounceStem(stemName, newDirectory, versionNumber, firstLoop); firstLoop = false; }); sf.system.exec({ commandLine: `open "${newDirectory}"` }); } main();
- TIn reply toTristan⬆:Tristan Hoogland @Tristan
The og code should still be working in 2021.6 and 2021.7, but just in case this is what's working for me rn.
var sessionPath = sf.ui.proTools.mainWindow.invalidate().sessionPath; var sessionDirectory = sessionPath.split('/').slice(0, -1).join('/'); var sessionName = sessionPath.split('/').slice(-1)[0].split('.').slice(0, -1).join('.'); function makeNewDirectory({ templatePath }) { const parentDirectory = templatePath.split('/').slice(0, -1).join('/'); const directoryTemplateName = templatePath.split('/').slice(-1)[0]; for (let i = 1; ; i++) { let directoryName = `${directoryTemplateName}${i}`; let directoryPath = parentDirectory + '/' + directoryName; if (!sf.file.directoryExists({ path: directoryPath }).exists) { sf.file.directoryCreate({ path: directoryPath }); return directoryPath; } } } function bounceStem(stemName, directory, versionNumber, firstRun) { sf.ui.proTools.menuClick({ menuPath: ["File", "Bounce Mix..."], }); let bounceDlg = sf.ui.proTools.dialogWaitForManual({ dialogTitle: 'Bounce Mix' }).dialog; sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').waitFor(); let stemFolder = directory; if (firstRun === true) { sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Location').first.radioButtons.whoseTitle.is('Directory:').first.elementClick(); sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Location').first.buttons.whoseTitle.is('Choose...').first.elementClick(); const openWin = sf.ui.proTools.windows.whoseTitle.is('Open').first; openWin.elementWaitFor(); sf.keyboard.press({ keys: "slash" }); openWin.sheets.first.comboBoxes.whoseValue.is('/').first.elementWaitFor(); openWin.sheets.first.comboBoxes.first.elementSetTextFieldWithAreaValue({ value: stemFolder, }); openWin.sheets.first.buttons.whoseTitle.is('Go').first.elementClick(); openWin.sheets.first.comboBoxes.first.elementWaitFor({ waitType: 'Disappear' }); const openButton = sf.ui.proTools.windows.whoseTitle.is("Open").first.buttons.whoseTitle.is("Open").first; openButton.elementWaitFor(); openButton.elementClick(); openWin.elementWaitFor({ waitType: 'Disappear' }); } function maximiseBounceWindow() { const bounceWin = sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first; bounceWin.groups.forEach(g => { if (g.frame.h === 22) { if (g.buttons.whoseTitle.is('Collapser').first.exists) { g.buttons.whoseTitle.is('Collapser').first.elementClick(); } } }) } //Maximise 'Bounce Mix' Window maximiseBounceWindow(); const bounceCheckboxes = sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.checkBoxes; sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Audio').first.checkBoxes.whoseTitle.is('Add MP3').first.checkboxSet({ targetValue: "Disable", }); let popupButtons = bounceDlg.getElements("AXChildren").filter(function (e) { return e.fullRole == "AXPopUpButton" }).slice(-4); let fileTypeBtn = popupButtons[0]; let formatBtn = popupButtons[1]; let bitDepthBtn = popupButtons[2]; let sampleRateBtn = popupButtons[3]; //File Name sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.textFields.whoseTitle.is('').first.elementSetTextFieldWithAreaValue({ value: stemName + versionNumber, }); sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.groups.whoseTitle.is('Location').first.checkBoxes.whoseTitle.is('Import After Bounce').first.checkboxSet({ targetValue: "Disable", }); //Offline Bounce bounceCheckboxes.whoseTitle.is('Offline').first.checkboxSet({ targetValue: 'Enable', }); sf.wait({ intervalMs: 200 }); sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.buttons.whoseTitle.is('Bounce').first.elementClick(); //Wait for bounce to finish (Wait for bounce dialog to appear, then to disappear) sf.ui.proTools.confirmationDialog.elementWaitFor({ timeout: 100000 }); sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: 'Disappear', timeout: -1 }); //-1 is endless timeout (cancel by Ctrl+Shift+Esc) while (sf.ui.frontmostApp.title.value !== "Pro Tools") { sf.wait({ intervalMs: 500 }); } } function soloModeXOr() { sf.ui.proTools.menuClick({ menuPath: ["Options", "Solo Mode", "X-OR (Cancels Previous Solo)"], targetValue: "Enable", }); } function main() { sf.ui.proTools.appActivateMainWindow(); sf.ui.proTools.mainWindow.invalidate(); const newDirectory = makeNewDirectory({ templatePath: `${sessionDirectory}/Bounced Files/${sessionName}_STEMS V` }); const versionNumber = '_STEM_' + newDirectory.split(" ").slice(-1); sf.ui.proTools.groupsEnsureGroupListIsVisible(); var groupListPopup = sf.ui.proTools.groupsOpenListPopupMenu().popupMenu; groupListPopup.menuClickPopupMenu({ menuPath: ["Suspend All Groups"], targetValue: 'Enable' }); sf.ui.proTools.appActivateMainWindow(); soloModeXOr(); let firstLoop = true; sf.ui.proTools.selectedTracks.trackHeaders.slice().map(track => { track.trackSetSolo({ targetValue: "Enable", }); let stemName = track.title.value.split(" - ")[0]; bounceStem(stemName, newDirectory, versionNumber, firstLoop); firstLoop = false; }); sf.system.exec({ commandLine: `open "${newDirectory}"` }); } main();
- TTristan Hoogland @Tristan
I forgot - the only thing I never included when PT updated was selecting the MON L/R or Output 1-2 in the dropdown menu. I'll look into it once I get a chance otherwise if you're about @Kitch would love your help!
Kitch Membery @Kitch2021-09-04 23:41:25.815Z2021-09-05 21:38:16.387Z
@Tristan_Hoogland, mate!
To set the output, you will need to change the following code so the
mixSourceButtonTitle
and themixSourcePath
reflect your output;Mine look like this in Pro Tools;
mixSourceButtonTitle:
mixSourcePath:
Here is the code to set the output;
const mixSourceButtonTitle = 'Playback 1-2 (Stereo)'; const mixSourcePath = ["output", "Playback 1-2 (Stereo) -> Playback 1-2"]; const bounceDlg = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first; if (bounceDlg.popupButtons.allItems[1].title.invalidate().value !== mixSourceButtonTitle) { bounceDlg.popupButtons.allItems[1].popupMenuSelect({ menuPath: mixSourcePath, }); }
Update 09/05/21:
A better way would be to use the following script as it will allow you to select output paths that are indented in the Source Output menu menu.
const mixSourceButtonTitle = 'Playback 1-2 (Stereo)'; const mixSourcePath = ["output", "Playback 1-2 (Stereo) -> Playback 1-2"]; const bounceDlg = sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first; //Mix Source - Popup Menu if (bounceDlg.popupButtons.allItems[1].value.invalidate().value !== mixSourceButtonTitle) { bounceDlg.popupButtons.allItems[1].popupMenuSelect({ menuSelector: items => items.filter(item => item.path[0].endsWith(mixSourcePath[0]) && item.path[1].endsWith(mixSourcePath[1]))[0] }); }
- TTristan Hoogland @Tristan
Hey @Kitch !
I've made a lot of progress on this (and many other scripts) in the past few months, I'll share everything once its done.
I'm trying to log the discrete output path of a track when selected so I can recall it in the bounce window. I've been successful in getting the final output path i.e. Out 1-2 or Bus 3-4, etc. But the one variable I'm having trouble logging is the step(s) before it - i.e. determining whether it's a "output" or a "bus" path it needs to go to in the submenu.
This is what i have so far:
Pt 1://get output path from mix bus const audioIO = sf.ui.proTools.selectedTrack.groups.whoseTitle.is('Audio IO'); const outputBtn = audioIO.first.popupButtons.whoseTitle.startsWith('Audio Output Path selector').first; globalState.outputPath = outputBtn.title.invalidate().value.split('\n').pop()
Recalling that path later
//Set Bounce Settings here; const mixSourceButtonTitle = globalState.outputPath; const mixSourcePath = ["output", globalState.outputPath];
That "output" path needs to be defined as either an "output" or "bus" (then bus 1-128, 129-256). Do you know how to log the state of that menu? This is the area I'm talking about.
I know I could get it with a if/else function, but that seems clunky.
Thanks K! <3
- TTristan Hoogland @Tristan
Hey @Kitch just bumping this one. I know how to pull it from most other places with menuselector, but I'm having trouble here. <3
- TIn reply toTristan⬆:Tristan Hoogland @Tristan
Hey @Kitch have you encountered any issues with OS Monterey and scripts as a general thing? @Hamish_Patrick recently got one of the new lapt
click to show - TIn reply toTristan⬆:Tristan Hoogland @Tristan
Hero! I don't have either of these so I can't be of help haha
- TTristan Hoogland @Tristan
clearly meant to respond to you, @Kitch !!!
- CIn reply toTristan⬆:clement roussel @clement_roussel
Hi everyone,
does this script have an updated version ? that would work sequoia ?
Best- TTristan Hoogland @Tristan
Hey Clement,
I've since moved on to a much more elaborate script, so sadly I outgrew this version years ago. Are you trying to accomplish the same thing as above?
- Cclement roussel @clement_roussel
Hi Tristan,
Yes indeed, trying to find a script to bounce stems as I'm trying to stop using bounce butler, i'm having way ti much errors with it- SSteve Vealey @Steve_Vealey
I've been relying on this script since 2020 but recently upgraded my computer with Sequoia. @Tristan Would you mind sharing your new version? This thread has been a god-send and your work is very much appreciated.
- TTristan Hoogland @Tristan
Hey Steve - that's super cool! Let me have a think on this and get back to you as my script has evolved a lot since this.