To abstract the libasound and PCM setup, we can create a function that performs all of the setup operations. In this section, we will write the function setup_snd(). To set up these components, we use the libasound library.
Let's begin with the signature of our sound setup function.
int setup_snd(const char * name)
The function accepts a parameter, const char *name, which is the name of the device to set up. If the parameter is NULL, the default value is "pcmPreferred". The return value is SUCCESS on a successful run and FAILURE on an unsuccessful run.
int fragsize = -1; int num_frags = -1; int rtn; char *dev_name;
if (NULL == name) {
dev_name = "pcmPreferred";
} else {
dev_name = (char *) name;
}
if ((rtn = snd_pcm_open_name(&pcm_handle, dev_name,
SND_PCM_OPEN_PLAYBACK)) < 0) {
snprintf(msg, MSG_SIZE, "snd_pcm_open_name failed: %s\n",
snd_strerror(rtn));
show_dialog_message(msg);
return FAILURE;
}
if ((rtn = snd_pcm_info(pcm_handle, &info)) < 0) {
snprintf(msg, MSG_SIZE, "snd_pcm_info failed: %s\n",
snd_strerror(rtn));
goto setup_failure;
}
card = info.card;
if ((rtn = snd_pcm_plugin_set_disable(pcm_handle,
PLUGIN_DISABLE_MMAP)) < 0) {
snprintf(msg, MSG_SIZE, "snd_pcm_plugin_set_disable failed: %s\n",
snd_strerror(rtn));
goto setup_failure;
}
memset(&pi, 0, sizeof(pi));
pi.channel = SND_PCM_CHANNEL_PLAYBACK;
if ((rtn = snd_pcm_plugin_info(pcm_handle, &pi)) < 0) {
snprintf(msg, MSG_SIZE, "snd_pcm_plugin_info failed: %s\n",
snd_strerror(rtn));
goto setup_failure;
}
Clear the snd_pcm_channel_params_t structure using the memset() function. Then we configure the PCM parameter information based on .wav file format. To do so, we set the channel direction (to play the audio file instead of record new audio), channel mode, start mode (to start playing audio when the whole queue is filled), stop mode (to stop playing audio when an underrun or overrun occurs), audio fragment size, maximum audio fragment size, and the number of audio fragments. An underrun occurs if data is written at lower speed than it's being read, and an overrun occurs if data is written at higher speed than it's being read.
memset(&pp, 0, sizeof(pp));
pp.mode = SND_PCM_MODE_BLOCK;
pp.channel = SND_PCM_CHANNEL_PLAYBACK;
pp.start_mode = SND_PCM_START_FULL;
pp.stop_mode = SND_PCM_STOP_STOP;
pp.buf.block.frag_size = pi.max_fragment_size;
if (fragsize != -1) {
pp.buf.block.frag_size = fragsize;
}
pp.buf.block.frags_max = num_frags;
pp.buf.block.frags_min = 1;
pp.format.interleave = 1;
pp.format.rate = sample_rate;
pp.format.voices = sample_channels;
if (ENDIAN_LE16(wav_header.format_tag) == 6)
pp.format.format = SND_PCM_SFMT_A_LAW;
else if (ENDIAN_LE16(wav_header.format_tag) == 7)
pp.format.format = SND_PCM_SFMT_MU_LAW;
else if (sample_bits == 8)
pp.format.format = SND_PCM_SFMT_U8;
else if (sample_bits == 24)
pp.format.format = SND_PCM_SFMT_S24;
else
pp.format.format = SND_PCM_SFMT_S16_LE;
strcpy(pp.sw_mixer_subchn_name, "Wave playback channel");
if ((rtn = snd_pcm_plugin_params(pcm_handle, &pp)) < 0) {
snprintf(msg, MSG_SIZE, "snd_pcm_plugin_params failed: %s\n",
snd_strerror(rtn));
goto setup_failure;
if ((rtn = snd_pcm_plugin_prepare(pcm_handle,
SND_PCM_CHANNEL_PLAYBACK)) < 0) {
snprintf(msg, MSG_SIZE, "snd_pcm_plugin_prepare failed: %s\n",
snd_strerror(rtn));
goto setup_failure;
}
Now we set up the setup and group structures. First, clear both structures using the memset() function. Then set the setup.channel to playback mode using the SND_PCM_CHANNEL_PLAYBACK macro. Set the mixer group ID in the setup structure to point to the mixer group ID memory location in the group structure, so when we obtain the mixer group information, our PCM channel configuration structure already references the appropriate mixer group. Then we call snd_pcm_plugin_setup() to fill the setup structure with information about the current configuration of the PCM channel. If this function returns a value less than 0, we have an error case. In an error case, we display the error to the user, close the PCM handle, and exit with an error condition.
memset(&setup, 0, sizeof(setup));
memset(&group, 0, sizeof(group));
setup.channel = SND_PCM_CHANNEL_PLAYBACK;
setup.mixer_gid = &group.gid;
if ((rtn = snd_pcm_plugin_setup(pcm_handle, &setup)) < 0) {
snprintf(msg, MSG_SIZE, "snd_pcm_plugin_setup failed: %s\n",
snd_strerror(rtn));
goto setup_failure;
}
if (group.gid.name[0] == 0) {
snprintf(msg, MSG_SIZE, "Mixer Pcm Group [%s] Not Set \n",
group.gid.name);
goto setup_failure;
}
if ((rtn = snd_mixer_open(&mixer_handle, card,
setup.mixer_device)) < 0) {
snprintf(msg, MSG_SIZE, "snd_mixer_open failed: %s\n",
snd_strerror(rtn));
goto setup_failure;
}
char tmp[MSG_SIZE]; snprintf(msg, MSG_SIZE, "Format %s \n", snd_pcm_get_format_name(setup.format.format)); snprintf(tmp, MSG_SIZE, "Frag Size %d \n", setup.buf.block.frag_size); strlcat(msg, tmp, MSG_SIZE); snprintf(tmp, MSG_SIZE, "Total Frags %d \n", setup.buf.block.frags); strlcat(msg, tmp, MSG_SIZE); snprintf(tmp, MSG_SIZE, "Rate %d \n", setup.format.rate); strlcat(msg, tmp, MSG_SIZE); snprintf(tmp, MSG_SIZE, "Voices %d \n", setup.format.voices); strlcat(msg, tmp, MSG_SIZE); snprintf(tmp, MSG_SIZE, "Mixer Pcm Group [%s]\n", group.gid.name); strlcat(msg, tmp, MSG_SIZE); show_dialog_message(msg);
setup_failure:
show_dialog_message(msg);
snd_pcm_close(pcm_handle);
return FAILURE;
}
if (setup_snd(NULL)) {
goto fail2;
}