/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("Initial write error: %s\n", snd_strerror(err));
00389                         exit(EXIT_FAILURE);
00390                 }
00391                 if (err != period_size) {
00392                         printf("Initial 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         err = snd_pcm_start(handle);
00428         if (err < 0) {
00429                 printf("Start error: %s\n", snd_strerror(err));
00430                 exit(EXIT_FAILURE);
00431         }
00432 
00433         /* because all other work is done in the signal handler,
00434            suspend the process */
00435         while (1) {
00436                 sleep(1);
00437         }
00438 }
00439 
00440 /*
00441  *   Transfer method - asynchronous notification + direct write
00442  */
00443 
00444 static void async_direct_callback(snd_async_handler_t *ahandler)
00445 {
00446         snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler);
00447         struct async_private_data *data = snd_async_handler_get_callback_private(ahandler);
00448         const snd_pcm_channel_area_t *my_areas;
00449         snd_pcm_uframes_t offset, frames, size;
00450         snd_pcm_sframes_t avail, commitres;
00451         snd_pcm_state_t state;
00452         int first = 0, err;
00453         
00454         while (1) {
00455                 state = snd_pcm_state(handle);
00456                 if (state == SND_PCM_STATE_XRUN) {
00457                         err = xrun_recovery(handle, -EPIPE);
00458                         if (err < 0) {
00459                                 printf("XRUN recovery failed: %s\n", snd_strerror(err));
00460                                 exit(EXIT_FAILURE);
00461                         }
00462                         first = 1;
00463                 } else if (state == SND_PCM_STATE_SUSPENDED) {
00464                         err = xrun_recovery(handle, -ESTRPIPE);
00465                         if (err < 0) {
00466                                 printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
00467                                 exit(EXIT_FAILURE);
00468                         }
00469                 }
00470                 avail = snd_pcm_avail_update(handle);
00471                 if (avail < 0) {
00472                         err = xrun_recovery(handle, avail);
00473                         if (err < 0) {
00474                                 printf("avail update failed: %s\n", snd_strerror(err));
00475                                 exit(EXIT_FAILURE);
00476                         }
00477                         first = 1;
00478                         continue;
00479                 }
00480                 if (avail < period_size) {
00481                         if (first) {
00482                                 first = 0;
00483                                 err = snd_pcm_start(handle);
00484                                 if (err < 0) {
00485                                         printf("Start error: %s\n", snd_strerror(err));
00486                                         exit(EXIT_FAILURE);
00487                                 }
00488                         } else {
00489                                 break;
00490                         }
00491                         continue;
00492                 }
00493                 size = period_size;
00494                 while (size > 0) {
00495                         frames = size;
00496                         err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
00497                         if (err < 0) {
00498                                 if ((err = xrun_recovery(handle, err)) < 0) {
00499                                         printf("MMAP begin avail error: %s\n", snd_strerror(err));
00500                                         exit(EXIT_FAILURE);
00501                                 }
00502                                 first = 1;
00503                         }
00504                         generate_sine(my_areas, offset, frames, &data->phase);
00505                         commitres = snd_pcm_mmap_commit(handle, offset, frames);
00506                         if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
00507                                 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
00508                                         printf("MMAP commit error: %s\n", snd_strerror(err));
00509                                         exit(EXIT_FAILURE);
00510                                 }
00511                                 first = 1;
00512                         }
00513                         size -= frames;
00514                 }
00515         }
00516 }
00517 
00518 static int async_direct_loop(snd_pcm_t *handle,
00519                              signed short *samples ATTRIBUTE_UNUSED,
00520                              snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED)
00521 {
00522         struct async_private_data data;
00523         snd_async_handler_t *ahandler;
00524         const snd_pcm_channel_area_t *my_areas;
00525         snd_pcm_uframes_t offset, frames, size;
00526         snd_pcm_sframes_t commitres;
00527         int err, count;
00528 
00529         data.samples = NULL;    /* we do not require the global sample area for direct write */
00530         data.areas = NULL;      /* we do not require the global areas for direct write */
00531         data.phase = 0;
00532         err = snd_async_add_pcm_handler(&ahandler, handle, async_direct_callback, &data);
00533         if (err < 0) {
00534                 printf("Unable to register async handler\n");
00535                 exit(EXIT_FAILURE);
00536         }
00537         for (count = 0; count < 2; count++) {
00538                 size = period_size;
00539                 while (size > 0) {
00540                         frames = size;
00541                         err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
00542                         if (err < 0) {
00543                                 if ((err = xrun_recovery(handle, err)) < 0) {
00544                                         printf("MMAP begin avail error: %s\n", snd_strerror(err));
00545                                         exit(EXIT_FAILURE);
00546                                 }
00547                         }
00548                         generate_sine(my_areas, offset, frames, &data.phase);
00549                         commitres = snd_pcm_mmap_commit(handle, offset, frames);
00550                         if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
00551                                 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
00552                                         printf("MMAP commit error: %s\n", snd_strerror(err));
00553                                         exit(EXIT_FAILURE);
00554                                 }
00555                         }
00556                         size -= frames;
00557                 }
00558         }
00559         err = snd_pcm_start(handle);
00560         if (err < 0) {
00561                 printf("Start error: %s\n", snd_strerror(err));
00562                 exit(EXIT_FAILURE);
00563         }
00564 
00565         /* because all other work is done in the signal handler,
00566            suspend the process */
00567         while (1) {
00568                 sleep(1);
00569         }
00570 }
00571 
00572 /*
00573  *   Transfer method - direct write only
00574  */
00575 
00576 static int direct_loop(snd_pcm_t *handle,
00577                        signed short *samples ATTRIBUTE_UNUSED,
00578                        snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED)
00579 {
00580         double phase = 0;
00581         const snd_pcm_channel_area_t *my_areas;
00582         snd_pcm_uframes_t offset, frames, size;
00583         snd_pcm_sframes_t avail, commitres;
00584         snd_pcm_state_t state;
00585         int err, first = 1;
00586 
00587         while (1) {
00588                 state = snd_pcm_state(handle);
00589                 if (state == SND_PCM_STATE_XRUN) {
00590                         err = xrun_recovery(handle, -EPIPE);
00591                         if (err < 0) {
00592                                 printf("XRUN recovery failed: %s\n", snd_strerror(err));
00593                                 return err;
00594                         }
00595                         first = 1;
00596                 } else if (state == SND_PCM_STATE_SUSPENDED) {
00597                         err = xrun_recovery(handle, -ESTRPIPE);
00598                         if (err < 0) {
00599                                 printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
00600                                 return err;
00601                         }
00602                 }
00603                 avail = snd_pcm_avail_update(handle);
00604                 if (avail < 0) {
00605                         err = xrun_recovery(handle, avail);
00606                         if (err < 0) {
00607                                 printf("avail update failed: %s\n", snd_strerror(err));
00608                                 return err;
00609                         }
00610                         first = 1;
00611                         continue;
00612                 }
00613                 if (avail < period_size) {
00614                         if (first) {
00615                                 first = 0;
00616                                 err = snd_pcm_start(handle);
00617                                 if (err < 0) {
00618                                         printf("Start error: %s\n", snd_strerror(err));
00619                                         exit(EXIT_FAILURE);
00620                                 }
00621                         } else {
00622                                 err = snd_pcm_wait(handle, -1);
00623                                 if (err < 0) {
00624                                         if ((err = xrun_recovery(handle, err)) < 0) {
00625                                                 printf("snd_pcm_wait error: %s\n", snd_strerror(err));
00626                                                 exit(EXIT_FAILURE);
00627                                         }
00628                                         first = 1;
00629                                 }
00630                         }
00631                         continue;
00632                 }
00633                 size = period_size;
00634                 while (size > 0) {
00635                         frames = size;
00636                         err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
00637                         if (err < 0) {
00638                                 if ((err = xrun_recovery(handle, err)) < 0) {
00639                                         printf("MMAP begin avail error: %s\n", snd_strerror(err));
00640                                         exit(EXIT_FAILURE);
00641                                 }
00642                                 first = 1;
00643                         }
00644                         generate_sine(my_areas, offset, frames, &phase);
00645                         commitres = snd_pcm_mmap_commit(handle, offset, frames);
00646                         if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
00647                                 if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
00648                                         printf("MMAP commit error: %s\n", snd_strerror(err));
00649                                         exit(EXIT_FAILURE);
00650                                 }
00651                                 first = 1;
00652                         }
00653                         size -= frames;
00654                 }
00655         }
00656 }
00657  
00658 /*
00659  *   Transfer method - direct write only using mmap_write functions
00660  */
00661 
00662 static int direct_write_loop(snd_pcm_t *handle,
00663                              signed short *samples,
00664                              snd_pcm_channel_area_t *areas)
00665 {
00666         double phase = 0;
00667         signed short *ptr;
00668         int err, cptr;
00669 
00670         while (1) {
00671                 generate_sine(areas, 0, period_size, &phase);
00672                 ptr = samples;
00673                 cptr = period_size;
00674                 while (cptr > 0) {
00675                         err = snd_pcm_mmap_writei(handle, ptr, cptr);
00676                         if (err == -EAGAIN)
00677                                 continue;
00678                         if (err < 0) {
00679                                 if (xrun_recovery(handle, err) < 0) {
00680                                         printf("Write error: %s\n", snd_strerror(err));
00681                                         exit(EXIT_FAILURE);
00682                                 }
00683                                 break;  /* skip one period */
00684                         }
00685                         ptr += err * channels;
00686                         cptr -= err;
00687                 }
00688         }
00689 }
00690  
00691 /*
00692  *
00693  */
00694 
00695 struct transfer_method {
00696         const char *name;
00697         snd_pcm_access_t access;
00698         int (*transfer_loop)(snd_pcm_t *handle,
00699                              signed short *samples,
00700                              snd_pcm_channel_area_t *areas);
00701 };
00702 
00703 static struct transfer_method transfer_methods[] = {
00704         { "write", SND_PCM_ACCESS_RW_INTERLEAVED, write_loop },
00705         { "write_and_poll", SND_PCM_ACCESS_RW_INTERLEAVED, write_and_poll_loop },
00706         { "async", SND_PCM_ACCESS_RW_INTERLEAVED, async_loop },
00707         { "async_direct", SND_PCM_ACCESS_MMAP_INTERLEAVED, async_direct_loop },
00708         { "direct_interleaved", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_loop },
00709         { "direct_noninterleaved", SND_PCM_ACCESS_MMAP_NONINTERLEAVED, direct_loop },
00710         { "direct_write", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_write_loop },
00711         { NULL, SND_PCM_ACCESS_RW_INTERLEAVED, NULL }
00712 };
00713 
00714 static void help(void)
00715 {
00716         int k;
00717         printf(
00718 "Usage: pcm [OPTION]... [FILE]...\n"
00719 "-h,--help      help\n"
00720 "-D,--device    playback device\n"
00721 "-r,--rate      stream rate in Hz\n"
00722 "-c,--channels  count of channels in stream\n"
00723 "-f,--frequency sine wave frequency in Hz\n"
00724 "-b,--buffer    ring buffer size in us\n"
00725 "-p,--period    period size in us\n"
00726 "-m,--method    transfer method\n"
00727 "-o,--format    sample format\n"
00728 "-v,--verbose   show the PCM setup parameters\n"
00729 "-n,--noresample  do not resample\n"
00730 "-e,--pevent    enable poll event after each period\n"
00731 "\n");
00732         printf("Recognized sample formats are:");
00733         for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) {
00734                 const char *s = snd_pcm_format_name(k);
00735                 if (s)
00736                         printf(" %s", s);
00737         }
00738         printf("\n");
00739         printf("Recognized transfer methods are:");
00740         for (k = 0; transfer_methods[k].name; k++)
00741                 printf(" %s", transfer_methods[k].name);
00742         printf("\n");
00743 }
00744 
00745 int main(int argc, char *argv[])
00746 {
00747         struct option long_option[] =
00748         {
00749                 {"help", 0, NULL, 'h'},
00750                 {"device", 1, NULL, 'D'},
00751                 {"rate", 1, NULL, 'r'},
00752                 {"channels", 1, NULL, 'c'},
00753                 {"frequency", 1, NULL, 'f'},
00754                 {"buffer", 1, NULL, 'b'},
00755                 {"period", 1, NULL, 'p'},
00756                 {"method", 1, NULL, 'm'},
00757                 {"format", 1, NULL, 'o'},
00758                 {"verbose", 1, NULL, 'v'},
00759                 {"noresample", 1, NULL, 'n'},
00760                 {"pevent", 1, NULL, 'e'},
00761                 {NULL, 0, NULL, 0},
00762         };
00763         snd_pcm_t *handle;
00764         int err, morehelp;
00765         snd_pcm_hw_params_t *hwparams;
00766         snd_pcm_sw_params_t *swparams;
00767         int method = 0;
00768         signed short *samples;
00769         unsigned int chn;
00770         snd_pcm_channel_area_t *areas;
00771 
00772         snd_pcm_hw_params_alloca(&hwparams);
00773         snd_pcm_sw_params_alloca(&swparams);
00774 
00775         morehelp = 0;
00776         while (1) {
00777                 int c;
00778                 if ((c = getopt_long(argc, argv, "hD:r:c:f:b:p:m:o:vne", long_option, NULL)) < 0)
00779                         break;
00780                 switch (c) {
00781                 case 'h':
00782                         morehelp++;
00783                         break;
00784                 case 'D':
00785                         device = strdup(optarg);
00786                         break;
00787                 case 'r':
00788                         rate = atoi(optarg);
00789                         rate = rate < 4000 ? 4000 : rate;
00790                         rate = rate > 196000 ? 196000 : rate;
00791                         break;
00792                 case 'c':
00793                         channels = atoi(optarg);
00794                         channels = channels < 1 ? 1 : channels;
00795                         channels = channels > 1024 ? 1024 : channels;
00796                         break;
00797                 case 'f':
00798                         freq = atoi(optarg);
00799                         freq = freq < 50 ? 50 : freq;
00800                         freq = freq > 5000 ? 5000 : freq;
00801                         break;
00802                 case 'b':
00803                         buffer_time = atoi(optarg);
00804                         buffer_time = buffer_time < 1000 ? 1000 : buffer_time;
00805                         buffer_time = buffer_time > 1000000 ? 1000000 : buffer_time;
00806                         break;
00807                 case 'p':
00808                         period_time = atoi(optarg);
00809                         period_time = period_time < 1000 ? 1000 : period_time;
00810                         period_time = period_time > 1000000 ? 1000000 : period_time;
00811                         break;
00812                 case 'm':
00813                         for (method = 0; transfer_methods[method].name; method++)
00814                                         if (!strcasecmp(transfer_methods[method].name, optarg))
00815                                         break;
00816                         if (transfer_methods[method].name == NULL)
00817                                 method = 0;
00818                         break;
00819                 case 'o':
00820                         for (format = 0; format < SND_PCM_FORMAT_LAST; format++) {
00821                                 const char *format_name = snd_pcm_format_name(format);
00822                                 if (format_name)
00823                                         if (!strcasecmp(format_name, optarg))
00824                                         break;
00825                         }
00826                         if (format == SND_PCM_FORMAT_LAST)
00827                                 format = SND_PCM_FORMAT_S16;
00828                         break;
00829                 case 'v':
00830                         verbose = 1;
00831                         break;
00832                 case 'n':
00833                         resample = 0;
00834                         break;
00835                 case 'e':
00836                         period_event = 1;
00837                         break;
00838                 }
00839         }
00840 
00841         if (morehelp) {
00842                 help();
00843                 return 0;
00844         }
00845 
00846         err = snd_output_stdio_attach(&output, stdout, 0);
00847         if (err < 0) {
00848                 printf("Output failed: %s\n", snd_strerror(err));
00849                 return 0;
00850         }
00851 
00852         printf("Playback device is %s\n", device);
00853         printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels);
00854         printf("Sine wave rate is %.4fHz\n", freq);
00855         printf("Using transfer method: %s\n", transfer_methods[method].name);
00856 
00857         if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
00858                 printf("Playback open error: %s\n", snd_strerror(err));
00859                 return 0;
00860         }
00861         
00862         if ((err = set_hwparams(handle, hwparams, transfer_methods[method].access)) < 0) {
00863                 printf("Setting of hwparams failed: %s\n", snd_strerror(err));
00864                 exit(EXIT_FAILURE);
00865         }
00866         if ((err = set_swparams(handle, swparams)) < 0) {
00867                 printf("Setting of swparams failed: %s\n", snd_strerror(err));
00868                 exit(EXIT_FAILURE);
00869         }
00870 
00871         if (verbose > 0)
00872                 snd_pcm_dump(handle, output);
00873 
00874         samples = malloc((period_size * channels * snd_pcm_format_physical_width(format)) / 8);
00875         if (samples == NULL) {
00876                 printf("No enough memory\n");
00877                 exit(EXIT_FAILURE);
00878         }
00879         
00880         areas = calloc(channels, sizeof(snd_pcm_channel_area_t));
00881         if (areas == NULL) {
00882                 printf("No enough memory\n");
00883                 exit(EXIT_FAILURE);
00884         }
00885         for (chn = 0; chn < channels; chn++) {
00886                 areas[chn].addr = samples;
00887                 areas[chn].first = chn * snd_pcm_format_physical_width(format);
00888                 areas[chn].step = channels * snd_pcm_format_physical_width(format);
00889         }
00890 
00891         err = transfer_methods[method].transfer_loop(handle, samples, areas);
00892         if (err < 0)
00893                 printf("Transfer failed: %s\n", snd_strerror(err));
00894 
00895         free(areas);
00896         free(samples);
00897         snd_pcm_close(handle);
00898         return 0;
00899 }
00900 

Generated on Wed Jan 21 18:54:00 2009 for ALSA project - the C library reference by  doxygen 1.4.7