Now let's set up the audio components we need to play a .wav file in your
application. This tutorial is based on the PlayWav sample app, so we use
main() as the calling function. The PlayWav sample application also
handles navigator and dialog events, but in this tutorial, we focus on the libasound
audio components.
Audio variables and macros
There are several variables and macros that we need to define, both in the global
scope and local scope of our application.
Global scope
Let's begin by declaring variables we use in the global scope of the
application. Declare constant character arrays for RIFF and WAVE identifiers. After
you declare these variables, you can reference the strings,
"RIFF" and
"WAVE", by using
riff_id and
wave_id,
respectively. Next, declare a macro for the relative path of the .wav file. The
PlayWav sample app uses sample.wav, but you can change that to match the name of the
.wav file you want to use in your
application.
const char *riff_id = "RIFF";
const char *wave_id = "WAVE";
#define WAV_RELATIVE_PATH "app/native/sample.wav"
Declare an integer variable for the sound card number and initialize
it to -1. Then declare the PCM handle structure and a structure that describes the
PCM
capabilities.
int card = -1;
snd_pcm_t *pcm_handle;
snd_pcm_info_t info;
Declare a structure that describes the parameters of a PCM capture or
playback channel, a structure that describes the current configuration of a PCM
channel, and a structure that describes PCM channel information. Next, declare a
mixer handle structure and a control structure for a mixer
group.
snd_pcm_channel_params_t pp;
snd_pcm_channel_setup_t setup;
snd_pcm_channel_info_t pi;
snd_mixer_t *mixer_handle;
snd_mixer_group_t group;
Last, declare integer variables for the sample rate, number of
channels for sample data, and number of bits per
sample.
int sample_rate;
int sample_channels;
int sample_bits;
Local scope
Next, let's declare and initialize the local variables in your calling function.
Declare a file pointer for the .wav file. Next, declare the number of samples as an
integer and the sample buffer as a character pointer (the size of one character is 1
byte).
FILE *file;
int samples;
char *sample_buffer;
Declare integer variables for return values, buffer size, bytes read,
and the total number of bytes written. Then, declare the file descriptor sets for
read and write. A file descriptor is an abstract indicator for accessing a
file.
int rtn, final_return_code = -1, exit_application = 0;
int bsize, bytes_read, total_written = 0;
fd_set rfds, wfds;
Last, declare character arrays for the .wav file and the current
working directory. The size of each array is set to
PATH_MAX, which specifies the maximum path size for the
OS.
char input_file[PATH_MAX];
char cwd[PATH_MAX];
Additional components
As we mentioned earlier, this tutorial focuses on the .wav audio
aspects of the PlayWav sample app. Here, we'll briefly discuss how to handle
navigator and dialog events and how to set up dialog boxes.
Request navigator events
To handle navigator events in our application, we must initialize the
BlackBerry Platform Services (BPS) and then request navigator events by calling
navigator_request_events(). If the value returned from
the function is not
BPS_SUCCESS, we have an error
case and we need to exit with a failure
condition.
bps_initialize();
if (BPS_SUCCESS != navigator_request_events(0)) {
fprintf(stderr, "Error requesting navigator events: %s",
strerror(errno));
exit(-1);
}
For
more information on navigator events, see the
API reference.
Handle dialog boxes
To set up and display dialog boxes, the PlayWav sample app provides
functions in dialogutil.h and dialogutil.c. Here, we describe these functions:
-
int setup_screen()
This function sets up the screen by doing the
following:
- Gets a handle for the screen
- Creates context with the screen
- Creates a window for the screen
- Gets a window buffer
- Sets the window group ID for the window
-
static char*
get_window_group_id()
This function obtains
the window group ID based on the process ID of the application. A
character string of the window group ID is returned.
-
void cleanup_screen()
This function destroys and clears the window and context
handles.
-
void create_dialog()
This function creates an alert dialog box.
-
void show_dialog_message(const char*
msg)
This function sets the message text for
the alert dialog box and updates the display. This function also
displays the error messages to the standard error stream (STDERR).
-
void destroy_dialog()
This function destroys the dialog box.
-
int err(char *message)
This function is defined in main.c, and it displays the
error message to the user in the dialog box that we set up with create_dialog().
To display status and error messages, we must initialize the dialog box
subsystem. To receive the dialog box events, we call
dialog_request_events(0). If this function doesn't return
BPS_SUCCESS, we have an error case, so we exit with a
failure
condition.
if (BPS_SUCCESS != dialog_request_events(0)) {
fprintf(stderr, "Error requesting dialog events: %s",
strerror(errno));
exit(-1);
}
For
more information about dialog boxes, see the
API reference. For more information about
the functions above, take a look at the
dialogutil.c file.
Handle audio device events
In
main(), after we call
bps_initialize(), we can request to receive audio device events from
the
BlackBerry Platform Services infrastructure by calling
audiodevice_request_events(0). We need to receive audio events to
handle changes in the audio output. If this function doesn't return
BPS_SUCCESS, we have an error case, so we exit with a
failure
condition.
if (BPS_SUCCESS != audiodevice_request_events(0)) {
fprintf(stderr, "Error requesting audio device events: %s",
strerror(errno));
exit(-1);
}
Obtain the .wav file path
We get the current working directory for the application by using
getcwd(char*, size_t), which is declared in
unistd.h. Then we combine the current working directory with the relative path of
the .wav file and store it in the
input_file
variable. To combine the two character arrays, we use the
snprintf() function. This function returns the number of characters
outputted (which is the total file path), less the null-terminating character. If
that number of characters is greater than
PATH_MAX
(which is defined in limits.h), then our input file location is too large and we
have an error
case.
getcwd(cwd, PATH_MAX);
rtn = snprintf(input_file, PATH_MAX, "%s/%s", cwd, WAV_RELATIVE_PATH);
if (rtn > PATH_MAX - 1) {
err("File name and path too long");
goto fail1;
}
Open the .wav file
To open the .wav file, we call
fopen(), which is declared in stdio.h. If the file pointer returned is
NULL or
0,
we have an error case. In an error case, we want to display a message to the user,
destroy the dialog box, clean up resources, close the
BlackBerry Platform Services infrastructure to stop receiving events, and exit with a failure
condition.
if ((file = fopen(input_file, "r")) == 0) {
err("File open failed");
goto fail1;
}