OSDN Git Service

Android: add makefile and config.h
[android-x86/external-alsa-lib.git] / test / pcm.c
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
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
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
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 %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