3
votes

I'm writing a tipical capture-process-playback loop. Capture using snd_pcm_readi(), some cheap audio process and then playback through snd_pcm_writei(). Single threaded. At 128 period size and 96000KHz I cannot perceive any latency. Good.

Periodically I get a buffer underrun (snd_pcm_writei() returns -EPIPE); It's ok, I'm running a regular Ubuntu 16.04 desktop not configured for low audio latency.

After some underruns the latency becomes perceivable. I don't understand why.

this is my capture device configuration:

Plug PCM: Linear Integer <-> Linear Float conversion PCM (S32_LE)
Its setup is:
  stream       : CAPTURE
  access       : RW_INTERLEAVED
  format       : FLOAT_LE
  subformat    : STD
  channels     : 1
  rate         : 96000
  exact rate   : 96000 (96000/1)
  msbits       : 32
  buffer_size  : 4096
  period_size  : 128
  period_time  : 1333
  tstamp_mode  : NONE
  tstamp_type  : MONOTONIC
  period_step  : 1
  avail_min    : 128
  period_event : 0
  start_threshold  : 0
  stop_threshold   : 4096
  silence_threshold: 0
  silence_size : 0

And this is the playback device config:

Plug PCM: Linear Integer <-> Linear Float conversion PCM (S32_LE)
Its setup is:
  stream       : PLAYBACK
  access       : RW_INTERLEAVED
  format       : FLOAT_LE
  subformat    : STD
  channels     : 1
  rate         : 96000
  exact rate   : 96000 (96000/1)
  msbits       : 32
  buffer_size  : 384
  period_size  : 128
  period_time  : 1333
  tstamp_mode  : NONE
  tstamp_type  : MONOTONIC
  period_step  : 1
  avail_min    : 128
  period_event : 0
  start_threshold  : 384
  stop_threshold   : 384
  silence_threshold: 0
  silence_size : 0
  boundary     : 6917529027641081856

when the underrun occurs I run snd_pcm_recover() and forget about the audio period that was just refused by snd_pcm_writei(). In my mind the next audio period will be played as soon I run snd_pcm_writei() and I will be again in a low-latency loop. But this is wrong, the latency increases.

No errors from the capture side of my loop.

What's happening? What Am I doing wrong?

Thanks.

1

1 Answers

2
votes

The capture buffer is larger than the playback buffer. This is a good idea to reduce the risk of capture overruns (and you migth want to make it even larger, if supported by the hardware), but when the playback device stops for a short time, more data will have piled up in the capture buffer and does not go away faster than you can play it.

To ensure that the capture and playback devices do not lose synchronization, either

  • stop both devices, and then start them again properly, or
  • configure the playback device to disable underrun detection (set the stop threshold to the boundary value); this means that playback will just continue, even if there are no valid samples in the buffer, and your code then has to write samples until is has caught up. (With the default settings, the old samples in the ring buffer are played again during an underrun; if you've set the silence threshold/size to zero/boundary, silence is played instead.)