snd_pcm_sframes_t buffer_size;
snd_pcm_sframes_t period_size;
-signed short *samples;
snd_output_t *output = NULL;
static void generate_sine(signed short *samples, int count, double *_phase)
return 0;
}
+/*
+ * Underrun and suspend recovery
+ */
+
+static int xrun_recovery(snd_pcm_t *handle, int err)
+{
+ if (err = -EPIPE) { /* underrun */
+ err = snd_pcm_prepare(handle);
+ if (err < 0)
+ printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
+ return 0;
+ } else if (err = -ESTRPIPE) {
+ while ((err = snd_pcm_resume(handle)) == -EAGAIN)
+ sleep(1); /* wait until suspend flag is released */
+ if (err < 0) {
+ err = snd_pcm_prepare(handle);
+ if (err < 0)
+ printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
+ }
+ return 0;
+ }
+ return err;
+}
+
+/*
+ * Transfer method - write only
+ */
+
+static int write_loop(snd_pcm_t *handle, signed short *samples)
+{
+ int ufds_count;
+ struct pollfd *ufds;
+ double phase = 0;
+ signed short *ptr;
+ int err, count, cptr;
+
+ while (1) {
+ generate_sine(ptr = samples, cptr = period_size, &phase);
+ while (cptr > 0) {
+ err = snd_pcm_writei(handle, ptr, cptr);
+ if (err == -EAGAIN)
+ continue;
+ if (err < 0) {
+ if (xrun_recovery(handle, err) < 0) {
+ printf("Write error: %s\n", snd_strerror(err));
+ exit(EXIT_FAILURE);
+ }
+ break; /* skip one period */
+ }
+ ptr += err * channels;
+ cptr -= err;
+ }
+ }
+}
+
+/*
+ * Transfer method - write and wait for room in buffer using poll
+ */
+
+static int wait_for_poll(struct pollfd *ufds, int count)
+{
+ int i;
+ unsigned int events;
+
+ while (1) {
+ poll(ufds, count, -1);
+ for (i = 0; i < count; i++) {
+ events = ufds[i].revents;
+ if (events & POLLERR) {
+ printf("Poll - POLLERR detected\n");
+ return -EIO;
+ }
+ if (events & POLLOUT)
+ return 0;
+ }
+ }
+}
+
+static int write_and_poll_loop(snd_pcm_t *handle, signed short *samples)
+{
+ int ufds_count;
+ struct pollfd *ufds;
+ double phase = 0;
+ signed short *ptr;
+ int err, count, cptr;
+
+ count = snd_pcm_poll_descriptors_count (handle);
+ if (count <= 0) {
+ printf("Invalid poll descriptors count\n");
+ return count;
+ }
+
+ ufds = malloc(sizeof(struct pollfd) * count);
+ if (ufds == NULL) {
+ printf("No enough memory\n");
+ return err;;
+ }
+ if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) {
+ printf("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+
+ while (1) {
+ err = wait_for_poll(ufds, count);
+ if (err < 0) {
+ printf("Wait for poll failed\n");
+ return err;
+ }
+
+ generate_sine(ptr = samples, cptr = period_size, &phase);
+ while (cptr > 0) {
+ err = snd_pcm_writei(handle, ptr, cptr);
+ if (err < 0) {
+ if (xrun_recovery(handle, err) < 0) {
+ printf("Write error: %s\n", snd_strerror(err));
+ exit(EXIT_FAILURE);
+ }
+ break; /* skip one period */
+ }
+ ptr += err * channels;
+ cptr -= err;
+ if (cptr == 0)
+ break;
+ /* it is possible, that initial buffer cannot store */
+ /* all data from last period, so wait awhile */
+ err = wait_for_poll(ufds, count);
+ if (err < 0) {
+ printf("Wait for poll failed\n");
+ return err;
+ }
+ }
+ }
+}
+
+/*
+ *
+ */
+
+struct transfer_method {
+ const char *name;
+ int (*transfer_loop)(snd_pcm_t *handle, signed short *samples);
+};
+
+static struct transfer_method transfer_methods[] = {
+ { "write", write_loop },
+ { "write_and_poll", write_and_poll_loop },
+ { NULL, NULL }
+};
+
static void help(void)
{
int k;
-f,--frequency sine wave frequency in Hz
-b,--buffer ring buffer size in samples
-p,--period period size in us
+-m,--method tranfer method
+
");
printf("Recognized sample formats are:");
for (k = 0; k < SND_PCM_FORMAT_LAST; ++(unsigned long) k) {
printf(" %s", s);
}
printf("\n");
+ printf("Recognized tranfer methods are:");
+ for (k = 0; transfer_methods[k].name; k++)
+ printf(" %s", transfer_methods[k].name);
+ printf("\n");
}
int main(int argc, char *argv[])
{"frequency", 1, NULL, 'f'},
{"buffer", 1, NULL, 'b'},
{"period", 1, NULL, 'p'},
+ {"method", 1, NULL, 'm'},
{NULL, 0, NULL, 0},
};
snd_pcm_t *handle;
char *buffer;
- int err, morehelp, i, count;
+ int err, morehelp;
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
- struct pollfd *ufds;
- unsigned int events;
double phase;
- signed short *ptr;
- int cptr;
+ int method = 0;
+ signed short *samples;
snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_sw_params_alloca(&swparams);
morehelp = 0;
while (1) {
int c;
- if ((c = getopt_long(argc, argv, "hD:r:c:f:b:p:", long_option, NULL)) < 0)
+ if ((c = getopt_long(argc, argv, "hD:r:c:f:b:p:m:", long_option, NULL)) < 0)
break;
switch (c) {
case 'h':
period_time = period_time < 1000 ? 1000 : period_time;
period_time = period_time > 1000000 ? 1000000 : period_time;
break;
+ case 'm':
+ for (method = 0; transfer_methods[method].name; method++)
+ if (!strcasecmp(transfer_methods[method].name, optarg))
+ break;
+ if (transfer_methods[method].name == NULL)
+ method = 0;
+ break;
}
}
printf("Playback device is %s\n", device);
printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels);
printf("Sine wave rate is %.4fHz\n", freq);
+ printf("Using transfer method: %s\n", transfer_methods[method].name);
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
- count = snd_pcm_poll_descriptors_count (handle);
- if (count <= 0) {
- printf("Invalid poll descriptors count\n");
- exit(EXIT_FAILURE);
- }
- ufds = malloc(sizeof(struct pollfd) * count);
- if (ufds == NULL) {
- printf("No enough memory\n");
- exit(EXIT_FAILURE);
- }
- if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) {
- printf("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
- exit(EXIT_FAILURE);
- }
-
samples = malloc((period_size * channels * snd_pcm_format_width(format)) / 8);
if (samples == NULL) {
printf("No enough memory\n");
exit(EXIT_FAILURE);
}
- phase = 0;
-
- while (1) {
- poll(ufds, count, -1);
- for (i = 0; i < count; i++) {
- events = ufds[i].revents;
- if (events & POLLOUT)
- goto __write;
- }
- continue;
-
- __write:
- generate_sine(ptr = samples, cptr = period_size, &phase);
- while (cptr > 0) {
- err = snd_pcm_writei(handle, ptr, cptr);
- if (err < 0) {
- printf("Write error: %s\n", snd_strerror(err));
- exit(EXIT_FAILURE);
- }
- ptr += err * channels;
- cptr -= err;
- while (1) {
- poll(ufds, count, -1);
- for (i = 0; i < count; i++) {
- events = ufds[i].revents;
- if (events & POLLOUT)
- goto __write1;
- }
- }
- __write1:
- }
- }
+ err = transfer_methods[method].transfer_loop(handle, samples);
+ if (err < 0)
+ printf("Transfer failed: %s\n", snd_strerror(err));
snd_pcm_close(handle);
return 0;