libsent/src/adin/adin_mic_linux_alsa.c

Go to the documentation of this file.
00001 
00044 /*
00045  * Copyright (c) 1991-2007 Kawahara Lab., Kyoto University
00046  * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology
00047  * Copyright (c) 2005-2007 Julius project team, Nagoya Institute of Technology
00048  * All rights reserved
00049  */
00050 
00051 #include <sent/stddefs.h>
00052 #include <sent/adin.h>
00053 
00054 #include <sys/ioctl.h>
00055 #include <sys/types.h>
00056 #include <sys/stat.h>
00057 #include <fcntl.h>
00058 
00059 #include <alsa/asoundlib.h>
00060 
00061 static snd_pcm_t *handle;       
00062 static snd_pcm_hw_params_t *hwparams; 
00063 static char *pcm_name = "hw:0,0"; 
00064 
00065 static boolean need_swap;       
00066 static int latency = 32;        
00067 
00068 static struct pollfd *ufds;     
00069 static int count;               
00070 
00071 
00080 boolean
00081 adin_mic_standby(int sfreq, void *dummy)
00082 {
00083   int err;
00084 #if (SND_LIB_MAJOR == 0)
00085   int actual_rate;              /* sample rate returned by hardware */
00086 #else
00087   unsigned int actual_rate;             /* sample rate returned by hardware */
00088 #endif
00089   int dir;                      /* comparison result of exact rate and given rate */
00090 
00091   /* allocate hwparam structure */
00092   snd_pcm_hw_params_alloca(&hwparams);
00093 
00094   /* open device (for resource test, open in non-block mode) */
00095   if ((err = snd_pcm_open(&handle, pcm_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) {
00096     jlog("Error: adin_alsa: cannot open PCM device %s (%s)\n", pcm_name, snd_strerror(err));
00097     return(FALSE);
00098   }
00099   
00100   /* set device to non-block mode */
00101   if ((err = snd_pcm_nonblock(handle, 0)) < 0) {
00102     jlog("Error: adin_alsa: cannot set PCM device to block mode\n");
00103     return(FALSE);
00104   }
00105 
00106   /* initialize hwparam structure */
00107   if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0) {
00108     jlog("Error: adin_alsa: cannot initialize PCM device parameter structure (%s)\n", snd_strerror(err));
00109     return(FALSE);
00110   }
00111 
00112   /* set interleaved read/write format */
00113   if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
00114     jlog("Error: adin_alsa: cannot set PCM device access mode (%s)\n", snd_strerror(err));
00115     return(FALSE);
00116   }
00117 
00118   /* set sample format */
00119 #ifdef WORDS_BIGENDIAN
00120   /* try big endian, then little endian with byte swap */
00121   if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_BE)) >= 0) {
00122     need_swap = FALSE;
00123   } else if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE)) >= 0) {
00124     need_swap = TRUE;
00125   } else {
00126     jlog("Error: adin_alsa: cannot set PCM device format to 16bit-signed (%s)\n", snd_strerror(err));
00127     return(FALSE);
00128   }
00129 #else  /* LITTLE ENDIAN */
00130   /* try little endian, then big endian with byte swap */
00131   if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE)) >= 0) {
00132     need_swap = FALSE;
00133   } else if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_BE)) >= 0) {
00134     need_swap = TRUE;
00135   } else {
00136     jlog("Error: adin_alsa: cannot set PCM device format to 16bit-signed (%s)\n", snd_strerror(err));
00137     return(FALSE);
00138   }
00139 #endif
00140   
00141   /* set sample rate (if the exact rate is not supported by the hardware, use nearest possible rate */
00142 #if (SND_LIB_MAJOR == 0)
00143   actual_rate = snd_pcm_hw_params_set_rate_near(handle, hwparams, sfreq, &dir);
00144   if (actual_rate < 0) {
00145     jlog("Error: adin_alsa: cannot set PCM device sample rate to %d (%s)\n", sfreq, snd_strerror(actual_rate));
00146     return(FALSE);
00147   }
00148 #else
00149   actual_rate = sfreq;
00150   err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &actual_rate, &dir);
00151   if (err < 0) {
00152     jlog("Error: adin_alsa: cannot set PCM device sample rate to %d (%s)\n", sfreq, snd_strerror(err));
00153     return(FALSE);
00154   }
00155 #endif
00156   if (actual_rate != sfreq) {
00157     jlog("Warning: adin_alsa: the rate %d Hz is not supported by your PCM hardware.\n", sfreq);
00158     jlog("Warning: adin_alsa: using %d Hz instead.\n", actual_rate);
00159   }
00160   jlog("Stat: adin_alsa: sampling rate is %dHz\n", actual_rate);
00161 
00162   /* set number of channels */
00163   {
00164     int minchannels;
00165     snd_pcm_hw_params_get_channels_min(hwparams, &minchannels);
00166     if (minchannels > 1) {
00167       jlog("Error: adin_alsa: monoral recording not supported on this device/driver\n");
00168       return(FALSE);
00169     }
00170   }
00171   
00172   if ((err = snd_pcm_hw_params_set_channels(handle, hwparams, 1)) < 0) {
00173     jlog("Error: adin_alsa: cannot set PCM channel to %d (%s)\n", 1, snd_strerror(err));
00174     return(FALSE);
00175   }
00176 
00177   /* set period size */
00178   {
00179 #if (SND_LIB_MAJOR == 0)
00180     int periodsize;             /* period size (bytes) */
00181     int actual_size;
00182     int maxsize, minsize;
00183 #else
00184     snd_pcm_uframes_t periodsize;               /* period size (bytes) */
00185     snd_pcm_uframes_t actual_size;
00186     snd_pcm_uframes_t maxsize, minsize;
00187 #endif
00188     char *p;
00189     
00190     /* get hardware max/min size */
00191     dir = 0;
00192 #if (SND_LIB_MAJOR == 0)
00193     if ((maxsize = snd_pcm_hw_params_get_period_size_max(hwparams, &dir)) < 0) {
00194       jlog("Error: adin_alsa: cannot get maximum period size\n");
00195       return(FALSE);
00196     }
00197     if ((minsize = snd_pcm_hw_params_get_period_size_min(hwparams, &dir)) < 0) {
00198       jlog("Error: adin_alsa: cannot get minimum period size\n");
00199       return(FALSE);
00200     }
00201 #else    
00202     if ((err = snd_pcm_hw_params_get_period_size_max(hwparams, &maxsize, &dir)) < 0) {
00203       jlog("Error: adin_alsa: cannot get maximum period size\n");
00204       return(FALSE);
00205     }
00206     if ((err = snd_pcm_hw_params_get_period_size_min(hwparams, &minsize, &dir)) < 0) {
00207       jlog("Error: adin_alsa: cannot get minimum period size\n");
00208       return(FALSE);
00209     }
00210 #endif
00211 
00212     /* set apropriate period size */
00213     if ((p = getenv("LATENCY_MSEC")) != NULL) {
00214       latency = atoi(p);
00215       jlog("Stat: adin_alsa: set latency to %d msec (obtained from LATENCY_MSEC)\n", latency);
00216     } else {
00217       jlog("Stat: adin_alsa: set latency to %d msec\n", latency);
00218     }
00219 
00220     periodsize = actual_rate * latency / 1000 * sizeof(SP16);
00221     if (periodsize < minsize) {
00222       jlog("Stat: adin_alsa: PCM latency of %d ms (%d bytes) too small, use device minimum %d bytes\n", latency, periodsize, minsize);
00223       periodsize = minsize;
00224     } else if (periodsize > maxsize) {
00225       jlog("Stat: adin_alsa: PCM latency of %d ms (%d bytes) too large, use device maximum %d bytes\n", latency, periodsize, maxsize);
00226       periodsize = maxsize;
00227     }
00228     
00229     /* set size (near value will be used) */
00230 #if (SND_LIB_MAJOR == 0)
00231     actual_size = snd_pcm_hw_params_set_period_size_near(handle, hwparams, periodsize, &dir);
00232     if (actual_size < 0) {
00233       jlog("Error: adin_alsa: cannot set PCM record period size to %d (%s)\n", periodsize, snd_strerror(actual_size));
00234       return(FALSE);
00235     }
00236 #else
00237     actual_size = periodsize;
00238     err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &actual_size, &dir);
00239     if (err < 0) {
00240       jlog("Error: adin_alsa: cannot set PCM record period size to %d (%s)\n", periodsize, snd_strerror(err));
00241       return(FALSE);
00242     }
00243 #endif
00244     if (actual_size != periodsize) {
00245       jlog("Stat: adin_alsa: PCM period size: %d bytes (%d ms) -> %d bytes\n", periodsize, latency, actual_size);
00246     }
00247     jlog("Stat: Audio I/O Latency = %d msec (data fragment = %d frames)\n", actual_size * 1000 / (actual_rate * sizeof(SP16)), actual_size / sizeof(SP16));
00248       
00249     /* set number of periods ( = 2) */
00250     if ((err = snd_pcm_hw_params_set_periods(handle, hwparams, sizeof(SP16), 0)) < 0) {
00251       jlog("Error: adin_alsa: cannot set PCM number of periods to %d (%s)\n", sizeof(SP16), snd_strerror(err));
00252       return(FALSE);
00253     }
00254   }
00255 
00256   /* apply the configuration to the PCM device */
00257   if ((err = snd_pcm_hw_params(handle, hwparams)) < 0) {
00258     jlog("Error: adin_alsa: cannot set PCM hardware parameters (%s)\n", snd_strerror(err));
00259     return(FALSE);
00260   }
00261 
00262   /* prepare for recording */
00263   if ((err = snd_pcm_prepare(handle)) < 0) {
00264     jlog("Error: adin_alsa: cannot prepare audio interface (%s)\n", snd_strerror(err));
00265   }
00266 
00267   /* prepare for polling */
00268   count = snd_pcm_poll_descriptors_count(handle);
00269   if (count <= 0) {
00270     jlog("Error: adin_alsa: invalid PCM poll descriptors count\n");
00271     return(FALSE);
00272   }
00273   ufds = mymalloc(sizeof(struct pollfd) * count);
00274 
00275   if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) {
00276     jlog("Error: adin_alsa: unable to obtain poll descriptors for PCM recording (%s)\n", snd_strerror(err));
00277     return(FALSE);
00278   }
00279 
00280   return(TRUE);
00281 }
00282 
00291 static int
00292 xrun_recovery(snd_pcm_t *handle, int err)
00293 {
00294   if (err == -EPIPE) {    /* under-run */
00295     err = snd_pcm_prepare(handle);
00296     if (err < 0)
00297       jlog("Error: adin_alsa: can't recovery from PCM buffer underrun, prepare failed: %s\n", snd_strerror(err));
00298     return 0;
00299   } else if (err == -ESTRPIPE) {
00300     while ((err = snd_pcm_resume(handle)) == -EAGAIN)
00301       sleep(1);       /* wait until the suspend flag is released */
00302     if (err < 0) {
00303       err = snd_pcm_prepare(handle);
00304       if (err < 0)
00305         jlog("Error: adin_alsa: can't recovery from PCM buffer suspend, prepare failed: %s\n", snd_strerror(err));
00306     }
00307     return 0;
00308   }
00309   return err;
00310 }
00311 
00317 boolean
00318 adin_mic_begin()
00319 {
00320   int err;
00321   snd_pcm_state_t status;
00322 
00323   /* check hardware status */
00324   while(1) {                    /* wait till prepared */
00325     status = snd_pcm_state(handle);
00326     switch(status) {
00327     case SND_PCM_STATE_PREPARED: /* prepared for operation */
00328       if ((err = snd_pcm_start(handle)) < 0) {
00329         jlog("Error: adin_alsa: cannot start PCM (%s)\n", snd_strerror(err));
00330         return (FALSE);
00331       }
00332       return(TRUE);
00333       break;
00334     case SND_PCM_STATE_RUNNING: /* capturing the samples of other application */
00335       if ((err = snd_pcm_drop(handle)) < 0) { /* discard the existing samples */
00336         jlog("Error: adin_alsa: cannot drop PCM (%s)\n", snd_strerror(err));
00337         return (FALSE);
00338       }
00339       break;
00340     case SND_PCM_STATE_XRUN:    /* buffer overrun */
00341       if ((err = xrun_recovery(handle, -EPIPE)) < 0) {
00342         jlog("Error: adin_alsa: PCM XRUN recovery failed (%s)\n", snd_strerror(err));
00343         return(FALSE);
00344       }
00345       break;
00346     case SND_PCM_STATE_SUSPENDED:       /* suspended by power management system */
00347       if ((err = xrun_recovery(handle, -ESTRPIPE)) < 0) {
00348         jlog("Error: adin_alsa: PCM XRUN recovery failed (%s)\n", snd_strerror(err));
00349         return(FALSE);
00350       }
00351       break;
00352     }
00353   }
00354 
00355   return(TRUE);
00356 }
00357   
00363 boolean
00364 adin_mic_end()
00365 {
00366   return(TRUE);
00367 }
00368 
00381 int
00382 adin_mic_read(SP16 *buf, int sampnum)
00383 {
00384   int cnt;
00385   snd_pcm_sframes_t avail;
00386 
00387   while ((avail = snd_pcm_avail_update(handle)) <= 0) {
00388     usleep(latency * 1000);
00389   }
00390   if (avail < sampnum) {
00391     cnt = snd_pcm_readi(handle, buf, avail);
00392   } else {
00393     cnt = snd_pcm_readi(handle, buf, sampnum);
00394   }
00395 
00396   if (cnt < 0) {
00397     jlog("Error: adin_alsa: failed to read PCM (%s)\n", snd_strerror(cnt));
00398     return(-2);
00399   }
00400 
00401   if (need_swap) {
00402     swap_sample_bytes(buf, cnt);
00403   }
00404   return(cnt);
00405 }

Generated on Tue Dec 18 15:59:54 2007 for Julius by  doxygen 1.5.4