Skip to main content

Aural Alert System

Introduction

Aural Alert System is an SDK API which sits on top of SoundServer and provides a more powerful interface for playing avionics-based aural alerts. This API aims to abstract away some of the queue and state logic associated with playing aural alerts triggered by specific conditions. By using Aural Alert System, you can focus on defining your alerts and their triggers instead of having to worry about the mechanical details of how to get sounds to play at the correct times and in the correct order.

System Description

Aural Alert System is based on queues. Each alert is assigned to a specific queue, and each queue can only play one alert at a time. If multiple alerts are set to play in the same queue, they will play in sequence instead of overlapping one another. Alerts with higher priority will play before alerts with lower priority in the same queue if they are set to play at the same time. However, a new alert will never interrupt an alert that is already playing, even if the new alert has higher priority. Alerts belonging to different queues can play simultaneously (overlap).

The AuralAlertSystem class handles the state logic governing aural alerts and controls which sounds are playing at any given moment. An airplane should have at most one instance of AuralAlertSystem across all instruments. Because AuralAlertSystem uses SoundServer to play sounds, an instance of SoundServer is required for AuralAlertSystem to function properly. The instances do not necessarily have to be created on the same instrument, and there is no requirement to wait for SoundServer to initialize before creating or using AuralAlertSystem.

Registering Alerts

Alerts must be registered before they can be used. Registering an alert is done using the AuralAlertRegistrationManager class:

import { AuralAlertRegistrationManager, EventBus } from '@microsoft/msfs-sdk';

const bus = new EventBus();
const manager = new AuralAlertRegistrationManager(bus); // `bus` is the event bus

manager.register({
uuid: 'no-takeoff',
queue: 'my-queue',
priority: 0,
sequence: 'aural_no_takeoff',
continuous: false,
repeat: true
});

The register() method takes in a single object that satisfies the AuralAlertDefinition type. Each alert definition must define the properties seen in the above example.

uuid is a unique string ID used to reference the alert.

queue is the name of the queue to assign the alert.

priority determines the priority of the alert within its queue.

sequence defines which sound events are played for the alert. It can either be a single string or an array of strings. Each string in the sequence should be an avionics sound event ID defined in the airplane's sound.xml. If an array of strings is used, then each sound event in the array is played in order.

continuous determines whether the alert's sound event sequence plays in an infinite loop.

repeat determines whether the alert should be re-queued to play again once it is finished playing as long as the alert is active. A repeating alert is not the same as a continuous alert. You can think of a continuous alert as an alert with an infinite duration - once the alert starts playing, it will continue to play forever (in a loop) unless forced to stop. This means that a continuous alert can never be interrupted by another alert. On the other hand, a repeating alert has a finite duration and will stop playing once the end of its sound event sequence is reached. At that point, if the alert is still active, it is inserted back into its queue. It will only begin playing again if there are no other alerts of higher priority in the queue. Therefore, repeating alerts can be interrupted by higher priority alerts (but only once the repeating alert has finished playing its sequence).

In addition to the above required properties, AuralAlertDefinition also defines an optional timeout property. This property determines the maximum amount of time the alert's sound event sequence is considered to be "still playing". After the timeout duration, the system proceeds as if the sequence has finished playing, regardless of whether all the sound events in the sequence have actually finished playing. This behavior is included to prevent the system from permanently locking up if a sound event fails to play. If timeout is not included in the definition, it defaults to 10000 milliseconds.

Activating Alerts

Once an alert is registered with the system, it carries an activation state and can either be inactive or active at any given time. When an alert transitions from inactive to active, an instance of the alert is queued to play. When an alert transitions from active to inactive, the activated instance will be removed from the queue. If the activated instance of the alert is already playing when it is deactivated, it will continue to play. Repeating alerts are queued to play again when they finish playing as long as they are active.

To activate and deactivate alerts, use the event bus:

import { AuralAlertControlEvents } from '@microsoft/msfs-sdk';

// Activates the alert with ID 'no-takeoff'.
bus.getPublisher<AuralAlertControlEvents>().pub('aural_alert_activate', 'no-takeoff', true, false);

// Deactivates the alert with ID 'no-takeoff'.
bus.getPublisher<AuralAlertControlEvents>().pub('aural_alert_deactivate', 'no-takeoff', true, false);
caution

