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

Logic Pro X - User Patch Loader?

By Justin Krol @Justin_Krol
    2024-06-15 14:21:03.337Z

    The instrument loader in the Logic Pro X SoundFlow package is great for creating basic user presets, however, if you create a "User Patch" with custom multi-outputs, it appears to only be accessible through the Library pane...(let me know if there's another option). I thought it appeared in the channel strip settings, but it doesn't seem to be an option there either.

    Does anyone have a script to load a User Patch with SoundFlow? Or some ideas to write a script? Thanks!

    Solved in post #8, click to view
    • 26 replies

    There are 26 replies. Estimated reading time: 16 minutes

    1. Kitch Membery @Kitch2024-06-15 18:16:18.374Z

      Hi @Justin_Krol,

      This is one feature that I should add to the Logic Pro package for sure. I’ll add it to my list and see if I can make it happen. Thanks for the great suggestion/request. :-)

      1. J
        In reply toJustin_Krol:
        Justin Krol @Justin_Krol
          2024-06-15 19:06:09.067Z

          @Kitch Thanks man! Much appreciated.

          1. Kitch Membery @Kitch2024-06-18 08:10:46.662Z

            Ok @Justin_Krol,

            Below is a work in progress... Unfortunately, the script needs to use a mouse click as the UI elements are not responding to the .elementClick() method.

            I'll take another swing at it when I have a chance but this should provide you with some functionality till I can dig deeper.

            const logic = sf.ui.logic;
            logic.appActivate();
            logic.invalidate();
            
            function ensureLibraryVisibility() {
                const libraryGroup = logic.mainWindow.groups.whoseDescription.is("Library").first;
            
                // Check if the Library panel is visible
                if (!libraryGroup.exists) {
                    const viewLibraryButton = sf.ui.logic.mainWindow.controlBar.viewLibraryButton;
            
                    // Check if the Library button is available and toggle it if necessary
                    if (viewLibraryButton.exists) {
                        if (!viewLibraryButton.isEnabled) {
                            viewLibraryButton.checkboxSet({ targetValue: "Enable" });
                        }
                    } else {
                        // Navigate through the menu to show the Library if the button isn't available
                        logic.getMenuItem("View").elementClick();
                        logic.getMenuItem("View", "Show Library").elementClick();
                    }
                }
            
                // Wait for the library element to be visible after attempting to enable it
                libraryGroup.elementWaitFor();
            }
            
            function recallLibraryPreset({ path }) {
                const libraryGroup = logic.mainWindow.groups.whoseDescription.is("Library").first;
            
                // Ensure the Library panel is visible
                ensureLibraryVisibility();
            
                const libraryPanel = libraryGroup.groups.whoseDescription.is("Library").first;
            
                const libraryBrowser = libraryPanel.groups.first.splitGroups.first.groups.allItems[2].children.whoseRole.is("AXBrowser")
            
                const horizontalScrollBar = libraryBrowser.whoseDescription.is("Library").first
                    .scrollAreas.first.getElement("AXHorizontalScrollBar").children.whoseRole.is("AXValueIndicator").first;
            
                // If the Horizontal Scroll Bar exists scroll it all the way to the left
                if (horizontalScrollBar.exists) horizontalScrollBar.value.intValue = 0;
            
                const columns = libraryBrowser.whoseDescription.is("Library").first.scrollAreas.first.scrollAreas;
            
                // Click each path item
                path.forEach((item, index) => {
                    const column = columns.invalidate().allItems[index];
                    const list = column.children.whoseRole.is("AXList").first;
                    const textFields = list.children.whoseRole.is("AXStaticText");
                    const targetElement = textFields.find(e => e.value.value === item);
            
                    if (!targetElement) throw `Could not find the path item "${item}" in the path:${JSON.stringify(path)}`
            
                    // Requires Menu Click
                    targetElement.mouseClickElement();
                });
            }
            
            recallLibraryPreset({ path: ["Voice", "Experimental", "Delay Vocal"] });
            
            1. JJustin Krol @Justin_Krol
                2024-06-18 20:21:13.999Z

                Amazing, thanks so much! I'm gonna take it for a spin in a little bit.

                1. JJustin Krol @Justin_Krol
                    2024-08-30 01:10:44.721Z

                    Hey @Kitch, I finally got back around to playing with this one. When I try to update the script to my specific patches, the script closes out the second column. For example, using the screenshot from my original post above, when I run the command, the user patch "AD DRUM TEST NEW" vanishes from the column. No rush at all, but maybe to help me better understand, could you update your script specifically to my example path? It would be "User Patches" > "AD DRUM TEST NEW" (for this specific example).

                    Thanks so much!

                    1. Kitch Membery @Kitch2024-08-30 03:36:50.343Z

                      Hi @Justin_Krol ,

                      It looks like I'll have to take a different approach to it, instead using the search field.

                      Try out this script. (unfortunately, it still requires a mouse click, but only one).

                      const logic = sf.ui.logic;
                      logic.appActivate();
                      logic.invalidate();
                      
                      function ensureLibraryVisibility() {
                          const libraryGroup = logic.mainWindow.groups.whoseDescription.is("Library").first;
                      
                          // Check if the Library panel is visible
                          if (!libraryGroup.exists) {
                              const viewLibraryButton = sf.ui.logic.mainWindow.controlBar.viewLibraryButton
                      
                              // Check if the Library button is available and toggle it if necessary
                              if (viewLibraryButton.exists && viewLibraryButton.value.intValue === 0) {
                                  viewLibraryButton.checkboxSet({ targetValue: "Enable" });
                              } else {
                                  // Navigate through the menu to show the Library if the button isn't available
                                  logic.getMenuItem("View").elementClick();
                                  logic.getMenuItem("View", "Show Library").elementClick();
                              }
                          }
                      
                          // Wait for the library element to be visible after attempting to enable it
                          libraryGroup.elementWaitFor();
                      }
                      
                      function recallLibraryPreset({ name }) {
                          ensureLibraryVisibility();
                      
                          const mainLibraryGroup = sf.ui.logic.mainWindow.groups.whoseDescription.is("Library")
                              .first.groups.whoseDescription.is("Library")
                              .first.groups
                              .first.splitGroups
                              .first.groups;
                      
                          const searchField = mainLibraryGroup.allItems[1].textFields.first;
                      
                          searchField.value.value = name
                          searchField.elementClick({ actionName: "AXConfirm" });
                      
                          const rows = mainLibraryGroup.allItems[2].scrollAreas.first.tables.first.children.whoseRole.is("AXRow");
                      
                          const targetRow = rows.find(row => row.children.first.children.first.value.value === name);
                      
                          targetRow.children.first.children.first.mouseClickElement();
                      }
                      
                      recallLibraryPreset({
                          name: "AD DRUM TEST NEW",
                      });
                      

                      Let me know if it works for you. :-)

                      1. JJustin Krol @Justin_Krol
                          2024-08-30 15:43:46.683Z

                          Beautiful! Works perfectly over here. Thank you so much!

                          Reply1 LikeSolution
                          1. Kitch Membery @Kitch2024-08-30 19:13:33.308Z

                            Awesome!!

                            1. JJustin Krol @Justin_Krol
                                2024-09-09 19:34:26.826Z

                                PS - this would be an awesome one to add to future package updates, even if it's just me being selfish about having to edit the script for my 50+ user patches :)

                                1. Kitch Membery @Kitch2024-09-09 19:36:16.128Z

                                  This could easily be turned into a command template. Do you know how to make command templates?

                                  1. JJustin Krol @Justin_Krol
                                      2024-09-10 01:17:56.809Z2024-09-10 14:23:20.624Z

                                      I haven't done it before, but I bet I could figure it out. I'll play around with it this week!

                                      1. Kitch Membery @Kitch2024-09-10 01:19:25.783Z

                                        I bet you could too...

                                        Check out this link for more information. :-)
                                        https://soundflow.org/docs/how-to/custom-commands/command-templates

                                        If you get stuck let me know!

                                        1. JJustin Krol @Justin_Krol
                                            2024-09-10 01:20:39.409Z

                                            Awesome, thanks! Will do.

                                            1. In reply toKitch:
                                              JJustin Krol @Justin_Krol
                                                2024-09-10 14:22:54.502Z

                                                Question - Am I adding a property for every step and the functions (like the pause, check if piano roll is open, etc)?

                                                1. Kitch Membery @Kitch2024-09-10 17:47:21.648Z

                                                  Hi @Justin_Krol,

                                                  For this one, you'd just create a command template property with the Title "Preset Name" with the "Type" set to "String".

                                                  And then change the script to this...

                                                  const {presetName} = event.props;
                                                  
                                                  const logic = sf.ui.logic;
                                                  logic.appActivate();
                                                  logic.invalidate();
                                                  
                                                  function ensureLibraryVisibility() {
                                                      const libraryGroup = logic.mainWindow.groups.whoseDescription.is("Library").first;
                                                  
                                                      // Check if the Library panel is visible
                                                      if (!libraryGroup.exists) {
                                                          const viewLibraryButton = sf.ui.logic.mainWindow.controlBar.viewLibraryButton
                                                  
                                                          // Check if the Library button is available and toggle it if necessary
                                                          if (viewLibraryButton.exists && viewLibraryButton.value.intValue === 0) {
                                                              viewLibraryButton.checkboxSet({ targetValue: "Enable" });
                                                          } else {
                                                              // Navigate through the menu to show the Library if the button isn't available
                                                              logic.getMenuItem("View").elementClick();
                                                              logic.getMenuItem("View", "Show Library").elementClick();
                                                          }
                                                      }
                                                  
                                                      // Wait for the library element to be visible after attempting to enable it
                                                      libraryGroup.elementWaitFor();
                                                  }
                                                  
                                                  function recallLibraryPreset({ name }) {
                                                      ensureLibraryVisibility();
                                                  
                                                      const mainLibraryGroup = sf.ui.logic.mainWindow.groups.whoseDescription.is("Library")
                                                          .first.groups.whoseDescription.is("Library")
                                                          .first.groups
                                                          .first.splitGroups
                                                          .first.groups;
                                                  
                                                      const searchField = mainLibraryGroup.allItems[1].textFields.first;
                                                  
                                                      searchField.value.value = name
                                                      searchField.elementClick({ actionName: "AXConfirm" });
                                                  
                                                      const rows = mainLibraryGroup.allItems[2].scrollAreas.first.tables.first.children.whoseRole.is("AXRow");
                                                  
                                                      const targetRow = rows.find(row => row.children.first.children.first.value.value === name);
                                                  
                                                      targetRow.children.first.children.first.mouseClickElement();
                                                  }
                                                  
                                                  recallLibraryPreset({
                                                      name: presetName,
                                                  });
                                                  

                                                  Then you can create presets for each of your patches.

                                                  1. JJustin Krol @Justin_Krol
                                                      2024-09-10 18:23:44.119Z

                                                      Oh, amazing! Thanks so much for this. I'll hop on this as soon as I get chance today!

                                                      1. Kitch Membery @Kitch2024-09-10 18:30:46.319Z

                                                        Let me know if you get stuck.

                                                        1. JJustin Krol @Justin_Krol
                                                            2024-09-10 20:08:51.759Z

                                                            And we have liftoff!! Thanks so much...as always!

                                                            1. Kitch Membery @Kitch2024-09-10 20:09:27.995Z

                                                              Woo!!!

                              • J
                                In reply toJustin_Krol:
                                Justin Krol @Justin_Krol
                                  2024-10-24 20:33:17.973Z

                                  Hey @Kitch!

                                  Something weird is happening now where my "edit a copy" button is missing from the default preset for this command. Let me know if you have any ideas. I've got about 30 more patches to add :)

                                  1. JJustin Krol @Justin_Krol
                                      2024-10-24 20:34:32.073Z

                                      PS - the source code is still the same as above (the last one from you).

                                      1. In reply toJustin_Krol:
                                        Kitch Membery @Kitch2024-10-24 21:38:51.782Z

                                        Hi Justin,

                                        Do you mean the "Add Preset" button? If so, you need to select the "USER PATCH LOADER" in the Command Name column. :-)

                                      2. J
                                        In reply toJustin_Krol:
                                        Justin Krol @Justin_Krol
                                          2024-10-25 01:49:45.017Z

                                          Yeah--like what's highlighted here? It's still not showing unless I'm having a massive brain fart here. Which is entirely possible :)

                                          1. JJustin Krol @Justin_Krol
                                              2024-10-29 15:34:55.066Z

                                              Update: So it appears that any user created template that I've made is missing the "Edit a copy" button. All of the SoundFlow package ones are still there, so it only seems to be affecting the ones that I've created. If that helps.

                                              1. Chad Wahlbrink @Chad2024-10-29 16:27:48.851Z

                                                Hey @Justin_Krol,

                                                Chiming in.

                                                The "Add New Preset" and "Make Editable Copy" buttons are only available when you install a package from the Store.

                                                For example, I don't have those options when editing my CW Pro Tools Utilities package from "My Packages." In this scenario, you can select any preset and use the "New" button to create a preset or a preset folder for your package. Alternatively, you can use CMD+D to duplicate your presets. Then you can rename them, etc.

                                                When I install the same package from the store, I can use the "Add New Preset" and "Make Editable Copy" buttons for the same command when I have the parent command ("Show Automation Lanes for Tracks" in this case):

                                                1. JJustin Krol @Justin_Krol
                                                    2024-10-30 00:32:46.852Z

                                                    Oh man, there's that brain fart I was talking about! Sorry, it's been a minute since I tweaked my own presets and I completely forgot about this crucial piece of information, haha.

                                                    Many thanks!