1

I have developed some code to play audio (wave files). When I try to play the same file twice, it plays fine the first time but always fails when called the second time.

Below is the one-time audio init function.

/* PCM interface objects */
static snd_pcm_t *pcm_handle;
static snd_pcm_uframes_t frames;
static int rate = 44100, channels = 2;

void audio_init(void)
{
    snd_pcm_hw_params_t *params;
    unsigned int pcm = 0;

    /* Open the PCM device in playback mode */
    if ((pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
        printf("ERROR: Can't open \"%s\" PCM device. %s\n",PCM_DEVICE, snd_strerror(pcm));

    /* Allocate parameters object and fill it with default values*/
    snd_pcm_hw_params_alloca(&params);
    snd_pcm_hw_params_any(pcm_handle, params);

    /* Interleaved mode */
    if ((pcm = snd_pcm_hw_params_set_access(pcm_handle, params,
            SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
        printf("ERROR: Can't set interleaved mode. %s\n", snd_strerror(pcm));

    /* Signed 16 bit little-endian format */
    if ((pcm = snd_pcm_hw_params_set_format(pcm_handle, params,
            SND_PCM_FORMAT_S16_LE)) < 0)
        printf("ERROR: Can't set format. %s\n", snd_strerror(pcm));

    /* Two channels, stereo */
    if ((pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, channels)) < 0)
        printf("ERROR: Can't set channels number. %s\n", snd_strerror(pcm));

    /* 44100 bits/second sampling rate (CD quality) */
    if ((pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0)) < 0)
        printf("ERROR: Can't set rate. %s\n", snd_strerror(pcm));

    /* Write parameters to the driver */
    if ((pcm = snd_pcm_hw_params(pcm_handle, params) < 0))
        printf("ERROR: Can't set harware parameters. %s\n", snd_strerror(pcm));

    /* Resume information */
    printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle));
    printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));

    int tmp = 0;
    snd_pcm_hw_params_get_channels(params, &tmp);
    printf("channels: %i ", tmp);

    if (tmp == 1)
        printf("(mono)\n");
    else if (tmp == 2)
        printf("(stereo)\n");

    snd_pcm_hw_params_get_rate(params, &tmp, 0);
    printf("rate: %d bps\n", tmp);

    /* Allocate large enough buffer to hold single period (No. of samples) */
    snd_pcm_hw_params_get_period_size(params, &frames, 0);
    printf("period size = %d frames\n", (int)frames);

    /* Get the period time */
    snd_pcm_hw_params_get_period_time(params, &tmp, NULL);
    printf("period time = %d us\n", tmp);

    audio_playTrack(AUDIO_HOMING_TRACK);
    sleep(1);
    printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
    audio_playTrack(AUDIO_ARRIVE_TRACK);
    sleep(1);
    printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
}

Below is the function I call twice at the end of the audio_init( ).

void audio_playTrack(char *audioFile)
{
    short int* buf = NULL;
    int readcount = 0;
    int pcmrc;
    SNDFILE *infile = NULL;
    SF_INFO sfinfo;

    infile = sf_open(audioFile, SFM_READ, &sfinfo);

    printf("\r\n");
    printf("[FILE]Channels      : %d\n", sfinfo.channels);
    printf("[FILE]Sample rate   : %d\n", sfinfo.samplerate);
    printf("[FILE]Sections      : %d\n", sfinfo.sections);
    printf("[FILE]Format        : %d\n", sfinfo.format);
    printf("\r\n");

    printf("+------------------\n");
    printf("### AUDIO BEGIN ###\n");
    printf("+------------------\n\n");
    buf = malloc(frames * sfinfo.channels * sizeof(int));
    while ((readcount = sf_readf_short(infile, buf, frames))>0)
    {
        pcmrc = snd_pcm_writei(pcm_handle, buf, readcount);
        if (pcmrc == -EPIPE)
        {
            printf("Underrun!\n");
            snd_pcm_prepare(pcm_handle);
        }
        else if (pcmrc < 0)
        {
            printf("Error writing to PCM device: %s\n", snd_strerror(pcmrc));
        }
        else if (pcmrc != readcount)
        {
            printf("PCM write differs from PCM read.\n");
        }
    }
    printf("+------------------\n");
    printf("### AUDIO END ###\n");
    printf("+------------------\n\n");
    free(buf);
    sf_close (infile);
}

