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