Tuesday, August 18, 2015

StationPlaylist Add-on Internals: Event handling: announcing status changes and alarm notifications

When you are producing a live show, it is important for your screen reader to announce various status changes such as number of listeners, outro notification, playback status and so on. NVDA is an expert when it comes to handling status changes and activating alarms. In this article, we'll learn the magic behind this expertise: handling events.
NVDA is event-driven
Windows applications (especially those using Windows API) are event-driven programs. Somewhere in the application is an event loop that responds to various events, such as when a check box is clicked, computer is shutting down and so on. NVDA, being a Windows screen reader, does use events for various purposes, ranging from announcing new chat notification in Skype to ignoring it completely.
To handle various events, NVDA uses an event queue (queue handler) and an event handler for event processing. When an event is fired by itself or from other programs, NVDA first checks if the given event is worthy of its attention. Then it performs actions associated with the event, such as announcing changes to a control (name, value, etc.), playing beeps and sounds (progress bar updates) and so on. For add-ons (mostly global plugins and app modules), NVDA is eager to listen to certain events and let controls fire events.
Typically, an event handling routine is declared like this:
event_eventname(module, object in question, next handler)
For example, for Studio app module, one event handler declared is gain focus (you have moved to a different control), and is written as follows:
event_gainFocus(self, obj, nextHandler)
The routine is just like any other function (callable) except that the routine called nextHandler function at the end to allow other controls to respond to events.
What does events have to do with Studio app module?
Events and event handlers are crucial to the operation of the Studio app module, described as "heartbeat" of the add-on. Events and their handlers are used to perform bulk of the work required to allow the app module to function. These include:
* Announcing status changes such as when microphone is turned on or off.
* Alarm notification, such as when end of intro is approaching.
* Activating certain announcement and background tasks such as activating microphone alarm, announcing library scan progress and more.
* Perform workarounds for issues such as focus problems when deleting a track.
Of all the event handlers declared, the most important one is name change event. It is declared as follows:
event_nameChange(self, obj, nextHandler)
When this event is fired by Studio, NVDA performs following operations:
1. Various checks are performed. These include, but are not limited to:
A. Make sure there is something to announce.
B. If using another app, NVDA will ensure that background monitor flag is set (see the discussion on app module constructor in previous articles for more details).
C. If the status to be announced is a common one such as listener count, schedule and cart playback status, NVDA will check if it is permited to announce them.
2. Depending on the type of control (mostly window class name), NVDA performs different operations (see below).
3. Lastly, NVDA calls nextHandler() to let other controls respond to name change event.
Status announcements versus alarm notification
There are two groups of controls NVDA is interested in: status bar and status text with window class name of TStatusBar and TStaticText, respectively (Studio is a Delphi application). Depending on which control fired the event, NVDA will respond differently.
For status announcements (TStatusBar), NVDA:
1. Checks IAccessible child ID (position of the control relative to the parent control as exposed by MSAA (Microsoft Active Accessibility)/IAccessible).
2. If IAccessible child ID is 1, it either announces library scan progress or playback status.
3. For other status bar objects (controls or windows), NVDA first checks if it is a toggle change (ends with "On" or "Off"), and if so, it does the following:
A. If you set status announcement to words (Control+NVDA+1), NVDA announces toggle change notification via speech.
B. If status announcement is set to beeps, an appropriate wave file is selected for playback via NVWave module (nvwave.playWaveFile; this is done via messageSound function).
C. Braille output is not affected - it'll announce toggle changes.
D. For cart edit mode or microphone toggle, extra steps are performed, (by calling extraAction method with the status string as the argument) such as activating microphone alarm or announcing that you are using Cart Explorer (if this is the case). We'll come back to how this works in future installments.
4. For all other announcements, NvDA announces them. In case of schedule announcement, to prevent itself from repeating the same message, NVDA checks if a cached name is the same as the just changed name.
For static text controls (mostly used for alarm notifications):
1. Checks whether NVDA is looking at remaining time for the intro or the whole track.
2. For both cases, NVDA checks if it can braille this (braille timer is not off).
3. If NVDA is told to play or announce alarms (for outro and intro), NVDA plays appropriate tones (middle A (440 hertz) for outro, an octave above middle C (512 hertz) for intro), or if told to do so, warns you that end of track or intro is approaching (add-on 6.0 and later).
The structure of event_nameChange function defined in the Studio app module is such that it can be extended to handle name change event for other controls (it is a tree structure, with the root being the event and two subtrees, one for the status bar and another for static text). Just like other add-ons that define event handling routines, name change event calls nextHandler().
Other events defined in the Studio app module
There are four more events defined in the Studio app module. They are:
* Gain focus: performs focus-related routines such as checking if you are in Insert Tracks dialog in order to turn off background library scanning (more on background library scan in a future installment).
* Lose focus: Opposite of gain focus, used for library scanning.
* App module gain focus: Used to handle touchscreens (yes, Studio app module has a dedicated SPL touch mode) such as assigning additional commands.
* App module lose focus: opposite of the event above.
Events and event handlers allow an app to take appropriate action. NVDA is no exception to this rule: With event handling routines, Studio app module can perform sophisticated actions, ranging from announcing toggle messages to activating microphone alarms and more.
So far, we have covered things that the app module performs wherever you are in Studio. the next few installments will cover how the app module handles specific situations, such as working with items in the playlist viewer, finding tracks, library scans and so on.
1. Event loop (Wikipedia): https://en.wikipedia.org/wiki/Event_loop
2. Event-driven programming (Wikipedia): https://en.wikipedia.org/wiki/Event-driven_programming

No comments:

Post a Comment