Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

adin_mic_linux_alsa.c

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

Generated on Tue Mar 28 16:01:39 2006 for Julius by  doxygen 1.4.2