Coherent UI  2.5.3
A modern user interface library for games
 All Classes Namespaces Functions Variables Enumerations Enumerator Pages
Sample - Client Audio Playback

This sample demonstrates the usage of the Web Audio notifications that Coherent UI provides through the Coherent::UI::ViewListener class and its derivates.

Note
Supplied code is not representative for a well-designed, high-performance solution. It's written for simplicity and its purpose is to demonstrate a feature of Coherent UI.

Building the sample

The sample solution is already configured and you only have to compile and run it.

It uses a sample mini game framework that provides very basic functionality.

The output will be in the Coherent/Samples/UI/bin directory.

Key points

  1. You should enable client audio playback in order to receive notifications.
  2. There are 4 notifications that you should handle. Those are the stream Create/Play/Pause/Close events. The Create notification will contain information about the audio stream format. More details about each one follow in the sample walkthrough.
  3. When you receive a Play notification, you should start consuming data using the Coherent::UI::View::GetAudioData API.
  4. It's imperative to keep getting data until a Pause notification is received since this API call informs the Coherent UI Host that the stream playback has progressed.
  5. Upon receiving the Close notification you can release all related resources.
Warning
If you're using a low-level audio API such as OpenAL, you should always keep an eye for buffer underruns. Depending on your audio system design, calls to Coherent::UI::View::GetAudioData might stop before a stream Pause notification is received, causing possible deadlocks.

Simple Audio System Interface

The sample utilizes a very basic audio system framework. It consists of the following interfaces:

  • IAudioSystem - manages audio device creation and update, sound creation and destruction
  • ISound - provides an API for sound manipulation
  • IMoreDataProvider - and interface that can be passed to an ISound, which will then be used as a callback when more audio data needs to be buffered.

The sample provides two implementations - using OpenAL and FMOD.

Note
If you want to use FMOD, you need to download the FMOD Studio Programmer API (http://www.fmod.org/download/). Then, you should copy the inc and lib directories located in the FMOD_SDK/api/lowlevel folder into Coherent/Samples/UI/C++/Sample_ClientAudio/external/FMOD.

Sample walkthrough

The first important thing to do when you want to play web audio yourself is to use the EnableClientAudioPlayback property in the ContextSettings structure before initializing the View Context:

settings.EnableClientAudioPlayback = true;

The sample view listener handles the web audio stream notifications. Those are:

void OnAudioStreamCreated(
int streamId, int channels, int bitDepth, int frequency);
void OnAudioStreamPlay(int streamId);
void OnAudioStreamPause(int streamId);
void OnAudioStreamClose(int streamId);

The view listener keeps a collection of the streams and information about them. When the OnAudioStreamCreated event is received, a new entry in the collection is added with information about the stream with the received ID. This information also contains a pointer to an ISound instance, which is null initially. The information is contained in this structure:

struct AudioStreamInfo
{
int Channels;
int BitDepth;
int Frequency;
ISound* Sound; // null initially
};

When the OnAudioStreamPlay event is received, a new ISound is created that will be responsible for playing the data from the stream. The entry in the collection for the corresponding stream ID is updated so we can identify the ISound by stream ID. A reverse map is also maintained so we can identify the stream ID by an ISound instance. Next, a very important call is made. The newly created ISound instance is assigned an IMoreDataProvider - in the case of this sample, this is the view listener, which implements the interface.

Now that the ISound is ready for playing, we have to provide the actual audio data. The ISound uses the IMoreDataProvider that was set to get it. In our case, as we already saw, the IMoreDataProvider is the view listener. The view listener is a convenient audio data provider since it already has access to the Coherent::UI::View instance. The Coherent::UI::View instance is used to obtain more data using the Coherent::UI::View::GetAudioData API. The GetAudioData method provides the ISound instance which requested the data. Using the reverse map in the view listener, we can obtain the stream ID for the corresponding ISound and use it in the API call.

Here's the whole method:

virtual int OnMoreData(void* invoker, void* buffer, int capacityInBytes)
{
int total = 0;
if (m_View)
{
// m_SoundToIDMap is the reverse map (ISound -> stream ID)
auto streamIt = m_SoundToIDMap.find(static_cast<ISound*>(invoker));
if (streamIt != m_SoundToIDMap.end())
{
total = m_View->GetAudioData(
streamIt->second, buffer, capacityInBytes, 0);
}
}
return total;
}
Note
Coherent::UI::View::GetAudioData is a synchronous call
Warning
Coherent::UI::View::GetAudioData must be called continuously after OnAudioStreamPlay until a OnAudioStreamPause event is received. Failure to do so can result in program exceptions and deadlocks.
Note
Coherent::UI::View::GetAudioData can be called from any thread but it's recommended to invoke it from the same thread that does Coherent::UI::ViewContext::Update. Usually, GetAudioData will be called from the audio dedicated thread, in which case it's recommended that you use the same thread for managing you Audio and UI calls.

When the OnAudioStreamPause event is received, we can release the ISound instance or pause it. In this sample we're just releasing it and creating new ones on each 'Play'.

When the OnAudioStreamClose event is received we just clear the information about the corresponding stream ID.

As a very brief summary, when we receive a Create notification we save the information about the stream; when we receive a Play notification we create a new sound and continuously request data until we receive Pause. The requested data is played by the audio system. Upon Close we delete the saved information.

We encourage you to play with the sample for a more hands-on experience.