I'm getting a Line 80 error
Hi David,
I seem to have a new problem with Stem Bounce. Last week it worked fine and this week, after an update, it seems to be struggling. I've named every track, deleted what isn't names, turned all my groups and whatnot off. I highlight the entire song with all tracks, enter the workflow, and I can get up to the point where I name the stems folder and choose the output. But from there I get a failed error, citing Line 80. "Popup Menu was not found. Bounce Tracks: Line 80. Popup window was not found after waiting 2000ms."
Any ideas?
- SSoundFlow Bot @soundflowbot
Thanks for posting a question or an issue related to the 'Stem Bounce' package.
This package is made by @David_Simpson. We're auto-tagging them here so that they will hopefully be able to help you.Please note, that the best way to get help with a script, macro or other content installed from the Store is to select the script you installed, then click the red Need help button, and then click "Get help with this script or macro".
By using this workflow, the developer of the package will get access to more information so they'll be able to help you quicker.
You can read more about how to best get help in this article: bit.ly/sfscripthelp - BIn reply toJordan_Young⬆:Bao Pham @Bao_Pham
Having this exact same issue. I'm currently on 2024.3. was working fine on 2023.12. @David_Simpson could you please update?
- BIn reply toJordan_Young⬆:Bao Pham @Bao_Pham
Here's a link to fix the script, something has changed in the latest protools updates
I had an issue with the pop up menu as well, and this part of the script should fix the issue. Just need @David_Simpson to kindly update please
- In reply toJordan_Young⬆:Chris Shaw @Chris_Shaw2024-03-25 16:13:45.513Z
SF 5.7 now clicks elements in the center middle instead of the top left. While this shouldn't effct most scripts, if you are using relative clicks then you probably need to set the anchor property to
"TopLeft"
.
See this post for other options and detailed explanation: Track Height selection not working with PT 2024.3 #post-2- BBao Pham @Bao_Pham
i'm on 5.7.6, i would change it myself but the script is not open or made available to change. Hopefully @David_Simpson can get to it, but from the looks of past posts he's not very active on here. I may have to look for another stem bouncing package elsewhere, but his is excellent, just doesn't work right now with 2024.3
- EIn reply toJordan_Young⬆:Edward Sokolowski @Edward_Sokolowski
Would love for this script to be updated, it was so good but PT broke compatibility with it a few versions back.
- TIn reply toJordan_Young⬆:T Michaels @T_Michaels
I can't get access to the actual script code anymore, but when I first had this problem and posted it on here I somehow did get to see it. Pasted below, if anyone can help out seems like there'd be a bunch of us here very grateful!
sf.ui.proTools.appActivateMainWindow()
sf.ui.proTools.windows.invalidate()
sf.ui.proTools.mainWindow.invalidate()let bouncedStems = []
// FUNCTIONS -------------------------------------------------------------------------------------
// SAVE USER CONFIG WINDOW
function saveUserWinConfig() {
sf.ui.proTools.appActivateMainWindow()
sf.ui.proTools.menuClick({ menuPath: ['Window', 'Configurations', 'New Configuration...'] })
// Wait for the Mem Location dialog to appear and assign it to the memLocDlg variable
const winConfigDlg = sf.ui.proTools.dialogWaitForManual({
dialogTitle: 'New Window Configuration'
}).dialog// We want to save a window layout winConfigDlg.radioButtons[1].elementClick() // We want to include all windows winConfigDlg.checkBoxes[0].checkboxSet({ targetValue: 'Enable' }) // Name the Location Track Visibilty winConfigDlg.textFields.allItems[1].elementSetTextFieldWithAreaValue({ value: 'User Window Setup' }) // Get the Location Number and pass it back so we can delete it when we're done const configNumber = winConfigDlg.textFields.allItems[0].value.invalidate().value // Create the Location winConfigDlg.buttons.whoseTitle.is('OK').first.elementClick() sf.ui.proTools.windows.whoseTitle.is('New Window Configuration').first.elementWaitFor({ waitType: 'Disappear' }) return configNumber
}
// RECALL AND DELETE WINDOW CONFIG
function recallAndDeleteWinConfig(configNumber) {
sf.ui.proTools.appActivateMainWindow()
sf.ui.proTools.menuClick({
menuPath: ['Window', 'Configurations', 'Window Configuration List'],
targetValue: 'Enable'
})
sf.ui.proTools.windows.whoseTitle.is('Window Configurations').first.elementWaitFor()const winConfigWindow = sf.ui.proTools.windows.whoseTitle.is('Window Configurations').first // Recall Window Config which will also leave it selected for deleting const configNumStr = configNumber.toString() let winConfigWords = 'numpad comma, ' for (let i = 0; i < configNumStr.length; i++) { winConfigWords = winConfigWords.concat('numpad ' + configNumStr[i] + ', ') } let keys = winConfigWords + 'numpad multiply' sf.keyboard.press({ keys: keys }) sf.ui.proTools.windows.whoseTitle.is('Window Configurations').first.buttons.first.popupMenuSelect({ menuSelector: items => items.filter(i => i.names[0].match(/^Clear \"/))[0] }) sf.ui.proTools.menuClick({ menuPath: ['Window', 'Configurations', 'Window Configuration List'], targetValue: 'Disable' //or 'Enable' or 'Toggle' }) sf.ui.proTools.windows.whoseTitle.is('Window Configurations').first.elementWaitFor({ waitType: 'Disappear' })
}
// SET SELECTED TRACKS SIZE
function setSelectedTracksSize(size) {
const f = sf.ui.proTools.selectedTrack.frame
const popupMenu = sf.ui.proTools.selectedTrack.popupMenuOpenFromElement({
relativePosition: { x: f.w - 10, y: 5 },
isOption: true,
isShift: true
}).popupMenupopupMenu.menuClickPopupMenu({ menuPath: [size] })
}
// SAVE CURRENT TRACK VIEW
function saveCurrentTrackView() {
sf.ui.proTools.appActivateMainWindow()
sf.keyboard.press({ keys: 'numpad enter' })
sf.ui.proTools.newMemoryLocationDialog.elementWaitFor()//Wait for the Mem Location dialog to appear and assign it to the memLocDlg variable const memLocDlg = sf.ui.proTools.dialogWaitForManual({ dialogTitle: 'New Memory Location' }).dialog // We only want to store the track visibilty so clear all check boxes and then check the appropriate one let checkBoxes = memLocDlg.getElements('AXChildren').filter(function (e) { return e.fullRole == 'AXCheckBox' }) for (let i = 0; i < 6; i++) { checkBoxes[i].checkboxSet({ targetValue: 'Disable' }) } checkBoxes[2].checkboxSet({ targetValue: 'Enable' }) checkBoxes[3].checkboxSet({ targetValue: 'Enable' }) //Name the Location Track Visibilty sf.ui.proTools.newMemoryLocationDialog.textFields.allItems[1].elementSetTextFieldWithAreaValue({ value: 'Track Visibility' }) //Get the Location Number and pass it back so we can delete it when we're done const locationNumber = sf.ui.proTools.newMemoryLocationDialog.textFields.allItems[2].value.value // Set time Properties to none sf.ui.proTools.newMemoryLocationDialog.radioButtons.allItems[2].elementClick() //Create the Location sf.ui.proTools.newMemoryLocationDialog.buttons.whoseTitle.is('OK').first.elementClick() sf.ui.proTools.newMemoryLocationDialog.elementWaitFor({ waitType: 'Disappear' }) return locationNumber
}
// RECALL AND DELETE TRACK VIEW
function recallAndDeleteTrackView(locNumber) {sf.ui.proTools.memoryLocationsGoto({ memoryLocationNumber: Number(locNumber) }) sf.ui.proTools.memoryLocationsShowWindow() sf.ui.proTools.memoryLocationsWindow.popupButtons.whoseTitle.is('Memory Locations').first.popupMenuSelect({ menuSelector: items => items.filter(i => i.names[0].match(/^Clear \"/))[0] }) sf.ui.proTools.menuClick({ menuPath: ['Window', 'Memory Locations'], targetValue: 'Disable' //or 'Enable' or 'Toggle' }) sf.ui.proTools.memoryLocationsWindow.elementWaitFor({ waitType: 'Disappear' })
}
// SUSPEND GROUPS
function suspendGroups(status) {
sf.ui.proTools.groupsEnsureGroupListIsVisible()const groupListPopup = sf.ui.proTools.groupsOpenListPopupMenu().popupMenu groupListPopup.menuClickPopupMenu({ menuPath: ['Suspend All Groups'], targetValue: status }) sf.ui.proTools.appActivateMainWindow()
}
// LINK TRACK AND EDIT SELECTION
function linkTrackAndEditSelection() {
if (!sf.ui.proTools.getMenuItem('Options', 'Link Track and Edit Selection').isMenuChecked) {
sf.ui.proTools.menuClick({
menuPath: ["Options", "Link Track and Edit Selection"],
})
}
}// SETUP SCREEN
function setupScreen() {
// Make sure we're on the Edit window
if (!sf.ui.proTools.getMenuItem('Window', 'Edit').isMenuChecked) {
sf.ui.proTools.menuClick({ menuPath: ['Window', 'Edit'] })
}// Make sure comments are visible if (!sf.ui.proTools.getMenuItem('View', 'Edit Window Views', 'Comments').isMenuChecked) { sf.ui.proTools.menuClick({ menuPath: ['View', 'Edit Window Views', 'Comments'] }) }
}
// GET SESSION SAMPLE-RATE AND BIT-DEPTH
function getSessionFormat() {
sf.ui.proTools.menuClick({ menuPath: ['Setup', 'Session'] })let sessionDlg = sf.ui.proTools.dialogWaitForManual({ dialogTitle: 'Session Setup' }).dialog sf.ui.proTools.windows.whoseTitle.is('Session Setup').waitFor() let sampleRate = sessionDlg.groups.whoseTitle.is('Session Format').first.children.whoseRole.is('AXStaticText').allItems[2].value.invalidate().value let bitDepth = sessionDlg.groups.whoseTitle.is('Session Format').first.children.whoseRole.is('AXPopUpButton').allItems[1].value.invalidate().value sf.ui.proTools.viewCloseFocusedFloatingWindow() return [sampleRate, bitDepth]
}
// INPUT STEM PREFIX
function inputStemPrefix() {
return sf.interaction.popupText({
popupIdentifier: 'stemPrefix',
title: 'Stem Prefix'
}).text
}// SELECT BOUNCE OUTPUT
function selectBounceOutput() {
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 mainPanelBtns = bounceDlg.getElements('AXChildren').filter(function (e) { return e.fullRole == 'AXPopUpButton' }) let outputBtn = mainPanelBtns[1] let outputs = outputBtn.popupMenuFetchAllItems().menuItems let outputGroups = [] let groupOutputs = [] for (let i = 0; i < outputs.length; i++) { const outputGroup = outputs[i].path[0] if (!outputGroups.includes(outputGroup)) { outputGroups.push(outputGroup) } } let outputGroupsList = [] outputGroups.map(outputGroup => outputGroupsList.push({ name: outputGroup }) ) sf.ui.proTools.appActivateMainWindow() sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.buttons.whoseTitle.is('Cancel').first.elementClick() sf.ui.proTools.waitForNoModals() while (sf.ui.frontmostApp.title.value !== 'Pro Tools') { sf.wait({ intervalMs: 500 }) } const selectedOutputGroup = sf.interaction.popupSearch({ items: outputGroupsList, title: 'Select output group' }).item.name for (let i = 0; i < outputs.length; i++) { const outputGroup = outputs[i].path[0] if (outputGroup === selectedOutputGroup) { groupOutputs.push(outputs[i].path[1]) } } let groupOutputsList = [] groupOutputs.map(groupOutput => groupOutputsList.push({ name: groupOutput }) ) const selectedOutput = sf.interaction.popupSearch({ items: groupOutputsList, title: 'Select output' }).item.name return [selectedOutputGroup, selectedOutput]
}
// GET STEM NAMES
function getStemNames(selectedTracks) {
let stems = []for (let i = 0; i < selectedTracks.length; i++) { const stemName = sf.ui.proTools.trackGetByName({ name: selectedTracks[i] }).track.textFields.first.value.value if (!stems.includes(stemName)) { stems.push(stemName) } } return stems
}
// CHANGE TRACK ACTIVITY
function changeTrackActivity(status) {
if (sf.ui.proTools.getMenuItem('Track', status).exists) {
sf.ui.proTools.menuClick({ menuPath: ['Track', status] })
}sf.wait()
}
// SELECT AND SCROLL TO TRACK
function selectAndScrollTracks(tracks) {
sf.ui.proTools.menuClick({ menuPath: ['Track', 'Scroll to Track...'] })
sf.ui.proTools.confirmationDialog.elementWaitFor()
sf.ui.proTools.confirmationDialog.textFields.first.elementSetTextFieldWithAreaValue({ value: tracks[0] })
sf.ui.proTools.confirmationDialog.buttons.whoseTitle.is('OK').first.elementClick()
sf.ui.proTools.confirmationDialog.elementWaitFor({ waitType: 'Disappear' })
sf.ui.proTools.trackSelectByName({ names: tracks, deselectOthers: true })
}// GET TRACK WIDTH
function getTrackWidth(track) {
var width = track.groups.whoseTitle.is('Audio IO').first.sliders.whoseTitle.contains('Pan').count
return width
}// SET DYN VALUE
function setDynPopupBtn(targetValue) {
const btn = sf.ui.proTools.selectedTrack.popupButtons.whoseTitle.startsWith('Voice selector').firstbtn.popupMenuSelect({ menuPath: [targetValue], isOption: true, isShift: true })
}
// SET TRACK VOICE SELECTOR
function setTrackVoiceSelector({ targetValue }) {
sf.ui.proTools.appActivateMainWindow()
sf.ui.proTools.mainWindow.invalidate()const originalTrackNames = sf.ui.proTools.selectedTrackNames const selectedTrackHeaders = sf.ui.proTools.selectedTrackHeaders const trackInfo = selectedTrackHeaders.map(track => ({ trackTitle: track.normalizedTrackName, trackType: track.title.invalidate().value.split(' - ')[1].trim(), trackWidth: getTrackWidth(track) })) setSelectedTracksSize('medium') // @ts-ignore const trackWidths = [...new Set(trackInfo.map(t => t.trackWidth))] trackWidths.forEach(trackWidth => { if (trackWidth > 0 && trackWidth <= 2) { let trackNames = trackInfo .filter(track => track.trackWidth === trackWidth) .map(track => track.trackTitle) let selectedTrackName = sf.ui.proTools.selectedTrack.normalizedTrackName selectAndScrollTracks(trackNames) setDynPopupBtn(targetValue) } }) selectAndScrollTracks(originalTrackNames) setSelectedTracksSize('mini') selectAndScrollTracks(originalTrackNames)
}
// BOUNCE STEMS
function bounceStems(selectedTracks, stems, stemPrefix, sessionFormat, bounceOutput) {let firstLoop = true // Loop through stems stems.forEach(stem => { // Deselect all tracks sf.ui.proTools.trackDeselectAll() // Select tracks with stem name in comments selectedTracks.forEach(track => { const trackComment = sf.ui.proTools.trackGetByName({ name: track }).track.textFields.first.value.value if (trackComment === stem) { sf.ui.proTools.trackSelectByName({ names: [track], deselectOthers: false }) } }) // Scroll selected tracks into view selectAndScrollTracks(sf.ui.proTools.selectedTracks.names) // Click first selected track (solves selection bug in PT) const trackFrame = sf.ui.proTools.selectedTrack.frame sf.mouse.click({ position: { 'x': trackFrame.x + 45, 'y': trackFrame.y + 10 } }) // Make current stem tracks active, solo or dyn off switch (event.props.method) { case 'Activity': changeTrackActivity('Make Active') break case 'Solo': sf.keyboard.press({ keys: 'shift+s' }) break case 'Dyn': setTrackVoiceSelector({ targetValue: 'dyn' }) } if (event.props.exportType === 'Masters') { // Set selection length if none if (sf.ui.proTools.selectionGetInfo().isEmpty) { sf.ui.proTools.mainWindow.groups.whoseTitle.is("Counter Display Cluster").first.textFields.whoseTitle.is("Edit Selection Length").first.elementClick() sf.keyboard.type({ text: '1' }) sf.keyboard.press({ keys: 'return' }) } // Select all clips in track sf.ui.proTools.children.whoseRole.is('AXMenuBar').whoseTitle.is('').first.children.whoseRole.is('AXMenuBarItem').whoseTitle.is('Edit').first.popupMenuSelect({ menuPath: ['Select All'] }) } // BOUNCE STEM TO DISK try { bounceStem(stem, firstLoop, stemPrefix, sessionFormat, bounceOutput) } catch(err) { // Make current stem tracks inactive switch (event.props.method) { case 'Activity': changeTrackActivity('Make Inactive') break case 'Solo': sf.keyboard.press({ keys: 'shift+s' }) break case 'Dyn': setTrackVoiceSelector({ targetValue: 'off' }) } throw err } firstLoop = false // Make current stem tracks inactive switch (event.props.method) { case 'Activity': changeTrackActivity('Make Inactive') break case 'Solo': sf.keyboard.press({ keys: 'shift+s' }) break case 'Dyn': setTrackVoiceSelector({ targetValue: 'off' }) } }) return bouncedStems
}
// BOUNCE STEM
function bounceStem(stemName, firstRun, stemPrefix, sessionFormat, output) {// Open Bounce dialogue sf.ui.proTools.menuClick({ menuPath: ['File', 'Bounce Mix...'] }) let bounceDlg = sf.ui.proTools.dialogWaitForManual({ dialogTitle: 'Bounce Mix' }).dialog //File Name switch (event.props.exportType) { case 'Stems': bounceDlg.textFields.whoseTitle.is('').first.elementSetTextFieldWithAreaValue({ value: stemPrefix + '_' + event.props.stemSuffix + '_' + stemName }) break case 'Masters': bounceDlg.textFields.whoseTitle.is('').first.elementSetTextFieldWithAreaValue({ value: stemName }) break } if (firstRun === true) { const audioPanel = bounceDlg.groups.whoseTitle.is('Audio').first const locationPanel = bounceDlg.groups.whoseTitle.is('Location').first // Maximise bounce window function maximiseBounceWindow() { function openAudioPanel() { if (audioPanel.buttons.whoseTitle.is('Collapser').exists) { audioPanel.buttons.whoseTitle.is('Collapser').first.elementClick() } } function openLocationPanel() { if (locationPanel.buttons.whoseTitle.is('Collapser').exists) { locationPanel.buttons.whoseTitle.is('Collapser').first.elementClick() } } switch (bounceDlg.frame.h) { case 268: openAudioPanel() openLocationPanel() break case 406: openLocationPanel() break case 393: openAudioPanel() break } } maximiseBounceWindow() let mainPanelBtns = bounceDlg.getElements('AXChildren').filter(function (e) { return e.fullRole == 'AXPopUpButton' }) let audioPanelBtns = audioPanel.getElements('AXChildren').filter(function (e) { return e.fullRole == 'AXPopUpButton' }) let mainCheckboxes = bounceDlg.getElements('AXChildren').filter(function (e) { return e.fullRole == 'AXCheckBox' }) let audioCheckboxes = audioPanel.getElements('AXChildren').filter(function (e) { return e.fullRole == 'AXCheckBox' }) let locationCheckboxes = locationPanel.getElements('AXChildren').filter(function (e) { return e.fullRole == 'AXCheckBox' }) let locationRadios = locationPanel.getElements('AXChildren').filter(function (e) { return e.fullRole == 'AXRadioButton' }) let locationText = locationPanel.getElements('AXChildren').filter(function (e) { return e.fullRole == 'AXTextField' }) let fileTypeBtn = mainPanelBtns[0] let outputBtn = mainPanelBtns[1] let formatBtn = audioPanelBtns[1] let bitDepthBtn = audioPanelBtns[2] let sampleRateBtn = audioPanelBtns[3] let mp3CheckBox = audioCheckboxes[0] let padCheckBox = audioCheckboxes[1] let importCheckBox = locationCheckboxes[0] let sessionFolderButton = locationRadios[0] let sessionFolderText = locationText[0] let offlineCheckBox = mainCheckboxes[0] let sampleRate = sessionFormat[0] let bitDepth = sessionFormat[1] //Set Output if (outputBtn.value.invalidate().value != output) { outputBtn.popupMenuSelect({ menuPath: output, useWildcards: true }) } //File Type if (fileTypeBtn.value.invalidate().value != event.props.fileType) fileTypeBtn.popupMenuSelect({ menuPath: [event.props.fileType] }) //File Format if (formatBtn.value.value != 'Interleaved') formatBtn.popupMenuSelect({ menuPath: ['Interleaved'] }) if (event.props.fileType !== 'MP3') { //Bit Depth if (event.props.bitDepth === 'session') { if (bitDepthBtn.value.invalidate().value != bitDepth) bitDepthBtn.popupMenuSelect({ menuPath: [bitDepth] }) } else { if (bitDepthBtn.value.invalidate().value != event.props.bitDepth) bitDepthBtn.popupMenuSelect({ menuPath: [event.props.bitDepth] }) } // Add MP3 mp3CheckBox.checkboxSet({ targetValue: event.props.addMP3 ? 'Enable' : 'Disable', }) //Import After Bounce importCheckBox.checkboxSet({ targetValue: 'Disable', }) // Pad To Frame Boundary padCheckBox.checkboxSet({ targetValue: 'Disable', }) } //Sample Rate if (event.props.sampleRate === 'session') { if (sampleRateBtn.value.invalidate().value != sampleRate) sampleRateBtn.popupMenuSelect({ menuPath: [sampleRate] }) } else { if (sampleRateBtn.value.invalidate().value != event.props.sampleRate) sampleRateBtn.popupMenuSelect({ menuPath: [event.props.sampleRate] }) } // Session Folder sessionFolderButton.elementClick() // Session Folder Name switch (event.props.exportType) { case 'Stems': sessionFolderText.elementSetTextFieldWithAreaValue({ value: 'Bounced Files/' + stemPrefix + '_' + event.props.folderSuffix }) break case 'Masters': sessionFolderText.elementSetTextFieldWithAreaValue({ value: 'Bounced Files/' }) break } // Offline Bounce offlineCheckBox.checkboxSet({ targetValue: event.props.offline }) } sf.wait({ intervalMs: 200 }) sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.buttons.whoseTitle.is('Bounce').first.elementClick() let replaceDlg = sf.ui.proTools.windows.whoseTitle.is("").first.children.whoseRole.is("AXStaticText").whoseValue.matches(/\" already exists\. Do you want to replace it\?/).first if (replaceDlg.exists) { let yesBtn = sf.ui.proTools.windows.whoseTitle.is("").first.children.whoseRole.is("AXButton").whoseTitle.is("Yes").first let noBtn = sf.ui.proTools.windows.whoseTitle.is("").first.children.whoseRole.is("AXButton").whoseTitle.is("No").first switch (event.props.overwriteExistingFiles) { case 'Yes': yesBtn.elementClick() sf.ui.proTools.waitForNoModals() bouncedStems.push(stemName) break case 'No': noBtn.elementClick() sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.buttons.whoseTitle.is('Cancel').first.elementClick() sf.ui.proTools.waitForNoModals() throw 'Stem "' + stemName + '" already exists in ' + stemPrefix + '_' + event.props.folderSuffix case 'Dismiss': noBtn.elementClick() sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first.buttons.whoseTitle.is('Cancel').first.elementClick() sf.ui.proTools.waitForNoModals() return } } else { bouncedStems.push(stemName) } // MP3 dialogue if (event.props.fileType === 'MP3' || event.props.addMP3) { let mp3Dlg = sf.ui.proTools.dialogWaitForManual({ dialogTitle: 'MP3' }).dialog if (firstRun === true) { let mainMp3Btns = mp3Dlg.getElements('AXChildren').filter(function (e) { return e.fullRole == 'AXPopUpButton' }) let encodingSpeedBtn = mainMp3Btns.length === 4 ? mainMp3Btns[3] : mainMp3Btns[2] let cbrBtn = mainMp3Btns.length === 4 ? mainMp3Btns[2] : mainMp3Btns[1] let tagBtn = mainMp3Btns.length === 4 ? mainMp3Btns[1] : mainMp3Btns[0] if (tagBtn.value.invalidate().value != 'None') tagBtn.popupMenuSelect({ menuPath: ['None'] }) if (cbrBtn.value.invalidate().value != '320kbit/s') cbrBtn.popupMenuSelect({ menuPath: ['320kbit/s (' + sessionFormat[0].replace(' k', '000') + ')'] }) if (encodingSpeedBtn.value.invalidate().value != 'Slowest') encodingSpeedBtn.popupMenuSelect({ menuPath: ['Highest Quality, Slower Encoding Time'] }) } sf.ui.proTools.windows.whoseTitle.is('MP3').first.buttons.whoseTitle.is('OK').first.elementClick() } sf.ui.proTools.waitForNoModals() while (sf.ui.frontmostApp.title.value !== 'Pro Tools') { sf.wait({ intervalMs: 500 }) }
}
// MAIN ------------------------------------------------------------------------------------------
function main() {
// Get selected tracks
const selectedTracks = sf.ui.proTools.trackGetSelectedTracks().names
const stems = getStemNames(selectedTracks)if (sf.ui.proTools.selectedTracks.trackHeaders.filter(e => e.title.value.includes('Folder')).length > 0 && event.props.method ==='Dyn') { sf.interaction.displayDialog({ title: 'Stem Bouncing Stopped', prompt: 'Method cannot be set to Dyn On/Off if using folder tracks in your selection.' }) return } selectAndScrollTracks(selectedTracks) const stemPrefix = event.props.exportType === 'Stems' ? inputStemPrefix() : '' sf.ui.proTools.appActivateMainWindow() const bounceOutput = selectBounceOutput() // Save the way the user has the screen set up and setup screen const winConfigNum = saveUserWinConfig() const tracksViewNum = saveCurrentTrackView() sf.ui.proTools.viewCloseFloatingWindows() setupScreen() setSelectedTracksSize('mini') suspendGroups('Enable') linkTrackAndEditSelection() switch (event.props.method) { case 'Activity': changeTrackActivity('Make Inactive') break case 'Solo': sf.ui.proTools.mainWindow.counterDisplay.mouseClickElement({ relativePosition: { x: 299, y: 67 } }) break case 'Dyn': setTrackVoiceSelector({ targetValue: 'off' }) } // Get session format const sessionFormat = getSessionFormat() // Bounce stems try { bounceStems(selectedTracks, stems, stemPrefix, sessionFormat, bounceOutput) } catch(err) { // Error dialogue sf.interaction.displayDialog({ title: 'Stems Bouncing Stopped', prompt: err }) } // Finish and cleanup selectAndScrollTracks(selectedTracks) switch (event.props.method) { case 'Activity': changeTrackActivity('Make Active') break case 'Solo': sf.ui.proTools.mainWindow.counterDisplay.mouseClickElement({ relativePosition: { x: 299, y: 67 } }) break case 'Dyn': setTrackVoiceSelector({ targetValue: 'dyn' }) } suspendGroups('Disable') recallAndDeleteTrackView(tracksViewNum) recallAndDeleteWinConfig(winConfigNum) // Complete dialogue sf.interaction.displayDialog({ title: 'Stems Complete', prompt: 'Bounced ' + bouncedStems.length + ' ' + event.props.exportType.toLowerCase() + ' of ' + stems.length + '.' })
}
main()
- TIn reply toJordan_Young⬆:T Michaels @T_Michaels
Sorry that formatted weird, go on my profile and see my first post for a cleaner version
- DIn reply toJordan_Young⬆:David Simpson @David_Simpson
Hi all,
Apologies for the delay in getting to this. I've now updated the script to work with Pro Tools 2024.3 so let me know if you're still having issues and I'll try my best to get to them.
Cheers!
David