I've a script that has been working fine until recently (perhaps PT2024.6 or SF5.8?). It sets up a stereo track for reverse fx from a selected mono track
However, after copying clips and then creating new tracks and selecting the top one, it seems to be failing at pasting the clips. I've discovered that once it runs the app.trackSelectByName({names: [newlyCreatedTracks[0].normalizedTrackName]
the Edit Menu is mostly greyed out. I've tried a few kinds of invalidating (sf.ui.proTools.windows.invalidate();
etc) but can't seem to get the Edit menu to reactivate. If I manually facilitate a keypress (such as ; or p) to move the selection up and down, the Edit menu comes back online and I am able to get the copied clips to Paste.
Is there a way to reactivate the Edit menu and allow access to the Paste command, without putting in a random keypress? The first paste attempt on the script below Line 247 is where it stops working
const app = sf.ui.proTools;
app.appActivateMainWindow()
app.invalidate()
function scrollToTrackNamed(trackName, selectedTracks) {
// Open scroll to track dialog and enter track name
app.menuClick({ menuPath: ['Track', 'Scroll to Track...'] });
var confirmationDialogWin = app.confirmationDialog;
confirmationDialogWin.elementWaitFor();
confirmationDialogWin.textFields.first.elementSetTextFieldWithAreaValue({ value: trackName });
confirmationDialogWin.buttons.whoseTitle.is('OK').first.elementClick();
confirmationDialogWin.elementWaitFor({ waitType: 'Disappear' });
//Re-select originally selected tracks
//app.trackSelectByName({ names: selectedTracks })
};
function saveUserWinConfig() {
app.appActivateMainWindow();
app.menuClick({
menuPath: ["Window", "Configurations", "New Configuration..."],
});
//Wait for the Window Config dialog to appear and assign it to the memLocDlg variable
var winConfigDlg = app.dialogWaitForManual({
dialogTitle: 'New Window Configuration'
}).dialog;
//We want to save a window layout
winConfigDlg.radioButtons[1].elementClick()
//We want to include all windows
winConfigDlg.checkBoxes[0].checkboxSet({
targetValue: 'Enable',
})
//Name the Location Track Visibilty
winConfigDlg.textFields.allItems[1].elementSetTextFieldWithAreaValue({
value: "User Window Setup",
});
//Get the Location Number and pass it back so we can delete it when we're done
var configNumber = winConfigDlg.textFields.allItems[0].value.invalidate().value;
//Create the Location
winConfigDlg.buttons.whoseTitle.is('OK').first.elementClick();
app.windows.whoseTitle.is('New Window Configuration').first.elementWaitFor({
waitType: "Disappear",
});
return configNumber
}
function recallAndDeleteWinConfig(configNumber) {
app.appActivateMainWindow();
app.menuClick({
menuPath: ["Window", "Configurations", "Window Configuration List"],
targetValue: 'Enable',
});
app.windows.whoseTitle.is('Window Configurations').first.elementWaitFor();
var winConfigWindow = app.windows.whoseTitle.is('Window Configurations').first
// Recall Window Config which will also leave it selected for deleting
const configNumStr = configNumber.toString();
let winConfigWords = 'numpad comma, ';
for (var i = 0; i < configNumStr.length; i++) {
winConfigWords = winConfigWords.concat('numpad ' + configNumStr[i] + ", ")
}
let keys = winConfigWords + "numpad multiply";
sf.keyboard.press({
keys: keys,
});
app.windows.whoseTitle.is('Window Configurations').first.buttons.first.popupMenuSelect({
menuSelector: items => items.filter(i => i.names[0].match(/^Clear \"/))[0]
});
app.menuClick({
menuPath: ["Window", "Configurations", "Window Configuration List"],
targetValue: 'Disable' //or 'Enable' or 'Toggle'
});
app.windows.whoseTitle.is('Window Configurations').first.elementWaitFor({
waitType: "Disappear",
});
}
function createTracks(trackName) {
app.menuClick({
menuPath: ['Track', 'New...']
});
const newTrackWin = app.windows.whoseTitle.is("New Tracks").first
newTrackWin.elementWaitFor();
const numberField = newTrackWin.textFields.whoseTitle.is('Number of new tracks').first;
const numberOfTracks = '2';
if (numberField.value.invalidate().value.trim() !== numberOfTracks) {
numberField.elementClick();
sf.keyboard.type({ text: numberOfTracks });
var i = 0;
while (numberField.value.invalidate().value !== numberOfTracks) {
sf.wait({ intervalMs: 30 });
};
}
newTrackWin.popupButtons.whoseDescription.is("Track format").first.popupMenuSelect({
menuPath: ["Mono"]
});
newTrackWin.popupButtons.whoseDescription.is("Track type").first.popupMenuSelect({
menuPath: ["Audio Track"]
});
newTrackWin.buttons.whoseTitle.is("Add row").first.elementClick();
newTrackWin.popupButtons.allItems[3].popupMenuSelect({
menuPath: ['Stereo']
})
newTrackWin.popupButtons.allItems[4].popupMenuSelect({
menuPath: ["Audio Track"]
});
newTrackWin.textFields.whoseTitle.is("Track Name").allItems[1].elementSetTextFieldWithAreaValue({
value: 'reverses ' + trackName,
});
newTrackWin.buttons.whoseTitle.is("Create").first.elementClick();
};
function recallPlugins() {
const presetName = "reverses"
const tracks = app.selectedTrackHeaders
//Scroll firsr track to View
tracks[0].trackScrollToView()
app.selectedTrack.insertSelectorButtons[0].popupMenuSelect({
menuPath: ["recall inserts", "Mix Templates", "VOCALS", presetName],
})
if (app.confirmationDialog.buttons.whoseTitle.is("Change").first.exists) {
app.confirmationDialog.buttons.whoseTitle.is("Change").first.elementClick();
}
//dismissConfirmationDialog("OK")
};
function getSelection() {
const timelineSelection = app.selectionGet();
let originalSelectionSamples = app.selectionGetInSamples();
if (originalSelectionSamples.selectionLength === 0) {
sf.interaction.displayDialog({
prompt: 'Make a selction on the track to be reversed.',
giveUpAfterSeconds: 2
})
throw 0
};
return timelineSelection
}
function main() {
//Store window config for recall
saveUserWinConfig()
//Hide Windows
if (app.getMenuItem('Window', 'Hide All Floating Windows').isEnabled && !app.getMenuItem('Window', 'Hide All Floating Windows').isMenuChecked) {
app.menuClick({ menuPath: ['Window', 'Hide All Floating Windows'] });
}
//Get timeline selection
const originalSelection = getSelection()
//store selected track name for use
const trackName = app.selectedTrack.normalizedTrackName;
//log(trackName)
//Change state of Automation Follows Edit to OFF
app.menuClick({
menuPath: ["Options", "Automation Follows Edit"],
targetValue: 'Disable',
});
// Change all selected (Audio)tracks to Waveform
try {
app.selectedTrack.trackDisplaySelect({
displayPath: ["waveform"],
selectForAllSelectedTracks: true
})
} catch (err) {
sf.interaction.displayDialog({
prompt: 'Select the track to be reversed.',
giveUpAfterSeconds: 2
});
throw 0
}
// define track output selector
const audioOutput = app.selectedTrack.groups.whoseTitle.is("Audio IO")
const trackOutput = audioOutput.first.popupButtons.allItems[1]
// get ouput of first track
let outputPaths = trackOutput.popupMenuFetchAllItems().menuItems
const outputPath = outputPaths.map((mi, i) => ({ menuItem: mi, index: i })).filter(m => m.menuItem.element.isMenuChecked)[0].menuItem.path;
app.appActivateMainWindow();
//Select the entire track to be stored in the "reverse" track's first playlist
app.menuClick({ menuPath: ['Edit', 'Select All'] });
app.menuClick({ menuPath: ['Edit', 'Copy'] });
//Creates 2x temp mono and a stereo track to stereoize mono vocal track
createTracks(trackName);
const newlyCreatedTracks = app.selectedTrackHeaders;
//Select first temp mono track and paste
app.trackSelectByName({
names: [newlyCreatedTracks[0].normalizedTrackName
]
});
app.windows.invalidate();
app.menuClick({
menuPath: ['Edit', 'Paste']
});
if (app.confirmationDialog.buttons.whoseTitle.is("OK").first.exists) {
app.confirmationDialog.buttons.whoseTitle.is("OK").first.elementClick();
app.confirmationDialog.buttons.whoseTitle.is("OK").first.elementWaitFor({ waitType: "Disappear" });
};
//Select second temp mono track and paste
app.trackSelectByName({ names: [newlyCreatedTracks[1].normalizedTrackName] });
app.menuClick({
menuPath: ['Edit', 'Paste']
});
if (app.confirmationDialog.buttons.whoseTitle.is("OK").first.exists) {
app.confirmationDialog.buttons.whoseTitle.is("OK").first.elementClick();
app.confirmationDialog.buttons.whoseTitle.is("OK").first.elementWaitFor({ waitType: "Disappear" });
};
//Select both mono tracks to copy to stereo track
app.menuClick({
menuPath: ['Edit', 'Selection', 'Extend Edit Up']
});
app.menuClick({ menuPath: ['Edit', 'Copy'] });
app.trackSelectByName({ names: [newlyCreatedTracks[2].normalizedTrackName] });
app.menuClick({ menuPath: ['Edit', 'Paste'] });
// Set Original Selection
app.selectionSet({
selectionStart: originalSelection.selectionStart,
selectionEnd: originalSelection.selectionEnd
});
//Copy original selection on stereo track, create new playlist and paste
app.menuClick({ menuPath: ['Edit', 'Copy'] });
//app.appActivateMainWindow()
scrollToTrackNamed('reverses ' + trackName)
app.selectedTrack.popupButtons.whoseTitle.is('Playlist selector').first.popupMenuSelect({
menuPath: ["New..."],
relativePosition: { x: 5, y: 5 }
});
app.confirmationDialog.buttons.whoseTitle.is("OK").first.elementClick();
app.trackSelectByName({ names: ['reverses ' + trackName + '.01'] });
app.appActivateMainWindow()
app.menuClick({ menuPath: ['Edit', 'Paste'] });
//Load Track preset for reverse FX
recallPlugins()
//Additional clear for Windows due to Eucon/PT bug
if (app.getMenuItem('Window', 'Hide All Floating Windows').isEnabled && !app.getMenuItem('Window', 'Hide All Floating Windows').isMenuChecked) {
app.menuClick({ menuPath: ['Window', 'Hide All Floating Windows'] });
}
//Route reverses stereo track to same as originally selected track
app.selectedTrack.groups.whoseTitle.is("Audio IO").first.
popupButtons.whoseTitle.contains("Audio Output").first.popupMenuSelect({
menuPath: outputPath,
});
//Delete the two temp mono tracks
app.trackSelectByName({ names: [newlyCreatedTracks[0].normalizedTrackName, newlyCreatedTracks[1].normalizedTrackName] });
app.trackDelete();
app.windows.first.buttons.whoseTitle.is("Delete").first.elementClick();
scrollToTrackNamed(trackName)
//recallAndDeleteWinConfig(saveUserWinConfig)
app.audioSuiteOpenPlugin({
category: 'Reverb',
name: 'MannyM Reverb Stereo',
}).window;
};
main()
Linked from:
- Raphael Sepulveda @raphaelsepulveda2024-07-18 01:43:56.936Z
Hey @Forrester_Savell, try replacing all instances of:
app.menuClick({ menuPath: ['Edit', 'Copy'] });
app.menuClick({ menuPath: ['Edit', 'Paste'] });
with:
sf.app.proTools.copy();
sf.app.proTools.paste();
These are SDK commands that talk directly to Pro Tools, so no invalidating should be required.
Haven't tested your script to make sure it works, but that is the first thing I'd try.
- FForrester Savell @Forrester_Savell
Nailed it, thanks @raphaelsepulveda
Serendipitous that you replied, as I saw your Insta story on exactly the same script idea, which reminded me I had this one and to use it more often!
Raphael Sepulveda @raphaelsepulveda2024-07-18 06:58:55.068Z
Awesome!! Definitely beats doing it manually, that’s for sure!
- SIn reply toForrester_Savell⬆:SoundFlow Bot @soundflowbot
This report was now added to the internal issue tracked by SoundFlow as SF-1368
- In reply toForrester_Savell⬆:Chad Wahlbrink @Chad2024-07-18 15:37:26.675Z
Hey @Forrester_Savell,
I'm glad Raphael's suggestion helped! We are tracking this issue withsf.ui.proTools.trackSelectByName()
not updating menus in SF 5.8.0. Thanks for this post to further confirm the behavior.
In this other post, @Jack_Green discovered that usingsf.keyboard.press({ keys: 'left' });
will reset the Pro Tools menu state with the new selection ifinvalidate()
does not. This is not an ideal solution, as it's using keyboard simulation, but it could help navigate this issue until we get a fix.- FForrester Savell @Forrester_Savell
Thanks for the update @Chad
After Raphael solved the copy/paste issue, I found that post by @Jack_Green yesterday and used that workaround line because I have the
trackDelete()
on Line 323 in the above code which wasn't working either.Glad we've this forum and community to help each other!
- SIn reply toForrester_Savell⬆:SoundFlow Bot @soundflowbot
Linked issue SF-1368 updated: This should be fixed in the next release of SoundFlow.
- SIn reply toForrester_Savell⬆:SoundFlow Bot @soundflowbot
The linked internal issue SF-1368 has been marked as Done