When publishing any of the topics defined by AuralAlertControlEvents, you must specify that the topic be synced to other instruments and not cached (the third and fourth parameters of pub(), respectively). Failure to specify these options will result in incorrect behavior.

tip

Activating an alert that is already active has no effect (the alert will not be queued to play again). If you wish to play alerts in response to a discrete event rather than a state transition, the trigger function may be more useful.

Triggering Alerts

Sometimes you will want an alert to play as a response to a discrete event instead of a state transition. You can do this by triggering the alert instead of activating it.

Each alert has a trigger state in addition to its activation state; an alert can either be triggered or untriggered. When an alert transitions from untriggered to triggered, an instance of the alert is queued to play. Once a triggered alert is finished playing, it automatically reverts to the untriggered state. You may also manually untrigger an alert. When an alert transitions from triggered to untriggered, the triggered instance will be removed from the queue. If the triggered instance of the alert is already playing when it is untriggered, it will continue to play. Triggered alerts do not repeat.

Triggered instances of alerts are treated entirely independently of activated instances. The activation state of an alert has no effect on any triggered instances and vice versa. If an alert is activated and triggered at the same time, both the activated and triggered instances will be queued to play and the alert will be heard twice.

To trigger and untrigger alerts, use the event bus:

import { AuralAlertControlEvents } from '@microsoft/msfs-sdk';

// Triggers the alert with ID 'no-takeoff'.
bus.getPublisher<AuralAlertControlEvents>().pub('aural_alert_trigger', 'no-takeoff', true, false);

// Untriggers the alert with ID 'no-takeoff'.
bus.getPublisher<AuralAlertControlEvents>().pub('aural_alert_untrigger', 'no-takeoff', true, false);

Customizing Alert Instances

You are allowed to customize the instance of an alert that is queued to play when activating and triggering alerts. In doing so, you alter how the alert instance is played compared to how the alert was defined during registration. You can change the alert's sound event sequence, whether it is continuous, and whether it is repeating. A customized alert always retains its original queue and priority.

To activate/trigger a customized alert instance, send an AuralAlertActivation object as the event data instead of just an ID string when activating/triggering the alert:

const manager = new AuralAlertRegistrationManager(bus);

manager.register({
uuid: 'no-takeoff',
queue: 'my-queue',
priority: 0,
sequence: 'aural_no_takeoff',
continuous: false,
repeat: true
});

// Activates the alert with ID 'no-takeoff'.
// The alert will play the 'aural_no_takeoff' sound event twice instead of repeatedly.
bus.getPublisher<AuralAlertControlEvents>().pub('aural_alert_activate', {
uuid: 'no-takeoff',
sequence: ['aural_no_takeoff', 'aural_no_takeoff'],
repeat: false
}, true, false);
tip

Activating or triggering a customized alert instance while the alert is already active or triggered, respectively, has no effect. The new custom instance will not replace the one that is already activated/triggered. To force the new custom instance to replace the existing one, you must deactivate/untrigger the alert first.

Alert Aliases

Normally, only one activated and one triggered instance of an alert can be queued simultaneously. However, there are cases where you may want to queue multiple similar and related alerts simultaneously. One solution would be to register a separate alert for each simultaneous instance that can be queued. But this isn't possible if you don't know beforehand how many simultaneous instances can be queued, or if the number of queued instances is unbounded.

To get around the above limitations, Aural Alert System allows you to activate and trigger alerts using aliases. When an alert is activated/triggered with an alias, the alias effectively replaces the alert's registered ID string for that instance of the alert. An alert that is activated/triggered without an explicit alias can be considered to spawn an instance with an alias equal to its registered ID. The system treats alert instances with different aliases as separate alerts. Therefore, alert instances with different aliases are allowed to co-exist in the queue, even if they were spawned from the same alert.

caution

Alert aliases, like IDs, must be unique. Do not choose aliases that match existing alert IDs or other aliases that are in use.

When deactivating or untriggering aliased alerts, you must use the alias everywhere an ID is normally expected.

The following example shows how to use aliased alerts to queue a separate alert whenever a new traffic advisory is issued:

/**
* Called when a new traffic advisory is issued.
* @param threatId The ID of the intruder.
* @param bus The event bus.
*/
function onTrafficAdvisoryIssued(intruderId: string, bus: EventBus): void {
bus.getPublisher<AuralAlertControlEvents>().pub('aural_alert_activate', {
uuid: 'traffic',
alias: `traffic-${intruderId}`
}, true, false);
}

