No internet connection
  1. Home
  2. How to

scroll to track

By Ricardo Cutz @Ricardo_Cutz
    2022-07-20 17:19:38.099Z

    Hi @Kitch how to automate Scroll to Track, the scenario:

    • My Mix sessions are pretty organize over a pretty stable template where most food groups are contained in routing folder groups that names does not change over the time, ex: cDX A CHAIN, cFX A CHAIN, etc... I would like to create a series of macros, that would be mapped to stream deck buttons to easily jump around the session, using the "Scroll to Track" to jump acros those "anchor" points, i.e. the Routing Folders.

    What I tried using macros:

    1º Step: Click Menu item: Track > Scroll to Track
    2º Step: Wait to Ui Element: Appear > Pro Tools Confirmation Dialog
    3º Step: I try to enter track names in to the text field and here nothings happens
    There is "Text Field" and it seems to not be accessible by macros...
    I try several commands, like "Open & Select item in pop up menu" or "Set Value of Text Area", but none of them seems to work...

    There is any way to do that?
    Many thanks

    • 14 replies

    There are 14 replies. Estimated reading time: 21 minutes

    1. Ricardo,
      I use this function quite a bit with my packages.
      Just just change the value of trackName to the name of the track you wish to scroll to.
      You can also use an array of track names and the function will scroll to the first track in the array.

      // `trackName` is the name of the track to be scrolled to.
      // A single track name or an array of track names can be used
      // If an array is used then the function will scroll to the first name in the array
      
      const trackName  = "cDX A CHAIN"
      
      function csScrollToTrack(trackName) {
          var confirmationDialogWin = sf.ui.proTools.confirmationDialog;
          var scrollTrack = (typeof trackName == "object") ? trackName[0] : trackName;
      
          try {
              sf.ui.proTools.menuClick({ menuPath: ['Track', 'Scroll to Track...'] });
      
              confirmationDialogWin.elementWaitFor({ timeout: 100 });
          } catch (err) {
              sf.keyboard.press({ keys: "n", repetitions: 2 });
      
              sf.ui.proTools.menuClick({ menuPath: ['Track', 'Scroll to Track...'] });
      
              confirmationDialogWin.elementWaitFor({}, `Could not find Scroll to track Dialog`);
          }
      
          confirmationDialogWin.textFields.first.elementSetTextFieldWithAreaValue({
              value: scrollTrack,
              useMouseKeyboard: true
          });
      
          confirmationDialogWin.buttons.whoseTitle.is('OK').first.elementClick();
      
          confirmationDialogWin.elementWaitFor({ waitType: 'Disappear' });
      };
      
      
      
      /////////////
      // M A I N //
      /////////////
      
      csScrollToTrack(trackName)
      
      1. RRicardo Cutz @Ricardo_Cutz
          2022-07-20 21:30:31.850Z

          thanks! I don't know why it is not working, nothing happens...
          I copy everything, the only change I made, and I understand that it is what I need to change is in the first line

          const trackName  = "cDX A CHAIN"
          
          

          I change the name of the track correct? Or should I change after the MAIN?

          1. Yes, you only need to change the name of the track (trackName = )
            Hmm, it's working on my system.
            Which versions of PT and SF are you using?

            1. RRicardo Cutz @Ricardo_Cutz
                2022-07-20 21:53:55.761Z

                PT 2022.6, MacOs Catalina e SF 5.1.5

            2. In reply toChris_Shaw:
              Jack Green @Jack_Green
                2024-07-23 11:02:11.912Z

                I use something similar to this all over the place in several scripts.

                Im just wondering if there is a way to use error handling to output the trackName is attempted to scroll to in the event of a failure ?

                Thanks !

                1. There a two ways to do this. With most SF commands you can add an error message like in line 30 here:

                  // `trackName` is the name of the track to be scrolled to.
                  // A single track name or an array of track names can be used
                  // If an array is used then the function will scroll to the first name in the array
                  
                  const trackName  = "Audio 14"
                  
                  function csScrollToTrack(trackName) {
                      var confirmationDialogWin = sf.ui.proTools.confirmationDialog;
                      var scrollTrack = (typeof trackName == "object") ? trackName[0] : trackName;
                  
                      try {
                          sf.ui.proTools.menuClick({ menuPath: ['Track', 'Scroll to Track...'] });
                  
                          confirmationDialogWin.elementWaitFor({ timeout: 100 });
                      } catch (err) {
                          sf.keyboard.press({ keys: "n", repetitions: 2 });
                  
                          sf.ui.proTools.menuClick({ menuPath: ['Track', 'Scroll to Track...'] });
                  
                          confirmationDialogWin.elementWaitFor({}, `Could not find Scroll to track Dialog`);
                      }
                  
                      confirmationDialogWin.textFields.first.elementSetTextFieldWithAreaValue({
                          value: scrollTrack,
                          useMouseKeyboard: true
                      });
                  
                      confirmationDialogWin.buttons.whoseTitle.is('OK').first.elementClick();
                  
                      confirmationDialogWin.elementWaitFor({ waitType: 'Disappear' },`Could not find track named ${trackName}`);
                  };
                  
                  
                  
                  /////////////
                  // M A I N //
                  /////////////
                  
                  csScrollToTrack(trackName)
                  

                  The issue here is that the track scroll window will stay open.
                  To get around this you can add a second try/catch block around the confirmation dialog "OK" button press that will notify the track name then dismiss the scroll to track window with two esc key presses:

                  // `trackName` is the name of the track to be scrolled to.
                  // A single track name or an array of track names can be used
                  // If an array is used then the function will scroll to the first name in the array
                  
                  const trackName  = "Audio 14"
                  
                  function csScrollToTrack(trackName) {
                      var confirmationDialogWin = sf.ui.proTools.confirmationDialog;
                      var scrollTrack = (typeof trackName == "object") ? trackName[0] : trackName;
                  
                      try {
                          sf.ui.proTools.menuClick({ menuPath: ['Track', 'Scroll to Track...'] });
                  
                          confirmationDialogWin.elementWaitFor({ timeout: 100 });
                      } catch (err) {
                          sf.keyboard.press({ keys: "n", repetitions: 2 });
                  
                          sf.ui.proTools.menuClick({ menuPath: ['Track', 'Scroll to Track...'] });
                  
                          confirmationDialogWin.elementWaitFor({}, `Could not find Scroll to track Dialog`);
                      }
                  
                      confirmationDialogWin.textFields.first.elementSetTextFieldWithAreaValue({
                          value: scrollTrack,
                          useMouseKeyboard: true
                      });
                  
                      confirmationDialogWin.buttons.whoseTitle.is('OK').first.elementClick();
                      try{
                      confirmationDialogWin.elementWaitFor({ waitType: 'Disappear' });
                      }catch(err){
                          sf.interaction.notify({
                              title:`Could not find track "${trackName}"`
                          });
                          sf.keyboard.press({keys:"esc", repetitions:2})
                      }
                  };
                  
                  
                  
                  /////////////
                  // M A I N //
                  /////////////
                  
                  csScrollToTrack(trackName)
                  
                2. In reply toChris_Shaw:
                  Ian Bodzasi @Ian_Bodzasi
                    2024-07-30 23:46:05.463Z

                    This is great Chris, thanks!

                    1. In reply toChris_Shaw:
                      Ian Bodzasi @Ian_Bodzasi
                        2024-07-31 19:22:04.277Z

                        Hi Chris. Would there be a simple way to alter this to search for a match from a list of possible track names? For example, if I want to scroll to the drums it might be called Drums or it might be called Kit. A B3 might be B3 or it might be Organ, etc.

                        1. Chris Shaw @Chris_Shaw2024-08-01 15:26:01.566Z2024-08-01 18:21:01.737Z

                          This should do it. It will scroll to the first name it finds.
                          Read the comments to customize it how you wish:

                          /*
                              Edit the `trackNames` object as follows:
                          
                              - trackType - this is a general name for the track type. 
                              It's used to display error messages (line 21)
                          
                              - possibleNames - theses are the names that you are searching for
                          */
                          
                          const trackNames = {
                              trackType: "Drums",
                              possibleNames: ["Drums", "Kit"]
                          }
                          /**
                           * @param {object} trackNames
                           * @param {string} trackNames.trackType
                           * @param {string[]} trackNames.possibleNames
                           */
                          
                          function findMatchingTrackName(trackNames) {
                              const { trackType, possibleNames } = trackNames;
                              const sessionTracks = sf.ui.proTools.visibleTrackNames
                              const matchingTracks = sessionTracks.filter(t => possibleNames.includes(t));
                              if (matchingTracks.length == 0) {
                                  sf.interaction.notify({
                                      title: `No matching tracks were found for ${trackType}`
                                  })
                                  throw 0
                              }
                              return (matchingTracks[0])
                          }
                          
                          function csScrollToTrack(trackName) {
                              var confirmationDialogWin = sf.ui.proTools.confirmationDialog;
                              var scrollTrack = (typeof trackName == "object") ? trackName[0] : trackName;
                          
                              try {
                                  sf.ui.proTools.menuClick({ menuPath: ['Track', 'Scroll to Track...'] });
                          
                                  confirmationDialogWin.elementWaitFor({ timeout: 100 });
                              } catch (err) {
                                  sf.keyboard.press({ keys: "n", repetitions: 2 });
                          
                                  sf.ui.proTools.menuClick({ menuPath: ['Track', 'Scroll to Track...'] });
                          
                                  confirmationDialogWin.elementWaitFor({}, `Could not find Scroll to track Dialog`);
                              }
                          
                              confirmationDialogWin.textFields.first.elementSetTextFieldWithAreaValue({
                                  value: scrollTrack,
                                  useMouseKeyboard: true
                              });
                          
                              confirmationDialogWin.buttons.whoseTitle.is('OK').first.elementClick();
                              try {
                                  confirmationDialogWin.elementWaitFor({ waitType: 'Disappear' });
                              } catch (err) {
                                  sf.interaction.notify({
                                      title: `Could not find track "${trackName}"`
                                  });
                                  sf.keyboard.press({ keys: "esc", repetitions: 2 })
                              }
                          };
                          
                          /////////////
                          // M A I N //
                          /////////////
                          
                          const trackToScrollTo = findMatchingTrackName(trackNames)
                          
                          csScrollToTrack(trackToScrollTo)
                          
                          1. Ian Bodzasi @Ian_Bodzasi
                              2024-08-01 16:49:35.262Z

                              Thanks Chris! I'm getting the follow error back when running it...

                              01.08.2024 12:48:09.26 [EditorWindow:Renderer]: Active Focus Container: commandsPage/folders Line 33963 file:///Applications/SoundFlow.app/Contents/Helpers/SoundFlow.app/Contents/Resources/app.asar/dist/editor.js
                              01.08.2024 12:48:11.91 [Backend]: #StreamDeck: deck-button-click -> Go to Drums

                              Command: Go to Drums [user:cky4td7z60002qk10y9y2hr59:clzbi6xx40000zm10skfjyofo]

                              01.08.2024 12:48:11.95 [Backend]: JavaScript error with InnerException: Newtonsoft.Json.JsonSerializationException: Error converting value "AudioTrack" to type 'SoundFlow.Shortcuts.Automation.Actions.PtAppTrackType'. Path 'track_list[0].type', line 10, position 25.
                              ---> System.ArgumentException: Requested value 'AudioTrack' was not found.
                              at Newtonsoft.Json.Utilities.EnumUtils.ParseEnum(Type enumType, NamingStrategy namingStrategy, String value, Boolean disallowNumber) + 0x4e0
                              at Newtonsoft.Json.Converters.StringEnumConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) + 0xfc
                              --- End of inner exception stack trace ---
                              at Newtonsoft.Json.Converters.StringEnumConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) + 0x2e0
                              at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue) + 0x148
                              at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target) + 0x11c
                              at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id) + 0x77c
                              at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) + 0x2e4
                              at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) + 0xb8
                              at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id) + 0x36c
                              at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id) + 0x194
                              at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) + 0xdc
                              at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target) + 0x158
                              at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id) + 0x77c
                              at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) + 0x2e4
                              at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) + 0xb8
                              at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent) + 0x288
                              at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType) + 0x100
                              at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) + 0x94
                              at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings) + 0x38
                              at SoundFlow.Shortcuts.Ax.AxNodes.PtTrackList.d__5.MoveNext() + 0x250
                              --- End of stack trace from previous location ---
                              at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x24
                              at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task) + 0x100
                              at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task, ConfigureAwaitOptions) + 0x68
                              at SoundFlow.Shortcuts.Ax.AxNodes.PtAppList1.<GetItems>d__8.MoveNext() + 0xdc --- End of stack trace from previous location --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x24 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task) + 0x100 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task, ConfigureAwaitOptions) + 0x68 at SoundFlow.Shortcuts.Ax.AxNodes.PtAppList1.<>c__DisplayClass6_0.<<get_allItems>b__0>d.MoveNext() + 0xcc
                              --- End of stack trace from previous location ---
                              at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x24
                              at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task) + 0x100
                              at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task, ConfigureAwaitOptions) + 0x68
                              at Nito.AsyncEx.Synchronous.TaskExtensions.WaitAndUnwrapException[TResult](Task1 task) + 0x44 at System.Threading.Tasks.ContinuationResultTaskFromResultTask2.InnerInvoke() + 0x54
                              at System.Threading.ExecutionContext.RunInternal(ExecutionContext, ContextCallback, Object) + 0x8c
                              --- End of stack trace from previous location ---
                              at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x24
                              at System.Threading.ExecutionContext.RunInternal(ExecutionContext, ContextCallback, Object) + 0xdc
                              at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task&, Thread) + 0xa0
                              --- End of stack trace from previous location ---
                              at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + 0x24
                              at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task) + 0x100
                              at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task, ConfigureAwaitOptions) + 0x68
                              at Nito.AsyncEx.Synchronous.TaskExtensions.WaitAndUnwrapException[TResult](Task1 task) + 0x44 at Nito.AsyncEx.AsyncContext.Run[TResult](Func1 action) + 0xe0
                              at sfbackend!+0x282a0f0
                              at System.Reflection.DynamicInvokeInfo.Invoke(Object, IntPtr, Object[], BinderBundle, Boolean) + 0xcc
                              !! Command Error: Go to Drums [user:cky4td7z60002qk10y9y2hr59:clzbi6xx40000zm10skfjyofo]:
                              Error: Error converting value "AudioTrack" to type 'SoundFlow.Shortcuts.Automation.Actions.PtAppTrackType'. Path 'track_list[0].type', line 10, position 25.
                              (Go to Drums line 22)

                              01.08.2024 12:48:11.95 [Backend]: << Command: Go to Drums [user:cky4td7z60002qk10y9y2hr59:clzbi6xx40000zm10skfjyofo]

                              1. Which version of PT are you running?

                                1. I re-edited the code above - give it a try.

                                  1. Ian Bodzasi @Ian_Bodzasi
                                      2024-08-01 19:40:25.170Z

                                      That's perfect, thanks so much Chris! I'm on 2023.6 here. Had some GUI issues with 2023.9, meters acting weird, etc, so I've been staying put where things are stable. New features are starting to add up though, it might be time for an update. Out of curiosity, what's the most recent version you're finding stable there?

                                      1. I'm doing fine with 2024.6 / Ventura