Now that we've covered the "kernel" (innermost parts) of the Studio add-on, it is time to talk about the icing: SPL Utilities global plugins and its contents. The next few articles will talk about what the global plugin does, introduce you to inner workings of SPL Controller layer and tour how encoder support is implemented.
Studio app module versus SPL Utilities global plugin
As described in the add-on design article, SPL Studio add-on comes with two app modules and a global plugin. This was needed not only to differentiate between module types and expertese, but also allow Studio functions to be invoked from other programs. With the introduction of encoder support in add-on 3.0 (fall 2014), the global plugin portion of the add-on (SPL Utilities) took on an important role: working as an encoders manager to report connection status and to perform other routines.
SPL Utilities package contents
The SPL Utilities global plugin consists of the following modules:
* Main plugin code (__init__.py), containing essential global routines such as SPL Controller (described below) and a procedure to focus to Studio window upon request. This module defines constants used by Studio to receive messages, a function to focus to Studio window and the global plugin class containing definitions for SPL Controller layer commands.
* Encoder support (encoders.py), outlining NVDA's support for various encoders (see the next article; the main global plugin module plays an important part in helping the encoder module as you'll see in the next article).
SPL Controller layer
The SPL Controller layer (entry command unassigned, same reason as the Assistant layer entry command) is used to invoke Studio functions from anywhere. The entry routine is similar to the app module counterpart (SPL Assistant) except for the following:
* NVDA will make sure Studio is running (if so, it'll cache the window handle value just as in the Studio app module), otherwise it cannot enter SPL Controller layer.
* All commands (except two) use Studio API (Studio API and use of user32.dll's SendMessage was described in a previous article).
For mechanics of layer commands, see a previous article on add-on design where layer commands were discussed.
The following commands utilize Studio API:
* A/Shift+A: Automation on/off.
* L/Shift+L: Line in on/off.
* M/Shift+M/N: Microphone on/off/instant on/off toggle.
* P: Play.
* R: Remaining time for the currently playing track (if any).
* Shift+R: Library scan progress and umber of items scanned.
* S/T: Stop with fade/instant stop.
* U: Play/pause.
For readers familiar with Studio keyboard commands, you'll find yourself at home (they are indeed Studio commands except pressing Shift will turn a feature off and Shift+R will remind you of Control+Shift+R for library scan from Insert Tracks dialog).
Here are the two exceptions
* E: If you tell NVDA to monitor one or more encoders in the background, this command will announce number of encoders being monitored (see the next aritlce on the format of this message).
* F1: Opens a dialog displaying Controller layer commands (does this sound familiar?).
Focusing to Studio window from anywhere
As you are broadcasting a show with Studio, you may find yourself in a situation where you need to switch to Studio quickly to take care of automation, insert new tracks and so on. An ideal situation is to switch to Studio when you press Alt+TAB (this isn't the case if you have more than two programs opened). For this reason, screen reader scripts for Studio includes a command to switch to Studio upon request (unassigned in NVDA).
In NVDA world, this is accomplished with a function in the SPL Utilities module (SPLStudioUtils.fetchSPLForegroundWindow). This is employed not only by the main global plugin module (called from a script to focus to Studio window), but also used in encoders for various functions. The routine is as follows:
1. The focus to Studio script will check if Studio is running, and if so, it'll call the fetch window function, which in turn locates the desktop (shell) window to serve as the starting point for locating Studio window.
2. NVDA will scan top-level windows (children of desktop object) until a Studio window (where the window's app module is the Studio app module) is found, and if found, NVDA will increment a Studio window candidate counter.
3. Once top-level window scanning is complete, NVDA will take action based on what the Studio window candidate counter says before passing the foreground object back to the main script. It can do one of the following:
A. If counter is 0 (fg is None), NVDA will know that you have minimized Studio, so it'll tell you that Studio is minimized.
B. If counter is 1, NVDA will locate the Studio window by looking for the main Studio window (user32.dll is involved).
C. For all other values, NVDA will assume the last window found is the Studio window (held in fg variable) and return it.
4. Back at the focus to Studio script, NvDA will either announce if Studio is minimized or switch to the foreground window returned by the fetch window function (fg.SetFocus).
The routines discussed above (SPL Controller and the command to switch to Studio window) is one of the two pillars of the SPL Studio Utilities global plugin (the other is encoder support). With these routines, it became possible to perform playback operations without focusing to studio, and you can switch to Studio window from anywhere, anytime. We'll visit the other side of this global plugin story in the next StationPlaylist Add-on Internals article, and after that, we'll conclude with an interview with the maintainer of the add-on to learn about how he (I) develop new add-on features.