I've got a bit of a doozy here. I'm using memory locations to automatically place BGs throughout my timeline. I'm naming all scene-change markers "BG something" (i.e. "BG City Night" or "BG Warehouse"). I'd like to be able to paste to fill within all identically named 'scenes'.
In order to do this, I'm simply looping through each memory location by identical name, nudging my cursor over a frame, "selecting between memory locations", and fill-pasting.
const markers = sf.proTools.memoryLocationsFetch().collection["List"].filter(m =>
m.name.includes("BG CityStreetNight"))
for(let i = 0; i < markers.length; i++) {
//Calling command "Go to Marker BG CityStreetNight" from package "undefined" (installed from user/pkg/version "di7dN0keyHdE1HAif7Qi47Oz53E2/cksaoi7h50002iu10rj34wut2/cliyyqz970000dx10amw7ygcl")
sf.soundflow.runCommand({
commandId: 'user:clkehtez10008ns10nx65f61i:ckzhjqw3a000lkr10xuofy46g#clkg5gm01000e8l10uqhswwb4',
props: {}
});
//moving cursor between markers
sf.keyboard.press({
keys: "period",
});
//select between markers
sf.ui.proTools.memoryLocationsSelectBetween();
//paste to fill inside of markers
sf.keyboard.press({
keys: "cmd+alt+v",
});
}
The problem is that I have a ton of NON-BG memory locations on my timeline, so my selections are all beginning in the right place but are often ending in the wrong place once they hit a "Missing mic" or "Mx request" memory location, for example.
SO -- I'm looking to paste-fill between each memory location of a given name (in the example above, "BG CityStreetNight" and the next memory location that includes the string "BG". In other words, it needs to ignore memory locations that don't begin with "BG"
Anyone able to help out?
- OOwen Granich-Young @Owen_Granich_Young
This may help? It excludes instead of filters for next BG but maybe it can be reverse engineered?
- AAMPM101 @AMPM101
Oh nice — this should be easy to adjust! Will try tomorrow. Thanks!
- AIn reply toAMPM101⬆:AMPM101 @AMPM101
For anyone in the future who needs this:
// Bg of Concern const bgOfConcern = 'BG Downtown' /** * @param {object} selection - initial user selection */ const getLegalMemLocs = (selection) => sf.proTools.memoryLocationsFetch().collection['List'].filter(m => m.name.includes('BG') && m.mainCounterValue >= selection.selectionStart && m.mainCounterValue <= selection.selectionEnd ); const getBGofConcernFilteredMemLocs = (selection) => sf.proTools.memoryLocationsFetch().collection['List'].filter(m => m.name.includes(bgOfConcern) && m.mainCounterValue >= selection.selectionStart && m.mainCounterValue <= selection.selectionEnd ); function timeCounter() { // get current counter const mainCounter = sf.ui.proTools.getCurrentTimecode().stringValue let originalTimeCounter /// Bars Beats . if (mainCounter.includes("|")) originalTimeCounter = "Bars|Beats" // Min Secs . else if (mainCounter.includes(":") && mainCounter.includes(".")) originalTimeCounter = "Min:Secs" // Time code else if (mainCounter.split(":").length == 4) originalTimeCounter = "Timecode" // Feet+Frames else if (mainCounter.includes("+")) originalTimeCounter = "Feet+Frames" // Samples. else originalTimeCounter = "Samples" return originalTimeCounter } /** * @param {number} startN * @param {number} endN */ function selectBetween(startN, endN) { sf.ui.proTools.memoryLocationsGoto({ memoryLocationNumber: startN }); sf.ui.proTools.memoryLocationsGoto({ memoryLocationNumber: endN, extendSelection: true }); }; function main() { sf.ui.proTools.invalidate(); const originalTimeCOunter = timeCounter(); sf.ui.proTools.mainCounterSetValue({ targetValue: "Samples" }); const originalSelection = sf.ui.proTools.selectionGetInSamples(); const legalMemLocs = getLegalMemLocs(originalSelection); const bgofConcernFilteredMemLocs = getBGofConcernFilteredMemLocs(originalSelection); const filteredMemLocs = legalMemLocs.reduce((acc, obj, index) => { if (obj.name === bgOfConcern) { acc.push(obj); if (index < legalMemLocs.length - 1) { acc.push(legalMemLocs[index + 1]); } } return acc; }, []); try { for (let i = 0; i < filteredMemLocs.length; i += 2) { selectBetween(filteredMemLocs[i].number, filteredMemLocs[i + 1].number); sf.ui.proTools.menuClick({ menuPath: ["Edit", "Paste Special", "Repeat to Fill Selection"] }); }; } catch (err) { throw err } finally { sf.ui.proTools.selectionSetInSamples({ selectionStart: originalSelection.selectionStart, selectionEnd: originalSelection.selectionEnd }); sf.ui.proTools.mainCounterSetValue({ targetValue: originalTimeCOunter }); } } main();
- OOwen Granich-Young @Owen_Granich_Young
Nice work!
You're good with templating right? Will be a much happier script as a template.
Alternatively, you could also make it one where it prompts for 'BG of Concern' either via string or searching current Memory locations so you have a 1 button solution.
- AAMPM101 @AMPM101
That's a good idea!
I'm not very familiar with templating in soundflow -- I've been brute forcing through every BG style with a new script haha...Tips on templating?
- OOwen Granich-Young @Owen_Granich_Young
There's not super good documentation out there, here's me walking someone through templating for a string with a video with no sound:
https://www.dropbox.com/s/4qemef5lr3fhk2e/Creating Template with String.mov?dl=0You should be able to rip the 'search memory locations code for the const pretty easily that's probably the sexier version instead of giant deck you're picking from. I THINK something like this for your
const bgOfConcern
gets you there? (PSlet
andconst
very similar, I'm not the best at knowing which to use when...)function prompt(item) { let prompt = sf.interaction.popupSearch({ items: item.map(x => ({ name: x["Name"] })) }).item.name return prompt }; let selectedMemoryLocations = sf.proTools.memoryLocationsFetch().collection['List'] let promptValue = prompt(selectedMemoryLocations)
- AAMPM101 @AMPM101
That was pretty easy! Converted it all over to a template and presets. Thanks again!
- OOwen Granich-Young @Owen_Granich_Young
You bet! Did you try the 'Search Memory Locations' version? I was like you when I started building BG scripts (I base all mine off of Scene cut tracks instead of memory locations) where I had it templated for every BG is a button, but I soon grew tired of the 'hunt and peck' when the BG deck got too big and found the one button on the deck Search Step a bit faster actually.
- AAMPM101 @AMPM101
I'm about to try that right now cuz I love the idea. I don't mind having the big deck for placing markers -- that way I know I'm always naming things with a convention instead of "wait was I calling the cabin scenes 'nature' or 'forest'". But when it comes to filling in, the search function makes a lot of sense and would eliminate the big deck I have.
Re: cut tracks vs memory locations, I have only recently switched to memory locations for BGs and the only reason I switched was because it became faster for me to tab through a cut track and just hit a stream deck button every time the scene changed, vs holding shift and tabbing and then creating a clip group and then renaming the clip to "BG Office" or whatever. But I'm sure there's a more automated way of doing that as well!
- OOwen Granich-Young @Owen_Granich_Young
Yeah, Ediload can generate a Scene Change list, and if you load in the script it will name all the scenes too. Which is really really sexy. Because it's all named and ready to start filling your BG's that way. ;)
- AAMPM101 @AMPM101
That sounds real nice -- sadly I never get EDLs haha. Mostly working on reality TV and unscripted/doc series and they usually turn me down if I ask for an EDL. So I use "Cut It" the app.
- OOwen Granich-Young @Owen_Granich_Young
ahhh Yes, that makes the sense.
- AAMPM101 @AMPM101
Got the memory location search working -- thanks again for the help and the idea!
- OOwen Granich-Young @Owen_Granich_Young
Post that one! Or update above?
You bet, hit me with anything anytime.
- AAMPM101 @AMPM101
Update here for all those future folks who stumble into this thread!
Note: this version does not use a selection but fills-to-paste for every BG memory location on the timeline. You simply select a track and this will prompt you for the mem location name you're after, then fill the whole timeline.
function prompt(item) { let prompt = sf.interaction.popupSearch({ items: item.map(x => ({ name: x["Name"] })) }).item.name return prompt }; let selectedMemoryLocations = sf.proTools.memoryLocationsFetch().collection['List'] let bgOfConcern = prompt(selectedMemoryLocations); /** * @param {object} selection - initial user selection */ const getLegalMemLocs = () => sf.proTools.memoryLocationsFetch().collection['List'].filter(m => m.name.includes('BG') ); function timeCounter() { // get current counter const mainCounter = sf.ui.proTools.getCurrentTimecode().stringValue let originalTimeCounter /// Bars Beats . if (mainCounter.includes("|")) originalTimeCounter = "Bars|Beats" // Min Secs . else if (mainCounter.includes(":") && mainCounter.includes(".")) originalTimeCounter = "Min:Secs" // Time code else if (mainCounter.split(":").length == 4) originalTimeCounter = "Timecode" // Feet+Frames else if (mainCounter.includes("+")) originalTimeCounter = "Feet+Frames" // Samples. else originalTimeCounter = "Samples" return originalTimeCounter } /** * @param {number} startN * @param {number} endN */ function selectBetween(startN, endN) { sf.ui.proTools.memoryLocationsGoto({ memoryLocationNumber: startN }); sf.ui.proTools.memoryLocationsGoto({ memoryLocationNumber: endN, extendSelection: true }); }; function main() { sf.ui.proTools.invalidate(); const originalTimeCOunter = timeCounter(); sf.ui.proTools.mainCounterSetValue({ targetValue: "Samples" }); const legalMemLocs = getLegalMemLocs(); const filteredMemLocs = legalMemLocs.reduce((acc, obj, index) => { if (obj.name === bgOfConcern) { acc.push(obj); if (index < legalMemLocs.length - 1) { acc.push(legalMemLocs[index + 1]); } } return acc; }, []); try { for (let i = 0; i < filteredMemLocs.length; i += 2) { selectBetween(filteredMemLocs[i].number, filteredMemLocs[i + 1].number); sf.ui.proTools.menuClick({ menuPath: ["Edit", "Paste Special", "Repeat to Fill Selection"] }); }; } catch (err) { throw err } finally { sf.ui.proTools.mainCounterSetValue({ targetValue: originalTimeCOunter }); } } main();
- In reply toAMPM101⬆:
Nathan Salefski @nathansalefski
I've been trying to reduce this code to select between two memory locations i.e. a
'START'
marker and an'END'
marker. I have read two other threads but it doesn't seem to be functioning for me (these two to be specific, Selecting between 2 markers using marker names., Bounce between start/end markers not working in PT 2023). Any suggestions on how to reduce this to simply select between those two markers? Trying to implement this into a larger Print Script.THANKS!!