Julius 4.2
libsent/src/adin/adin_mic_linux_oss.c
説明を見る。
00001 
00057 /*
00058  * Copyright (c) 1991-2011 Kawahara Lab., Kyoto University
00059  * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology
00060  * Copyright (c) 2005-2011 Julius project team, Nagoya Institute of Technology
00061  * All rights reserved
00062  */
00063 
00064 #include <sent/stddefs.h>
00065 
00066 #include <sent/adin.h>
00067 #include <sys/ioctl.h>
00068 #include <sys/types.h>
00069 #include <sys/stat.h>
00070 #include <sys/time.h>
00071 #include <fcntl.h>
00072 
00073 /* sound header */
00074 #ifdef HAS_OSS
00075 #if defined(HAVE_SYS_SOUNDCARD_H)
00076 #include <sys/soundcard.h>
00077 #elif defined(HAVE_MACHINE_SOUNDCARD_H)
00078 #include <machine/soundcard.h>
00079 #endif
00080 #endif
00081 
00083 #define DEFAULT_DEVICE "/dev/dsp"
00084 
00085 #define FREQALLOWRANGE 200      ///< Acceptable width of sampling frequency
00086 #define MAXPOLLINTERVAL 300     ///< Read timeout in msec.
00087 
00094 #define MAX_FRAGMENT_MSEC 50
00095 
00099 #define MIN_FRAGMENT_SIZE 256
00100 
00101 static int srate;               
00102 static int audio_fd;            
00103 static boolean need_swap;       
00104 static int frag_size;           
00105 static boolean stereo_rec;      
00106 static char *defaultdev = DEFAULT_DEVICE; 
00107 static char devname[MAXPATHLEN];                
00108 
00117 boolean
00118 adin_oss_standby(int sfreq, void *dummy)
00119 {
00120 #ifndef HAS_OSS
00121   jlog("Error: OSS not compiled in\n");
00122   return FALSE;
00123 #else
00124   /* store required sampling rate for checking after opening device */
00125   srate = sfreq;
00126   return TRUE;
00127 }
00128 
00136 static boolean
00137 adin_oss_open(char *devstr)
00138 {
00139   int fmt, fmt_can, fmt1, fmt2, rfmt; /* sampling format */
00140   int samplerate;       /* 16kHz */
00141   int frag;
00142   int frag_msec;
00143   char *env, *p;
00144 
00145   /* open device */
00146   if ((audio_fd = open(devstr, O_RDONLY|O_NONBLOCK)) == -1) {
00147     jlog("Error: adin_oss: failed to open %s\n", devstr);
00148     return(FALSE);
00149   }
00150 
00151   /* check whether soundcard can record 16bit data */
00152   /* and set fmt */
00153   if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt_can) == -1) {
00154     jlog("Error: adin_oss: failed to get formats from audio device\n");
00155     return(FALSE);
00156   }
00157 #ifdef WORDS_BIGENDIAN
00158   fmt1 = AFMT_S16_BE;
00159   fmt2 = AFMT_S16_LE;
00160 #else
00161   fmt1 = AFMT_S16_LE;               /* 16bit signed (little endian) */
00162   fmt2 = AFMT_S16_BE;               /* (big endian) */
00163 #endif /* WORDS_BIGENDIAN */
00164   /* fmt2 needs byte swap */
00165   if (fmt_can & fmt1) {
00166     fmt = fmt1;
00167     need_swap = FALSE;
00168   } else if (fmt_can & fmt2) {
00169     fmt = fmt2;
00170     need_swap = TRUE;
00171   } else {
00172     jlog("Error: adin_oss: 16bit recording not supported on this device\n");
00173     return FALSE;
00174   }
00175 #ifdef DEBUG
00176   if (need_swap) {
00177     jlog("Stat: adin_oss: samples need swap\n");
00178   } else {
00179     jlog("Stat: adin_oss: samples need not swap\n");
00180   }
00181 #endif
00182   
00183   if (close(audio_fd) != 0) return FALSE;
00184 
00185   /* re-open for recording */
00186   /* open device */
00187   if ((audio_fd = open(devstr, O_RDONLY)) == -1) {
00188     jlog("Error: adin_oss: failed to open %s", devstr);
00189     return(FALSE);
00190   }
00191 
00192   /* try to set a small fragment size to minimize delay, */
00193   /* although many devices use static fragment size... */
00194   /* (and smaller fragment causes busy buffering) */
00195   {
00196     int arg;
00197     int f, f2;
00198 
00199     /* if environment variable "LATENCY_MSEC" is defined, try to set it
00200        as a minimum latency in msec (will be rouneded to 2^x). */
00201     if ((env = getenv("LATENCY_MSEC")) == NULL) {
00202       frag_msec = MAX_FRAGMENT_MSEC;
00203     } else {
00204       frag_msec = atoi(env);
00205     }
00206       
00207     /* get fragment size from MAX_FRAGMENT_MSEC and MIN_FRAGMENT_SIZE */
00208     f = 0;
00209     f2 = 1;
00210     while (f2 * 1000 / (srate * sizeof(SP16)) <= frag_msec
00211            || f2 < MIN_FRAGMENT_SIZE) {
00212       f++;
00213       f2 *= 2;
00214     }
00215     frag = f - 1;
00216 
00217     /* set to device */
00218     arg = 0x7fff0000 | frag;
00219     if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &arg)) {
00220       jlog("Stat: adin_oss: set fragment size to 2^%d=%d bytes (%d msec)\n", frag, 2 << (frag-1), (2 << (frag-1)) * 1000 / (srate * sizeof(SP16)));
00221     }
00222   }
00223   
00224   /* set format, samplerate, channels */
00225   rfmt = fmt;
00226   if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rfmt) == -1) {
00227     jlog("Error: adin_oss: failed to get available formats from device\n");
00228     return(FALSE);
00229   }
00230   if (rfmt != fmt) {
00231     jlog("Error: adin_oss: 16bit recording is not supported on this device\n");
00232     return FALSE;
00233   }
00234 
00235   {
00236     /* try SNDCTL_DSP_STEREO, SNDCTL_DSP_CHANNELS, monaural, stereo */
00237     int channels;
00238     int stereo;
00239     boolean ok_p = FALSE;
00240 
00241     stereo = 0;                 /* mono */
00242     if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == -1) {
00243       /* failed: SNDCTL_DSP_STEREO not supported */
00244       jlog("Stat: adin_oss: sndctl_dsp_stereo not supported, going to try another...\n");
00245     } else {
00246       if (stereo != 0) {
00247         /* failed to set monaural recording by SNDCTL_DSP_STEREO */
00248         jlog("Stat: adin_oss: failed to set monaural recording by sndctl_dsp_stereo\n");
00249         jlog("Stat: adin_oss: going to try another...\n");
00250       } else {
00251         /* succeeded to set monaural recording by SNDCTL_DSP_STEREO */
00252         //jlog("Stat: adin_oss: recording now set to mono\n");
00253         stereo_rec = FALSE;
00254         ok_p = TRUE;
00255       }
00256     }
00257     if (! ok_p) {               /* not setup yet */
00258       /* try using sndctl_dsp_channels */
00259       channels = 1;
00260       if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels) == -1) {
00261         /* failed: SNDCTL_DSP_CHANNELS not supported */
00262         jlog("Stat: adin_oss: sndctl_dsp_channels not supported, try another...\n");
00263       } else {
00264         if (channels != 1) {
00265           /* failed to set monaural recording by SNDCTL_DSP_CHANNELS */
00266           jlog("Stat: adin_oss: failed to set monaural recording by sndctl_dsp_channels\n");
00267           jlog("Stat: adin_oss: going to try another...\n");
00268         } else {
00269           /* succeeded to set monaural recording by SNDCTL_DSP_CHANNELS */
00270           //jlog("Stat: adin_oss: recording now set to mono\n");
00271           stereo_rec = FALSE;
00272           ok_p = TRUE;
00273         }
00274       }
00275     }
00276     if (! ok_p) {
00277       /* try using stereo input */
00278       jlog("Warning: adin_oss: failed to setup monaural recording, trying to use the left channel of stereo input\n");
00279       stereo = 1;                       /* stereo */
00280       if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == -1) {
00281         /* failed: SNDCTL_DSP_STEREO not supported */
00282         jlog("Stat: adin_oss: failed to set stereo input using sndctl_dsp_stereo\n");
00283       } else {
00284         if (stereo != 1) {
00285           /* failed to set stereo recording by SNDCTL_DSP_STEREO */
00286           jlog("Stat: adin_oss: failed to set stereo input using sndctl_dsp_stereo\n");
00287         } else {
00288           /* succeeded to set stereo recording by SNDCTL_DSP_STEREO */
00289           jlog("Stat: adin_oss: recording now set to stereo, using left channel\n");
00290           stereo_rec = TRUE;
00291           ok_p = TRUE;
00292         }
00293       }
00294     }
00295     if (! ok_p) {               /* not setup yet */
00296       /* try using stereo input with sndctl_dsp_channels */
00297       channels = 2;
00298       if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels) == -1) {
00299         /* failed: SNDCTL_DSP_CHANNELS not supported */
00300         jlog("Stat: adin_oss: failed to set stereo input using sndctl_dsp_channels\n");
00301       } else {
00302         if (channels != 2) {
00303           /* failed to set stereo recording by SNDCTL_DSP_CHANNELS */
00304           jlog("Stat: adin_oss: failed to set stereo input using sndctl_dsp_channels\n");
00305         } else {
00306           /* succeeded to set stereo recording by SNDCTL_DSP_CHANNELS */
00307           jlog("Stat: adin_oss: recording now set to stereo, using left channel\n");
00308           stereo_rec = TRUE;
00309           ok_p = TRUE;
00310         }
00311       }
00312     }
00313     if (! ok_p) {               /* all failed */
00314       jlog("Error: adin_oss: failed to setup recording channels\n");
00315       return FALSE;
00316     }
00317   }
00318 
00319   samplerate = srate;
00320   if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &samplerate) == -1) {
00321     jlog("Erorr: adin_oss: failed to set sample rate to %dHz\n", srate);
00322     return(FALSE);
00323   }
00324   if (samplerate < srate - FREQALLOWRANGE || samplerate > srate + FREQALLOWRANGE) {
00325     jlog("Error: adin_oss: failed to set sampling rate to near %dHz. (%d)\n", srate, samplerate);
00326     return FALSE;
00327   }
00328   if (samplerate != srate) {
00329     jlog("Warning: adin_oss: specified sampling rate was %dHz but set to %dHz, \n", srate, samplerate);
00330   }
00331   jlog("Stat: adin_oss: sampling rate = %dHz\n", samplerate);
00332 
00333   /* get actual fragment size */
00334   if (ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) == -1) {
00335     jlog("Error: adin_oss: failed to get fragment size\n");
00336     return(FALSE);
00337   }
00338   if (env == NULL) {
00339     jlog("Stat: adin_oss: going to set latency to %d msec\n", frag_msec);
00340   } else {
00341     jlog("Stat: adin_oss: going to set latency to %d msec (from env LATENCY_MSEC)\n", frag_msec);
00342   }
00343   jlog("Stat: adin_oss: audio I/O Latency = %d msec (fragment size = %d samples)\n", frag_size * 1000/ (srate * sizeof(SP16)), frag_size / sizeof(SP16));
00344 
00345   return TRUE;
00346 
00347 #endif /* HAS_OSS */
00348 }
00349  
00357 boolean
00358 adin_oss_begin(char *pathname)
00359 {
00360   char buf[4];
00361   char *p;
00362 
00363   /* set device name */
00364   if (pathname != NULL) {
00365     strncpy(devname, pathname, MAXPATHLEN);
00366     jlog("Stat: adin_oss: device name = %s (from argument)\n", devname);
00367   } else if ((p = getenv("AUDIODEV")) != NULL) {
00368     strncpy(devname, p, MAXPATHLEN);
00369     jlog("Stat: adin_oss: device name = %s (from AUDIODEV)\n", devname);
00370   } else {
00371     strncpy(devname, defaultdev, MAXPATHLEN);
00372     jlog("Stat: adin_oss: device name = %s (application default)\n", devname);
00373   }
00374 
00375   /* open the device */
00376   if (adin_oss_open(devname) == FALSE) return FALSE;
00377 
00378   /* Read 1 sample (and ignore it) to tell the audio device start recording.
00379      (If you knows better way, teach me...) */
00380   if (stereo_rec) {
00381     read(audio_fd, buf, 4);
00382   } else {
00383     read(audio_fd, buf, 2);
00384   }
00385   return(TRUE);
00386 }
00387 
00393 boolean
00394 adin_oss_end()
00395 {
00396   if (close(audio_fd) != 0) return FALSE;
00397   return TRUE;
00398 }
00399 
00417 int
00418 adin_oss_read(SP16 *buf, int sampnum)
00419 {
00420 #ifndef HAS_OSS
00421   return -2;
00422 #else
00423   int size,cnt,i;
00424   audio_buf_info info;
00425   fd_set rfds;
00426   struct timeval tv;
00427   int status;
00428 
00429   /* check for incoming samples in device buffer */
00430   /* if there is at least one sample fragment, go next */
00431   /* if not exist, wait for the data to come for at most MAXPOLLINTERVAL msec */
00432   /* if no sample fragment has come in the MAXPOLLINTERVAL period, go next */
00433   FD_ZERO(&rfds);
00434   FD_SET(audio_fd, &rfds);
00435   tv.tv_sec = 0;
00436   tv.tv_usec = MAXPOLLINTERVAL * 1000;
00437   status = select(audio_fd+1, &rfds, NULL, NULL, &tv);
00438   if (status < 0) {
00439     /* select() failed */
00440     jlog("Error: adin_oss: failed to poll device\n");
00441     return(-2);                 /* error */
00442   }
00443   if (FD_ISSET(audio_fd, &rfds)) { /* has some data */
00444     /* get sample num that can be read without blocking */
00445     if (ioctl(audio_fd, SNDCTL_DSP_GETISPACE, &info) == -1) {
00446       jlog("Error: adin_oss: failed to get number of samples in the buffer\n");
00447       return(-2);
00448     }
00449     /* get them as much as possible */
00450     size = sampnum * sizeof(SP16);
00451     if (size > info.bytes) size = info.bytes;
00452     if (size < frag_size) size = frag_size;
00453     size &= ~ 1;                /* Force 16bit alignment */
00454     cnt = read(audio_fd, buf, size);
00455     if ( cnt < 0 ) {
00456       jlog("Error: adin_oss: failed to read samples\n");
00457       return ( -2 );
00458     }
00459     cnt /= sizeof(short);
00460 
00461     if (stereo_rec) {
00462       /* remove R channel */
00463       for(i=1;i<cnt;i+=2) buf[(i-1)/2]=buf[i];
00464       cnt/=2;
00465     }
00466     
00467     if (need_swap) swap_sample_bytes(buf, cnt);
00468   } else {                      /* no data after waiting */
00469     jlog("Warning: adin_oss: no data fragment after %d msec?\n", MAXPOLLINTERVAL);
00470     cnt = 0;
00471   }
00472 
00473   return(cnt);
00474 #endif /* HAS_OSS */
00475 }
00476 
00484 char *
00485 adin_oss_input_name()
00486 {
00487 #ifndef HAS_OSS
00488   return NULL;
00489 #else
00490   return(devname);
00491 #endif
00492 }