Set up audio and additional components

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;
}

Last modified: 2013-12-21

comments powered by Disqus