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

Generated on Tue Apr 10 08:32:00 2007 for ALSA project - the C library reference by  doxygen 1.5.1