How can I prevent KbCheck from missing key presses/mouse clicks?

I'm trying to record a row of key presses/mouse clicks that occur during each trial.The interval between consecutive presses/clicks varies within a large range and can be as small as 150ms.I used the following lines to record clicks:
while 1

while ~any(buttons)
[x,y,buttons]=GetMouse;
end

timeQueue=[timeQueue GetSecs];

while any(buttons)
[x,y,buttons]=GetMouse;
end

end
But clicks were frequently missed and the same occured when key presses were collected. Would anyone here help me work around this?

As I guess, the problem might lie in the part I designed to wait for mouse release before next enquiry. Is there a better way to do this?For example, what if FlushEvents command is used instead of the while loop? Thanks go to anyone who might help.
Hi Bai,

FlushEvents may sound promising, but isn't it possible that it would accidentally flush away mouse clicks/keypresses you don't want it to?

I agree with you that the reason might be the way your program waits for the mouse to release: If you release a button and then press a button within the time " timeQueue=[timeQueue GetSecs];"

is executed, then the program would think you have never released the button and will wait until this button is released for the second time. Thus, one button click would be missed. Unfortunately, "

timeQueue=[timeQueue GetSecs];" is not a very effective way to construct arrays. It will take longer and longer to run this line since the size of 'timeQueue' is ever increasing, and therefore you
have greater danger of missing button clicks.

My idea is that don't wait for the key to press/release(the 'blocking' way). Use the 'unblocking' way to keep polling as frequently as possible, and store the current status of the button in an 'event array' . If the button is down at the time of query, then add the current time to the end of the event array; If unpressed, add '0'. In the end, the event array would look like:

000000 6e4 6.1e4 6.2e4 00000000 7e4 7.1e4 0000000000 8.3e4 8.4e4 0000000...

And the number of non-zero clusters (such as '6e4 6.1e4 6.2e4') would equal that of the mouse clicks.
The following is a demonstration of my idea:

% ------ MouseClickTester.m ---------
NUM_MAX_EVENTS = 1000000
event_array = zeros(NUM_MAX_EVENTS, 1);
% Make it as big as possible

i = 0;
duration = 10
startTime = GetSecs;

while 1
[x,y,buttons]=GetMouse;
i = i + 1;
event_array(i) = any(buttons).*GetSecs;
if GetSecs - startTime > duration
break;
end
end

% convert the event array to logical
event_binary = logical(event_array);

% Right shift event_binary by 1 element to make a mask
event_mask = ~[false event_binary(1:end-1)];

% select only the first timestamp of each non-zero cluster
event_stamps = event_binary & event_mask;

% Get the actual timestamps (the onset time of the clicks)
timeQueue = event_array(event_stamps);
%---- End of MouseClickTester.m --------

It works perfectly on my machine. The only problem is that it takes too much memory while running.

Hope that helps

Matthias


--- In psychtoolbox@yahoogroups.com, "bai.shui" <bai.shui@...> wrote:
>
> I'm trying to record a row of key presses/mouse clicks that occur during each trial.The interval between consecutive presses/clicks varies within a large range and can be as small as 150ms.I used the following lines to record clicks:
> while 1
>
> while ~any(buttons)
> [x,y,buttons]=GetMouse;
> end
>
> timeQueue=[timeQueue GetSecs];
>
> while any(buttons)
> [x,y,buttons]=GetMouse;
> end
>
> end
> But clicks were frequently missed and the same occured when key presses were collected. Would anyone here help me work around this?
>
> As I guess, the problem might lie in the part I designed to wait for mouse release before next enquiry. Is there a better way to do this?For example, what if FlushEvents command is used instead of the while loop? Thanks go to anyone who might help.
>
--- In psychtoolbox@yahoogroups.com, "bai.shui" <bai.shui@...> wrote:
> I'm not quite sure about what you said about periodical pausing. Is this a common strategy to avoid CPU overloading? I should get it better if you could explain more of it. I also wonder if there's a similar strategy that can be applied to key presses?

Yes, this is a common strategy. It is implemented in KbWait / KbStrokeWait / KbwhateverWait and in the GetClicks() function, so you should simply use those, or understand their code if you need something special.

-mario


