Title
Select Between Markers Fails
What do you expect to happen when you run the script/macro?
Select Between Markers
Are you seeing an error?
Could not set left edge of selection (Select Between Markers: Line 1) No next/previous memory location found
SF is not finding the markers.
What happens when you run this script?
the script fails and i get an error message
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": "Select Between Markers", "inputIsError": true, "inputError": "Could not set left edge of selection (Select Between Markers: Line 1)\n No next/previous memory location found\n\nSF is not finding the markers. ", "inputWhatHappens": "the script fails and i get an error message", "inputHowRun": { "key": "-Mpfwh4RkPLb2LPwjePT", "title": "I used a keyboard shortcut within the target app" }, "inputImportance": 5, "inputTitle": "Select Between Markers Fails" }
Source
sf.ui.proTools.memoryLocationsSelectBetween();
Links
User UID: yesbmwLVy0dRQLFNZzxo8kQx4ri2
Feedback Key: sffeedback:yesbmwLVy0dRQLFNZzxo8kQx4ri2:-NhXJ2C8YUaWsN6ptc2W
Feedback ZIP: CVaZpFShieWWwHN21IILWI+ftZRoLxgpiaSEMBzffATY+l1CmvrSpxjHmCqSTBqO2537WEWHBk0MRdax3oj5ntWJMr+RYPZTTQjPZXNWslu6dG4p1fswMOXhZGCHLNcx/tmLZiRiKK14WP+kxxhQoF3evtom4DVLn9PVHXj7kQrdhBtXpb6T4/BFoJCmT2Z8rFaQyRKdMUvZOjk6dFy7VupEk49OoJUhVEWdzHgxg0E46nKWWOczerBKKJ7RJVAPt0sx1OD1DxTMXNxUG8QN5zBgSPiul7BtxFFGbQ4NFvyFtK+IiJ8NOiGs1XFpe/NQCQHmBe31BMf6TPddsqoUBQ==
Linked from:
- Ben Rubin @Ben_Rubin
Feels like this issue was caused by changes to pro tools during 2023.6 and/or 2023.9. perhaps something to due to the changes in markers.
Kitch Membery @Kitch2023-10-30 03:07:04.787Z
Oh think I have a script that works for this, will check in the morning:-)
Ben Rubin @Ben_Rubin
thanks @kitch. but it seems like a built-in command is failing here, no? should be addressed if so. of course, want your script :)
Kitch Membery @Kitch2023-11-02 06:10:24.975Z
Hi @Ben_Rubin,
Sorry for the delay on this. I realized that the script I had was only working on older versions of Pro Tools, and Memory Locations have changed in recent versions. So, here's an updated version. Let me know if it works for you.
/** @param {function} callback */ function setupRestore(callback) { sf.ui.proTools.appActivateMainWindow(); sf.ui.proTools.invalidate(); const isMemoryLocationsWindowOpen = sf.ui.proTools.memoryLocationsWindow.exists; if (!isMemoryLocationsWindowOpen) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Memory Locations"], }, 'Could not click to open Memory Locations window'); sf.ui.proTools.memoryLocationsWindow.elementWaitFor({}, `Failed waiting for Memory Locations window`); } const oldMainCounterMode = getMainCounterMode(); // Set Main Counter to Samples sf.ui.proTools.mainCounterSetValue({ targetValue: "Samples" }); try { callback(); } finally { // Restore Memory Locations open state if (!isMemoryLocationsWindowOpen) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Memory Locations"], targetValue: "Disable", }); } // Restore main Counter mode sf.ui.proTools.mainCounterSetValue({ targetValue: oldMainCounterMode }); } } function getMainCounterMode() { const mainCounterValue = sf.ui.proTools.mainWindow.counterDisplay.mainCounter.value.invalidate().value; if (mainCounterValue.includes("|")) { return "Bars|Beats"; } else if (mainCounterValue.includes(":") && mainCounterValue.includes(".")) { return "Min:Secs"; } else if (mainCounterValue.split(":").length >= 3) { return "Timecode"; } else if (mainCounterValue.includes("+")) { return "Feet+Frames"; } else { return "Samples"; } } function getMemoryLocations() { sf.ui.proTools.appActivate(); sf.ui.proTools.mainWindow.elementRaise(); const memoryLocationsWindow = sf.ui.proTools.memoryLocationsWindow; memoryLocationsWindow.invalidate(); const memoryLocationsTable = memoryLocationsWindow.tables.allItems[1]; const headerTable = sf.ui.proTools.memoryLocationsWindow.tables.allItems[0]; const columnTitles = headerTable.children[0].children.map(e => e.children[0].title.value); const numerationColumnIndex = columnTitles.indexOf("Numeration"); const mainCounterColumnIndex = columnTitles.indexOf("Main Counter"); const items = memoryLocationsTable.childrenByRole("AXRow").map(row => { const numberCell = row.children[numerationColumnIndex]; const mainCounterCell = row.children[mainCounterColumnIndex]; return { number: Number(numberCell.children.first.title.value.replace("Selected. ", "")), mainCounterValue: Number(mainCounterCell.children.first.title.value.replace("Selected. ", "")), } }); return items; } function selectBetweenMemoryLocations() { setupRestore(() => { const memoryLocations = getMemoryLocations(); const selectionStart = Number(sf.ui.proTools.selectionGetInSamples().selectionStart); const selectionEnd = Number(sf.ui.proTools.selectionGetInSamples().selectionEnd); let previousMarker = memoryLocations.filter(ml => ml.mainCounterValue <= selectionStart).length > 0 ? memoryLocations.filter(ml => ml.mainCounterValue <= selectionStart).slice(-1)[0].number : undefined; if (!previousMarker) throw `No previous marker found`; let nextMarker = memoryLocations.filter(ml => ml.mainCounterValue >= selectionEnd).length > 0 ? memoryLocations.filter(ml => ml.mainCounterValue >= selectionEnd).slice(0, 1)[0].number : undefined; if (!nextMarker) throw `No next marker found`; // If the cursor position is on a marker, select to the following marker if (previousMarker === nextMarker) { ++nextMarker } // Extend Selection to Previous Marker sf.ui.proTools.memoryLocationsGoto({ memoryLocationNumber: previousMarker, extendSelection: true, }, `Could not goto memory location number ${previousMarker}`); // Extend Selection to Next Marker sf.ui.proTools.memoryLocationsGoto({ memoryLocationNumber: nextMarker, extendSelection: true, }, `Could not goto memory location number ${nextMarker}`); }); } selectBetweenMemoryLocations();
Hopefully, we can resolve the issue with
sf.ui.proTools.memoryLocationsSelectBetween();
Ben Rubin @Ben_Rubin
Hey @Kitch, sorry to report that this is not working for me. Will only select from the selection point to the next marker, never also selecting to the previous marker, which is the behavior I'm seeking.
This seems to be related to some markers I have that are not time-based. When I delete them, the script runs fine, but I need them. I noticed that when I run the script, the last marker in the list is highlighted for a moment, but this is one of the markers with no time. Here is my markers window:
For this test, I was just trying to select between TOP and END.
Also, when I was right on the first marker, it wouldn't select to the next marker.Kitch Membery @Kitch2023-11-04 03:15:40.413Z
Thanks for that info, Ben. I'll take another look when I have a moment.
If you could upload a session, (no audio files needed just the PTX) with those markers in place that may help me troubleshoot the issue further.
What is the specific workflow that you are trying to achieve? Are you just wanting to select between the markers "Top" and "End"?
We plan on making some updates to memory locations functionality in the not-too-distant future. With the introduction of Track markers, it seems like Pro Tools has made some changes.
Ben Rubin @Ben_Rubin
thank you @Kitch.
the workflow I am trying to achieve is just to put the cursor in between any two adjacent markers (even if they do not have consecutive numbers.
My markers usually look more like the image i posted to Christian below. So more than just TOP and END, but just was doing that for simplicity. Happens in all sessions.
I'm also noticing now that this script also hides all audio tracks.
pls download the session here: https://house-of-cha-cha.digitalpigeon.com/shr/-T1L4H6IEe6wRQaDhYOasw/xQd2fcrCPIx85vV5Y___sw#
Kitch Membery @Kitch2023-11-09 09:23:02.108Z
Hi @Ben_Rubin,
Thanks so much for sending that session through. I think I worked it out, fingers crossed. :-)
/** @param {function} callback */ function setupRestore(callback) { sf.ui.proTools.appActivateMainWindow(); sf.ui.proTools.invalidate(); const isMemoryLocationsWindowOpen = sf.ui.proTools.memoryLocationsWindow.exists; if (!isMemoryLocationsWindowOpen) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Memory Locations"], }, 'Could not click to open Memory Locations window'); sf.ui.proTools.memoryLocationsWindow.elementWaitFor({}, `Failed waiting for Memory Locations window`); } const oldMainCounterMode = getMainCounterMode(); // Set Main Counter to Samples sf.ui.proTools.mainCounterSetValue({ targetValue: "Samples" }); try { callback(); } finally { // Restore Memory Locations open state if (!isMemoryLocationsWindowOpen) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Memory Locations"], targetValue: "Disable", }); } // Restore main Counter mode sf.ui.proTools.mainCounterSetValue({ targetValue: oldMainCounterMode }); } } function getMainCounterMode() { const mainCounterValue = sf.ui.proTools.mainWindow.counterDisplay.mainCounter.value.invalidate().value; if (mainCounterValue.includes("|")) { return "Bars|Beats"; } else if (mainCounterValue.includes(":") && mainCounterValue.includes(".")) { return "Min:Secs"; } else if (mainCounterValue.split(":").length >= 3) { return "Timecode"; } else if (mainCounterValue.includes("+")) { return "Feet+Frames"; } else { return "Samples"; } } function getMemoryLocations() { sf.ui.proTools.appActivate(); sf.ui.proTools.mainWindow.elementRaise(); const memoryLocationsWindow = sf.ui.proTools.memoryLocationsWindow; memoryLocationsWindow.invalidate(); const memoryLocationsTable = memoryLocationsWindow.tables.allItems[1]; const headerTable = sf.ui.proTools.memoryLocationsWindow.tables.allItems[0]; const columnTitles = headerTable.children[0].children.map(e => e.children[0].title.value); const nameColumnIndex = columnTitles.indexOf("Name"); const numerationColumnIndex = columnTitles.indexOf("Numeration"); const mainCounterColumnIndex = columnTitles.indexOf("Main Counter"); const items = memoryLocationsTable.childrenByRole("AXRow").map(row => { const nameCell = row.children[nameColumnIndex]; const numberCell = row.children[numerationColumnIndex]; const mainCounterCell = row.children[mainCounterColumnIndex]; return { name: nameCell.children.first.title.value.replace("Selected. ", ""), number: Number(numberCell.children.first.title.value.replace("Selected. ", "")), // Filter out any Memory Locations that have no Main Counter Value (Important) mainCounterValue: mainCounterCell.children.first.title.value.replace("Selected. ", "") !== "" ? Number(mainCounterCell.children.first.title.value.replace("Selected. ", "")) : undefined, } }); return items; } function selectBetweenMemoryLocations() { setupRestore(() => { const memoryLocations = getMemoryLocations(); const selectionStart = Number(sf.ui.proTools.selectionGetInSamples().selectionStart); const selectionEnd = Number(sf.ui.proTools.selectionGetInSamples().selectionEnd); let previousMarker = memoryLocations.filter(ml => ml.mainCounterValue <= selectionStart).length > 0 ? memoryLocations .filter((ml) => ml.mainCounterValue <= selectionStart) .sort((a, b) => a.mainCounterValue - b.mainCounterValue) .slice(-1)[0].number : undefined; if (!previousMarker) throw `No previous marker found`; let nextMarker = memoryLocations.filter(ml => ml.mainCounterValue >= selectionEnd).length > 0 ? memoryLocations .filter((ml) => ml.mainCounterValue >= selectionEnd) .sort((a, b) => b.mainCounterValue - a.mainCounterValue) .slice(-1)[0].number : undefined; if (!nextMarker) throw `No next marker found`; // If the cursor position is on a marker, select to the following marker if (previousMarker === nextMarker) { ++nextMarker } // Extend Selection to Previous Marker sf.ui.proTools.memoryLocationsGoto({ memoryLocationNumber: previousMarker, extendSelection: true, }, `Could not goto memory location number ${previousMarker}`); // Extend Selection to Next Marker sf.ui.proTools.memoryLocationsGoto({ memoryLocationNumber: nextMarker, extendSelection: true, }, `Could not goto memory location number ${nextMarker}`); }); } selectBetweenMemoryLocations();
Let me know if it works for you.
Rock on!Ben Rubin @Ben_Rubin
Thank you, @kitch! On first tests, this is doing the job!
Kitch Membery @Kitch2023-11-09 21:25:32.102Z
Awesome! Thanks for reporting back, Ben!
Ben Rubin @Ben_Rubin
So happy to have this back. Combining it with Selecting the Edge Tool to edit automation is my jam.
Ben Rubin @Ben_Rubin
Also, just noticing the same issue with the built-in commands "Extend Selection to Previous/Next Marker". They are no longer working and kicking up a similar error.
Kitch Membery @Kitch2023-11-10 18:05:32.895Z
Hi @Ben_Rubin,
Thanks for the heads up. I'll take a look at this on Monday and see if there is a workaround. :-)
Ben Rubin @Ben_Rubin
as always, you rock!
- In reply toBen_Rubin⬆:
Kitch Membery @Kitch2023-11-13 23:33:33.559Z
Hi @Ben_Rubin,
As promised, here are a couple of scripts to extend the selection in Pro Tools.
Extend Selection to Next Marker script:
/** @param {function} callback */ function setupRestore(callback) { sf.ui.proTools.appActivateMainWindow(); sf.ui.proTools.invalidate(); const isMemoryLocationsWindowOpen = sf.ui.proTools.memoryLocationsWindow.exists; if (!isMemoryLocationsWindowOpen) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Memory Locations"], }, 'Could not click to open Memory Locations window'); sf.ui.proTools.memoryLocationsWindow.elementWaitFor({}, `Failed waiting for Memory Locations window`); } const oldMainCounterMode = getMainCounterMode(); // Set Main Counter to Samples sf.ui.proTools.mainCounterSetValue({ targetValue: "Samples" }); try { callback(); } finally { // Restore Memory Locations open state if (!isMemoryLocationsWindowOpen) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Memory Locations"], targetValue: "Disable", }); } // Restore main Counter mode sf.ui.proTools.mainCounterSetValue({ targetValue: oldMainCounterMode }); } } function getMainCounterMode() { const mainCounterValue = sf.ui.proTools.mainWindow.counterDisplay.mainCounter.value.invalidate().value; if (mainCounterValue.includes("|")) { return "Bars|Beats"; } else if (mainCounterValue.includes(":") && mainCounterValue.includes(".")) { return "Min:Secs"; } else if (mainCounterValue.split(":").length >= 3) { return "Timecode"; } else if (mainCounterValue.includes("+")) { return "Feet+Frames"; } else { return "Samples"; } } function getMemoryLocations() { sf.ui.proTools.appActivate(); sf.ui.proTools.mainWindow.elementRaise(); const memoryLocationsWindow = sf.ui.proTools.memoryLocationsWindow; memoryLocationsWindow.invalidate(); const memoryLocationsTable = memoryLocationsWindow.tables.allItems[1]; const headerTable = sf.ui.proTools.memoryLocationsWindow.tables.allItems[0]; const columnTitles = headerTable.children[0].children.map(e => e.children[0].title.value); const nameColumnIndex = columnTitles.indexOf("Name"); const numerationColumnIndex = columnTitles.indexOf("Numeration"); const mainCounterColumnIndex = columnTitles.indexOf("Main Counter"); const items = memoryLocationsTable.childrenByRole("AXRow").map(row => { const nameCell = row.children[nameColumnIndex]; const numberCell = row.children[numerationColumnIndex]; const mainCounterCell = row.children[mainCounterColumnIndex]; return { name: nameCell.children.first.title.value.replace("Selected. ", ""), number: Number(numberCell.children.first.title.value.replace("Selected. ", "")), // Filter out any Memory Locations that have no Main Counter Value (Important) mainCounterValue: mainCounterCell.children.first.title.value.replace("Selected. ", "") !== "" ? Number(mainCounterCell.children.first.title.value.replace("Selected. ", "")) : undefined, } }); return items; } function extendSelectionToNextMarker() { setupRestore(() => { const memoryLocations = getMemoryLocations(); const selectionEnd = Number(sf.ui.proTools.selectionGetInSamples().selectionEnd); let nextMarker = memoryLocations.filter(ml => ml.mainCounterValue >= selectionEnd).length > 0 ? memoryLocations .filter((ml) => ml.mainCounterValue >= selectionEnd) .sort((a, b) => b.mainCounterValue - a.mainCounterValue) .slice(-1)[0].number : undefined; if (!nextMarker) throw `No next marker found`; // Extend Selection to Next Marker sf.ui.proTools.memoryLocationsGoto({ memoryLocationNumber: nextMarker, extendSelection: true, }, `Could not goto memory location number ${nextMarker}`); }); } extendSelectionToNextMarker();
Extend Selection to Previous Marker
/** @param {function} callback */ function setupRestore(callback) { sf.ui.proTools.appActivateMainWindow(); sf.ui.proTools.invalidate(); const isMemoryLocationsWindowOpen = sf.ui.proTools.memoryLocationsWindow.exists; if (!isMemoryLocationsWindowOpen) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Memory Locations"], }, 'Could not click to open Memory Locations window'); sf.ui.proTools.memoryLocationsWindow.elementWaitFor({}, `Failed waiting for Memory Locations window`); } const oldMainCounterMode = getMainCounterMode(); // Set Main Counter to Samples sf.ui.proTools.mainCounterSetValue({ targetValue: "Samples" }); try { callback(); } finally { // Restore Memory Locations open state if (!isMemoryLocationsWindowOpen) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Memory Locations"], targetValue: "Disable", }); } // Restore main Counter mode sf.ui.proTools.mainCounterSetValue({ targetValue: oldMainCounterMode }); } } function getMainCounterMode() { const mainCounterValue = sf.ui.proTools.mainWindow.counterDisplay.mainCounter.value.invalidate().value; if (mainCounterValue.includes("|")) { return "Bars|Beats"; } else if (mainCounterValue.includes(":") && mainCounterValue.includes(".")) { return "Min:Secs"; } else if (mainCounterValue.split(":").length >= 3) { return "Timecode"; } else if (mainCounterValue.includes("+")) { return "Feet+Frames"; } else { return "Samples"; } } function getMemoryLocations() { sf.ui.proTools.appActivate(); sf.ui.proTools.mainWindow.elementRaise(); const memoryLocationsWindow = sf.ui.proTools.memoryLocationsWindow; memoryLocationsWindow.invalidate(); const memoryLocationsTable = memoryLocationsWindow.tables.allItems[1]; const headerTable = sf.ui.proTools.memoryLocationsWindow.tables.allItems[0]; const columnTitles = headerTable.children[0].children.map(e => e.children[0].title.value); const nameColumnIndex = columnTitles.indexOf("Name"); const numerationColumnIndex = columnTitles.indexOf("Numeration"); const mainCounterColumnIndex = columnTitles.indexOf("Main Counter"); const items = memoryLocationsTable.childrenByRole("AXRow").map(row => { const nameCell = row.children[nameColumnIndex]; const numberCell = row.children[numerationColumnIndex]; const mainCounterCell = row.children[mainCounterColumnIndex]; return { name: nameCell.children.first.title.value.replace("Selected. ", ""), number: Number(numberCell.children.first.title.value.replace("Selected. ", "")), // Filter out any Memory Locations that have no Main Counter Value (Important) mainCounterValue: mainCounterCell.children.first.title.value.replace("Selected. ", "") !== "" ? Number(mainCounterCell.children.first.title.value.replace("Selected. ", "")) : undefined, } }); return items; } function extendSelectionToPreviousMarker() { setupRestore(() => { const memoryLocations = getMemoryLocations(); const selectionStart = Number(sf.ui.proTools.selectionGetInSamples().selectionStart); let previousMarker = memoryLocations.filter(ml => ml.mainCounterValue <= selectionStart).length > 0 ? memoryLocations .filter((ml) => ml.mainCounterValue <= selectionStart) .sort((a, b) => a.mainCounterValue - b.mainCounterValue) .slice(-1)[0].number : undefined; if (!previousMarker) throw `No previous marker found`; // Extend Selection to Previous Marker sf.ui.proTools.memoryLocationsGoto({ memoryLocationNumber: previousMarker, extendSelection: true, }, `Could not goto memory location number ${previousMarker}`); }); } extendSelectionToPreviousMarker();
Ben Rubin @Ben_Rubin
Thanks a lot @Kitch. Much appreciated!
Kitch Membery @Kitch2023-11-17 23:09:59.913Z
My pleasure, @Ben_Rubin!
BTW Have you checked out "CS control" by @Chris_Shaw yet? If I recall correctly you were hoping to have a SoundFlow solution to replace Mulligan. Chris's app may be the solution you are looking for. :-)
Ben Rubin @Ben_Rubin
havent checked it out yet. actually very happy with mulligan. (i even endorse them on their website!) it adds way more functionality beyond just plugins. transport, prev/next track buttons, solo/mute/send/etc, master fader access.
that said, mulligan cannot control audiosuite plugins, so i am still intrigued and will try.
- In reply toBen_Rubin⬆:Christian Scheuer @chrscheuer2023-10-30 16:20:24.526Z
Hi Ben,
Are you displaying the timecode in your Memory Locations window? Can you share a screenshot of your memory locations window?
Ben Rubin @Ben_Rubin
No, not displaying timecode.
- In reply tochrscheuer⬆:
Kitch Membery @Kitch2023-11-09 22:07:28.847Z
Hi C,
FYI: Here is the possible solution for selecting between markers, ie the
sf.ui.proTools.memoryLocationsSelectBetween();
method.