Subversion Repositories svnkaklik

Rev

Rev 654 | Details | Compare with Previous | Last modification | View Log

Rev Author Line No. Line
654 kaklik 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>
662 kaklik 11
#include <alsa/asoundlib.h>
654 kaklik 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
 
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[%i].first == %i, 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[%i].step == %i, 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) * maxval;
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
 
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 (%i) 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 %iHz 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 %iHz, 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 %i 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 %i 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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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 %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels);
879
	printf("Sine wave rate is %.4fHz\n", freq);
880
	printf("Using transfer method: %s\n", transfer_methods[method].name);
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