Create New FX Aux Script Throws Errors if No Send on Slot 1
Title
Create New FX Aux Script Throws Errors if No Send on Slot 1
What do you expect to happen when you run the script/macro?
This wonderful script for creating FX auxes that I borrowd from Phil Weinrobe creates a send to a new aux, routes the new aux to the same destination as the original track, and prompts to add an insert on slot 2.
Are you seeing an error?
If there is no send in slot one, I get this error. If there's already a send in slot 1 the scripts works totally fine, creating a new send in slot 2.
05.11.2023 22:13:06.71 [Backend]: JavaScript error with InnerException: null
Logging error in action (01) RunCommandAction: TypeError: Cannot read property 'toLowerCase' of undefined
(Phil New Send Thing line 228)
Error in action (00) SearchCommandsAction: RunCommandAction
!! Command Error: Search Commands [soundflow.searchCommands]:
TypeError: Cannot read property 'toLowerCase' of undefined
(Phil New Send Thing line 228)
<< Command: Search Commands [soundflow.searchCommands]
What happens when you run this script?
If the source track has no send on slot 1, the script throws an error. If there is a send on slot 1 in the source track, the script works totally fine creating a new send in slot 2. However, I'm using the script most often when there are no sends on the source track.
How were you running this script?
I used a keyboard shortcut within the target app
How important is this issue to you?
5
Details
{
"inputExpected": "This wonderful script for creating FX auxes that I borrowd from Phil Weinrobe creates a send to a new aux, routes the new aux to the same destination as the original track, and prompts to add an insert on slot 2.",
"inputIsError": true,
"inputError": "If there is no send in slot one, I get this error. If there's already a send in slot 1 the scripts works totally fine, creating a new send in slot 2.\n\n05.11.2023 22:13:06.71 [Backend]: JavaScript error with InnerException: null\nLogging error in action (01) RunCommandAction: TypeError: Cannot read property 'toLowerCase' of undefined\n(Phil New Send Thing line 228) \nError in action (00) SearchCommandsAction: RunCommandAction\n!! Command Error: Search Commands [soundflow.searchCommands]:\n TypeError: Cannot read property 'toLowerCase' of undefined\n(Phil New Send Thing line 228)\n<< Command: Search Commands [soundflow.searchCommands]",
"inputWhatHappens": "If the source track has no send on slot 1, the script throws an error. If there is a send on slot 1 in the source track, the script works totally fine creating a new send in slot 2. However, I'm using the script most often when there are no sends on the source track.",
"inputHowRun": {
"key": "-Mpfwh4RkPLb2LPwjePT",
"title": "I used a keyboard shortcut within the target app"
},
"inputImportance": 5,
"inputTitle": "Create New FX Aux Script Throws Errors if No Send on Slot 1"
}
Source
/**
* @param {string} sendAssignment
*/
function assignToNewTrack(sendAssignment) {
const selectedTracks = sf.ui.proTools.selectedTrackNames
sf.ui.proTools.trackGetByName({ name: selectedTracks[selectedTracks.length - 1] }).track.groups.whoseTitle.is(getGroup("Sends", sendAssignment)).first.buttons.whoseTitle.is(`Send Assignment ${sendAssignment}`).first.popupMenuSelect({
isShift: true,
isOption: true,
menuPath: ["new track..."],
});
}
function moveSendWinOutOfSlots() {
sf.ui.proTools.invalidate()
const sendWin = sf.ui.proTools.mainTrackOutputWindow.elementWaitFor().element
const sendWinPos = sendWin.position.x
const sendSlots = sf.ui.proTools.selectedTrack.groups.whoseTitle.is("Inserts A-E").first
const sendSlotsPos = sendSlots.position.x
if (sendWinPos - sendSlotsPos < 450 && sendSlotsPos - sendWinPos < 170) {
sendWin.windowMove({
position: {
y: sendWin.position.y,
x: sendSlotsPos + 450
}
})
}
}
function getFirstFreeSend() {
const selectedTracks = sf.ui.proTools.selectedTrackHeaders
const sendLetters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
let sendAssignmentList = []
for (let i = 0; i < selectedTracks.length; i++) {
let trackInfo = sf.ui.proTools.trackGetByName({ name: selectedTracks[i].normalizedTrackName })
.track.sendButtons.filter(x => x.value.invalidate().value != "unassigned").map(x => x.title.invalidate().value.split(" ")[2])
sendAssignmentList.push(...trackInfo)
}
const firstFreeSend = sendLetters.filter(letter => sendAssignmentList.indexOf(letter) === -1)[0]
if (firstFreeSend) {
return firstFreeSend
} else {
alert("Must have at least one free send, that is the same for all tracks.")
throw 0
};
};
/**
* @param {'Insert'|'Send'} slotType
* @param {array} [defaultUsedSlots] = [] - If parameter is not used, default is []
*/
function getFirstFreeInsertOrSend(slotType, defaultUsedSlots) {
let buttons
slotType == "Insert" ? buttons = "insertButtons" : buttons = "sendButtons"
const selectedTracks = sf.ui.proTools.selectedTrackHeaders
const sendLetters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
let sendAssignmentList = defaultUsedSlots || []
try {
for (let i = 0; i < selectedTracks.length; i++) {
let trackInfo = sf.ui.proTools.trackGetByName({ name: selectedTracks[i].normalizedTrackName })
// .insertButtons or .sendButtons
.track[buttons].filter(x => x.value.invalidate().value != "unassigned").map(x => x.title.invalidate().value.split(" ")[2])
sendAssignmentList.push(...trackInfo)
}
} catch (err) { }
const firstFreeSend = sendLetters.filter(letter => sendAssignmentList.indexOf(letter) === -1)[0]
if (firstFreeSend) {
return firstFreeSend
} else {
if (slotType == "Insert") { alert(`Must have at least one free ${slotType} slot.`) }
else if (slotType == "Send") { alert(`Must have at least one free ${slotType} slot, that is the same for selected tracks.`) }
throw 0
};
};
/**
* @param {string} sendLetter
* @param {'Inserts'|'Sends'} slotType
*/
function getGroup(slotType, sendLetter) {
let group
sendLetter.toLowerCase().charCodeAt(0) - 97 <= 4 ? group = `${slotType} A-E` : group = `${slotType} F-J`
return group
}
function newTrackSettings() {
const newTrackWin = sf.ui.proTools.windows.whoseTitle.is("New Track").first.elementWaitFor().element
const format = newTrackWin.popupButtons.whoseTitle.is("Stereo").first
const type = newTrackWin.popupButtons.whoseTitle.is("Aux Input").first
const timeBase = newTrackWin.popupButtons.whoseTitle.is("Samples").first
if (format.value.invalidate().value != "Stereo") { format.popupMenuSelect({ menuPath: ["Stereo"] }); }
if (type.value.invalidate().value != "Aux Input") { type.popupMenuSelect({ menuPath: ["Aux Input"] }); }
if (timeBase.value.invalidate().value != "Samples") { timeBase.popupMenuSelect({ menuPath: ["Samples"] }); }
newTrackWin.checkBoxes.whoseTitle.is("Create next to current track").first.checkboxSet({ targetValue: "Enable" });
// Wait to name track and press ok
newTrackWin.elementWaitFor({ waitType: "Disappear", timeout: -1 })
sf.ui.proTools.invalidate()
}
/**
* @param {'Enable'|'Disable'} targetValue
* @param {array} [viewsNames] = [] - If parameter is not used, default is set inside
*/
function setWindowViews(targetValue, viewsNames) {
let views = viewsNames || ["Inserts A-E", "Inserts F-J", "Sends A-E", "Sends F-J"]
views.forEach(view => {
sf.ui.proTools.menuClick({ menuPath: ["View", "Edit Window Views", view], targetValue: targetValue })
// sf.ui.proTools.menuClick({ menuPath: ["View", "Mix Window Views", view], targetValue: targetValue })
})
}
const getSelectedTrackOut = (trackName) => sf.ui.proTools.trackGetByName({ name: trackName }).track.outputPathButton.title.invalidate().value.split('\n')[1].trim();
let insertSlot
let firstFreeSend
function actions() {
const originalSelectedTracks = sf.ui.proTools.selectedTrackNames
// Save first selected track output on variable
const firstSelectedOut = getSelectedTrackOut(originalSelectedTracks[0])
// Get free sends
firstFreeSend = getFirstFreeInsertOrSend("Send")
// Assign new send to new track
assignToNewTrack(firstFreeSend)
// New track setting and wait for track name
newTrackSettings()
// If new send win is blocking the inserts or sends, move win away
moveSendWinOutOfSlots()
// Expand created send
sf.ui.proTools.menuClick({ menuPath: ["View", "Expanded Sends", `Send ${firstFreeSend}`], targetValue: "Enable" })
// Solo safe new Aux
const selectedTracks = sf.ui.proTools.selectedTrackNames
const auxTrack = sf.ui.proTools.trackGetByName({ name: selectedTracks[selectedTracks.length - 1] }).track
auxTrack.trackSelect()
auxTrack.trackScrollToView()
auxTrack.buttons.whoseTitle.is("Solo").first.mouseClickElement({ isCommand: true })
// set new aux output
if (getSelectedTrackOut(auxTrack.normalizedTrackName) != firstSelectedOut) {
auxTrack.trackOutputSelect({ outputSelector: items => items.filter(item => item.element.title.value.includes(firstSelectedOut))[0] })
}
// Get free insert slot after A
insertSlot = getFirstFreeInsertOrSend("Insert", ["A"])
// Open search...
auxTrack.groups.whoseTitle.is(getGroup("Inserts", insertSlot)).first
.buttons.whoseTitle.is(`Insert Assignment ${insertSlot}`)
.first.popupMenuSelect({
menuPath: ["search..."],
});
// Wait to open new plug
while (true) {
const plugIsOpen = auxTrack.groups.whoseTitle.is(getGroup("Inserts", insertSlot)).first
.buttons.whoseTitle.is(`Insert Assignment ${insertSlot}`).first
.value.invalidate().value != "unassigned"
if (!plugIsOpen) {
sf.wait({ intervalMs: 1000 })
} else {
break;
}
}
sf.ui.proTools.trackSelectByName({ names: originalSelectedTracks })
}
function main() {
sf.ui.proTools.appActivateMainWindow();
sf.ui.proTools.invalidate();
// const isActiveInsGroupFMix = sf.ui.proTools.getMenuItem("View", "Mix Window Views", "Inserts F-J").isMenuChecked
const isActiveInsGroupFEdit = sf.ui.proTools.getMenuItem("View", "Edit Window Views", "Inserts F-J").isMenuChecked
// const isActiveSendGroupFMix = sf.ui.proTools.getMenuItem("View", "Mix Window Views", "Sends F-J").isMenuChecked
const isActiveSendGroupFEdit = sf.ui.proTools.getMenuItem("View", "Edit Window Views", "Sends F-J").isMenuChecked
const isEditWin = sf.ui.proTools.focusedWindow.title.value.startsWith("Edit")
if (!isEditWin) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Edit"] }) }
// Close expand created sends
sf.ui.proTools.menuClick({ menuPath: ["View", "Expanded Sends", "None"], targetValue: "Enable" })
setWindowViews("Enable")
try {
actions()
} catch (err) { throw err }
finally {
// Go to mix if needed
if (!isEditWin) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Mix"] }) }
// Close Inserts Views if not needed
if (insertSlot.toLowerCase().charCodeAt(0) - 97 <= 4 && (/* !isActiveInsGroupFMix || */ !isActiveInsGroupFEdit)) {
// if (!isActiveInsGroupFMix) { setWindowViews("Disable", ["Inserts F-J"]) }
if (!isActiveInsGroupFEdit) { setWindowViews("Disable", ["Inserts F-J"]) }
}
// Close Sends F-J if not needed
if (firstFreeSend.toLowerCase().charCodeAt(0) - 97 <= 4 && (/* !isActiveSendGroupFMix || */ !isActiveSendGroupFEdit)) {
// if (!isActiveSendGroupFMix) { setWindowViews("Disable", ["Sends F-J"]) }
if (!isActiveSendGroupFEdit) { setWindowViews("Disable", ["Sends F-J"]) }
}
}
}
main()
Links
User UID: 04vQUq1KmYSCMZHeEkMslw0IpRu2
Feedback Key: sffeedback:04vQUq1KmYSCMZHeEkMslw0IpRu2:-NiXq8AvFjK9miDqreFf
Feedback ZIP: oQjqNyxNyjiJPBc3Azu5nuJYqPMByIHms3rW+q+wYDSi9USj1HLMtFwTpst3nId5Meh7HkPi/aAnMqOo0kQbBOJ+BJpn6hJbTG1ElgScK4pPlG4bv5GPk5Z811bV99vm0plGC7JAfhjyFTPy3u9aNi0qS1JeZNk8LuYXjIGX4Br0OdwbJZS8aIBpic6zBGdpLX423Xd2L2grWjHeRDAEH6xCft0zB3aLtzqkoHX1fGBZVW613M0LVTQq1eDZsUdAxSBmJKBJLSzJsmgNHs7yxDQzWD85PIwzTuZ5lihEeV85+sy6FagxjVgCFFSIGzHkxhbqeGdi7R5Ki9DHnDZ1orfehc8AgEY2DWkH08p4yyQ=