Using KeyDown to control trial start/stop and record metadata

426 views
Skip to first unread message

Philip Parker

unread,
Jan 5, 2022, 12:45:26 AM1/5/22
to Bonsai Users
Hello,

I'm trying to create a task where I initiate the trial with a spacebar KeyDown (which starts video acquisition), then end the trial by pressing one of three 'outcome' keys to stop the trial (end video acquisition) and record the outcome to a CSV file. I specifically want to be able to press 0, 1, or 2 to indicate one of three outcomes for a given trial, append the outcome to csv, and have any of those three keys end the acquisition but importantly prevent other key presses (e.g. spacebar, another number) from ending acquisition. So e.g. an experiment with five trials of spacebar followed by 0,1, or 2 should result in five individual videos and one csv with five rows of outcomes.

The attached workflow seems to achieve this except right now any key will end acquisition (I've left the KeyDown node going to TakeUntil as default so any key will trigger it). It's not clear to me how to tell the 'TakeUntil' node that ends video acquisition to only look for one of three specific key presses, since the KeyDown node only allows you to specify one key. I've tried using multiple KeyDowns with Zip and Concat but these require all three keys to be pressed before it will work. I thought the easiest way would be to use the 'Outcome' BehaviorSubject I use to store the outcome, but I couldn't figure out which operation would signal an update to the Outcome. Any help would be greatly appreciated.

Thanks!
Phil
split_trials_demo.bonsai.layout
split_trials_demo.bonsai

brunocruz

unread,
Jan 5, 2022, 3:04:37 AM1/5/22
to Bonsai Users
Hey Phil,

If I understood correctly, I think you were very close. The only thing missing was using Outcome as a trigger for the TakeUntil, instead of the general KeyPress node.
I just cleaned the code a bit and added the CSV log you mention. On each trialOutcome, a new row is appended to the .csv file, with the clip index and corresponding outcome. 

Let me know if it works :)

Cheers,

B
split_trials_demo.bonsai

Philip Parker

unread,
Jan 5, 2022, 3:18:02 AM1/5/22
to Bonsai Users
Yes this works perfectly, thank you Bruno for the quick response!

Phil

Philip Parker

unread,
Jan 5, 2022, 4:39:52 PM1/5/22
to Bonsai Users
Hey Bruno, quick follow up about one of the variables in your workflow: TrialOutcome appears to be a BehaviorSubject that you update with multicasting, but it doesn't appear to be initialized anywhere. I have a few other task variables I'm trying to save out alongside the outcome in the CSV, but I can't figure out how to create them without initializing them - when I add a BehaviorSubject node it requires an input and displays an little circle on the left side of the node, whereas the BehaviorSubject you created doesn't appear to require an input, and I'm assuming it isn't initialized but just gets updated through multicasting? Pics of your version of BehaviorSubject vs. mine are attached.

The reason this becomes an issue for me is that the other variables I have to initialize are saved out of sync with the outcomes - these variables are saved out in the first line of the CSV, then on the next trial the previous trial's outcome is combined with the current trial's metadata.

Thanks!
Phil

behaviorsubject.PNG
behaviorsubject_phil.PNG

brunocruz

unread,
Jan 6, 2022, 3:17:12 AM1/6/22
to Bonsai Users
Hi,

Prior to Bonsai 2.6 you indeed had to initialize subjects. You could use the logic you used in your initial workflow (Int(X) -> BehaviorSubject(Y)) or, in order to drop the first value, the typical solution was Int(X) -> IgnoreElements -> BehaviorSubject(Y).
You could then Int(Z) -> MulticastSubject(Y) your new values.
In 2.6, Gonçalo added a neat feature where it allows you to create standalone subject sources. While there are probably subtle differences in the way both solutions are implemented, I don't recall coming across a situation where they behave differently, but maybe Gonçalo can speak to that at some point.

Anyway, how to use this feature?
First, you should have a node with the type of the Subject to-be-created. Let's say I want to have an int-type BehaviorSubject. Add a "temporary" Int node, right-click on it, Create Source, BehaviorSubject (A). This will create a BehaviorSubject source with the type Int that you can connect to other nodes (e.g., CSV). Similar to the previous workflow, you can simply MulticastSubject to it (B).

(A)
int.png

(B)
Init+multicast.PNG
Sometimes, you might want to have a subject with a type that is not natively available as a source node in bonsai. The logic remains the same, as long as you can construct a node with the intended type, it does not matter. For instance, in the workflow that I sent you, I have a tuple with two ints (Tuple<int,int>). We can construct this type by using Zip on two Int. (C). Finally, repeat the "right-click on it, Create Source, BehaviorSubject (A)", and you will have your subject source!

(C)
int2.png

Hope it is clear & cheers,

Bruno
Reply all
Reply to author
Forward
0 new messages