After our call to setup_snd() returns successfully, we have a few more operations in main() to complete before we start writing our main event and audio loops.
bsize = setup.buf.block.frag_size;
samples = find_tag(file, "data");
sample_buffer = malloc(bsize);
if (!sample_buffer) {
goto fail3;
}
FD_ZERO(&rfds); FD_ZERO(&wfds); bytes_read = 1;
The main audio loop checks two conditions. First, it checks whether the total number of samples written is smaller than the total number of samples (which indicates that we still have samples to write). Second, it checks whether the number of bytes read is more than 0 (so that we're not stuck reading no bytes).
while (total_written < samples && bytes_read > 0 ) {
bps_event_t *event = NULL;
while (BPS_SUCCESS == bps_get_event(&event, 0) && event) {
Handle navigator exit events
if (bps_event_get_domain(event) == navigator_get_domain()) {
if (NAVIGATOR_EXIT == bps_event_get_code(event)) {
exit_application = 1;
goto success;
}
}
Handle audio device events
if (bps_event_get_domain(event) == audiodevice_get_domain()) {
const char *audiodevice_path = audiodevice_event_get_path(event);
if (NULL == audiodevice_path) {
snprintf(msg, MSG_SIZE, "audiodevice_event_get_path failed:
%s\n", snd_strerror(rtn));
show_dialog_message(msg);
goto fail5;
}
if ((rtn = snd_mixer_close(mixer_handle)) < 0) {
snprintf(msg, MSG_SIZE, "snd_mixer_close failed: %s\n",
snd_strerror(rtn));
show_dialog_message(msg);
goto fail4;
}
if ((rtn = snd_pcm_close(pcm_handle)) < 0) {
snprintf(msg, MSG_SIZE, "snd_pcm_close failed: %s\n",
snd_strerror(rtn));
show_dialog_message(msg);
goto fail3;
}
if (setup_snd(audiodevice_path)) {
goto fail3;
}
}
if (tcgetpgrp(0) == getpid())
FD_SET(STDIN_FILENO, &rfds);
FD_SET(snd_mixer_file_descriptor(mixer_handle), &rfds);
FD_SET(snd_pcm_file_descriptor(pcm_handle, SND_PCM_CHANNEL_PLAYBACK),
&wfds);
rtn = max(snd_mixer_file_descriptor(mixer_handle),
snd_pcm_file_descriptor(pcm_handle,
SND_PCM_CHANNEL_PLAYBACK));
if (select(rtn + 1, &rfds, &wfds, NULL, NULL) == -1) {
err("select");
goto fail5;
}
if (FD_ISSET(snd_pcm_file_descriptor(pcm_handle,
SND_PCM_CHANNEL_PLAYBACK), &wfds)) {
snd_pcm_channel_status_t status;
int written = 0;
if ((bytes_read = fread(sample_buffer, 1, min(samples -
total_written, bsize), file)) <= 0)
continue;
written = snd_pcm_plugin_write(pcm_handle, sample_buffer,
bytes_read);
if (written < bytes_read) {
memset(&status, 0, sizeof(status));
status.channel = SND_PCM_CHANNEL_PLAYBACK;
if (snd_pcm_plugin_status(pcm_handle, &status) < 0) {
show_dialog_message("underrun: playback channel status
error\n");
goto fail5;
}
if (status.status == SND_PCM_STATUS_READY ||
status.status == SND_PCM_STATUS_UNDERRUN) {
if (snd_pcm_plugin_prepare(pcm_handle, SND_PCM_CHANNEL_PLAYBACK)
< 0) {
show_dialog_message("underrun: playback channel prepare
error\n");
goto fail5;
}
}
if (written < 0)
written = 0;
written += snd_pcm_plugin_write(pcm_handle, sample_buffer +
written, bytes_read - written);
}
If the number of bytes written is greater than or equal to the number of bytes read, we don't have a buffer underrun and we can add the number of bytes written this iteration to the total number of bytes written. We can also close the if statement that checks if the PCM device is ready and close the main audio loop.
total_written += written;
success:
bytes_read = snd_pcm_plugin_flush(pcm_handle,
SND_PCM_CHANNEL_PLAYBACK);
final_return_code = 0;
fail5:
snd_mixer_close(mixer_handle);
fail4:
snd_pcm_close(pcm_handle);
fail3:
free(sample_buffer);
sample_buffer = NULL;
fail2:
fclose(file);
fail1:
while (!exit_application) {
/*
* Something went wrong so there is probably an error message
* and we don't want to exit right away because we want the
* user to see the message in the dialog.
*
* Using a negative timeout (-1) in the call to
* bps_get_event(...) ensures that we don't busy wait by
* blocking until an event is available.
*/
bps_event_t *event = NULL;
bps_get_event(&event, -1);
if (event) {
/*
* If it is a NAVIGATOR_EXIT event then we are done so stop
* processing events, clean up and exit
*/
if (bps_event_get_domain(event) == navigator_get_domain()) {
if (NAVIGATOR_EXIT == bps_event_get_code(event)) {
exit_application = 1;
}
}
}
}
destroy_dialog();
cleanup_screen();
bps_shutdown();
return final_return_code;
We're finished! Now you can add .wav files to your applications to create a better user experience and make more money from your application.
If you want more information about the Native SDK audio architecture, mixer architecture, and additional audio information, take a look at the following links: