/test/pcm.c

00001 /*
00002  *  This small demo sends a simple sinusoidal wave to your speakers.
00003  */
00004 
00005 #include <stdio.h>
00006 #include <stdlib.h>
00007 #include <string.h>
00008 #include <sched.h>
00009 #include <errno.h>
00010 #include <getopt.h>
00011 #include "../include/asoundlib.h"
00012 #include <sys/time.h>
00013 #include <math.h>
00014 
00015 static char *device = "plughw:0,0";                     /* playback device */
00016 static snd_pcm_format_t format = SND_PCM_FORMAT_S16;    /* sample format */
00017 static unsigned int rate = 44100;                       /* stream rate */
00018 static unsigned int channels = 1;                       /* count of channels */
00019 static unsigned int buffer_time = 500000;               /* ring buffer length in us */
00020 static unsigned int period_time = 100000;               /* period time in us */
00021 static double freq = 440;                               /* sinusoidal wave frequency in Hz */
00022 static int verbose = 0;                                 /* verbose flag */
00023 static int resample = 1;                                /* enable alsa-lib resampling */
00024 static int period_event = 0;                            /* produce poll event after each period */
00025 
00026 static snd_pcm_sframes_t buffer_size;
00027 static snd_pcm_sframes_t period_size;
00028 static snd_output_t *output = NULL;
00029 
00030 static void generate_sine(const snd_pcm_channel_area_t *areas, 
00031                           snd_pcm_uframes_t offset,
00032                           int count, double *_phase)
00033 {
00034         static double max_phase = 2. * M_PI;
00035         double phase = *_phase;
00036         double step = max_phase*freq/(double)rate;
00037         double res;
00038         unsigned char *samples[channels], *tmp;
00039         int steps[channels];
00040         unsigned int chn, byte;
00041         union {
00042                 int i;
00043                 unsigned char c[4];
00044         } ires;
00045         unsigned int maxval = (1 << (snd_pcm_format_width(format) - 1)) - 1;
00046         int bps = snd_pcm_format_width(format) / 8;  /* bytes per sample */
00047         
00048         /* verify and prepare the contents of areas */
00049         for (chn = 0; chn < channels; chn++) {
00050                 if ((areas[chn].first % 8) != 0) {
00051                         printf("areas[%i].first == %i, aborting...\n", chn, areas[chn].first);
00052                         exit(EXIT_FAILURE);
00053                 }
00054                 samples[chn] = /*(signed short *)*/(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
00055                 if ((areas[chn].step % 16) != 0) {
00056                         printf("areas[%i].step == %i, aborting...\n", chn, areas[chn].step);
00057                         exit(EXIT_FAILURE);
00058                 }
00059                 steps[chn] = areas[chn].step / 8;
00060                 samples[chn] += offset * steps[chn];
00061         }
00062         /* fill the channel areas */
00063         while (count-- > 0) {
00064                 res = sin(phase) * maxval;
00065                 ires.i = res;
00066                 tmp = ires.c;
00067                 for (chn = 0; chn < channels; chn++) {
00068                         for (byte = 0; byte < (unsigned int)bps; byte++)
00069                                 *(samples[chn] + byte) = tmp[byte];
00070                         samples[chn] += steps[chn];
00071                 }
00072                 phase += step;
00073                 if (phase >= max_phase)
00074                         phase -= max_phase;
00075         }
00076         *_phase = phase;
00077 }
00078 
00079 static int set_hwparams(snd_pcm_t *handle,
00080                         snd_pcm_hw_params_t *params,
00081                         snd_pcm_access_t access)
00082 {
00083         unsigned int rrate;
00084         snd_pcm_uframes_t size;
00085         int err, dir;
00086 
00087         /* choose all parameters */
00088         err = snd_pcm_hw_params_any(handle, params);
00089         if (err < 0) {
00090                 printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
00091                 return err;
00092         }
00093         /* set hardware resampling */
00094         err = snd_pcm_hw_params_set_rate_resample(handle, params, resample);
00095         if (err < 0) {
00096                 printf("Resampling setup failed for playback: %s\n", snd_strerror(err));
00097                 return err;
00098         }
00099         /* set the interleaved read/write format */
00100         err = snd_pcm_hw_params_set_access(handle, params, access);
00101         if (err < 0) {
00102                 printf("Access type not available for playback: %s\n", snd_strerror(err));
00103                 return err;
00104         }
00105         /* set the sample format */
00106         err = snd_pcm_hw_params_set_format(handle, params, format);
00107         if (err < 0) {
00108                 printf("Sample format not available for playback: %s\n", snd_strerror(err));
00109                 return err;
00110         }
00111         /* set the count of channels */
00112         err = snd_pcm_hw_params_set_channels(handle, params, channels);
00113         if (err < 0) {
00114                 printf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
00115                 return err;
00116         }
00117         /* set the stream rate */
00118         rrate = rate;
00119         err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
00120         if (err < 0) {
00121                 printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
00122                 return err;
00123         }
00124         if (rrate != rate) {
00125                 printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
00126                 return -EINVAL;
00127         }
00128         /* set the buffer time */
00129         err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
00130         if (err < 0) {
00131                 printf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
00132                 return err;
00133         }
00134         err = snd_pcm_hw_params_get_buffer_size(params, &size);
00135         if (err < 0) {
00136                 printf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
00137                 return err;
00138         }
00139         buffer_size = size;
00140         /* set the period time */
00141         err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
00142         if (err < 0) {
00143                 printf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
00144                 return err;
00145         }
00146         err = snd_pcm_hw_params_get_period_size(params, &size, &dir);
00147         if (err < 0) {
00148                 printf("Unable to get period size for playback: %s\n", snd_strerror(err));
00149                 return err;
00150         }
00151         period_size = size;
00152         /* write the parameters to device */
00153         err = snd_pcm_hw_params(handle, params);
00154         if (err < 0) {
00155                 printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
00156                 return err;
00157         }
00158         return 0;
00159 }
00160 
00161 static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
00162 {
00163         int err;
00164 
00165         /* get the current swparams */
00166         err = snd_pcm_sw_params_current(handle, swparams);
00167         if (err < 0) {
00168                 printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
00169                 return err;
00170         }
00171         /* start the transfer when the buffer is almost full: */
00172         /* (buffer_size / avail_min) * avail_min */
00173         err = snd_pcm_sw_params_set_start_threshold(handle, swparams, (buffer_size / period_size) * period_size);
00174         if (err < 0) {
00175                 printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
00176                 return err;
00177         }
00178         /* allow the transfer when at least period_size samples can be processed */
00179         /* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
00180         err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? buffer_size : period_size);
00181         if (err < 0) {
00182                 printf("Unable to set avail min for playback: %s\n", snd_strerror(err));
00183                 return err;
00184         }
00185         /* enable period events when requested */
00186         if (period_event) {
00187                 err = snd_pcm_sw_params_set_period_event(handle, swparams, 1);
00188                 if (err < 0) {
00189                         printf("Unable to set period event: %s\n", snd_strerror(err));
00190                         return err;
00191                 }
00192         }
00193         /* write the parameters to the playback device */
00194         err = snd_pcm_sw_params(handle, swparams);
00195         if (err < 0) {
00196                 printf("Unable to set sw params for playback: %s\n", snd_strerror(err));
00197                 return err;
00198         }
00199         return 0;
00200 }
00201 
00202 /*
00203  *   Underrun and suspend recovery
00204  */
00205  
00206 static int xrun_recovery(snd_pcm_t *handle, int err)
00207 {
00208         if (verbose)
00209                 printf("stream recovery\n");
00210         if (err == -EPIPE) {    /* under-run */
00211                 err = snd_pcm_prepare(handle);
00212                 if (err < 0)
00213                         printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
00214                 return 0;
00215         } else if (err == -ESTRPIPE) {
00216                 while ((err = snd_pcm_resume(handle)) == -EAGAIN)
00217                         sleep(1);       /* wait until the suspend flag is released */
00218                 if (err < 0) {
00219                         err = snd_pcm_prepare(handle);
00220                         if (err < 0)
00221                                 printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
00222                 }
00223                 return 0;
00224         }
00225         return err;
00226 }
00227 
00228 /*
00229  *   Transfer method - write only
00230  */
00231 
00232 static int write_loop(snd_pcm_t *handle,
00233                       signed short *samples,
00234                       snd_pcm_channel_area_t *areas)
00235 {
00236         double phase = 0;
00237         signed short *ptr;
00238         int err, cptr;
00239 
00240         while (1) {
00241                 generate_sine(areas, 0, period_size, &phase);
00242                 ptr = samples;
00243                 cptr = period_size;
00244                 while (cptr > 0) {
00245                         err = snd_pcm_writei(handle, ptr, cptr);
00246                         if (err == -EAGAIN)
00247                                 continue;
00248                         if (err < 0) {
00249                                 if (xrun_recovery(handle, err) < 0) {
00250                                         printf("Write error: %s\n", snd_strerror(err));
00251                                         exit(EXIT_FAILURE);
00252                                 }
00253                                 break;  /* skip one period */
00254                         }
00255                         ptr += err * channels;
00256                         cptr -= err;
00257                 }
00258         }
00259 }
00260  
00261 /*
00262  *   Transfer method - write and wait for room in buffer using poll
00263  */
00264 
00265 static int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count)
00266 {
00267         unsigned short revents;
00268 
00269         while (1) {
00270                 poll(ufds, count, -1);
00271                 snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);
00272                 if (revents & POLLERR)
00273                         return -EIO;
00274                 if (revents & POLLOUT)
00275                         return 0;
00276         }
00277 }
00278 
00279 static int write_and_poll_loop(snd_pcm_t *handle,
00280                                signed short *samples,
00281                                snd_pcm_channel_area_t *areas)
00282 {
00283         struct pollfd *ufds;
00284         double phase = 0;
00285         signed short *ptr;
00286         int err, count, cptr, init;
00287 
00288         count = snd_pcm_poll_descriptors_count (handle);
00289         if (count <= 0) {
00290                 printf("Invalid poll descriptors count\n");
00291                 return count;
00292         }
00293 
00294         ufds = malloc(sizeof(struct pollfd) * count);
00295         if (ufds == NULL) {
00296                 printf("No enough memory\n");
00297                 return -ENOMEM;
00298         }
00299         if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) {
00300                 printf("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
00301                 return err;
00302         }
00303 
00304         init = 1;
00305         while (1) {
00306                 if (!init) {
00307                         err = wait_for_poll(handle, ufds, count);
00308                         if (err < 0) {
00309                                 if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN ||
00310                                     snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) {
00311                                         err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
00312                                         if (xrun_recovery(handle, err) < 0) {
00313                                                 printf("Write error: %s\n", snd_strerror(err));
00314                                                 exit(EXIT_FAILURE);
00315                                         }
00316                                         init = 1;
00317                                 } else {
00318                                         printf("Wait for poll failed\n");
00319                                         return err;
00320                                 }
00321                         }
00322                 }
00323 
00324                 generate_sine(areas, 0, period_size, &phase);
00325                 ptr = samples;
00326                 cptr = period_size;
00327                 while (cptr > 0) {
00328                         err = snd_pcm_writei(handle, ptr, cptr);
00329                         if (err < 0) {
00330                                 if (xrun_recovery(handle, err) < 0) {
00331                                         printf("Write error: %s\n", snd_strerror(err));
00332                                         exit(EXIT_FAILURE);
00333                                 }
00334                                 init = 1;
00335                                 break;  /* skip one period */
00336                         }
00337                         if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING)
00338                                 init = 0;
00339                         ptr += err * channels;
00340                         cptr -= err;
00341                         if (cptr == 0)
00342                                 break;
00343                         /* it is possible, that the initial buffer cannot store */
00344                         /* all data from the last period, so wait awhile */
00345                         err = wait_for_poll(handle, ufds, count);
00346                         if (err < 0) {
00347                                 if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN ||
00348                                     snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) {
00349                                         err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
00350                                         if (xrun_recovery(handle, err) < 0) {
00351                                                 printf("Write error: %s\n", snd_strerror(err));
00352                                                 exit(EXIT_FAILURE);
00353                                         }
00354                                         init = 1;
00355                                 } else {
00356                                         printf("Wait for poll failed\n");
00357                                         return err;
00358                                 }
00359                         }
00360                 }
00361         }
00362 }
00363 
00364 /*
00365  *   Transfer method - asynchronous notification
00366  */
00367 
00368 struct async_private_data {
00369         signed short *samples;
00370         snd_pcm_channel_area_t *areas;
00371         double phase;
00372 };
00373 
00374 static void async_callback(snd_async_handler_t *ahandler)
00375 {
00376         snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler);
00377         struct async_private_data *data = snd_async_handler_get_callback_private(ahandler);
00378         signed short *samples = data->samples;
00379         snd_pcm_channel_area_t *areas = data->areas;
00380         snd_pcm_sframes_t avail;
00381         int err;
00382         
00383         avail = snd_pcm_avail_update(handle);
00384         while (avail >= period_size) {
00385                 generate_sine(areas, 0, period_size, &data->phase);
00386                 err = snd_pcm_writei(handle, samples, period_size);
00387                 if (err < 0) {
00388                         printf("Write error: %s\n", snd_strerror(err));
00389                         exit(EXIT_FAILURE);
00390                 }
00391                 if (err != period_size) {
00392                         printf("Write error: written %i expected %li\n", err, period_size);
00393                         exit(EXIT_FAILURE);
00394                 }
00395                 avail = snd_pcm_avail_update(handle);
00396         }
00397 }
00398 
00399 static int async_loop(snd_pcm_t *handle,
00400                       signed short *samples,
00401                       snd_pcm_channel_area_t *areas)
00402 {
00403         struct async_private_data data;
00404         snd_async_handler_t *ahandler;
00405         int err, count;
00406 
00407         data.samples = samples;
00408         data.areas = areas;
00409         data.phase = 0;
00410         err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, &data);
00411         if (err < 0) {
00412                 printf("Unable to register async handler\n");
00413                 exit(EXIT_FAILURE);
00414         }
00415         for (count = 0; count < 2; count++) {
00416                 generate_sine(areas, 0, period_size, &data.phase);
00417                 err = snd_pcm_writei(handle, samples, period_size);
00418                 if (err < 0) {
00419                         printf("Initial write error: %s\n", snd_strerror(err));
00420                         exit(EXIT_FAILURE);
00421                 }
00422                 if (err != period_size) {
00423                         printf("Initial write error: written %i expected %li\n", err, period_size);
00424                         exit(EXIT_FAILURE);
00425                 }
00426         }
00427         if (snd_pcm_state(handle) == SND_PCM_STATE_PREPARED) {
00428                 err = snd_pcm_start(handle);
00429                 if (err < 0) {
00430                         printf("Start error: %s\n", snd_strerror(err));
00431                         exit(EXIT_FAILURE);
00432                 }
00433         }
00434 
00435         /* because all other work is done in the signal handler,
00436            suspend the process */
00437         while (1) {
00438                 sleep(1);
00439         }
00440 }
00441 
00442 /*
00443  *   Transfer method - asynchronous notification + direct write
00444  */
00445 
00446 static void async_direct_callback(snd_async_handler_t *ahandler)
00447 {
00448         snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler);
00449         struct async_private_data *data = snd_async_handler_get_callback_private(ahandler);
00450         const snd_pcm_channel_area_t *my_areas;
00451         snd_pcm_uframes_t offset, frames, size;
00452         snd_pcm_sframes_t avail, commitres;
00453         snd_pcm_state_t state;
00454         int first = 0, err;
00455         
00456         while (1) {
00457                 state = snd_pcm_state(handle);
00458                 if (state == SND_PCM_STATE_XRUN) {
00459                         err = xrun_recovery(handle, -EPIPE);
00460                         if (err < 0) {
00461                                 printf("XRUN recovery failed: %s\n", snd_strerror(err));
00462                                 exit(EXIT_FAILURE);
00463                         }
00464                         first = 1;
00465                 } else if (state == SND_PCM_STATE_SUSPENDED) {
00466                         err = xrun_recovery(handle, -ESTRPIPE);
00467                         if (err < 0) {
00468                                 printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
00469                                 exit(EXIT_FAILURE);
00470                         }
00471                 }
00472                 avail = snd_pcm_avail_update(handle);
00473                 if (avail < 0) {
00474                         err = xrun_recovery(handle, avail);
00475                         if (err < 0) {
00476                                 printf("avail update failed: %s\n", snd_strerror(err));
00477                                 exit(EXIT_FAILURE);
00478                         }
00479                         first = 1;
00480                         continue;
00481                 }
00482                 if (avail < period_size) {
00483                         if (first) {
00484                                 first = 0;
00485                                 err = snd_pcm_start(handle);
00486                                 if (err < 0) {
00487                                         printf("Start error: %s\n", snd_strerror(err));
00488                                         exit(EXIT_FAILURE);
00489                                 }
00490                         } else {
00491                                 break;
00492                         }
00493                         continue;
00494                 }
00495                 size = period_size;
00496                 while (size > 0) {
00497                         frames = size;
00498                         err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
00499                         if (err < 0) {
00500                                 if ((err = xrun_recovery(handle, err)) < 0) {
00501                                         printf("MMAP begin avail error: %s\n", snd_strerror(err));
00502                                         exit(EXIT_FAILURE);
00503                                 }
00504                                 first = 1;
00505                         }
00506                         generate_sine(my_areas, offset, frames, &data->phase);
00507                         commitres = snd_pcm_mmap_commit(handle, offset, frames);
00508                         if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
00509                                 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
00510                                         printf("MMAP commit error: %s\n", snd_strerror(err));
00511                                         exit(EXIT_FAILURE);
00512                                 }
00513                                 first = 1;
00514                         }
00515                         size -= frames;
00516                 }
00517         }
00518 }
00519 
00520 static int async_direct_loop(snd_pcm_t *handle,
00521                              signed short *samples ATTRIBUTE_UNUSED,
00522                              snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED)
00523 {
00524         struct async_private_data data;
00525         snd_async_handler_t *ahandler;
00526         const snd_pcm_channel_area_t *my_areas;
00527         snd_pcm_uframes_t offset, frames, size;
00528         snd_pcm_sframes_t commitres;
00529         int err, count;
00530 
00531         data.samples = NULL;    /* we do not require the global sample area for direct write */
00532         data.areas = NULL;      /* we do not require the global areas for direct write */
00533         data.phase = 0;
00534         err = snd_async_add_pcm_handler(&ahandler, handle, async_direct_callback, &data);
00535         if (err < 0) {
00536                 printf("Unable to register async handler\n");
00537                 exit(EXIT_FAILURE);
00538         }
00539         for (count = 0; count < 2; count++) {
00540                 size = period_size;
00541                 while (size > 0) {
00542                         frames = size;
00543                         err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
00544                         if (err < 0) {
00545                                 if ((err = xrun_recovery(handle, err)) < 0) {
00546                                         printf("MMAP begin avail error: %s\n", snd_strerror(err));
00547                                         exit(EXIT_FAILURE);
00548                                 }
00549                         }
00550                         generate_sine(my_areas, offset, frames, &data.phase);
00551                         commitres = snd_pcm_mmap_commit(handle, offset, frames);
00552                         if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
00553                                 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
00554                                         printf("MMAP commit error: %s\n", snd_strerror(err));
00555                                         exit(EXIT_FAILURE);
00556                                 }
00557                         }
00558                         size -= frames;
00559                 }
00560         }
00561         err = snd_pcm_start(handle);
00562         if (err < 0) {
00563                 printf("Start error: %s\n", snd_strerror(err));
00564                 exit(EXIT_FAILURE);
00565         }
00566 
00567         /* because all other work is done in the signal handler,
00568            suspend the process */
00569         while (1) {
00570                 sleep(1);
00571         }
00572 }
00573 
00574 /*
00575  *   Transfer method - direct write only
00576  */
00577 
00578 static int direct_loop(snd_pcm_t *handle,
00579                        signed short *samples ATTRIBUTE_UNUSED,
00580                        snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED)
00581 {
00582         double phase = 0;
00583         const snd_pcm_channel_area_t *my_areas;
00584         snd_pcm_uframes_t offset, frames, size;
00585         snd_pcm_sframes_t avail, commitres;
00586         snd_pcm_state_t state;
00587         int err, first = 1;
00588 
00589         while (1) {
00590                 state = snd_pcm_state(handle);
00591                 if (state == SND_PCM_STATE_XRUN) {
00592                         err = xrun_recovery(handle, -EPIPE);
00593                         if (err < 0) {
00594                                 printf("XRUN recovery failed: %s\n", snd_strerror(err));
00595                                 return err;
00596                         }
00597                         first = 1;
00598                 } else if (state == SND_PCM_STATE_SUSPENDED) {
00599                         err = xrun_recovery(handle, -ESTRPIPE);
00600                         if (err < 0) {
00601                                 printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
00602                                 return err;
00603                         }
00604                 }
00605                 avail = snd_pcm_avail_update(handle);
00606                 if (avail < 0) {
00607                         err = xrun_recovery(handle, avail);
00608                         if (err < 0) {
00609                                 printf("avail update failed: %s\n", snd_strerror(err));
00610                                 return err;
00611                         }
00612                         first = 1;
00613                         continue;
00614                 }
00615                 if (avail < period_size) {
00616                         if (first) {
00617                                 first = 0;
00618                                 err = snd_pcm_start(handle);
00619                                 if (err < 0) {
00620                                         printf("Start error: %s\n", snd_strerror(err));
00621                                         exit(EXIT_FAILURE);
00622                                 }
00623                         } else {
00624                                 err = snd_pcm_wait(handle, -1);
00625                                 if (err < 0) {
00626                                         if ((err = xrun_recovery(handle, err)) < 0) {
00627                                                 printf("snd_pcm_wait error: %s\n", snd_strerror(err));
00628                                                 exit(EXIT_FAILURE);
00629                                         }
00630                                         first = 1;
00631                                 }
00632                         }
00633                         continue;
00634                 }
00635                 size = period_size;
00636                 while (size > 0) {
00637                         frames = size;
00638                         err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
00639                         if (err < 0) {
00640                                 if ((err = xrun_recovery(handle, err)) < 0) {
00641                                         printf("MMAP begin avail error: %s\n", snd_strerror(err));
00642                                         exit(EXIT_FAILURE);
00643                                 }
00644                                 first = 1;
00645                         }
00646                         generate_sine(my_areas, offset, frames, &phase);
00647                         commitres = snd_pcm_mmap_commit(handle, offset, frames);
00648                         if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
00649                                 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
00650                                         printf("MMAP commit error: %s\n", snd_strerror(err));
00651                                         exit(EXIT_FAILURE);
00652                                 }
00653                                 first = 1;
00654                         }
00655                         size -= frames;
00656                 }
00657         }
00658 }
00659  
00660 /*
00661  *   Transfer method - direct write only using mmap_write functions
00662  */
00663 
00664 static int direct_write_loop(snd_pcm_t *handle,
00665                              signed short *samples,
00666                              snd_pcm_channel_area_t *areas)
00667 {
00668         double phase = 0;
00669         signed short *ptr;
00670         int err, cptr;
00671 
00672         while (1) {
00673                 generate_sine(areas, 0, period_size, &phase);
00674                 ptr = samples;
00675                 cptr = period_size;
00676                 while (cptr > 0) {
00677                         err = snd_pcm_mmap_writei(handle, ptr, cptr);
00678                         if (err == -EAGAIN)
00679                                 continue;
00680                         if (err < 0) {
00681                                 if (xrun_recovery(handle, err) < 0) {
00682                                         printf("Write error: %s\n", snd_strerror(err));
00683                                         exit(EXIT_FAILURE);
00684                                 }
00685                                 break;  /* skip one period */
00686                         }
00687                         ptr += err * channels;
00688                         cptr -= err;
00689                 }
00690         }
00691 }
00692  
00693 /*
00694  *
00695  */
00696 
00697 struct transfer_method {
00698         const char *name;
00699         snd_pcm_access_t access;
00700         int (*transfer_loop)(snd_pcm_t *handle,
00701                              signed short *samples,
00702                              snd_pcm_channel_area_t *areas);
00703 };
00704 
00705 static struct transfer_method transfer_methods[] = {
00706         { "write", SND_PCM_ACCESS_RW_INTERLEAVED, write_loop },
00707         { "write_and_poll", SND_PCM_ACCESS_RW_INTERLEAVED, write_and_poll_loop },
00708         { "async", SND_PCM_ACCESS_RW_INTERLEAVED, async_loop },
00709         { "async_direct", SND_PCM_ACCESS_MMAP_INTERLEAVED, async_direct_loop },
00710         { "direct_interleaved", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_loop },
00711         { "direct_noninterleaved", SND_PCM_ACCESS_MMAP_NONINTERLEAVED, direct_loop },
00712         { "direct_write", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_write_loop },
00713         { NULL, SND_PCM_ACCESS_RW_INTERLEAVED, NULL }
00714 };
00715 
00716 static void help(void)
00717 {
00718         int k;
00719         printf(
00720 "Usage: pcm [OPTION]... [FILE]...\n"
00721 "-h,--help      help\n"
00722 "-D,--device    playback device\n"
00723 "-r,--rate      stream rate in Hz\n"
00724 "-c,--channels  count of channels in stream\n"
00725 "-f,--frequency sine wave frequency in Hz\n"
00726 "-b,--buffer    ring buffer size in us\n"
00727 "-p,--period    period size in us\n"
00728 "-m,--method    transfer method\n"
00729 "-o,--format    sample format\n"
00730 "-v,--verbose   show the PCM setup parameters\n"
00731 "-n,--noresample  do not resample\n"
00732 "-e,--pevent    enable poll event after each period\n"
00733 "\n");
00734         printf("Recognized sample formats are:");
00735         for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) {
00736                 const char *s = snd_pcm_format_name(k);
00737                 if (s)
00738                         printf(" %s", s);
00739         }
00740         printf("\n");
00741         printf("Recognized transfer methods are:");
00742         for (k = 0; transfer_methods[k].name; k++)
00743                 printf(" %s", transfer_methods[k].name);
00744         printf("\n");
00745 }
00746 
00747 int main(int argc, char *argv[])
00748 {
00749         struct option long_option[] =
00750         {
00751                 {"help", 0, NULL, 'h'},
00752                 {"device", 1, NULL, 'D'},
00753                 {"rate", 1, NULL, 'r'},
00754                 {"channels", 1, NULL, 'c'},
00755                 {"frequency", 1, NULL, 'f'},
00756                 {"buffer", 1, NULL, 'b'},
00757                 {"period", 1, NULL, 'p'},
00758                 {"method", 1, NULL, 'm'},
00759                 {"format", 1, NULL, 'o'},
00760                 {"verbose", 1, NULL, 'v'},
00761                 {"noresample", 1, NULL, 'n'},
00762                 {"pevent", 1, NULL, 'e'},
00763                 {NULL, 0, NULL, 0},
00764         };
00765         snd_pcm_t *handle;
00766         int err, morehelp;
00767         snd_pcm_hw_params_t *hwparams;
00768         snd_pcm_sw_params_t *swparams;
00769         int method = 0;
00770         signed short *samples;
00771         unsigned int chn;
00772         snd_pcm_channel_area_t *areas;
00773 
00774         snd_pcm_hw_params_alloca(&hwparams);
00775         snd_pcm_sw_params_alloca(&swparams);
00776 
00777         morehelp = 0;
00778         while (1) {
00779                 int c;
00780                 if ((c = getopt_long(argc, argv, "hD:r:c:f:b:p:m:o:vne", long_option, NULL)) < 0)
00781                         break;
00782                 switch (c) {
00783                 case 'h':
00784                         morehelp++;
00785                         break;
00786                 case 'D':
00787                         device = strdup(optarg);
00788                         break;
00789                 case 'r':
00790                         rate = atoi(optarg);
00791                         rate = rate < 4000 ? 4000 : rate;
00792                         rate = rate > 196000 ? 196000 : rate;
00793                         break;
00794                 case 'c':
00795                         channels = atoi(optarg);
00796                         channels = channels < 1 ? 1 : channels;
00797                         channels = channels > 1024 ? 1024 : channels;
00798                         break;
00799                 case 'f':
00800                         freq = atoi(optarg);
00801                         freq = freq < 50 ? 50 : freq;
00802                         freq = freq > 5000 ? 5000 : freq;
00803                         break;
00804                 case 'b':
00805                         buffer_time = atoi(optarg);
00806                         buffer_time = buffer_time < 1000 ? 1000 : buffer_time;
00807                         buffer_time = buffer_time > 1000000 ? 1000000 : buffer_time;
00808                         break;
00809                 case 'p':
00810                         period_time = atoi(optarg);
00811                         period_time = period_time < 1000 ? 1000 : period_time;
00812                         period_time = period_time > 1000000 ? 1000000 : period_time;
00813                         break;
00814                 case 'm':
00815                         for (method = 0; transfer_methods[method].name; method++)
00816                                         if (!strcasecmp(transfer_methods[method].name, optarg))
00817                                         break;
00818                         if (transfer_methods[method].name == NULL)
00819                                 method = 0;
00820                         break;
00821                 case 'o':
00822                         for (format = 0; format < SND_PCM_FORMAT_LAST; format++) {
00823                                 const char *format_name = snd_pcm_format_name(format);
00824                                 if (format_name)
00825                                         if (!strcasecmp(format_name, optarg))
00826                                         break;
00827                         }
00828                         if (format == SND_PCM_FORMAT_LAST)
00829                                 format = SND_PCM_FORMAT_S16;
00830                         break;
00831                 case 'v':
00832                         verbose = 1;
00833                         break;
00834                 case 'n':
00835                         resample = 0;
00836                         break;
00837                 case 'e':
00838                         period_event = 1;
00839                         break;
00840                 }
00841         }
00842 
00843         if (morehelp) {
00844                 help();
00845                 return 0;
00846         }
00847 
00848         err = snd_output_stdio_attach(&output, stdout, 0);
00849         if (err < 0) {
00850                 printf("Output failed: %s\n", snd_strerror(err));
00851                 return 0;
00852         }
00853 
00854         printf("Playback device is %s\n", device);
00855         printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels);
00856         printf("Sine wave rate is %.4fHz\n", freq);
00857         printf("Using transfer method: %s\n", transfer_methods[method].name);
00858 
00859         if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
00860                 printf("Playback open error: %s\n", snd_strerror(err));
00861                 return 0;
00862         }
00863         
00864         if ((err = set_hwparams(handle, hwparams, transfer_methods[method].access)) < 0) {
00865                 printf("Setting of hwparams failed: %s\n", snd_strerror(err));
00866                 exit(EXIT_FAILURE);
00867         }
00868         if ((err = set_swparams(handle, swparams)) < 0) {
00869                 printf("Setting of swparams failed: %s\n", snd_strerror(err));
00870                 exit(EXIT_FAILURE);
00871         }
00872 
00873         if (verbose > 0)
00874                 snd_pcm_dump(handle, output);
00875 
00876         samples = malloc((period_size * channels * snd_pcm_format_physical_width(format)) / 8);
00877         if (samples == NULL) {
00878                 printf("No enough memory\n");
00879                 exit(EXIT_FAILURE);
00880         }
00881         
00882         areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
00883         if (areas == NULL) {
00884                 printf("No enough memory\n");
00885                 exit(EXIT_FAILURE);
00886         }
00887         for (chn = 0; chn < channels; chn++) {
00888                 areas[chn].addr = samples;
00889                 areas[chn].first = chn * snd_pcm_format_physical_width(format);
00890                 areas[chn].step = channels * snd_pcm_format_physical_width(format);
00891         }
00892 
00893         err = transfer_methods[method].transfer_loop(handle, samples, areas);
00894         if (err < 0)
00895                 printf("Transfer failed: %s\n", snd_strerror(err));
00896 
00897         free(areas);
00898         free(samples);
00899         snd_pcm_close(handle);
00900         return 0;
00901 }
00902 

Generated on Wed May 6 09:16:47 2009 for ALSA project - the C library reference by  doxygen 1.5.0