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

Generated on Sun Apr 27 19:01:25 2008 for ALSA project - the C library reference by  doxygen 1.5.1