Monday, August 24, 2015

StationPlaylist Add-on Internals: All about SPL Assistant layer commands

Hi,
You may recall visiting two layer command sets in a previous article: SPL Controller and SPL Assistant, the former used to perform Studio functions from any program and the latter for status announcements. I mentioned throughout this series that we'll tour these layer sets, and we'll start with SPL Assistant layer.
Talk about layer commands
One of the common features of Studio scripts for JAWS, Window-Eyes and NVDA is extensive use of layer commands. This was popularized by JAWS and its Studio layer (grave key). Some of the benefits of this approach include saving keyboard commands, reminding users as to commands available in Studio and so on.
Birth of SPL Assistant layer
As mentioned previously, since version 1.0 in January 2014, Studio add-on comes with two layer commands to represent the global plugin and the studio app module. In case of Studio app module and its layer set (SPL Assistant), I borrowed some commands from both JAWS and Window-Eyes scripts with some notable differences, namely some commands and how things were announced.
When I sat down to design this layer set, I felt it would be helpful for broadcasters if most of the Assistant layer commands borrowed from Studio command assignments. For example, a broadcaster will press M to toggle microphone on and off, and in SPL Assistant layer, pressing M announces microphone status. Another example was Cart Edit Mode - pressing Control+T in Studio will toggle this, and pressing SPL Assistant, T will announce whether this mode is on or off (the reason for assigning T for Cart Edit Mode status will be discussed later).
Originally, one could invoke SPL Assistant layer by pressing Control+NVDA+grave key from within Studio. However, some NVDA translators told me that this key combination is used for NVDA screen reader commands in their language. Thus, in add-on 2.0 (late spring 2014), I decided to remove this command, which means in order for you (broadcasters) to invoke SPL Assistant layer, you need to go to Input Gestures dialog while focused in Studio, expand StationPlaylist category and look for the Assistant entry (I personally use Control+NvDA+grave, and in recent add-on development builds, I told Studio add-on to let SPL Controller layer command (discussed in a future article) to invoke Assistant layer).
Categorizing SPL Assistant commands
Once you invoke SPL Assistant layer (a beep will be heard), you can perform one of the following operations:
* Status announcements (automation, microphone, etc.).
* Tools (library scan, track time analysis and so on).
* Configuration (switching broadcast profiles).
* Ask for help (opening SPL Assistant help dialog or the online user guide).
For the first two categories, they can be divided further into commands which uses studio API (via statusAPI function discussed in a previous article) and those relying on object navigation (multiple components are involved and is sensitive to user interface changes). We'll go through each of these categories in turn.
SPL Assistant 1: status announcements
These commands allow you to obtain various status information such as title and duration of the next track, cart edit mode status and so on. These can be divided into those which uses object navigation (old style) and Studio API (new style) commands.
The following commands (sorted alphabetically) utilize Studio API to perform needed functions:
* D: Remaining time for the opened playlist.
* H: Duration of tracks in the selected hour.
* P: Playback status.
Revisiting the past: object navigation
Before the new style routines were written, all commands used object navigation. Typically, the command will use a helper function and an object map to locate the needed object and will announce what you are looking for (typically obj.name or an error message). The process was as follows:
1. The Studio app module contains a map of indecies where the object lives in relation to the foreground window. For example, for a given object, if index was 0, NVDA nows that the object is the first child of the foreground object. Technically, it is a dictionary of lists, with each list item (indecies) corresponding to the version of Studio the add-on supports.
2. To fetch needed objects and to record the command type, a number of constants are defined in the app module (all integers, denoting what you wish to hear). These constants serve as keys to the object index map.
3. After entering SPL Assistant layer and once you press one of the commands below, NVDA will do the following:
A. Each command will obtain the object in question by calling object fetcher (status function) with the announcement type as the parameter (status(SPLConstant; for example, for cart edit mode, the signature is self.status(self.SPLPlayStatus), with the constant denoting a status bar).
B. The object fetcher (status function) will first consult an object cache (part of the Studio app module) hoping that the needed object is ready for use (for performance reasons).
C. If the object was not cached, the fetcher will first write down the foreground window, then use the directions specified in the object index map (the constant passed into the status function is the key to this map and different values are returned based on Studio version) to locate, cache and return the object in question (in that order).
D. Back at the routine for the command, it is up to the routine as to what to do with it (sometimes, the actual object is a child of the just returned object).
The commands which utilizes object navigation steps above include:
* A. Automation.
* C: Title of the currently playing track.
* Shift+H: Duration of selected tracks in the current hour slot.
* I: Listener count (I have tried using Studio API to obtain this information, but after experimenting with it, object navigation routine was more stable).
* L: Line in.
* M: Microphone.
* N: Title and duration for the next track.
* Shift+P: Track pitch.
* R: Record to file.
* S: Track scheduled for.
* T: Cart Edit Mode (I assigned T to this command for efficiency reasons).
* U: Studio up time.
* W: Weather and temperature (if configured).
* Y: Playlist modification.
For example, if you press A to obtain automation status from Studio 5.10:
1. Invoke SPL Assistant, then press A.
2. The status function (object fetcher) is called, taking the status bar constant (SPLPlayStatus, which is 0) as the key to the index map.
3. Object fetcher will see if the status bar object (cache dictionary with the key of 0) has been cached. For this example, it isn't.
4. Seeing that the status bar isn't cached, object fetcher will now look at the index map and will decide which column to read (for this example, it is column 2 (index 1)). The column records the child position of the status bar relative to the foreground window (in our case, index is 6 or the seventh child).
5. Once the child object position index is obtained, object fetcher will locate the actual object and cache it (self._cachedStatusObjs[infoIndex] = fg.children[statusObj]), then returns the object to the automation announcement routine.
6. Back at the script routine, NVDA will be reminded that it needs to look at one of the object's children (status bars can contain child objects if exposed by accessibility API's), then will announce one of it's contents (second child object, which records automation status).
SPL Assistant 2: tools
These are miscellaneous commands in SPL Assistant, and all of them (except one) uses Studio API:
* Shift+R: Library scan. This is a convenience function to start library scan in the background, useful if you have added new tracks from a number of folders via Studio Options dialog. Consult a previous article on library scan for details on library scan internals.
* F9: Marks the current position of the playlist as start of track time analysis (more on this feature below).
* F10: Performs track time analysis (add-on 6.0).
Track time analysis: Duration of "selected" tracks
A few months ago, during a Skype chat with a number of add-on users, someone suggested a feature where NvDA will tell you how long it'll take to play selected tracks. Since I was familiar with this concept from JAWS scripts, I decided to work on it as part of add-on 6.0.
The resulting routine (which is available if you are focused on the main playlist viewer with the playlist loaded) is as follows:
1. Move to the position in a playlist to mark as start of track time analysis.
2. Enter SPL assistant, then press F9.
3. Move to another track in the playlist, open SPL Assistant then press F10. NVDA will then:
A. Determine analysis range. For most cases, it'll be top to bottom analysis, but in some cases, it could be reverse (bottom to top). Also, a variable to hold total duration will be prepared.
B. For each track in the analysis range, NvDA will obtain file name and track duration via Studio API. Once the track duration is received, it is then added to the total duration variable.
C. Once time analysis (calculating total duration) is done, NVDA will announce number of tracks selected and the total duration using mm:ss format.
If you are a seasoned NVDA user, you may have noticed a familiar pattern: the command to set a marker to copy review cursor text is NVDA+F9, and you would move to a different location and press NvDA+F10 to copy the selected text to the clipboard. Replacing the NvDA modifier key with SPL Assistant produces the commands above: F9 to mark current position for time analysis, and F10 to perform the actual calculation. I intentionally chose these two function keys to provide consistent experience and to reenforce concepts used in NvDA screen reader: review cursor.
SPL Assistant 3: configuration
There is another function key assigned to SPL Assistant: pressing F12 will switch to an instant switch profile (if defined). We'll come back to what is meant by "instant switch profile" and the mechanics of it (and internals of SPL Assistant, F12) in the next article.
SPL Assistant 4: getting help
I believe that a product isn't complete without a good quality documentation. For this reason, SPL Assistant provides two commands to help you use the layer commands or the add-on itself. They are:
* F1: Displays a dialog presenting a list of SPL Assistant layer commands.
* Shift+F1: Opens the online user guide (os.startfile).
A surprise: some Assistant layer commands can be invoked without entering the layer first
There are times when a broadcaster will need to obtain certain information quickly. So the question becomes, "is there a way to announce something without first invoking Assistant layer?" Yes, you can assigg a custom command for the following Assistant commands:
* Name of the next track.
* Name of the current track.
* Weather and temperature.
* Track time analysis marker.
* Track time analysis.
For these routines, an extra step is performed to make sure that SPL Assistant flag is turned off automatically after the shortcut for these routines are pressed. Without this step, you might end up with a situation like the following:
1. You invoke Assistant layer.
2. You then press the shortcut key (not the layer counterpart) for the layer command you wish to use.
3. You press another key which may announce something else, or you hear the same thing twice if you do press the layer command counterpart to the command you have pressed. In effect, you have invoked two layer commands in one sitting (the purpose of the layer set is to let you hear one announcement at a time).
Conclusion
By now you should have a better understanding of how SPL Assistant layer commands work. You also learned that some commands do not require you to invoke the Assistant layer, and toured the necessary infrastructure to support certain layer commands. We'll visit the Controller layer commands in a future article. Our next station stop is an overview of add-on configuration management internals, including how NVDA can remember various settings, present options in add-on settings dialog and an introduction to broadcast profiles.
References:
1. Cache (Wikipedia): https://en.wikipedia.org/wiki/Cache_(computing)
2. Os (Python documentation, Python Software Foundation): https://docs.python.org/2/library/os.html

No comments:

Post a Comment