• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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