No internet connection
  1. Home
  2. How to

How to batch process iZotope modules through Connect

By jk79 @jk79
    2020-04-07 17:23:48.267Z
    I would like a macro that sends to RX7 like this:

    0. RX CONNECT SEND PRO TOOLS AUDIO SUITE

    1. EQ MATCH PRESET 1

    2. EQ MATCH PRESET 2

    3. LEVELER

    4. SEND BACK TO PRO TOOLS

    5. RX CONNECT RENDER

    Is this possible?
    Solved in post #3, click to view
    • 23 replies

    There are 23 replies. Estimated reading time: 27 minutes

    1. Hi Joe - welcome to the forum!

      I'll work on a script for you in a second.

      1. Hi Joe.

        Please see this (rather crazy and advanced) long script that should do what you need:

        
        function sendToIzotope() {
            var win = sf.ui.proTools.getAudioSuiteWindow('RX 7 Connect');
            if (!win || !win.exists)
                win = sf.ui.proTools.getAudioSuiteWindow('RX 6 Connect');
            if (!win || !win.exists) {
                if (sf.ui.proTools.getMenuItem('AudioSuite', 'Noise Reduction', 'RX 7 Connect').exists) {
                    win = sf.ui.proTools.audioSuiteOpenPlugin({
                        category: 'Noise Reduction',
                        name: 'RX 7 Connect'
                    }).window;
                } else if (sf.ui.proTools.getMenuItem('AudioSuite', 'Noise Reduction', 'RX 6 Connect').exists) {
                    win = sf.ui.proTools.audioSuiteOpenPlugin({
                        category: 'Noise Reduction',
                        name: 'RX 6 Connect'
                    }).window;
                } else
                    throw "RX 6/7 Connect not installed, or you are not sorting modules by Category";
            }
        
            win.audioSuiteSetOptions({ processingInputMode: 'ClipByClip', processingOutputMode: 'CreateIndividualFiles' });
            win.getFirstWithTitle("Analyze").elementClick();
        }
        
        function sendToIzotopeAndWaitForFileToBeReady() {
            function getPath() {
                var docs = sf.ui.izotope.windows.filter(w => w.getString("AXDocument") != null);
                return docs.length > 0 ? decodeURIComponent(docs[0].getString("AXDocument")).replace(/^file:\/\//, "") : null;
            }
        
            //Make sure to clean any existing files for RX connect
            var path = getPath();
            if (path && sf.file.exists({ path: path }).exists) {
                sf.file.delete({ path: path });
            }
        
            sf.ui.proTools.appActivateMainWindow();
            sendToIzotope();
            sf.ui.izotope.appActivateMainWindow();
        
            //Wait for file to exist and be ready in the document
            path = getPath();
            while (!path || !sf.file.exists({ path: path }).exists) {
                sf.wait({ intervalMs: 100 });
                path = getPath();
            }
        }
        
        function waitForIzotopeProcessing() {
            const isProcessing = () =>
                sf.ui.izotope.floatingWindows.whoseTitle.startsWith("Pro Tools ").exists ||
                sf.ui.izotope.floatingWindows.whoseTitle.startsWith("Composite").exists;
        
            sf.wait({ intervalMs: 1000 });
        
            while (isProcessing()) {
                sf.wait({ intervalMs: 500 });
            }
        }
        
        function renderAndSpot() {
            var shuttleBtn = sf.ui.izotope.mainWindow.children.whoseDescription.endsWith('Main Window').first.children.whoseDescription.is("Shuttle").first;
            shuttleBtn.elementClick({}, "Could not click Send Back button");
        
            sf.wait({ intervalMs: 500 });
        
            sf.ui.proTools.appActivateMainWindow({}, "Could not activate Pro Tools");
        
            var win = sf.ui.proTools.floatingWindows.filter(function (w) { var t = w.title.value; return t.indexOf("Audio Suite: RX") == 0 && t.indexOf("Connect") >= 0 })[0];
            if (!win || !win.exists) throw "Could not find iZotope RX Connect AudioSuite window";
        
            win.buttons.whoseTitle.is("Render").first.elementClick({}, "Could not click Render");
        }
        
        /**
         * @param {AxElement} moduleWin
         */
        function selectPluginPreset(moduleWin, name) {
            var btn = moduleWin.groups.whoseDescription.startsWith('EffectPanel').first.groups.whoseDescription.startsWith('EffectPanel').first.groups.whoseDescription.contains('Header Background').first.popupButtons.whoseTitle.contains('Preset').first;
            if (btn.value.invalidate().value == name) return;
            btn.elementClick();
            sf.keyboard.press({ keys: 'enter' });
            while (true) {
                sf.engine.checkForCancellation();
                if (btn.value.invalidate().value == name) break;
                sf.keyboard.press({ keys: 'down' });
            }
        }
        
        /**
         * @param {AxElement} moduleWin
         */
        function refreshPluginPreset(moduleWin) {
            var btn = moduleWin.groups.whoseDescription.startsWith('EffectPanel').first.groups.whoseDescription.startsWith('EffectPanel').first.groups.whoseDescription.contains('Header Background').first.popupButtons.whoseTitle.contains('Preset').first;
            btn.elementClick();
            sf.keyboard.press({ keys: 'return' });
        }
        
        function doProcess(modules) {
        
            sendToIzotopeAndWaitForFileToBeReady();
        
            //Make sure to click composite view button
            const compositeViewBtn = sf.ui.izotope.mainWindow.children.whoseDescription.endsWith('Main Window').first.getFirstWithDescription("Composite View");
            if (compositeViewBtn.exists) {
                compositeViewBtn.elementClick({}, 'Could not click composite view button');
            }
        
            //Process modules
            modules.map(({ moduleName, presetName }) => {
        
                //Make sure module is open
                var moduleWin = sf.ui.izotope.floatingWindows.whoseTitle.is(moduleName).first;
                if (!moduleWin.exists) {
                    sf.ui.izotope.getMenuItem('Modules', moduleName + '...').elementClick();
                    moduleWin.elementWaitFor({}, `Could not open module '${moduleName}'`);
                }
        
                //Try to select the correct preset
                refreshPluginPreset(moduleWin);
                selectPluginPreset(moduleWin, presetName);
        
                //Click Process
                var btn = moduleWin.getFirstOf('AXGroup').getFirstOf('AXGroup').getElements('AXChildren')[1].getFirstOf('AXButton');
                if (!btn.exists)
                    throw 'Could not find Process button in module';
        
                //Sometimes iZotope is busy from the previous operation, so keep trying to click process until we succeed :)
                while (true) {
                    var clickResult = btn.elementClick({ onError: 'Continue' });
                    if (clickResult.success) break;
        
                    sf.wait({ intervalMs: 1000 });
                }
        
                waitForIzotopeProcessing();
            });
        
            //Done processing, send back
            renderAndSpot();
        }
        
        
        /* MAIN */
        
        function main() {
            doProcess([
                {
                    moduleName: 'EQ Match',
                    presetName: 'Attenuate High Frequencies',
                },
                {
                    moduleName: 'EQ Match',
                    presetName: 'Bright',
                },
                {
                    moduleName: 'Leveler',
                    presetName: 'Consistent Voiceover',
                },
            ]);
        }
        
        main();
        

        You select the processes that need to be run right at the end.

        Let me know if you need help getting this to work :)
        Note: It's VERY important that the naming of the presets match their names in iZotope correctly, including punctuation, spacing and capitalization.

        Reply1 LikeSolution
        1. Jjk79 @jk79
            2020-04-07 17:47:18.419Z

            Wow.. I apologize, I havent actually used your software yet. Can you tell me how I execute the script in your program?

            Thank you

            1. No worries Joe.

              Since you haven't run any scripts yet, perhaps this is quite the mouthful to start with (at least if you need to change it etc.)

              To get the script to run, follow these instructions:

              • Go to the "Editor" page.
              • Click "+ New"
              • Choose Script
              • In the script editor (lower right half of the screen), paste the code in.
              • Now you need to assign a trigger (a way for SoundFlow to know when it should run this script).
              • In the top right section, click "+ New Trigger"
              • Choose Keyboard Trigger
              • Click "RECORD" and press a keyboard shortcut - for example "Ctrl+I" (for processing in Izotope)

              Make sure iZotope RX 7 Audio Editor is already open.
              Now go to Pro Tools, select a few clips, and click Ctrl+I.

              1. Jjk79 @jk79
                  2020-04-07 17:53:10.488Z

                  When I choose script it says

                  Please select a custom command package that you want to create your command in.
                  You select your package on the left hand side (the folders).

                  Does it matter which one I choose?

                  1. You should select the "Default Package" under "Custom Commands" - this is your first package (your first "folder" of scripts).

                    1. Jjk79 @jk79
                        2020-04-07 18:07:59.808Z

                        Sorry, I don't see Default Package, besides the one I selected but still get same message

                        1. When you set up your Profile, you had chosen to not show this package.
                          I've changed your settings for the "Untitled" profile so that it should show now.

                          If it doesn't work, you can either

                          1. Create a new Package (click "+ New" and choose "Package") or

                          2. Make sure the "Default Package" is selected in your Profile.
                            To do this, click the "Profile" page, click "Edit ...", then go to the last screen:

                            Here make sure "Show me all features" is clicked.

                          See more here:
                          https://forum.soundflow.org/-1809#post-5

                          1. Jjk79 @jk79
                              2020-04-07 18:26:06.332Z

                              Cool I'm getting there thanks! In the RX Connect audio suite macro is there a way to use the

                              Entire selection / Create continuous file settings?

                              1. Jjk79 @jk79
                                  2020-04-07 18:37:50.470Z

                                  Sorry nevermind I like the create individual files / clip by clip works great.

                                  Thank you so much!

                                  1. Awesome to hear Joe! Happy that we could make this work :)

                                    1. Dustin Harris @Dustin_Harris
                                        2020-04-15 12:46:47.648Z

                                        Wow this is pretty heavy. Can you help me break this down a bit:

                                            function getPath() {
                                                var docs = sf.ui.izotope.windows.filter(w => w.getString("AXDocument") != null);
                                                return docs.length > 0 ? decodeURIComponent(docs[0].getString("AXDocument")).replace(/^file:\/\//, "") : null;
                                            }
                                        

                                        The above gets the path of the 'temp' files sent by RX Connect for RX Editor to open?

                                            //Make sure to clean any existing files for RX connect
                                            var path = getPath();
                                            if (path && sf.file.exists({ path: path }).exists) {
                                                sf.file.delete({ path: path });
                                            }
                                        

                                        The above looks for the existence of 'old' files from RX Connect, and deletes them if found? (Useful to prevent desyncronization between RX Connect and Editor (unexpected quit/error etc)?

                                            sf.ui.proTools.appActivateMainWindow();
                                            sendToIzotope();
                                            sf.ui.izotope.appActivateMainWindow();
                                        

                                        Above switches to Protools, sends selected to RX Editor (via sendToIzotope function), then switches to Izotope?

                                            //Wait for file to exist and be ready in the document
                                            path = getPath();
                                            while (!path || !sf.file.exists({ path: path }).exists) {
                                                sf.wait({ intervalMs: 100 });
                                                path = getPath();
                                            }
                                        }
                                        

                                        Does this 'wait' for the file coming from Pro Tools to exist where RX expects it to?
                                        (if not, what is it in-fact doing?)

                                        Thank you so much!

                                        1. That's gonna be a big YES to each of your questions - great code reading :)

                                          1. Dustin Harris @Dustin_Harris
                                              2020-04-15 13:37:08.606Z

                                              Oh this is fantastic. The file delete before send is superb. I'm incorporating this into my own RX send script which handles things slightly differently (I keep RX connect open at the bottom of the screen to save time from having to open the plugin each time)

                                              1. In reply tochrscheuer:
                                                Dustin Harris @Dustin_Harris
                                                  2020-04-15 15:35:41.732Z

                                                  am I correct in thinking that RX Editor needs to be already open for

                                                      function getPath() 
                                                  

                                                  to return correctly?

                                                  1. Yes - I believe what this actually returns is the filename of the first open document in iZotope.

                                                    1. Dustin Harris @Dustin_Harris
                                                        2020-04-15 16:40:42.817Z

                                                        Right... so the script could throw an error if RX Connect is open but RX Editor is not?
                                                        I'm messing with something like this for it but I'm clumsy:

                                                        try {
                                                            sf.ui.izotope.appActivateMainWindow();
                                                        }
                                                        catch (err) {
                                                            log('Waiting for Rx Editor To Open');
                                                            sf.wait({ intervalMs: 2200 }); /* Try getting rid of this delay */
                                                        
                                                        }
                                                        
                                                        path = getPath();
                                                        
                                                        while (!path || !sf.file.exists({ path: path }).exists) {
                                                            sf.wait({ intervalMs: 100 });
                                                            path = getPath();
                                                        }
                                                        

                                                        Thoughts?

                                                        1. In terms of waiting for iZotope to be responsive, I remember that being particularly annoyingly hard to figure out.

                                                          Try checking out this:
                                                          https://forum.soundflow.org/-955#post-12

                                                          And this:
                                                          https://forum.soundflow.org/-1159/how-to-wait-for-rx7-audio-editor-to-open-before-proceeding

                                                          for inspiration

                                                          1. You could also consider first checking if RX is open before hitting connect, and if it isn't, first launch RX before hitting Connect, which may also save you some time.

                              2. In reply tochrscheuer:
                                RRobert Mallory @Robert_Mallory
                                  2022-04-12 02:01:55.092Z

                                  This is amazing and I LOVE you for it. It was pretty simple to update for RX 9, and to only process using De-wind. I copied your script and swapped out a few things:

                                  function sendToIzotope() {
                                  var win = sf.ui.proTools.getAudioSuiteWindow('RX 9 Connect');
                                  if (!win || !win.exists)
                                  win = sf.ui.proTools.getAudioSuiteWindow('RX 8 Connect');
                                  if (!win || !win.exists) {
                                  if (sf.ui.proTools.getMenuItem('AudioSuite', 'Noise Reduction', 'RX 9 Connect').exists) {
                                  win = sf.ui.proTools.audioSuiteOpenPlugin({
                                  category: 'Noise Reduction',
                                  name: 'RX 9 Connect'
                                  }).window;
                                  } else if (sf.ui.proTools.getMenuItem('AudioSuite', 'Noise Reduction', 'RX 8 Connect').exists) {
                                  win = sf.ui.proTools.audioSuiteOpenPlugin({
                                  category: 'Noise Reduction',
                                  name: 'RX 8 Connect'
                                  }).window;
                                  } else
                                  throw "RX 8/9 Connect not installed, or you are not sorting modules by Category";
                                  }

                                  win.audioSuiteSetOptions({ processingInputMode: 'ClipByClip', processingOutputMode: 'CreateIndividualFiles' });
                                  win.getFirstWithTitle("Analyze").elementClick();
                                  

                                  }

                                  function sendToIzotopeAndWaitForFileToBeReady() {
                                  function getPath() {
                                  var docs = sf.ui.izotope.windows.filter(w => w.getString("AXDocument") != null);
                                  return docs.length > 0 ? decodeURIComponent(docs[0].getString("AXDocument")).replace(/^file:///, "") : null;
                                  }

                                  //Make sure to clean any existing files for RX connect
                                  var path = getPath();
                                  if (path && sf.file.exists({ path: path }).exists) {
                                      sf.file.delete({ path: path });
                                  }
                                  
                                  sf.ui.proTools.appActivateMainWindow();
                                  sendToIzotope();
                                  sf.ui.izotope.appActivateMainWindow();
                                  
                                  //Wait for file to exist and be ready in the document
                                  path = getPath();
                                  while (!path || !sf.file.exists({ path: path }).exists) {
                                      sf.wait({ intervalMs: 100 });
                                      path = getPath();
                                  }
                                  

                                  }

                                  function waitForIzotopeProcessing() {
                                  const isProcessing = () =>
                                  sf.ui.izotope.floatingWindows.whoseTitle.startsWith("Pro Tools ").exists ||
                                  sf.ui.izotope.floatingWindows.whoseTitle.startsWith("Composite").exists;

                                  sf.wait({ intervalMs: 1000 });
                                  
                                  while (isProcessing()) {
                                      sf.wait({ intervalMs: 500 });
                                  }
                                  

                                  }

                                  function renderAndSpot() {
                                  var shuttleBtn = sf.ui.izotope.mainWindow.children.whoseDescription.endsWith('Main Window').first.children.whoseDescription.is("Shuttle").first;
                                  shuttleBtn.elementClick({}, "Could not click Send Back button");

                                  sf.wait({ intervalMs: 500 });
                                  
                                  sf.ui.proTools.appActivateMainWindow({}, "Could not activate Pro Tools");
                                  
                                  var win = sf.ui.proTools.floatingWindows.filter(function (w) { var t = w.title.value; return t.indexOf("Audio Suite: RX") == 0 && t.indexOf("Connect") >= 0 })[0];
                                  if (!win || !win.exists) throw "Could not find iZotope RX Connect AudioSuite window";
                                  
                                  win.buttons.whoseTitle.is("Render").first.elementClick({}, "Could not click Render");
                                  

                                  }

                                  /**

                                  • @param {AxElement} moduleWin
                                    */
                                    function selectPluginPreset(moduleWin, name) {
                                    var btn = moduleWin.groups.whoseDescription.startsWith('EffectPanel').first.groups.whoseDescription.startsWith('EffectPanel').first.groups.whoseDescription.contains('Header Background').first.popupButtons.whoseTitle.contains('Preset').first;
                                    if (btn.value.invalidate().value == name) return;
                                    btn.elementClick();
                                    sf.keyboard.press({ keys: 'enter' });
                                    while (true) {
                                    sf.engine.checkForCancellation();
                                    if (btn.value.invalidate().value == name) break;
                                    sf.keyboard.press({ keys: 'down' });
                                    }
                                    }

                                  /**

                                  • @param {AxElement} moduleWin
                                    */
                                    function refreshPluginPreset(moduleWin) {
                                    var btn = moduleWin.groups.whoseDescription.startsWith('EffectPanel').first.groups.whoseDescription.startsWith('EffectPanel').first.groups.whoseDescription.contains('Header Background').first.popupButtons.whoseTitle.contains('Preset').first;
                                    btn.elementClick();
                                    sf.keyboard.press({ keys: 'return' });
                                    }

                                  function doProcess(modules) {

                                  sendToIzotopeAndWaitForFileToBeReady();
                                  
                                  //Make sure to click composite view button
                                  const compositeViewBtn = sf.ui.izotope.mainWindow.children.whoseDescription.endsWith('Main Window').first.getFirstWithDescription("Composite View");
                                  if (compositeViewBtn.exists) {
                                      compositeViewBtn.elementClick({}, 'Could not click composite view button');
                                  }
                                  
                                  //Process modules
                                  modules.map(({ moduleName, presetName }) => {
                                  
                                      //Make sure module is open
                                      var moduleWin = sf.ui.izotope.floatingWindows.whoseTitle.is(moduleName).first;
                                      if (!moduleWin.exists) {
                                          sf.ui.izotope.getMenuItem('Modules', moduleName + '...').elementClick();
                                          moduleWin.elementWaitFor({}, `Could not open module '${moduleName}'`);
                                      }
                                  
                                      //Try to select the correct preset
                                      refreshPluginPreset(moduleWin);
                                      selectPluginPreset(moduleWin, presetName);
                                  
                                      //Click Process
                                      var btn = moduleWin.getFirstOf('AXGroup').getFirstOf('AXGroup').getElements('AXChildren')[1].getFirstOf('AXButton');
                                      if (!btn.exists)
                                          throw 'Could not find Process button in module';
                                  
                                      //Sometimes iZotope is busy from the previous operation, so keep trying to click process until we succeed :)
                                      while (true) {
                                          var clickResult = btn.elementClick({ onError: 'Continue' });
                                          if (clickResult.success) break;
                                  
                                          sf.wait({ intervalMs: 1000 });
                                      }
                                  
                                      waitForIzotopeProcessing();
                                  });
                                  
                                  //Done processing, send back
                                  renderAndSpot();
                                  

                                  }

                                  /* MAIN */

                                  function main() {
                                  doProcess([
                                  {
                                  moduleName: 'De-wind',
                                  presetName: 'StartHere',
                                  },

                                  ]);
                                  

                                  }

                                  main();

                                  I had been trying to figure it out a different way but couldn't quite get there with it. Probably just missing a step or two? (see image).

                                  In Rx Stand alone, you can use quick keys to view, and also to render using a module. So in this case, Shift+7 opens the view for De-wind, and then Command+7 renders it. So using two Press Key macros, I was trying to view De-wind then render. Something's not quite working yet with this method but I'm still working on it. If you can see what I'm missing, pls lmk! Thanks!!

                                  1. RRobert Mallory @Robert_Mallory
                                      2022-04-12 02:22:18.845Z

                                      OK! I updated this macro (the one from the screenshot above) but am still getting stuck at the final step. I realized I had to add a line to wait for Izotope to become Active, before I could Press Keys (Izotope shortcuts: Shift+7 then Command+7) to Open De-wind then Render De-wind. However, what I can't figure out now, is how to make it wait for Izotope to process the audio before Render & Spot Back into Pro Tools. (see new image below). Any ideas? Thanks in advance!

                                      *note: I figured out how to make it work by adding a 5000ms Wait command before Render & Spot Back into Pro Tools, but I bet there's a smoother way to do that... ?

                                • D
                                  In reply tojk79:
                                  Davide Favargiotti @dieffe
                                    2020-04-17 14:24:01.855Z

                                    I love Soundflow, but isn't it easier to create a chain in Izotope with the Module Chain that has all your presets and then just do this:

                                    • Rx connect
                                    • Apply Module Chain with your named preset (that has all your treatments you want to apply)
                                    • Send back to PT
                                    • Render

                                    Cheers

                                    Davide

                                    1. Yes that's also a good idea that definitely might speed things up even further :)