LINUX.ORG.RU

Сообщения Agathodaimon

 

[C++]Программирование c Alsa с постоянным underrun

Форум — Development

Доброго времени суток! Уважаемые, кто пользовался 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(&params);
  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;
}

 

Agathodaimon
()

RSS подписка на новые темы