2 * This small demo sends a simple sinusoidal wave to your speakers.
11 #include "../include/asoundlib.h"
15 char *device = "plughw:0,0"; /* playback device */
16 snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */
17 int rate = 44100; /* stream rate */
18 int channels = 1; /* count of channels */
19 int buffer_time = 500000; /* ring buffer length in us */
20 int period_time = 100000; /* period time in us */
21 double freq = 440; /* sinusoidal wave frequency in Hz */
23 snd_pcm_sframes_t buffer_size;
24 snd_pcm_sframes_t period_size;
25 snd_output_t *output = NULL;
27 static void generate_sine(const snd_pcm_channel_area_t *areas,
28 snd_pcm_uframes_t offset,
29 int count, double *_phase)
31 double phase = *_phase;
32 double max_phase = 1.0 / freq;
33 double step = 1.0 / (double)rate;
35 signed short *samples[channels];
39 /* verify and prepare the contents of areas */
40 for (chn = 0; chn < channels; chn++) {
41 if ((areas[chn].first % 8) != 0) {
42 printf("areas[%i].first == %i, aborting...\n", chn, areas[chn].first);
45 samples[chn] = (signed short *)(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
46 if ((areas[chn].step % 16) != 0) {
47 printf("areas[%i].step == %i, aborting...\n", chn, areas[chn].step);
50 steps[chn] = areas[chn].step / 16;
51 samples[chn] += offset * steps[chn];
53 /* fill the channel areas */
55 res = sin((phase * 2 * M_PI) / max_phase - M_PI) * 32767;
57 for (chn = 0; chn < channels; chn++) {
59 samples[chn] += steps[chn];
62 if (phase >= max_phase)
68 static int set_hwparams(snd_pcm_t *handle,
69 snd_pcm_hw_params_t *params,
70 snd_pcm_access_t access)
74 /* choose all parameters */
75 err = snd_pcm_hw_params_any(handle, params);
77 printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
80 /* set the interleaved read/write format */
81 err = snd_pcm_hw_params_set_access(handle, params, access);
83 printf("Access type not available for playback: %s\n", snd_strerror(err));
86 /* set the sample format */
87 err = snd_pcm_hw_params_set_format(handle, params, format);
89 printf("Sample format not available for playback: %s\n", snd_strerror(err));
92 /* set the count of channels */
93 err = snd_pcm_hw_params_set_channels(handle, params, channels);
95 printf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
98 /* set the stream rate */
99 err = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0);
101 printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
105 printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
108 /* set the buffer time */
109 err = snd_pcm_hw_params_set_buffer_time_near(handle, params, buffer_time, &dir);
111 printf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
114 buffer_size = snd_pcm_hw_params_get_buffer_size(params);
115 /* set the period time */
116 err = snd_pcm_hw_params_set_period_time_near(handle, params, period_time, &dir);
118 printf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
121 period_size = snd_pcm_hw_params_get_period_size(params, &dir);
122 /* write the parameters to device */
123 err = snd_pcm_hw_params(handle, params);
125 printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
131 static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
135 /* get the current swparams */
136 err = snd_pcm_sw_params_current(handle, swparams);
138 printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
141 /* start the transfer when the buffer is full */
142 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size);
144 printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
147 /* allow the transfer when at least period_size samples can be processed */
148 err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size);
150 printf("Unable to set avail min for playback: %s\n", snd_strerror(err));
153 /* align all transfers to 1 sample */
154 err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1);
156 printf("Unable to set transfer align for playback: %s\n", snd_strerror(err));
159 /* write the parameters to the playback device */
160 err = snd_pcm_sw_params(handle, swparams);
162 printf("Unable to set sw params for playback: %s\n", snd_strerror(err));
169 * Underrun and suspend recovery
172 static int xrun_recovery(snd_pcm_t *handle, int err)
174 if (err == -EPIPE) { /* under-run */
175 err = snd_pcm_prepare(handle);
177 printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
179 } else if (err == -ESTRPIPE) {
180 while ((err = snd_pcm_resume(handle)) == -EAGAIN)
181 sleep(1); /* wait until the suspend flag is released */
183 err = snd_pcm_prepare(handle);
185 printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
193 * Transfer method - write only
196 static int write_loop(snd_pcm_t *handle,
197 signed short *samples,
198 snd_pcm_channel_area_t *areas)
205 generate_sine(areas, 0, period_size, &phase);
209 err = snd_pcm_writei(handle, ptr, cptr);
213 if (xrun_recovery(handle, err) < 0) {
214 printf("Write error: %s\n", snd_strerror(err));
217 break; /* skip one period */
219 ptr += err * channels;
226 * Transfer method - write and wait for room in buffer using poll
229 static int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count)
231 unsigned short revents;
234 poll(ufds, count, -1);
235 snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);
236 if (revents & POLLERR)
238 if (revents & POLLOUT)
243 static int write_and_poll_loop(snd_pcm_t *handle,
244 signed short *samples,
245 snd_pcm_channel_area_t *areas)
250 int err, count, cptr, init;
252 count = snd_pcm_poll_descriptors_count (handle);
254 printf("Invalid poll descriptors count\n");
258 ufds = malloc(sizeof(struct pollfd) * count);
260 printf("No enough memory\n");
263 if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) {
264 printf("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
271 err = wait_for_poll(handle, ufds, count);
273 if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN ||
274 snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) {
275 err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
276 if (xrun_recovery(handle, err) < 0) {
277 printf("Write error: %s\n", snd_strerror(err));
282 printf("Wait for poll failed\n");
288 generate_sine(areas, 0, period_size, &phase);
292 err = snd_pcm_writei(handle, ptr, cptr);
294 if (xrun_recovery(handle, err) < 0) {
295 printf("Write error: %s\n", snd_strerror(err));
299 break; /* skip one period */
301 if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING)
303 ptr += err * channels;
307 /* it is possible, that the initial buffer cannot store */
308 /* all data from the last period, so wait awhile */
309 err = wait_for_poll(handle, ufds, count);
311 if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN ||
312 snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) {
313 err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
314 if (xrun_recovery(handle, err) < 0) {
315 printf("Write error: %s\n", snd_strerror(err));
320 printf("Wait for poll failed\n");
329 * Transfer method - asynchronous notification
332 struct async_private_data {
333 signed short *samples;
334 snd_pcm_channel_area_t *areas;
338 static void async_callback(snd_async_handler_t *ahandler)
340 snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler);
341 struct async_private_data *data = snd_async_handler_get_callback_private(ahandler);
342 signed short *samples = data->samples;
343 snd_pcm_channel_area_t *areas = data->areas;
344 snd_pcm_sframes_t avail;
347 avail = snd_pcm_avail_update(handle);
348 while (avail >= period_size) {
349 generate_sine(areas, 0, period_size, &data->phase);
350 err = snd_pcm_writei(handle, samples, period_size);
352 printf("Initial write error: %s\n", snd_strerror(err));
355 if (err != period_size) {
356 printf("Initial write error: written %i expected %li\n", err, period_size);
359 avail = snd_pcm_avail_update(handle);
363 static int async_loop(snd_pcm_t *handle,
364 signed short *samples,
365 snd_pcm_channel_area_t *areas)
367 struct async_private_data data;
368 snd_async_handler_t *ahandler;
371 data.samples = samples;
374 err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, &data);
376 printf("Unable to register async handler\n");
379 for (count = 0; count < 2; count++) {
380 generate_sine(areas, 0, period_size, &data.phase);
381 err = snd_pcm_writei(handle, samples, period_size);
383 printf("Initial write error: %s\n", snd_strerror(err));
386 if (err != period_size) {
387 printf("Initial write error: written %i expected %li\n", err, period_size);
391 err = snd_pcm_start(handle);
393 printf("Start error: %s\n", snd_strerror(err));
397 /* because all other work is done in the signal handler,
398 suspend the process */
405 * Transfer method - direct write only
408 static int direct_loop(snd_pcm_t *handle,
409 signed short *samples,
410 snd_pcm_channel_area_t *areas)
413 const snd_pcm_channel_area_t *my_areas;
414 snd_pcm_uframes_t offset, frames, size;
415 snd_pcm_sframes_t avail, commitres;
416 snd_pcm_state_t state;
420 state = snd_pcm_state(handle);
421 if (state == SND_PCM_STATE_XRUN) {
422 err = xrun_recovery(handle, -EPIPE);
424 printf("XRUN recovery failed: %s\n", snd_strerror(err));
428 } else if (state == SND_PCM_STATE_SUSPENDED) {
429 err = xrun_recovery(handle, -ESTRPIPE);
431 printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
435 avail = snd_pcm_avail_update(handle);
437 err = xrun_recovery(handle, avail);
439 printf("avail update failed: %s\n", snd_strerror(err));
445 if (avail < period_size) {
448 err = snd_pcm_start(handle);
450 printf("Start error: %s\n", snd_strerror(err));
454 err = snd_pcm_wait(handle, -1);
456 if ((err = xrun_recovery(handle, err)) < 0) {
457 printf("snd_pcm_wait error: %s\n", snd_strerror(err));
468 err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
470 if ((err = xrun_recovery(handle, err)) < 0) {
471 printf("MMAP begin avail error: %s\n", snd_strerror(err));
476 generate_sine(my_areas, offset, frames, &phase);
477 commitres = snd_pcm_mmap_commit(handle, offset, frames);
478 if (commitres < 0 || commitres != frames) {
479 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
480 printf("MMAP commit error: %s\n", snd_strerror(err));
491 * Transfer method - direct write only using mmap_write functions
494 static int direct_write_loop(snd_pcm_t *handle,
495 signed short *samples,
496 snd_pcm_channel_area_t *areas)
503 generate_sine(areas, 0, period_size, &phase);
507 err = snd_pcm_mmap_writei(handle, ptr, cptr);
511 if (xrun_recovery(handle, err) < 0) {
512 printf("Write error: %s\n", snd_strerror(err));
515 break; /* skip one period */
517 ptr += err * channels;
527 struct transfer_method {
529 snd_pcm_access_t access;
530 int (*transfer_loop)(snd_pcm_t *handle,
531 signed short *samples,
532 snd_pcm_channel_area_t *areas);
535 static struct transfer_method transfer_methods[] = {
536 { "write", SND_PCM_ACCESS_RW_INTERLEAVED, write_loop },
537 { "write_and_poll", SND_PCM_ACCESS_RW_INTERLEAVED, write_and_poll_loop },
538 { "async", SND_PCM_ACCESS_RW_INTERLEAVED, async_loop },
539 { "direct_interleaved", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_loop },
540 { "direct_noninterleaved", SND_PCM_ACCESS_MMAP_NONINTERLEAVED, direct_loop },
541 { "direct_write", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_write_loop },
542 { NULL, SND_PCM_ACCESS_RW_INTERLEAVED, NULL }
545 static void help(void)
549 Usage: latency [OPTION]... [FILE]...
551 -D,--device playback device
552 -r,--rate stream rate in Hz
553 -c,--channels count of channels in stream
554 -f,--frequency sine wave frequency in Hz
555 -b,--buffer ring buffer size in samples
556 -p,--period period size in us
557 -m,--method transfer method
560 printf("Recognized sample formats are:");
561 for (k = 0; k < SND_PCM_FORMAT_LAST; ++(unsigned long) k) {
562 const char *s = snd_pcm_format_name(k);
567 printf("Recognized transfer methods are:");
568 for (k = 0; transfer_methods[k].name; k++)
569 printf(" %s", transfer_methods[k].name);
573 int main(int argc, char *argv[])
575 struct option long_option[] =
577 {"help", 0, NULL, 'h'},
578 {"device", 1, NULL, 'D'},
579 {"rate", 1, NULL, 'r'},
580 {"channels", 1, NULL, 'c'},
581 {"frequency", 1, NULL, 'f'},
582 {"buffer", 1, NULL, 'b'},
583 {"period", 1, NULL, 'p'},
584 {"method", 1, NULL, 'm'},
589 snd_pcm_hw_params_t *hwparams;
590 snd_pcm_sw_params_t *swparams;
592 signed short *samples;
594 snd_pcm_channel_area_t *areas;
596 snd_pcm_hw_params_alloca(&hwparams);
597 snd_pcm_sw_params_alloca(&swparams);
602 if ((c = getopt_long(argc, argv, "hD:r:c:f:b:p:m:", long_option, NULL)) < 0)
609 device = strdup(optarg);
613 rate = rate < 4000 ? 4000 : rate;
614 rate = rate > 196000 ? 196000 : rate;
617 channels = atoi(optarg);
618 channels = channels < 1 ? 1 : channels;
619 channels = channels > 1024 ? 1024 : channels;
623 freq = freq < 50 ? 50 : freq;
624 freq = freq > 5000 ? 5000 : freq;
627 buffer_size = atoi(optarg);
628 buffer_size = buffer_size < 64 ? 64 : buffer_size;
629 buffer_size = buffer_size > 64*1024 ? 64*1024 : buffer_size;
632 period_time = atoi(optarg);
633 period_time = period_time < 1000 ? 1000 : period_time;
634 period_time = period_time > 1000000 ? 1000000 : period_time;
637 for (method = 0; transfer_methods[method].name; method++)
638 if (!strcasecmp(transfer_methods[method].name, optarg))
640 if (transfer_methods[method].name == NULL)
651 err = snd_output_stdio_attach(&output, stdout, 0);
653 printf("Output failed: %s\n", snd_strerror(err));
657 printf("Playback device is %s\n", device);
658 printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels);
659 printf("Sine wave rate is %.4fHz\n", freq);
660 printf("Using transfer method: %s\n", transfer_methods[method].name);
662 if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
663 printf("Playback open error: %s\n", snd_strerror(err));
667 if ((err = set_hwparams(handle, hwparams, transfer_methods[method].access)) < 0) {
668 printf("Setting of hwparams failed: %s\n", snd_strerror(err));
671 if ((err = set_swparams(handle, swparams)) < 0) {
672 printf("Setting of swparams failed: %s\n", snd_strerror(err));
676 samples = malloc((period_size * channels * snd_pcm_format_width(format)) / 8);
677 if (samples == NULL) {
678 printf("No enough memory\n");
682 areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
684 printf("No enough memory\n");
687 for (chn = 0; chn < channels; chn++) {
688 areas[chn].addr = samples;
689 areas[chn].first = chn * 16;
690 areas[chn].step = channels * 16;
693 err = transfer_methods[method].transfer_loop(handle, samples, areas);
695 printf("Transfer failed: %s\n", snd_strerror(err));
699 snd_pcm_close(handle);