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