No internet connection
  1. Home
  2. How to

Split Into Mono Script Failing

By Philip weinrobe @Philip_weinrobe
    2024-12-12 18:18:01.492Z

    Hi Genius Folks
    I have this handy little script that I put together from folks here that splits a stereo track into mono and keeps only one side and cleans it all up nicely but it now fails due to some pro tools/soundflow updates that i can't quite track down.

    can anyone please take a look at this and bring it into the modern world?
    it is very very useful!

    sf.ui.proTools.appActivate();
    sf.ui.proTools.invalidate();
    
    // Place the following two functions at the top of your script:
    
    // #1
    function mainWindowStatus() {
        if (sf.ui.proTools.getMenuItem('Window', 'Mix').isMenuChecked) {
            sf.ui.proTools.menuClick({
                    menuPath: ["Window", "Edit"],
                });
            return "Mix";
        } else {
            return "Edit";
        }
    }
    
    //#2
    function returnToStartingMainWIndow(mainWindow) {
        if (mainWindow =="Mix") {
                sf.ui.proTools.menuClick({
                    menuPath: ["Window", "Mix"],
                });
        }
    }
    
    
    /// Place this line at the top of your script
    let startingWindow = mainWindowStatus();
    
    
    
    //Activate Pro Tools
    sf.ui.proTools.appActivateMainWindow();
    sf.ui.proTools.invalidate();
    
    //Do batch rename all selected tracks
    sf.ui.proTools.appActivateMainWindow();
    sf.ui.proTools.selectedTrack.titleButton.popupMenuSelect({
        isRightClick: true,
        menuPath: ['Batch Rename...']
    });
    
    
    //Batch rename setting 'add "_ORIGINAL!"'
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.checkBoxes.whoseTitle.is('Replace').first.checkboxSet({
        targetValue: "Disable",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.checkBoxes.whoseTitle.is('Trim').first.checkboxSet({
        targetValue: "Disable",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.checkBoxes.whoseTitle.is('Add').first.checkboxSet({
        targetValue: "Enable",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.checkBoxes.whoseTitle.is('Prefix:').first.checkboxSet({
        targetValue: "Disable",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.checkBoxes.whoseTitle.is('Insert:').first.checkboxSet({
        targetValue: "Disable",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.checkBoxes.whoseTitle.is('Suffix:').first.checkboxSet({
        targetValue: "Enable",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.checkBoxes.whoseTitle.is('Numbering').first.checkboxSet({
        targetValue: "Disable",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.textFields.whoseTitle.is('').allItems[9].elementSetTextFieldWithAreaValue({
        value: "_ORIGINAL!",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.buttons.whoseTitle.is('OK').first.elementClick();
    
    
    //split into mono all selected tracks
    sf.ui.proTools.menuClick({
        menuPath: ["Track","Split into Mono"],
    });
    
    
    sf.ui.proTools.waitForNoModals();
    
    sf.wait({
        intervalMs: 50,
    });
    
    
    sf.ui.proTools.trackDeselectAll();
    
    
    sf.wait({
        intervalMs: 50,
    });
    
    //select named "_ORIGINAL!.R"
    var trackNamesR = sf.ui.proTools.trackNames.filter(n => n.match(/(_ORIGINAL!.R)/i));
    
    sf.ui.proTools.trackSelectByName({
        names: trackNamesR,
        deselectOthers: true,
    });
    
    
    //Delete it
    sf.ui.proTools.trackDelete();
    
    
    sf.ui.proTools.windows.whoseTitle.is('').first.buttons.whoseTitle.is('Delete').first.elementClick();
    
    
    sf.ui.proTools.waitForNoModals();
    
    sf.wait({
        intervalMs: 50,
    });
    
    
    sf.ui.proTools.trackDeselectAll();
    
    
    sf.wait({
        intervalMs: 50,
    });
    
    
    
    //select named "_ORIGINAL!.L"
    var trackNamesL = sf.ui.proTools.trackNames.filter(n => n.match(/(_ORIGINAL!.L)/i));
    
    sf.ui.proTools.trackSelectByName({
        names: trackNamesL,
        deselectOthers: true,
    });
    
    
    
    //Do Batch rename all selected tracks
    sf.keyboard.press({
        keys: "alt+shift+r",
    });
    
    
    
    //Batch rename setting 'delete "_ORIGINAL!.L"'
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.checkBoxes.whoseTitle.is('Replace').first.checkboxSet({
        targetValue: "Enable",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.checkBoxes.whoseTitle.is('Clear Existing Name').first.checkboxSet({
        targetValue: "Disable",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.checkBoxes.whoseTitle.is('Regular Expressions').first.checkboxSet({
        targetValue: "Disable",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.checkBoxes.whoseTitle.is('Trim').first.checkboxSet({
        targetValue: "Disable",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.checkBoxes.whoseTitle.is('Add').first.checkboxSet({
        targetValue: "Disable",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.checkBoxes.whoseTitle.is('Numbering').first.checkboxSet({
        targetValue: "Disable",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.textFields.whoseTitle.is('').first.elementSetTextFieldWithAreaValue({
        value: "_ORIGINAL!.L",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.textFields.whoseTitle.is('').allItems[1].elementSetTextFieldWithAreaValue({
        value: "",
    });
    
    sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first.buttons.whoseTitle.is('OK').first.elementClick();
    
    
    sf.ui.proTools.waitForNoModals();
    
    sf.wait({
        intervalMs: 50,
    });
    
    
    sf.ui.proTools.trackDeselectAll();
    
    
    sf.wait({
        intervalMs: 50,
    });
    
    
    //select original stereo tracks
    var trackNamesOriginal = sf.ui.proTools.visibleTrackNames.filter(n => n.match(/(_ORIGINAL!)/i));
    sf.ui.proTools.trackSelectByName({
        names: trackNamesOriginal,
        deselectOthers: true,
    });
    
    //Hide and Inactive it
    if (trackNamesOriginal.length > 0){
        sf.ui.proTools.trackHideAndMakeInactiveSelected();
    }
    
    
    
    // end your script with this line
    returnToStartingMainWIndow(startingWindow);
    
    • 2 replies
    1. Kitch Membery @Kitch2024-12-17 23:26:15.793Z2024-12-17 23:37:56.868Z

      Hi @Philip_weinrobe,

      Take this for a spin legend!

      sf.ui.proTools.appActivate();
      sf.ui.proTools.invalidate();
      sf.app.proTools.invalidate();
      
      function doWithEditWindowFocused(action) {
          const frontMostProToolsWindow = sf.ui.proTools.windows.first;
          const frontMostProToolsWindowTitle = frontMostProToolsWindow.title.invalidate().value.split(":")[0];
      
          if (frontMostProToolsWindowTitle !== "Edit") {
              sf.ui.proTools.menuClick({ menuPath: ["Window", "Edit"] });
          }
          try {
              action();
          } catch (err) {
              throw err;
          } finally {
              // Restore Window
              frontMostProToolsWindow.elementRaise();
          }
      }
      
      /**
       * @param {Object} obj
       * @param {string} obj.dialogText
       * @param {string} obj.buttonName
       */
      function dismissDialog({ dialogText, buttonName }) {
          try {
      
              const dlg = sf.ui.proTools.confirmationDialog;
      
              dlg.elementWaitFor({ timeout: 200, onError: "Continue" });
      
              if (dlg.children.whoseRole.is("AXStaticText").whoseValue.contains(dialogText).first.exists) {
                  dlg.buttons.whoseTitle.is(buttonName).first.elementClick();
      
                  dlg.elementWaitFor({ waitForNoElement: true });
              }
          } catch (err) {
              throw `There was an error Dismissing the dialog.\n${err}`;
          }
      }
      
      function splitTracksToMono() {
          try {
              sf.ui.proTools.menuClick({ menuPath: ["Track", "Split into Mono"], });
      
              sf.ui.proTools.waitForNoModals();
      
          } catch (err) {
              throw `Failed to split tracks into mono.\n${err}`;
          }
      }
      
      function batchRenameOriginalTracks() {
          try {
              sf.ui.proTools.appActivateMainWindow();
      
              sf.ui.proTools.selectedTrack.titleButton.popupMenuSelect({
                  isRightClick: true,
                  menuPath: ['Batch Rename...']
              });
      
              const renameWindow = sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first;
      
              renameWindow.elementWaitFor();
      
              const setCheckboxByName = ({ name, targetValue }) => renameWindow.checkBoxes.whoseTitle.is(name).first.checkboxSet({ targetValue });
      
              //Batch rename setting 'add "_ORIGINAL!"'
              setCheckboxByName({ name: "Replace", targetValue: "Disable", });
              setCheckboxByName({ name: "Trim", targetValue: "Disable", });
              setCheckboxByName({ name: "Add", targetValue: "Enable", });
              setCheckboxByName({ name: "Prefix:", targetValue: "Disable", });
              setCheckboxByName({ name: "Insert:", targetValue: "Disable", });
              setCheckboxByName({ name: "Suffix:", targetValue: "Enable", });
              setCheckboxByName({ name: 'Numbering', targetValue: "Disable", });
      
              const suffixTextField = renameWindow.textFields.whoseTitle.is('').allItems[9]
      
              suffixTextField.elementSetTextFieldWithAreaValue({ value: "_ORIGINAL!", });
      
              // Click OK
              renameWindow.buttons.whoseTitle.is('OK').first.elementClick();
          } catch (err) {
              throw `Failed batch renaming original tracks.\n${err}`;
          }
      }
      
      function deleteRightChannelTracks() {
          try {
              const rightChannelTrackNames = sf.app.proTools.tracks.allItems
                  .filter(track => track.name.match(/(_ORIGINAL!.R)/i))
                  .map(track => track.name);
      
              // Select tracks with named "_ORIGINAL!.R"
              sf.app.proTools.selectTracksByName({ trackNames: rightChannelTrackNames });
      
              // Invalidate the main window as tracks have changed
              sf.ui.proTools.mainWindow.invalidate();
      
              // Delete Right Channel Tracks
              sf.ui.proTools.trackDelete();
      
              dismissDialog({
                  dialogText: "Do you still want to delete these tracks?",
                  buttonName: "Delete"
              });
      
              sf.ui.proTools.waitForNoModals();
          } catch (err) {
              throw `Failed to delete right channel tracks.\n${err}`;
          }
      }
      
      function batchRemoveSuffix() {
          try {
              const rightChannelTrackNames = sf.app.proTools.tracks.allItems
                  .filter(track => track.name.match(/(_ORIGINAL!.L)/i))
                  .map(track => track.name);
      
              // Select Left Channel Tracks
              sf.app.proTools.selectTracksByName({ trackNames: rightChannelTrackNames });
      
              sf.ui.proTools.appActivateMainWindow();
      
              const renameWindow = sf.ui.proTools.windows.whoseTitle.is('Batch Track Rename').first;
      
              sf.ui.proTools.selectedTrack.titleButton.popupMenuSelect({
                  isRightClick: true,
                  menuPath: ['Batch Rename...']
              });
      
              renameWindow.elementWaitFor();
              
              const setCheckboxByName = ({ name, targetValue }) => renameWindow.checkBoxes.whoseTitle.is(name).first.checkboxSet({ targetValue });
      
              //Batch rename setting 'delete "_ORIGINAL!.L"'
              setCheckboxByName({ name: 'Replace', targetValue: "Enable", });
              setCheckboxByName({ name: 'Clear Existing Name', targetValue: "Disable", });
              setCheckboxByName({ name: 'Regular Expressions', targetValue: "Disable", });
              setCheckboxByName({ name: 'Trim', targetValue: "Disable", });
              setCheckboxByName({ name: 'Add', targetValue: "Disable", });
              setCheckboxByName({ name: 'Numbering', targetValue: "Disable", });
      
              // Set Find field Text
              const findTextField = renameWindow.textFields.whoseTitle.is('').allItems[0]
              findTextField.elementSetTextFieldWithAreaValue({ value: "_ORIGINAL!.L", });
      
              // Clear Replace field text
              const replaceTextField = renameWindow.textFields.whoseTitle.is('').allItems[1]
              replaceTextField.elementSetTextFieldWithAreaValue({ value: "", });
      
              // Click OK
              renameWindow.buttons.whoseTitle.is('OK').first.elementClick();
      
              sf.ui.proTools.waitForNoModals();
          } catch (err) {
              throw `Failed to remove "_ORIGINAL!.L" suffix.\n${err}`;
          }
      }
      
      function selectAndHideOriginalTracks() {
          try {
              const originalTrackNames = sf.app.proTools.tracks.allItems
                  .filter(track => track.name.match(/(_ORIGINAL!)/i))
                  .map(track => track.name);
      
              sf.app.proTools.selectTracksByName({ trackNames: originalTrackNames });
      
              //Hide and Inactive it
              sf.app.proTools.setTrackHiddenState({
                  trackNames: originalTrackNames,
                  enabled: true,
                  onError: "Continue",
              });
      
          } catch (err) {
              throw `Failed selecting and hiding tracks.\n${err}`;
          }
      }
      
      function main() {
          doWithEditWindowFocused(() => {
              batchRenameOriginalTracks();
      
              splitTracksToMono();
      
              deleteRightChannelTracks();
      
              batchRemoveSuffix();
      
              selectAndHideOriginalTracks();
          });
      }
      
      main();
      

      Note: I've not done much testing on this one, so proceed with caution :-)

      [UPDATED]

      1. PPhilip weinrobe @Philip_weinrobe
          2025-07-16 15:39:33.234Z

          just getting around to this and it's failing at the delete window.

          << Command: _UTIL stereo to mono LEFT [user:default:cl0fw35j700049d10ujgy3vxl]
          
          16.07.2025 11:38:24.27 <info> [EditorWindow:Renderer]: Active Focus Container:  code Line 33963 file:///Applications/SoundFlow.app/Contents/Helpers/SoundFlow.app/Contents/Resources/app.asar/dist/editor.js
          16.07.2025 11:38:29.81 <info> [Backend]: [KeyboardInterfaceController] #Key: command+shift+backspace  (51) -> Delete Track(s) [ckyg7d7fh000l8w10n3dw59bn]
          
          16.07.2025 11:38:29.81 <info> [Backend]: >> Command: Delete Track(s) [user:default:ckx2amtqg00001710vnn369xb]
          Executing SelectedTrackDeleteAction
          
          16.07.2025 11:38:29.82 <info> [Backend]: Logging error in action (01) ClickMenuAction: Could not find menu item: Track->Delete
          
          16.07.2025 11:38:29.82 <info> [Backend]: << Command: Delete Track(s) [user:default:ckx2amtqg00001710vnn369xb]
          
          16.07.2025 11:38:32.08 <info> [EditorWindow:Renderer]: Active Focus Container:  code Line 33963 file:///Applications/SoundFlow.app/Contents/Helpers/SoundFlow.app/Contents/Resources/app.asar/dist/editor.js
          16.07.2025 11:38:56.24 <info> [EditorWindow:Renderer]: Active Focus Container:  code Line 33963 file:///Applications/SoundFlow.app/Contents/Helpers/SoundFlow.app/Contents/Resources/app.asar/dist/editor.js
          16.07.2025 11:39:06.70 <info> [Backend]: [SF_FIREBASE_WS]: Sending keep-alive