No internet connection
  1. Home
  2. How to

Identical Marker Navigation

By Jeremy Bowker @Jeremy_Bowker
    2021-03-17 18:13:17.415Z

    Has anyone sorted out a way to tab back and forth through identically named markers? (So instead of "go to next marker" it would be "go to next marker of this name" and/or "go to the next marker that contains this text as part of the marker name".) Thoughts?

    • 23 replies

    There are 23 replies. Estimated reading time: 24 minutes

    1. samuel henriques @samuel_henriques
        2021-03-17 23:32:12.980Z

        Hey @Jeremy_Bowker,

        This will jump to the next marker containing "yadda" on marker's name.
        It only works if you'r using sort by time, since it's looking for the next marker relative to the timeline location.

        here you go:

        sf.ui.proTools.appActivate();
        
        function setValue(mainCounterValue) {
            sf.ui.proTools.mainCounterSetValue({ targetValue: mainCounterValue })
        }
        
        function reSetMainCounter(mainCounter) {
        
            /// match Bars Beats .  
            mainCounter.match(/\|/) ? setValue("Bars|Beats") : null
        
            // match Min Secs .    
            mainCounter.match(/\d+:\d+\.+\d/) ? setValue("Min:Secs") : null
        
            // match Time code
            mainCounter.match(/\d+:\d+:\d+:\d/) ? setValue("Timecode") : null
        
            // match Feet+Frames
            mainCounter.match(/\+/) ? setValue("Feet+Frames") : null
        
            // match Samples.    
            mainCounter.match(/[^0-9]/) == null ? setValue("Samples") : null
        }
        
        
        
        function findLocationNameMatch(locationName) {
        
            const mainCounter = sf.ui.proTools.getCurrentTimecode().stringValue
            sf.ui.proTools.mainCounterSetValue({ targetValue: "Samples" })
            const userSelection = sf.ui.proTools.selectionGetInSamples()
        
        
            const memoryLocations = sf.proTools.memoryLocationsFetchFromGui().collection.list.filter(x =>
                x.mainCounterValue > userSelection.selectionStart &&
                x.name.match(locationName));
        
            try {
                sf.ui.proTools.memoryLocationsGoto({
                    memoryLocationNumber: memoryLocations[0].number
                });
            } catch (err) { log(`End of location markers containing:\n${locationName}`) }
        
            reSetMainCounter(mainCounter)
        }
        
        
        
        findLocationNameMatch("yadda")
        
        
        1. JJeremy Bowker @Jeremy_Bowker
            2021-03-18 16:48:55.644Z

            @samuel_henriques this is fantastic! Thank you for sharing.

            I wonder if there could be two commands:

            1. To define a global variable or at least store in a clipboard to determine the desired text. (Side question: Does SF have it's own clipboard that wouldn't be changed by clipboard changes in PT? And do global variables exist in SF?)
            2. To use the text that has already been defined (to sequentially go thru each of the markers with the same name).

            Here's a work in progress of "1." (It's just what you did with the addition of a prompt for the text we're searching for.)

            sf.ui.proTools.appActivate();
            
            function setValue(mainCounterValue) {
                sf.ui.proTools.mainCounterSetValue({ targetValue: mainCounterValue })
            }
            
            function reSetMainCounter(mainCounter) {
            
                /// match Bars Beats .  
                mainCounter.match(/\|/) ? setValue("Bars|Beats") : null
            
                // match Min Secs .    
                mainCounter.match(/\d+:\d+\.+\d/) ? setValue("Min:Secs") : null
            
                // match Time code
                mainCounter.match(/\d+:\d+:\d+:\d/) ? setValue("Timecode") : null
            
                // match Feet+Frames
                mainCounter.match(/\+/) ? setValue("Feet+Frames") : null
            
                // match Samples.    
                mainCounter.match(/[^0-9]/) == null ? setValue("Samples") : null
            }
            
            
            
            function findLocationNameMatch(locationName) {
            
                const mainCounter = sf.ui.proTools.getCurrentTimecode().stringValue
                sf.ui.proTools.mainCounterSetValue({ targetValue: "Samples" })
                const userSelection = sf.ui.proTools.selectionGetInSamples()
            
            
                const memoryLocations = sf.proTools.memoryLocationsFetchFromGui().collection.list.filter(x =>
                    x.mainCounterValue > userSelection.selectionStart &&
                    x.name.match(locationName));
            
                try {
                    sf.ui.proTools.memoryLocationsGoto({
                        memoryLocationNumber: memoryLocations[0].number
                    });
                } catch (err) { log(`End of location markers containing:\n${locationName}`) }
            
                reSetMainCounter(mainCounter)
            }
            
            var markerName = prompt(`Please Enter Marker Name`);
            sf.clipboard.setText({ text: markerName });
            
            
            findLocationNameMatch(markerName)
            
            
            1. samuel henriques @samuel_henriques
                2021-03-18 17:32:21.957Z

                cool, happy to help.
                Are you thinking about something like ctrl+script to get a prompt and then use script with the saved word?

                1. JJeremy Bowker @Jeremy_Bowker
                    2021-03-18 18:05:57.253Z

                    Yes! Exactly. I.e. you're prompted & then type "cut" and then you can cycle thru all of the markers named "cut". And then type "VFX update" and then cycle thru all of the VFX update markers. (It'd be great to easliy change the text that you're looking for.)

                    1. samuel henriques @samuel_henriques
                        2021-03-18 18:12:05.336Z

                        I'm pretty sure you are going to ask next how to go to the previous marker😂😂

                        1. samuel henriques @samuel_henriques
                            2021-03-18 18:41:13.802Z

                            Just made it work backwards, might be two cool buttons next & previous with the same globalState. let me know how it's working

                            1. samuel henriques @samuel_henriques
                                2021-03-18 19:32:45.152Z

                                BTW if you only use timecode on main counter, I think I can make the script without having to change main counter to samples, removing some steps to get more speed.

                                1. JJeremy Bowker @Jeremy_Bowker
                                    2021-03-18 21:11:25.643Z

                                    Very exciting! Which is the most up to date one that I should try out? Thanks again!

                                  • JJeremy Bowker @Jeremy_Bowker
                                      2021-03-18 21:11:57.174Z

                                      Haha! You read my mind

                                      1. samuel henriques @samuel_henriques
                                          2021-03-18 22:15:25.372Z

                                          So... This is go to next memory location:

                                          
                                          
                                          function setValue(mainCounterValue) {
                                              sf.ui.proTools.mainCounterSetValue({ targetValue: mainCounterValue })
                                          }
                                          
                                          function reSetMainCounter(mainCounter) {
                                          
                                              /// match Bars Beats .  
                                              mainCounter.match(/\|/) ? setValue("Bars|Beats") : null
                                          
                                              // match Min Secs .    
                                              mainCounter.match(/\d+:\d+\.+\d/) ? setValue("Min:Secs") : null
                                          
                                              // match Time code
                                              mainCounter.match(/\d+:\d+:\d+:\d/) ? setValue("Timecode") : null
                                          
                                              // match Feet+Frames
                                              mainCounter.match(/\+/) ? setValue("Feet+Frames") : null
                                          
                                              // match Samples.    
                                              mainCounter.match(/[^0-9]/) == null ? setValue("Samples") : null
                                          }
                                          
                                          
                                          
                                          function goToNextMemoryLocation(locationName) {
                                          
                                              const mainCounter = sf.ui.proTools.getCurrentTimecode().stringValue
                                              sf.ui.proTools.mainCounterSetValue({ targetValue: "Samples" })
                                              const userSelection = sf.ui.proTools.selectionGetInSamples()
                                          
                                          
                                              const memoryLocations = sf.proTools.memoryLocationsFetchFromGui().collection['list'].filter(x =>
                                                  x.mainCounterValue > userSelection.selectionStart &&
                                                  x.name.match(locationName));
                                          
                                              try {
                                                  sf.ui.proTools.memoryLocationsGoto({
                                                      memoryLocationNumber: memoryLocations[0].number
                                                  });
                                              } catch (err) { log(`End of location markers containing:\n${locationName}`) }
                                          
                                              reSetMainCounter(mainCounter)
                                          }
                                          
                                          
                                          sf.ui.proTools.appActivate();
                                          
                                          const ctrlIsEnabled = event.keyboardState.hasControl
                                          
                                          if (globalState.markerName == undefined || ctrlIsEnabled) {
                                          
                                              globalState.markerName = prompt(`Please Enter Marker Name`);
                                          
                                          } else {
                                          
                                              goToNextMemoryLocation(globalState.markerName)
                                          }
                                          

                                          this goes to previous memory location:

                                          
                                          
                                          function setValue(mainCounterValue) {
                                              sf.ui.proTools.mainCounterSetValue({ targetValue: mainCounterValue })
                                          }
                                          
                                          function reSetMainCounter(mainCounter) {
                                          
                                              /// match Bars Beats .  
                                              mainCounter.match(/\|/) ? setValue("Bars|Beats") : null
                                          
                                              // match Min Secs .    
                                              mainCounter.match(/\d+:\d+\.+\d/) ? setValue("Min:Secs") : null
                                          
                                              // match Time code
                                              mainCounter.match(/\d+:\d+:\d+:\d/) ? setValue("Timecode") : null
                                          
                                              // match Feet+Frames
                                              mainCounter.match(/\+/) ? setValue("Feet+Frames") : null
                                          
                                              // match Samples.    
                                              mainCounter.match(/[^0-9]/) == null ? setValue("Samples") : null
                                          }
                                          
                                          
                                          
                                          function goToPreviousMemoryLocator(locationName) {
                                          
                                              const mainCounter = sf.ui.proTools.getCurrentTimecode().stringValue
                                              sf.ui.proTools.mainCounterSetValue({ targetValue: "Samples" })
                                              const userSelection = sf.ui.proTools.selectionGetInSamples()
                                          
                                          
                                              const memoryLocations = sf.proTools.memoryLocationsFetchFromGui().collection['list'].filter(x =>
                                                  x.mainCounterValue < userSelection.selectionStart &&
                                                  x.name.match(locationName)).reverse();
                                          
                                              try {
                                                  sf.ui.proTools.memoryLocationsGoto({
                                                      memoryLocationNumber: memoryLocations[0].number
                                                  });
                                              } catch (err) { log(`End of location markers containing:\n${locationName}`) }
                                          
                                              reSetMainCounter(mainCounter)
                                          }
                                          
                                          
                                          sf.ui.proTools.appActivate();
                                          
                                          const ctrlIsEnabled = event.keyboardState.hasControl
                                          
                                          if (globalState.markerName == undefined || ctrlIsEnabled) {
                                          
                                              globalState.markerName = prompt(`Please Enter Marker Name`);
                                          
                                          } else {
                                          
                                              goToPreviousMemoryLocator(globalState.markerName)
                                          }
                                          

                                          i'm still working on a version that doesn't need to change te main counter, I'm sure I'm not seeing something properly, but I'll get there.

                                          let me know how these are working for you

                                          1. JJeremy Bowker @Jeremy_Bowker
                                              2021-03-19 03:31:04.317Z

                                              This is so impressive! Yeah, it's working like a charm. Thank you!

                                              1. samuel henriques @samuel_henriques
                                                  2021-03-19 10:17:36.644Z

                                                  Thank you it's been great fun! Thank you to Master @Kitch for the help with this one, I got really stuck with my scripting bases.

                                                  here you go to NEXT:

                                                  
                                                  
                                                  function getNextMatchedMemoryLocation(locationName) {
                                                  
                                                      let mainCounter = sf.ui.proTools.getCurrentTimecode().stringValue
                                                  
                                                      //  if main counter is bars beats, remove last three digits, since memory locations list ignores them
                                                      mainCounter.match(/\|/) ? mainCounter = mainCounter.slice(0, -3) : null
                                                  
                                                      let cleanMainCounter = Number(mainCounter.replace(/[^0-9]/g, '').trim())
                                                  
                                                      const memoryLocations = sf.proTools.memoryLocationsFetch().collection["list"].filter(x =>
                                                          Number(x.mainCounterValue.replace(/[^0-9]/g, '').trim()) > cleanMainCounter &&
                                                          x.name.match(locationName)
                                                      );
                                                  
                                                      try {
                                                          sf.ui.proTools.memoryLocationsGoto({
                                                              memoryLocationNumber: memoryLocations[0].number
                                                          });
                                                      } catch (err) { log(`End of location markers containing:\n${locationName}`) }
                                                  }
                                                  
                                                  sf.ui.proTools.appActivate();
                                                  
                                                  const ctrlIsEnabled = event.keyboardState.hasControl
                                                  
                                                  if (globalState.markerName == undefined || ctrlIsEnabled) {
                                                  
                                                      globalState.markerName = prompt(`Please Enter Marker Name`);
                                                  
                                                  } else {
                                                  
                                                      getNextMatchedMemoryLocation(globalState.markerName)
                                                  }
                                                  

                                                  and go to previous:

                                                  
                                                  
                                                  function getPreviousMatchedMemoryLocation(locationName) {
                                                  
                                                      let mainCounter = sf.ui.proTools.getCurrentTimecode().stringValue
                                                  
                                                      //  if main counter is bars beats, remove last three digits, since memory locations list ignores them
                                                      mainCounter.match(/\|/) ? mainCounter = mainCounter.slice(0, -3) : null
                                                  
                                                      let cleanMainCounter = Number(mainCounter.replace(/[^0-9]/g, '').trim())
                                                  
                                                      const memoryLocations = sf.proTools.memoryLocationsFetch().collection["list"].filter(x =>
                                                          Number(x.mainCounterValue.replace(/[^0-9]/g, '').trim()) < cleanMainCounter &&
                                                          x.name.match(locationName)
                                                      ).reverse();
                                                  
                                                      try {
                                                          sf.ui.proTools.memoryLocationsGoto({
                                                              memoryLocationNumber: memoryLocations[0].number
                                                          });
                                                      } catch (err) { log(`End of location markers containing:\n${locationName}`) }
                                                  }
                                                  
                                                  sf.ui.proTools.appActivate();
                                                  
                                                  const ctrlIsEnabled = event.keyboardState.hasControl
                                                  
                                                  if (globalState.markerName == undefined || ctrlIsEnabled) {
                                                  
                                                      globalState.markerName = prompt(`Please Enter Marker Name`);
                                                  
                                                  } else {
                                                  
                                                      getPreviousMatchedMemoryLocation(globalState.markerName)
                                                  }
                                                  

                                                  Let me know how it goes.

                                                  1. JJeremy Bowker @Jeremy_Bowker
                                                      2021-03-19 16:11:12.099Z

                                                      Yup! Both work very well. So great!

                                          2. samuel henriques @samuel_henriques
                                              2021-03-18 18:07:31.049Z

                                              here you go, just replace the line:

                                              findLocationNameMatch(markerName)
                                              

                                              with:

                                              const ctrlIsEnabled = event.keyboardState.hasControl
                                              
                                              if (globalState.markerName == undefined || ctrlIsEnabled) {
                                              
                                                  globalState.markerName = prompt(`Please Enter Marker Name`);
                                              
                                              } else {
                                              
                                                  findLocationNameMatch(globalState.markerName)
                                              }
                                              
                                              1. be aware, global state will be cleared id you restart soundFlow.
                                              2. if you are using a keyboard shortcut for this you need to assign both the shortcut and the shortcut+ctrl to be able to use this functionality. If you are using a surface, you don't need this, just press ctrl and the surface button.

                                              hope this helps

                                              1. samuel henriques @samuel_henriques
                                                  2021-03-18 18:10:29.992Z

                                                  I'm working on a version that doesn't need to change the main counter, but ran into some problems. But it works well if you only use timecode, if you want to try its here:

                                                  1. JJeremy Bowker @Jeremy_Bowker
                                                      2023-12-19 21:06:12.054Z

                                                      This has been an amazing tool. Thanks again. I'm running into issues with this not working with PT 2023.12.0. Please let me know if you have any brilliant ideas.

                                                      All the best,
                                                      Jeremy

                                                      1. samuel henriques @samuel_henriques
                                                          2023-12-20 17:52:56.441Z

                                                          Hello @Jeremy_Bowker here's a version for PT 2023.12. The other scripts should be working soon.

                                                          function getMemoryLocationsList() {
                                                          
                                                              let memLocList = []
                                                              sf.ui.proTools.memoryLocationsEnsureWindow({
                                                                  action: () => {
                                                                      const memLocWin = sf.ui.proTools.memoryLocationsWindow.tables.whoseTitle.is("Memory Locations")
                                                                      const memLocColumns = memLocWin.first.children.whoseRole.is("AXColumn").first.children.whoseRole.is("AXRow").first.children.whoseRole.is("AXCell").map(mem =>
                                                                          mem.children.whoseRole.is("AXStaticText").first.title.value
                                                                              .replace("Numeration", "Number")
                                                                              .replace("Marker Name", "Name")
                                                                              .replace("Main Counter", "MainCounterValue")
                                                                      );
                                                          
                                                                      memLocWin.allItems[1].children.whoseRole.is("AXRow").map(r => {
                                                                          let obj = {};
                                                                          r.children.whoseRole.is("AXCell").map((cell, i) => {
                                                          
                                                                              obj["isSelected"] = false
                                                                              const cellValue = cell.children.whoseRole.is("AXStaticText").first.value.value
                                                          
                                                                              if (cellValue.startsWith("Selected. ")) {
                                                                                  obj["isSelected"] = true
                                                                              }
                                                          
                                                                              obj[memLocColumns[i]] = cellValue.replace("Selected. ", "")
                                                                          })
                                                                          memLocList.push(obj)
                                                                      })
                                                          
                                                                  },
                                                                  restoreWindowOpenState: true
                                                              })
                                                              return memLocList
                                                          };
                                                          
                                                          
                                                          /**
                                                           * @param {'Previous'|'Next'} direction
                                                           * @param {string} locationName
                                                           */
                                                          function getMatchedMemoryLocation(locationName, direction = 'Next') {
                                                          
                                                              let mainCounter = sf.ui.proTools.getCurrentTimecode().stringValue
                                                          
                                                          
                                                              //  if main counter is bars beats, remove last three digits, since memory locations list ignores them
                                                              mainCounter.match(/\|/) ? mainCounter = mainCounter.slice(0, -3) : null
                                                          
                                                              let cleanMainCounter = Number(mainCounter.replace(/[^0-9]/g, '').trim())
                                                          
                                                              let memoryLocations
                                                              if (direction === "Previous") {
                                                          
                                                                  memoryLocations = getMemoryLocationsList().filter(memLoc =>
                                                                      Number(memLoc['MainCounterValue'].replace(/[^0-9]/g, '').trim()) < cleanMainCounter &&
                                                                      memLoc['Name'].toLowerCase().includes(locationName.toLowerCase())
                                                                  ).reverse()
                                                          
                                                          
                                                              } else {
                                                                  memoryLocations = getMemoryLocationsList().filter(memLoc =>
                                                                      Number(memLoc['MainCounterValue'].replace(/[^0-9]/g, '').trim()) > cleanMainCounter &&
                                                                      memLoc['Name'].toLowerCase().includes(locationName.toLowerCase())
                                                                  )
                                                              }
                                                          
                                                              try {
                                                                  sf.ui.proTools.memoryLocationsGoto({
                                                                      memoryLocationNumber: +memoryLocations[0]['Number']
                                                                  });
                                                              } catch (err) { log(`End of location markers containing:\n${locationName}`) }
                                                          }
                                                          
                                                          
                                                          sf.ui.proTools.appActivate();
                                                          
                                                          const ctrlIsEnabled = event.keyboardState.hasControl
                                                          
                                                          if (globalState.markerName == undefined || ctrlIsEnabled) {
                                                          
                                                              globalState.markerName = prompt(`Please Enter Marker Name`);
                                                          
                                                          } else {
                                                          
                                                              getMatchedMemoryLocation(globalState.markerName)
                                                          }
                                                          

                                                          This by default will go to the next memory locator

                                                          to go to previous replace the last line
                                                          getMatchedMemoryLocation(globalState.markerName)
                                                          with
                                                          getMatchedMemoryLocation(globalState.markerName, 'Previous')

                                                          1. JJeremy Bowker @Jeremy_Bowker
                                                              2023-12-20 17:59:30.541Z

                                                              Ammmmazing!!! Thank you!!! Trying now

                                                • J
                                                  Jeremy Bowker @Jeremy_Bowker
                                                    2025-05-07 21:17:17.500Z

                                                    Hi @samuel_henriques !

                                                    This script is SO helpful. Thanks again.

                                                    With PT 2024.10.2 I'm now getting an error and the log says "TypeError: Cannot read property 'replace' of undefined". Any thoughts as to what this is?

                                                    Thanks for your time!
                                                    Jeremy

                                                    1. samuel henriques @samuel_henriques
                                                        2025-05-07 23:51:17.124Z

                                                        Hello Jeremy,
                                                        No problem here also on PT 2024.10.2. and Sequoia 15.4
                                                        But I'm noticing pro tools is getting some personality so I keep quitting to fix some issues.
                                                        Is this constant on every session?

                                                        1. JJeremy Bowker @Jeremy_Bowker
                                                            2025-05-08 00:40:08.320Z

                                                            Hi!

                                                            I've restarted and tried other sessions and the problem persists. I'll keep investigating. Thanks for your fast reply.

                                                            -Jeremy

                                                        2. J
                                                          Jeremy Bowker @Jeremy_Bowker
                                                            2025-05-08 00:45:59.609Z

                                                            While this is happening, the memory locations window opens and closes extremely fast. I wonder if a "wait" is necessary somewhere (on this computer)...

                                                            1. samuel henriques @samuel_henriques
                                                                2025-05-08 08:49:04.488Z

                                                                Mine does the same if its closed. You get same problem if its open?
                                                                Try to send a screen recording while its failing, I might spot something