No internet connection
  1. Home
  2. Support

Batch normalize to EBU R128 of Selected Clips in Track

By Stefan Möhl @Stefan_Mohl
    2020-07-12 15:39:25.941Z

    My goal with this script is to render each clip in a selection with an audiosuite plugin, in my case with Nugen LMCorrect2.
    So it acts lika a batch normalization. I have found some useful text snippet that I glued together but I recognized a strange behaviour using the function sf.ui.proTools.clipDoForEachSelectedClip().

    The scripts cycles clip by clip through the section but only the first one gets rendered. The following ones only gets analyzed. I have create a video of the behaviour.
    https://www.dropbox.com/s/xl1bkh9e6p4viw7/Batch_LmCorrect2.mov?dl=0

    
    // open NUGEN LMCorrect2
    
    var asWin = sf.ui.proTools.getAudioSuiteWindow("LMCorrect2");
    if (!asWin.exists) {
        asWin = sf.ui.proTools.audioSuiteOpenPlugin({
            category: 'Sound Field',
            name: 'NUGEN LMCorrect2'
        }).window;
    }
    
    // wait for Audiosuite plugin to appear
    sf.ui.proTools.firstAudioSuiteWindow.elementWaitFor({
        waitType: "Appear",
    });
    
    // load preset
    asWin.audioSuiteSelectPreset({
        presetMenuPath: ["EBU-R128-23LUFS-3TP"]
    });
    
    // set options
    asWin.audioSuiteSetOptions({
        processingInputMode: "EntireSelection",
        processingOutputMode: "CreateContinuousFile"
    });
    
    // set input mode    
    sf.ui.proTools.firstAudioSuiteWindow.popupButtons.whoseTitle.is('input mode').first.popupMenuSelect({
        menuPath: ["multi-input mode"]
    });
    
    // audio suite procees function.
    // Analyze -> Wait until it is done -> Render
    function analyzeAndRenderWithLMCorrect2() {
        sf.engine.checkForCancellation();
        asWin.getFirstWithTitle("Analyze").elementClick();
        sf.ui.proTools.waitForNoModals();
        asWin.audioSuiteRender();
    }
    
    
    // 
    sf.ui.proTools.mainCounterDoWithValue({
        targetValue: 'Samples',
        action: () => {
            var oldSelection = sf.ui.proTools.selectionGetInSamples();
            try {
                //analyzeAndRenderWithLMCorrect2();
                sf.ui.proTools.clipDoForEachSelectedClip({
                    action: analyzeAndRenderWithLMCorrect2,
                });
            } finally {
                sf.ui.proTools.selectionSetInSamples({
                    selectionStart: oldSelection.selectionStart,
                    selectionLength: oldSelection.selectionLength
                });
            }
        }
    });
    

    I get an error message in line 45 that says 'DoMainCounterAction'.
    Can someone help me out with this batch?

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

    There are 11 replies. Estimated reading time: 21 minutes

    1. Stefan Möhl @Stefan_Mohl
        2020-07-12 19:22:31.627Z2020-07-12 20:10:35.782Z

        I'm already one step ahead. The clips must be real files and not edited clips. Then the batch conversion works for each clip but not on each pass. Sometimes it is working sometimes it only analyzes and then jumps to the next clip without rendering. Is this a timing issue?
        It would be great if this would also work with edited clips that are cut at the front and back.
        Can I find some documention how exactly does the function sf.ui.proTools.clipDoForEachSelectedClip({ action: myfunction}); work?
        Meanwhile I have edited my script by creating real duplicate files with handle lenght set to 0 before the next step.

        I am not sure if this workflow make sense but it would be wonderful to have a batch converter in-the-box in ProTools.

        
        sf.ui.proTools.appActivateMainWindow();
         // create real files
        var asWinDuplicate = sf.ui.proTools.getAudioSuiteWindow("Duplicate");
        if (!asWinDuplicate.exists) {
            asWinDuplicate = sf.ui.proTools.audioSuiteOpenPlugin({
                category: "Other",
                name: "Duplicate",
            }).window;
        }
        
        // wait for Audiosuite plugin to appear
        sf.ui.proTools.firstAudioSuiteWindow.elementWaitFor({
            waitType: "Appear",
        });
        
        //Change processing modes
        asWinDuplicate.audioSuiteSetOptions({
            processingInputMode: "ClipByClip",
            processingOutputMode: "CreateIndividualFiles"
        });
        
        //Select Handle Length
        sf.ui.proTools.firstAudioSuiteWindow.textFields.whoseTitle.is('Processing Handle Length in Seconds').first.elementClick();
        
        //Change Value to 0
        sf.keyboard.type({
            text: "0",
        });
        
        //Press Enter/Return
        sf.keyboard.press({
            keys: "return",
        });
        
        asWinDuplicate.audioSuiteRender();
        sf.ui.proTools.waitForNoModals();
        asWinDuplicate.windowClose();
        sf.ui.proTools.firstAudioSuiteWindow.elementWaitFor({
            waitType: "Disappear",
        });
        
        
        
        
        
        
        /**Start Nugen */ 
        
        // open NUGEN LMCorrect2
        var asWin = sf.ui.proTools.getAudioSuiteWindow("LMCorrect2");
        if (!asWin.exists) {
            asWin = sf.ui.proTools.audioSuiteOpenPlugin({
                category: 'Sound Field',
                name: 'NUGEN LMCorrect2'
            }).window;
        }
        
        // wait for Audiosuite plugin to appear
        sf.ui.proTools.firstAudioSuiteWindow.elementWaitFor({
            waitType: "Appear",
        });
        
        // load preset
        asWin.audioSuiteSelectPreset({
            presetMenuPath: ["EBU-R128-23LUFS-3TP"]
        });
        
        // set options
        asWin.audioSuiteSetOptions({
            processingInputMode: "EntireSelection",
            processingOutputMode: "CreateContinuousFile"
        });
        
        // set input mode    
        sf.ui.proTools.firstAudioSuiteWindow.popupButtons.whoseTitle.is('input mode').first.popupMenuSelect({
            menuPath: ["multi-input mode"]
        });
        
        // audio suite procees function.
        // Analyze -> Wait until it is done -> Render
        function analyzeAndRenderWithLMCorrect2() {
            sf.engine.checkForCancellation();
            
            asWin.getFirstWithTitle("Analyze").elementClick();
            sf.ui.proTools.waitForNoModals();
            asWin.audioSuiteRender();
           
        }
        
        
        
        sf.ui.proTools.mainCounterDoWithValue({
            targetValue: 'Samples',
            action: () => {
                var oldSelection = sf.ui.proTools.selectionGetInSamples();
                try {
                    //analyzeAndRenderWithLMCorrect2();
                    sf.ui.proTools.clipDoForEachSelectedClip({
                        action: analyzeAndRenderWithLMCorrect2,
                    });
                } finally {
                    sf.ui.proTools.selectionSetInSamples({
                        selectionStart: oldSelection.selectionStart,
                        selectionLength: oldSelection.selectionLength
                    });
                }
            }
        });
        
        
        /*
        sf.ui.proTools.clipDoForEachSelectedClip({
            action: analyzeAndRenderWithLMCorrect2,
        });
        */
        
        1. Hi Stefan

          I saw your post and was gonna comment. The clipDoForEachSelectedClip action works with a variety of half-sufficient information from Pro Tools and tries to patch it together to form a whole. This works reliably for 80-90% of cases but not for all corner cases.
          Building something that works for all corner cases, including all types of fades in and out, cross-fades, with and without margin from fade to clip boundaries, and with or without empty space between clips - is not currently supported by the function. It only works for most cases right now.

          We will hopefully update the action to be more controllable in the future, but for now, simply due to the lack of information from Pro Tools, it has its limits. You could try to replace it with your own logic, that might be easier to get going for the specific use cases you're mostly encountering. But again, it's a rabbit hole to go down trying to build something that works for all types of clips, since there simply is not enough info available yet to always determine what the selection contains.

          1. Stefan Möhl @Stefan_Mohl
              2020-07-13 19:29:09.023Z

              Hi Christian,
              thanks for your feedback.

              I updated the script and added two lines after analyze and render and check if the process window disappeared. This seems to work stable after some testing.
              https://www.dropbox.com/s/ksb9r92yiyo34z7/Batch_LmCorrect2_v2.mov?dl=0

              Only after finishing all processing I still get an error message from Soundflow:
              DoMainCounterAction

              I assume it is as you mentioned the lack of information from ProTools.
              Can I catch this exception in my script.

              Here is the code

              /* 
                  This script renders all clips in a track with NUGEN´s LMCorrect2 with a user preset called "EBU-R128-23LUFS-3TP"
                  Works only on files, cutted clips and group slips without fades
              */
              
              /**Start Nugen */ 
              
              // open NUGEN LMCorrect2
              var asWin = sf.ui.proTools.getAudioSuiteWindow("LMCorrect2");
              if (!asWin.exists) {
                  asWin = sf.ui.proTools.audioSuiteOpenPlugin({
                      category: 'Sound Field',
                      name: 'NUGEN LMCorrect2'
                  }).window;
              }
              
              // wait for Audiosuite plugin to appear
              sf.ui.proTools.firstAudioSuiteWindow.elementWaitFor({
                  waitType: "Appear"
              });
              
              // load preset
              asWin.audioSuiteSelectPreset({
                  presetMenuPath: ["EBU-R128-23LUFS-3TP"]
              });
              
              // set options
              asWin.audioSuiteSetOptions({
                  processingInputMode: "EntireSelection",
                  processingOutputMode: "CreateContinuousFile"
              });
              
              // set input mode    
              sf.ui.proTools.firstAudioSuiteWindow.popupButtons.whoseTitle.is('input mode').first.popupMenuSelect({
                  menuPath: ["multi-input mode"]
              });
              
              // audio suite procees function.
              // Analyze -> Wait until it is done -> Render
              function analyzeAndRenderWithLMCorrect2() {
                 sf.engine.checkForCancellation();
                  
                  asWin.getFirstWithTitle("Analyze").elementClick();
                  sf.ui.proTools.waitForNoModals();
                  sf.ui.proTools.confirmationDialog.elementWaitFor({
                      waitType: "Disappear",
                  });
              
                  
                  asWin.audioSuiteRender();
                  sf.ui.proTools.waitForNoModals();
                  sf.ui.proTools.confirmationDialog.elementWaitFor({
                      waitType: "Disappear",
                  });
                 
              }
              
              
              
              sf.ui.proTools.mainCounterDoWithValue({
                  targetValue: 'Samples',
                  action: () => {
                      var oldSelection = sf.ui.proTools.selectionGetInSamples();
                      try {
                          //analyzeAndRenderWithLMCorrect2();
                          sf.ui.proTools.clipDoForEachSelectedClip({
                              action: analyzeAndRenderWithLMCorrect2,
                          });
                      } finally {
                          sf.ui.proTools.selectionSetInSamples({
                              selectionStart: oldSelection.selectionStart,
                              selectionLength: oldSelection.selectionLength
                          });
                      }
                  }
              });
              
              
              /*
              sf.ui.proTools.clipDoForEachSelectedClip({
                  action: analyzeAndRenderWithLMCorrect2,
              });
              */
              

              Do I need to switch the main counter to samples. I saw that code snippet on another script and copied it to my script or is it ok to use just the last two lines in the comments. In my tests I can see any differences.

              Is it possible to get the number of selected clips in a track with a specific function? If this is possible I could be a loop with the number of clips.
              The function sf.ui.proTools.clipGetSpottingInfo(). gives a lot of information but I can´t find a return value for the amount.

              1. You can add a onError: 'Continue' property to your mainCounterDoWithValue action to swallow the error, if you want.

                1. Is it possible to get the number of selected clips in a track with a specific function? If this is possible I could be a loop with the number of clips.

                  No, not at this time. You're running the best version of this that we have right now.

                  1. Do I need to switch the main counter to samples. I saw that code snippet on another script and copied it to my script or is it ok to use just the last two lines in the comments. In my tests I can see any differences.

                    I don't think you manually need to do this, no. But your code looks great as it is now, so I wouldn't remove it either.
                    Code that ensures stability is generally good.

                    1. In reply tochrscheuer:
                      Stefan Möhl @Stefan_Mohl
                        2020-07-15 05:31:16.187Z

                        Hi Christian,

                        I found a way to get the clip count of a selection

                        // make sure clip list is visible
                        if (sf.ui.proTools.getMenuItem('View','Other Displays','Clip List').isMenuChecked == false) {
                            sf.ui.proTools.menuClick({
                                menuPath: ["View","Other Displays","Clip List"],
                            });
                        }
                        
                        
                        // get clip cout from selection .-) finally
                        var selectedClipRows = sf.ui.proTools.mainWindow.clipListView.getElements('AXSelectedRows');
                        var numClips = Number(selectedClipRows.count)
                        log (numClips);
                        
              2. In reply toStefan_Mohl:
                Stefan Möhl @Stefan_Mohl
                  2020-07-15 05:36:43.484Z

                  I updated the script and it works like charm.
                  https://www.dropbox.com/s/1xxrgoeycb3iw9d/Batch_LmCorrect2_v3.mov?dl=0
                  Mandatory for the script is that I assume we don´t have fades on each clip. It also checks if tab to transinet is off and the clip list is visible. I use it for e.g on final mixes that have to be delivered to -23 LUFS level in Germany.

                  /* Batch converter with NUGEN LMCorrect2
                  
                      DESC:   This script analyzes each clip in a selection on a track and analzes and render each track with NUGEN LMCorrect2 Audiosuite plugin.
                              The clip can be real files,cutted clip or group clips.
                   
                       MANDATORY: 1. No Fades on each clip
                  */
                  
                  sf.ui.proTools.appActivateMainWindow();
                  
                  // place the cursor BEFORE the clips ou like to render
                  /*
                  var strClips = prompt("How many clips do you want to render?", "1");
                  if (!strClips) 
                      throw 0;
                  */
                  /**Start Nugen */ 
                  
                  // open NUGEN LMCorrect2
                  var asWin = sf.ui.proTools.getAudioSuiteWindow("LMCorrect2");
                  if (!asWin.exists) {
                      asWin = sf.ui.proTools.audioSuiteOpenPlugin({
                          category: 'Sound Field',
                          name: 'NUGEN LMCorrect2'
                      }).window;
                  }
                  
                  // wait for Audiosuite plugin to appear
                  sf.ui.proTools.firstAudioSuiteWindow.elementWaitFor({
                      waitType: "Appear"
                  });
                  
                  // load preset
                  asWin.audioSuiteSelectPreset({
                      presetMenuPath: ["EBU-R128-23LUFS-3TP"]
                  });
                  
                  // set options
                  asWin.audioSuiteSetOptions({
                      processingInputMode: "EntireSelection",
                      processingOutputMode: "CreateContinuousFile"
                  });
                  
                  // set input mode    
                  sf.ui.proTools.firstAudioSuiteWindow.popupButtons.whoseTitle.is('input mode').first.popupMenuSelect({
                      menuPath: ["multi-input mode"]
                  });
                  
                  // make sure tabtoTransient is off
                  if (sf.ui.proTools.getMenuItem('Options', 'Tab to Transient').isMenuChecked == true) {
                      sf.ui.proTools.menuClick({
                          menuPath: ["Options", "Tab to Transient"],
                      });
                  }
                  
                  // make sure clip list is visible
                  if (sf.ui.proTools.getMenuItem('View','Other Displays','Clip List').isMenuChecked == false) {
                      sf.ui.proTools.menuClick({
                          menuPath: ["View","Other Displays","Clip List"],
                      });
                  }
                  
                  
                  
                  
                  // get clip cout from selection .-) finally
                  var selectedClipRows = sf.ui.proTools.mainWindow.clipListView.getElements('AXSelectedRows');
                  var numClips = Number(selectedClipRows.count)
                  log (numClips);
                  
                  // save last selection
                  //Set main Counter to samples view for sample accuracy
                  sf.ui.proTools.mainCounterSetValue({
                      targetValue: 'Samples'
                  });
                  var oldSelection = sf.ui.proTools.selectionGetInSamples();
                  
                  for (var i = 0; i < numClips; i++) {
                      sf.keyboard.press({ keys: "ctrl+tab" });
                      analyzeAndRenderWithLMCorrect2();
                  };
                  
                  
                  // audio suite procees function.
                  // Analyze -> Wait until it is done -> Render
                  function analyzeAndRenderWithLMCorrect2() {
                      sf.engine.checkForCancellation();
                      
                      asWin.getFirstWithTitle("Analyze").elementClick();
                      sf.ui.proTools.waitForNoModals();
                      sf.ui.proTools.confirmationDialog.elementWaitFor({
                          waitType: "Disappear",
                      });
                  
                      
                      asWin.audioSuiteRender();
                      sf.ui.proTools.waitForNoModals();
                      sf.ui.proTools.confirmationDialog.elementWaitFor({
                          waitType: "Disappear",
                      });
                  
                     
                  }
                  
                  // restore selection
                  sf.ui.proTools.selectionSetInSamples({
                      selectionStart: oldSelection.selectionStart,
                      selectionLength: oldSelection.selectionLength
                  });
                  
                  //Set main Counter back to Timecode view 
                  sf.ui.proTools.mainCounterSetValue({
                      targetValue: 'Timecode'
                  });
                  
                  
                  
                  
                  ReplySolution
                  1. AWESOME! Thanks for sharing :)

                  2. S
                    In reply toStefan_Mohl:
                    Sergio PinkNoise @Sergio_PinkNoise
                      2020-09-24 11:06:20.778Z

                      Great script, thanks for sharing!

                      Now that Nugen has the option "Copy Source to Target" I´m trying this script but matching Clips on one track to the ones on the other track.
                      Let´s say you´ve got a track with original files (VO) and, on the lower track, you´ve got the dubbed version that need to be matched to the VO.
                      The logic it´s pretty easy: select VO clip> analyze it>Copy souce to target>select the clip below>analyze>Render.
                      Then you could write the code to: go up+select next clip, etc...by using "press key" for commands on PT (ctrl+tab, P, semicolon...) so you could process a batch.

                      The problem with that, is the step for "Copy Source to Target". I´ve tried to get that button by picking it using "Click UI Element" , since the button is inside the plugin interface and has no external button, the message I get is: "sf.ui.proTools.firstAudioSuiteWindow.groups.whoseTitle.is('PluginView').first.elementClick();". So no button is picked (PluginView)
                      I´ve also tried the "mouse position" combined with "mouse click" but same result.

                      Any thoughts?

                      Here´s a video in order to make myself clear :):
                      https://vimeo.com/461351064

                      Thanks!

                      1. SSergio PinkNoise @Sergio_PinkNoise
                          2020-09-25 13:43:34.633Z

                          Here´s a not vey clean solution :):

                          sf.ui.proTools.appActivateMainWindow();

                          var selectedClipRows = sf.ui.proTools.mainWindow.clipListView.getElements('AXSelectedRows');
                          var numClips = Number(selectedClipRows.count)
                          log(numClips);

                          var selectedClipRows = sf.ui.proTools.mainWindow.clipListView.getElements('AXSelectedRows');
                          var numClips = Number(selectedClipRows.count)
                          log(numClips);

                          for (var i = 0; i < numClips; i++) {

                          sf.keyboard.press({
                              keys: "tab,p,ctrl+tab",
                          });
                          
                          sf.ui.proTools.firstAudioSuiteWindow.buttons.whoseTitle.is('Analyze').first.elementClick();
                          
                          sf.ui.proTools.waitForNoModals();
                          
                          sf.mouse.setPosition({
                              position: { "x": 223, "y": 257 },
                          });
                          
                          sf.mouse.click({
                              position: { "x": 223, "y": 257 },
                          });
                          
                          sf.keyboard.press({
                              keys: "alt+tab, semicolon, ctrl+tab",
                          });
                          
                          sf.ui.proTools.firstAudioSuiteWindow.buttons.whoseTitle.is('Analyze').first.elementClick();
                          
                          sf.ui.proTools.waitForNoModals();
                          
                          sf.ui.proTools.firstAudioSuiteWindow.buttons.whoseTitle.is('Render').first.elementClick();