The call to first audio_playTrack() works. The second one does not work as the sf_readf_short returns 0 frames and does not even enter the while loop.

I'd like to be able to stop audio and re-start audio anytime from within the code. Can someone help me understand why sf_readf_short returns 0 and audio_playTrack() fails when invoked the second time?

Below is console prints.

PCM name: 'default'
PCM state: PREPARED
channels: 2 (stereo)
rate: 44100 bps
period size = 940 frames
period time = 21333 us

[FILE]Channels          : 2
[FILE]Sample rate       : 44100
[FILE]Sections          : 1
[FILE]Format            : 65538

+------------------
### AUDIO BEGIN ###
+------------------

+------------------
### AUDIO END ###
+------------------

PCM state: RUNNING

[FILE]Channels          : 2
[FILE]Sample rate       : 44100
[FILE]Sections          : 1
[FILE]Format            : 65538

+------------------
### AUDIO BEGIN ###
+------------------

Underrun!
+------------------
### AUDIO END ###
+------------------

Edit: Removed snd_pcm_drain( ) from audio_playTrack( ) and made snd_pcm_hw_params_t params local to audio_init( )

1 Answers1

0

The file object infile is the same one for both calls. So you have reached the end of the file the first time, then the second time you try to play, you are still at the end of the file. You either need to close and reopen the file, or rewind to the beginning before trying to play it again.

lxop
  • 7,596
  • 3
  • 27
  • 42
  • In the audio_playTrack( ), at the end after free(buf), I added sf_seek(infile, 0, SEEK_SET) to position the pointer back to the first data sample. But after adding this line, snd_pcm_writei( ) fails with the error "Error writing to PCM device: File descriptor in bad state" – Vinay Divakar Nov 01 '19 at 02:39
  • That will be because you have drained the PCM I am guessing. Remove the `snd_pcm_drain` line and see what happens. Overall though, you are using a bunch of global variables and I don't like that. – lxop Nov 01 '19 at 02:50
  • Also, I tried opening the file at the beginning of audio_playTrack( ) and then closing it at the end and it still gives the "bad descriptor error". Do you have insights on what may be causing this? – Vinay Divakar Nov 01 '19 at 02:53
  • Did you try what I said above? The bad descriptor error isn't about the audio file, it's about the PCM device – lxop Nov 01 '19 at 03:01
  • Let me better my code, update and test with removing snd_pcm_drain and be back. – Vinay Divakar Nov 01 '19 at 03:04
  • Apologies for the delayed response. Was off this weekend. Yes, after I remove pcm drain, it's working. But if I remove the drain, the PCM state shows as Running and the next call to audio_playTrack( ) give's an "Underrun" once and then starts playing the track (shown in the console). Any insights why this is happening? I also can't make the snd_pcm_uframes_t and SF_INFO local as I am using it in both the functions. But happy to take your advises on making it better. – Vinay Divakar Nov 03 '19 at 19:30
  • Also, could you please explain why removing the pcm drain got it working - as in what lead to the bad file descriptor error? – Vinay Divakar Nov 03 '19 at 19:37
  • The error was occurring because draining the pcm caused it to be finalised, and no longer ready to receive data. An option would be to always initialise the pcm before sending it data, rather than initializing once and calling Play multiple times. I'm not familiar with all the features and characteristics of the library sorry, so there might be better ways. – lxop Nov 03 '19 at 20:24
  • no problem. I just started and will be going in-depth of alsa. At least the above issue has been fixed. I will be marking it off now. Thanks. – Vinay Divakar Nov 03 '19 at 20:37