Hi! I have a question regarding decks and folders within decks.
Is it possible that, if I have a Pro Tools deck, with the corresponding sub decks that I created (eg: mixing, vocal editing, etc) and I set a trigger for said Pro Tools deck to show when the app is focused, that it remembers which sub-deck I was before and it returns to that sub-deck instead of to the general PT one?
For example: I'm mixing (and my mixing sub-deck is showing) and then I go to Google Chrome, so other deck appears. Then I come back to PT to keep mixing and I want my mixing deck to show. But if I was editing, I would want my editing deck to show.
I'm using the Android app.
Thanks!
Linked from:
- Dustin Harris @Dustin_Harris
Hi Irene!
I don't have an android device (I use streamdeck) so I'm hoping someone can chime in and adapt these scripts to a mobile device for you... but the idea is you use a script to load the deck, and a script to store the deck:
create a new script in the same package folder as your pro tools deck, and assign an application trigger to it, with the application pro tools, and the event as 'Activate':
function showProToolsDeck() { //Don't run if no device connected if (sf.devices.streamDeck.deviceCount === 0) return; /* replace the quotes with your own commandID by clicking on your default pro tools deck and selecting Command -> Copy Command ID from the menu bar in soundflow */ const defaultProToolsDeck = "package:xxxxxxxxxxxxxx"; const proToolsDeckToOpen = globalState['lastProToolsDeck'] ? globalState['lastProToolsDeck'] : defaultProToolsDeck; try { //get Pro Tools deck const deckToOpen = sf.decks.get(proToolsDeckToOpen); //show the deck on device deckToOpen.showOnStreamDeck(); } catch (err) { log('Check device Connection') throw 0 } } showProToolsDeck();
and then in a separate script, use this with an application trigger, with pro tools as the application and the event as 'Deactivate':
function storeCurrentProToolsDeck() { //Don't run if no device connected if (sf.devices.streamDeck.deviceCount === 0) return; //store current deck on device to globalState globalState['lastProToolsDeck'] = sf.devices.streamDeck.firstDevice.currentDeckInstance.deck.deckId; } storeCurrentProToolsDeck();
Hope this helps!
- IIrene Genova @irene_genova
Thanks a lot! I hope someone can help with the coding for Android :)
- TIn reply toirene_genova⬆:Torsten Zumhof @Torsten_Zumhof
Hey Dustin,
this is a nice one, but I have two Stream Decks, is it possible to adapt this for this use case ?cheers
Torsten
Dustin Harris @Dustin_Harris
I'll give it a shot. I don't have multiple stream decks to test with, but this might work in theory... although this is completely untested :)
function showMultipleDecks() { //Don't run if no device connected if (sf.devices.streamDeck.deviceCount === 0) return; /* replace the quotes with your own commandIDs by clicking on the desired decks and selecting Command -> Copy Command ID from the menu bar in soundflow */ const defaultDeckIdArray = [ "package:xxxxxxxxxxxxxx", "package:xxxxxxxxxxxxxx", ]; const deckIdArray = globalState['storedDeckIdArray'] ? globalState['storedDeckIdArray'] : defaultDeckIdArray; try { const streamDecks = sf.devices.streamDeck.connectedDevices deckIdArray.forEach((deckId, index) => { //get Pro Tools deck const deckToOpen = sf.decks.get(deckId); //show the deck on device deckToOpen.deck.showOnStreamDeck({ device: streamDecks[index]}); }) } catch (err) { log('Check device Connection') throw 0 } } showMultipleDecks();
and
function storeMutipleDecks() { //Don't run if no device connected if (sf.devices.streamDeck.deviceCount === 0) return; //store current decks on devices to globalState globalState['storedDeckIdArray'] = sf.devices.streamDeck.connectedDevices.map(deck => deck.currentDeckInstance.invalidate().deck.deckId) } storeMutipleDecks();
use the same directions as above for the triggers. Let me know if it works hahahaha :)
- TTorsten Zumhof @Torsten_Zumhof
Hey Dustin,
thanks for the try.
Now when I return to Pro Tools the default deck array is always shown and not the decks wich were shown at least. In my case the left stream deck is always the "Views Deck" and never changes ... the right stream deck brings up different decks for editing, audiosuite etc. So I can only see that the right deck is not going back to the last used deck, cause the left always stays the same.
Dustin Harris @Dustin_Harris
I discovered an error in my code that I fixed with an edit, but maybe you were able to it before I fixed it. Double check line 21 in the script above, it should be
deckIdArray
and notdefaulDeckIdArray
- TTorsten Zumhof @Torsten_Zumhof
It's always going back to the default deck array.
Dustin Harris @Dustin_Harris
I've make a slight changes to the scripts above. Please grab both, assign the correct triggers (and remove any other deck triggers related to pro tools) and try them again... let me know what happens :)
Dustin
- TTorsten Zumhof @Torsten_Zumhof
Now when I switch to safari my safari decks are shown (left) and the favourite pages (Right), than when I return to Protools nothing happens...?? The two safari Decks are still shown. Same with finder decks. Just to get you right the first script looks like this :
function showMultipleDecks() {
//Don't run if no device connected if (sf.devices.streamDeck.deviceCount === 0) return; /* replace the quotes with your own commandIDs by clicking on the desired decks and selecting Command -> Copy Command ID from the menu bar in soundflow */ const defaultDeckIdArray = [ "user:clltjbi3a0000hr10jy7relrl:cm1gcoq5i0002kj10mcbsc448", "user:clltjbi3a0000hr10jy7relrl:clltjmbp60003hr100e6s0li4", ]; const deckIdArray = globalState['storedDeckIdArray'] ? globalState['storedDeckIdArray'] : defaultDeckIdArray; try { const streamDecks = sf.devices.streamDeck.connectedDevices deckIdArray.forEach((deckId, index) => { //get Pro Tools deck const deckToOpen = sf.decks.get(deckId); //show the deck on device deckToOpen.deck.showOnStreamDeck({ device: streamDecks[index]}); }) } catch (err) { log('Check device Connection') throw 0 }
}
showMultipleDecks();
Dustin Harris @Dustin_Harris
heyya @Torsten_Zumhof , try this for the
storeMultpleDecks()
function instead (application trigger Pro Tools on event 'Deactivate')... again I'm just guessing here because I can't test against the same configuration you have...function storeMutipleDecks() { //Don't run if no device connected if (sf.devices.streamDeck.deviceCount === 0) return; //store current decks on devices to globalState const streamdeckDeckIds = sf.devices.streamDeck.connectedDevices .filter(d => d.deviceType == "StreamDeck" && d.currentDeckInstance.deck.deckId) .map(deck => deck.currentDeckInstance.deck.deckId) //end streamdeckDeckIds globalState['storedDeckIdArray'] = streamdeckDeckIds; } storeMutipleDecks();
- TTorsten Zumhof @Torsten_Zumhof
Hey Dustin,
I restart Soundflow and stop all running commands than :
- activate Pro Tools by clicking in the editor window - the default deck array is shown.
- choosing audiosuite on one Deck - the audiosuite array is shown.
- clicking on the desktop -- the finder array is shown.
- clicking back into the Pro Tools editor window - nothing happens, the finder array is still shown.
Sorry for bothering you, but it would be nice to get this working.
cheers
TorstenDustin Harris @Dustin_Harris
Now I wonder if the Finder Deck activate script is happening -before- the pro tools deactivate script, which would store the finder deck in the
globalState['storedDeckIdArray']
property...try this as a test, do whatever you do in protools, switch your decks, then go to your finder deck (stream decks should now show the finder deck), then run this:
function showStoredAndCurrentDecks() { const currentDeckIDs = sf.devices.streamDeck.connectedDevices .map(deck => deck.currentDeckInstance.deck.deckId) //end currentDeckIDs const storedDeckIds = globalState['storedDeckIdArray']; alert(`Current Deck IDs:\n\n${currentDeckIDs.join("\n")}\n\nStored Deck IDs:\n\n${storedDeckIds.join("\n")}`) } showStoredAndCurrentDecks();
If both sets of deckIDs are the same, that would be a good indication that the Finder Activate trigger is happening before the Pro Tools Deactivate trigger, and I'd need to redesign the code to account for it.
Dustin Harris @Dustin_Harris
ok I figured something out that will take a lot of headscratching to work around:
@chrscheuer @Chad : It seems (via simple logging) that if you switch from one application that has an script with an application trigger with 'deactivate' to another application with a script with an application trigger of 'activate', the deactivate script does not fire, and the that 'activate' script supersedes it:
example: when switching from Pro Tools that fires a script with a 'deactivate' trigger to a the finder which has an 'activate' trigger, the pro tools 'deactivate' trigger does not fire. I'll post a video.
Edit: the deactivate script DOES fire, but after the next activate script fires, aka switching to the finder triggers the finder activate script first, then the pro tools deactivate script second
Chad Wahlbrink @Chad2024-10-14 16:48:25.866Z
Interesting. Yes, if you can take a short video and log a Help/Issue request for that bug separately, that would be great, @Dustin_Harris! I'll look into it when I can.
Dustin Harris @Dustin_Harris
done
- In reply toDustin_Harris⬆:
Christian Scheuer @chrscheuer2024-10-14 17:08:03.827Z
Cool, thanks for reporting.
This being said, that order of the events is likely a macOS thing (while illogical).
I don't have the time to fully read this thread, but I don't fully understand why you're all not just using the deck history as I suggested in my post. That should give you a much simpler solution.
- In reply toDustin_Harris⬆:TTorsten Zumhof @Torsten_Zumhof
both are the same :
Current Deck IDs:
user:clltjbi3a0000hr10jy7relrl:cm1nehvyw0000kg10amlutl4o
user:clltjbi3a0000hr10jy7relrl:clobaskew0005tt10km1qt4tvStored Deck IDs:
user:clltjbi3a0000hr10jy7relrl:cm1nehvyw0000kg10amlutl4o
user:clltjbi3a0000hr10jy7relrl:clobaskew0005tt10km1qt4tvDustin Harris @Dustin_Harris
Yeah I discovered my approach won’t work yesterday while testing. Let me keep thinking about it…
Chad Wahlbrink @Chad2024-10-15 22:21:18.150Z2024-10-15 22:27:28.097Z
Hey @Dustin_Harris and @Torsten_Zumhof,
I took a stab at this today—a similar approach to the Single Stream Deck Scenario.
Here's what I have:
Set Up
Name the Stream Decks
For both scripts, replace the names 'Stream Deck Standard' and 'Stream Deck XL' with the names of your Stream Decks defined in the "Set Up Stream Deck" section of the SoundFlow Menubar app.Define the Default Decks
You'll also need to fill out the command IDs fordefaultProToolsDeck1
anddefaultProToolsDeck2
and again fordefaultNONProToolsDeck1
anddefaultNONProToolsDeck2
. Select the deck you'd like for each in the SoundFlow Editor, then use Command > Copy Command ID.const defaultProToolsDeck1 = "user:default:cm2az0csu000011100yh9rp56"; const defaultProToolsDeck2 = "user:default:clcf86lse0005xk10di5in8y2";
const defaultNONProToolsDeck1 = "user:default:afdafdafdafafafdafadfafdafdf"; const defaultNONProToolsDeck2 = "user:default:adfafdafafdafdadfadfafaffafa";
Disclaimer
As Irene discovered, if you are using a deck that has the "Navigate to Previous Deck" button, then you will likely need to replace that on all potential subdecks, or at least be aware you will need to click it multiple times from each deck to get back to the "original" deck.This is mainly an issue if you use the official "Pro Tools Main Deck - 5x3" provided by SoundFlow, which has many subdecks.
In this scenario, you'd have to create editable copies of each deck and reconnect them. In my opinion, it'd be easiest to assign a keyboard shortcut to load the "home deck" in this scenario, allowing you to get back to the top-level deck quickly without having to re-route every subdeck.
When Pro Tools Activate Script
//Don't run if no device connected if (sf.devices.streamDeck.deviceCount === 0) throw 0; /*Replace the 'Stream Deck Standard' and 'Stream Deck XL' names with the names of your Stream Decks https://www.dropbox.com/scl/fi/7lwm9o5b0utzmpf2gqpx3/Stream-Deck-Names.png?rlkey=oc7zo9m6505n3gxhp37c1kssr&dl=0 */ let deck1 = sf.devices.streamDeck.connectedDevices.filter(x => x.name === 'Stream Deck Standard')[0] let deck2 = sf.devices.streamDeck.connectedDevices.filter(x => x.name === 'Stream Deck XL')[0] function showProToolsDeck() { /* replace the quotes with your own commandID by clicking on your default pro tools deck and selecting Command -> Copy Command ID from the menu bar in soundflow */ const defaultProToolsDeck1 = "user:default:cm2az0csu000011100yh9rp56"; const defaultProToolsDeck2 = "user:default:clcf86lse0005xk10di5in8y2"; const proToolsDeck1ToOpen = globalState['lastProToolsDeck1'] ? globalState['lastProToolsDeck1'] : defaultProToolsDeck1; const proToolsDeck2ToOpen = globalState['lastProToolsDeck2'] ? globalState['lastProToolsDeck2'] : defaultProToolsDeck2; try { //show the deck on device 1 if (deck1) { sf.decks.get(proToolsDeck1ToOpen).open({ device: deck1, }); } //show the deck on device 2 if (deck2) { sf.decks.get(proToolsDeck2ToOpen).open({ device: deck2, }); } } catch (err) { log('Check Device Connection') throw 0 } } showProToolsDeck();
When Pro Tools Deactivates Script
//Don't run if no device connected if (sf.devices.streamDeck.deviceCount === 0) throw 0; // Define Decks let deck1 = sf.devices.streamDeck.connectedDevices.filter(x => x.name === 'Stream Deck Standard')[0] let deck2 = sf.devices.streamDeck.connectedDevices.filter(x => x.name === 'Stream Deck XL')[0] /* replace the quotes with your own commandID by clicking on your default NON pro tools deck and selecting Command -> Copy Command ID from the menu bar in soundflow */ const defaultNONProToolsDeck1 = "user:default:ckze5vq7z00028r102q85xp2h"; const defaultNONProToolsDeck2 = "user:default:ckze5vq7z00028r102q85xp2h"; function storeCurrentProToolsDeck() { //store current deck on device to globalState if (deck1) { globalState['lastProToolsDeck1'] = deck1.history[0].deck.deckId; } if (deck2) { globalState['lastProToolsDeck2'] = deck2.history[0].deck.deckId; } } storeCurrentProToolsDeck(); try { if (deck1) { sf.decks.get(defaultNONProToolsDeck1).open({ device: deck1, }); } if (deck2) { sf.decks.get(defaultNONProToolsDeck2).open({ device: deck2, }); } } catch (err) { log('Check Device Connection') throw 0 }
Photos of the Two Scripts
- SScott Robinson @Scott_Robinson
This worked perfectly for me on a single Stream Deck, thanks!! 2nd one comes tomorrow, so I’ll see if it works there too.
EDIT: It doesn’t work perfectly.. I have a Finder deck that opens when Finder activates, and now that deck seems to be getting stored somewhere along the way if I go to Finder. That deck now shows sometimes after I cruise around some apps/decks and return to PT. Any ideas? Thanks!!
The finder deck
- In reply toirene_genova⬆:Christian Scheuer @chrscheuer2024-10-08 16:09:20.808Z
Hi Irene,
You can use the "Navigate to Previous Deck" command from the Basics package:
Or, use the following code in a script that's run from within a deck:
event.deck.device.goBack();
- IIrene Genova @irene_genova
Hi Christian,
thanks for this, but what I'm trying to accomplish is a bit more complicated than that :)
I'll try to explain it a bit better:
1-I have a "Finder" deck that shows whenever Pro Tools is deactivated
2-I have a "Pro Tools" deck that shows whenever Pro Tools is activated. In this deck, I have assigned commands to go to other decks, that I use when I'm working on different tasks in Pro Tools. See: editing, session setup, mixing, etc.
3-Let's say I'm in Pro Tools editing. I was on my Pro Tools deck and from there, I went to my editing deck. Then, I switch for a moment to Google Chrome, so my "Finder" deck shows.
4-I go back to Pro Tools, and what I'd love to accomplish, is that Soundflow remembers that I was working in my editing deck, inside that Pro Tools deck that is assigned to show when Pro Tools is active.Is this possible in any way?
Christian Scheuer @chrscheuer2024-10-10 16:03:28.023Z
It's possible to access the "history" of the device (basically, which decks were loaded in which order), like so:
/**@type {AxDeckOrPreset} */ const deck = event.props.deck; /**@type {AxDeckHostDevice} */ const device = event.props.device || sf.devices.streamDeck.firstDevice; if (!device) throw 0; //The last loaded entry would be at index 0, and so on (it's a stack, so in reverse order of loading time) const lastEntry = device.history[0]; const lastLoadedDeck = lastEntry && lastEntry.deck; //At this point, you could iterate through the decks until you find one that's in your "set of decks that are for Pro Tools"
If you look into the source code of the "Navigate back from Sub-decks" in the Pro Tools package under the Decks folder, this has similar (but not identical) functionality, where it looks through the history stack to find the first matching deck to show.
Christian Scheuer @chrscheuer2024-10-10 16:04:52.688Z
I'll loop in @Chad who should be able to help with more details here.
- In reply toirene_genova⬆:
Chad Wahlbrink @Chad2024-10-10 20:12:09.748Z
Hi @irene_genova,
@Dustin_Harris was off to a great start in the general structure of this workflow! Props, Dustin.
I have updated his workflow a bit to work for mobile devices. To make this work, you'll need a script with an application trigger for when Pro Tools Activates and another script with an application trigger for when Pro Tools Deactivates.
After these scripts are set up, it's also important to remove any other triggers for decks that "activate" when Pro Tools is activated. So, if you currently have application triggers for your Pro Tools main deck or Finder Deck, you should remove or disable those triggers.
Here's a quick video walkthrough of how I'm setting it up on my system:
The "Activate" script will look like this:
function showProToolsDeck() { //Don't run if no device connected if (sf.devices.mobile.allDevices.length === 0) return; /* replace the quotes with your own commandID by clicking on your default pro tools deck and selecting Command -> Copy Command ID from the menu bar in soundflow */ const defaultProToolsDeck = "user:default:clcf86lse0005xk10di5in8y2"; const proToolsDeckToOpen = globalState['lastProToolsDeck'] ? globalState['lastProToolsDeck'] : defaultProToolsDeck; try { //show the deck on device sf.decks.get(proToolsDeckToOpen).open({ device: sf.devices.mobile.allDevices[0], }); } catch (err) { log('Check Device Connection') throw 0 } } showProToolsDeck();
And then the Deactivate script would look like this:
/* replace the quotes with your own commandID by clicking on your default NON pro tools deck and selecting Command -> Copy Command ID from the menu bar in soundflow */ const defaultNONProToolsDeck = "user:default:ckze5vq7z00028r102q85xp2h"; function storeCurrentProToolsDeck() { //Don't run if no device connected if (sf.devices.mobile.allDevices.length === 0) return; //store current deck on device to globalState globalState['lastProToolsDeck'] = sf.devices.mobile.allDevices[0].history[0].deck.deckId; } storeCurrentProToolsDeck(); sf.decks.get(defaultNONProToolsDeck).open({ device: sf.devices.mobile.allDevices[0], });
- IIrene Genova @irene_genova
Wow, that worked so well, thank you very much!!
The only thing that I would point out, for other people that may read this thread (and for your own info Chad):
I had to replace the "Navigate to Previous Folder" button in all my Pro Tools sub-decks, to a button that navigates specifically to the main Pro Tools deck. If I used the "Navigate to Previous Folder", after returning from my Finder deck to a certain sub-deck, it would return to the Finder deck instead of the main Pro Tools deck (which makes sense, of course), thus breaking the chain and the scripts wouldn't run anymore. I would have to re-start my main Pro Tools deck and it would run again. But by changing that button, it all works flawlessly!!
Thanks again :D
Chad Wahlbrink @Chad2024-10-11 14:20:25.911Z
Great, @irene_genova! Glad to hear.
- In reply toirene_genova⬆:
Dustin Harris @Dustin_Harris
Yeah that is why I was trying to approach it without using
.goBack()
, because it's possible to open more than one deck before returning to protools, and.goBack()
would desynchronize with the intended deck to be shown.
- In reply toChad⬆:Ggarret farrell @garret_farrell
Hi Chad
Is it possible to get this full script for the Streamdeck please? I tried altering it but I am just getting errors "cannot read property "length" of undefined".
Thanks
Garret
Chad Wahlbrink @Chad2024-10-15 21:39:36.965Z
Hi @garret_farrell,
For a single Stream Deck, this should work well:
When Pro Tools Activate Script
//Don't run if no device connected if(sf.devices.streamDeck.deviceCount === 0) throw 0; function showProToolsDeck() { /* replace the quotes with your own commandID by clicking on your default pro tools deck and selecting Command -> Copy Command ID from the menu bar in soundflow */ const defaultProToolsDeck = "user:default:clcf86lse0005xk10di5in8y2"; const proToolsDeckToOpen = globalState['lastProToolsDeck'] ? globalState['lastProToolsDeck'] : defaultProToolsDeck; try { //show the deck on device sf.decks.get(proToolsDeckToOpen).open({ device: sf.devices.streamDeck.firstDevice, }); } catch (err) { log('Check Device Connection') throw 0 } } showProToolsDeck();
When Pro Tools Deactivates Script
/* replace the quotes with your own commandID by clicking on your default NON pro tools deck and selecting Command -> Copy Command ID from the menu bar in soundflow */ const defaultNONProToolsDeck = "user:default:ckze5vq7z00028r102q85xp2h"; //Don't run if no device connected if (sf.devices.streamDeck.deviceCount === 0) throw 0; function storeCurrentProToolsDeck() { //store current deck on device to globalState globalState['lastProToolsDeck'] = sf.devices.streamDeck.firstDevice.history[0].deck.deckId; } storeCurrentProToolsDeck(); sf.decks.get(defaultNONProToolsDeck).open({ device: sf.devices.streamDeck.firstDevice, });
Dustin Harris @Dustin_Harris
Hi @Chad ! Great work, and your code does work when going between between pro tools and a predefined deck, but the use case that I'm trying to account for is a variable non-protools deck, could be Soundminer, Rx, Finder, and any combination of that back and forth before returning to pro tools, which makes history tracking quite difficult. It would be cool (although a nightmare to implement) if the history array were objects storing both the deckID and the active application bundle ID at the time the deck was called {"user:default:ckze5vq7z00028r102q85xp2h" : "com.avid.proTools"} then at least the history could be filtered by application :)
Chad Wahlbrink @Chad2024-10-16 15:30:09.409Z
That makes sense. That is a trickier scenario.
That may be possible if you set an application activation trigger for each application for one script and then an application deactivation for each application in a second script. Then have a switch case for a few named apps and their default decks. If you switch to an application without a default deck, we could leave the decks unchanged. Then, in the deactivate script, we could store an object with the application bundle ID, device name (Stream Deck), and last deck from that device to be retrieved when that application is activated next.
The pre-requisite is that you'd control ALL of your "application trigger" related decks from these two decks. Otherwise, you'd start to have triggers firing on top of one another.
This functionality starts to push against the default behaviors of how SoundFlow works with decks and application triggers, meaning it would likely cause a lot more overhead and individual maintenance. If remembering deck history per app was desired behavior by many users, it'd best be implemented as a feature-add to SoundFlow. Then, it would be best to log this idea to the "Ideas" section of the forum for future consideration.
Dustin Harris @Dustin_Harris
Yeah exactly that. It starts getting really complex and then you're manually doing a lot of the things SF was designed to do automatically, and although I love the idea, it would involve reinventing a lot of wheels :)
- In reply toChad⬆:Ggarret farrell @garret_farrell
Hi Chad
Thanks a million for this. It works great! So useful.
Thanks
Garret