0

I am working on an embedded audio device, it is using kernel-5.10, and ALSA-LIB to play audio.

When I am trying to call snd_pcm_writei() to play audio, I got a little confused on what is the right way to set its input parameters and handle its return value.

Here is my code.

    /* Get periodsize from system */
        snd_pcm_hw_params_get_period_size(hw_params, &periodsize, NULL);

        left = frames;
        sent = 0;
        while (left > 0) {
            sent = (left > periodsize) ? periodsize : left;
            rc = snd_pcm_writei(pcm_handle, buf, sent);
            if (rc < 0) {
                if (rc == -EAGAIN) {
                    usleep(1000);
                }
                rc = snd_pcm_recover(pcm_handle, rc, 0);
                if (rc < 0) {
                    snd_pcm_prepare(pcm_handle);
                }
            } else if (rc == 0) {
/* How to handle this case ?*/
            } else {
/* Samples less than were sent to audio-card */
                left -= rc;
                buf += rc * frame_size;
            }
        }

In my testing, the frames is 820, and periodsize is got from system, now it is 400. I am trying to sent data to audio-card upto the size of periodsize, is it correct?
Should I send data with frames of 820 directly to the audio card to play?

   snd_pcm_writei(pcm_handle, buf, frames);

Updated with testing codes and testing results.

#ifdef TEST_APLAY
    frame_size = chan * 2;
    buf = databuf;
    left = data_frames;
    sent = 0;

    while (left > 0) {
        sent = (left > periodsize) ? periodsize : left;
        rc = snd_pcm_writei(hah->pcm_handle, buf, sent);
        if (rc == -EAGAIN || (rc >= 0 && (size_t)rc < sent)) {
            snd_pcm_wait(hah->pcm_handle, 10);
        } else if (rc == -EPIPE) {
            snd_pcm_recover(hah->pcm_handle, rc, 0); 
////            snd_pcm_prepare(hah->pcm_handle);
        } else if (rc < 0) {
            break;
        }
        if (rc > 0) {
            left -= rc; 
            buf += rc * frame_size;
        }
    }   
#endif

And I got the following results (start and stop playing 10 times).

Playing/stoping quickly for times, pid: 3168
Got end of media, breaking
Got end of media, breaking
ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred
Got end of media, breaking
ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred
ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred
Got end of media, breaking
ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred
ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred
ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred
ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred
ALSA lib pcm.c:8526:(snd_pcm_recover) underrun occurred
Got end of media, breaking

Above is tested in Ubuntu-20.04 X86_64 VM, with AC97 sound card.

11
  • Have to looked at aplay sources?
    – dimich
    Commented Nov 22 at 11:43
  • Good point, not read it.
    – wangt13
    Commented Nov 22 at 12:25
  • See stackoverflow.com/q/2180909/1216776
    – stark
    Commented Nov 22 at 18:37
  • I read aplay source codes and the discussion in the link stark provided. I think I got 2 different answers. aplay seemed to feed sound card with date length of chunk_size, and send samples in a loop. But the SO link said I just need to write everything in one go. So which one should I follow?
    – wangt13
    Commented Nov 23 at 0:10
  • @wangt13 It depends on requirements for your application. aplay is "semi-interactive", it can be interrupted by user. When you write entire buffer, the process calling snd_pcm_writei() is blocked until [almost] all buffer content is played.
    – dimich
    Commented Nov 23 at 2:32

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.