/**
* Called when a traffic advisory is cancelled.
* @param threatId The ID of the intruder.
* @param bus The event bus.
*/
function onTrafficAdvisoryCancelled(threatId: string, bus: EventBus): void {
bus.getPublisher<AuralAlertControlEvents>().pub('aural_alert_deactivate', `traffic-${intruderId}`, true, false);
}

Alert Suffixes

Suppose you want to queue an alert if and only if at least one of a number of separate conditions has been met. Aural Alert System supports this type of logic through the use of alert suffixes.

When an alert is activated or triggered using a suffix, the activation/trigger state of each suffix is tracked independently. The activation/trigger state of the alert as a whole then becomes dependent on the states of the suffixes such that the alert is considered to be active or triggered if and only if at least one of its suffixes is active or triggered, respectively. An alert that is activated/triggered without an explicit suffix can be considered to activate/trigger the empty suffix, which is treated the same as any other suffix. Suffixes are applied on a per-alias basis, so activating/triggering a suffix on alias A has no effect on the activation/trigger state of alias B.

When deactivating or untriggering suffixed alerts, you must use a suffixed form of the alert ID/alias equal to `${uuid}::${suffix}` everywhere an ID is normally expected.

The following example shows how to use suffixed alerts to implement a master warning alert that is queued to play as long as one warning is active:

/**
* Called when a warning is activated.
* @param warningId The ID of the warning.
* @param bus The event bus.
*/
function onWarningActivated(warningId: string, bus: EventBus): void {
bus.getPublisher<AuralAlertControlEvents>().pub('aural_alert_trigger', {
uuid: 'master-warning',
suffix: warningId
}, true, false);
}

/**
* Called when a warning is deactivated.
* @param warningId The ID of the warning.
* @param bus The event bus.
*/
function onWarningDeactivated(warningId: string, bus: EventBus): void {
bus.getPublisher<AuralAlertControlEvents>().pub('aural_alert_untrigger', `master-warning::${warningId}`, true, false);
}

AuralAlertSystem Wake/Sleep State

When instantiating AuralAlertSystem, it is initialized to a sleeping state. While asleep, it will continue to track alert state but will not play any sounds. This is meant to model what would occur when the avionics' sound subsystem is turned off. To wake the system, call the wake() method. Waking the system will cause it to queue any continuous or repeating alerts that are active. Active alerts that are neither continuous nor repeating will not be queued. However, any alert that becomes activated or triggered while the system is awake will be queued normally.

Calling the sleep() method will put the system back to sleep. Putting the system to sleep will immediately unqueue all alerts and untrigger any triggered alerts. Activated alerts will remain active until they are deactivated normally. Any alerts that are already playing will be stopped at the earliest opportunity without waiting for them to finish. While the system is asleep, alerts cannot be triggered. Alerts can still be activated but will not be queued.

Using CasAuralAlertTransporter

A common pattern found in many avionics systems is to bind the activation state of aural alerts to CAS (crew alerting system) alerts. If your CAS is implemented using the CAS System API, you can use CasAuralAlertTransporter to activate alerts based on whether a particular CAS alert is displayed as a message.

Let's say that we want to activate the no-takeoff aural alert whenever the associated cas-no-takeoff CAS alert is displayed as a message with warning priority:

import { AnnunciationType, CasAuralAlertTransporter } from '@microsoft/msfs-sdk';

CasAuralAlertTransporter.create(
bus, // event bus
'no-takeoff', // aural alert ID
undefined, // optional AuralAlertActivation object to customize the aural alert
'cas-no-takeoff', // CAS alert ID
AnnunciationType.Warning, // CAS alert priority level
undefined, // CAS alert suffix - in this case the alert has no suffixes
true // whether to activate the aural alert if the CAS alert has been acknowledged
);

When creating a CasAuralAlertTransporter, you can optionally provide a reference to CasSystem. Doing so will ensure that the aural alert activation state is initialized correctly (e.g. if the bound CAS alert is already displayed when the transporter is created). If the reference is not provided, then care must be taken to create the transporter before the bound CAS alert has a chance to be activated and displayed.

For more detailed information on CasAuralAlertTransporter, please refer to the API documentation.