libsent/src/adin/adin_mic_linux_alsa.c

説明を見る。
00001 
00050 /*
00051  * Copyright (c) 1991-2007 Kawahara Lab., Kyoto University
00052  * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology
00053  * Copyright (c) 2005-2007 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 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 /* HAS_ALSA */
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   /* get PCM information to set current device and subdevice name */
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   /* open control associated with the pcm device name */
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   /* get its card info */
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   /* get detailed PCM information of current device from control */
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   /* output */
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   /* close controller */
00149   snd_ctl_close(ctl);
00150 
00151 }
00152 #endif /* HAS_ALSA */
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;              /* sample rate returned by hardware */
00173 #else
00174   unsigned int actual_rate;             /* sample rate returned by hardware */
00175 #endif
00176   int dir = 0;                  /* comparison result of exact rate and given rate */
00177   char *p;
00178 
00179   /* check $ALSADEV for device name */
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   /* open device in non-block mode) */
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   /* set device to non-block mode */
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   /* allocate hwparam structure */
00197   snd_pcm_hw_params_alloca(&hwparams);
00198 
00199   /* initialize hwparam structure */
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   /* set interleaved read/write format */
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   /* set sample format */
00212 #ifdef WORDS_BIGENDIAN
00213   /* try big endian, then little endian with byte swap */
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  /* LITTLE ENDIAN */
00223   /* try little endian, then big endian with byte swap */
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   /* set number of channels */
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   /* set sample rate (if the exact rate is not supported by the hardware, use nearest possible rate */
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   /* set period size */
00261   {
00262 #if (SND_LIB_MAJOR == 0)
00263     int periodsize;             /* period size (bytes) */
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     /* set apropriate period size */
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     /* get hardware max/min size */
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     /* set period time (near value will be used) */
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     /* set number of periods ( = 2) */
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   /* apply the configuration to the PCM device */
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   /* prepare for recording */
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   /* prepare for polling */
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   /* output status */
00369   output_card_info(pcm_name, handle);
00370 
00371   return(TRUE);
00372 #endif /* HAS_ALSA */
00373 }
00374 
00375 #ifdef HAS_ALSA
00376 
00384 static int
00385 xrun_recovery(snd_pcm_t *handle, int err)
00386 {
00387   if (err == -EPIPE) {    /* under-run */
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);       /* wait until the suspend flag is released */
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 /* HAS_ALSA */
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   /* check hardware status */
00421   while(1) {                    /* wait till prepared */
00422     status = snd_pcm_state(handle);
00423     switch(status) {
00424     case SND_PCM_STATE_PREPARED: /* prepared for operation */
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: /* capturing the samples of other application */
00432       if ((err = snd_pcm_drop(handle)) < 0) { /* discard the existing samples */
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:    /* buffer overrun */
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:       /* suspended by power management system */
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       /* do nothing */
00451       break;
00452     }
00453   }
00454 
00455   return(TRUE);
00456 #endif /* HAS_ALSA */
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:                       /* timeout */
00513     jlog("Warning: adin_alsa: no data fragment after %d msec?\n", MAXPOLLINTERVAL);
00514     cnt = 0;
00515     break;
00516   case 1:                       /* has data */
00517     cnt = snd_pcm_readi(handle, buf, sampnum); /* read available (non-block) */
00518     break;
00519   case -EPIPE:                  /* pipe error */
00520     /* try to recover the broken pipe */
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;         /* ok, data should be accepted again */
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:                      /* other poll error */
00549     jlog("Error: adin_alsa: error in snd_pcm_wait() (%s)\n", snd_strerror(ret));
00550     return(-2);                 /* error */
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 /* HAS_ALSA */
00563 }
00564 
00565 /* end of file */

Juliusに対してThu Jul 23 12:16:23 2009に生成されました。  doxygen 1.5.1