ALSA: опустошение буфера при вызове snd_pcm_writei

Я получаю опустошение буфера при запуске старой программы, которую я недавно восстановил из пепла.

Программа полностью загружает в память необработанный звуковой файл (длиной 2100 байт, 525 кадров) и подготавливает ALSA для вывода (44,1 кГц, 2 канала, 16 бит со знаком):

if ((err = snd_pcm_set_params(audio_handle,
                              SND_PCM_FORMAT_S16_LE,
                              SND_PCM_ACCESS_RW_INTERLEAVED,
                              2,
                              44100,
                              1,
                              sound.playback_us)) < 0) {
  printf("E: Failed to prepare PCM: %s\n", snd_strerror(err));
  return -1;
}

Состояние PCM PREPARED перед первым воспроизведением звука. Звук воспроизводится правильно в первый раз, однако во второй раз он воспроизводится в состоянии RUNNING, а snd_pcm_writei выбрасывает -EPIPE ("Сломанная труба"). Логика воспроизведения:

frames = snd_pcm_writei(audio_handle,
                        sound.data,
                        write_size);

if(frames < 0) {
  printf("E: %s: attempting to recover\n", snd_strerror(frames));
  frames = snd_pcm_recover(audio_handle, frames, 0);

  if(frames < 0) {
    printf("E: snd_pcm_writei failed\n");
    break;
  }
} else if(frames > 0 && frames < write_size)
  printf("E: Short write (expected %li, wrote %li)\n", write_size, frames);
else
  printf("wrote %li frames\n", frames);

Как ни странно, в третий раз он играет правильно, а в следующий раз снова ошибается. Другими словами, через раз он завершается с ошибкой -EPIPE.

Для простоты я воспроизвожу звуки с интервалом в 1 секунду.

Что не так с логикой выше?


Обновлен код для использования нового API ALSA; можно найти по адресу: http://paste.ubuntu.com/7257181/


ИЗМЕНИТЬ

Только что обнаружил, что если кто-то действительно проверяет состояние -EPIPE и повторно подготавливает дескриптор PCM перед повторным вызовом snd_pcm_writei, все хорошо. Тогда мой (новый) вопрос: почему?

if(frames == -EPIPE) {
  snd_pcm_prepare(pcm.handle);
  frames = snd_pcm_writei(pcm.handle,
                          sound.data, //sound.data + (offset << 1),
                          write_size);
}

person miguelg    schedule 15.04.2014    source источник


Ответы (1)


Если вы заполните буфер 525 кадрами (около 12 мс), а затем подождите одну секунду, вы гарантированно получите опустошение, потому что для оставшихся 988 мс нет данных.

Вы можете использовать snd_pcm_drain, чтобы дождаться прекращения воспроизведения фактических данных (примечание: обычно устройство фактически не останавливается до границы следующего периода). В качестве альтернативы продолжайте подавать нулевые образцы в буфер.

person CL.    schedule 16.04.2014
comment
Боже, я явно неправильно понял, как на самом деле работает воспроизведение с ALSA. Из вашего комментария я понял, что сразу после воспроизведения звука, который длится всего несколько миллисекунд, мне приходится отключать и закрывать соединение вместо того, чтобы держать его открытым (как я делаю в данный момент) и снова открывать его при необходимости? - person miguelg; 16.04.2014
comment
Вам не нужно закрывать/повторно открывать; snd_pcm_prepare достаточно. - person CL.; 17.04.2014