SoundServer
Introduction
The sim's native API for playing sounds from JS/HTML instruments (Coherent.call('PLAY_INSTRUMENT_SOUND', soundId)
) is functional but bare. The SDK class SoundServer
provides a richer interface for playing sounds. With SoundServer, you can queue sounds, play multiple sound events in sequence, loop sounds, and be notified when sounds are finished playing.
Setting Up SoundServer
SoundServer was designed to be lightweight. Using it only requires a single instance of the SoundServer
class and access to the event bus.
The first step to using SoundServer is to create an instance of the SoundServer
class. There should be only one instance of SoundServer
across all instruments in your airplane. Once you have an instance of SoundServer
, you need to hook up its onSoundEnd()
callback method to the identically named callback in BaseInstrument
.
import { EventBus, SoundServer } from '@microsoft/msfs-sdk';
class MyInstrument extends BaseInstrument {
private readonly bus = new EventBus();
private readonly soundServer = new SoundServer(this.bus);
// ...
public onSoundEnd(soundEventId: Name_Z): void {
super.onSoundEnd(soundEventId);
this.soundServer.onSoundEnd(soundEventId);
}
}
Once the above items are completed, the final step is to ensure that all code that interacts with SoundServer
waits until the server is initialized. To know exactly when the server is initialized, you should subscribe to the sound_server_initialized
topic on the event bus:
import { SoundServerEvents } from '@microsoft/msfs-sdk';
bus.getSubscriber<SoundServerEvents>().on('sound_server_initialized').handle(isInit => {
if (isInit) {
// Do things...
}
});
Using Sound Packets
SoundServer operates using the concept of sound packets. A sound packet is the fundamental "unit" of sound that can be played with SoundServer. Each sound packet has the following properties:
key
: A string that is used to reference the packet. Packets with the same key are not allowed to play at the same time. When a packet is queued, it is queued with other packets with the same key.sequence
: A string or array of strings that define the sound events that are played for the packet. Each string in the sequence should be an avionics sound event ID defined in the airplane'ssound.xml
. If an array of strings is used, then each sound event in the array is played in order. Sound events are also known as sound atoms because they are the shortest bits of sound that can be played, and once a sound event starts playing it cannot be stopped until it ends.continuous
: Whether the packet's sound event sequence plays in an infinite loop.timeout
: The maximum amount of time the packet'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 the packet is continuous, then the timeout is reset every time the packet loops to the beginning of its sequence. The default timeout is 10000 milliseconds.
To play sound packets, use the event bus to send commands to SoundServer
. There are three different commands that will cause a packet to be played, each with slightly different behavior:
import { SoundServerControlEvents } from '@microsoft/msfs-sdk';
// This will play the sound packet if and only if another packet with the same key is not currently playing.
bus.getPublisher<SoundServerControlEvents>().pub('sound_server_play', {
key: 'my-packet',
sequence: 'my_sound_event',
continuous: false
}, true, false);
// This will queue the sound packet to be played at the earliest opportunity. If no packet with the same key is
// already playing or queued, then the packet will be played immediately. Otherwise, it will be queued to play after
// all other packets with the same key that are currently playing or queued have finished playing.
bus.getPublisher<SoundServerControlEvents>().pub('sound_server_queue', {
key: 'my-packet',
sequence: 'my_sound_event',
continuous: false
}, true, false);
// This will play the sound packet at the earliest opportunity. If no packet with the same key is already playing,
// then the packet will be played immediately. Otherwise, the currently playing packet with the same key will be
// stopped at the earliest opportunity (remember that sound atoms cannot be interrupted while they are playing), any
// queued packets with the same key will be discarded, and the new packet will start playing once the currently playing
// packet has been stopped.
bus.getPublisher<SoundServerControlEvents>().pub('sound_server_interrupt', {
key: 'my-packet',
sequence: 'my_sound_event',
continuous: false
}, true, false);
When publishing any of the topics defined by SoundServerControlEvents
, 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.
The sim does not allow more than one instance of the same avionics sound event to be played simultaneously. Attempting to play a sound event while it is already playing simply has no effect. Therefore, it is recommended to avoid playing sound packets with different keys that also contain the same sound event at the same time. Doing so will not cause any errors, but the results will likely not match the original intention.
You can also command SoundServer
to stop packets from playing once they are already playing or queued by referencing their keys:
import { SoundServerControlEvents } from '@microsoft/msfs-sdk';
// This will stop a continuous sound packet from looping. Has no effect on non-continuous packets that are already
// playing. Any queued packets with the specified key will be discarded.
bus.getPublisher<SoundServerControlEvents>().pub('sound_server_stop', 'my-packet', true, false);
// This will stop an already playing packet at the earliest opportunity (remember that sound atoms cannot be
// interrupted while they are playing). Any queued packets with the specified key will be discarded.
bus.getPublisher<SoundServerControlEvents>().pub('sound_server_kill', 'my-packet', true, false);
// Same as 'sound_server_stop', but for all packets.
bus.getPublisher<SoundServerControlEvents>().pub('sound_server_stop_all', undefined, true, false);
// Same as 'sound_server_kill', but for all packets.
bus.getPublisher<SoundServerControlEvents>().pub('sound_server_kill_all', undefined, true, false);
Playing Simple Sounds
Sometimes you just need to play a simple sound event and don't need the fancy sequencing or queueing features of SoundServer. To facilitate these use cases, SoundServer supports simplified commands:
import { SoundServerControlEvents } from '@microsoft/msfs-sdk';
bus.getPublisher<SoundServerControlEvents>().pub('sound_server_play_sound', 'my_sound_event', true, false);
// ... is equivalent to ...
bus.getPublisher<SoundServerControlEvents>().pub('sound_server_play', {
key: 'my_sound_event',
sequence: 'my_sound_event',
continuous: false
}, true, false);
// ---------------------------------------------------------------------------------------------------------
bus.getPublisher<SoundServerControlEvents>().pub('sound_server_start_sound', 'my_sound_event', true, false);
// ... is equivalent to ...
bus.getPublisher<SoundServerControlEvents>().pub('sound_server_play', {
key: 'my_sound_event',
sequence: 'my_sound_event',
continuous: true
}, true, false);
// ---------------------------------------------------------------------------------------------------------
bus.getPublisher<SoundServerControlEvents>().pub('sound_server_stop_sound', 'my_sound_event', true, false);
// ... is equivalent to ...
bus.getPublisher<SoundServerControlEvents>().pub('sound_server_stop', 'my_sound_event', true, false);
Subscribing to Sound Packet Events
If you need to know when a sound packet starts or finishes playing, you can subscribe to the following event bus topics:
import { SoundServerEvents } from '@microsoft/msfs-sdk';
bus.getSubscriber<SoundServerEvents>().on('sound_server_packet_started').handle(key => {
console.log(`A sound packet with key: ${key} has started playing.`);
});
bus.getSubscriber<SoundServerEvents>().on('sound_server_packet_ended').handle(key => {
console.log(`A sound packet with key: ${key} has finished playing.`);
});
Using SoundServerController
For convenience, the SoundServerController
class is provided to abstract away some of the boilerplate associated with publishing commands to SoundServer
over the event bus. For each command topic in SoundServerControlEvents
, there is a corresponding method on SoundServerController
which publishes the command. The controller also facilitates waiting for SoundServer
to be initialized. For example:
import { SoundServerController } from '@microsoft/msfs-sdk';
const controller = new SoundServerController(bus);
// Wait for SoundServer to be initialized...
controller.awaitInitialized().then(() => {
controller.play({
key: 'my-packet',
sequence: 'my_sound_event',
continuous: false
});
controller.queue({
key: 'my-packet',
sequence: 'my_sound_event',
continuous: false
});
// ...
});