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

Generated on Thu May 11 09:52:35 2006 for ALSA project - the C library reference by  doxygen 1.4.6