Julius 4.2
|
00001 00050 /* 00051 * Copyright (c) 1991-2011 Kawahara Lab., Kyoto University 00052 * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology 00053 * Copyright (c) 2005-2011 Julius project team, Nagoya Institute of Technology 00054 * All rights reserved 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 int srate; 00073 static snd_pcm_t *handle; 00074 static char pcm_name[MAXPATHLEN]; 00075 static int latency = 32; 00076 static boolean need_swap; 00077 00078 #if (SND_LIB_MAJOR == 0) 00079 static struct pollfd *ufds; 00080 static int count; 00081 #endif 00082 00083 #define MAXPOLLINTERVAL 300 ///< Read timeout in msec. 00084 00085 #endif /* HAS_ALSA */ 00086 00087 #ifdef HAS_ALSA 00088 00095 static void 00096 output_card_info(char *pcm_name, snd_pcm_t *handle) 00097 { 00098 int err; 00099 snd_ctl_t *ctl; 00100 snd_ctl_card_info_t *info; 00101 snd_pcm_info_t *pcminfo; 00102 snd_ctl_card_info_alloca(&info); 00103 snd_pcm_info_alloca(&pcminfo); 00104 char ctlname[30]; 00105 int card; 00106 00107 /* get PCM information to set current device and subdevice name */ 00108 if ((err = snd_pcm_info(handle, pcminfo)) < 0) { 00109 jlog("Warning: adin_alsa: failed to obtain pcm info\n"); 00110 jlog("Warning: adin_alsa: skip output of detailed audio device info\n"); 00111 return; 00112 } 00113 /* open control associated with the pcm device name */ 00114 card = snd_pcm_info_get_card(pcminfo); 00115 if (card < 0) { 00116 strcpy(ctlname, "default"); 00117 } else { 00118 snprintf(ctlname, 30, "hw:%d", card); 00119 } 00120 if ((err = snd_ctl_open(&ctl, ctlname, 0)) < 0) { 00121 jlog("Warning: adin_alsa: failed to open control device \"%s\", \n", ctlname); 00122 jlog("Warning: adin_alsa: skip output of detailed audio device info\n"); 00123 return; 00124 } 00125 /* get its card info */ 00126 if ((err = snd_ctl_card_info(ctl, info)) < 0) { 00127 jlog("Warning: adin_alsa: unable to get card info for %s\n", ctlname); 00128 jlog("Warning: adin_alsa: skip output of detailed audio device info\n"); 00129 snd_ctl_close(ctl); 00130 return; 00131 } 00132 00133 /* get detailed PCM information of current device from control */ 00134 if ((err = snd_ctl_pcm_info(ctl, pcminfo)) < 0) { 00135 jlog("Error: adin_alsa: unable to get pcm info from card control\n"); 00136 jlog("Warning: adin_alsa: skip output of detailed audio device info\n"); 00137 snd_ctl_close(ctl); 00138 return; 00139 } 00140 /* output */ 00141 jlog("Stat: \"%s\": %s [%s] device %s [%s] %s\n", 00142 pcm_name, 00143 snd_ctl_card_info_get_id(info), 00144 snd_ctl_card_info_get_name(info), 00145 snd_pcm_info_get_id(pcminfo), 00146 snd_pcm_info_get_name(pcminfo), 00147 snd_pcm_info_get_subdevice_name(pcminfo)); 00148 00149 /* close controller */ 00150 snd_ctl_close(ctl); 00151 00152 } 00153 #endif /* HAS_ALSA */ 00154 00163 boolean 00164 adin_alsa_standby(int sfreq, void *dummy) 00165 { 00166 #ifndef HAS_ALSA 00167 jlog("Error: ALSA not compiled in\n"); 00168 return FALSE; 00169 #else 00170 /* store required sampling rate for checking after opening device */ 00171 srate = sfreq; 00172 return TRUE; 00173 #endif 00174 } 00175 00176 00184 static boolean 00185 adin_alsa_open(char *devstr) 00186 { 00187 #ifndef HAS_ALSA 00188 jlog("Error: ALSA not compiled in\n"); 00189 return FALSE; 00190 #else 00191 int err; 00192 snd_pcm_hw_params_t *hwparams; 00193 #if (SND_LIB_MAJOR == 0) 00194 int actual_rate; /* sample rate returned by hardware */ 00195 #else 00196 unsigned int actual_rate; /* sample rate returned by hardware */ 00197 #endif 00198 int dir = 0; /* comparison result of exact rate and given rate */ 00199 00200 /* open the device in non-block mode) */ 00201 if ((err = snd_pcm_open(&handle, devstr, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { 00202 jlog("Error: adin_alsa: cannot open PCM device \"%s\" (%s)\n", devstr, snd_strerror(err)); 00203 return(FALSE); 00204 } 00205 /* set device to non-block mode */ 00206 if ((err = snd_pcm_nonblock(handle, 1)) < 0) { 00207 jlog("Error: adin_alsa: cannot set PCM device to non-blocking mode\n"); 00208 return(FALSE); 00209 } 00210 00211 /* allocate hwparam structure */ 00212 snd_pcm_hw_params_alloca(&hwparams); 00213 00214 /* initialize hwparam structure */ 00215 if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0) { 00216 jlog("Error: adin_alsa: cannot initialize PCM device parameter structure (%s)\n", snd_strerror(err)); 00217 return(FALSE); 00218 } 00219 00220 /* set interleaved read/write format */ 00221 if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { 00222 jlog("Error: adin_alsa: cannot set PCM device access mode (%s)\n", snd_strerror(err)); 00223 return(FALSE); 00224 } 00225 00226 /* set sample format */ 00227 #ifdef WORDS_BIGENDIAN 00228 /* try big endian, then little endian with byte swap */ 00229 if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_BE)) >= 0) { 00230 need_swap = FALSE; 00231 } else if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE)) >= 0) { 00232 need_swap = TRUE; 00233 } else { 00234 jlog("Error: adin_alsa: cannot set PCM device format to signed 16bit (%s)\n", snd_strerror(err)); 00235 return(FALSE); 00236 } 00237 #else /* LITTLE ENDIAN */ 00238 /* try little endian, then big endian with byte swap */ 00239 if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE)) >= 0) { 00240 need_swap = FALSE; 00241 } else if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_BE)) >= 0) { 00242 need_swap = TRUE; 00243 } else { 00244 jlog("Error: adin_alsa: cannot set PCM device format to signed 16bit (%s)\n", snd_strerror(err)); 00245 return(FALSE); 00246 } 00247 #endif 00248 /* set number of channels */ 00249 if ((err = snd_pcm_hw_params_set_channels(handle, hwparams, 1)) < 0) { 00250 jlog("Error: adin_alsa: cannot set PCM channel to %d (%s)\n", 1, snd_strerror(err)); 00251 return(FALSE); 00252 } 00253 00254 /* set sample rate (if the exact rate is not supported by the hardware, use nearest possible rate */ 00255 #if (SND_LIB_MAJOR == 0) 00256 actual_rate = snd_pcm_hw_params_set_rate_near(handle, hwparams, srate, &dir); 00257 if (actual_rate < 0) { 00258 jlog("Error: adin_alsa: cannot set PCM device sample rate to %d (%s)\n", srate, snd_strerror(actual_rate)); 00259 return(FALSE); 00260 } 00261 #else 00262 actual_rate = srate; 00263 err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &actual_rate, &dir); 00264 if (err < 0) { 00265 jlog("Error: adin_alsa: cannot set PCM device sample rate to %d (%s)\n", srate, snd_strerror(err)); 00266 return(FALSE); 00267 } 00268 #endif 00269 if (actual_rate != srate) { 00270 jlog("Warning: adin_alsa: the exact rate %d Hz is not available by your PCM hardware.\n", srate); 00271 jlog("Warning: adin_alsa: using %d Hz instead.\n", actual_rate); 00272 } 00273 jlog("Stat: capture audio at %dHz\n", actual_rate); 00274 00275 /* set period size */ 00276 { 00277 #if (SND_LIB_MAJOR == 0) 00278 int periodsize; /* period size (bytes) */ 00279 int actual_size; 00280 int maxsize, minsize; 00281 #else 00282 unsigned int period_time, period_time_current; 00283 snd_pcm_uframes_t chunk_size; 00284 boolean has_current_period; 00285 #endif 00286 boolean force = FALSE; 00287 char *p; 00288 00289 /* set apropriate period size */ 00290 if ((p = getenv("LATENCY_MSEC")) != NULL) { 00291 latency = atoi(p); 00292 jlog("Stat: adin_alsa: trying to set latency to %d msec from LATENCY_MSEC)\n", latency); 00293 force = TRUE; 00294 } 00295 00296 /* get hardware max/min size */ 00297 #if (SND_LIB_MAJOR == 0) 00298 if ((maxsize = snd_pcm_hw_params_get_period_size_max(hwparams, &dir)) < 0) { 00299 jlog("Error: adin_alsa: cannot get maximum period size\n"); 00300 return(FALSE); 00301 } 00302 if ((minsize = snd_pcm_hw_params_get_period_size_min(hwparams, &dir)) < 0) { 00303 jlog("Error: adin_alsa: cannot get minimum period size\n"); 00304 return(FALSE); 00305 } 00306 #else 00307 has_current_period = TRUE; 00308 if ((err = snd_pcm_hw_params_get_period_time(hwparams, &period_time_current, &dir)) < 0) { 00309 has_current_period = FALSE; 00310 } 00311 if (has_current_period) { 00312 jlog("Stat: adin_alsa: current latency time: %d msec\n", period_time_current / 1000); 00313 } 00314 #endif 00315 00316 /* set period time (near value will be used) */ 00317 #if (SND_LIB_MAJOR == 0) 00318 periodsize = actual_rate * latency / 1000 * sizeof(SP16); 00319 if (periodsize < minsize) { 00320 jlog("Stat: adin_alsa: PCM latency of %d ms (%d bytes) too small, use device minimum %d bytes\n", latency, periodsize, minsize); 00321 periodsize = minsize; 00322 } else if (periodsize > maxsize) { 00323 jlog("Stat: adin_alsa: PCM latency of %d ms (%d bytes) too large, use device maximum %d bytes\n", latency, periodsize, maxsize); 00324 periodsize = maxsize; 00325 } 00326 actual_size = snd_pcm_hw_params_set_period_size_near(handle, hwparams, periodsize, &dir); 00327 if (actual_size < 0) { 00328 jlog("Error: adin_alsa: cannot set PCM record period size to %d (%s)\n", periodsize, snd_strerror(actual_size)); 00329 return(FALSE); 00330 } 00331 if (actual_size != periodsize) { 00332 jlog("Stat: adin_alsa: PCM period size: %d bytes (%dms) -> %d bytes\n", periodsize, latency, actual_size); 00333 } 00334 jlog("Stat: Audio I/O Latency = %d msec (data fragment = %d frames)\n", actual_size * 1000 / (actual_rate * sizeof(SP16)), actual_size / sizeof(SP16)); 00335 #else 00336 period_time = latency * 1000; 00337 if (!force && has_current_period && period_time > period_time_current) { 00338 jlog("Stat: adin_alsa: current latency (%dms) is shorter than %dms, leave it\n", period_time_current / 1000, latency); 00339 period_time = period_time_current; 00340 } else { 00341 if ((err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, 0)) < 0) { 00342 jlog("Error: adin_alsa: cannot set PCM record period time to %d msec (%s)\n", period_time / 1000, snd_strerror(err)); 00343 return(FALSE); 00344 } 00345 snd_pcm_hw_params_get_period_size(hwparams, &chunk_size, 0); 00346 jlog("Stat: adin_alsa: latency set to %d msec (chunk = %d bytes)\n", period_time / 1000, chunk_size); 00347 } 00348 #endif 00349 00350 #if (SND_LIB_MAJOR == 0) 00351 /* set number of periods ( = 2) */ 00352 if ((err = snd_pcm_hw_params_set_periods(handle, hwparams, sizeof(SP16), 0)) < 0) { 00353 jlog("Error: adin_alsa: cannot set PCM number of periods to %d (%s)\n", sizeof(SP16), snd_strerror(err)); 00354 return(FALSE); 00355 } 00356 #endif 00357 } 00358 00359 /* apply the configuration to the PCM device */ 00360 if ((err = snd_pcm_hw_params(handle, hwparams)) < 0) { 00361 jlog("Error: adin_alsa: cannot set PCM hardware parameters (%s)\n", snd_strerror(err)); 00362 return(FALSE); 00363 } 00364 00365 /* prepare for recording */ 00366 if ((err = snd_pcm_prepare(handle)) < 0) { 00367 jlog("Error: adin_alsa: failed to prepare audio interface (%s)\n", snd_strerror(err)); 00368 } 00369 00370 #if (SND_LIB_MAJOR == 0) 00371 /* prepare for polling */ 00372 count = snd_pcm_poll_descriptors_count(handle); 00373 if (count <= 0) { 00374 jlog("Error: adin_alsa: invalid PCM poll descriptors count\n"); 00375 return(FALSE); 00376 } 00377 ufds = mymalloc(sizeof(struct pollfd) * count); 00378 if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) { 00379 jlog("Error: adin_alsa: unable to obtain poll descriptors for PCM recording (%s)\n", snd_strerror(err)); 00380 return(FALSE); 00381 } 00382 #endif 00383 00384 /* output status */ 00385 output_card_info(devstr, handle); 00386 00387 return(TRUE); 00388 #endif /* HAS_ALSA */ 00389 } 00390 00391 #ifdef HAS_ALSA 00392 00400 static int 00401 xrun_recovery(snd_pcm_t *handle, int err) 00402 { 00403 if (err == -EPIPE) { /* under-run */ 00404 err = snd_pcm_prepare(handle); 00405 if (err < 0) 00406 jlog("Error: adin_alsa: can't recovery from PCM buffer underrun, prepare failed: %s\n", snd_strerror(err)); 00407 return 0; 00408 } else if (err == -ESTRPIPE) { 00409 while ((err = snd_pcm_resume(handle)) == -EAGAIN) 00410 sleep(1); /* wait until the suspend flag is released */ 00411 if (err < 0) { 00412 err = snd_pcm_prepare(handle); 00413 if (err < 0) 00414 jlog("Error: adin_alsa: can't recovery from PCM buffer suspend, prepare failed: %s\n", snd_strerror(err)); 00415 } 00416 return 0; 00417 } 00418 return err; 00419 } 00420 #endif /* HAS_ALSA */ 00421 00429 boolean 00430 adin_alsa_begin(char *pathname) 00431 { 00432 #ifndef HAS_ALSA 00433 return FALSE; 00434 #else 00435 int err; 00436 snd_pcm_state_t status; 00437 char *p; 00438 00439 /* set device name to open to pcm_name */ 00440 if (pathname != NULL) { 00441 strncpy(pcm_name, pathname, MAXPATHLEN); 00442 jlog("Stat: adin_alsa: device name from argument: \"%s\"\n", pcm_name); 00443 } else if ((p = getenv("ALSADEV")) != NULL) { 00444 strncpy(pcm_name, p, MAXPATHLEN); 00445 jlog("Stat: adin_alsa: device name from ALSADEV: \"%s\"\n", pcm_name); 00446 } else { 00447 strcpy(pcm_name, "default"); 00448 } 00449 /* open the device */ 00450 if (adin_alsa_open(pcm_name) == FALSE) { 00451 return FALSE; 00452 } 00453 00454 /* check hardware status */ 00455 while(1) { /* wait till prepared */ 00456 status = snd_pcm_state(handle); 00457 switch(status) { 00458 case SND_PCM_STATE_PREPARED: /* prepared for operation */ 00459 if ((err = snd_pcm_start(handle)) < 0) { 00460 jlog("Error: adin_alsa: cannot start PCM (%s)\n", snd_strerror(err)); 00461 return (FALSE); 00462 } 00463 return(TRUE); 00464 break; 00465 case SND_PCM_STATE_RUNNING: /* capturing the samples of other application */ 00466 if ((err = snd_pcm_drop(handle)) < 0) { /* discard the existing samples */ 00467 jlog("Error: adin_alsa: cannot drop PCM (%s)\n", snd_strerror(err)); 00468 return (FALSE); 00469 } 00470 break; 00471 case SND_PCM_STATE_XRUN: /* buffer overrun */ 00472 if ((err = xrun_recovery(handle, -EPIPE)) < 0) { 00473 jlog("Error: adin_alsa: PCM XRUN recovery failed (%s)\n", snd_strerror(err)); 00474 return(FALSE); 00475 } 00476 break; 00477 case SND_PCM_STATE_SUSPENDED: /* suspended by power management system */ 00478 if ((err = xrun_recovery(handle, -ESTRPIPE)) < 0) { 00479 jlog("Error: adin_alsa: PCM XRUN recovery failed (%s)\n", snd_strerror(err)); 00480 return(FALSE); 00481 } 00482 break; 00483 default: 00484 /* do nothing */ 00485 break; 00486 } 00487 } 00488 00489 return(TRUE); 00490 #endif /* HAS_ALSA */ 00491 } 00492 00498 boolean 00499 adin_alsa_end() 00500 { 00501 int err; 00502 00503 if ((err = snd_pcm_close(handle)) < 0) { 00504 jlog("Error: adin_alsa: cannot close PCM device (%s)\n", snd_strerror(err)); 00505 return(FALSE); 00506 } 00507 return(TRUE); 00508 } 00509 00522 int 00523 adin_alsa_read(SP16 *buf, int sampnum) 00524 { 00525 #ifndef HAS_ALSA 00526 return -2; 00527 #else 00528 int cnt; 00529 00530 #if (SND_LIB_MAJOR == 0) 00531 00532 snd_pcm_sframes_t avail; 00533 00534 while ((avail = snd_pcm_avail_update(handle)) <= 0) { 00535 usleep(latency * 1000); 00536 } 00537 if (avail < sampnum) { 00538 cnt = snd_pcm_readi(handle, buf, avail); 00539 } else { 00540 cnt = snd_pcm_readi(handle, buf, sampnum); 00541 } 00542 00543 #else 00544 00545 int ret; 00546 snd_pcm_status_t *status; 00547 int res; 00548 struct timeval now, diff, tstamp; 00549 00550 ret = snd_pcm_wait(handle, MAXPOLLINTERVAL); 00551 switch (ret) { 00552 case 0: /* timeout */ 00553 jlog("Warning: adin_alsa: no data fragment after %d msec?\n", MAXPOLLINTERVAL); 00554 cnt = 0; 00555 break; 00556 case 1: /* has data */ 00557 cnt = snd_pcm_readi(handle, buf, sampnum); /* read available (non-block) */ 00558 break; 00559 case -EPIPE: /* pipe error */ 00560 /* try to recover the broken pipe */ 00561 snd_pcm_status_alloca(&status); 00562 if ((res = snd_pcm_status(handle, status))<0) { 00563 jlog("Error: adin_alsa: broken pipe: status error (%s)\n", snd_strerror(res)); 00564 return -2; 00565 } 00566 if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) { 00567 gettimeofday(&now, 0); 00568 snd_pcm_status_get_trigger_tstamp(status, &tstamp); 00569 timersub(&now, &tstamp, &diff); 00570 jlog("Warning: adin_alsa: overrun!!! (at least %.3f ms long)\n", 00571 diff.tv_sec * 1000 + diff.tv_usec / 1000.0); 00572 if ((res = snd_pcm_prepare(handle))<0) { 00573 jlog("Error: adin_alsa: overrun: prepare error (%s)", snd_strerror(res)); 00574 return -2; 00575 } 00576 break; /* ok, data should be accepted again */ 00577 } else if (snd_pcm_status_get_state(status) == SND_PCM_STATE_DRAINING) { 00578 jlog("Warning: adin_alsa: draining: capture stream format change? attempting recover...\n"); 00579 if ((res = snd_pcm_prepare(handle))<0) { 00580 jlog("Error: adin_alsa: draining: prepare error (%s)", snd_strerror(res)); 00581 return -2; 00582 } 00583 break; 00584 } 00585 jlog("Error: adin_alsa: error in snd_pcm_wait() (%s)\n", snd_pcm_state_name(snd_pcm_status_get_state(status))); 00586 return -2; 00587 00588 default: /* other poll error */ 00589 jlog("Error: adin_alsa: error in snd_pcm_wait() (%s)\n", snd_strerror(ret)); 00590 return(-2); /* error */ 00591 } 00592 #endif 00593 if (cnt < 0) { 00594 jlog("Error: adin_alsa: failed to read PCM (%s)\n", snd_strerror(cnt)); 00595 return(-2); 00596 } 00597 if (need_swap) { 00598 swap_sample_bytes(buf, cnt); 00599 } 00600 00601 return(cnt); 00602 #endif /* HAS_ALSA */ 00603 } 00604 00612 char * 00613 adin_alsa_input_name() 00614 { 00615 #ifndef HAS_ALSA 00616 return NULL; 00617 #else 00618 return(pcm_name); 00619 #endif 00620 } 00621 00622 /* end of file */