OSDN Git Service

Corrections by Kevin Conder <kconder@interaccess.com>
[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 char *device = "plughw:0,0";                     /* playback device */
16 snd_pcm_format_t format = SND_PCM_FORMAT_S16;    /* sample format */
17 int rate = 44100;                                /* stream rate */
18 int channels = 1;                                /* count of channels */
19 int buffer_time = 500000;                        /* ring buffer length in us */
20 int period_time = 100000;                        /* period time in us */
21 double freq = 440;                               /* sinusoidal wave frequency in Hz */
22
23 snd_pcm_sframes_t buffer_size;
24 snd_pcm_sframes_t period_size;
25 snd_output_t *output = NULL;
26
27 static void generate_sine(const snd_pcm_channel_area_t *areas, 
28                           snd_pcm_uframes_t offset,
29                           int count, double *_phase)
30 {
31         double phase = *_phase;
32         double max_phase = 1.0 / freq;
33         double step = 1.0 / (double)rate;
34         double res;
35         signed short *samples[channels];
36         int steps[channels];
37         int chn, ires;
38         
39         /* verify and prepare the contents of areas */
40         for (chn = 0; chn < channels; chn++) {
41                 if ((areas[chn].first % 8) != 0) {
42                         printf("areas[%i].first == %i, aborting...\n", chn, areas[chn].first);
43                         exit(EXIT_FAILURE);
44                 }
45                 samples[chn] = (signed short *)(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
46                 if ((areas[chn].step % 16) != 0) {
47                         printf("areas[%i].step == %i, aborting...\n", chn, areas[chn].step);
48                         exit(EXIT_FAILURE);
49                 }
50                 steps[chn] = areas[chn].step / 16;
51                 samples[chn] += offset * steps[chn];
52         }
53         /* fill the channel areas */
54         while (count-- > 0) {
55                 res = sin((phase * 2 * M_PI) / max_phase - M_PI) * 32767;
56                 ires = res;
57                 for (chn = 0; chn < channels; chn++) {
58                         *samples[chn] = ires;
59                         samples[chn] += steps[chn];
60                 }
61                 phase += step;
62                 if (phase >= max_phase)
63                         phase -= max_phase;
64         }
65         *_phase = phase;
66 }
67
68 static int set_hwparams(snd_pcm_t *handle,
69                         snd_pcm_hw_params_t *params,
70                         snd_pcm_access_t access)
71 {
72         int err, dir;
73
74         /* choose all parameters */
75         err = snd_pcm_hw_params_any(handle, params);
76         if (err < 0) {
77                 printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
78                 return err;
79         }
80         /* set the interleaved read/write format */
81         err = snd_pcm_hw_params_set_access(handle, params, access);
82         if (err < 0) {
83                 printf("Access type not available for playback: %s\n", snd_strerror(err));
84                 return err;
85         }
86         /* set the sample format */
87         err = snd_pcm_hw_params_set_format(handle, params, format);
88         if (err < 0) {
89                 printf("Sample format not available for playback: %s\n", snd_strerror(err));
90                 return err;
91         }
92         /* set the count of channels */
93         err = snd_pcm_hw_params_set_channels(handle, params, channels);
94         if (err < 0) {
95                 printf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
96                 return err;
97         }
98         /* set the stream rate */
99         err = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0);
100         if (err < 0) {
101                 printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
102                 return err;
103         }
104         if (err != rate) {
105                 printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
106                 return -EINVAL;
107         }
108         /* set the buffer time */
109         err = snd_pcm_hw_params_set_buffer_time_near(handle, params, buffer_time, &dir);
110         if (err < 0) {
111                 printf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
112                 return err;
113         }
114         buffer_size = snd_pcm_hw_params_get_buffer_size(params);
115         /* set the period time */
116         err = snd_pcm_hw_params_set_period_time_near(handle, params, period_time, &dir);
117         if (err < 0) {
118                 printf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
119                 return err;
120         }
121         period_size = snd_pcm_hw_params_get_period_size(params, &dir);
122         /* write the parameters to device */
123         err = snd_pcm_hw_params(handle, params);
124         if (err < 0) {
125                 printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
126                 return err;
127         }
128         return 0;
129 }
130
131 static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
132 {
133         int err;
134
135         /* get the current swparams */
136         err = snd_pcm_sw_params_current(handle, swparams);
137         if (err < 0) {
138                 printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
139                 return err;
140         }
141         /* start the transfer when the buffer is full */
142         err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size);
143         if (err < 0) {
144                 printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
145                 return err;
146         }
147         /* allow the transfer when at least period_size samples can be processed */
148         err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size);
149         if (err < 0) {
150                 printf("Unable to set avail min for playback: %s\n", snd_strerror(err));
151                 return err;
152         }
153         /* align all transfers to 1 sample */
154         err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1);
155         if (err < 0) {
156                 printf("Unable to set transfer align for playback: %s\n", snd_strerror(err));
157                 return err;
158         }
159         /* write the parameters to the playback device */
160         err = snd_pcm_sw_params(handle, swparams);
161         if (err < 0) {
162                 printf("Unable to set sw params for playback: %s\n", snd_strerror(err));
163                 return err;
164         }
165         return 0;
166 }
167
168 /*
169  *   Underrun and suspend recovery
170  */
171  
172 static int xrun_recovery(snd_pcm_t *handle, int err)
173 {
174         if (err == -EPIPE) {    /* under-run */
175                 err = snd_pcm_prepare(handle);
176                 if (err < 0)
177                         printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
178                 return 0;
179         } else if (err == -ESTRPIPE) {
180                 while ((err = snd_pcm_resume(handle)) == -EAGAIN)
181                         sleep(1);       /* wait until the suspend flag is released */
182                 if (err < 0) {
183                         err = snd_pcm_prepare(handle);
184                         if (err < 0)
185                                 printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
186                 }
187                 return 0;
188         }
189         return err;
190 }
191
192 /*
193  *   Transfer method - write only
194  */
195
196 static int write_loop(snd_pcm_t *handle,
197                       signed short *samples,
198                       snd_pcm_channel_area_t *areas)
199 {
200         double phase = 0;
201         signed short *ptr;
202         int err, cptr;
203
204         while (1) {
205                 generate_sine(areas, 0, period_size, &phase);
206                 ptr = samples;
207                 cptr = period_size;
208                 while (cptr > 0) {
209                         err = snd_pcm_writei(handle, ptr, cptr);
210                         if (err == -EAGAIN)
211                                 continue;
212                         if (err < 0) {
213                                 if (xrun_recovery(handle, err) < 0) {
214                                         printf("Write error: %s\n", snd_strerror(err));
215                                         exit(EXIT_FAILURE);
216                                 }
217                                 break;  /* skip one period */
218                         }
219                         ptr += err * channels;
220                         cptr -= err;
221                 }
222         }
223 }
224  
225 /*
226  *   Transfer method - write and wait for room in buffer using poll
227  */
228
229 static int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count)
230 {
231         unsigned short revents;
232
233         while (1) {
234                 poll(ufds, count, -1);
235                 snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);
236                 if (revents & POLLERR)
237                         return -EIO;
238                 if (revents & POLLOUT)
239                         return 0;
240         }
241 }
242
243 static int write_and_poll_loop(snd_pcm_t *handle,
244                                signed short *samples,
245                                snd_pcm_channel_area_t *areas)
246 {
247         struct pollfd *ufds;
248         double phase = 0;
249         signed short *ptr;
250         int err, count, cptr, init;
251
252         count = snd_pcm_poll_descriptors_count (handle);
253         if (count <= 0) {
254                 printf("Invalid poll descriptors count\n");
255                 return count;
256         }
257
258         ufds = malloc(sizeof(struct pollfd) * count);
259         if (ufds == NULL) {
260                 printf("No enough memory\n");
261                 return err;;
262         }
263         if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) {
264                 printf("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
265                 return err;
266         }
267
268         init = 1;
269         while (1) {
270                 if (!init) {
271                         err = wait_for_poll(handle, ufds, count);
272                         if (err < 0) {
273                                 if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN ||
274                                     snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) {
275                                         err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
276                                         if (xrun_recovery(handle, err) < 0) {
277                                                 printf("Write error: %s\n", snd_strerror(err));
278                                                 exit(EXIT_FAILURE);
279                                         }
280                                         init = 1;
281                                 } else {
282                                         printf("Wait for poll failed\n");
283                                         return err;
284                                 }
285                         }
286                 }
287
288                 generate_sine(areas, 0, period_size, &phase);
289                 ptr = samples;
290                 cptr = period_size;
291                 while (cptr > 0) {
292                         err = snd_pcm_writei(handle, ptr, cptr);
293                         if (err < 0) {
294                                 if (xrun_recovery(handle, err) < 0) {
295                                         printf("Write error: %s\n", snd_strerror(err));
296                                         exit(EXIT_FAILURE);
297                                 }
298                                 init = 1;
299                                 break;  /* skip one period */
300                         }
301                         if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING)
302                                 init = 0;
303                         ptr += err * channels;
304                         cptr -= err;
305                         if (cptr == 0)
306                                 break;
307                         /* it is possible, that the initial buffer cannot store */
308                         /* all data from the last period, so wait awhile */
309                         err = wait_for_poll(handle, ufds, count);
310                         if (err < 0) {
311                                 if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN ||
312                                     snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) {
313                                         err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
314                                         if (xrun_recovery(handle, err) < 0) {
315                                                 printf("Write error: %s\n", snd_strerror(err));
316                                                 exit(EXIT_FAILURE);
317                                         }
318                                         init = 1;
319                                 } else {
320                                         printf("Wait for poll failed\n");
321                                         return err;
322                                 }
323                         }
324                 }
325         }
326 }
327
328 /*
329  *   Transfer method - asynchronous notification
330  */
331
332 struct async_private_data {
333         signed short *samples;
334         snd_pcm_channel_area_t *areas;
335         double phase;
336 };
337
338 static void async_callback(snd_async_handler_t *ahandler)
339 {
340         snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler);
341         struct async_private_data *data = snd_async_handler_get_callback_private(ahandler);
342         signed short *samples = data->samples;
343         snd_pcm_channel_area_t *areas = data->areas;
344         snd_pcm_sframes_t avail;
345         int err;
346         
347         avail = snd_pcm_avail_update(handle);
348         while (avail >= period_size) {
349                 generate_sine(areas, 0, period_size, &data->phase);
350                 err = snd_pcm_writei(handle, samples, period_size);
351                 if (err < 0) {
352                         printf("Initial write error: %s\n", snd_strerror(err));
353                         exit(EXIT_FAILURE);
354                 }
355                 if (err != period_size) {
356                         printf("Initial write error: written %i expected %li\n", err, period_size);
357                         exit(EXIT_FAILURE);
358                 }
359                 avail = snd_pcm_avail_update(handle);
360         }
361 }
362
363 static int async_loop(snd_pcm_t *handle,
364                       signed short *samples,
365                       snd_pcm_channel_area_t *areas)
366 {
367         struct async_private_data data;
368         snd_async_handler_t *ahandler;
369         int err, count;
370
371         data.samples = samples;
372         data.areas = areas;
373         data.phase = 0;
374         err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, &data);
375         if (err < 0) {
376                 printf("Unable to register async handler\n");
377                 exit(EXIT_FAILURE);
378         }
379         for (count = 0; count < 2; count++) {
380                 generate_sine(areas, 0, period_size, &data.phase);
381                 err = snd_pcm_writei(handle, samples, period_size);
382                 if (err < 0) {
383                         printf("Initial write error: %s\n", snd_strerror(err));
384                         exit(EXIT_FAILURE);
385                 }
386                 if (err != period_size) {
387                         printf("Initial write error: written %i expected %li\n", err, period_size);
388                         exit(EXIT_FAILURE);
389                 }
390         }
391         err = snd_pcm_start(handle);
392         if (err < 0) {
393                 printf("Start error: %s\n", snd_strerror(err));
394                 exit(EXIT_FAILURE);
395         }
396
397         /* because all other work is done in the signal handler,
398            suspend the process */
399         while (1) {
400                 sleep(1);
401         }
402 }
403
404 /*
405  *   Transfer method - direct write only
406  */
407
408 static int direct_loop(snd_pcm_t *handle,
409                        signed short *samples,
410                        snd_pcm_channel_area_t *areas)
411 {
412         double phase = 0;
413         const snd_pcm_channel_area_t *my_areas;
414         snd_pcm_uframes_t offset, frames, size;
415         snd_pcm_sframes_t avail, commitres;
416         snd_pcm_state_t state;
417         int err, first = 1;
418
419         while (1) {
420                 state = snd_pcm_state(handle);
421                 if (state == SND_PCM_STATE_XRUN) {
422                         err = xrun_recovery(handle, -EPIPE);
423                         if (err < 0) {
424                                 printf("XRUN recovery failed: %s\n", snd_strerror(err));
425                                 return err;
426                         }
427                         first = 1;
428                 } else if (state == SND_PCM_STATE_SUSPENDED) {
429                         err = xrun_recovery(handle, -ESTRPIPE);
430                         if (err < 0) {
431                                 printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
432                                 return err;
433                         }
434                 }
435                 avail = snd_pcm_avail_update(handle);
436                 if (avail < 0) {
437                         err = xrun_recovery(handle, avail);
438                         if (err < 0) {
439                                 printf("avail update failed: %s\n", snd_strerror(err));
440                                 return err;
441                         }
442                         first = 1;
443                         continue;
444                 }
445                 if (avail < period_size) {
446                         if (first) {
447                                 first = 0;
448                                 err = snd_pcm_start(handle);
449                                 if (err < 0) {
450                                         printf("Start error: %s\n", snd_strerror(err));
451                                         exit(EXIT_FAILURE);
452                                 }
453                         } else {
454                                 err = snd_pcm_wait(handle, -1);
455                                 if (err < 0) {
456                                         if ((err = xrun_recovery(handle, err)) < 0) {
457                                                 printf("snd_pcm_wait error: %s\n", snd_strerror(err));
458                                                 exit(EXIT_FAILURE);
459                                         }
460                                         first = 1;
461                                 }
462                         }
463                         continue;
464                 }
465                 size = period_size;
466                 while (size > 0) {
467                         frames = size;
468                         err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
469                         if (err < 0) {
470                                 if ((err = xrun_recovery(handle, err)) < 0) {
471                                         printf("MMAP begin avail error: %s\n", snd_strerror(err));
472                                         exit(EXIT_FAILURE);
473                                 }
474                                 first = 1;
475                         }
476                         generate_sine(my_areas, offset, frames, &phase);
477                         commitres = snd_pcm_mmap_commit(handle, offset, frames);
478                         if (commitres < 0 || commitres != frames) {
479                                 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
480                                         printf("MMAP commit error: %s\n", snd_strerror(err));
481                                         exit(EXIT_FAILURE);
482                                 }
483                                 first = 1;
484                         }
485                         size -= frames;
486                 }
487         }
488 }
489  
490 /*
491  *   Transfer method - direct write only using mmap_write functions
492  */
493
494 static int direct_write_loop(snd_pcm_t *handle,
495                              signed short *samples,
496                              snd_pcm_channel_area_t *areas)
497 {
498         double phase = 0;
499         signed short *ptr;
500         int err, cptr;
501
502         while (1) {
503                 generate_sine(areas, 0, period_size, &phase);
504                 ptr = samples;
505                 cptr = period_size;
506                 while (cptr > 0) {
507                         err = snd_pcm_mmap_writei(handle, ptr, cptr);
508                         if (err == -EAGAIN)
509                                 continue;
510                         if (err < 0) {
511                                 if (xrun_recovery(handle, err) < 0) {
512                                         printf("Write error: %s\n", snd_strerror(err));
513                                         exit(EXIT_FAILURE);
514                                 }
515                                 break;  /* skip one period */
516                         }
517                         ptr += err * channels;
518                         cptr -= err;
519                 }
520         }
521 }
522  
523 /*
524  *
525  */
526
527 struct transfer_method {
528         const char *name;
529         snd_pcm_access_t access;
530         int (*transfer_loop)(snd_pcm_t *handle,
531                              signed short *samples,
532                              snd_pcm_channel_area_t *areas);
533 };
534
535 static struct transfer_method transfer_methods[] = {
536         { "write", SND_PCM_ACCESS_RW_INTERLEAVED, write_loop },
537         { "write_and_poll", SND_PCM_ACCESS_RW_INTERLEAVED, write_and_poll_loop },
538         { "async", SND_PCM_ACCESS_RW_INTERLEAVED, async_loop },
539         { "direct_interleaved", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_loop },
540         { "direct_noninterleaved", SND_PCM_ACCESS_MMAP_NONINTERLEAVED, direct_loop },
541         { "direct_write", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_write_loop },
542         { NULL, SND_PCM_ACCESS_RW_INTERLEAVED, NULL }
543 };
544
545 static void help(void)
546 {
547         int k;
548         printf("\
549 Usage: latency [OPTION]... [FILE]...
550 -h,--help       help
551 -D,--device     playback device
552 -r,--rate       stream rate in Hz
553 -c,--channels   count of channels in stream
554 -f,--frequency  sine wave frequency in Hz
555 -b,--buffer     ring buffer size in samples
556 -p,--period     period size in us
557 -m,--method     transfer method
558
559 ");
560         printf("Recognized sample formats are:");
561         for (k = 0; k < SND_PCM_FORMAT_LAST; ++(unsigned long) k) {
562                 const char *s = snd_pcm_format_name(k);
563                 if (s)
564                         printf(" %s", s);
565         }
566         printf("\n");
567         printf("Recognized transfer methods are:");
568         for (k = 0; transfer_methods[k].name; k++)
569                 printf(" %s", transfer_methods[k].name);
570         printf("\n");
571 }
572
573 int main(int argc, char *argv[])
574 {
575         struct option long_option[] =
576         {
577                 {"help", 0, NULL, 'h'},
578                 {"device", 1, NULL, 'D'},
579                 {"rate", 1, NULL, 'r'},
580                 {"channels", 1, NULL, 'c'},
581                 {"frequency", 1, NULL, 'f'},
582                 {"buffer", 1, NULL, 'b'},
583                 {"period", 1, NULL, 'p'},
584                 {"method", 1, NULL, 'm'},
585                 {NULL, 0, NULL, 0},
586         };
587         snd_pcm_t *handle;
588         int err, morehelp;
589         snd_pcm_hw_params_t *hwparams;
590         snd_pcm_sw_params_t *swparams;
591         int method = 0;
592         signed short *samples;
593         int chn;
594         snd_pcm_channel_area_t *areas;
595
596         snd_pcm_hw_params_alloca(&hwparams);
597         snd_pcm_sw_params_alloca(&swparams);
598
599         morehelp = 0;
600         while (1) {
601                 int c;
602                 if ((c = getopt_long(argc, argv, "hD:r:c:f:b:p:m:", long_option, NULL)) < 0)
603                         break;
604                 switch (c) {
605                 case 'h':
606                         morehelp++;
607                         break;
608                 case 'D':
609                         device = strdup(optarg);
610                         break;
611                 case 'r':
612                         rate = atoi(optarg);
613                         rate = rate < 4000 ? 4000 : rate;
614                         rate = rate > 196000 ? 196000 : rate;
615                         break;
616                 case 'c':
617                         channels = atoi(optarg);
618                         channels = channels < 1 ? 1 : channels;
619                         channels = channels > 1024 ? 1024 : channels;
620                         break;
621                 case 'f':
622                         freq = atoi(optarg);
623                         freq = freq < 50 ? 50 : freq;
624                         freq = freq > 5000 ? 5000 : freq;
625                         break;
626                 case 'b':
627                         buffer_size = atoi(optarg);
628                         buffer_size = buffer_size < 64 ? 64 : buffer_size;
629                         buffer_size = buffer_size > 64*1024 ? 64*1024 : buffer_size;
630                         break;
631                 case 'p':
632                         period_time = atoi(optarg);
633                         period_time = period_time < 1000 ? 1000 : period_time;
634                         period_time = period_time > 1000000 ? 1000000 : period_time;
635                         break;
636                 case 'm':
637                         for (method = 0; transfer_methods[method].name; method++)
638                                 if (!strcasecmp(transfer_methods[method].name, optarg))
639                                         break;
640                         if (transfer_methods[method].name == NULL)
641                                 method = 0;
642                         break;
643                 }
644         }
645
646         if (morehelp) {
647                 help();
648                 return 0;
649         }
650
651         err = snd_output_stdio_attach(&output, stdout, 0);
652         if (err < 0) {
653                 printf("Output failed: %s\n", snd_strerror(err));
654                 return 0;
655         }
656
657         printf("Playback device is %s\n", device);
658         printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels);
659         printf("Sine wave rate is %.4fHz\n", freq);
660         printf("Using transfer method: %s\n", transfer_methods[method].name);
661
662         if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
663                 printf("Playback open error: %s\n", snd_strerror(err));
664                 return 0;
665         }
666         
667         if ((err = set_hwparams(handle, hwparams, transfer_methods[method].access)) < 0) {
668                 printf("Setting of hwparams failed: %s\n", snd_strerror(err));
669                 exit(EXIT_FAILURE);
670         }
671         if ((err = set_swparams(handle, swparams)) < 0) {
672                 printf("Setting of swparams failed: %s\n", snd_strerror(err));
673                 exit(EXIT_FAILURE);
674         }
675
676         samples = malloc((period_size * channels * snd_pcm_format_width(format)) / 8);
677         if (samples == NULL) {
678                 printf("No enough memory\n");
679                 exit(EXIT_FAILURE);
680         }
681         
682         areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
683         if (areas == NULL) {
684                 printf("No enough memory\n");
685                 exit(EXIT_FAILURE);
686         }
687         for (chn = 0; chn < channels; chn++) {
688                 areas[chn].addr = samples;
689                 areas[chn].first = chn * 16;
690                 areas[chn].step = channels * 16;
691         }
692
693         err = transfer_methods[method].transfer_loop(handle, samples, areas);
694         if (err < 0)
695                 printf("Transfer failed: %s\n", snd_strerror(err));
696
697         free(areas);
698         free(samples);
699         snd_pcm_close(handle);
700         return 0;
701 }
702