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