Play audio data

Once you've opened and configured a PCM playback device and prepared the PCM subchannel, you're ready to play back sound data.

To open and configure PCM playback devices and configure a subchannel, see Handle PCM devices.

You must set the permissions for your application to allow it to play sounds. To set these permissions in the Momentics IDE for BlackBerry, open the project's bar-descriptor.xml file and select the Play Sounds check box on the Application tab. Alternatively, you can edit the bar-descriptor.xml file by hand to add the play_audio element.

If your application has the option to produce playback data in multiple formats, choosing a format that the hardware supports directly will reduce the CPU requirements.

Playback states

Let's consider the state transitions for a PCM device during playback.

State diagram showing state transitions for PCM devices during playback.

The transition between SND_PCM_STATUS_* states is the result of executing an API call, or the result of conditions that occur in the hardware:

From To Cause
NOTREADY READY Calling snd_pcm_channel_params() or snd_pcm_plugin_params()
READY PREPARED Calling snd_pcm_channel_prepare(), snd_pcm_playback_prepare(), or snd_pcm_plugin_prepare()
PREPARED RUNNING Calling snd_pcm_write(), snd_pcm_plugin_write(), snd_pcm_channel_go(), or against the capture file descriptors, snd_pcm_playback_go()
RUNNING PAUSED Calling snd_pcm_channel_pause() or snd_pcm_playback_pause()
PAUSED RUNNING Calling snd_pcm_channel_resume() or snd_pcm_playback_resume()
PAUSED PREPARED Calling snd_pcm_playback_resume()
PAUSED CHANGED The stream changed or an event occurred
PREPARED CHANGED The stream changed or an event occurred
RUNNING UNDERRUN The hardware buffer became empty during playback
RUNNING UNSECURE The application marked the stream as protected, the hardware level supports a secure transport (e.g., HDCP for HDMI), and authentication was lost
RUNNING CHANGED The stream changed
RUNNING ERROR A hardware error occurred
UNDERRUN, UNSECURE, CHANGE, or ERROR PREPARED Calling snd_pcm_channel_prepare(), snd_pcm_playback_prepare(), or snd_pcm_plugin_prepare()
RUNNING PREEMPTED Audio is blocked because another libasound session has initiated playback, and the audio driver has determined that that session has higher priority

For more details on these transitions, see the description of each function in the Audio Library chapter.

Send data to the PCM subchannel

The function that you call to send data to the subchannel depends on whether or not you're using plugin converters.

The number of bytes written must be a multiple of the fragment size, or the write will fail.
The plugin accumulates partial writes until a complete fragment can be sent to the driver.

A full nonblocking write mode is supported if the application can't afford to be blocked on the PCM subchannel. You can enable nonblocking mode when you open the handle or by calling snd_pcm_nonblock_mode().

This approach results in a polled operation mode that isn't recommended.

Another method that your application can use to avoid blocking on the write is to call select() to wait until the PCM subchannel can accept more data. It allows the program to wait on user input while at the same time sending the playback data to the PCM subchannel.

To get the file descriptor to pass to select(), call snd_pcm_file_descriptor().

With this technique, select() returns when there's space for frag_size bytes in the subchannel. If your application tries to write more data than this, it may block on the call.

If the PCM subchannel stops during playback

When playing back, the PCM subchannel stops if the hardware consumes all the data in its buffer.

This can happen if the application can't produce data at the rate that the hardware is consuming data. A real-world example of this is when the application is preempted for a period of time by a higher-priority process. If this preemption continues long enough, all data in the buffer may be played before the application can add any more.

When this happens, the subchannel changes state to SND_PCM_STATUS_UNDERRUN. In this state, it doesn't accept any more data (i.e., snd_pcm_write() and snd_pcm_plugin_write() fail) and the subchannel doesn't restart playing.

The only ways to move out of this state are to close the subchannel or to reprepare the channel as you did before (see Preparing the PCM subchannel, earlier in this chapter). This forces the application to recognize and take action to get out of the underrun state; this is primarily for applications that want to synchronize audio with something else. Consider the difficulties involved with synchronization if the subchannel simply were to move back to the SND_PCM_STATUS_RUNNING state from underrun when more data became available.

Stop playback

If the application wishes to stop playback, it can simply stop sending data and let the subchannel underrun as described above, but there are better ways.

If you want your application to stop as soon as possible, call one of the drain functions to remove any unplayed data from the hardware buffer:

If you want to play out all data in the buffers before stopping, call one of:

Synchronize with the PCM subchannel

QSA provides some basic synchronization capabilities.

Your application can find out where in the stream the hardware play position is. The resolution of this position is entirely a function of the hardware driver; consult the specific device driver documentation for details if this is important to your application.

The API calls to get this information are:

Both of these functions fill in a snd_pcm_channel_status_t structure. You'll need to check the following members of this structure:

The hardware play position, in bytes relative to the start of the stream since the last time the channel was prepared. The act of preparing a channel resets this count.
The play position, in bytes relative to the total number of bytes written to the device.

The count member isn't used if the mmap plugin is used. To disable the mmap plugin, call snd_pcm_plugin_set_disable().

For example, consider a stream where 1,000,000 bytes have been written to the device. If the status call sets scount to 999,000 and count to 1000, there are 1000 bytes of data in the buffer remaining to be played, and 999,000 bytes of the stream have already been played.

Last modified: 2014-11-17

Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus