3.3 Combining Different Spaces in AFL

In general, the output device may consist of any number of speech and non-speech components, each of which is a named component of the audio formatter. Individual components provide operators for moving in the space, etc., as described in the sections on the individual component spaces. In this section, we describe how these component spaces are combined to form the total audio space. The AFL block and assignment statements, which have been described in the context of a single component, will now be extended to work in the total audio space.

States in AFL

Conceptually, AFL maintains one local and one global state in program variables *current-total-audio-state*and *global-total-audio-state*. Since it is easier to think of speech and non-speech audio separately, we treat these as named component subspaces with their own state variables. Thus, variables *global-speech-state* and *current-speech-state*are really components of these states, so that the fully qualified references to these variables are

*global-total-audio-state*.*global-speech-state*

and

*current-total-audio-state*.*current-speech -state*

The names of these fields provide the external interface to the individual components.

We now describe how components are combined to form the total audio space. The AFL block is now extended to work in the total audio space; it introduces a local variable *current-*for each of the components.

Assignment statements

(local-set-state <point>)

and

(global-set-state <point>)

assign the value of expression <point> to AFL state variables. The type of <point> determines the component state variable that will be set. Hence, if there are several component spaces of a given type, e.g., several speech components, the fully qualified name of the state variable must also be given, as in

(local-set-state <point> *current-speech -state*)

AFL assignment statements are implemented as generic functions. The space-specific methods on the assignment statements perform the following steps:

  1. Synchronize actions as necessary for this component space.
  2. Set the variable that encapsulates the state of this component space to the value of the expression that is supplied.
  3. Call the hardware specific set-state function.

Adding a component space requires some work but can be done easily by someone familiar with the implementation. To add a new component space:

  1. Define the space and build the underlying implementation.
  2. Define the external interface by providing state variables.
  3. Provide the space-specific methods on the assignment statements.

Synchronization

The various components of an audio formatter can have separate threads of control. AFL constructs are needed to synchronize events occurring on these multiple components. The AFL block serves as a cobegin statement; a block terminates only after all events started within it have been completed. We now address issues that come up in designing the AFL block as a cobegin statement.

In a typical audio formatter having a speech and sound component, we may specify that a sound is to play repeatedly on the sound component while certain actions are executing on the speech component. The AFL block should not terminate until speech commenced from inside that block has completed —otherwise the sound will be turned off prematurely in this example. Invariably, with any speech synthesizer, the speech will fall far behind the host computer that is generating the text to be spoken. One way to avoid this is to synthesize a word at a time and wait till it has been spoken, but then all intonation is lost. Text must be sent in large enough chunks so that intonational cues are retained. This means that events on other component spaces have to be explicitly synchronized with speech space events. The AFL block, by serving as a cobegin statement, abstracts these details from an AFL program.

AFL events are of two types:

  1. Simple events that execute an action once. Such events are executed by statements like send-text.
  2. Events that repeatedly execute an action. These are executed within an implicit loop foreverstatement. The actions executed within the loop forever statement are typically simple events.

An AFL block terminates only after all simple events commenced within that block have terminated. Components executing events of the second type (within a loop forever construct) are said to be busy. AFL blocks keep track of busy components, and when a block terminates, all events of the second type commenced within that block are aborted.

Note that any event on an audio component terminates in the AFL block in which it was begun. It is not possible to begin an event in one block and have the block terminate but let the event continue. Upon first glance, this restriction may seem too strong. However, in many experiments with AS TE R, AFL has proven to be sufficiently expressive. Further, this restriction tremendously simplifies the semantics of the AFL language and its implementation.

Tracking Busy Components

To preserve the scope rules of AFL, two tables are used to record busy components. Table busy-table, with global scope, has one entry for each component, and all its entries are initially false. When a loop forever event is started on a component, the corresponding entry in busy-table is set to true. Once a component has become busy, no other events can be executed on it; but its current state can be changed, e.g., the pitch of the sound that is playing on a busy component could be changed.

When a block terminates, only loop forever events commenced inside that block are aborted. To implement this, we introduce table local-busy-table with an entry for each component. This table is local to the AFL block, and all its entries are initialized to false. When terminating a block, entries in the local copy of local-busy-table are checked to determine the components whose loop forever events are to be aborted.

Thus, when terminating a block, the following steps are performed in sequence:

  1. Wait for completion of events on components whose busy-table entry is false.
  2. Abort ongoing events on components whose entry in the local-busy-table is true.