Hi, all.
I'm writing a script for a session that would contain a bunch of different songs/tracks in a full sequence where I need to bounce all of them individually. The different songs are always marked at their start by a memory location named after the title of the song, and always extend until the next memory location, marking the beginning of the next.
I'm trying to make a script that moves to my first marker, extends the region to the next, and bounces, setting the file name to "the name of the memory location + [bounce type]", and filling in the bounce window with the desired settings. This would repeat as many times as there are memory locations, minus 1, to handle the inclusion of an "END" marker. For example, the first 1644 WAV bounce in a session where the first track is titled "trackName1" would be named "trackName1 1644 WAV". This would handle in a similar fashion to adding MP3's, by titling those files as "trackName1 MP3-REFERENCE" in the MP3 window.
Most of the script functions as intended, but I can't figure out how to extract and store information from the memory locations window. I need the total number of memory locations, which is used to determine how many times to loop, as well as the names of all the memory locations. I want to loop the bounce function while iterating through and referencing an array of memory location names, so the process is entirely automated.
Disorganized, but potentially relevant info: Currently, referencing the "AXRow" children in the memory locations window returns an array of size 2, while using "AXColumn" returns 12. I can tell that the AXColumns are the cells that contain the labels for the columns like "#", "Marker Name", etc. I found that the "Marker Names" column is reference by "children.whoseRole.is("AXCell").allItems[2]". Not sure how helpful that is to know, but it's info I found trying to figure this out, so I figured I'd add it. I can't find any documentation on "AXCell" or "AXStaticText", but I suspect the answer could be there.
Any and all help would be greatly appreciated. I'm relatively new to JavaScript, but I've been programming for a while. Thanks in advance!
sf.keyboard.press({
keys: "return",
});
sf.wait();
sf.ui.proTools.memoryLocationsShowWindow();
sf.ui.proTools.memoryLocationsWindow.elementWaitFor();
// Get the memory locations table
const memoryLocations = sf.ui.proTools.memoryLocationsWindow.tables
.whoseTitle.is("Memory Locations")
.first.invalidate()
.children.whoseRole.is("AXRow");
// Extract memory location names
const memoryLocationNames = memoryLocations.map(l =>
l.children.whoseRole.is("AXCell").allItems[2].children.whoseRole.is("AXStaticText").first.title.value.replace("Selected. ", "")
);
// Get the total number of memory locations
const totalMemoryLocations = memoryLocationNames.length;
// Loop through for all memory locations except the 'END'
for (let i = 0; i < totalMemoryLocations; i++) {
sf.wait();
// Execute the code block for each memory location, using i for the numpad key
sf.keyboard.press({
keys: `numpad comma, numpad ${i}, numpad comma`, // Dynamically presses numpad I
});
sf.wait();
// Calling command "Extend Selection to Next Marker"
sf.soundflow.runCommand({
commandId: 'user:ckp49i4j60000a2100yfwywgf:ckou1qcq5007ozv10n96bgqcj',
props: {}
});
bounceSettings1644(memoryLocationNames[i]);
}
function bounceSettings1644(fileName) {
// Call the command to open the Bounce Mix window
sf.soundflow.runCommand({
commandId: 'user:ckp49i4j60000a2100yfwywgf:cktcnf0ra000x9r109i8wen1u#cktcms2j4000h9r103zqxnmg5',
props: {}
});
//Wait for 'Bounce Mix' window
let bounceWin = sf.ui.proTools.dialogWaitForManual({
dialogTitle: 'Bounce Mix'
}).dialog;
const safeFileName = fileName || "No Track Name"; // Default name if fileName is undefined/null
sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.textFields.first.elementSetTextFieldWithAreaValue(
{ value: safeFileName.trim() + " 1644 WAV(CD)" }
);
//Set File Type
if (bounceWin.popupButtons.first.value.invalidate().value != 'WAV (BWF)')
bounceWin.popupButtons.first.popupMenuSelect({ menuPath: ['WAV (BWF)'] });
//AUDIO PANEL
const audioPanel = bounceWin.groups.whoseTitle.is('Audio').first;
//Popup Buttons
const popupButtons = audioPanel.children.filter(e => e.fullRole == "AXPopUpButton").slice(-4);
const compressionType = popupButtons[0];
const formatBtn = popupButtons[1];
const bitDepthBtn = popupButtons[2];
const sampleRateBtn = popupButtons[3];
//Compression Type
if (compressionType.value.invalidate().value != 'PCM (Uncompressed)')
compressionType.popupMenuSelect({ menuPath: ['PCM (Uncompressed)'] });
//File Format
if (formatBtn.value.invalidate().value != 'Interleaved')
formatBtn.popupMenuSelect({ menuPath: ['Interleaved'] });
//Bit Depth
if (bitDepthBtn.value.invalidate().value != '16 Bit')
bitDepthBtn.popupMenuSelect({ menuPath: ['16 Bit'] });
//Sample Rate
if (sampleRateBtn.value.invalidate().value != '44.1 kHz')
sampleRateBtn.popupMenuSelect({ menuPath: ['44.1 kHz'] });
//Enable MP3
audioPanel.checkBoxes.whoseTitle.is('Add MP3').first.checkboxSet({
targetValue: 'Enable'
});
sf.wait();
sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first.buttons.whoseTitle.is("Bounce").first.elementClick();
sf.ui.proTools.windows.whoseTitle.is('MP3').first.textFields.first.elementSetTextFieldWithAreaValue(
{ value: safeFileName.trim() + " MP3-REFERENCE" }
);
sf.wait();
sf.ui.proTools.windows.whoseTitle.is("MP3").first.buttons.whoseTitle.is("OK").first.elementClick();
// Wait for no modals before continuing with the script
sf.ui.proTools.waitForNoModals();
}
- Ddanielkassulke @danielkassulke
Hi @jpdharing,
I have a few scripts that do versions of this. For example, I have a "Bounce nearest selection region" that lets you select sample rate and bit depth from a drop down list, and appends that to the bounce name. Both of these scripts bounce selection-type markers that contain a string, in my case "BNC". It seems to me like you would get a lot of mileage out of those scripts, but they would rely on selection-type markers rather than location-type markers. Would it fit with your workflow to rely on selection markers?
- Jjpdharing @jpdharing
It's possible that something like that could work. I can always modify, however, I do work at a facility that standardizes certain things like this, so I'm not perfectly sure at the moment. My issue isn't so much in changing the text field itself or appending, that I have working. My issue is with referencing the names of my markers so I can store them into a map/variable, and referencing the total number of markers. Are you referencing the names of your markers in your scripts?
- Ddanielkassulke @danielkassulke
Can you provide a screenshot showing how you would typically label these markers in your session? If you could show a couple of these in your screenshot that would be ace.
- Jjpdharing @jpdharing
Actually, was given a solution on this thread that works for what I'm doing, I really appreciate your input though! I started looking into selection markers after you mentioned them and I've already found a few applications for myself, so, again, thanks for helping out!
In reply tojpdharing⬆:Sreejesh Nair @Sreejesh_NairThis will give you marker name, the marker number, start time and the total count of markers in the session using the Pro Tools SDK. You can then use these values to modify your script.
const markerNameList = sf.app.proTools.memoryLocations.invalidate().allItems.map(m => ({ name: m.name, number: m.number, startTime: m.startTime })); // Marker names const markerNames = markerNameList.map(marker => marker.name); // Marker numbers const markerNumbers = markerNameList.map(marker => marker.number); // Marker start const markerStart = markerNameList.map(marker => marker.startTime); //Total Marker Count const markerCount = markerNameList.length;The following code will go through the marker in order of start time, in case the markers are not created that way, and make selections. You can insert your bounce code within that. You can use the SDK methods for this.
const markerNameList = sf.app.proTools.memoryLocations.invalidate().allItems.map(m => ({ name: m.name, number: m.number, startTime: m.startTime })); // Sort markerNameList by startTime const markerInTimelineOrder = markerNameList .slice() .sort((a, b) => parseInt(a.startTime, 10) - parseInt(b.startTime, 10)) // Convert startTime to number and sort .map(marker => marker.startTime); // Store sorted names //Make selections and loop till final location for (let i = 0; i < markerInTimelineOrder.length - 1; i++) { const startMarker = markerInTimelineOrder[i]; const endMarker = markerInTimelineOrder[i + 1]; sf.app.proTools.invalidate(); sf.app.proTools.setTimelineSelection({ inTime: startMarker, outTime: endMarker }); //Put your bounce code here }- Jjpdharing @jpdharing
This is perfect, I can definitely make this work with what I've got. Thank you so much!