Julius 4.2
libsent/src/adin/adin_portaudio.c
説明を見る。
00001 
00062 /*
00063  * Copyright (c) 2004-2005 Shikano Lab., Nara Institute of Science and Technology
00064  * Copyright (c) 2005-2011 Julius project team, Nagoya Institute of Technology
00065  * All rights reserved
00066  */
00067 
00068 #include <sent/stddefs.h>
00069 #include <sent/speech.h>
00070 #include <sent/adin.h>
00071 
00072 /* sound header */
00073 #include <portaudio.h>
00074 
00075 #ifndef paNonInterleaved
00076 #define OLDVER
00077 #endif
00078 
00079 #undef DDEBUG
00080 
00094 #ifndef OLDVER
00095 #define CHOOSE_HOST_API
00096 #endif
00097 
00107 #ifdef OLDVER
00108 #define MAX_FRAGMENT_MSEC 128
00109 #endif
00110 
00111 /* temporal buffer */
00112 static SP16 *speech;            
00113 static int current;             
00114 static int processed;           
00115 static boolean buffer_overflowed = FALSE; 
00116 static int cycle_buffer_len;    
00117 
00130 static int
00131 #ifdef OLDVER
00132 Callback(void *inbuf, void *outbuf, unsigned long len, PaTimestamp outTime, void *userdata)
00133 #else
00134 Callback(const void *inbuf, void *outbuf, unsigned long len, const PaStreamCallbackTimeInfo *outTime, PaStreamCallbackFlags statusFlags, void *userdata)
00135 #endif
00136 {
00137 #ifdef OLDVER
00138   SP16 *now;
00139   int avail;
00140 #else
00141   const SP16 *now;
00142   unsigned long avail;
00143 #endif
00144   int processed_local;
00145   int written;
00146 
00147   now = inbuf;
00148 
00149   processed_local = processed;
00150 
00151 #ifdef DDEBUG
00152   printf("callback-1: processed=%d, current=%d: recordlen=%d\n", processed_local, current, len);
00153 #endif
00154 
00155   /* check overflow */
00156   if (processed_local > current) {
00157     avail = processed_local - current;
00158   } else {
00159     avail = cycle_buffer_len + processed_local - current;
00160   }
00161   if (len > avail) {
00162 #ifdef DDEBUG
00163     printf("callback-*: buffer overflow!\n");
00164 #endif
00165     buffer_overflowed = TRUE;
00166     len = avail;
00167   }
00168 
00169   /* store to buffer */
00170   if (current + len <= cycle_buffer_len) {
00171     memcpy(&(speech[current]), now, len * sizeof(SP16));
00172 #ifdef DDEBUG
00173     printf("callback-2: [%d..%d] %d samples written\n", current, current+len, len);
00174 #endif
00175   } else {
00176     written = cycle_buffer_len - current;
00177     memcpy(&(speech[current]), now, written * sizeof(SP16));
00178 #ifdef DDEBUG
00179     printf("callback-2-1: [%d..%d] %d samples written\n", current, current+written, written);
00180 #endif
00181     memcpy(&(speech[0]), &(now[written]), (len - written) * sizeof(SP16));
00182 #ifdef DDEBUG
00183     printf("callback-2-2: ->[%d..%d] %d samples written (total %d samples)\n", 0, len-written, len-written, len);
00184 #endif
00185   }
00186   current += len;
00187   if (current >= cycle_buffer_len) current -= cycle_buffer_len;
00188 #ifdef DDEBUG
00189   printf("callback-3: new current: %d\n", current);
00190 #endif
00191 
00192   return(0);
00193 }
00194 
00195 #ifdef OLDVER
00196 static PortAudioStream *stream = NULL;          
00197 #else
00198 static PaStream *stream = NULL;         
00199 #endif
00200 static int srate;               
00201 
00202 
00203 #ifdef CHOOSE_HOST_API
00204 
00205 // Get device list
00206 // If first argument is NULL, return the maximum number of devices
00207 int
00208 get_device_list(int *devidlist, char **namelist, int maxstrlen, int maxnum)
00209 {
00210   PaDeviceIndex numDevice = Pa_GetDeviceCount(), i;
00211   const PaDeviceInfo *deviceInfo;
00212   const PaHostApiInfo *apiInfo;
00213   static char buf[256];
00214   int n;
00215 
00216   n = 0;
00217   for(i=0;i<numDevice;i++) {
00218     deviceInfo = Pa_GetDeviceInfo(i);
00219     if (!deviceInfo) continue;
00220     if (deviceInfo->maxInputChannels <= 0) continue;
00221     apiInfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
00222     if (!apiInfo) continue;
00223     if (devidlist != NULL) {
00224       if ( n >= maxnum ) break;
00225       snprintf(buf, 255, "%s: %s", apiInfo->name, deviceInfo->name);
00226       buf[255] = '\0';
00227       devidlist[n] = i;
00228       strncpy(namelist[n], buf, maxstrlen);
00229     }
00230     n++;
00231   }
00232   
00233   return n;
00234 }
00235 
00236 // Automatically choose a device to open
00237 // 
00238 // 1. if the env value of "PORTAUDIO_DEV" is defined and matches any of 
00239 //    "apiInfo->name: deviceInfo->name" string, use it.
00240 // 2. if the env value "PORTAUDIO_DEV_NUM" is defined, use the (value-1)
00241 //    as device id.
00242 // 3. if not yet, default device will be chosen:
00243 // 3.1. in WIN32 environment, search for supported and available API in
00244 //      the following order: WASAPI, ASIO, DirectSound, MME.
00245 //      Note that only the APIs supported by the linked PortAudio
00246 //      library are available.
00247 // 3.2  in other environment, use the default input device.
00248 //
00249 // store the device id to devId_ret and returns 0.
00250 // if devId_ret is -1, tell caller to use default.
00251 // returns -1 on error.
00252 static int
00253 auto_determine_device(int *devId_ret)
00254 {
00255   int devId;
00256   PaDeviceIndex numDevice = Pa_GetDeviceCount(), i;
00257   const PaDeviceInfo *deviceInfo;
00258   const PaHostApiInfo *apiInfo;
00259   char *devname;
00260   static char buf[256];
00261 #if defined(_WIN32) || defined(__CYGWIN__)
00262   // at win32, force preference order: iWASAPI > ASIO > DirectSound > MME > Other
00263   int iMME = -1, iDS = -1, iASIO = -1, iWASAPI = -1;
00264 #endif
00265 
00266   // if PORTAUDIO_DEV is specified, match it against available APIs
00267   devname = getenv("PORTAUDIO_DEV");
00268   devId = -1;
00269 
00270   // get list of available capture devices
00271   jlog("Stat: adin_portaudio: sound capture devices:\n");
00272   for(i=0;i<numDevice;i++) {
00273     deviceInfo = Pa_GetDeviceInfo(i);
00274     if (!deviceInfo) continue;
00275     if (deviceInfo->maxInputChannels <= 0) continue;
00276     apiInfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
00277     if (!apiInfo) continue;
00278     snprintf(buf, 255, "%s: %s", apiInfo->name, deviceInfo->name);
00279     buf[255] = '\0';
00280     jlog("  %d [%s]\n", i+1, buf);
00281     if (devname && !strncmp(devname, buf, strlen(devname))) {
00282       // device name (partially) matches PORTAUDIO_DEV
00283       devId = i;
00284     }
00285 #if defined(_WIN32) || defined(__CYGWIN__)
00286     // store the device ID for each API
00287     switch(apiInfo->type) {
00288     case paWASAPI: if (iWASAPI < 0) iWASAPI = i; break;
00289     case paMME:if (iMME   < 0) iMME   = i; break;
00290     case paDirectSound:if (iDS    < 0) iDS    = i; break;
00291     case paASIO:if (iASIO  < 0) iASIO  = i; break;
00292     }
00293 #endif
00294   }
00295   if (devname) {
00296     if (devId == -1) {
00297       jlog("Error: adin_portaudio: PORTAUDIO_DEV=\"%s\", but no device matches it\n", devname);
00298       return -1;
00299     }
00300     jlog("  --> #%d matches PORTAUDIO_DEV, use it\n", devId + 1);
00301     *devId_ret = devId;
00302     return 0;
00303   }
00304   if (getenv("PORTAUDIO_DEV_NUM")) {
00305     devId = atoi(getenv("PORTAUDIO_DEV_NUM")) - 1;
00306     if (devId < 0 || devId >= numDevice) {
00307       jlog("Error: PORTAUDIO_DEV_NUM=%d, but device %d not exist\n", devId+1, devId+1);
00308       return -1;
00309     }
00310     jlog("  --> use device %d, specified by PORTAUDIO_DEV_NUM\n", devId + 1);
00311     *devId_ret = devId;
00312     return 0;
00313   }
00314 #if defined(_WIN32) || defined(__CYGWIN__)
00315   jlog("Stat: adin_portaudio: APIs:");
00316   if (iWASAPI >= 0) jlog(" WASAPI");
00317   if (iASIO >= 0) jlog(" ASIO");
00318   if (iDS >= 0) jlog(" DirectSound");
00319   if (iMME >= 0) jlog(" MME");
00320   jlog("\n");
00321   if (iWASAPI >= 0) {
00322     jlog("Stat: adin_portaudio: -- WASAPI selected\n");
00323     devId = iWASAPI;
00324   } else if (iASIO >= 0) {
00325     jlog("Stat: adin_portaudio: -- ASIO selected\n");
00326     devId = iASIO;
00327   } else if (iDS >= 0) {
00328     jlog("Stat: adin_portaudio: -- DirectSound selected\n");
00329     devId = iDS;
00330   } else if (iMME >= 0) {
00331     jlog("Stat: adin_portaudio: -- MME selected\n");
00332     devId = iMME;
00333   } else {
00334     jlog("Error: adin_portaudio: no device available, try default\n");
00335     devId = -1;
00336   }
00337   *devId_ret = devId;
00338 #else
00339   jlog("Stat: adin_portaudio: use default device\n");
00340   *devId_ret = -1;
00341 #endif
00342   return 0;
00343 }
00344 
00345 #endif
00346 
00355 boolean
00356 adin_mic_standby(int sfreq, void *dummy)
00357 {
00358   /* store required sampling rate for checking after opening device */
00359   srate = sfreq;
00360   return TRUE;
00361 }
00362 
00370 static boolean
00371 adin_mic_open(char *arg)
00372 {
00373   int sfreq = srate;
00374   PaError err;
00375 #ifdef OLDVER
00376   int frames_per_buffer;
00377   int num_buffer;
00378 #endif
00379   int latency;
00380   char *p;
00381   int devId;
00382 
00383   /* set cycle buffer length */
00384   cycle_buffer_len = INPUT_DELAY_SEC * sfreq;
00385   //jlog("Stat: adin_portaudio: INPUT_DELAY_SEC = %d\n", INPUT_DELAY_SEC);
00386   jlog("Stat: adin_portaudio: audio cycle buffer length = %d bytes\n", cycle_buffer_len * sizeof(SP16));
00387 
00388 #ifdef OLDVER
00389   /* for safety... */
00390   if (sizeof(SP16) != paInt16) {
00391     jlog("Error: adin_portaudio: SP16 != paInt16 !!\n");
00392     return FALSE;
00393   }
00394   /* set buffer parameter*/
00395   frames_per_buffer = 256;
00396 #endif
00397 
00398   /* allocate and init */
00399   current = processed = 0;
00400   speech = (SP16 *)mymalloc(sizeof(SP16) * cycle_buffer_len);
00401   buffer_overflowed = FALSE;
00402 
00403 
00404   /* get user-specified latency parameter */
00405   latency = 0;
00406   if ((p = getenv("LATENCY_MSEC")) != NULL) {
00407     latency = atoi(p);
00408     jlog("Stat: adin_portaudio: setting latency to %d msec (obtained from LATENCY_MSEC)\n", latency);
00409   }
00410 #ifdef OLDVER
00411   if (latency == 0) {
00412     latency = MAX_FRAGMENT_MSEC;
00413     jlog("Stat: adin_portaudio: setting latency to %d msec\n", latency);
00414   }
00415   num_buffer = sfreq * latency / (frames_per_buffer * 1000);
00416   jlog("Stat: adin_portaudio: framesPerBuffer=%d, NumBuffers(guess)=%d\n",
00417        frames_per_buffer, num_buffer);
00418   jlog("Stat: adin_portaudio: audio I/O Latency = %d msec (data fragment = %d frames)\n",
00419            (frames_per_buffer * num_buffer) * 1000 / sfreq, 
00420            (frames_per_buffer * num_buffer));
00421 #endif
00422 
00423   /* initialize device */
00424   err = Pa_Initialize();
00425   if (err != paNoError) {
00426     jlog("Error: adin_portaudio: failed to initialize: %s\n", Pa_GetErrorText(err));
00427     return(FALSE);
00428   }
00429 
00430 #ifdef CHOOSE_HOST_API
00431 
00432   if (arg == NULL) {
00433     // first try to determine the best device
00434     if (auto_determine_device(&devId) == -1) {
00435       jlog("Error: adin_portaudio: failed to choose the specified device\n");
00436       return(FALSE);
00437     }
00438     if (devId == -1) {
00439       // No device has been determined, use the default input device
00440       devId = Pa_GetDefaultInputDevice();
00441       if (devId == paNoDevice) {
00442         jlog("Error: adin_portaudio: no default input device is available or an error was encountered\n");
00443         return FALSE;
00444       }
00445     }
00446   } else {
00447     // use the given number as device id
00448     devId = atoi(arg);
00449   }
00450   // output device information to use
00451   {
00452     const PaDeviceInfo *deviceInfo;
00453     const PaHostApiInfo *apiInfo;
00454     static char buf[256];
00455     deviceInfo = Pa_GetDeviceInfo(devId);
00456     if (deviceInfo == NULL) {
00457       jlog("Error: adin_portaudio: failed to get info for device id %d\n", devId);
00458       return FALSE;
00459     }
00460     apiInfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
00461     if (apiInfo == NULL) {
00462       jlog("Error: adin_portaudio: failed to get API info for device id %d\n", devId);
00463       return FALSE;
00464     }
00465     snprintf(buf, 255, "%s: %s", apiInfo->name, deviceInfo->name);
00466     buf[255] = '\0';
00467     jlog("Stat: adin_portaudio: [%s]\n", buf);
00468     jlog("Stat: adin_portaudio: (you can specify device by \"PORTAUDIO_DEV_NUM=number\"\n");
00469   }
00470   
00471   // open the device
00472   {
00473     PaStreamParameters param;
00474     memset( &param, 0, sizeof(param));
00475     param.channelCount = 1;
00476     param.device = devId;
00477     param.sampleFormat = paInt16;
00478     if (latency == 0) {
00479       param.suggestedLatency = Pa_GetDeviceInfo(devId)->defaultLowInputLatency;
00480       jlog("Stat: adin_portaudio: try to set default low latency from portaudio: %d msec\n", param.suggestedLatency * 1000.0);
00481     } else {
00482       param.suggestedLatency = latency / 1000.0;
00483       jlog("Stat: adin_portaudio: try to set latency to %d msec\n", param.suggestedLatency * 1000.0);
00484     }
00485     err = Pa_OpenStream(&stream, &param, NULL, sfreq, 
00486                         0, paNoFlag,
00487                         Callback, NULL);
00488     if (err != paNoError) {
00489       jlog("Error: adin_portaudio: error in opening stream: %s\n", Pa_GetErrorText(err));
00490       return(FALSE);
00491     }
00492   }
00493   {
00494     const PaStreamInfo *stinfo;
00495     stinfo = Pa_GetStreamInfo(stream);
00496     jlog("Stat: adin_portaudio: latency was set to %f msec\n", stinfo->inputLatency * 1000.0);
00497   }
00498 
00499 #else
00500 
00501   // Just open the default stream
00502   err = Pa_OpenDefaultStream(&stream, 1, 0, paInt16, sfreq, 
00503 #ifdef OLDVER
00504                              frames_per_buffer,
00505                              num_buffer, 
00506 #else
00507                                  0,
00508 #endif
00509                              Callback, NULL);
00510   if (err != paNoError) {
00511     jlog("Error: adin_portaudio: error in opening stream: %s\n", Pa_GetErrorText(err));
00512     return(FALSE);
00513   }
00514 
00515 #endif /* CHOOSE_HOST_API */
00516 
00517   return(TRUE);
00518 }
00519 
00527 boolean
00528 adin_mic_begin(char *arg)
00529 {
00530   PaError err;
00531 
00532   /* initialize device and open stream */
00533   if (adin_mic_open(arg) == FALSE) {
00534     stream = NULL;
00535     return(FALSE);
00536   }
00537   /* start stream */
00538   err = Pa_StartStream(stream);
00539   if (err != paNoError) {
00540     jlog("Error: adin_portaudio: failed to begin stream: %s\n", Pa_GetErrorText(err));
00541     stream = NULL;
00542     return(FALSE);
00543   }
00544   
00545   return(TRUE);
00546 }
00547 
00553 boolean
00554 adin_mic_end()
00555 {
00556   PaError err;
00557 
00558   if (stream == NULL) return(TRUE);
00559 
00560   /* stop stream (do not wait callback and buffer flush, stop immediately) */
00561   err = Pa_AbortStream(stream);
00562   if (err != paNoError) {
00563     jlog("Error: adin_portaudio: failed to stop stream: %s\n", Pa_GetErrorText(err));
00564     return(FALSE);
00565   }
00566   /* close stream */
00567   err = Pa_CloseStream(stream);
00568   if (err != paNoError) {
00569     jlog("Error: adin_portaudio: failed to close stream: %s\n", Pa_GetErrorText(err));
00570     return(FALSE);
00571   }
00572   /* terminate library */
00573   err = Pa_Terminate();
00574   if (err != paNoError) {
00575     jlog("Error: adin_portaudio: failed to terminate library: %s\n", Pa_GetErrorText(err));
00576     return(FALSE);
00577   }
00578   
00579   stream = NULL;
00580 
00581   return TRUE;
00582 }
00583 
00596 int
00597 adin_mic_read(SP16 *buf, int sampnum)
00598 {
00599   int current_local;
00600   int avail;
00601   int len;
00602 
00603   if (buffer_overflowed) {
00604     jlog("Error: adin_portaudio: input buffer OVERFLOW, increase INPUT_DELAY_SEC in sent/speech.h\n");
00605     buffer_overflowed = FALSE;
00606   }
00607 
00608   while (current == processed) {
00609 #ifdef DDEBUG
00610     printf("process  : current == processed: %d: wait\n", current);
00611 #endif
00612     Pa_Sleep(20); /* wait till some input comes */
00613     if (stream == NULL) return(-1);
00614   }
00615 
00616   current_local = current;
00617 
00618 #ifdef DDEBUG
00619   printf("process-1: processed=%d, current=%d\n", processed, current_local);
00620 #endif
00621 
00622   if (processed < current_local) {
00623     avail = current_local - processed;
00624     if (avail > sampnum) avail = sampnum;
00625     memcpy(buf, &(speech[processed]), avail * sizeof(SP16));
00626 #ifdef DDEBUG
00627     printf("process-2: [%d..%d] %d samples read\n", processed, processed+avail, avail);
00628 #endif
00629     len = avail;
00630     processed += avail;
00631   } else {
00632     avail = cycle_buffer_len - processed;
00633     if (avail > sampnum) avail = sampnum;
00634     memcpy(buf, &(speech[processed]), avail * sizeof(SP16));
00635 #ifdef DDEBUG
00636     printf("process-2-1: [%d..%d] %d samples read\n", processed, processed+avail, avail);
00637 #endif
00638     len = avail;
00639     processed += avail;
00640     if (processed >= cycle_buffer_len) processed -= cycle_buffer_len;
00641     if (sampnum - avail > 0) {
00642       if (sampnum - avail < current_local) {
00643         avail = sampnum - avail;
00644       } else {
00645         avail = current_local;
00646       }
00647       if (avail > 0) {
00648         memcpy(&(buf[len]), &(speech[0]), avail * sizeof(SP16));
00649 #ifdef DDEBUG
00650         printf("process-2-2: [%d..%d] %d samples read (total %d)\n", 0, avail, avail, len + avail);
00651 #endif
00652         len += avail;
00653         processed += avail;
00654         if (processed >= cycle_buffer_len) processed -= cycle_buffer_len;
00655       }
00656     }
00657   }
00658 #ifdef DDEBUG
00659   printf("process-3: new processed: %d\n", processed);
00660 #endif
00661   return len;
00662 }
00663 
00671 char *
00672 adin_mic_input_name()
00673 {
00674   return("Portaudio default device");
00675 }