Logic Pro X: Bounce selected tracks in place, hiding the original tracks and turn them off
Hello! I've borrowed the brilliant script from this thread:
Logic Pro X: How to automate printing tracks
The things I've added to the script:
- Hide the original tracks
- Turn the original track off
Things I would like some help adding:
- Keep the original track name (without the "_bip" ending)
- Only 'bounce in place' SELECTED tracks
- Bounce every region on the tracks separately and add them back to the same track (as they where laid out on the original track)
- Making the script more correct and neat
I am really new to coding so sorry for some bad code. :)
Best, Johan
var app = sf.ui.app('com.apple.logic10')
//Hide hidden tracks
sf.ui.app('com.apple.logic10').menuClick({
menuPath: ["Track","Hide Hidden Tracks"],
onError: "Continue",
});
function checkTrackName() {
sf.clipboard.clear();
app.getMenuItem('Track', 'Rename Track').elementClick();
app.getMenuItem('Edit', 'Copy').elementClick();
sf.keyboard.press({ keys: 'escape' })
var trackName = sf.clipboard.waitForText().text;
sf.wait({ intervalMs: 100 })
return trackName
}
function checkForRegionsOnSelectedTrack() {
app.getMenuItem('Edit', 'Select', 'Deselect All').elementClick();
sf.keyboard.press({ keys: 'right' })
sf.wait({ intervalMs: 500 })
if (app.getMenuItem('Edit', 'Repeat', 'Once').isEnabled)
return true
else return false
}
function checkForLastTrack() {
var oldTrackName = checkTrackName()
sf.keyboard.press({ keys: 'down' })
var newTrackName = checkTrackName();
if (oldTrackName == newTrackName)
return true
else return false
}
function waitForBounceToComplete() {
while (true) {
try {
if (!app.windows.whoseTitle.is('Logic Pro X').first.exists) {
//We didn't get an error - and the modal popup doesn't exist.
//Break out, the bounce is complete
break;
}
} catch (err) {
//We got an error. Logic is still busy. Continue with another loop
}
//Wait a bit before continuing
sf.wait({ intervalMs: 200 });
}
}
function bounceRegions() {
sf.keyboard.press({ keys: "alt+shift+n", });
app.getMenuItem('File', 'Bounce', 'Regions in Place…').elementClick();
app.mainWindow.sheets.first.elementWaitFor();
app.mainWindow.sheets.first.checkBoxes.whoseTitle.is('Bypass Effect Plug-ins').first.checkboxSet({ targetValue: "Enable", });
app.mainWindow.sheets.first.checkBoxes.whoseTitle.is('Include Audio Tail in File').first.checkboxSet({ targetValue: "Enable", });
app.mainWindow.sheets.first.checkBoxes.whoseTitle.is('Include Audio Tail in Region').first.checkboxSet({ targetValue: "Enable", });
app.mainWindow.sheets.first.buttons.whoseTitle.is('OK').first.elementClick({
asyncSwallow: true
});
//Wait for the progress window to appear
sf.wait({ intervalMs: 2000 });
//Wait for the progress window to disappear
waitForBounceToComplete();
//Select original track
sf.keyboard.press({
keys: "up",
});
//Turn original track off
sf.keyboard.press({
keys: "alt+m",
});
//Hide original track
sf.ui.app('com.apple.logic10').menuClick({
menuPath: ["Track","Hide Selected Track(s)"],
});
}
function main() {
while (true) {
if (checkForRegionsOnSelectedTrack()) {
app.getMenuItem('Edit', 'Select', 'All Following of Same Track').elementClick();
sf.wait({ intervalMs: 100 })
bounceRegions()
}
if (checkForLastTrack()) {
log('Command Finished', 'Last Track Reached')
break;
}
}
}
main();
- AAlex Oldroyd @Alex_Oldroyd8
Your script is great. Did you ever work out how to only include selected tracks?
- JJohan Nordin @Johan_Nordin
No, unfortunately not.
- AAlex Oldroyd @Alex_Oldroyd8
I think I've done it, @Johan_Nordin !
This will bounce all selected tracks and name as the track, followed by the Alternative name. you can of course adjust to bip instead of Bounce.
Let me know if it works :)
const app = sf.ui.app('com.apple.logic10') const logic = sf.ui.app("com.apple.logic10"); logic.appActivateMainWindow() function waitForBounceToComplete() { while (true) { try { if (checkTrackName()) { //We didn't get an error - and the modal popup doesn't exist. //Break out, the bounce is complete sf.keyboard.press({ keys: 'escape' }) break; } } catch (err) { //We got an error. Logic is still busy. Continue with another loop } //Wait a bit before continuing sf.wait({ intervalMs: 200 }); } } function bounceRegions() { const soloButtonMixer = sf.ui.app("com.apple.logic10").mainWindow.groups.whoseDescription.is("Inspector").first.children.whoseRole.is("AXList").first.groups.allItems[3].children.whoseRole.is("AXLayoutArea").whoseDescription.is("Mixer").first.children.whoseRole.is("AXLayoutItem").first.buttons.whoseDescription.is("solo").first; // Get Selected Track and Project Name const trackArea = logic.mainWindow.groups.whoseDescription.is('Tracks').first.groups.whoseDescription.is('Tracks'); const trackHeaders = trackArea.allItems[1].splitGroups.first.splitGroups.allItems[1].scrollAreas.first.groups.whoseDescription.is('Tracks header').first; const selectedTrackName = trackHeaders.getElements('AXSelectedChildren').first.getString("AXDescription").match(/“(.*)”/)[1] const projectName = logic.mainWindow.getElement("AXTitleUIElement").value.invalidate().value.split(" - ")[0]; soloButtonMixer.mouseClickElement({ anchor: "MidCenter", isOption: true, }); app.getMenuItem('File', 'Bounce', 'Project or Section…').elementClick(); app.children.whoseRole.is("AXWindow").first.scrollAreas.first.tables.first.children.whoseRole.is("AXRow").first.children.whoseRole.is("AXCell").first.checkBoxes.first.checkboxSet({ targetValue: "Enable", onError: "Continue", }); app.children.whoseRole.is("AXWindow").first.scrollAreas.first.tables.first.children.whoseRole.is("AXRow").first.children.whoseRole.is("AXCell").allItems[1].getElement("AXTitleUIElement").mouseClickElement({ anchor: "MidCenter", onError: "Continue", }); app.children.whoseRole.is("AXWindow").first.scrollAreas.first.tables.first.children.whoseRole.is("AXRow").allItems[1].children.whoseRole.is("AXCell").first.checkBoxes.first.checkboxSet({ targetValue: "Disable", onError: "Continue", }); app.children.whoseRole.is("AXWindow").first.scrollAreas.first.tables.first.children.whoseRole.is("AXRow").allItems[2].children.whoseRole.is("AXCell").first.checkBoxes.first.checkboxSet({ targetValue: "Disable", onError: "Continue", }); app.children.whoseRole.is("AXWindow").first.scrollAreas.first.tables.first.children.whoseRole.is("AXRow").allItems[3].children.whoseRole.is("AXCell").first.checkBoxes.first.checkboxSet({ targetValue: "Disable", onError: "Continue", }); app.children.whoseRole.is("AXWindow").first.groups.first.checkBoxes.whoseTitle.is("Add to Project").first.checkboxSet({ targetValue: "Disable", onError: "Continue", }); app.children.whoseRole.is("AXWindow").first.groups.first.popupButtons.allItems[1].popupMenuSelect({ menuPath: ["Wave"], onError: "Continue", }); sf.ui.app("com.apple.logic10").windows.first.buttons.whoseTitle.is("OK").first.elementClick(); //Wait for the progress window to appear sf.wait({ intervalMs: 2000 }); // Set the "Save as..." Field logic.windows.first.textFields.first.elementSetTextAreaValue({ value: `${selectedTrackName} ${projectName}` }); sf.ui.app("com.apple.logic10").windows.first.buttons.whoseTitle.is("Bounce").first.elementClick(); //Wait for the progress window to disappear waitForBounceToComplete(); sf.keyboard.press({ keys: 'escape' }) } function checkTrackName() { sf.clipboard.clear(); app.getMenuItem('Track', 'Rename Track').elementClick(); app.getMenuItem('Edit', 'Copy').elementClick(); sf.keyboard.press({ keys: 'escape' }) var trackName = sf.clipboard.waitForText().text; sf.wait({ intervalMs: 100 }) return trackName } function doForEachSelectedTrack(functionToDo) { const logic = sf.ui.app('com.apple.logic10'); const trackArea = logic.mainWindow.groups.whoseDescription.is('Tracks').first.groups.whoseDescription.is('Tracks'); const trackHeaders = trackArea.allItems[1].splitGroups.first.splitGroups.allItems[1].scrollAreas.first.groups.whoseDescription.is('Tracks header').first; //Get Selected track names const selectedTrackNames = trackHeaders.getElements('AXSelectedChildren').map(trackHeader => trackHeader.getString("AXDescription").match(/“(.*)”/)[1]); selectedTrackNames.forEach(item => functionToDo(item)); } function selectTrackAndPerformFunction(trackName) { let originalMousePosition = sf.mouse.getPosition().position; const logicApp = sf.ui.app("com.apple.logic10"); const trackToSelect = () => { const trackHeaders = logicApp.invalidate().mainWindow.groups.whoseDescription.is("Tracks").first .groups.whoseDescription.is("Tracks").allItems[1].splitGroups.first.splitGroups.allItems[1] .scrollAreas.first.groups.whoseDescription.is("Tracks header").first.children; const trackToSelect = trackHeaders.find(x => x.textFields.whoseDescription.is(trackName).first.exists); return trackToSelect } if (!trackToSelect()) { log(`Track with the name '${trackName}' not found`) throw 0; } const mainWindowFrame = logicApp.mainWindow.frame; const logicRulerFrame = logicApp.mainWindow.groups.whoseDescription.is("Tracks").first.groups.whoseDescription.is("Tracks").allItems[1] .splitGroups.first.splitGroups.first.scrollAreas.first.frame; const trackFrame = trackToSelect().frame; const mainWindowTotalY = (mainWindowFrame.y + mainWindowFrame.h); const offWindowBottom = ((trackFrame.y + trackFrame.h) > (mainWindowFrame.y + mainWindowFrame.h)); const offScreenTop = (trackFrame.y < logicRulerFrame.y + logicRulerFrame.h); const trackAreaFrame = sf.ui.app("com.apple.logic10").mainWindow.groups.whoseDescription.is("Tracks").first .groups.whoseDescription.is("Tracks").allItems[1].splitGroups.first.splitGroups.allItems[1].scrollAreas.allItems[1].frame; const middleOfTrackHeaderPane = { x: trackAreaFrame.x + (trackAreaFrame.w / 2), y: trackAreaFrame.y + (trackAreaFrame.h / 2) }; sf.mouse.setPosition({ position: middleOfTrackHeaderPane }) if (offWindowBottom) { sf.mouse.setPosition({ position: middleOfTrackHeaderPane }) sf.mouse.scroll({ delta: -(trackFrame.y + trackFrame.h - mainWindowTotalY), delta2: 0, unit: "Pixel" }) } else if (offScreenTop) { sf.mouse.setPosition({ position: middleOfTrackHeaderPane }) sf.mouse.scroll({ delta: ((logicRulerFrame.y + logicRulerFrame.h) - trackFrame.y), delta2: 0, unit: "Pixel" }) } trackToSelect().mouseClickElement({ relativePosition: { x: (trackFrame.w / 2), y: 5 } }) sf.mouse.setPosition({ position: originalMousePosition }) bounceRegions(); } doForEachSelectedTrack(selectTrackAndPerformFunction);
- In reply toJohan_Nordin⬆:AAlex Oldroyd @Alex_Oldroyd8
Hey @Johan_Nordin - I've worked on your code with a lot of help from @Kitch . This bounces in place the selected tracks and then turns them off, mutes and hides them.
const app = sf.ui.app('com.apple.logic10') const logic = sf.ui.app("com.apple.logic10"); logic.appActivateMainWindow() function logTrackNameAsNote() { /** * @param {object} obj * @param {'Library'|'Inspector'|'Quick Help'|'Toolbar'|'Smart Controls'|'Mixer'|'Editors'} obj.viewName * @param {'Enable'|'Disable'|'Toggle'} [obj.targetValue] */ function setViews({ viewName, targetValue }) { const logic = sf.ui.app("com.apple.logic10"); const controlBar = logic.mainWindow.groups.whoseDescription.is("Control Bar").first; const viewButton = controlBar.checkBoxes.whoseTitle.is(viewName).first; viewButton.checkboxSet({ targetValue, }); } /** * @param {object} obj * @param {'Quick Help'|'Region'|'Groups'|'Track'} obj.inspectorName * @param {'Enable'|'Disable'|'Toggle'} [obj.targetValue] */ function ensureInspector({ inspectorName, targetValue = "Toggle" }) { const logic = sf.ui.app('com.apple.logic10'); const mainWin = logic.mainWindow; const inspector = mainWin.groups.whoseDescription.is('Inspector').first; const inspectorList = inspector.childrenByRole("AXList").first; const inspectorRow = inspectorList.groups.filter(e => e.childrenByRole("AXStaticText").whoseValue.is(inspectorName + ":").first.exists || e.childrenByRole("AXStaticText").whoseValue.is(inspectorName).first.exists, )[0]; const disclosureTriangle = inspectorRow.childrenByRole("AXDisclosureTriangle").first; const isInspectorOpen = disclosureTriangle.value.invalidate().intValue === 1; switch (true) { case targetValue === "Enable" && !isInspectorOpen: disclosureTriangle.elementClick(); break; case targetValue === "Disable" && isInspectorOpen: disclosureTriangle.elementClick(); break; case targetValue === "Toggle": disclosureTriangle.elementClick(); break; } } function openNotePad() { const logic = sf.ui.app("com.apple.logic10"); logic.appActivateMainWindow(); setViews({ viewName: "Note Pads", targetValue: "Enable", }); } function closeNotePad() { const logic = sf.ui.app("com.apple.logic10"); logic.appActivateMainWindow(); setViews({ viewName: "Note Pads", targetValue: "Disable", }); } function logTrackName() { const selectedTrackName = trackHeaders.getElements('AXSelectedChildren').first.getString("AXDescription").match(/“(.*)”/)[1] logic.appActivateMainWindow() sf.ui.app("com.apple.logic10").mainWindow.groups.allItems[3].groups.whoseDescription.is("Project").first.scrollAreas.first.textAreas.first.mouseClickElement({ anchor: "MidCenter", clickCount: 2, }); sf.keyboard.press({ keys: "right, return", }); sf.keyboard.type({ text: selectedTrackName; }); } openNotePad(); logTrackName(); closeNotePad(); } function waitForBounceToComplete() { while (true) { try { if (checkTrackName()) { //We didn't get an error - and the modal popup doesn't exist. //Break out, the bounce is complete sf.keyboard.press({ keys: 'escape' }) break; } } catch (err) { //We got an error. Logic is still busy. Continue with another loop } //Wait a bit before continuing sf.wait({ intervalMs: 200 }); } } function bounceRegions() { sf.wait({ intervalMs: 1000, }); sf.keyboard.press({ keys: "down", }); sf.keyboard.press({ keys: "up", }); sf.keyboard.press({ keys: "alt+shift+n", }); app.getMenuItem('File', 'Bounce', 'Regions in Place…').elementClick(); app.mainWindow.sheets.first.elementWaitFor(); app.mainWindow.sheets.first.checkBoxes.whoseTitle.is('Bypass Effect Plug-ins').first.checkboxSet({ targetValue: "Disable", }); app.mainWindow.sheets.first.checkBoxes.whoseTitle.is('Include Audio Tail in File').first.checkboxSet({ targetValue: "Enable", }); app.mainWindow.sheets.first.checkBoxes.whoseTitle.is('Include Audio Tail in Region').first.checkboxSet({ targetValue: "Enable", }); app.mainWindow.sheets.first.buttons.whoseTitle.is('OK').first.elementClick({ asyncSwallow: true }); //Wait for the progress window to disappear waitForBounceToComplete(); sf.wait({ intervalMs: 1000, }); //Select original track sf.keyboard.press({ keys: "up", }); //Turn original track off sf.keyboard.press({ keys: "alt+m", }); //Mute original track sf.keyboard.press({ keys: "m", }); //Hide original track sf.ui.app('com.apple.logic10').menuClick({ menuPath: ["Track", "Hide Selected Track(s)"], }); } function checkTrackName() { sf.clipboard.clear(); app.getMenuItem('Track', 'Rename Track').elementClick(); app.getMenuItem('Edit', 'Copy').elementClick(); sf.keyboard.press({ keys: 'escape' }) var trackName = sf.clipboard.waitForText().text; sf.wait({ intervalMs: 100 }) return trackName } function doForEachSelectedTrack(functionToDo) { const logic = sf.ui.app('com.apple.logic10'); const trackArea = logic.mainWindow.groups.whoseDescription.is('Tracks').first.groups.whoseDescription.is('Tracks'); const trackHeaders = trackArea.allItems[1].splitGroups.first.splitGroups.allItems[1].scrollAreas.first.groups.whoseDescription.is('Tracks header').first; //Get Selected track names const selectedTrackNames = trackHeaders.getElements('AXSelectedChildren').map(trackHeader => trackHeader.getString("AXDescription").match(/“(.*)”/)[1]); selectedTrackNames.forEach(item => functionToDo(item)); } function selectTrackAndPerformFunction(trackName) { let originalMousePosition = sf.mouse.getPosition().position; const logicApp = sf.ui.app("com.apple.logic10"); const trackToSelect = () => { const trackHeaders = logicApp.invalidate().mainWindow.groups.whoseDescription.is("Tracks").first .groups.whoseDescription.is("Tracks").allItems[1].splitGroups.first.splitGroups.allItems[1] .scrollAreas.first.groups.whoseDescription.is("Tracks header").first.children; const trackToSelect = trackHeaders.find(x => x.textFields.whoseDescription.is(trackName).first.exists); return trackToSelect } if (!trackToSelect()) { log(`Track with the name '${trackName}' not found`) throw 0; } const mainWindowFrame = logicApp.mainWindow.frame; const logicRulerFrame = logicApp.mainWindow.groups.whoseDescription.is("Tracks").first.groups.whoseDescription.is("Tracks").allItems[1] .splitGroups.first.splitGroups.first.scrollAreas.first.frame; const trackFrame = trackToSelect().frame; const mainWindowTotalY = (mainWindowFrame.y + mainWindowFrame.h); const offWindowBottom = ((trackFrame.y + trackFrame.h) > (mainWindowFrame.y + mainWindowFrame.h)); const offScreenTop = (trackFrame.y < logicRulerFrame.y + logicRulerFrame.h); const trackAreaFrame = sf.ui.app("com.apple.logic10").mainWindow.groups.whoseDescription.is("Tracks").first .groups.whoseDescription.is("Tracks").allItems[1].splitGroups.first.splitGroups.allItems[1].scrollAreas.allItems[1].frame; const middleOfTrackHeaderPane = { x: trackAreaFrame.x + (trackAreaFrame.w / 2), y: trackAreaFrame.y + (trackAreaFrame.h / 2) }; sf.mouse.setPosition({ position: middleOfTrackHeaderPane }) if (offWindowBottom) { sf.mouse.setPosition({ position: middleOfTrackHeaderPane }) sf.mouse.scroll({ delta: -(trackFrame.y + trackFrame.h - mainWindowTotalY), delta2: 0, unit: "Pixel" }) } else if (offScreenTop) { sf.mouse.setPosition({ position: middleOfTrackHeaderPane }) sf.mouse.scroll({ delta: ((logicRulerFrame.y + logicRulerFrame.h) - trackFrame.y), delta2: 0, unit: "Pixel" }) } trackToSelect().mouseClickElement({ relativePosition: { x: (trackFrame.w / 2), y: 5 } }) sf.mouse.setPosition({ position: originalMousePosition }) try{ bounceRegions(); } catch(err){ logTrackNameAsNote(); } } doForEachSelectedTrack(selectTrackAndPerformFunction);
- JJohan Nordin @Johan_Nordin
WOW! Thank you so much. Really nice job. :)
I tried with som apple loops and one of them gave the track a name that made the script fail.
25.04.2022 22:45:41.21 [Backend]: [LOG] Track with the name '12 String Acoustic Guitar' not found
Could it be that the name includes numbers?
Best, Johan
- AAlex Oldroyd @Alex_Oldroyd8
Hmmm i don't know - i haven't come across that. Does it point to a specific line of text that cause it to fail? Does it work if you take the '12' out?