Clip EQ with Knobs (Eucon or Midi or Streamdeck +)
My kingdom for Clip EQ on Knobs of some sort.... Anybody have even a really Janky version of this?
- OOwen Granich-Young @Owen_Granich_Young
Hell I'll just take HP and LP frequency...
Chad Wahlbrink @Chad2023-09-18 22:15:42.290Z
Hey @Owen_Granich_Young!
Here's an idea to possibly run with. I was able to create a pretty "simple" filter control using Generic Knob Commands and the Stream Deck Plus.
The Scripts are as follows:
Decrementlet clipEffectsPanel = sf.ui.proTools.windows.whoseTitle.startsWith('Edit: ').first.children.whoseTitle.is('Clip Effects'); clipEffectsPanel.first.children.whoseTitle.is('Filter 1 Frequency').first.elementClick({actionName:"AXDecrement"});
Incrementlet clipEffectsPanel = sf.ui.proTools.windows.whoseTitle.startsWith('Edit: ').first.children.whoseTitle.is('Clip Effects'); clipEffectsPanel.first.children.whoseTitle.is('Filter 1 Frequency').first.elementClick({actionName:"AXIncrement"});
Toggle In/Outlet clipEffectsPanel = sf.ui.proTools.windows.whoseTitle.startsWith('Edit: ').first.children.whoseTitle.is('Clip Effects'); if(clipEffectsPanel.first.children.whoseTitle.is('Filter 1 In/Out').first.value.intValue == 0){ clipEffectsPanel.first.children.whoseTitle.is('Filter 1 In/Out').first.elementClick({actionName:"AXIncrement"}); } else{ clipEffectsPanel.first.children.whoseTitle.is('Filter 1 In/Out').first.elementClick({actionName:"AXDecrement"}); }
If you make 3 separate scripts, map them to a generic knob command, you will be off to the races.
- OOwen Granich-Young @Owen_Granich_Young
Sweet bud I'll take a look at your build! I bet it's better than mine is working. I've been building with relative ui click and key press down and up
- In reply toChad⬆:OOwen Granich-Young @Owen_Granich_Young
Hmm is there a way to multiply the speed? so like AXIncrement*2? Here's a quick video of yours on HP and mine on Low Pass Wondering if I can use the more robust one that is yours but with the speed of my down arrow relative click?
- OOwen Granich-Young @Owen_Granich_Young
Reading around the Forum seems the only solution is to just trigger it twice... still feels a little clunkier then the down arrow simulation but I think I'll live with it since it's a more stable option. If there's a 'faster' way to repeat AXincrement (like fast repetitions for key presses) I'd love to know that tech.
Chad Wahlbrink @Chad2023-09-19 00:21:16.539Z
@Owen_Granich_Young, the "AXIncrement" method is a bit limiting overall, but it is addressable UI, at least!
sf.ui.proTools.mainWindow.childrenByRole('AXGroup').whoseTitle.is('Clip Effects').first.elementDebug();
↑ If you use
elementDebug()
you can get a full list of the UI elements I'm addressing.
Then open the log file and you'll have the list there!
This will give you this list: https://www.dropbox.com/scl/fi/spcf20affdz24r03yx5yn/Clip-Effects-deBug.rtf?rlkey=0eonsxo9fv0t4mr5konhm4wpl&dl=0
- OOwen Granich-Young @Owen_Granich_Young
Aha, elementDebug() very very cool, thank you!
Chad Wahlbrink @Chad2023-09-19 01:31:43.146Z
No problem! It's a fun method. After doing more digging, it seems that these sliders are not "settable." - you can find what values are settable by using the
attributes
argument inelementDebug()
. This basically just means, you are limited to either using "AXIncrement" and "AXDecrement" or you can utilize relative mouse simulation like you were exploring to either use "up" and "down" arrow keys or use a scroll action simulation.
let clipEffectsPanel = sf.ui.proTools.mainWindow.children.whoseTitle.is('Clip Effects'); clipEffectsPanel.first.children.whoseTitle.is('Filter 1 Frequency').first.elementDebug({attributes:true});
↑ This showed me the following output:
"AXValue": { "class": "__NSCFNumber", "isSettable": false }, "AXValueDescription": { "class": "NSTaggedPointerString", "isSettable": false, "value": "20.6 Hz" },
Thus not settable.
- OOwen Granich-Young @Owen_Granich_Young
Hey @Chad it's me again!
So is the above the reason :
let clipEffectsPanel = sf.ui.proTools.windows.whoseTitle.startsWith('Edit: ').first.children.whoseTitle.is('Clip Effects'); clipEffectsPanel.first.children.whoseTitle.is('Filter 1 Frequency').first.mouseClickElement();
Won't work? I was hoping to dodge Click Relative to UI element but use the mouseClickElement(); to click into the text box...
Chad Wahlbrink @Chad2023-09-19 17:40:41.421Z
It may be! @Kitch has more experience here. So I'm curious if he can chime in and confirm that an element that is "not settable" is also "not-clickable" by mouse simulation.
But yes, I ran into that same issue... I haven't found a workaround yet unfortunately.- OOwen Granich-Young @Owen_Granich_Young
Yeah the workaround seems to be Click Relative UI
const {parameter, boxValue} = event.props const positionLookup = { "High-Mid Frequency" : {"x":505,"y":75}, "High-Mid Gain" : {"x":555,"y":75}, "High-Mid Q" : {"x":605,"y":75} } const selectedPosition = positionLookup[parameter]; sf.ui.proTools.mainWindow.children.whoseRole.is("AXStaticText").whoseValue.is('Clip:').first.mouseClickElement({ relativePosition: selectedPosition, }); sf.keyboard.type({ text: boxValue }); sf.keyboard.press({ keys: "numpad enter", });
A lot of brute force to be programmed to affect every parameter... snappy though
This is gonna take a while
- In reply toChad⬆:OOwen Granich-Young @Owen_Granich_Young
Ps how do I get the info on the other children.whoseTitle.is ? such as these elements
- In reply toChad⬆:OOwen Granich-Young @Owen_Granich_Young
@Chad Ok, so going back to the original idea... I think that scroll wheel approach would be the snappiest solution. I've built the lookup table of XY relative to UI. My question to you is, what's the 'most stable' way (yes I know it's not stable) to 'float mouse over postion' 'Simulate Scroll Wheel'
Because watch this action... this is how I'd love the knobs to perform.
https://www.dropbox.com/scl/fi/men5pdtvx3fywr34ohxln/Float-and-Scroll-Wheel-Up-Down.mov?rlkey=x4rr6n8scu4h0e4tmrevgvou4&dl=0XY table
const {parameter, boxValue} = event.props const positionLookup = { "High Frequency" : {"x":505,"y":75}, "High Gain" : {"x":555,"y":75}, "High Q" : {"x":605,"y":75}, "High-Mid Frequency" : {"x":505,"y":75}, "High-Mid Gain" : {"x":555,"y":75}, "High-Mid Q" : {"x":605,"y":75}, "Low-Mid Frequency" : {"x":505,"y":145}, "Low-Mid Gain" : {"x":555,"y":145}, "Low-Mid Q" : {"x":605,"y":145}, "Low Frequency" : {"x":505,"y":175}, "Low Gain" : {"x":555,"y":175}, "Low Q" : {"x":605,"y":175}, "Filter 1 Freq" : {"x":740,"y":90}, "Filter 2 Freq" : {"x":737,"y":154}, } const selectedPosition = positionLookup[parameter]; ////Help code here to float over above XY and I guess seprate script for up down scroll wheel? Not sure best approach
Chad Wahlbrink @Chad2023-09-19 20:24:11.856Z
Hey @Owen_Granich_Young
I'm using some code found here:
How move the Mouse to a position in an active Window #post-3
Something like this would work:const { parameter } = event.props const positionLookup = { "High Frequency": { "x": 505, "y": 75 }, "High Gain": { "x": 555, "y": 75 }, "High Q": { "x": 605, "y": 75 }, "High-Mid Frequency": { "x": 505, "y": 75 }, "High-Mid Gain": { "x": 555, "y": 75 }, "High-Mid Q": { "x": 605, "y": 75 }, "Low-Mid Frequency": { "x": 505, "y": 145 }, "Low-Mid Gain": { "x": 555, "y": 145 }, "Low-Mid Q": { "x": 605, "y": 145 }, "Low Frequency": { "x": 505, "y": 175 }, "Low Gain": { "x": 555, "y": 175 }, "Low Q": { "x": 605, "y": 175 }, "Filter 1 Freq": { "x": 740, "y": 90 }, "Filter 2 Freq": { "x": 737, "y": 154 }, } // Lookup Selected Position const selectedPosition = positionLookup[parameter]; // Define anchor for relative position const clipFXFrame = sf.ui.proTools.mainWindow.children.whoseRole.is("AXStaticText").whoseValue.is('Clip:').first.frame; // Set Mouse Position to Relative Position sf.mouse.setPosition({ position: { x: clipFXFrame.x + selectedPosition.x, y: clipFXFrame.y + selectedPosition.y, }, });
This would be template script with one property called "parameter"
The generic knob command would look like
You could then change the parameter or "delta" value from the Mouse Scroll macro action in the Generic Knob Command to Modify Functions
Chad Wahlbrink @Chad2023-09-19 20:25:55.275Z
It's not quite as fluid as scrolling on a "mouse wheel" since most mouse drivers seem to react to some amount of "acceleration" and we are hardcoding a single "delta" value, but it's a bit more responsive overall for sure!
- OOwen Granich-Young @Owen_Granich_Young
Awesome will experiment! If it really gains us nothing in fluidity I'll go back to the AXIncrement methodology. The up down arrows x10 fast was a bit smoother, but I don't think worth the unstablity.
Kitch Membery @Kitch2023-09-19 22:22:28.834Z
Fingers crossed the scrolling works well...
The following script will get you information on the sliders in the Clip Effects panel so you will no longer need the hard-coded lookup positions.
const clipEffectsPanel = sf.ui.proTools.mainWindow.groups.whoseTitle.is("Clip Effects").first; let clipGainSlidersInfo = clipEffectsPanel.children.filter(c => c.sliders).map(e => ({ element: e, title: e.title.value, value: e.value.intValue, position: { x: e.position.x, y: e.position.y }, centerPosition: { x: e.position.x + (e.frame.w / 2), // x position plus half the width of the element y: e.position.y + (e.frame.h / 2), // y position plus half the height of the element }, })); log(clipGainSlidersInfo);
Probably best to use the slider's center position for placing the mouse.
Rock on!
- OOwen Granich-Young @Owen_Granich_Young
Awww man really?!?! I've spent all day on these look up tables LOLOLOL. This is more stable?
Yeah here's increment vs scroll wheel action, scroll wheel is a LOT more satisfying.
https://www.dropbox.com/scl/fi/mcdgbqphvg90z7ejryziu/scroll-wheel-delta-of-3-vs-AX-increment.mov?rlkey=qg4u9czst9sgc17p3ygb1n85d&dl=0a delta of 3 on the scroll wheel seems to be the sweet spot
Kitch Membery @Kitch2023-09-19 22:29:38.519Z
Hahahaha Sorry!!! :-0
- In reply toOwen_Granich_Young⬆:
Kitch Membery @Kitch2023-09-19 22:25:11.251Z
Also, I think you and @Chad have exhausted the possibilities of controlling the AXIncrement AXDecrement, and Mouse scrolling techniques, and as Chad mentioned, it does not look like the slider values can be set directly. So I think you might be on the right track already.
- In reply toOwen_Granich_Young⬆:OOwen Granich-Young @Owen_Granich_Young
Interestinly this breaks it:
One is making it so it hovers before scrolling but then it keeps 'rehovering' vs the other direction is just scroll wheel. I wonder if there's an 'if' statment that says 'yo if this is in the right place just ignore this part and scroll... Otherwise i'm just going to break it out into 'hover with knob kilck, and scroll only with the knobs'
Kitch Membery @Kitch2023-09-19 22:54:28.398Z
Is it only the reverse scroll that this is occurring with?
- OOwen Granich-Young @Owen_Granich_Young
No, it's the fact that on the reverse scroll I've put the 'trigger the hover action first' so it needs some sort of 'is it in the right place, no go there' is it in the right place? Yes ok just scroll' but I couldn't quite figure out the right 'sf.mouse.getmousePosistion' build to make it do that move first then check...
Kitch Membery @Kitch2023-09-19 22:58:30.052Z
Try
sf.mouse.getPosition().position
- OOwen Granich-Young @Owen_Granich_Young
BETA version for testing
Chad Wahlbrink @Chad2023-09-20 13:14:24.496Z
@Owen_Granich_Young, this is really great! The clicking functionality is interesting. I saw that code on the other thread.
I did a bit of a refactor on your code for "Hover EQ Parameter Toggle" and tried using modifiers to change frequency, gain, and Q instead of the multiple clicks on the Stream Deck. Not sure which way is actually more intuitive yet, but interesting code all around!
const { band } = event.props const positionLookupFreq = { "High Band": { "x": 505, "y": 75 }, "High-Mid Band": { "x": 505, "y": 115 }, "Low-Mid Band": { "x": 505, "y": 145 }, "Low Band": { "x": 505, "y": 175 }, "Filter 1": { "x": 740, "y": 90 }, "Filter 2": { "x": 737, "y": 154 }, } const positionLookupGain = { "High Band": { "x": 555, "y": 75 }, "High-Mid Band": { "x": 555, "y": 115 }, "Low-Mid Band": { "x": 555, "y": 145 }, "Low Band": { "x": 555, "y": 175 }, } const positionLookupQ = { "High Band": { "x": 605, "y": 75 }, "High-Mid Band": { "x": 605, "y": 115 }, "Low-Mid Band": { "x": 605, "y": 145 }, "Low Band": { "x": 605, "y": 175 }, } const clipEffectsPanel = sf.ui.proTools.windows.whoseTitle.startsWith('Edit: ').first.children.whoseTitle.is('Clip Effects'); const clipFXisEnabled = sf.ui.proTools.getMenuItem("View", "Other Displays", "Clip Effects").isMenuChecked function showMeClipFX() { if (!clipFXisEnabled) { sf.ui.proTools.menuClick({ menuPath: ["View", "Other Displays", "Clip Effects"], targetValue: "Enable", }); sf.ui.proTools.mainWindow.buttons.whoseTitle.is('Compare').first.elementWaitFor(); //Button var bypassBtn = sf.ui.proTools.mainWindow.buttons.whoseTitle.is('Effect Bypass').first; //Check if button is enabled if (bypassBtn.invalidate().value.value == "not equal") log("Compare Button is enabled"); if (bypassBtn.invalidate().value.value == "equal") log("Compare Button is not enabled"); if (bypassBtn.invalidate().value.value == "no selection") log("⚠️ • No Full Audio Clip Selected • ⚠️"); } } function hover(band, control) { const clipFXFrame = sf.ui.proTools.mainWindow.children.whoseRole.is("AXStaticText").whoseValue.is('Clip:').first.frame; // Set Mouse Position to Relative Position sf.mouse.setPosition({ position: { x: clipFXFrame.x + control[band].x, y: clipFXFrame.y + control[band].y, }, }); } function filterOnCheck() { if (band.startsWith('Filter')) { if (clipEffectsPanel.first.children.whoseTitle.is(`${band} In/Out`).first.value.intValue == 0) { clipEffectsPanel.first.children.whoseTitle.is(`${band} In/Out`).first.elementClick({ actionName: "AXIncrement" }); } } } function main() { showMeClipFX() filterOnCheck() const kb = event.keyboardState; switch (true) { case (kb.hasCommand): hover(band, positionLookupGain); break; case (kb.hasAlt): hover(band, positionLookupQ); break; default: hover(band, positionLookupFreq); break; } }; main();
- In reply toOwen_Granich_Young⬆:
Chad Wahlbrink @Chad2023-09-20 13:26:25.766Z
@Owen_Granich_Young, here's an example of checking the mouse position with an if statement
onst { band } = event.props const positionLookupFreq = { "High Band": { "x": 505, "y": 75 }, "High-Mid Band": { "x": 505, "y": 115 }, "Low-Mid Band": { "x": 505, "y": 145 }, "Low Band": { "x": 505, "y": 175 }, "Filter 1": { "x": 740, "y": 90 }, "Filter 2": { "x": 737, "y": 154 }, } function hover(band, control) { const clipFXFrame = sf.ui.proTools.mainWindow.children.whoseRole.is("AXStaticText").whoseValue.is('Clip:').first.frame; const controlPosition = { x: clipFXFrame.x + control[band].x, y: clipFXFrame.y + control[band].y, } // Check Position of mouse if(sf.mouse.getPosition() != controlPosition ){ // Set Mouse Position to Relative Position sf.mouse.setPosition({ position: controlPosition, }); } } hover(band, positionLookupFreq);
Chad Wahlbrink @Chad2023-09-20 13:28:16.258Z
Using that script as the "check for placement" template action in the generic knob command. Seems to work on my end!
- In reply toChad⬆:
Kitch Membery @Kitch2023-09-20 14:09:02.752Z
Hi @Chad,
I believe the way you are comparing the two objects will always return true.
if(sf.mouse.getPosition() != controlPosition )
Try instead using
JSON.stringify
to compare the two position objects.Something like this, untested pseudo code...
const { band } = event.props; const positionLookupFreq = { "High Band": { x: 505, y: 75 }, "High-Mid Band": { x: 505, y: 115 }, "Low-Mid Band": { x: 505, y: 145 }, "Low Band": { x: 505, y: 175 }, "Filter 1": { x: 740, y: 90 }, "Filter 2": { x: 737, y: 154 }, }; function comparePositions(pos1, pos2) { return JSON.stringify(pos1) === JSON.stringify(pos2); } function hover(band, control) { const clipFXFrame = sf.ui.proTools.mainWindow.children.whoseRole .is("AXStaticText") .whoseValue.is("Clip:").first.frame; const controlPosition = { x: clipFXFrame.x + control[band].x, y: clipFXFrame.y + control[band].y, }; const currentPosition = sf.mouse.getPosition().position; // Check position of mouse const isInPosition = comparePositions(currentPosition, controlPosition); if (!isInPosition) { // Set Mouse Position to Relative Position sf.mouse.setPosition({ position: controlPosition, }); } } hover(band, positionLookupFreq);
Rock on!
Chad Wahlbrink @Chad2023-09-20 14:32:44.791Z
@Kitch is right! Of course, he is 👏👏
This seems to work:const { band } = event.props const positionLookupFreq = { "High Band": { "x": 505, "y": 75 }, "High-Mid Band": { "x": 505, "y": 115 }, "Low-Mid Band": { "x": 505, "y": 145 }, "Low Band": { "x": 505, "y": 175 }, "Filter 1": { "x": 740, "y": 90 }, "Filter 2": { "x": 737, "y": 154 }, } // Compare String of object 1 and object 2 function comparePositions(pos1, pos2) { return JSON.stringify(pos1) === JSON.stringify(pos2); } function hover(band, control) { // Define Clip FX Frame const clipFXFrame = sf.ui.proTools.mainWindow.children.whoseRole .is("AXStaticText") .whoseValue.is('Clip:').first.frame; // Lookup control position const controlPosition = { x: clipFXFrame.x + control[band].x, y: clipFXFrame.y + control[band].y, } // Get current Position Coords const currentPositionCoords = sf.mouse.getPosition().position; // Map the Coords to a new Position Object const currentPosition = { x: currentPositionCoords.x, y: currentPositionCoords.y, } // Check position of mouse const isInPosition = comparePositions(currentPosition, controlPosition); if (!isInPosition) { // Set Mouse Position to Relative Position sf.mouse.setPosition({ position: controlPosition, }); } } hover(band, positionLookupFreq);
- OOwen Granich-Young @Owen_Granich_Young
Cool guys! I'll play around with this version and then post it to the package. I like the hold modifier one if I switch the Streamdeck+ to sit on my mouse side just above my mouse. if this works as intended would be really easy to have a 4 band on knobs. Filter 1 Low Band High Band Filter 2 spread across the 4 knobs and you're good to party. Gonna build the change filter mode template today, knob click will switch type.
- OOwen Granich-Young @Owen_Granich_Young
@Chad this 'check if the mouse is in the right place thing doesn't seem to be working on my end... here's a video of it set with the script ON the knob followed by scroll wheel (as your above snapshot) and then one of JUST scroll wheel. You can see how different the action is. Does it almost need an if/else with the scroll wheel macro built in?
So IF we're in the wrong place, move to the right place, if not just scroll and stop checking to slow us down? Kinda why I was unable to wrap my head around the order of operations.
I really do want to be able to just twist the Knob and it goes not having to press a button to float would be great, but also it's gotta be as fast as the pure scroll wheel mode or what's the point.
Chad Wahlbrink @Chad2023-09-20 21:37:44.397Z
Hey @Owen_Granich_Young!
Check out this demo video of where I got it on my end:
https://www.dropbox.com/scl/fi/cxxnsawf7sjznybw2qkzq/2023-09-20-Clip-FX-Knobx.mp4?rlkey=azwnv1kzl0ephjy2tyqt7e2df&dl=0
I think the biggest gain was removing:showMeClipFX() filterOnCheck()
from the scrolling scripts.
Chad Wahlbrink @Chad2023-09-20 21:42:56.478Z
Clip Effects Control (Scrolling)
if (!sf.ui.proTools.isRunning) throw `Pro Tools is not running`; const { band, scrollDelta } = event.props const positionLookupFreq = { "EQ High": { "x": 505, "y": 75 }, "EQ High-Mid": { "x": 505, "y": 115 }, "EQ Low-Mid": { "x": 505, "y": 145 }, "EQ Low": { "x": 505, "y": 175 }, "Filter 1": { "x": 730, "y": 85 }, "Filter 2": { "x": 730, "y": 154 }, } const positionLookupGain = { "EQ High": { "x": 555, "y": 75 }, "EQ High-Mid": { "x": 555, "y": 115 }, "EQ Low-Mid": { "x": 555, "y": 145 }, "EQ Low": { "x": 555, "y": 175 }, } const positionLookupQ = { "EQ High": { "x": 605, "y": 75 }, "EQ High-Mid": { "x": 605, "y": 115 }, "EQ Low-Mid": { "x": 605, "y": 145 }, "EQ Low": { "x": 605, "y": 175 }, } // Compare String of object 1 and object 2 function comparePositions(pos1, pos2) { return JSON.stringify(pos1) === JSON.stringify(pos2); } function hover(band, control) { // Define Clip FX Frame const clipFXFrame = sf.ui.proTools.mainWindow.children.whoseRole .is("AXStaticText") .whoseValue.is('Clip:').first.frame; // Lookup control position const controlPosition = { x: clipFXFrame.x + control[band].x, y: clipFXFrame.y + control[band].y, } // Get current Position Coords const currentPositionCoords = sf.mouse.getPosition().position; // Map the Coords to a new Position Object const currentPosition = { x: currentPositionCoords.x, y: currentPositionCoords.y, } // Check position of mouse const isInPosition = comparePositions(currentPosition, controlPosition); if (!isInPosition) { // Set Mouse Position to Relative Position sf.mouse.setPosition({ position: controlPosition, }); } } function main() { const kb = event.keyboardState; switch (true) { case (kb.hasCommand): if (positionLookupGain[band]) { hover(band, positionLookupGain); sf.mouse.scroll({ delta: Number(scrollDelta) }); } break; case (kb.hasAlt): if (positionLookupQ[band]) { hover(band, positionLookupQ); sf.mouse.scroll({ delta: Number(scrollDelta) * 3 }); } break; case (kb.hasShift): hover(band, positionLookupFreq); sf.mouse.scroll({ delta: Number(scrollDelta)}); break; default: hover(band, positionLookupFreq); sf.mouse.scroll({ delta: Number(scrollDelta) * 3 }); break; } }; main();
- In reply toChad⬆:
Chad Wahlbrink @Chad2023-09-20 21:43:54.135Z2023-09-20 21:57:17.639Z
Clip Effects Buttons (In/Out)
if (!sf.ui.proTools.isRunning) throw `Pro Tools is not running`; const { band, } = event.props; const clipEffectsPanel = sf.ui.proTools.windows.whoseTitle.startsWith('Edit: ').first.children.whoseTitle.is('Clip Effects'); const clipFXisEnabled = sf.ui.proTools.getMenuItem("View", "Other Displays", "Clip Effects").isMenuChecked function showMeClipFX() { if (!clipFXisEnabled) { sf.ui.proTools.menuClick({ menuPath: ["View", "Other Displays", "Clip Effects"], targetValue: "Enable", }); sf.ui.proTools.mainWindow.buttons.whoseTitle.is('Compare').first.elementWaitFor(); //Button var bypassBtn = sf.ui.proTools.mainWindow.buttons.whoseTitle.is('Effect Bypass').first; //Check if button is enabled if (bypassBtn.invalidate().value.value == "not equal") log("Compare Button is enabled"); if (bypassBtn.invalidate().value.value == "equal") log("Compare Button is not enabled"); if (bypassBtn.invalidate().value.value == "no selection") log("⚠️ • No Full Audio Clip Selected • ⚠️"); } } function inOut() { if (clipEffectsPanel.first.children.whoseTitle.is(`${band} In/Out`).first.value.intValue == 0) { clipEffectsPanel.first.children.whoseTitle.is(`${band} In/Out`).first.elementClick({ actionName: "AXIncrement" }); } else{ clipEffectsPanel.first.children.whoseTitle.is(`${band} In/Out`).first.elementClick({ actionName: "AXDecrement" }); } } function main() { showMeClipFX() inOut() }; main();
- OOwen Granich-Young @Owen_Granich_Young
Masterful Work Chad! All makes sense to me! Will play around on my end and upload for ppl to explore.
I also made the AXIncrement version today for fun. It's nice cuz you can just spread a single band's parameters across 3 wheels, it's not so nice because it's slow.
ps... how would you toggle through 4 filter types?
Toggling the HF and LF is easy via
const {eqType} = event.props let clipEffectsPanel = sf.ui.proTools.windows.whoseTitle.startsWith('Edit: ').first.children.whoseTitle.is('Clip Effects'); if(clipEffectsPanel.first.children.whoseTitle.is(eqType).first.value.intValue == 0){ clipEffectsPanel.first.children.whoseTitle.is(eqType).first.elementClick({actionName:"AXIncrement"}); } else{ clipEffectsPanel.first.children.whoseTitle.is(eqType).first.elementClick({actionName:"AXDecrement"}); }
But it breaks on the filters which have 4 options.. so you either want it to reach 3 and start back at 1 or reach three and incremnt back down.... little bit of a syntax head scratcher for me.
Chad Wahlbrink @Chad2023-09-20 22:10:06.039Z
It's tricky because you can't "set" the value. This will work for EQ High, EQ Low, and the Filters, though.
if (!sf.ui.proTools.isRunning) throw `Pro Tools is not running`; const { band, } = event.props; const clipEffectsPanel = sf.ui.proTools.windows.whoseTitle.startsWith('Edit: ').first.children.whoseTitle.is('Clip Effects'); const clipFXisEnabled = sf.ui.proTools.getMenuItem("View", "Other Displays", "Clip Effects").isMenuChecked function showMeClipFX() { if (!clipFXisEnabled) { sf.ui.proTools.menuClick({ menuPath: ["View", "Other Displays", "Clip Effects"], targetValue: "Enable", }); sf.ui.proTools.mainWindow.buttons.whoseTitle.is('Compare').first.elementWaitFor(); //Button var bypassBtn = sf.ui.proTools.mainWindow.buttons.whoseTitle.is('Effect Bypass').first; //Check if button is enabled if (bypassBtn.invalidate().value.value == "not equal") log("Compare Button is enabled"); if (bypassBtn.invalidate().value.value == "equal") log("Compare Button is not enabled"); if (bypassBtn.invalidate().value.value == "no selection") log("⚠️ • No Full Audio Clip Selected • ⚠️"); } } function switchFilters() { if (band.startsWith('Filter') && clipEffectsPanel.first.children.whoseTitle.is(`${band} Type`).first.value.intValue < 3) { clipEffectsPanel.first.children.whoseTitle.is(`${band} Type`).first.elementClick({ actionName: "AXIncrement" }); } else if (band.startsWith('Filter') && clipEffectsPanel.first.children.whoseTitle.is(`${band} Type`).first.value.intValue === 3){ clipEffectsPanel.first.children.whoseTitle.is(`${band} Type`).first.elementClick({ actionName: "AXDecrement" }); clipEffectsPanel.first.children.whoseTitle.is(`${band} Type`).first.elementClick({ actionName: "AXDecrement" }); clipEffectsPanel.first.children.whoseTitle.is(`${band} Type`).first.elementClick({ actionName: "AXDecrement" }); } else if ((band === 'EQ High' || band === 'EQ Low' ) && clipEffectsPanel.first.children.whoseTitle.is(`${band} Type`).first.value.intValue === 0){ clipEffectsPanel.first.children.whoseTitle.is(`${band} Type`).first.elementClick({ actionName: "AXIncrement" }); } else if ((band === 'EQ High' || band === 'EQ Low' ) && clipEffectsPanel.first.children.whoseTitle.is(`${band} Type`).first.value.intValue === 1){ clipEffectsPanel.first.children.whoseTitle.is(`${band} Type`).first.elementClick({ actionName: "AXDecrement" }); } } function main() { showMeClipFX() switchFilters() }; main();
- OOwen Granich-Young @Owen_Granich_Young
I THINK THIIS THE ONE! Don't hate me I'm going to change which modifier keys do what... personal preference. Don't feel like making it user defined in template only to find out that slows the script down, but if it doesn't that'd be a nice sweetener.
Chad Wahlbrink @Chad2023-09-20 23:41:36.990Z
Love it! Do as you please. I have my own versions haha
I don’t think making modifiers customizable would knock performance, but not essential for getting it out there!- OOwen Granich-Young @Owen_Granich_Young
New Version is up. All credit to @Chad
Chris Shaw @Chris_Shaw2023-09-21 00:48:16.705Z
You guys beat me to it!
- OOwen Granich-Young @Owen_Granich_Young
HAVE YOU BEEN WORKING ON THIS?! This is not Audiosuites (which i'm still waiting for btw), and I can't figure out how to get it on the Midi Fighter Twister knobs.
- In reply toChris_Shaw⬆:
Chad Wahlbrink @Chad2023-09-21 02:24:52.108Z
Yeah… if only I could use this with a midi fighter twister 😉