No internet connection
  1. Home
  2. How to
  3. Logic Pro

Logic Pro - import multiple audio files and spot to grid?

By Erik Huber @Erik_Huber
    2025-01-23 01:24:06.518Z

    Hello - I am looking to use the "Import - Audio File" action from SoundFlow's Logic package to batch import a folder's worth of audio tracks in one go, and then (hopefully) spot each of them to the nearest barline using the Event List editor. However, there are no available fields in the "Import" action that would seem to allow me to specify multiple files. Is there a workaround for this?

    Thanks in advance!

    Solved in post #10, click to view
    • 11 replies

    There are 11 replies. Estimated reading time: 16 minutes

    1. Kitch Membery @Kitch2025-01-24 01:43:56.075Z

      Hi @Erik_Huber,

      I believe this would require a custom script, as the "Import - Audio File…" command only clicks the menu item to open the "Import Audio" window.

      In point form would you be able to list the manual steps you'd do to perform this workflow? That way I can see if what you're after is possible.

      Thanks in advance. :-)

      1. EErik Huber @Erik_Huber
          2025-01-25 14:03:41.336Z

          @Kitch thank you for responding! Here are the points as I am seeing them:

          1. Identity the source folder where the needed audio files are located.
          2. “Select All” of the audio files in said folder.
          3. Drag them into an audio track within the Arrange window in Logic.
          4. When prompted, select the “Place All Files On One Track” option (this will lay out all files sequentially on a single track).
          5. Reduce the session tempo from 1000 BPM (my default starting tempo) to the “native” BPM of the newly imported clips - typically somewhere between 60-150 BPM. (I would need a way for the script to prompt for this input, I think?)
          6. Select all of the newly placed audio files within the Arrange window. (Usually I do this by setting my right locator position to the end of the session and then clicking on the track header, which automatically selects all the files/regions on that particular track.)
          7. From there, open the Event List editor. (“E” command shortcut on my rig)
          8. With all the audio files still selected and the Event List window active, use the Quantize function in the Event List editor to spot all the audio files to the nearest bar line (ie. quantize by whole note).

          Hopefully I explained that all clearly. Does that process seem achievable via a script?

          Thanks in advance!!

          1. Kitch Membery @Kitch2025-01-27 19:18:39.978Z

            This is a great breakdown @Erik_Huber, I see what I can come up with later this week. :-)

            1. EErik Huber @Erik_Huber
                2025-01-27 19:39:14.155Z

                Thank you @Kitch! I appreciate it very much.

              • In reply toErik_Huber:
                Kitch Membery @Kitch2025-01-31 23:36:47.554Z

                Hi Eric!

                Sorry for the delay on this, it has been a busy week.

                I must say, your breakdown made it very easy to understand the workflow you're after. Thank you for that!

                The process I've used to import the files to the audio track is a programmatic approach, I chose to do this as it's much more robust/stable than using mouse simulation to drag files.

                Here is a video of it in action!

                https://www.loom.com/share/3654147d7a5442f0a06e23d1af77654a?sid=d83720c7-40da-473a-a183-0fe7a56d545b

                And here is the script...

                const logic = sf.ui.logic
                
                logic.appActivate();
                logic.invalidate();
                
                const mainWindow = logic.mainWindow;
                const controlBar = mainWindow.controlBar;
                
                const allFilesPanel = () => mainWindow.groups.invalidate().allItems[mainWindow.groups.length - 1].groups.whoseDescription.is("All Files").first;
                const allFilesGroup = () => allFilesPanel().groups.whoseDescription.is("All Files").first;
                
                // Get the selected folder in finder
                const fileDirectory = sf.ui.finder.firstSelectedPath;
                const fileDirectoryPath = fileDirectory.split("/").filter(Boolean);
                
                // Get the default startup hard drive volume
                let volumeName = sf.system.exec({ commandLine: `diskutil info / | awk -F': ' '/Volume Name/ {print $2}'` }).result.trim()
                
                function openBrowsersAndFocusComputer() {
                    // Open Browsers panel if not already open
                    const wasBrowserPanelOpen = mainWindow.controlBar.viewBrowsersButton.value.invalidate().intValue === 1
                
                    if (!wasBrowserPanelOpen) {
                        mainWindow.controlBar.viewBrowsersButton.elementClick();
                    }
                
                    // Click "All Files" tab
                    mainWindow.radioGroups.first.radioButtons.whoseDescription.is("All Files").first.elementClick();
                
                    // Click "Computer" button
                    allFilesGroup().groups.find(g => g.buttons.first.exists).buttons.first.elementClick();
                }
                
                function selectDirectories(directoryName) {
                    const table = allFilesGroup().groups.first.scrollAreas.first.tables.first;
                    const rows = table.children.whoseRole.is("AXRow");
                
                    // Find and select the specified directory
                    const targetRow = rows.find(row =>
                        row.children.whoseRole.is("AXStaticText").whoseValue.is(directoryName).first.exists
                    );
                
                    if (targetRow) {
                        targetRow.elementSetAttributeValue({
                            attributeName: "AXSelected",
                            attributeValue: true,
                        });
                    }
                
                    // Click the "Open" button
                    allFilesGroup().buttons.whoseTitle.is("Open").first.elementClick();
                }
                
                function placeFilesOnTrack() {
                    const filesTable = allFilesGroup().groups
                        .find(g => g.scrollAreas.first.tables.first.exists)
                        .scrollAreas.first.tables.first;
                
                    const firstRow = filesTable.childrenByRole("AXRow").first;
                
                    // Select the first file
                    firstRow.elementSetAttributeValue({ attributeName: "AXSelected", attributeValue: true });
                
                    // Wait for selection to be registered
                    sf.waitFor({
                        callback: () => filesTable.invalidate().childrenByRole("AXRow").first.getElement("AXSelected") !== null,
                        timeout: 1000,
                    });
                
                    // Select all files
                    logic.menuClick({ menuPath: ["Edit", "Select All"] });
                
                    // Click the "Add" button
                    allFilesGroup().buttons.whoseTitle.is("Add").first.elementWaitFor().element.elementClick({ asyncSwallow: true });
                
                    // Select the "Place All Files On One Track" option
                    const addFilesWindow = logic.windows.whoseTitle.is("Add Selected Files to Tracks").first;
                    addFilesWindow.radioButtons.whoseTitle.is("Place all files on one track").first.elementClick();
                
                    // Confirm by clicking "OK"
                    addFilesWindow.buttons.getByTitle("OK").elementClick();
                }
                
                function reduceSessionTempo(targetTempo) {
                    const tempoSlider = mainWindow.controlBar
                        .groups.whoseDescription.is("Control Bar").first
                        .sliders.whoseDescription.is("Tempo").first;
                
                    while (Number(tempoSlider.invalidate().value.intValue) > Number(targetTempo)) {
                        tempoSlider.value.intValue = 1;
                    }
                }
                
                function quantizeEvents(quantizeValue) {
                    // Open the Event List editor
                    logic.menuClick({ menuPath: ["Window", "Open Event List"] });
                
                    const eventListWindow = logic.windows.whoseTitle.endsWith(" - Event List").first;
                    const eventGroup = eventListWindow.groups.whoseDescription.is("Event").first;
                
                    eventListWindow.elementWaitFor();
                
                    const quantizePopupButton = eventGroup.popupButtons.slice(-1)[0];
                
                    quantizePopupButton.elementWaitFor();
                
                    quantizePopupButton.popupMenuSelect({ menuPath: [quantizeValue], });
                
                    // Wait for Quantize Value to change.
                    sf.waitFor({
                        callback: () => quantizePopupButton.invalidate().value.value === quantizeValue,
                        timeout: 1000,
                    });
                
                    // Wait for regions to be Quantized.
                    sf.waitFor({
                        callback: () => {
                            const rows = eventGroup.scrollAreas.first.tables.first.childrenByRole("AXRow");
                
                            const regionsQuantized = rows.every(row =>
                                row.children.whoseRole.is("AXCell").allItems[2].groups.first.getString("AXDescription").endsWith(" 1 1 1 ")
                            );
                
                            return regionsQuantized;
                        },
                        timeout: 2000,
                    });
                
                    // Wait for rows to be updated (Need to find a better way to avoid an arbitrary wait)
                    sf.wait({ intervalMs: 400 });
                
                    eventListWindow.windowClose();
                }
                
                function main() {
                    // Open the browser and navigat to the root directory
                    openBrowsersAndFocusComputer();
                
                    // Create full path to the target folder
                    const directoryPathSegments = [
                        volumeName,
                        ...fileDirectoryPath,
                    ];
                
                    // Navigate to the directory in the browser
                    directoryPathSegments.forEach(selectDirectories);
                
                    // Place files on track
                    placeFilesOnTrack();
                
                    // Prompt user for tempo
                    const targetTempo = sf.interaction.displayDialog({
                        buttons: ["OK", "Cancel"],
                        cancelButton: "Cancel",
                        defaultAnswer: "105",
                        prompt: "Please enter the target project tempo",
                        defaultButton: "OK",
                        title: "Set Tempo",
                    }).text;
                
                    // Reduce the session tempo from 1000 to the target tempo
                    reduceSessionTempo(targetTempo);
                
                    // Quantize selected regions to the nearest bar line (ie. quantize by whole note "1/1 Note")
                    quantizeEvents("1/1 Note")
                }
                
                main();
                

                Please note, there are a few prerequisites before running the script.

                • The Logic Pro Arrange window needs to be frontmost.
                • An audio track needs to be selected.
                • The Tempo needs to be visible in the control bar area.
                • The folder with your target audio files needs to be selected in the finder before running the script.

                Let me know how it goes for you.

                It's so great to get some more automation working in Logic Pro. I learned a bunch when putting this script together. :-)

                1. EErik Huber @Erik_Huber
                    2025-02-13 20:18:14.239Z

                    @Kitch thank you so much for putting this together. I apologize for the delay on this as I ran into an error and then got caught up in other projects. Here is the error message that I am receiving when attempting to run the script:

                    "Spot Multiple Regions to Grid failed - Could not find menu item: Edit->Select All (Spot Multiple Regions to Grid: Line 71)"

                    Screenshot of the error is attached here as well.

                    Any ideas on what to try next? I'm afraid I am out of my depth here in troubleshooting this, so if you have any suggestions I would love it!

                    1. Kitch Membery @Kitch2025-02-13 20:31:40.878Z

                      Hi @Erik_Huber,

                      Are you able to do a screen recording of the process you are taking when you see the error?

                      Hopefully, this will provide me with the information to troubleshoot the issue further. :-)

                      1. EErik Huber @Erik_Huber
                          2025-02-17 16:06:52.192Z

                          @Kitch

                          Thanks again for your help with this process. I've recorded a short Loom video capturing the issue:

                          https://www.loom.com/share/bdc78a4c6010429d8ed01420ccab12d2?sid=ed40b072-4730-439c-a710-dc6c226e3088

                          Let me know if that makes sense. Happy to provide further context if that is helpful!

                          Thanks again.

                          1. Kitch Membery @Kitch2025-02-17 16:19:34.079Z

                            Hi Erik_Huber,

                            Thanks for that video. That was very helpful!

                            In the finder, instead of selecting the files inside the folder, can you try selecting the root folder instead?

                            The script is designed to navigate to the root folder in the Logic Browser and then select all the files within that folder.

                            Hopefully that will resolve the issue. :-)

                            ReplySolution
                            1. EErik Huber @Erik_Huber
                                2025-02-17 16:51:27.485Z

                                @Kitch

                                That worked! Thank you!!!

                                I have an unrelated question for you - one of my associates here is asking if we can set up a script for a "batch importing tracks" process that he frequently uses to import groups of tracks from one of several Logic templates into his current working session.

                                Am I correct in thinking that we CANNOT create a script for any workflows that make use of the cursor (ie. manually highlighting certain tracks via clicking & dragging)? Or in other words, does there need to be a keyboard shortcut for a particular item in order for it to be scripted?

                                Thanks again.

                                1. Kitch Membery @Kitch2025-02-18 18:11:02.590Z

                                  Hi @Erik_Huber

                                  Great to hear that worked!

                                  Regarding your associate's request, so we don't confuse this thread, please start, or have them start a new thread for this, and we can discuss the possibilities further.

                                  Thanks in advance.