[C++]Программирование c Alsa с постоянным underrun
Доброго времени суток! Уважаемые, кто пользовался Alsa для программирования приложений, помогите. Пишу консольный плеер. Использовал примеры из инета, разные версии, но все работают одинаково :( Суть проблемы такова, что почему-то периодически возникает underrun буфера альсы. При чем это не зависит от того использую ли я callback или проверяю буфер в цикле. Использую ф-цию snd_pcm_avail(pcm_handle). В логе программы возвращает 12 при заполнении буфера (из возможных 15052, то есть буфер заполнен почти полностью)...а при следующем вызове callback'а уже выбрасывается ошибка underrun, то есть буфер опустел... Если опрашивать в цикле, происходит то же самое, т.к. используется та же функция. Пробовал разные функции, но результат тот же: музыка воспроизводится, даже иногда ровно по несколько секунд, без сбоев, но довольно часто проскакивает( Уже после недели топтания на одном месте перестаю понимать что вообще происходит и как работает этот дурацкий рингбуфер... В данном куске циклический опрос буфера.
#define ALSA_PCM_NEW_HW_PARAMS_API
#include <alsa/asoundlib.h>
#include <stdio.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
using namespace std;
char *buffer;
int period_size;
ifstream is;
int main(int argc, char ** argv) {
long loops;
int rc;
int size;
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *sw_params;
unsigned int val, val2;
int dir;
snd_pcm_uframes_t frames, frames_to_deliver;
if (argc < 2) {
printf( "%s : you must specified wav file name\n", argv[0] );
return 0;
}
/* Open PCM device for playback. */
rc = snd_pcm_open(&handle, "default",
SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) {
fprintf(stderr,
"unable to open pcm device: %s\n",
snd_strerror(rc));
exit(1);
}
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(handle, params);
snd_pcm_hw_params_set_access(handle, params,
SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(handle, params,
SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_channels(handle, params, 2);
val = 44100;
snd_pcm_hw_params_set_rate_near(handle, params,
&val, &dir);
frames = 12;
snd_pcm_hw_params_set_period_size_near(handle,
params, &frames, &dir);
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
fprintf(stderr,
"unable to set hw parameters: %s\n",
snd_strerror(rc));
exit(1);
}
/* Use a buffer large enough to hold one period */
snd_pcm_hw_params_get_period_size(params, &frames,
&dir);
period_size = frames;
size = frames * 4; /* 2 bytes/sample, 2 channels */
buffer = (char *) malloc(size);
printf("%d\n", (int)frames);
frames_to_deliver = frames;
/* We want to loop for 60 seconds */
snd_pcm_hw_params_get_period_time(params, &val, &dir);
loops = 60000000 / val;
if ((rc = snd_pcm_sw_params_malloc (&sw_params)) < 0) {
fprintf (stderr, "cannot allocate software parameters structure (%s)\n",
snd_strerror (rc));
exit (1);
}
if ((rc = snd_pcm_sw_params_current (handle, sw_params)) < 0) {
fprintf (stderr, "cannot initialize software parameters structure (%s)\n",
snd_strerror (rc));
exit (1);
}
if ((rc = snd_pcm_sw_params_set_avail_min (handle, sw_params, period_size)) < 0) {
fprintf (stderr, "cannot set minimum available count (%s)\n",
snd_strerror (rc));
exit (1);
}
if ((rc = snd_pcm_sw_params_set_start_threshold (handle, sw_params, frames*2)) < 0) {
fprintf (stderr, "cannot set start mode (%s)\n",
snd_strerror (rc));
exit (1);
}
if ((rc = snd_pcm_sw_params (handle, sw_params)) < 0) {
fprintf (stderr, "cannot set software parameters (%s)\n",
snd_strerror (rc));
exit (1);
}
is.open (argv[1], ios::binary );
int avail_frames;
snd_pcm_hw_params_get_buffer_size(params, (snd_pcm_uframes_t *) &avail_frames);
int loops_count = avail_frames/(int)frames;
loops_count--;
while (loops > 0) {
loops--;
if (((int)snd_pcm_avail_update(handle)) > ((int)frames)) {
is.read((buffer),period_size*4);
printf("!");
rc = snd_pcm_writei(handle, buffer, frames_to_deliver);
printf("Write return = %d (of buffer %d). Now availible %d\n", rc, frames_to_deliver, (int)snd_pcm_avail_update(handle));
}
if (rc == -EPIPE) {
/* EPIPE means underrun */
fprintf(stderr, "underrun occurred, buffer = %d\n", (int)frames);
snd_pcm_prepare(handle);
} else if (rc < 0) {
fprintf(stderr,
"error from writei: %s\n",
snd_strerror(rc));
} else if (rc != (int)frames) {
fprintf(stderr,
"short write, write %d frames\n", rc);
}
printf("%d of %d\n",(int)snd_pcm_avail_update(handle), avail_frames);
long int delay = (int)frames;
if ((frames_to_deliver = snd_pcm_avail_update (handle)) < 0) {
if (frames_to_deliver == -EPIPE) {
fprintf (stderr, "an xrun occured\n");
break;
} else {
fprintf (stderr, "unknown ALSA avail update return value (%d)\n",
frames_to_deliver);
break;
}
}
frames_to_deliver = frames_to_deliver > 4096 ? 4096 : frames_to_deliver;
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
free(buffer);
return 0;
}