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?
- Christian Scheuer @chrscheuer2020-04-07 17:25:39.977Z
Hi Joe - welcome to the forum!
I'll work on a script for you in a second.
Christian Scheuer @chrscheuer2020-04-07 17:41:27.670Z
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.- Jjk79 @jk79
Wow.. I apologize, I havent actually used your software yet. Can you tell me how I execute the script in your program?
Thank you
Christian Scheuer @chrscheuer2020-04-07 17:50:54.648Z
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.- Jjk79 @jk79
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?
Christian Scheuer @chrscheuer2020-04-07 17:58:47.885Z
You should select the "Default Package" under "Custom Commands" - this is your first package (your first "folder" of scripts).
- Jjk79 @jk79
Sorry, I don't see Default Package, besides the one I selected but still get same message
Christian Scheuer @chrscheuer2020-04-07 18:14:34.207Z
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
-
Create a new Package (click "+ New" and choose "Package") or
-
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- Jjk79 @jk79
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?
- Jjk79 @jk79
Sorry nevermind I like the create individual files / clip by clip works great.
Thank you so much!
Christian Scheuer @chrscheuer2020-04-07 18:54:26.346Z
Awesome to hear Joe! Happy that we could make this work :)
Dustin Harris @Dustin_Harris
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!
Christian Scheuer @chrscheuer2020-04-15 12:50:41.739Z
That's gonna be a big YES to each of your questions - great code reading :)
Dustin Harris @Dustin_Harris
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)
- In reply tochrscheuer⬆:
Dustin Harris @Dustin_Harris
am I correct in thinking that RX Editor needs to be already open for
function getPath()
to return correctly?
Christian Scheuer @chrscheuer2020-04-15 16:26:15.142Z
Yes - I believe what this actually returns is the filename of the first open document in iZotope.
Dustin Harris @Dustin_Harris
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?
Christian Scheuer @chrscheuer2020-04-15 16:52:01.846Z
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-12And this:
https://forum.soundflow.org/-1159/how-to-wait-for-rx7-audio-editor-to-open-before-proceedingfor inspiration
Christian Scheuer @chrscheuer2020-04-15 16:52:56.245Z
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.
-
- In reply tochrscheuer⬆:RRobert Mallory @Robert_Mallory
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!!
- RRobert Mallory @Robert_Mallory
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... ?
- @param {AxElement} moduleWin
- DIn reply tojk79⬆:Davide Favargiotti @dieffe
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
Christian Scheuer @chrscheuer2020-04-17 14:28:40.308Z
Yes that's also a good idea that definitely might speed things up even further :)