>
> Thanks!
>
>
> --- In psychtoolbox@yahoogroups.com, "Mario" <mario.kleiner@> wrote:
> >
> >
> >
> > Matthias tip of preallocating timeQueue is a good one - not preallocating may be one reason of missed mouseclicks. FlushEvents() is useless, it only applies to GetChar/CharAvail - and even there it has a different function than what you suppose.
> >
> > What happens if you replace you GetMouse by a GetClicks([],0); ?
> > GetClicks implements waiting for mouse-clicks, but it pauses for 2 msecs between each query to GetMouse, releasing the cpu to do other stuff.
> >
> > Not periodically pausing can also cause failures of mouse queries, depending on operating system, realtime priority and phase of moon. This due to either overloading the cpu or - if the os protects against such overloads - because your code will be punished by stopping it for long time intervals thereby causing missed mouseclicks.
> >
> > -mario
> >
> > --- In psychtoolbox@yahoogroups.com, "MatthiasZ" <matthiaszhang@> wrote:
> > >
> > > Sorry, I just realized that the algorithm used in MouseClickTester may seem a bit too awkward. Here is a revised version, without using the 'event array'. Enjoy!
> > >
> > > % --- Mouse Click Tester2 ---
> > > % --------
> > > NUM_MAX_TIMESTAMPS = 1000;
> > > timeQueue = zeros(NUM_MAX_TIMESTAMPS, 1);
> > >
> > > i = 0;
> > > duration = 10;
> > > startTime = GetSecs;
> > >
> > > ButtonReleasedFlag = true;
> > >
> > > while 1
> > > [x,y,buttons]=GetMouse;
> > > timestamp = GetSecs;
> > > if any(buttons) && ButtonReleasedFlag
> > > i = i + 1;
> > > ButtonReleasedFlag = false;
> > > timeQueue(i) = timestamp;
> > > elseif ~any(buttons) && ~ButtonReleasedFlag
> > > ButtonReleasedFlag = true;
> > > end
> > > if GetSecs - startTime > duration
> > > break;
> > > end
> > > end
> > >
> > > % --- End of Mouse Click Tester2---
> > >
> > > Matthias
> > >
> > > --- In psychtoolbox@yahoogroups.com, "MatthiasZ" <matthiaszhang@> wrote:
> > > >
> > > > Hi Bai,
> > > >
> > > > FlushEvents may sound promising, but isn't it possible that it would accidentally flush away mouse clicks/keypresses you don't want it to?
> > > >
> > > > I agree with you that the reason might be the way your program waits for the mouse to release: If you release a button and then press a button within the time " timeQueue=[timeQueue GetSecs];"
> > > >
> > > > is executed, then the program would think you have never released the button and will wait until this button is released for the second time. Thus, one button click would be missed. Unfortunately, "
> > > >
> > > > timeQueue=[timeQueue GetSecs];" is not a very effective way to construct arrays. It will take longer and longer to run this line since the size of 'timeQueue' is ever increasing, and therefore you
> > > > have greater danger of missing button clicks.
> > > >
> > > > My idea is that don't wait for the key to press/release(the 'blocking' way). Use the 'unblocking' way to keep polling as frequently as possible, and store the current status of the button in an 'event array' . If the button is down at the time of query, then add the current time to the end of the event array; If unpressed, add '0'. In the end, the event array would look like:
> > > >
> > > > 000000 6e4 6.1e4 6.2e4 00000000 7e4 7.1e4 0000000000 8.3e4 8.4e4 0000000...
> > > >
> > > > And the number of non-zero clusters (such as '6e4 6.1e4 6.2e4') would equal that of the mouse clicks.
> > > > The following is a demonstration of my idea:
> > > >
> > > > % ------ MouseClickTester.m ---------
> > > > NUM_MAX_EVENTS = 1000000
> > > > event_array = zeros(NUM_MAX_EVENTS, 1);
> > > > % Make it as big as possible
> > > >
> > > > i = 0;
> > > > duration = 10
> > > > startTime = GetSecs;
> > > >
> > > > while 1
> > > > [x,y,buttons]=GetMouse;
> > > > i = i + 1;
> > > > event_array(i) = any(buttons).*GetSecs;
> > > > if GetSecs - startTime > duration
> > > > break;
> > > > end
> > > > end
> > > >
> > > > % convert the event array to logical
> > > > event_binary = logical(event_array);
> > > >
> > > > % Right shift event_binary by 1 element to make a mask
> > > > event_mask = ~[false event_binary(1:end-1)];
> > > >
> > > > % select only the first timestamp of each non-zero cluster
> > > > event_stamps = event_binary & event_mask;
> > > >
> > > > % Get the actual timestamps (the onset time of the clicks)
> > > > timeQueue = event_array(event_stamps);
> > > > %---- End of MouseClickTester.m --------
> > > >
> > > > It works perfectly on my machine. The only problem is that it takes too much memory while running.
> > > >
> > > > Hope that helps
> > > >
> > > > Matthias
> > > >
> > > >
> > > > --- In psychtoolbox@yahoogroups.com, "bai.shui" <bai.shui@> wrote:
> > > > >
> > > > > I'm trying to record a row of key presses/mouse clicks that occur during each trial.The interval between consecutive presses/clicks varies within a large range and can be as small as 150ms.I used the following lines to record clicks:
> > > > > while 1
> > > > >
> > > > > while ~any(buttons)
> > > > > [x,y,buttons]=GetMouse;
> > > > > end
> > > > >
> > > > > timeQueue=[timeQueue GetSecs];
> > > > >
> > > > > while any(buttons)
> > > > > [x,y,buttons]=GetMouse;
> > > > > end
> > > > >
> > > > > end
> > > > > But clicks were frequently missed and the same occured when key presses were collected. Would anyone here help me work around this?
> > > > >
> > > > > As I guess, the problem might lie in the part I designed to wait for mouse release before next enquiry. Is there a better way to do this?For example, what if FlushEvents command is used instead of the while loop? Thanks go to anyone who might help.
> > > > >
> > > >
> > >
> >
>