Trim Fader Across Highlighted Session
Hi All,
Been searching the Forum, experimented a lot, and not really coming up with a lot of solutions how to automate this one. For Broadcast mixing with have to hit LKFS numbers all day long. Typically we get a number after our first playback, and trim our masters up or down (usually down) to get us closer.
This would be across a Master Fader to pull the overall level down. Kinda happens like this.
First I'd highlight the width of the session I'd like to pull down (usually a 45 minute section of the session). Manually, not automated.
Soundflow would take it from here.
1.) Show Trackview
2.) Show Volume Trim line
3.) Here I'd love for a box to open to ask how far to trim Up or Down. (enter as a +3 for db would be fine)
4.) Pull the Volume Trim line across the Selection. (up or down depending on level amount I input on Line 3 above)
5.) Shut off Volume Trim line (so it just looks like a normal volume automation line again)
I'd do this across all my Stems ultimately. Dialogue, Music, Group, Vocals, Narration.
Any thoughts on directions to take?
In advance....Thanks!
...Andy D.
Linked from:
- AAndy Daddario @Andy_Daddario
(up or down depending on the input level of line 3) is what that should have said on item #4.
Thanks!
Christian Scheuer @chrscheuer2020-11-05 10:22:00.061Z
Would it work for you, if we used Automation Preview as a way to write the trim automation to the selection (and then I'm assuming you have PT set to coalesce the trim auto to volume auto when exiting trim auto mode?)
We can't reliably automate dragging on the actual lines, but we could automate setting the trim value of the selection to a set value (supplied by the user).
- AAndy Daddario @Andy_Daddario
Hey Christian, yes that would work just fine.
Christian Scheuer @chrscheuer2020-11-07 14:18:09.687Z2020-11-07 17:14:01.153Z
So... this turned out to be fairly complex, because there's a lot of setup that needs to be made correctly.
The script needs to set up:- Ensure automation window is open
- Enable preview
- Enable volume automation
- Set the track to "touch trim" mode
- Ensure track output window is open
Before actually making the change and then writing the automation, and then restoring any settings that it temporarily changed.
The current script also assumes that you have automation warning dialogs ON - as this is the recommended setting when working in SF (since SF then monitors and ensures that you don't accidentally overwrite the whole session).
Here it goes:
/**@param {{ track: AxPtTrackHeader, automationMode?: string, automationTrim?: boolean, enableVolumeAuto?: boolean, enablePreview?: boolean, action: () => void, }} args */ const withAutomationMode = (args) => { const { track, automationMode, automationTrim, enableVolumeAuto, enablePreview, action, } = args; if (!track || !track.exists) throw `Track is required`; //Define reusable funcs to query the state before/after const isAutoWindowOpen = () => sf.ui.proTools.automationWindow.invalidate().exists; const isAutoVolumeEnabled = () => sf.ui.proTools.automationWindow.enableVolumeAutoButton.value.invalidate().value === "Selected"; const isAutoPreviewEnabled = () => (sf.ui.proTools.automationWindow.previewButton.value.invalidate().value === "Selected"); const getTrackAutomationMode = () => track.automationModeButton.value.invalidate().value.replace(/trim/g, '').trim(); const isTrackAutomationTrim = () => (track.automationModeButton.value.invalidate().value + '').includes('trim'); let initialState = { automationMode: undefined, automationTrim: undefined, autoWindowOpen: undefined, autoVolumeEnabled: undefined, autoPreviewEnabled: undefined, }; try { //Grab initial state initialState.automationMode = getTrackAutomationMode(); initialState.automationTrim = isTrackAutomationTrim(); initialState.autoWindowOpen = isAutoWindowOpen(); //Ensure automation window is open if (!initialState.autoWindowOpen) sf.ui.proTools.automationWindowRequireOpenAutomationWindow({}, `Couldn't open automation window`); //Complete the initialState, now that the automation window is ensured to be open initialState.autoVolumeEnabled = isAutoVolumeEnabled(); initialState.autoPreviewEnabled = isAutoPreviewEnabled(); //Select the track - TODO: This state change is currently not restored track.trackSelect({}, `Couldn't select track ${track.normalizedTrackName}`); //Ensure track is in the proper automation mode track.automationModeSet({ automationModeName: automationMode, trackTargetMode: 'SelectedTracks' }, `Couldn't select automation mode "${automationMode}" for track`); if (automationTrim !== undefined) { if (initialState.automationTrim !== automationTrim) track.automationModeSet({ automationModeName: 'trim', trackTargetMode: 'SelectedTracks' }, `Couldn't select automation mode "trim" for track`); } //Enable/disable volume auto if (enableVolumeAuto !== undefined && initialState.autoVolumeEnabled !== enableVolumeAuto) sf.ui.proTools.automationWindow.enableVolumeAutoButton.elementClick(); //Enable/disable preview if (enablePreview !== undefined) sf.ui.proTools.automationPreview({ targetValue: enablePreview ? 'Enable' : 'Disable' }); //Perform whatever it is you want to happen try { action(); } catch (err) { throw { inner: err }; } } catch (err) { //Errors happening inside the action shouldn't be wrapped by us, so re-throw them directly if (err.inner) throw err.inner; throw `Error setting up automation.\n${err}`; } finally { //Restore initial state, ignoring potential errors if (initialState.automationMode !== undefined && initialState.automationMode !== getTrackAutomationMode()) track.automationModeSet({ automationModeName: initialState.automationMode, onError: 'Continue' }); if (initialState.automationTrim !== undefined && initialState.automationTrim !== isTrackAutomationTrim()) track.automationModeSet({ automationModeName: 'trim', trackTargetMode: 'SelectedTracks', onError: 'Continue' }); if (initialState.autoPreviewEnabled !== undefined && initialState.autoPreviewEnabled !== isAutoPreviewEnabled()) sf.ui.proTools.automationPreview({ targetValue: 'Toggle', onError: 'Continue' }); if (initialState.autoVolumeEnabled !== undefined && initialState.autoVolumeEnabled !== isAutoVolumeEnabled()) sf.ui.proTools.automationWindow.enableVolumeAutoButton.elementClick({ onError: 'Continue' }); if (initialState.autoWindowOpen === false) sf.ui.proTools.automationWindow.windowClose({ onError: 'Continue' }); } } /**@param {{ track: AxPtTrackHeader, action: (AxWindow) => void, }} args */ function withTrackOutput(args) { const { track, action } = args; //Define reusable state funcs function getOutputWin() { return sf.ui.proTools.floatingWindows.whoseTitle.is('').filter(w => { let trackSel = w.children.whoseTitle.startsWith('Track selector').first; if (!trackSel.exists) return false; return trackSel.value.value === track.normalizedTrackName; })[0]; } let initialState = { outputOpen: undefined, }; try { let outputWin = getOutputWin(); initialState.outputOpen = !!outputWin; if (!initialState.outputOpen) { track.trackOutputToggleShow({}, `Couldn't open track output window.\nMake sure you're displaying track I/O`); sf.waitFor({ callback: () => { outputWin = getOutputWin(); return !!outputWin; }, timeout: 2000, }, `Track output window didn't open`); } try { action(outputWin); } catch (err) { throw { inner: err }; } } catch (err) { //Errors happening inside the action shouldn't be wrapped by us, so re-throw them directly if (err.inner) throw err.inner; throw `${err}`; } finally { //Restore initial state, ignoring potential errors if (initialState.outputOpen === false) track.trackOutputToggleShow({ onError: 'Continue' }); } } /**@param {{ track: AxPtTrackHeader, amount: number, autoConfirmation: boolean }} args */ function trimVolume(args) { const { track, amount, autoConfirmation } = args; if (!track) throw `Track is required`; if (isNaN(Number(amount))) throw `Amount must be a number`; /**@param {AxWindow} trackOutputWin */ function main(trackOutputWin) { try { let volumeField = trackOutputWin.textFields.whoseTitle.is('Volume Numerical').first; if (!volumeField.exists) throw `Couldn't find volume field`; volumeField.elementClick({}, `Couldn't focus volume text field`); sf.keyboard.type({ text: String(amount) }, `Couldn't type trim amount`); sf.keyboard.press({ keys: 'enter' }, `Couln't press enter`); sf.wait({ intervalMs: 50 }); sf.keyboard.press({ keys: 'left' }, `Couldn't update Pro Tools state`); //to update things... sf.ui.proTools.automationWindow.writeAutoToSelectionButton.proToolsAutomationClickButton({ autoConfirmation: autoConfirmation, }, `Couldn't write automation to selection.\nEnsure your automation warning dialogs are ${autoConfirmation ? "on" : "off"}`); } catch (err) { throw `Couldn't trim volume.\n${err}`; } } withAutomationMode({ track, automationMode: 'touch', automationTrim: true, enableVolumeAuto: true, enablePreview: true, action: () => { withTrackOutput({ track, action: main, }); } }); } function main() { let amountStr = prompt(`How much do you want to raise/lower the dB of the selected region?`); if (amountStr === undefined || amountStr === '' || String(amountStr).trim() === '') throw 0; //To make it easier in countries that use ',' as decimal point amountStr = amountStr.replace(/,/g, '.'); let amount = Number(amountStr); if (isNaN(amount)) throw `The amount needs to be a number, for example "-0.5"`; trimVolume({ track: sf.ui.proTools.invalidate().selectedTrack, amount, autoConfirmation: true, }); } main();
Christian Scheuer @chrscheuer2020-11-07 14:19:50.202Z
cc @Kitch, @Andrew_Scheps, @Dustin_Harris - see here for a good way to abstract away complex initial setup and cleanup, to keep the actual code more simple.
Dustin Harris @Dustin_Harris
Oh wow, this is beautiful. I have a lot of studying to do here. Thanks!
Christian Scheuer @chrscheuer2020-11-07 16:35:41.003Z
Yay :) Just edited the script and refactored some things to make for even better "best practices" for complex initial state / state restoration and error handling.
Dustin Harris @Dustin_Harris
If I'm reading the script right, this might be a worthwhile check too (or is it already handled as well?)
if (sf.ui.proTools.getMenuItem('Window', 'Hide All Floating Windows').isMenuChecked == true) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Hide All Floating Windows"], });
Christian Scheuer @chrscheuer2020-11-07 16:46:09.718Z
This could break other potential floating windows you have open. The script should already handle closing any windows that it opens.
Dustin Harris @Dustin_Harris
You're right, I just tested it; I was thinking that if that menu item was checked, new floating windows would NOT appear, but they do, so disregard that statement. (I use that in scripts to temporarily hide windows to ensure the Start / End / Length fields in the main counter aren't obscured)
- In reply tochrscheuer⬆:
Dustin Harris @Dustin_Harris
I'm going to have to study that script a lot. There is so much syntax here I'm unfamiliar with 😅
- In reply tochrscheuer⬆:
Christian Scheuer @chrscheuer2020-11-07 16:44:47.066Z
I've now added this as a template to the "Pro Tools Utilities" package as: "Trim Volume Automation in Selection" :)
Josh Cruz @spacebearaudio
I was forwarded to this thread from a similar question I had. Think this script will do pretty much what I need, but whenever I try to run it from the utilities package or the one above, it keeps giving me an error saying it can't open up the automation window. Any ideas?
Christian Scheuer @chrscheuer2021-05-31 12:33:41.469Z
Are you on PT Ultimate?
Josh Cruz @spacebearaudio
I am not. Is that required? I must have missed that.
If that is the case, is there any way to make it available with standard?
Christian Scheuer @chrscheuer2021-05-31 17:06:18.880Z
Hi Josh,
The version I built relies heavily on Ultimate features (automation preview in particular), so I think it would have to be started from scratch to make it work in the vanilla version.
I have to be honest and say that I don't know if it would be possible, and due to my work on the SF app itself I probably won't have time to look at this in the near future - but maybe if somebody (or you) could write up the steps required to do this manually on vanilla PT, that would be a great first step for trying to automate it there.
Please start that as a new thread linking to this though, just to keep things a bit more easy to organize :)
- In reply tochrscheuer⬆:AAndy Daddario @Andy_Daddario
WOW Christian, holy cow! Yeah, I can now see why I couldn't get anywhere....(huge LOL). I can't wait to get back in the studio and give this a try.
So new question....how do I move to the next level of complexity? I've done ok writing basic Macros, turning them into Scripts, making some small modifications and called it "close enough". I've struggled with I think you call them "child" menus that open, (a sub-menu of a menu) and still really not getting I guess. (at least consistently)
I've made some small progress, and maybe that's all I can expect and just takes hours of trial and error (as @Andrew_Scheps mentioned on his seminar). I'm not looking for short cuts, I want to learn, but not even sure where to begin on some ideas I want to try (like this one).
I've seen the page you've been pointing us to recently (I'm sorry I don't have it right in front of me) with all the Java commands. What I need I guess is how to put all that together? Or, is it just years of working in Java?
Very impressive Christian, there's a lot of logic going in that script that will take me quite awhile to wrap my head around.
Thanks for taking all that time, I'm sure it was quite the challenge.
Thanks!........Andy D.
Christian Scheuer @chrscheuer2020-11-09 12:46:17.405Z
Hi Andy :)
Happy to help!
For how to best learn... I guess you may actually get a better answer from the likes of @Dustin_Harris, @Kitch and @Andrew_Scheps - who all mostly learned by themselves and by using the forum. How did you guys get started making more advanced scripts?
Andrew Scheps @Andrew_Scheps
Searching the forum was by far the best way. Dissecting scripts that Christian had posted and reading tons of other posts.
Another thing that helped a lot was taking the scripts from the forum and modifying them just to see what everything did. I also spent a bunch of time trying to answer other people's How To questions, even if it was nothing I would ever need myself.
And lastly, I haven't slept in 6 months...
Andrew Scheps @Andrew_Scheps
I would also say that this script has a very advanced structure. Don't get thrown off by the second function call
main
inside thetrimVolume
function. It is completely separate from themain
script at the bottom that gets everything going. (@chrscheuer by the way, what's the thinking behind calling that onemain
too?)Also, within the
trimVolume
function, the object being sent towithAutomationMode
sends that secondmain
function as a variable calledaction
within an object that's sent towithTrackOutput
. So basically (if I have this right) the track is sent towithTrackOutput
along with the secondmain
function and what comes back from there is sent along with the rest of the object towithAutomationMode
You kind of have to unwrap it in your head because the order of operations can seem backwards on first read.
It's going to take me weeks to completely unpack this one...
Christian Scheuer @chrscheuer2020-11-09 13:06:23.814Z
I'd be happy to do a public webinar where we discuss complex scripts :) We could take this as an example.
Andrew Scheps @Andrew_Scheps
That would be great!
Christian Scheuer @chrscheuer2020-11-09 14:22:14.604Z
Let's do it :)
Chris Shaw @Chris_Shaw2020-11-13 19:52:44.740Z
Would love this!
- In reply tochrscheuer⬆:AAndy Daddario @Andy_Daddario
Yes would love that Christian. Just speaking for myself, I get really confused fast when writing directly in scripts (and not starting in a Macro) as the choices open as you type. Because the majority of them I have no idea what they mean, I just kinda back out and re-think how I can do it with Macros. Once I have it "kinda" working in a Macro, convert it to a Script and break it down a bit. That helps a little, but I still never really get the logic of actually writing directly in Java.
But definitely would watch anything you do as far as writing complex Scripts directly.
Thanks again for taking all that time, I'm finally headed to the studio tomorrow to give it a spin.
Thanks!......Andy D.
- In reply toAndrew_Scheps⬆:
Dustin Harris @Dustin_Harris
I echo (delay) what @Andrew_Scheps said; dissecting scripts here in the forums and downloading other people’s packages and looking how they have implemented things really helped understand how SoundFlow interfaces with programs. Another thing I did was work through these lessons here to understand the JavaScript language itself:
https://javascript.info/You can use the SoundFlow script editor as a sandbox to run the code examples in the tutorial above.
Note: where it says
console.log(‘your message here’)
SoundFlow uses
log(‘your message here’)
- AAndy Daddario @Andy_Daddario
Thank you for the tips Dustin! I'll check it out for sure!.....Andy D.
- In reply toAndrew_Scheps⬆:AAndy Daddario @Andy_Daddario
Hysterical Andrew, (not sleeping in 6 months) you made me laugh out here in LA!.....:)
My eyes start to glaze over after about the 5th line of Java code, so I found the perfect "sleeping pill" replacement......lol....:)
- In reply tochrscheuer⬆:
Kitch Membery @Kitch2020-11-09 18:21:57.870Z
Hi @Andy_Daddario,
Firstly, I agree with everything @Dustin_Harris and @Andrew_Scheps have said here in this post.
My process of learning Javascript (Still learning) was to break things down into the smallest pieces. I started out (like you) by creating macro's and then testing all the properties to see what they did. I'd then convert them into script form and see what changed. When I got stuck, I asked questions on the forum. My questions would be clear and specific usually just focusing one one section or line of code. by doing this I got a greater understanding of how the building blocks worked. Each thing I learned helped me to understand the next concept.
Be sure to check out the video's that I've been making here in the Learning SoundFlow playlist on YouTube which can also be found in the "Tutorials" category in the forum. And if you have something specific you'd like me to make a video on I'd love to hear your suggestions.
Also... @chrscheuer has put together a great collection of Javascript Resources that are invaluable when trying to get my head around stuff.
Most of all... enjoy the process and know that the SoundFlow community is here to help! :-)
Rock on!- AAndy Daddario @Andy_Daddario
Thanks Kitch. Yes, I've been taking in all your videos. Each one is a building block for sure. The XY coordinates for a UI Element had been especially helpful. I was doing it completely by trial and error previously until I magically hit the right spot......Big LOL!!!.
But when I looked at what Christian wrote for this I was thinking, "yeah, I've got a VERY long way to go here". I honestly thought it wouldn't be "quite" that difficult, but clearly it is.
Thanks again for all the tips, much appreciated!......Andy D.
Kitch Membery @Kitch2020-11-12 08:28:56.978Z
@Andy mate!
Thanks for the kind words. You are totally welcome, I'm glad the videos are helping. Let me know if there is anything you get stuck on as I'm keen to hear what people want to learn.
Rock on!
- AAndy Daddario @Andy_Daddario
You'll soon regret saying "Let me know if there is anything you get stuck on"......LOL!!!!
Like a Monty Python movie, the scroll will roll out!........:)
- In reply toAndy_Daddario⬆:
Chris Shaw @Chris_Shaw2020-11-13 19:57:23.840Z
If you're looking to get your hands dirty with JavaScript, I found this series of tutorials extremely helpful. Unlike most JS videos these are geared for JS only and do not involve much, if any, HTML.
Watching these and dissecting scripts in the store and forum got me going pretty quickly.
https://www.youtube.com/watch?v=zjVFuft1O2Y&list=PLyuRouwmQCjkYdv4VjuIbvcMZVWSdOm58- AAndy Daddario @Andy_Daddario
Thank you Chris, I'll check em out!
....Andy D.
- In reply tochrscheuer⬆:AAndy Daddario @Andy_Daddario
Hey Christian, works beautifully!!!! The only small change might be to make sure the I/O view is on in the edit window. Typically in my film mix template its not on as that's all pre-assigned. But that's obviously an easy add I actually can do on my own.....:)
Thanks again, an amazing time and button pushing saver!!!.........Andy D.
P.S. our ADR mixer Jamieson was showing me all the cool new stuff he's been doing in SF this morning. Especially in ADR with talent and film makers not really wanting to be here at all (well before too, but even more so now in our Covid world). Huge deal man. You need to apply for Emmy, Oscar, CAS, MPSE engineering consideration next year. This is a big deal Christian!
Christian Scheuer @chrscheuer2020-11-12 19:25:01.825Z
Wow, that's an awesome testimonial. Thank you so much for the kind words, Andy!
- In reply tochrscheuer⬆:AAndy Daddario @Andy_Daddario
Hey Christian, one follow up question. Because Andy is "insanely lazy" when it comes to button pushing, how would I take this one step fruther? Once the level amount is set (let's say -3db), how would I apply that to the remaining Stems without having to input the number each time?
Easy for me to script and Select the next Master Track Fader through my Stems, but how would I capture the input number (using -3db as the example) as the variable and apply it to the remaining Master Faders? (without having to input it each time on the screen).
Otherwise works beautifully. Added the short script to make sure the I/O view is active, and works perfectly.
Thanks!.......Andy
Christian Scheuer @chrscheuer2020-11-12 19:13:32.111Z
Hi Andy,
Check the template version I added of this in the "Pro Tools Utilities" package. In that, you can have a preset with -3dB for example :)
- AAndy Daddario @Andy_Daddario
Hey Christian, not sure why but getting an error on using these. I did make sure the I/O view was open.
- AAndy Daddario @Andy_Daddario
Let me clarify, I'm getting this in the Package, not the script you posted which is working beautifully.
- In reply tochrscheuer⬆:AAndy Daddario @Andy_Daddario
Whoops.....Never mind. My PT system sometimes stops following commands, not sure why. Did a full re-start and it's all fine now. I haven't been able to figure it out, but about once a day some of the SF commands stop communicating.
All is good on the new Package, completely on my end. Thanks......Andy