Is anyone having trouble with opening a file in iZotope RX using this? It isn't working for me. It was part of a larger script but even done as a single line it isn't working for me.
sf.file.openInIzotope({ path: ""});
Linked from:
- Christian Scheuer @chrscheuer2023-07-20 08:43:44.659Z
Hi Nathan,
I'd recommend using the macros/scripts that go via iZotope RX Connect instead. You'll find them in the free official iZotope RX integration that's available in the Store.
Nathan Salefski @nathansalefski
Hey Christian. I’m actually trying to bounce a file in Pro Tools then resample it in RX so unfortunately I can’t use RX Connect. Do you have any suggestions?
- In reply tonathansalefski⬆:Chad Wahlbrink @Chad2023-07-20 13:12:27.524Z
Hey @Nathan_Salefski,
This is the code I have used to open selected files in Finder in iZotope RX.
let izotopeRx = "/Applications/iZotope RX 10 Audio Editor.app"; let paths = getSelectedPathsInFinder().map(function(p){ return '"' + p + '"'; }).join(" "); function getSelectedPathsInFinder() { let count = sf.appleScript.finder.selection.length; for(let i=0; i<count; i++) { let result = []; result.push(decodeURIComponent(sf.appleScript.finder.selection.getItem(i).asItem.path)); } return result; } sf.system.exec({ commandLine: 'open -a "' + izotopeRx + '" ' + paths + '' });
Christian Scheuer @chrscheuer2023-07-20 14:11:50.220Z
The entire function
getSelectedPathsInFinder
can be replaced bysf.ui.finder.selectedPaths
if I recall correctly :)Christian Scheuer @chrscheuer2023-07-20 14:13:56.736Z
Making it:
const izotopeRxPath = "/Applications/iZotope RX 10 Audio Editor.app"; const quotedPaths = sf.ui.finder.selectedPaths.map(p => `"${p}"`).join(' '); sf.system.exec({ commandLine: `open -a "${izotopeRxPath}" ${quotedPaths}`, });
Chad Wahlbrink @Chad2023-07-20 14:24:43.214Z
Beautiful! That's great.
- In reply tochrscheuer⬆:
Nathan Salefski @nathansalefski
This would work equally if I wanted the path to be predefined correct? This is all a part of a much larger mastering script. It’ll bounce my mixes from Pro Tools, import and resample in RX, then create a new Mastering Session in Pro Tools with the upsampled file
Chad Wahlbrink @Chad2023-07-20 16:11:02.059Z
Hey @nathansalefski,
If you want to incorporate this into a larger script with a designated path. You can use this version as a function and pass the desired path in as a string:
function openFilesInIzotopeRX(directPath) { let path = sf.ui.finder.selectedPaths; const izotopeRxPath = "/Applications/iZotope RX 10 Audio Editor.app"; if (directPath) { path = [`${directPath}`]; } const quotedPaths = path.map(p => `"${p}"`).join(' '); sf.system.exec({ commandLine: `open -a "${izotopeRxPath}" ${quotedPaths}`, }); } // // This will open the selected files // openFilesInIzotopeRX(); // This will open a specific path openFilesInIzotopeRX('/Users/chadwahlbrink/Desktop/AGT-ac2LAWaitlist_ac2.wav');
If you don't give this function a path, it will default to opening the selected files in Finder ✨
Nathan Salefski @nathansalefski
This is amazing. Works so quickly! Can I use "const quotedPaths = path.map(p =>
"${p}"
).join(' ');" in other instances to get the finders highlighted path?Chad Wahlbrink @Chad2023-07-20 16:55:59.203Z
You'd likely only need
sf.ui.finder.selectedPaths
like @chrscheuer suggested above. This gives you an array of the selected paths in Finder.
const quotedPaths = path.map(p =>
"${p}").join(' ');
is only necessary for opening those paths from the Command Line/Terminal as we are doing here.
The command line wants each path to be encapsulated in quotations, with one space between each path.
So allconst quotedPaths = path.map(p =>
"${p}").join(' ');
is doing is making it so we can call this command:
sf.system.exec({ commandLine: `open -a "/Applications/iZotope RX 10 Audio Editor.app" "Path1" "Path2" "Path3"`, });
- In reply toChad⬆:
Nathan Salefski @nathansalefski
spoke a little bit too soon. It was working by itself but as a part of a larger script, it isn't working. It's not failing it's just simply not performing the action
Chad Wahlbrink @Chad2023-07-20 17:11:15.260Z2023-07-20 17:23:12.715Z
Can you give me an example of the "path" you are passing to the function in your larger script?
it's important that whatever path you pass into the function is formatted as a String (specifically, in quotation marks) and that the path ends in either ".wav" or is pointing to a folder.
For instance, these are valid paths for the function:
// Single Audio File openFilesInIzotopeRX('/Users/chadwahlbrink/Desktop/AGT-ac2LAWaitlist_ac2.wav') // Folder of audio openFilesInIzotopeRX('/Users/chadwahlbrink/Desktop/audio/')
Nathan Salefski @nathansalefski
The large script is extremely involved so I'm trying to make this as concise as possible. The RX function is the 6th function so assume some of these pathways are created before it's running.
const sessionPath = sf.ui.proTools.mainWindow.sessionPath; let sessionName = sessionPath.split("/").slice(-1)[0].split(".ptx")[0]; let sessionDir = sessionPath.split('/').slice(0, -1).join('/'); var bounceDir = sessionDir + '/Bounced Files' + '/Premaster' let regex = /^v(\d+)[\s_](.+)$/i; let match = sessionName.match(regex); let versionNumber let songtitle if (match) { versionNumber = match[1]; songtitle = match[2]; } else { versionNumber = 1; songtitle = sessionName; }; let bounceName = `${songtitle}_NS_Mix_${versionNumber}_Premaster`; const newSessionName = `${songtitle}_NS_Mix_${versionNumber}_NS_Master`; const rxFilePath = `${bounceDir}/${bounceName}.wav` function openFilesInIzotopeRX(directPath) { let path = sf.ui.finder.selectedPaths; const izotopeRxPath = "/Applications/iZotope RX 10 Audio Editor.app"; if (directPath) { path = [`${directPath}`]; } const quotedPaths = path.map(p => `"${p}"`).join(' '); sf.system.exec({ commandLine: `open -a "${izotopeRxPath}" ${quotedPaths}`, }); } // // This will open the selected files // openFilesInIzotopeRX(); // This will open a specific path openFilesInIzotopeRX(`${rxFilePath}`);
Chad Wahlbrink @Chad2023-07-20 17:34:43.560Z
I think the issue is in the naming elsewhere in this code. Can you add a
log(rxFilePath);
just before you call theopenFilesInIzotopeRX(
${rxFilePath})
function on line 47? Then look and see if that logged path matches the bounced file.Chad Wahlbrink @Chad2023-07-20 17:43:27.487Z
Yeah, I think there is an issue with the regex bit. If you can give an example session name, bounce name, etc. I may be able to help you more.
I just tried naming a session similar to how this flows, and the loggedrxFilePath
was always.../Bounced Files/Premaster/songTitle_NS_Mix_1_Premaster.wav/
, so it'd only open a version 1 mix if that existed in your Premaster folderNathan Salefski @nathansalefski
Yea that's correct. This is a script I use after the mix is approved to master the song. Usually I bounce a Mix without my Mix Bus, upsample the Mix in RX then put it into a Mastering session. This is a single press per Mix type of script and only if I'm Mastering myself as opposed to sending it off for Mastering, but I'd love for it to work lol. I can send the entire version that works perfectly minus the RX part if that could help decipher the issue. This will likely fail somewhere around importing audio to the next session because there's an extra window when converting files as opposed to copying
:
sf.ui.proTools.appActivate(); sf.ui.proTools.mainWindow.invalidate(); //////////////////////////////////////////////////////////////////////////////////////////////////// const sessionPath = sf.ui.proTools.mainWindow.sessionPath; let sessionName = sessionPath.split("/").slice(-1)[0].split(".ptx")[0]; let sessionDir = sessionPath.split('/').slice(0, -1).join('/'); var bounceDir = sessionDir + '/Bounced Files' + '/Premaster' let regex = /^v(\d+)[\s_](.+)$/i; let match = sessionName.match(regex); let versionNumber let songtitle if (match) { versionNumber = match[1]; songtitle = match[2]; } else { versionNumber = 1; songtitle = sessionName; }; let bounceName = `${songtitle}_NS_Mix_${versionNumber}_Premaster`; const newSessionName = `${songtitle}_NS_Master`; const rxFilePath = `${bounceDir}/${bounceName}.wav` //////////////////////////////////////////////////////////////////////////////////////////////////// function getSessionSampleRate() { //Refresh Cache sf.ui.proTools.mainWindow.invalidate(); if (!sf.ui.proTools.windows.whoseTitle.is('Session Setup').first.exists) { sf.ui.proTools.menuClick({ menuPath: ["Setup", "Session"], }); } const win = sf.ui.proTools.windows.whoseTitle.is('Session Setup').first; const sessionFormatPanel = win.groups.whoseTitle.is('Session Format').first; const sampleRateString = sessionFormatPanel.children.whoseRole.is("AXStaticText").allItems[2].value.invalidate().value; sf.ui.proTools.menuClick({ menuPath: ["Setup", "Session"], }); const sampleRate = Number(sampleRateString.split('kHz')[0]); return sampleRate; } const sampleRate = getSessionSampleRate(); const masterSampleRate = sampleRate * 2 //////////////////////////////////////////////////////////////////////////////////////////////////// function bouncePremaster() { function bounceMix({ name }) { //Refresh Cache sf.ui.proTools.mainWindow.invalidate(); //Open Bounce window sf.ui.proTools.menuClick({ menuPath: ["File", "Bounce Mix..."], }); // Make reference to Bounce window const bounceWindow = sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first; //Wait for Bounce window bounceWindow.elementWaitFor(); //Enter session name bounceWindow.textFields.first.elementSetTextFieldWithAreaValue({ value: name, }); } //////////////////////////////////////////////////////////////////////////////////////////////// function bounceInfo() { // Make reference to Bounce window const bounceWindow = sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first; //Select Stereo Bus Output bounceWindow.popupButtons.allItems[1].popupMenuSelect({menuPath: ["bus","STEREO BUS (Stereo)"],}); //Set Bit Depth sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first.groups.whoseTitle.is("Audio").first.popupButtons.allItems[2].popupMenuSelect({ menuPath: ["32 Bit Float"], }); //Set Sample Rate sf.ui.proTools.windows.whoseTitle.is("Bounce Mix").first.groups.whoseTitle.is("Audio").first.popupButtons.allItems[3].popupMenuSelect({ menuPath: [`${sampleRate} kHz`], }); //Enable Session Folder as Location bounceWindow.groups.whoseTitle.is("Location").first.radioButtons.whoseTitle.is("Session Folder:").first.checkboxSet({ targetValue: "Enable", }); //Set Bounce Location bounceWindow.groups.whoseTitle.is("Location").first.textFields.first.elementSetTextFieldWithAreaValue({ value: "Bounced Files/Premasters", }); } //////////////////////////////////////////////////////////////////////////////////////////////// function bounceAndSave () { const bounceWindow = sf.ui.proTools.windows.whoseTitle.is('Bounce Mix').first; //Click OK bounceWindow.buttons.whoseTitle.is("Bounce").first.elementClick(); //Wait sf.ui.proTools.waitForNoModals(); //Refresh Cache sf.ui.proTools.mainWindow.invalidate(); //Save the Session sf.ui.proTools.menuClick({menuPath: ["File","Save"],}); //Close the Session sf.ui.proTools.menuClick({menuPath: ["File","Close Session"],}); } bounceMix({ name: bounceName }); bounceInfo(); bounceAndSave(); } //////////////////////////////////////////////////////////////////////////////////////////////////// function upSampleInRX(){ //Function to Open Files in RX function openFilesInIzotopeRX(directPath) { let path = sf.ui.finder.selectedPaths; const izotopeRxPath = "/Applications/iZotope RX 10 Audio Editor.app"; if (directPath) { path = [`${directPath}`]; } const quotedPaths = path.map(p => `"${p}"`).join(' '); sf.system.exec({ commandLine: `open -a "${izotopeRxPath}" ${quotedPaths}`, }); } // This will open a specific path openFilesInIzotopeRX(`${rxFilePath}`); //ADD MORE CODE TO UPSAMPLE AND OVERWRITE PREVIOUS FILE } //////////////////////////////////////////////////////////////////////////////////////////////////// function newSession() { //////////////////////////////////////////////////////////////////////////////////////////////// function newSessionSetup() { //Activate Pro Tools sf.ui.proTools.appActivate(); //Refresh Cache sf.ui.proTools.mainWindow.invalidate(); //Create New Session sf.ui.proTools.menuClick({menuPath: ["File","Create New..."],}); //Reference Dashboard const newSessionWin = sf.ui.proTools.windows.whoseTitle.is("Dashboard").first; //Wait for Dashboard newSessionWin.elementWaitFor(); //Enable Create From Template newSessionWin.checkBoxes.whoseTitle.is("Create From Template").first.checkboxSet({ targetValue: "Enable", }); //Select My Templates newSessionWin.popupButtons.first.popupMenuSelect({ menuPath: ["NS MASTERING"], }); //Set Sample Rate sf.ui.proTools.windows.whoseTitle.is("Dashboard").first.popupButtons.allItems[2].popupMenuSelect({ menuPath: [`${masterSampleRate} kHz`], }); //Select Mastering I/O newSessionWin.popupButtons.allItems[4].popupMenuSelect({ menuPath: ["NS MASTERING"], }); //Enter Session Name newSessionWin.textFields.whoseTitle.is('').first.elementSetTextFieldWithAreaValue({ value: newSessionName, }); //Click Create newSessionWin.buttons.whoseTitle.is("Create").first.elementClick(); }; //////////////////////////////////////////////////////////////////////////////////////////////// function newSessionLoc(){ //Refresh Cache sf.ui.proTools.mainWindow.invalidate(); //Define Save Window const saveWin = sf.ui.proTools.windows.whoseTitle.is("Save").first; //Wait for Save Window saveWin.elementWaitFor({waitType: "Appear"}); //Open 'Go' Sheet sf.keyboard.type({ text: '/' }); //Wait for Sheet Window to Appear saveWin.sheets.first.elementWaitFor({waitType: "Appear"}); //Set Destination saveWin.sheets.first.textFields.first.elementSetTextAreaValue({ value: `${sessionDir}`, }); //Press Return sf.keyboard.press({ keys: "return"}); //Wait sf.wait({intervalMs: 250}); //Press Return sf.keyboard.press({ keys: "return"}); //Wait sf.wait({intervalMs: 250}); }; //////////////////////////////////////////////////////////////////////////////////////////////// newSessionSetup(); newSessionLoc(); } //////////////////////////////////////////////////////////////////////////////////////////////////// function importAudio(){ //Refresh Cache sf.ui.proTools.mainWindow.invalidate(); //File Import Audio sf.ui.proTools.menuClick({ menuPath: ["File","Import","Audio..."], }); //Define Import Audio Window const openWin = sf.ui.proTools.windows.whoseTitle.is('Open').first; //Wait for 'Open' Window openWin.elementWaitFor({waitType: "Appear"}); //Open 'Go' Sheet sf.keyboard.type({ text: '/' }); //Wait for 'Go' Sheet Window to Appear openWin.sheets.first.elementWaitFor({waitType: "Appear"}); //Set Destination openWin.sheets.first.textFields.first.elementSetTextAreaValue({ value: `${bounceDir}`, }); //Press Return sf.keyboard.press({ keys: "return"}); //Wait for 'Go' Sheet Window to Disappear openWin.sheets.first.elementWaitFor({waitType: "Disappear"}); //Select File sf.keyboard.press({ keys: "cmd+a"}); //Click "Open" openWin.buttons.whoseTitle.is('Open').first.elementClick(); //Reference to Audio Import Options Window const aIOWin = sf.ui.proTools.windows.whoseTitle.is("Audio Import Options").first //Wait for 'Go' Sheet Window to Appear aIOWin.elementWaitFor({waitType: "Appear"}); //Click "OK" to New Track aIOWin.buttons.whoseTitle.is("OK").first.elementClick(); //Wait sf.ui.proTools.waitForNoModals(); } //////////////////////////////////////////////////////////////////////////////////////////////////// function importSessionData() { //Refresh Cache sf.ui.proTools.mainWindow.invalidate(); //Import Session Data sf.ui.proTools.menuClick({ menuPath: ["File","Import","Session Data..."], }); //Define File Window const fileWin = sf.ui.proTools.windows.whoseTitle.is("Open").first; //Wait for 'Open' Window fileWin.elementWaitFor({waitType: "Appear"}); //Open 'Go' Sheet sf.keyboard.type({ text: '/' }); //Wait for 'Go' Sheet Window to Appear fileWin.sheets.first.elementWaitFor({waitType: "Appear"}); //Set Destination fileWin.sheets.first.textFields.first.elementSetTextAreaValue({ value: `${sessionPath}`, }); //Press Return sf.keyboard.press({ keys: "return",}); //Wait sf.wait({intervalMs: 250}); //Click "Open" fileWin.buttons.whoseTitle.is("Open").first.elementClick(); // Make Reference to Import Session Data Window const iSDWin = sf.ui.proTools.windows.whoseTitle.is("Import Session Data").first //Wait for Import Window iSDWin.elementWaitFor({waitType: "Appear"}); //Import Stereo Bus to Stereo Bus iSDWin.groups.whoseTitle.is("Tracks").first.tables.whoseTitle.is("Source Destination").first.children.whoseRole.is("AXRow").allItems[2].children.whoseRole.is("AXCell").allItems[1].children.whoseRole.is("AXMenuButton").whoseTitle.is("(none)").first.popupMenuSelect({ menuPath: ["STEREO BUS"], }); // Click "Choose..." iSDWin.groups.whoseTitle.is("Session Data").first.buttons.whoseTitle.is("Choose...").first.elementClick(); //Reference Track Data to Import Window const trackData = sf.ui.proTools.windows.whoseTitle.is("Track Data to Import").first //Wait for Track Data Window to Appear trackData.elementWaitFor({waitType: "Appear"}); //Select Import Template from Dropdown Menu trackData.children.whoseRole.is("AXMenuButton").whoseTitle.is("Librarian menu").first.popupMenuSelect({ menuPath: ["Import Plugins"], }); //Click "OK" in Track Data to Import Window trackData.buttons.whoseTitle.is("OK").first.elementClick(); //Wait for Track Data Window to Disappear trackData.elementWaitFor({waitType: "Disappear"}); //Click "OK" in Import Session Data Window iSDWin.buttons.whoseTitle.is("OK").first.elementClick(); //Wait sf.ui.proTools.waitForNoModals(); } //////////////////////////////////////////////////////////////////////////////////////////////////// function routeAudio(){ //Refresh Cache sf.ui.proTools.mainWindow.invalidate(); //Select Track by Name sf.ui.proTools.trackSelectByName({ names: [`${bounceName}`], }); //Change Color to Red sf.ui.proTools.colorsSelect({ colorTarget: "Tracks", colorNumber: 8, }); //Route to Stereo Bus sf.ui.proTools.selectedTrack.trackOutputSelect({ outputPath: ["bus","STEREO BUS (Stereo)"], //selectForAllSelectedTracks: true, }); //Wait sf.ui.proTools.waitForNoModals(); } //////////////////////////////////////////////////////////////////////////////////////////////////// function main() { sf.ui.proTools.appActivate(); sf.ui.proTools.mainWindow.invalidate(); bouncePremaster(); upSampleInRX(); newSession(); importAudio(); importSessionData(); routeAudio(); } main();
Chad Wahlbrink @Chad2023-07-20 18:04:57.881Z
I think the only info I need to figure out why the Regex is breaking is an example of your session naming convention for Pro Tools. If I have that, I can figure out the rest. You don’t have to provide an actual song title, but something “SongTitle_3.ptx,” or however you like to structure it.
An example of how the bounce is named in the “Premaster” folder may actually speed things up too.
- In reply tonathansalefski⬆:
Chad Wahlbrink @Chad2023-07-20 18:13:54.919Z
Thanks for sharing the script. Without knowing what the script is pointed at as far as a naming scheme goes, there’s not much that can be done. Right now, the Regex isn’t parsing the file name correctly on anything over version 1, so having examples of file naming is the only way to help.
Nathan Salefski @nathansalefski
No that makes perfect sense.
SongTitle.ptx. Each revision is named v# SongTitle.ptx. I have a bounce script that Kitch helped me with that uses the regex to automatically name my bounces and make sure my initials are in there as you can see the bounces will end up being something like SongTitle_NS_Mix_#. When I want to Master my own mixes, it will be bounced once as SongTitle_NS_Mix_#_Premaster and put into a session called SongTitle_NS_Master
Chad Wahlbrink @Chad2023-07-20 18:28:08.869Z
And the session name is “ v#ArtistName_SongTitle.ptx”
like “v3Coldplay_Yellow.ptx” not “ v3_Coldplay_Yellow.ptx”?Nathan Salefski @nathansalefski
“v3 Coldplay_Yellow.ptx”* sorry. Like This:
Nathan Salefski @nathansalefski
Nathan Salefski @nathansalefski
... @chadwahlbrink I found my problem! I missed a single "s" in my giant script. Line 8, it's supposed to be '/Premasters' not '/Premaster'
- In reply tonathansalefski⬆:
Chad Wahlbrink @Chad2023-07-20 19:06:46.149Z
@nathansalefski, Kitch's regex is correct! 👏
In Line 95 of the full script, change:
to//Set Bounce Location bounceWindow.groups.whoseTitle.is("Location").first.textFields.first.elementSetTextFieldWithAreaValue({ value: "Bounced Files/Premasters", });
//Set Bounce Location bounceWindow.groups.whoseTitle.is("Location").first.textFields.first.elementSetTextFieldWithAreaValue({ value: "Bounced Files/Premaster", });
All of your other paths are point towards
Premaster
instead ofPremasters
. Then you'll be good to go 🤘Chad Wahlbrink @Chad2023-07-20 19:07:24.201Z
Ah, yes~! There you go!
Nathan Salefski @nathansalefski
lol SUCH A SIMPLE MISTAKE. Gotta love it. Thank you for all the help!! This will be incredibly helpful