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