2 * This small demo sends a simple sinusoidal wave to your speakers.
11 #include "../include/asoundlib.h"
15 static char *device = "plughw:0,0"; /* playback device */
16 static snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */
17 static unsigned int rate = 44100; /* stream rate */
18 static unsigned int channels = 1; /* count of channels */
19 static unsigned int buffer_time = 500000; /* ring buffer length in us */
20 static unsigned int period_time = 100000; /* period time in us */
21 static double freq = 440; /* sinusoidal wave frequency in Hz */
22 static int verbose = 0; /* verbose flag */
23 static int resample = 1; /* enable alsa-lib resampling */
24 static int period_event = 0; /* produce poll event after each period */
26 static snd_pcm_sframes_t buffer_size;
27 static snd_pcm_sframes_t period_size;
28 static snd_output_t *output = NULL;
30 static void generate_sine(const snd_pcm_channel_area_t *areas,
31 snd_pcm_uframes_t offset,
32 int count, double *_phase)
34 static double max_phase = 2. * M_PI;
35 double phase = *_phase;
36 double step = max_phase*freq/(double)rate;
37 unsigned char *samples[channels];
40 int format_bits = snd_pcm_format_width(format);
41 unsigned int maxval = (1 << (format_bits - 1)) - 1;
42 int bps = format_bits / 8; /* bytes per sample */
43 int phys_bps = snd_pcm_format_physical_width(format) / 8;
44 int big_endian = snd_pcm_format_big_endian(format) == 1;
45 int to_unsigned = snd_pcm_format_unsigned(format) == 1;
46 int is_float = (format == SND_PCM_FORMAT_FLOAT_LE ||
47 format == SND_PCM_FORMAT_FLOAT_BE);
49 /* verify and prepare the contents of areas */
50 for (chn = 0; chn < channels; chn++) {
51 if ((areas[chn].first % 8) != 0) {
52 printf("areas[%i].first == %i, aborting...\n", chn, areas[chn].first);
55 samples[chn] = /*(signed short *)*/(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
56 if ((areas[chn].step % 16) != 0) {
57 printf("areas[%i].step == %i, aborting...\n", chn, areas[chn].step);
60 steps[chn] = areas[chn].step / 8;
61 samples[chn] += offset * steps[chn];
63 /* fill the channel areas */
74 res = sin(phase) * maxval;
76 res ^= 1U << (format_bits - 1);
77 for (chn = 0; chn < channels; chn++) {
78 /* Generate data in native endian format */
80 for (i = 0; i < bps; i++)
81 *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
83 for (i = 0; i < bps; i++)
84 *(samples[chn] + i) = (res >> i * 8) & 0xff;
86 samples[chn] += steps[chn];
89 if (phase >= max_phase)
95 static int set_hwparams(snd_pcm_t *handle,
96 snd_pcm_hw_params_t *params,
97 snd_pcm_access_t access)
100 snd_pcm_uframes_t size;
103 /* choose all parameters */
104 err = snd_pcm_hw_params_any(handle, params);
106 printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
109 /* set hardware resampling */
110 err = snd_pcm_hw_params_set_rate_resample(handle, params, resample);
112 printf("Resampling setup failed for playback: %s\n", snd_strerror(err));
115 /* set the interleaved read/write format */
116 err = snd_pcm_hw_params_set_access(handle, params, access);
118 printf("Access type not available for playback: %s\n", snd_strerror(err));
121 /* set the sample format */
122 err = snd_pcm_hw_params_set_format(handle, params, format);
124 printf("Sample format not available for playback: %s\n", snd_strerror(err));
127 /* set the count of channels */
128 err = snd_pcm_hw_params_set_channels(handle, params, channels);
130 printf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
133 /* set the stream rate */
135 err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
137 printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
141 printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
144 /* set the buffer time */
145 err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
147 printf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
150 err = snd_pcm_hw_params_get_buffer_size(params, &size);
152 printf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
156 /* set the period time */
157 err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
159 printf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
162 err = snd_pcm_hw_params_get_period_size(params, &size, &dir);
164 printf("Unable to get period size for playback: %s\n", snd_strerror(err));
168 /* write the parameters to device */
169 err = snd_pcm_hw_params(handle, params);
171 printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
177 static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
181 /* get the current swparams */
182 err = snd_pcm_sw_params_current(handle, swparams);
184 printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
187 /* start the transfer when the buffer is almost full: */
188 /* (buffer_size / avail_min) * avail_min */
189 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, (buffer_size / period_size) * period_size);
191 printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
194 /* allow the transfer when at least period_size samples can be processed */
195 /* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
196 err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? buffer_size : period_size);
198 printf("Unable to set avail min for playback: %s\n", snd_strerror(err));
201 /* enable period events when requested */
203 err = snd_pcm_sw_params_set_period_event(handle, swparams, 1);
205 printf("Unable to set period event: %s\n", snd_strerror(err));
209 /* write the parameters to the playback device */
210 err = snd_pcm_sw_params(handle, swparams);
212 printf("Unable to set sw params for playback: %s\n", snd_strerror(err));
219 * Underrun and suspend recovery
222 static int xrun_recovery(snd_pcm_t *handle, int err)
225 printf("stream recovery\n");
226 if (err == -EPIPE) { /* under-run */
227 err = snd_pcm_prepare(handle);
229 printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
231 } else if (err == -ESTRPIPE) {
232 while ((err = snd_pcm_resume(handle)) == -EAGAIN)
233 sleep(1); /* wait until the suspend flag is released */
235 err = snd_pcm_prepare(handle);
237 printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
245 * Transfer method - write only
248 static int write_loop(snd_pcm_t *handle,
249 signed short *samples,
250 snd_pcm_channel_area_t *areas)
257 generate_sine(areas, 0, period_size, &phase);
261 err = snd_pcm_writei(handle, ptr, cptr);
265 if (xrun_recovery(handle, err) < 0) {
266 printf("Write error: %s\n", snd_strerror(err));
269 break; /* skip one period */
271 ptr += err * channels;
278 * Transfer method - write and wait for room in buffer using poll
281 static int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count)
283 unsigned short revents;
286 poll(ufds, count, -1);
287 snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);
288 if (revents & POLLERR)
290 if (revents & POLLOUT)
295 static int write_and_poll_loop(snd_pcm_t *handle,
296 signed short *samples,
297 snd_pcm_channel_area_t *areas)
302 int err, count, cptr, init;
304 count = snd_pcm_poll_descriptors_count (handle);
306 printf("Invalid poll descriptors count\n");
310 ufds = malloc(sizeof(struct pollfd) * count);
312 printf("No enough memory\n");
315 if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) {
316 printf("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
323 err = wait_for_poll(handle, ufds, count);
325 if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN ||
326 snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) {
327 err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
328 if (xrun_recovery(handle, err) < 0) {
329 printf("Write error: %s\n", snd_strerror(err));
334 printf("Wait for poll failed\n");
340 generate_sine(areas, 0, period_size, &phase);
344 err = snd_pcm_writei(handle, ptr, cptr);
346 if (xrun_recovery(handle, err) < 0) {
347 printf("Write error: %s\n", snd_strerror(err));
351 break; /* skip one period */
353 if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING)
355 ptr += err * channels;
359 /* it is possible, that the initial buffer cannot store */
360 /* all data from the last period, so wait awhile */
361 err = wait_for_poll(handle, ufds, count);
363 if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN ||
364 snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) {
365 err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
366 if (xrun_recovery(handle, err) < 0) {
367 printf("Write error: %s\n", snd_strerror(err));
372 printf("Wait for poll failed\n");
381 * Transfer method - asynchronous notification
384 struct async_private_data {
385 signed short *samples;
386 snd_pcm_channel_area_t *areas;
390 static void async_callback(snd_async_handler_t *ahandler)
392 snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler);
393 struct async_private_data *data = snd_async_handler_get_callback_private(ahandler);
394 signed short *samples = data->samples;
395 snd_pcm_channel_area_t *areas = data->areas;
396 snd_pcm_sframes_t avail;
399 avail = snd_pcm_avail_update(handle);
400 while (avail >= period_size) {
401 generate_sine(areas, 0, period_size, &data->phase);
402 err = snd_pcm_writei(handle, samples, period_size);
404 printf("Write error: %s\n", snd_strerror(err));
407 if (err != period_size) {
408 printf("Write error: written %i expected %li\n", err, period_size);
411 avail = snd_pcm_avail_update(handle);
415 static int async_loop(snd_pcm_t *handle,
416 signed short *samples,
417 snd_pcm_channel_area_t *areas)
419 struct async_private_data data;
420 snd_async_handler_t *ahandler;
423 data.samples = samples;
426 err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, &data);
428 printf("Unable to register async handler\n");
431 for (count = 0; count < 2; count++) {
432 generate_sine(areas, 0, period_size, &data.phase);
433 err = snd_pcm_writei(handle, samples, period_size);
435 printf("Initial write error: %s\n", snd_strerror(err));
438 if (err != period_size) {
439 printf("Initial write error: written %i expected %li\n", err, period_size);
443 if (snd_pcm_state(handle) == SND_PCM_STATE_PREPARED) {
444 err = snd_pcm_start(handle);
446 printf("Start error: %s\n", snd_strerror(err));
451 /* because all other work is done in the signal handler,
452 suspend the process */
459 * Transfer method - asynchronous notification + direct write
462 static void async_direct_callback(snd_async_handler_t *ahandler)
464 snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler);
465 struct async_private_data *data = snd_async_handler_get_callback_private(ahandler);
466 const snd_pcm_channel_area_t *my_areas;
467 snd_pcm_uframes_t offset, frames, size;
468 snd_pcm_sframes_t avail, commitres;
469 snd_pcm_state_t state;
473 state = snd_pcm_state(handle);
474 if (state == SND_PCM_STATE_XRUN) {
475 err = xrun_recovery(handle, -EPIPE);
477 printf("XRUN recovery failed: %s\n", snd_strerror(err));
481 } else if (state == SND_PCM_STATE_SUSPENDED) {
482 err = xrun_recovery(handle, -ESTRPIPE);
484 printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
488 avail = snd_pcm_avail_update(handle);
490 err = xrun_recovery(handle, avail);
492 printf("avail update failed: %s\n", snd_strerror(err));
498 if (avail < period_size) {
501 err = snd_pcm_start(handle);
503 printf("Start error: %s\n", snd_strerror(err));
514 err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
516 if ((err = xrun_recovery(handle, err)) < 0) {
517 printf("MMAP begin avail error: %s\n", snd_strerror(err));
522 generate_sine(my_areas, offset, frames, &data->phase);
523 commitres = snd_pcm_mmap_commit(handle, offset, frames);
524 if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
525 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
526 printf("MMAP commit error: %s\n", snd_strerror(err));
536 static int async_direct_loop(snd_pcm_t *handle,
537 signed short *samples ATTRIBUTE_UNUSED,
538 snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED)
540 struct async_private_data data;
541 snd_async_handler_t *ahandler;
542 const snd_pcm_channel_area_t *my_areas;
543 snd_pcm_uframes_t offset, frames, size;
544 snd_pcm_sframes_t commitres;
547 data.samples = NULL; /* we do not require the global sample area for direct write */
548 data.areas = NULL; /* we do not require the global areas for direct write */
550 err = snd_async_add_pcm_handler(&ahandler, handle, async_direct_callback, &data);
552 printf("Unable to register async handler\n");
555 for (count = 0; count < 2; count++) {
559 err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
561 if ((err = xrun_recovery(handle, err)) < 0) {
562 printf("MMAP begin avail error: %s\n", snd_strerror(err));
566 generate_sine(my_areas, offset, frames, &data.phase);
567 commitres = snd_pcm_mmap_commit(handle, offset, frames);
568 if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
569 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
570 printf("MMAP commit error: %s\n", snd_strerror(err));
577 err = snd_pcm_start(handle);
579 printf("Start error: %s\n", snd_strerror(err));
583 /* because all other work is done in the signal handler,
584 suspend the process */
591 * Transfer method - direct write only
594 static int direct_loop(snd_pcm_t *handle,
595 signed short *samples ATTRIBUTE_UNUSED,
596 snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED)
599 const snd_pcm_channel_area_t *my_areas;
600 snd_pcm_uframes_t offset, frames, size;
601 snd_pcm_sframes_t avail, commitres;
602 snd_pcm_state_t state;
606 state = snd_pcm_state(handle);
607 if (state == SND_PCM_STATE_XRUN) {
608 err = xrun_recovery(handle, -EPIPE);
610 printf("XRUN recovery failed: %s\n", snd_strerror(err));
614 } else if (state == SND_PCM_STATE_SUSPENDED) {
615 err = xrun_recovery(handle, -ESTRPIPE);
617 printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
621 avail = snd_pcm_avail_update(handle);
623 err = xrun_recovery(handle, avail);
625 printf("avail update failed: %s\n", snd_strerror(err));
631 if (avail < period_size) {
634 err = snd_pcm_start(handle);
636 printf("Start error: %s\n", snd_strerror(err));
640 err = snd_pcm_wait(handle, -1);
642 if ((err = xrun_recovery(handle, err)) < 0) {
643 printf("snd_pcm_wait error: %s\n", snd_strerror(err));
654 err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
656 if ((err = xrun_recovery(handle, err)) < 0) {
657 printf("MMAP begin avail error: %s\n", snd_strerror(err));
662 generate_sine(my_areas, offset, frames, &phase);
663 commitres = snd_pcm_mmap_commit(handle, offset, frames);
664 if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
665 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
666 printf("MMAP commit error: %s\n", snd_strerror(err));
677 * Transfer method - direct write only using mmap_write functions
680 static int direct_write_loop(snd_pcm_t *handle,
681 signed short *samples,
682 snd_pcm_channel_area_t *areas)
689 generate_sine(areas, 0, period_size, &phase);
693 err = snd_pcm_mmap_writei(handle, ptr, cptr);
697 if (xrun_recovery(handle, err) < 0) {
698 printf("Write error: %s\n", snd_strerror(err));
701 break; /* skip one period */
703 ptr += err * channels;
713 struct transfer_method {
715 snd_pcm_access_t access;
716 int (*transfer_loop)(snd_pcm_t *handle,
717 signed short *samples,
718 snd_pcm_channel_area_t *areas);
721 static struct transfer_method transfer_methods[] = {
722 { "write", SND_PCM_ACCESS_RW_INTERLEAVED, write_loop },
723 { "write_and_poll", SND_PCM_ACCESS_RW_INTERLEAVED, write_and_poll_loop },
724 { "async", SND_PCM_ACCESS_RW_INTERLEAVED, async_loop },
725 { "async_direct", SND_PCM_ACCESS_MMAP_INTERLEAVED, async_direct_loop },
726 { "direct_interleaved", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_loop },
727 { "direct_noninterleaved", SND_PCM_ACCESS_MMAP_NONINTERLEAVED, direct_loop },
728 { "direct_write", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_write_loop },
729 { NULL, SND_PCM_ACCESS_RW_INTERLEAVED, NULL }
732 static void help(void)
736 "Usage: pcm [OPTION]... [FILE]...\n"
738 "-D,--device playback device\n"
739 "-r,--rate stream rate in Hz\n"
740 "-c,--channels count of channels in stream\n"
741 "-f,--frequency sine wave frequency in Hz\n"
742 "-b,--buffer ring buffer size in us\n"
743 "-p,--period period size in us\n"
744 "-m,--method transfer method\n"
745 "-o,--format sample format\n"
746 "-v,--verbose show the PCM setup parameters\n"
747 "-n,--noresample do not resample\n"
748 "-e,--pevent enable poll event after each period\n"
750 printf("Recognized sample formats are:");
751 for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) {
752 const char *s = snd_pcm_format_name(k);
757 printf("Recognized transfer methods are:");
758 for (k = 0; transfer_methods[k].name; k++)
759 printf(" %s", transfer_methods[k].name);
763 int main(int argc, char *argv[])
765 struct option long_option[] =
767 {"help", 0, NULL, 'h'},
768 {"device", 1, NULL, 'D'},
769 {"rate", 1, NULL, 'r'},
770 {"channels", 1, NULL, 'c'},
771 {"frequency", 1, NULL, 'f'},
772 {"buffer", 1, NULL, 'b'},
773 {"period", 1, NULL, 'p'},
774 {"method", 1, NULL, 'm'},
775 {"format", 1, NULL, 'o'},
776 {"verbose", 1, NULL, 'v'},
777 {"noresample", 1, NULL, 'n'},
778 {"pevent", 1, NULL, 'e'},
783 snd_pcm_hw_params_t *hwparams;
784 snd_pcm_sw_params_t *swparams;
786 signed short *samples;
788 snd_pcm_channel_area_t *areas;
790 snd_pcm_hw_params_alloca(&hwparams);
791 snd_pcm_sw_params_alloca(&swparams);
796 if ((c = getopt_long(argc, argv, "hD:r:c:f:b:p:m:o:vne", long_option, NULL)) < 0)
803 device = strdup(optarg);
807 rate = rate < 4000 ? 4000 : rate;
808 rate = rate > 196000 ? 196000 : rate;
811 channels = atoi(optarg);
812 channels = channels < 1 ? 1 : channels;
813 channels = channels > 1024 ? 1024 : channels;
817 freq = freq < 50 ? 50 : freq;
818 freq = freq > 5000 ? 5000 : freq;
821 buffer_time = atoi(optarg);
822 buffer_time = buffer_time < 1000 ? 1000 : buffer_time;
823 buffer_time = buffer_time > 1000000 ? 1000000 : buffer_time;
826 period_time = atoi(optarg);
827 period_time = period_time < 1000 ? 1000 : period_time;
828 period_time = period_time > 1000000 ? 1000000 : period_time;
831 for (method = 0; transfer_methods[method].name; method++)
832 if (!strcasecmp(transfer_methods[method].name, optarg))
834 if (transfer_methods[method].name == NULL)
838 for (format = 0; format < SND_PCM_FORMAT_LAST; format++) {
839 const char *format_name = snd_pcm_format_name(format);
841 if (!strcasecmp(format_name, optarg))
844 if (format == SND_PCM_FORMAT_LAST)
845 format = SND_PCM_FORMAT_S16;
846 if (!snd_pcm_format_linear(format) &&
847 !(format == SND_PCM_FORMAT_FLOAT_LE ||
848 format == SND_PCM_FORMAT_FLOAT_BE)) {
849 printf("Invalid (non-linear/float) format %s\n",
871 err = snd_output_stdio_attach(&output, stdout, 0);
873 printf("Output failed: %s\n", snd_strerror(err));
877 printf("Playback device is %s\n", device);
878 printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels);
879 printf("Sine wave rate is %.4fHz\n", freq);
880 printf("Using transfer method: %s\n", transfer_methods[method].name);
882 if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
883 printf("Playback open error: %s\n", snd_strerror(err));
887 if ((err = set_hwparams(handle, hwparams, transfer_methods[method].access)) < 0) {
888 printf("Setting of hwparams failed: %s\n", snd_strerror(err));
891 if ((err = set_swparams(handle, swparams)) < 0) {
892 printf("Setting of swparams failed: %s\n", snd_strerror(err));
897 snd_pcm_dump(handle, output);
899 samples = malloc((period_size * channels * snd_pcm_format_physical_width(format)) / 8);
900 if (samples == NULL) {
901 printf("No enough memory\n");
905 areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
907 printf("No enough memory\n");
910 for (chn = 0; chn < channels; chn++) {
911 areas[chn].addr = samples;
912 areas[chn].first = chn * snd_pcm_format_physical_width(format);
913 areas[chn].step = channels * snd_pcm_format_physical_width(format);
916 err = transfer_methods[method].transfer_loop(handle, samples, areas);
918 printf("Transfer failed: %s\n", snd_strerror(err));
922 snd_pcm_close(handle);