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