-
-
Save psobot/81635e6cbc933b7e8862 to your computer and use it in GitHub Desktop.
import os | |
import sys | |
import time | |
import atomac | |
import subprocess | |
if len(sys.argv) < 2: | |
print "Usage: bouncer.py <path_to_logic_project> (<path_to_logic_project>)" | |
os.exit(1) | |
bundleId = 'com.apple.logic10' | |
for project in sys.argv[1:]: | |
projectName = project.split('/')[-1].replace('.logicx', '') | |
filename = projectName + ".wav" | |
print "Opening %s..." % project | |
# Open a project file | |
subprocess.call(['open', project]) | |
print "Activating Logic Pro X..." | |
logic = atomac.getAppRefByBundleId(bundleId) | |
logic.activate() | |
print "Waiting for project '%s' to open..." % projectName | |
while len(filter(lambda x: projectName in x.AXTitle, logic.windows())) == 0: | |
time.sleep(0.1) | |
# Wait for the window to load | |
time.sleep(1) | |
print "Triggering bounce operation..." | |
logic.activate() | |
logic.sendGlobalKeyWithModifiers('b', [atomac.AXKeyCodeConstants.COMMAND]) | |
print "Waiting for bounce window..." | |
bounce_window = None | |
while not bounce_window: | |
bounce_window = filter(lambda x: ('Output 1-2' in x.AXTitle) or | |
('Bounce' in x.AXTitle), | |
logic.windows()) | |
time.sleep(0.1) | |
bounce_window = bounce_window[0] | |
print "Selecting output formats..." | |
qualityScrollArea = bounce_window.findFirst(AXRole='AXScrollArea') | |
qualityTable = qualityScrollArea.findFirst(AXRole='AXTable') | |
for row in qualityTable.findAll(AXRole='AXRow'): | |
rowName = row.findFirst(AXRole='AXTextField').AXValue | |
checkbox = row.findFirst(AXRole='AXCheckBox') | |
if rowName == 'PCM': | |
if checkbox.AXValue is 0: | |
print "Selected %s output format." % rowName | |
checkbox.Press() | |
else: | |
print "%s output format selected." % rowName | |
elif checkbox.AXValue is 1: | |
print "Deselected %s output format." % rowName | |
checkbox.Press() | |
print "Pressing Bounce button..." | |
bounce_button = bounce_window.findFirst(AXRole="AXButton", | |
AXTitle="Bounce") | |
if not bounce_button: | |
bounce_button = bounce_window.findFirst( | |
AXRole="AXButton", | |
AXTitle="OK" | |
) | |
bounce_button.Press() | |
bounce_window = None | |
# bounce_window is now gone and we have a modal dialog about saving | |
print "Waiting for save window..." | |
save_window = None | |
while not save_window: | |
save_window = filter(lambda x: ('Output 1-2' in x.AXTitle) or | |
('Bounce' in x.AXTitle), | |
logic.windows()) | |
time.sleep(0.1) | |
save_window = save_window[0] | |
print "Entering filename..." | |
filenameBox = save_window.findFirst(AXRole="AXTextField") | |
filenameBox.AXValue = filename | |
print "Pressing 'Bounce' on the save window..." | |
bounce_button = save_window.findFirst(AXRole="AXButton", AXTitle="Bounce") | |
bounce_button.Press() | |
# Check to see if we got a "this file already exists" dialog | |
if len(save_window.sheets()) > 0: | |
print "Allowing overwriting of existing file..." | |
overwrite_sheet = save_window.sheets()[0] | |
overwrite_sheet.findFirst(AXRole="AXButton", | |
AXTitle=u"Replace").Press() | |
print "Bouncing '%s'..." % projectName | |
# All UI calls will block now, because Logic blocks the UI thread while bouncing | |
while len(logic.windows()) > 1: | |
time.sleep(0.1) | |
print "Waiting for Logic to regain its senses..." | |
time.sleep(2) | |
# Done - should be saved now. | |
# Close the window with command-option-W | |
logic.activate() | |
time.sleep(1) | |
print "Closing project '%s'..." % projectName | |
logic.sendGlobalKeyWithModifiers('w', [ | |
atomac.AXKeyCodeConstants.COMMAND, atomac.AXKeyCodeConstants.OPTION | |
]) | |
print "Waiting for the 'do you want to save changes' window..." | |
save_window = None | |
attempts = 0 | |
while not save_window and attempts < 20: | |
save_window = filter(lambda x: '' == x.AXTitle, logic.windows()) | |
time.sleep(0.1) | |
attempts += 1 | |
if save_window: | |
print "Saying 'No, I don't want to save changes'..." | |
save_window = save_window[0] | |
logic.activate() | |
# Click the "Don't Save" button | |
filter(lambda x: 'Don' in x.AXTitle, save_window.findAll(AXRole="AXButton"))[0].Press() | |
print "Waiting for all Logic windows to close..." | |
while len(logic.windows()) > 0: | |
time.sleep(0.5) | |
print "Terminating Logic." | |
atomac.terminateAppByBundleId(bundleId) |
Hi, I've just stumbled across this script. I'm trying to find a way of bypass a certain plugin whenever I bounce from Logic, do you think it's possible? I'm not too good with code, so struggling to figure it out! It's in order to bypass the Sonarworks plugin on the master whenever bounce is initiated. Can it be done?
No I never got this to work. I didn’t find Apple to be helpful either. This was a dead end for me. Hopefully not for you.
Ah, that's a shame. Thought as much. Hopefully they allow it in the future! Thanks for the reply.
This could be done for sure - the same library I'm using here (atomac) could be used to click the bypass button on any channel strip, or any other button for that matter. Unfortunately I don't quite have time to investigate making a change like that, but it shouldn't require a huge amount of modification to this script. Best of luck!
For folks who visit this in the future and can't get this to work: atomac requires python2 and its dependency, pyobjc, requires python3. To work around this, you can use the fork atomacos which uses python3 as well.
Thanks @psobot for sharing—this helped a ton. I've adapted this script for transposing and subsequently bouncing music using the updated library: https://github.com/gzinck/logic-automator
Can logic be installed and run from an AWS ec2 instance?
@Scylla2020 Logic Pro only runs on macOS, so likely not. If you can find a VPS provider that will let you run macOS, nothing should prevent you from running this script.
This is awesome! Have you tried to bounce stems too?