00001
00050
00051
00052
00053
00054
00055
00056
00057 #include <sent/stddefs.h>
00058 #include <sent/adin.h>
00059 #include <sys/ioctl.h>
00060 #include <sys/types.h>
00061 #include <sys/stat.h>
00062 #include <fcntl.h>
00063
00064 #ifdef HAS_ALSA
00065
00066 #if defined(HAVE_ALSA_ASOUNDLIB_H)
00067 #include <alsa/asoundlib.h>
00068 #elif defined(HAVE_SYS_ASOUNDLIB_H)
00069 #include <sys/asoundlib.h>
00070 #endif
00071
00072 static snd_pcm_t *handle;
00073 static char *pcm_name = "default";
00074 static int latency = 32;
00075 static boolean need_swap;
00076
00077 #if (SND_LIB_MAJOR == 0)
00078 static struct pollfd *ufds;
00079 static int count;
00080 #endif
00081
00082 #define MAXPOLLINTERVAL 300
00083
00084 #endif
00085
00086 #ifdef HAS_ALSA
00087
00094 static void
00095 output_card_info(char *pcm_name, snd_pcm_t *handle)
00096 {
00097 int err;
00098 snd_ctl_t *ctl;
00099 snd_ctl_card_info_t *info;
00100 snd_pcm_info_t *pcminfo;
00101 snd_ctl_card_info_alloca(&info);
00102 snd_pcm_info_alloca(&pcminfo);
00103 char ctlname[30];
00104 int card;
00105
00106
00107 if ((err = snd_pcm_info(handle, pcminfo)) < 0) {
00108 jlog("Warning: adin_alsa: failed to obtain pcm info\n");
00109 jlog("Warning: adin_alsa: skip output of detailed audio device info\n");
00110 return;
00111 }
00112
00113 card = snd_pcm_info_get_card(pcminfo);
00114 if (card < 0) {
00115 strcpy(ctlname, "default");
00116 } else {
00117 snprintf(ctlname, 30, "hw:%d", card);
00118 }
00119 if ((err = snd_ctl_open(&ctl, ctlname, 0)) < 0) {
00120 jlog("Warning: adin_alsa: failed to open control device \"%s\", \n", ctlname);
00121 jlog("Warning: adin_alsa: skip output of detailed audio device info\n");
00122 return;
00123 }
00124
00125 if ((err = snd_ctl_card_info(ctl, info)) < 0) {
00126 jlog("Warning: adin_alsa: unable to get card info for %s\n", ctlname);
00127 jlog("Warning: adin_alsa: skip output of detailed audio device info\n");
00128 snd_ctl_close(ctl);
00129 return;
00130 }
00131
00132
00133 if ((err = snd_ctl_pcm_info(ctl, pcminfo)) < 0) {
00134 jlog("Error: adin_alsa: unable to get pcm info from card control\n");
00135 jlog("Warning: adin_alsa: skip output of detailed audio device info\n");
00136 snd_ctl_close(ctl);
00137 return;
00138 }
00139
00140 jlog("Stat: \"%s\": %s [%s] device %s [%s] %s\n",
00141 pcm_name,
00142 snd_ctl_card_info_get_id(info),
00143 snd_ctl_card_info_get_name(info),
00144 snd_pcm_info_get_id(pcminfo),
00145 snd_pcm_info_get_name(pcminfo),
00146 snd_pcm_info_get_subdevice_name(pcminfo));
00147
00148
00149 snd_ctl_close(ctl);
00150
00151 }
00152 #endif
00153
00162 boolean
00163 adin_alsa_standby(int sfreq, void *dummy)
00164 {
00165 #ifndef HAS_ALSA
00166 jlog("Error: ALSA not compiled in\n");
00167 return FALSE;
00168 #else
00169 int err;
00170 snd_pcm_hw_params_t *hwparams;
00171 #if (SND_LIB_MAJOR == 0)
00172 int actual_rate;
00173 #else
00174 unsigned int actual_rate;
00175 #endif
00176 int dir = 0;
00177 char *p;
00178
00179
00180 if ((p = getenv("ALSADEV")) != NULL) {
00181 pcm_name = p;
00182 jlog("Stat: adin_alsa: device name from ALSADEV: \"%s\"\n", pcm_name);
00183 }
00184
00185
00186 if ((err = snd_pcm_open(&handle, pcm_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) {
00187 jlog("Error: adin_alsa: cannot open PCM device \"%s\" (%s)\n", pcm_name, snd_strerror(err));
00188 return(FALSE);
00189 }
00190
00191 if ((err = snd_pcm_nonblock(handle, 1)) < 0) {
00192 jlog("Error: adin_alsa: cannot set PCM device to non-blocking mode\n");
00193 return(FALSE);
00194 }
00195
00196
00197 snd_pcm_hw_params_alloca(&hwparams);
00198
00199
00200 if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0) {
00201 jlog("Error: adin_alsa: cannot initialize PCM device parameter structure (%s)\n", snd_strerror(err));
00202 return(FALSE);
00203 }
00204
00205
00206 if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
00207 jlog("Error: adin_alsa: cannot set PCM device access mode (%s)\n", snd_strerror(err));
00208 return(FALSE);
00209 }
00210
00211
00212 #ifdef WORDS_BIGENDIAN
00213
00214 if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_BE)) >= 0) {
00215 need_swap = FALSE;
00216 } else if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE)) >= 0) {
00217 need_swap = TRUE;
00218 } else {
00219 jlog("Error: adin_alsa: cannot set PCM device format to signed 16bit (%s)\n", snd_strerror(err));
00220 return(FALSE);
00221 }
00222 #else
00223
00224 if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE)) >= 0) {
00225 need_swap = FALSE;
00226 } else if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_BE)) >= 0) {
00227 need_swap = TRUE;
00228 } else {
00229 jlog("Error: adin_alsa: cannot set PCM device format to signed 16bit (%s)\n", snd_strerror(err));
00230 return(FALSE);
00231 }
00232 #endif
00233
00234 if ((err = snd_pcm_hw_params_set_channels(handle, hwparams, 1)) < 0) {
00235 jlog("Error: adin_alsa: cannot set PCM channel to %d (%s)\n", 1, snd_strerror(err));
00236 return(FALSE);
00237 }
00238
00239
00240 #if (SND_LIB_MAJOR == 0)
00241 actual_rate = snd_pcm_hw_params_set_rate_near(handle, hwparams, sfreq, &dir);
00242 if (actual_rate < 0) {
00243 jlog("Error: adin_alsa: cannot set PCM device sample rate to %d (%s)\n", sfreq, snd_strerror(actual_rate));
00244 return(FALSE);
00245 }
00246 #else
00247 actual_rate = sfreq;
00248 err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &actual_rate, &dir);
00249 if (err < 0) {
00250 jlog("Error: adin_alsa: cannot set PCM device sample rate to %d (%s)\n", sfreq, snd_strerror(err));
00251 return(FALSE);
00252 }
00253 #endif
00254 if (actual_rate != sfreq) {
00255 jlog("Warning: adin_alsa: the exact rate %d Hz is not available by your PCM hardware.\n", sfreq);
00256 jlog("Warning: adin_alsa: using %d Hz instead.\n", actual_rate);
00257 }
00258 jlog("Stat: capture audio at %dHz\n", actual_rate);
00259
00260
00261 {
00262 #if (SND_LIB_MAJOR == 0)
00263 int periodsize;
00264 int actual_size;
00265 int maxsize, minsize;
00266 #else
00267 unsigned int period_time, period_time_current;
00268 snd_pcm_uframes_t chunk_size;
00269 boolean has_current_period;
00270 #endif
00271 boolean force = FALSE;
00272
00273
00274 if ((p = getenv("LATENCY_MSEC")) != NULL) {
00275 latency = atoi(p);
00276 jlog("Stat: adin_alsa: trying to set latency to %d msec from LATENCY_MSEC)\n", latency);
00277 force = TRUE;
00278 }
00279
00280
00281 #if (SND_LIB_MAJOR == 0)
00282 if ((maxsize = snd_pcm_hw_params_get_period_size_max(hwparams, &dir)) < 0) {
00283 jlog("Error: adin_alsa: cannot get maximum period size\n");
00284 return(FALSE);
00285 }
00286 if ((minsize = snd_pcm_hw_params_get_period_size_min(hwparams, &dir)) < 0) {
00287 jlog("Error: adin_alsa: cannot get minimum period size\n");
00288 return(FALSE);
00289 }
00290 #else
00291 has_current_period = TRUE;
00292 if ((err = snd_pcm_hw_params_get_period_time(hwparams, &period_time_current, &dir)) < 0) {
00293 has_current_period = FALSE;
00294 }
00295 if (has_current_period) {
00296 jlog("Stat: adin_alsa: current latency time: %d msec\n", period_time_current / 1000);
00297 }
00298 #endif
00299
00300
00301 #if (SND_LIB_MAJOR == 0)
00302 periodsize = actual_rate * latency / 1000 * sizeof(SP16);
00303 if (periodsize < minsize) {
00304 jlog("Stat: adin_alsa: PCM latency of %d ms (%d bytes) too small, use device minimum %d bytes\n", latency, periodsize, minsize);
00305 periodsize = minsize;
00306 } else if (periodsize > maxsize) {
00307 jlog("Stat: adin_alsa: PCM latency of %d ms (%d bytes) too large, use device maximum %d bytes\n", latency, periodsize, maxsize);
00308 periodsize = maxsize;
00309 }
00310 actual_size = snd_pcm_hw_params_set_period_size_near(handle, hwparams, periodsize, &dir);
00311 if (actual_size < 0) {
00312 jlog("Error: adin_alsa: cannot set PCM record period size to %d (%s)\n", periodsize, snd_strerror(actual_size));
00313 return(FALSE);
00314 }
00315 if (actual_size != periodsize) {
00316 jlog("Stat: adin_alsa: PCM period size: %d bytes (%dms) -> %d bytes\n", periodsize, latency, actual_size);
00317 }
00318 jlog("Stat: Audio I/O Latency = %d msec (data fragment = %d frames)\n", actual_size * 1000 / (actual_rate * sizeof(SP16)), actual_size / sizeof(SP16));
00319 #else
00320 period_time = latency * 1000;
00321 if (!force && has_current_period && period_time > period_time_current) {
00322 jlog("Stat: adin_alsa: current latency (%dms) is shorter than %dms, leave it\n", period_time_current / 1000, latency);
00323 period_time = period_time_current;
00324 } else {
00325 if ((err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, 0)) < 0) {
00326 jlog("Error: adin_alsa: cannot set PCM record period time to %d msec (%s)\n", period_time / 1000, snd_strerror(err));
00327 return(FALSE);
00328 }
00329 snd_pcm_hw_params_get_period_size(hwparams, &chunk_size, 0);
00330 jlog("Stat: adin_alsa: latency set to %d msec (chunk = %d bytes)\n", period_time / 1000, chunk_size);
00331 }
00332 #endif
00333
00334 #if (SND_LIB_MAJOR == 0)
00335
00336 if ((err = snd_pcm_hw_params_set_periods(handle, hwparams, sizeof(SP16), 0)) < 0) {
00337 jlog("Error: adin_alsa: cannot set PCM number of periods to %d (%s)\n", sizeof(SP16), snd_strerror(err));
00338 return(FALSE);
00339 }
00340 #endif
00341 }
00342
00343
00344 if ((err = snd_pcm_hw_params(handle, hwparams)) < 0) {
00345 jlog("Error: adin_alsa: cannot set PCM hardware parameters (%s)\n", snd_strerror(err));
00346 return(FALSE);
00347 }
00348
00349
00350 if ((err = snd_pcm_prepare(handle)) < 0) {
00351 jlog("Error: adin_alsa: failed to prepare audio interface (%s)\n", snd_strerror(err));
00352 }
00353
00354 #if (SND_LIB_MAJOR == 0)
00355
00356 count = snd_pcm_poll_descriptors_count(handle);
00357 if (count <= 0) {
00358 jlog("Error: adin_alsa: invalid PCM poll descriptors count\n");
00359 return(FALSE);
00360 }
00361 ufds = mymalloc(sizeof(struct pollfd) * count);
00362 if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) {
00363 jlog("Error: adin_alsa: unable to obtain poll descriptors for PCM recording (%s)\n", snd_strerror(err));
00364 return(FALSE);
00365 }
00366 #endif
00367
00368
00369 output_card_info(pcm_name, handle);
00370
00371 return(TRUE);
00372 #endif
00373 }
00374
00375 #ifdef HAS_ALSA
00376
00384 static int
00385 xrun_recovery(snd_pcm_t *handle, int err)
00386 {
00387 if (err == -EPIPE) {
00388 err = snd_pcm_prepare(handle);
00389 if (err < 0)
00390 jlog("Error: adin_alsa: can't recovery from PCM buffer underrun, prepare failed: %s\n", snd_strerror(err));
00391 return 0;
00392 } else if (err == -ESTRPIPE) {
00393 while ((err = snd_pcm_resume(handle)) == -EAGAIN)
00394 sleep(1);
00395 if (err < 0) {
00396 err = snd_pcm_prepare(handle);
00397 if (err < 0)
00398 jlog("Error: adin_alsa: can't recovery from PCM buffer suspend, prepare failed: %s\n", snd_strerror(err));
00399 }
00400 return 0;
00401 }
00402 return err;
00403 }
00404 #endif
00405
00411 boolean
00412 adin_alsa_begin()
00413 {
00414 #ifndef HAS_ALSA
00415 return FALSE;
00416 #else
00417 int err;
00418 snd_pcm_state_t status;
00419
00420
00421 while(1) {
00422 status = snd_pcm_state(handle);
00423 switch(status) {
00424 case SND_PCM_STATE_PREPARED:
00425 if ((err = snd_pcm_start(handle)) < 0) {
00426 jlog("Error: adin_alsa: cannot start PCM (%s)\n", snd_strerror(err));
00427 return (FALSE);
00428 }
00429 return(TRUE);
00430 break;
00431 case SND_PCM_STATE_RUNNING:
00432 if ((err = snd_pcm_drop(handle)) < 0) {
00433 jlog("Error: adin_alsa: cannot drop PCM (%s)\n", snd_strerror(err));
00434 return (FALSE);
00435 }
00436 break;
00437 case SND_PCM_STATE_XRUN:
00438 if ((err = xrun_recovery(handle, -EPIPE)) < 0) {
00439 jlog("Error: adin_alsa: PCM XRUN recovery failed (%s)\n", snd_strerror(err));
00440 return(FALSE);
00441 }
00442 break;
00443 case SND_PCM_STATE_SUSPENDED:
00444 if ((err = xrun_recovery(handle, -ESTRPIPE)) < 0) {
00445 jlog("Error: adin_alsa: PCM XRUN recovery failed (%s)\n", snd_strerror(err));
00446 return(FALSE);
00447 }
00448 break;
00449 default:
00450
00451 break;
00452 }
00453 }
00454
00455 return(TRUE);
00456 #endif
00457 }
00458
00464 boolean
00465 adin_alsa_end()
00466 {
00467 return(TRUE);
00468 }
00469
00482 int
00483 adin_alsa_read(SP16 *buf, int sampnum)
00484 {
00485 #ifndef HAS_ALSA
00486 return -2;
00487 #else
00488 int cnt;
00489
00490 #if (SND_LIB_MAJOR == 0)
00491
00492 snd_pcm_sframes_t avail;
00493
00494 while ((avail = snd_pcm_avail_update(handle)) <= 0) {
00495 usleep(latency * 1000);
00496 }
00497 if (avail < sampnum) {
00498 cnt = snd_pcm_readi(handle, buf, avail);
00499 } else {
00500 cnt = snd_pcm_readi(handle, buf, sampnum);
00501 }
00502
00503 #else
00504
00505 int ret;
00506 snd_pcm_status_t *status;
00507 int res;
00508 struct timeval now, diff, tstamp;
00509
00510 ret = snd_pcm_wait(handle, MAXPOLLINTERVAL);
00511 switch (ret) {
00512 case 0:
00513 jlog("Warning: adin_alsa: no data fragment after %d msec?\n", MAXPOLLINTERVAL);
00514 cnt = 0;
00515 break;
00516 case 1:
00517 cnt = snd_pcm_readi(handle, buf, sampnum);
00518 break;
00519 case -EPIPE:
00520
00521 snd_pcm_status_alloca(&status);
00522 if ((res = snd_pcm_status(handle, status))<0) {
00523 jlog("Error: adin_alsa: broken pipe: status error (%s)\n", snd_strerror(res));
00524 return -2;
00525 }
00526 if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
00527 gettimeofday(&now, 0);
00528 snd_pcm_status_get_trigger_tstamp(status, &tstamp);
00529 timersub(&now, &tstamp, &diff);
00530 jlog("Warning: adin_alsa: overrun!!! (at least %.3f ms long)\n",
00531 diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
00532 if ((res = snd_pcm_prepare(handle))<0) {
00533 jlog("Error: adin_alsa: overrun: prepare error (%s)", snd_strerror(res));
00534 return -2;
00535 }
00536 break;
00537 } else if (snd_pcm_status_get_state(status) == SND_PCM_STATE_DRAINING) {
00538 jlog("Warning: adin_alsa: draining: capture stream format change? attempting recover...\n");
00539 if ((res = snd_pcm_prepare(handle))<0) {
00540 jlog("Error: adin_alsa: draining: prepare error (%s)", snd_strerror(res));
00541 return -2;
00542 }
00543 break;
00544 }
00545 jlog("Error: adin_alsa: error in snd_pcm_wait() (%s)\n", snd_pcm_state_name(snd_pcm_status_get_state(status)));
00546 return -2;
00547
00548 default:
00549 jlog("Error: adin_alsa: error in snd_pcm_wait() (%s)\n", snd_strerror(ret));
00550 return(-2);
00551 }
00552 #endif
00553 if (cnt < 0) {
00554 jlog("Error: adin_alsa: failed to read PCM (%s)\n", snd_strerror(cnt));
00555 return(-2);
00556 }
00557 if (need_swap) {
00558 swap_sample_bytes(buf, cnt);
00559 }
00560
00561 return(cnt);
00562 #endif
00563 }
00564
00565