The ACCESS Linux Platform audio subsystem is designed for files that are already decoded, such as WAV files with PCM data. It works with audio streams; you have the option of writing your own encoder and decoder that work with raw data streams. In this case you can use an audio stream to transfer the encoded or decoded data.
The audio subsystem supports recording and playback within the confines outlined above. For more complex operations, including working with other audio formats and audio combined with video, you'll want to use a Media Session. See Chapter 2, "Media Session," for details.
Architecture
The ACCESS Linux Platform audio subsystem divided into three major components: the Audio Manager, the Audio Device Manager, and the ALSA driver. Figure 3.1 illustrates the design of the audio subsystem.
Figure 3.1 Audio subsystem architecture
Note that in a phone product based on dual-core or dual-chip technology, there are some audio streams that are not generated by the application processor. For example, the phone call voice stream comes from the modem chip. Or, an FM radio stream would come from a separate FM chip. Although such streams are externally generated, they are controlled by the Audio Manager as if they were generated by the application processor, allowing audio routing and conflict resolution rules to be applied correctly. See Appendix , "The Routing Table," for more on the routing table and its rules.
Audio Manager
The Audio Manager is a Linux daemon that is launched at system startup. It implements the API requests for audio support. Using a routing table it determines which device to output sounds to (speaker, headphones, Bluetooth headset, etc.) based upon the current device configuration.
The Audio Manager includes a shared library, libalp_audiomgr.so, that is used by developers making API calls to manage various aspects of sound in their application.
Audio Device Manager
The Audio Device Manager is itself divided into two sections: a daemon which is used to listen the status of audio devices such as headphones, and Bluetooth accessories; and the Audio Device Library, which is a set of device operation functions which are called by the Audio Manager.
The Audio Device Library is a set of abstract interfaces based on the ALSA library. It provides full operation of audio device functions such as PCM data write and read, volume setting, PCM format setting, router control, query functions for audio device status, and so on. The daemon listens for device status notifications from the ALSA driver and transfers them to the Audio Manager. Communication between the daemon and ALSA driver uses Netlink-connector; the socket IPC mechanism is used between the Audio Manager and the device daemon.
ALSA Driver
ALSA stands for Advanced Linux Sound Architecture. It is a suite of hardware drivers, libraries and utilities which provide audio and MIDI functionality for the Linux operating system. ALSA is an open-source effort; the official web site of the ALSA project is: http://www.alsa-project.org/.
The ALSA driver provides the interface between the audio subsystem components and ALSA itself.
The Routing Table
Additional logic is built into the Audio Manager through the use of a device-specific routing table. This table, constructed by the device manufacturer, contains a series of instructions (or "rules") for directing sound based on a number of factors including:
- device status, such as whether or not a given audio peripheral is plugged in
- conditions, or states. For instance, whether or not a phone call is currently active. A condition might have a physical correspondence—say, to indicate the position of a "ringer off" switch.
- the type of sound you are requesting.
- the preferred behavior when sound is disabled.
Based on the above factors, the Audio Router uses rules in the routing table to determine which output device each audio stream should be directed to, and which volume setting to respect. Under certain conditions, the table determines when it's appropriate to mute, attenuate, or stop entirely the requested sound. For instance, if a stream of a given type is playing and another stream of the same type is started, depending upon how the device manufacturer has set up the routing table,
- the two streams may be mixed, so that the user hears both at the requested stream volume levels,
- the two streams may be mixed, but one of them may have its volume attenuated so that the other takes priority,
- the first stream may be muted so that only the second stream is heard,
- the first stream may be stopped altogether, so that only the second stream is heard.
As conditions and device status change, streams are automatically re-routed as necessary. If, for instance, the user is listening to an MP3 file through the built-in speaker, and they plug in a set of headphones, the sound will be redirected from the speaker to the headphones (assuming that the appropriate routing table rules are in place). If the user is listening to an MP3 file and a phone call comes in, the MP3 playback might be paused for the duration of the call, and resumed when the call terminates (again, depending upon how the device manufacturer has configured the routing table). Rules can be set so that if the user has no way to hear music playback, the stream is automatically halted—this to conserve battery power. If, due to changing device status or conditions the Audio Manager is forced to step in and affect your audio stream—by halting it, by muting it, or by attenuating it, for instance—or if the path from the audio source to its final destination changes, the Audio Manager broadcasts a notification that allows you to take any necessary action. Details of these notifications and how you can register to receive them is documented in "Handling Outside Events." To obtain a list of all routing destinations in the path, see the reference documentation for the alp_snd_get_current_routepath() function.
Because of the routing table, you don't generally direct sound to or from a specific output device. Rather, you simply inform the Audio Manager of your sound stream type, and the Audio Manager, after consulting the routing table and considering the current situation, directs the sound to the appropriate device. See "Opening an Audio Object" for specifics on specifying the sound stream type.
Using the Audio Manager Library
Applications needing to process audio that is already decoded should use the Audio Manager APIs rather than create a media session. Examples of when to use the Audio Manager include handling WAV files containing PCM data, data streams decoded through a media session, and incoming voice from a telephone call.
Be aware that the Audio Manager is also used behind the scenes by other components, such as a media session requiring audio output.
Audio Manager Headers and Libraries
The Audio Manager is distinct from the ACCESS Linux Platform Multimedia library. The file libalp_audiomgr.so is the client-side portion of the Audio Manager library. It should be included in your makefile, along with the rest of the libraries you will be linking against (add alp_audiomgr to the list of included libraries).
The header file used with audio is media_audiomgr.h. It defines the structures, constants and API declarations related to audio. To use this file add the following line to your code:
#include <alp/media_audiomgr.h>
Note that if you are mixing Audio Manager calls in with media session calls then you will need to include the media session header files in your application. See "Media Session Headers and Libraries."
Playing and Recording Audio
The code in Listing 3.1 is a simple command-line program that plays back raw audio using the Audio Manager APIs. Recording is an analogous operation although instead of calling alp_snd_write() you would call alp_snd_read(), and instead of initiating the operation by passing ALP_SND_CTL_PLAY to alp_snd_ioctl(), you would instead pass ALP_SND_CTL_RECORD. But the basic structure of the program applies whether your are recording or playing audio.
Listing 3.1 Simple audio playback example
#include <gtk/gtk.h> // GTK/GDK/GLib declarations #include <glade/glade.h> // libglade declarations #include <alp/alp.h> // ACCESS Linux Platform declarations #include <alp/media_audiomgr.h> #include <pthread.h> #include <fcntl.h> #include <signal.h> #include "myapp.h "// Application specific declarations #define APP_GLADE_FILE "app.glade" int snd_fd; int play_end = 0; static void *handle = NULL; ssize_t safe_read(int fd, void *buf, size_t count){ ssize_t result = 0, res; while (count > 0) { if ((res = read(fd, buf, count)) == 0) break; if (res < 0) return result > 0 ? result : res; count -= res; result += res; buf = (char *)buf + res; } return result; } #define BLOCK_SIZE 1024 void play_thread(void){ int len; unsigned char *pbuf; pbuf = (unsigned char *) malloc(BLOCK_SIZE); while ((len = safe_read(snd_fd, pbuf, BLOCK_SIZE)) > 0) { if(alp_snd_write(handle, pbuf, len) != ALP_STATUS_SND_OK) { printf("alp_snd_write error\n"); break; } } printf("play_thread exit\n"); free(pbuf); play_end = 1; } int player_init(char *pathToAudio) { unsigned int i; int ret; pthread_t play_id; AlpSndPcmFmt format; if ((snd_fd = open(pathToAudio, O_RDONLY, 0)) == -1) { perror(pathToAudio); exit(EXIT_FAILURE); } alp_snd_open(&handle, "audio_out", ALP_SND_MODE_PLAYBACK); format.bits = 8; format.channels = 1; format.rate = 24000; alp_snd_ioctl(handle, ALP_SND_SET_FORMAT, &format); ret = pthread_create(&play_id, NULL, (void *)play_thread, NULL); if (ret != 0) { printf("create pthread error\n"); alp_snd_close(handle); return -1; } alp_snd_ioctl(handle, ALP_SND_CTL_PLAY, NULL); return 0; } APP_EXTERNC void on_play_button_clicked(GtkWidget *button, gpointer userdata) { alp_snd_ioctl(handle, ALP_SND_CTL_PLAY, NULL); } APP_EXTERNC void on_stop_button_clicked(GtkWidget *button, gpointer userdata) { alp_snd_ioctl(handle, ALP_SND_CTL_STOP, NULL); } APP_EXTERNC void on_pause_button_clicked(GtkWidget *button, gpointer userdata) { alp_snd_ioctl(handle, ALP_SND_CTL_PAUSE, NULL); } /*! * Standard application exit handler. */ static void onExitApp(gpointer userdata) { alp_snd_close(handle); gtk_main_quit(); } int alp_main(int argc, char *argv[]) { GladeXML *glade; g_print(">>> Entered %s ===\n", __DATE__ " " __TIME__); // Register exit handler alp_app_add_exit_handler(onExitApp, NULL); // Init GTK gtk_init(&argc, &argv); glade = alp_bundle_acquire_glade_xml(APP_GLADE_FILE, NULL); // Activate Glade signal handlers glade_xml_signal_autoconnect(glade); player_init("/var/home/audio/test.wav"); // Run the user interface gtk_main(); g_print("<<< Exit\n"); return 0; }
The Audio Manager works with raw data streams: data streams that are not encoded. Media files that do not require decoding, such as a WAV file, contain PCM data. The data in these files is already uncompressed and can be read directly into an audio stream without needing a decoder.
If you are working with data that is, or needs to be, encoded, let the Media Engine process your audio by using its Media Session APIs. The Media Engine loads the appropriate codec for you, and using the Audio Manager it creates a raw data stream (using the "audio_out" stream type) behind the scenes.
Opening an Audio Object
If you chose to work directly with raw data you will first need to open an audio object (equivalent to an audio stream; the terms are essentially interchangeable) with alp_snd_open(). The parameters to this function are:
- A pointer to a void pointer, which serves as the "handle" that links the various Audio Manager calls together. When calling any of the other Audio Manager functions (except for
alp_snd_notify()), pass the void pointer as the first parameter. - A C string containing the sound stream type. Valid strings are defined in the routing table, and thus can vary from device to device. See "Platform-Defined Sound Stream Types," below, for a listing of the standard sound stream types defined for the platform by ACCESS.
- The final parameter indicates the mode: either
ALP_SND_MODE_PLAYBACKorALP_SND_MODE_RECORD.
Once you have successfully opened your audio stream you can either read sound bytes from the stream or write sound bytes to the stream, depending upon whether you opened the stream for recording or for playback.
Platform-Defined Sound Stream Types
The standard output stream types supported by the platform are:
- button_sounds
- Sounds that are played when a button is pressed.
- audio_out
- Most audio, including that produced by a media player. The Media Engine sends its audio through a stream of this type.
- phone_call
- Ring tones.
- system_sounds
- System sounds that can't be classified into one of the other listed sound stream types.
- alerts
- Sounds used to get the user's attention.
- alarm
- Sounds that are generated when an alarm is triggered.
- message
- "New Message" sound, such as would be played when an SMS or MMS message is received.
- camera_sound
- Shutter sound, or the sound that might be played to signify the start of a recording session.
- power_sound
- Power on/off sound.
- game_sound
- Sounds produced by games.
- DTMF
- DTMF (telephone dialing) sounds.
In addition to the above, which are all used when producing sounds, the following two input stream types are defined by the platform, for use when recording:
- voice_record
- Audio recording, typically from a microphone.
- phone_record
- Recording of telephone calls.
Specifying the Audio Format
After opening the audio object, but before you being to play or record, you need to set the audio format. The format consists of three values:
- Bits per sample
- 8, 16, or 24
- Channels
- 1 or 2
- Sample rate
- 8000, 11025, 16000, 22050, 24000, 32000, 44100, and 48000
Allocate an AlpSndPcmFmt structure, set the values as appropriate for your application, and then use the ioctl function, as shown here:
AlpSndPcmFmt format; format.bits = 8; format.channels = 1; format.rate = 24000; alp_snd_ioctl(handle, ALP_SND_SET_FORMAT, &format);
IMPORTANT: You must set the audio format before attempting to play or record. The audio object does not have a default format.
Playing Audio
To play raw data to a specific device, first open the audio object in ALP_SND_MODE_PLAYBACK mode. Next, as was shown in Listing 3.1, spawn a thread that, in a loop, reads the PCM data from the source file and sends it to the Audio Manager by calling alp_snd_write(). Finally, initiate playback with:
alp_snd_ioctl(&handle, ALP_SND_CTL_PLAY, NULL);
You can control playback by sending the audio object ALP_SND_CTL_PAUSE and ALP_SND_CTL_STOP commands in addition to the "play" command.
Recording Audio
To record, first open an audio object in ALP_SND_MODE_RECORD mode, and specify the device from which the audio is to be recorded, such as the microphone device. Next, create a thread that, in a loop, uses alp_snd_read() to read the audio PCM data and then writes the data to the desired location. Finally, initiate recording with:
alp_snd_ioctl(&handle, ALP_SND_CTL_PLAY, NULL);
You can control the recording session by sending the audio object ALP_SND_CTL_PAUSE and ALP_SND_CTL_STOP commands in addition to the "play" command.
NOTE: While your application is processing the buffer, the device driver continues to buffer incoming data.
Closing the Audio Object
Once your application has finished playing or recording you must close the audio object with alp_snd_close(). The only parameter to this function is the void pointer returned when the audio object was opened.
Playing System Sounds
The audio subsystem provides two convenience functions for playing system sounds. One function plays based upon the name of the sound, and the other plays the sound from a specified file. Both of these functions are exposed in alp/media_session.h.
Playing a System Sound by Name
If you want to play the system sound designated for a particular function—the sound that is normally played when the user performs an illegal operation, say—use alp_media_session_syssnd_play(). This function takes one of the ALP_SYS_SOUND_... values defined in alp/media_defs.h indicating the type of sound to be played. Because you are specifying the sound by its function, rather than by giving a specific path to a sound file, choices the user makes in designating particular sounds to be played for particular functions are honored by your application. Thus, for instance, if the user has specified that the crowing of a rooster is to be played whenever an alarm is triggered, that sound will be played if your application plays the ALP_SYS_SOUND_ALARM sound.
Table 3.1 lists the currently defined system sound values.
Table 3.1 Standard system sounds Â
In addition to the sound to be played, you also need to specify the audio stream through which the sound should be played, plus the volume level. The audio stream is a C string; valid strings are defined in the routing table, and thus can vary from device to device. Typically you would use "system_sounds" for system sounds, but certain sounds may be more appropriately played through one of the other stream types; see "Platform-Defined Sound Stream Types" for a list of the more common ones.
The volume level is an integer value that ranges from 0 to 7.
Calling this function is a simple matter, as shown here:
alp_media_session_syssnd_play(ALP_SYS_SOUND_SOFTKEY_CLICK, Â Â Â Â "system_sounds", 7);
The first time you call either of the alp_media_session_syssnd_play... functions the function will initialize the audio subsystem and play the sound. Subsequent calls do not require the initialization step. This means that for the first call, there is a small delay before the sound is played. If minimizing this delay is important for your application, call alp_media_session_syssnd_init() earlier in your application; this initializes the audio subsystem so that it needn't be initialized when the first sound is played.
High-Performance Sounds
By calling alp_media_session_syssnd_init() before the first use of alp_media_session_syssnd_play(), you can reduce the delay between the call and when the sound is actually heard to about 0.1 seconds. In certain instances—largely when playing sounds to signify that a hardware or software button has been pressed—even this is too long of a delay. For these situations, the Audio Manager has a similar function that does nearly the same thing, although it operates much faster. alp_snd_syssnd_play()—the function that corresponds to alp_media_session_syssnd_play()—also takes a string identifying the sound, a stream identifier, and a volume level. However:
- The set of "system sounds" that this function will play is much smaller.
- The volume level you supply to this function should range from 0 to 100 (as opposed to from 0 to 7 for the Media Session equivalent of this function).
You should also supply an appropriate stream identifier depending upon the system sound you are playing.
Table 3.2 lists the system sounds that can be played with this function, and the recommended stream type to use when playing each sound.
Table 3.2 alp_snd_syssnd_play() supported sounds and recommended stream types
The sound that is produced when a key on the software keyboard is pressed. |
||
"Danger" sound, indicating that something bad could possibly happen. |
Playing a System Sound File
Instead of playing the user- or system-designated sound for a particular function, as was described in "Playing a System Sound by Name," if you instead need to play the sound stored in a specific system sound file, use alp_media_session_syssnd_play_file() instead. This function takes as its first argument the absolute path to a system sound file (system sound files are typically found in /usr/share/sounds/system/ or /usr/share/sounds/ringtones/).
As with alp_media_session_syssnd_play(), you also need to specify the audio stream through which the sound should be played, plus the volume level. The audio stream is a C string; valid strings are defined in the routing table, and thus can vary from device to device. For most true system sounds you would use "system_sounds", but certain sounds may be more appropriately played through one of the other stream types. See "Platform-Defined Sound Stream Types" for a list of the more common ones.
The volume level is an integer value that ranges from 0 to 7.
The following shows how you can use this function to play the contents of a system sound file:
alp_media_session_syssnd_play_file( Â Â Â Â "/usr/share/sounds/system/Low_Battery.mp3", Â Â Â Â "system_sounds", 7);
The first time you call either of the alp_media_session_syssnd_play... functions the function will initialize the audio subsystem and play the sound. Subsequent calls do not require the initialization step. This means that for the first call, there is a small delay before the sound is played. If minimizing this delay is important for your application, call alp_media_session_syssnd_init() earlier in your application; this initializes the audio subsystem so that it needn't be initialized when the first sound is played.
High-Performance Sounds
By calling alp_media_session_syssnd_init() before your first call to alp_media_session_syssnd_play_file(), you can reduce the delay between the call and when the sound is actually heard to about 0.1 seconds. In certain instances—largely when playing sounds to signify that a hardware or software button has been pressed—even this is too long of a delay. For these situations, the Audio Manager has a similar function that does much the same thing, although it operates much faster. alp_snd_syssnd_play_file()—the function that corresponds to alp_media_session_syssnd_play_file()—also takes an absolute path to the file containing the sound to be played, a stream identifier, and a volume level. However:
- The specified file must be in WAV format (the
alp_media_session_syssnd_play_file()function supports a wide range of formats). - The volume level you supply to this function should range from 0 to 100 (as opposed to from 0 to 7 for the Media Session equivalent of this function).
You should also supply an appropriate stream identifier depending upon the system sound you are playing. See "Platform-Defined Sound Stream Types" for the most common stream types.
Managing Volume Levels
Most applications don't need to manipulate volume levels themselves, relying instead upon the built-in facilities of the device (dedicated volume keys, volume settings in Preferences, etc.) to enable the user to adjust the volume level as desired. For those applications that do need to get or set volume levels, the remainder of this section describes the volume control mechanism built in to ACCESS Linux Platform.
Volume Control Architecture
As shown in Figure 3.2, the volume of an audio stream can be altered at any of four points between where it originates and where it is eventually output. For PCM audio controlled or produced by the Audio Manager, the actual volume of the sound that is heard by the end user is the product of these four volume levels. So, for example,
- If all four volume levels are set to 100, the sound is heard at full volume (1.0x1.0x1.0x1.0=1.0, or 100%).
- If three of the four volume levels are set to 100 and one of them is set to 50, the sound is heard at half volume (0.5x1.0x1.0x1.0=0.5, or 50%).
- If two of the four volume levels are set to 50, and the other two are set to 100, the sound is heard at one-quarter volume (0.5x0.5x1.0x1.0=0.25, or 25%).
- If any one of the volume levels is set to zero, no sound is heard.
All volume levels are intially set to 100.
Figure 3.2 Audio Manager volume controls
Although there are four volume levels, applications can only affect two of them: "Stream Volume" and "Stream Class Volume".
Stream Volume
This is the volume level for an individual stream. Because each stream's volume level can be set independently, you can adjust the relative volume of multiple streams, making one quieter or louder than another. You set the volume of a particular stream by calling alp_snd_ioctl(..., ALP_SND_SET_VOLUME, ...) and passing the handle for the stream. Similarly, you can obtain a stream's current volume level with alp_snd_ioctl (..., ALP_SND_GET_VOLUME, ...).
Stream volume levels initially default to 100. If you want to preserve volume levels across invocations of your application, you'll need to save the stream's volume level using a mechanism such as the Settings Service and restore it when your application is started.
Be aware that on many devices when two streams of the same class are playing at the same time, their sound is mixed (with the independently set volume levels of the two streams determining how well one stream can be heard relative to the other). The device manufacturer, however, has the option to construct the routing table such that only one stream of a given class will be played at one time: when the second stream is started, the currently playing stream is stopped. You can register a callback function so that you are notified when your streams are stopped: use alp_snd_callback_register() to register a function of type AlpSndCallBack.
This same callback is invoked whenever the stream is muted or the volume is changed. As well, it is invoked whenever the stream's playback status is changed (that is, when it is stopped, started, or paused). Finally, it is called for recording sessions when recording begins. See the AlpSndActionType enum in the API reference documentation for the complete list of action values.
Stream Class Volume
Each audio stream type, or class, has an associated volume level. (See "Platform-Defined Sound Stream Types" for a list of common stream types.) Altering a stream class's volume level affects the volume level of those streams of that type without affecting the volume of streams of other types. For instance, if you raise the volume of the "audio_out" class, sounds produced by a media player (or any other application using Media Engine to play content that contains audio data) will become louder, while the volume of other sounds—such as ring tones or alarms—will remain unchanged.
Certain stream class volume levels can be adjusted by the user using a preference panel: typically, "phone_call", "alerts", "system_sounds", and "button_sounds". If needed, you can also adjust these and other stream class volume levels programmatically.
To change the volume level for a given stream class, simply set up an AlpSndNotifyStru structure with the stream class name and desired volume level, and then call alp_snd_notify(), as shown in Listing 3.2.
Listing 3.2 Altering a stream class volume level
AlpSndNotifyStru alpSndNotify; strcpy(alpSndNotify.keyid, "audio_out"); alpSndNotify.val = volumelevel;Â Â Â Â // from 0 to 100 alp_snd_notify(&alpSndNotify, ALP_SND_NOTIFY_VOLUME, 1);
Stream class volume levels initially default to 100. The operating system maintains a record of certain class volume levels ("phone_call", "alerts", "system_sounds", and "button_sounds") using the Settings Service, restoring them whenever the device is restarted. Because this is done by the operating system, however, and not the Audio Manager, if you alter one of these stream class volume levels and want it similarly preserved, you'll need to update the appropriate settings yourself. Typically you do this each time you alter the volume level (rather than doing it when your application exits), as is shown in Listing 3.3.
Listing 3.3 Setting the Alert volume
// Set the volume level strcpy(alpSndNotify.keyid, g_strrstr(ALP_PREFS_VOLUME_ALERTS, "/") + 1); alpSndNotify.val = volumelevel; alp_snd_notify(&alpSndNotify, ALP_SND_NOTIFY_VOLUME, 1); // now store the new volume level in the global setting // Note that "context" is returned when the settings service is opened with a // call to alp_settings_open() ret = alp_settings_set_key_int_value(context, ALP_PREFS_VOLUME_ALERTS, volumelevel);
Master Volume
As illustrated in Figure 3.2, the master volume is a "global" volume control that affects all streams simultaneously. Because the master volume affects such a wide range of sound sources—from ring tones to alarms to multimedia audio—applications should avoid altering it. Many devices will have dedicated volume keys that allow the user to raise or lower the master volume level.
If, in the unlikely event you need to affect the master volume level, you would do so as shown in Listing 3.4.
Listing 3.4 Setting the master volume level
AlpSndNotifyStru alpSndNotify; strcpy(alpSndNotify.keyid, "mastervolume"); alpSndNotify.val = volumelevel;Â Â Â Â // from 0 to 100 alp_snd_notify(&alpSndNotify, ALP_SND_NOTIFY_VOLUME, 1);
Because the master volume level is maintained by the operating system using the Settings Service, you can check its level by querying the value of the ALP_PREFS_VOLUME_MASTERVOLUME setting.
NOTE: The Settings Service simply maintains a copy of the current volume levels; changing the value of this setting will not affect the master volume level.
Device Volume
The Audio Manager has a provision for setting the volume level of each separate output (or input) device independently. However, applications cannot count on a particular stream always being routed to or from a particular device, since as conditions change (for instance, when the user plugs in a headset or when a phone call is received) the audio router may redirect various streams depending upon the rules set forth in the routing table. The device volume levels are best left to the system, allowing the user to, for instance, adjust the relative volume of the speaker and headphones so that listening levels are equally comfortable through either.
Other Volume Settings
The diagram on on page 41 shows a somewhat simplified view of the way sound flows through the various volume controls on its way from source to destination. In reality, there are a couple of other ways that an audio stream's volume can be affected.
Threshold Volume
Threshold volume limits enable automatic control and management of audio surges to guard against hearing damage. For example, a limit can be set when on headphone volume, so that no matter how loud the sound is playing through a speaker, when the headphones are plugged in the volume won't exceed the threshold volume limit. Threshold volume limits are set on a per-device-basis by the device manufacturer; they cannot be set by application developers.
Attenuation Volume
Attenuation volume levels are designed to handle the case when two sounds are playing and one needs to be clearly heard above the other. For instance, if you are listening to music and a phone call is received, the "audio_out" stream's volume can be attenuated so that the ring tone can be clearly heard. As with the threshold volume limits, attenuation volume levels are set by the device manufacturer and may vary from one device to another. These levels cannot be altered by application developers.
Silent Mode
In certain rare circumstances it can be useful to mute the sounds coming from the device. The Audio Manager has an "intelligent" mute capability that suppresses all sounds except those that the device manufacturer has decided must always be heard (for instance, in some countries it is required that a camera shutter sound always be audible when a picture is taken).
Listing 3.5 shows how to mute the device. Set alp_sndNotify.val to FALSE to disable silent mode and re-enable the playing of sounds.
Listing 3.5 Enabling silent mode
AlpSndNotifyStru alpSndNotify; strcpy(alpSndNotify.keyid, "silent_mode"); alpSndNotify.val = TRUE;Â Â Â Â // mutes the device alp_snd_notify(&alpSndNotify, ALP_SND_NOTIFY_VOLUME, 1);
Keeping the Audio Manager Informed
"Managing Volume Levels" discussed, among other things, the use of the alp_snd_notify() function to request that the Audio Manager change the volume level for an entire class of audio stream. You can also use this function to keep the Audio Manager apprised of audio device status changes and changes in conditions that may trigger rules within the routing table. Note that this is normally taken care of for you by the system; applications rarely if ever use alp_snd_notify() to do other than change audio stream class volume levels.
Changes in Device Status
If your application is in the unique position to know about a change in the presence of an audio input or output device (such as a microphone or Bluetooth headset, for instance), you'll want to let the Audio Manager know about that change. This requires that you know the name of the device as it is used in the routing table. Then, you simply allocate and fill out an AlpSndNotifyStru structure and pass it to alp_snd_notify(), as is shown in Listing 3.6.
Listing 3.6 Registering a change in device status
AlpSndNotifyStru notify; int err; strcpy(notify.keyid, "BT-headphone"); notify.val = 1;Â Â Â // 1=present, 0=absent err = alp_snd_notify(¬ify, ALP_SND_NOTIFY_DEVPRESENT, 1);
Normally, the ACCESS Linux Platform device knows about these types of state changes, so that applications need not track and register the presence or absence of the various audio input/output devices.
Changes in Condition
Rules are constructed not only based upon the presence or absence of physical devices (a microphone, a Bluetooth headset, or an FM radio, for example), but also upon predefined conditions. A condition might have a physical correspondence--say, to indicate the position of a "ringer off" switch. Or, a condition can indicate a certain state. An example of this might be "In an active voice call". Here, too, ACCESS Linux Platform usually takes care of tracking condition changes and letting the audio router know about those changes. But if there is a condition of which the operating system is unaware, and your application is in a position to know about it, it should use alp_snd_notify() to inform the audio router of the change. Or, if you need to simulate a particular condition, providing that you know the name of the condition as it is defined in the routing table, you can use this technique to do so.
Listing 3.7 shows how you would inform the audio router of a condition change. Note that this particular example registers two condition changes with a single call. In this case, it tells the audio router that we are no longer in the state where we are dialing and waiting for the call to be answered ("in-outgoing-call" mode) but are now in the state where the call has been received and is active ("in-active-voicecall" mode).
Listing 3.7 Registering a condition change
AlpSndNotifyStru snd_notify[2]; memset(&snd_notify, 0, sizeof(snd_notify)); strncpy((char *)&snd_notify[0].keyid, (char *)"in-outgoing-call", strlen("in-outgoing-call")); snd_notify[0].val = 0; strncpy((char *)&snd_notify[1].keyid, (char *)"in-active-voicecall", strlen("in-active-voicecall")); snd_notify[1].val = 1; alp_snd_notify(snd_notify, ALP_SND_NOTIFY_CONDITION, 2);
Handling Outside Events
Although an application is generally aware of the current state of any stream it has created, in certain circumstances the Audio Manager can affect that stream. For example, if the device is configured to allow only one stream of a given class to play at a time (only the device manufacturer can so configure a device), a running stream will be terminated by the Audio Manager if another stream of that same class is started. Or, while the stream is playing, the user could do something to affect the stream—for instance, they could plug in a headset, causing the audio to be redirected to that headset. Or, if conditions change such that the user has no way to hear the stream, the Audio Manager may, depending upon rules in the routing table, stop the stream to conserve battery power.
Because any of these things could be of interest to your application, the Audio Manager broadcasts notifications if and when they occur, allowing your application to take appropriate action.
Handling a Change in Stream Status
So that your application can be made aware when your stream is stopped, started, or has its volume muted or attenuated by the Audio Manager, the Audio Manager allows you to register for a notification that is broadcast whenever such things happen. The AlpSndActionType enum defines the actions about which your application will be informed through this notification:
enum AlpSndActionType {
ALP_SND_ACTION_PLAY,
ALP_SND_ACTION_RECORD,
ALP_SND_ACTION_PAUSE,
ALP_SND_ACTION_STOP,
ALP_SND_ACTION_MUTE,
ALP_SND_ACTION_ATTENUATE,
ALP_SND_ACTION_NULL
}
Register for the ALP_NOTIFY_EVENT_AUDIO_ACTION_CHANGE notification as you would any other:
alp_notify_register(ALP_NOTIFY_EVENT_AUDIO_ACTION_CHANGE, Â Â Â Â on_notify, NULL, ALP_NOTIFY_PRIORITY_NORMAL, 0);
Your notification handler will be passed an AlpSndNotifyActionchangeStru structure, which contains three important pieces of information: the current action, the previous action, and the ID of the process that controls the affected stream. This last is particularly important, since your handler will receive audio state change notifications for every stream that is currently running, not just your own. If, as is typical, you are only concerned about state changes in your own stream(s), check the supplied PID against your own to ensure that the notification applies to the stream(s) you control.
Listing 3.8 illustrates a simple notification handler that terminates the application if its stream is stopped for any reason.
Listing 3.8 Handling a stream state change notification
alp_status_t on_notify(AlpSndNotifyActionChangeStru *details){
if(details->pid == getpid()) {
switch(details->cur_action) {
case ALP_SND_ACTION_STOP:
my_exit_handler(NULL);
break;
default:
break;
}
}
return ALP_STATUS_OK;
}
Handling a Stream Redirection
When something happens that causes the input or output of an active audio stream to be redirected to a different input or output device, the Audio Manager sends out a notification so that interested parties can stay informed. This ALP_NOTIFY_EVENT_AUDIO_ROUTE_CHANGE ("/alp/audio_route/change") notification is accompanied by an AlpSndNotifyPathChangeStru structure, which has two members: a comma-separate list of destination (or "sink") element IDs, and a single source element ID. The list of destination element IDs details the chain of elements through which the audio passes after leaving the source, up through its final destination.
Register for the ALP_NOTIFY_EVENT_AUDIO_ROUTE_CHANGE notification as you would any other:
alp_notify_register(ALP_NOTIFY_EVENT_AUDIO_ROUTE_CHANGE, Â Â Â Â on_notify, NULL, ALP_NOTIFY_PRIORITY_NORMAL, 0);
Note that you can obtain the current route path of a specified stream type (which varies according to the current sound device status and the routing rules defined in the routing table), even if the stream is not active in the Audio Manager server, by calling alp_snd_get_current_routepath(). Pass a C string containing the sound stream type for the first parameter (see "Platform-Defined Sound Stream Types"). The second parameter is used to pass back a string of all routing destinations, separated by commas.










