1 /*
2 * This small demo sends a simple sinusoidal wave to your speakers.
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sched.h>
9 #include <errno.h>
10 #include <getopt.h>
11 #include "../include/asoundlib.h"
12 #include <sys/time.h>
13 #include <math.h>
14
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 */
25
26 static snd_pcm_sframes_t buffer_size;
27 static snd_pcm_sframes_t period_size;
28 static snd_output_t *output = NULL;
29
generate_sine(const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,int count,double * _phase)30 static void generate_sine(const snd_pcm_channel_area_t *areas,
31 snd_pcm_uframes_t offset,
32 int count, double *_phase)
33 {
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];
38 int steps[channels];
39 unsigned int chn;
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);
48
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[%u].first == %u, aborting...\n", chn, areas[chn].first);
53 exit(EXIT_FAILURE);
54 }
55 samples[chn] = /*(signed short *)*/(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
56 if ((areas[chn].step % 16) != 0) {
57 printf("areas[%u].step == %u, aborting...\n", chn, areas[chn].step);
58 exit(EXIT_FAILURE);
59 }
60 steps[chn] = areas[chn].step / 8;
61 samples[chn] += offset * steps[chn];
62 }
63 /* fill the channel areas */
64 while (count-- > 0) {
65 union {
66 float f;
67 int i;
68 } fval;
69 int res, i;
70 if (is_float) {
71 fval.f = sin(phase);
72 res = fval.i;
73 } else
74 res = sin(phase) * maxval;
75 if (to_unsigned)
76 res ^= 1U << (format_bits - 1);
77 for (chn = 0; chn < channels; chn++) {
78 /* Generate data in native endian format */
79 if (big_endian) {
80 for (i = 0; i < bps; i++)
81 *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
82 } else {
83 for (i = 0; i < bps; i++)
84 *(samples[chn] + i) = (res >> i * 8) & 0xff;
85 }
86 samples[chn] += steps[chn];
87 }
88 phase += step;
89 if (phase >= max_phase)
90 phase -= max_phase;
91 }
92 *_phase = phase;
93 }
94
set_hwparams(snd_pcm_t * handle,snd_pcm_hw_params_t * params,snd_pcm_access_t access)95 static int set_hwparams(snd_pcm_t *handle,
96 snd_pcm_hw_params_t *params,
97 snd_pcm_access_t access)
98 {
99 unsigned int rrate;
100 snd_pcm_uframes_t size;
101 int err, dir;
102
103 /* choose all parameters */
104 err = snd_pcm_hw_params_any(handle, params);
105 if (err < 0) {
106 printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
107 return err;
108 }
109 /* set hardware resampling */
110 err = snd_pcm_hw_params_set_rate_resample(handle, params, resample);
111 if (err < 0) {
112 printf("Resampling setup failed for playback: %s\n", snd_strerror(err));
113 return err;
114 }
115 /* set the interleaved read/write format */
116 err = snd_pcm_hw_params_set_access(handle, params, access);
117 if (err < 0) {
118 printf("Access type not available for playback: %s\n", snd_strerror(err));
119 return err;
120 }
121 /* set the sample format */
122 err = snd_pcm_hw_params_set_format(handle, params, format);
123 if (err < 0) {
124 printf("Sample format not available for playback: %s\n", snd_strerror(err));
125 return err;
126 }
127 /* set the count of channels */
128 err = snd_pcm_hw_params_set_channels(handle, params, channels);
129 if (err < 0) {
130 printf("Channels count (%u) not available for playbacks: %s\n", channels, snd_strerror(err));
131 return err;
132 }
133 /* set the stream rate */
134 rrate = rate;
135 err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
136 if (err < 0) {
137 printf("Rate %uHz not available for playback: %s\n", rate, snd_strerror(err));
138 return err;
139 }
140 if (rrate != rate) {
141 printf("Rate doesn't match (requested %uHz, get %iHz)\n", rate, err);
142 return -EINVAL;
143 }
144 /* set the buffer time */
145 err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
146 if (err < 0) {
147 printf("Unable to set buffer time %u for playback: %s\n", buffer_time, snd_strerror(err));
148 return err;
149 }
150 err = snd_pcm_hw_params_get_buffer_size(params, &size);
151 if (err < 0) {
152 printf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
153 return err;
154 }
155 buffer_size = size;
156 /* set the period time */
157 err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
158 if (err < 0) {
159 printf("Unable to set period time %u for playback: %s\n", period_time, snd_strerror(err));
160 return err;
161 }
162 err = snd_pcm_hw_params_get_period_size(params, &size, &dir);
163 if (err < 0) {
164 printf("Unable to get period size for playback: %s\n", snd_strerror(err));
165 return err;
166 }
167 period_size = size;
168 /* write the parameters to device */
169 err = snd_pcm_hw_params(handle, params);
170 if (err < 0) {
171 printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
172 return err;
173 }
174 return 0;
175 }
176
set_swparams(snd_pcm_t * handle,snd_pcm_sw_params_t * swparams)177 static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
178 {
179 int err;
180
181 /* get the current swparams */
182 err = snd_pcm_sw_params_current(handle, swparams);
183 if (err < 0) {
184 printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
185 return err;
186 }
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);
190 if (err < 0) {
191 printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
192 return err;
193 }
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);
197 if (err < 0) {
198 printf("Unable to set avail min for playback: %s\n", snd_strerror(err));
199 return err;
200 }
201 /* enable period events when requested */
202 if (period_event) {
203 err = snd_pcm_sw_params_set_period_event(handle, swparams, 1);
204 if (err < 0) {
205 printf("Unable to set period event: %s\n", snd_strerror(err));
206 return err;
207 }
208 }
209 /* write the parameters to the playback device */
210 err = snd_pcm_sw_params(handle, swparams);
211 if (err < 0) {
212 printf("Unable to set sw params for playback: %s\n", snd_strerror(err));
213 return err;
214 }
215 return 0;
216 }
217
218 /*
219 * Underrun and suspend recovery
220 */
221
xrun_recovery(snd_pcm_t * handle,int err)222 static int xrun_recovery(snd_pcm_t *handle, int err)
223 {
224 if (verbose)
225 printf("stream recovery\n");
226 if (err == -EPIPE) { /* under-run */
227 err = snd_pcm_prepare(handle);
228 if (err < 0)
229 printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
230 return 0;
231 } else if (err == -ESTRPIPE) {
232 while ((err = snd_pcm_resume(handle)) == -EAGAIN)
233 sleep(1); /* wait until the suspend flag is released */
234 if (err < 0) {
235 err = snd_pcm_prepare(handle);
236 if (err < 0)
237 printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
238 }
239 return 0;
240 }
241 return err;
242 }
243
244 /*
245 * Transfer method - write only
246 */
247
write_loop(snd_pcm_t * handle,signed short * samples,snd_pcm_channel_area_t * areas)248 static int write_loop(snd_pcm_t *handle,
249 signed short *samples,
250 snd_pcm_channel_area_t *areas)
251 {
252 double phase = 0;
253 signed short *ptr;
254 int err, cptr;
255
256 while (1) {
257 generate_sine(areas, 0, period_size, &phase);
258 ptr = samples;
259 cptr = period_size;
260 while (cptr > 0) {
261 err = snd_pcm_writei(handle, ptr, cptr);
262 if (err == -EAGAIN)
263 continue;
264 if (err < 0) {
265 if (xrun_recovery(handle, err) < 0) {
266 printf("Write error: %s\n", snd_strerror(err));
267 exit(EXIT_FAILURE);
268 }
269 break; /* skip one period */
270 }
271 ptr += err * channels;
272 cptr -= err;
273 }
274 }
275 }
276
277 /*
278 * Transfer method - write and wait for room in buffer using poll
279 */
280
wait_for_poll(snd_pcm_t * handle,struct pollfd * ufds,unsigned int count)281 static int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count)
282 {
283 unsigned short revents;
284
285 while (1) {
286 poll(ufds, count, -1);
287 snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);
288 if (revents & POLLERR)
289 return -EIO;
290 if (revents & POLLOUT)
291 return 0;
292 }
293 }
294
write_and_poll_loop(snd_pcm_t * handle,signed short * samples,snd_pcm_channel_area_t * areas)295 static int write_and_poll_loop(snd_pcm_t *handle,
296 signed short *samples,
297 snd_pcm_channel_area_t *areas)
298 {
299 struct pollfd *ufds;
300 double phase = 0;
301 signed short *ptr;
302 int err, count, cptr, init;
303
304 count = snd_pcm_poll_descriptors_count (handle);
305 if (count <= 0) {
306 printf("Invalid poll descriptors count\n");
307 return count;
308 }
309
310 ufds = malloc(sizeof(struct pollfd) * count);
311 if (ufds == NULL) {
312 printf("No enough memory\n");
313 return -ENOMEM;
314 }
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));
317 return err;
318 }
319
320 init = 1;
321 while (1) {
322 if (!init) {
323 err = wait_for_poll(handle, ufds, count);
324 if (err < 0) {
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));
330 exit(EXIT_FAILURE);
331 }
332 init = 1;
333 } else {
334 printf("Wait for poll failed\n");
335 return err;
336 }
337 }
338 }
339
340 generate_sine(areas, 0, period_size, &phase);
341 ptr = samples;
342 cptr = period_size;
343 while (cptr > 0) {
344 err = snd_pcm_writei(handle, ptr, cptr);
345 if (err < 0) {
346 if (xrun_recovery(handle, err) < 0) {
347 printf("Write error: %s\n", snd_strerror(err));
348 exit(EXIT_FAILURE);
349 }
350 init = 1;
351 break; /* skip one period */
352 }
353 if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING)
354 init = 0;
355 ptr += err * channels;
356 cptr -= err;
357 if (cptr == 0)
358 break;
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);
362 if (err < 0) {
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));
368 exit(EXIT_FAILURE);
369 }
370 init = 1;
371 } else {
372 printf("Wait for poll failed\n");
373 return err;
374 }
375 }
376 }
377 }
378 }
379
380 /*
381 * Transfer method - asynchronous notification
382 */
383
384 struct async_private_data {
385 signed short *samples;
386 snd_pcm_channel_area_t *areas;
387 double phase;
388 };
389
async_callback(snd_async_handler_t * ahandler)390 static void async_callback(snd_async_handler_t *ahandler)
391 {
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;
397 int err;
398
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);
403 if (err < 0) {
404 printf("Write error: %s\n", snd_strerror(err));
405 exit(EXIT_FAILURE);
406 }
407 if (err != period_size) {
408 printf("Write error: written %i expected %li\n", err, period_size);
409 exit(EXIT_FAILURE);
410 }
411 avail = snd_pcm_avail_update(handle);
412 }
413 }
414
async_loop(snd_pcm_t * handle,signed short * samples,snd_pcm_channel_area_t * areas)415 static int async_loop(snd_pcm_t *handle,
416 signed short *samples,
417 snd_pcm_channel_area_t *areas)
418 {
419 struct async_private_data data;
420 snd_async_handler_t *ahandler;
421 int err, count;
422
423 data.samples = samples;
424 data.areas = areas;
425 data.phase = 0;
426 err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, &data);
427 if (err < 0) {
428 printf("Unable to register async handler\n");
429 exit(EXIT_FAILURE);
430 }
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);
434 if (err < 0) {
435 printf("Initial write error: %s\n", snd_strerror(err));
436 exit(EXIT_FAILURE);
437 }
438 if (err != period_size) {
439 printf("Initial write error: written %i expected %li\n", err, period_size);
440 exit(EXIT_FAILURE);
441 }
442 }
443 if (snd_pcm_state(handle) == SND_PCM_STATE_PREPARED) {
444 err = snd_pcm_start(handle);
445 if (err < 0) {
446 printf("Start error: %s\n", snd_strerror(err));
447 exit(EXIT_FAILURE);
448 }
449 }
450
451 /* because all other work is done in the signal handler,
452 suspend the process */
453 while (1) {
454 sleep(1);
455 }
456 }
457
458 /*
459 * Transfer method - asynchronous notification + direct write
460 */
461
async_direct_callback(snd_async_handler_t * ahandler)462 static void async_direct_callback(snd_async_handler_t *ahandler)
463 {
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;
470 int first = 0, err;
471
472 while (1) {
473 state = snd_pcm_state(handle);
474 if (state == SND_PCM_STATE_XRUN) {
475 err = xrun_recovery(handle, -EPIPE);
476 if (err < 0) {
477 printf("XRUN recovery failed: %s\n", snd_strerror(err));
478 exit(EXIT_FAILURE);
479 }
480 first = 1;
481 } else if (state == SND_PCM_STATE_SUSPENDED) {
482 err = xrun_recovery(handle, -ESTRPIPE);
483 if (err < 0) {
484 printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
485 exit(EXIT_FAILURE);
486 }
487 }
488 avail = snd_pcm_avail_update(handle);
489 if (avail < 0) {
490 err = xrun_recovery(handle, avail);
491 if (err < 0) {
492 printf("avail update failed: %s\n", snd_strerror(err));
493 exit(EXIT_FAILURE);
494 }
495 first = 1;
496 continue;
497 }
498 if (avail < period_size) {
499 if (first) {
500 first = 0;
501 err = snd_pcm_start(handle);
502 if (err < 0) {
503 printf("Start error: %s\n", snd_strerror(err));
504 exit(EXIT_FAILURE);
505 }
506 } else {
507 break;
508 }
509 continue;
510 }
511 size = period_size;
512 while (size > 0) {
513 frames = size;
514 err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
515 if (err < 0) {
516 if ((err = xrun_recovery(handle, err)) < 0) {
517 printf("MMAP begin avail error: %s\n", snd_strerror(err));
518 exit(EXIT_FAILURE);
519 }
520 first = 1;
521 }
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));
527 exit(EXIT_FAILURE);
528 }
529 first = 1;
530 }
531 size -= frames;
532 }
533 }
534 }
535
async_direct_loop(snd_pcm_t * handle,signed short * samples ATTRIBUTE_UNUSED,snd_pcm_channel_area_t * areas ATTRIBUTE_UNUSED)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)
539 {
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;
545 int err, count;
546
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 */
549 data.phase = 0;
550 err = snd_async_add_pcm_handler(&ahandler, handle, async_direct_callback, &data);
551 if (err < 0) {
552 printf("Unable to register async handler\n");
553 exit(EXIT_FAILURE);
554 }
555 for (count = 0; count < 2; count++) {
556 size = period_size;
557 while (size > 0) {
558 frames = size;
559 err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
560 if (err < 0) {
561 if ((err = xrun_recovery(handle, err)) < 0) {
562 printf("MMAP begin avail error: %s\n", snd_strerror(err));
563 exit(EXIT_FAILURE);
564 }
565 }
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));
571 exit(EXIT_FAILURE);
572 }
573 }
574 size -= frames;
575 }
576 }
577 err = snd_pcm_start(handle);
578 if (err < 0) {
579 printf("Start error: %s\n", snd_strerror(err));
580 exit(EXIT_FAILURE);
581 }
582
583 /* because all other work is done in the signal handler,
584 suspend the process */
585 while (1) {
586 sleep(1);
587 }
588 }
589
590 /*
591 * Transfer method - direct write only
592 */
593
direct_loop(snd_pcm_t * handle,signed short * samples ATTRIBUTE_UNUSED,snd_pcm_channel_area_t * areas ATTRIBUTE_UNUSED)594 static int direct_loop(snd_pcm_t *handle,
595 signed short *samples ATTRIBUTE_UNUSED,
596 snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED)
597 {
598 double phase = 0;
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;
603 int err, first = 1;
604
605 while (1) {
606 state = snd_pcm_state(handle);
607 if (state == SND_PCM_STATE_XRUN) {
608 err = xrun_recovery(handle, -EPIPE);
609 if (err < 0) {
610 printf("XRUN recovery failed: %s\n", snd_strerror(err));
611 return err;
612 }
613 first = 1;
614 } else if (state == SND_PCM_STATE_SUSPENDED) {
615 err = xrun_recovery(handle, -ESTRPIPE);
616 if (err < 0) {
617 printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
618 return err;
619 }
620 }
621 avail = snd_pcm_avail_update(handle);
622 if (avail < 0) {
623 err = xrun_recovery(handle, avail);
624 if (err < 0) {
625 printf("avail update failed: %s\n", snd_strerror(err));
626 return err;
627 }
628 first = 1;
629 continue;
630 }
631 if (avail < period_size) {
632 if (first) {
633 first = 0;
634 err = snd_pcm_start(handle);
635 if (err < 0) {
636 printf("Start error: %s\n", snd_strerror(err));
637 exit(EXIT_FAILURE);
638 }
639 } else {
640 err = snd_pcm_wait(handle, -1);
641 if (err < 0) {
642 if ((err = xrun_recovery(handle, err)) < 0) {
643 printf("snd_pcm_wait error: %s\n", snd_strerror(err));
644 exit(EXIT_FAILURE);
645 }
646 first = 1;
647 }
648 }
649 continue;
650 }
651 size = period_size;
652 while (size > 0) {
653 frames = size;
654 err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
655 if (err < 0) {
656 if ((err = xrun_recovery(handle, err)) < 0) {
657 printf("MMAP begin avail error: %s\n", snd_strerror(err));
658 exit(EXIT_FAILURE);
659 }
660 first = 1;
661 }
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));
667 exit(EXIT_FAILURE);
668 }
669 first = 1;
670 }
671 size -= frames;
672 }
673 }
674 }
675
676 /*
677 * Transfer method - direct write only using mmap_write functions
678 */
679
direct_write_loop(snd_pcm_t * handle,signed short * samples,snd_pcm_channel_area_t * areas)680 static int direct_write_loop(snd_pcm_t *handle,
681 signed short *samples,
682 snd_pcm_channel_area_t *areas)
683 {
684 double phase = 0;
685 signed short *ptr;
686 int err, cptr;
687
688 while (1) {
689 generate_sine(areas, 0, period_size, &phase);
690 ptr = samples;
691 cptr = period_size;
692 while (cptr > 0) {
693 err = snd_pcm_mmap_writei(handle, ptr, cptr);
694 if (err == -EAGAIN)
695 continue;
696 if (err < 0) {
697 if (xrun_recovery(handle, err) < 0) {
698 printf("Write error: %s\n", snd_strerror(err));
699 exit(EXIT_FAILURE);
700 }
701 break; /* skip one period */
702 }
703 ptr += err * channels;
704 cptr -= err;
705 }
706 }
707 }
708
709 /*
710 *
711 */
712
713 struct transfer_method {
714 const char *name;
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);
719 };
720
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 }
730 };
731
help(void)732 static void help(void)
733 {
734 int k;
735 printf(
736 "Usage: pcm [OPTION]... [FILE]...\n"
737 "-h,--help help\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"
749 "\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);
753 if (s)
754 printf(" %s", s);
755 }
756 printf("\n");
757 printf("Recognized transfer methods are:");
758 for (k = 0; transfer_methods[k].name; k++)
759 printf(" %s", transfer_methods[k].name);
760 printf("\n");
761 }
762
main(int argc,char * argv[])763 int main(int argc, char *argv[])
764 {
765 struct option long_option[] =
766 {
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'},
779 {NULL, 0, NULL, 0},
780 };
781 snd_pcm_t *handle;
782 int err, morehelp;
783 snd_pcm_hw_params_t *hwparams;
784 snd_pcm_sw_params_t *swparams;
785 int method = 0;
786 signed short *samples;
787 unsigned int chn;
788 snd_pcm_channel_area_t *areas;
789
790 snd_pcm_hw_params_alloca(&hwparams);
791 snd_pcm_sw_params_alloca(&swparams);
792
793 morehelp = 0;
794 while (1) {
795 int c;
796 if ((c = getopt_long(argc, argv, "hD:r:c:f:b:p:m:o:vne", long_option, NULL)) < 0)
797 break;
798 switch (c) {
799 case 'h':
800 morehelp++;
801 break;
802 case 'D':
803 device = strdup(optarg);
804 break;
805 case 'r':
806 rate = atoi(optarg);
807 rate = rate < 4000 ? 4000 : rate;
808 rate = rate > 196000 ? 196000 : rate;
809 break;
810 case 'c':
811 channels = atoi(optarg);
812 channels = channels < 1 ? 1 : channels;
813 channels = channels > 1024 ? 1024 : channels;
814 break;
815 case 'f':
816 freq = atoi(optarg);
817 freq = freq < 50 ? 50 : freq;
818 freq = freq > 5000 ? 5000 : freq;
819 break;
820 case 'b':
821 buffer_time = atoi(optarg);
822 buffer_time = buffer_time < 1000 ? 1000 : buffer_time;
823 buffer_time = buffer_time > 1000000 ? 1000000 : buffer_time;
824 break;
825 case 'p':
826 period_time = atoi(optarg);
827 period_time = period_time < 1000 ? 1000 : period_time;
828 period_time = period_time > 1000000 ? 1000000 : period_time;
829 break;
830 case 'm':
831 for (method = 0; transfer_methods[method].name; method++)
832 if (!strcasecmp(transfer_methods[method].name, optarg))
833 break;
834 if (transfer_methods[method].name == NULL)
835 method = 0;
836 break;
837 case 'o':
838 for (format = 0; format < SND_PCM_FORMAT_LAST; format++) {
839 const char *format_name = snd_pcm_format_name(format);
840 if (format_name)
841 if (!strcasecmp(format_name, optarg))
842 break;
843 }
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",
850 optarg);
851 return 1;
852 }
853 break;
854 case 'v':
855 verbose = 1;
856 break;
857 case 'n':
858 resample = 0;
859 break;
860 case 'e':
861 period_event = 1;
862 break;
863 }
864 }
865
866 if (morehelp) {
867 help();
868 return 0;
869 }
870
871 err = snd_output_stdio_attach(&output, stdout, 0);
872 if (err < 0) {
873 printf("Output failed: %s\n", snd_strerror(err));
874 return 0;
875 }
876
877 printf("Playback device is %s\n", device);
878 printf("Stream parameters are %uHz, %s, %u 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);
881
882 if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
883 printf("Playback open error: %s\n", snd_strerror(err));
884 return 0;
885 }
886
887 if ((err = set_hwparams(handle, hwparams, transfer_methods[method].access)) < 0) {
888 printf("Setting of hwparams failed: %s\n", snd_strerror(err));
889 exit(EXIT_FAILURE);
890 }
891 if ((err = set_swparams(handle, swparams)) < 0) {
892 printf("Setting of swparams failed: %s\n", snd_strerror(err));
893 exit(EXIT_FAILURE);
894 }
895
896 if (verbose > 0)
897 snd_pcm_dump(handle, output);
898
899 samples = malloc((period_size * channels * snd_pcm_format_physical_width(format)) / 8);
900 if (samples == NULL) {
901 printf("No enough memory\n");
902 exit(EXIT_FAILURE);
903 }
904
905 areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
906 if (areas == NULL) {
907 printf("No enough memory\n");
908 exit(EXIT_FAILURE);
909 }
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);
914 }
915
916 err = transfer_methods[method].transfer_loop(handle, samples, areas);
917 if (err < 0)
918 printf("Transfer failed: %s\n", snd_strerror(err));
919
920 free(areas);
921 free(samples);
922 snd_pcm_close(handle);
923 return 0;
924 }
925
926