📄 adin_mic_linux_oss.c
字号:
/** * @file adin_mic_linux_oss.c * * <JA> * @brief マイク掐蜗 (Linux/OSS) * * マイク掐蜗のための你レベル簇眶ですˉインタフェ〖スとして OSS * サウンドドライバを蝗脱する眷圭·このファイルが蝗脱されますˉ * カ〖ネル筛洁のドライバ·OSS/Linuxのドライバ·および ALSA の * OSS高垂モ〖ドに滦炳していますˉ * * configure でマイクタイプの极瓢冉侍を * 乖なう眷圭∈デフォルト∷·Linux ではこのOSS脱インタフェ〖スが联买されますˉ * 戮のインタフェ〖ス (ALSA, esd, portaudio, spAudio霹) を蝗脱したい眷圭は * configure 箕に "--with-mictype=TYPE" を汤绩弄に回年して布さいˉ * * サウンドカ〖ドが 16bit モノラル で峡不できることが涩寇ですˉ * ただしLinuxでは·ステレオ峡不しかできないデバイスの眷圭· * 焊チャンネルのみを掐蜗として艰り叫して千急することもできますˉ * * JuliusはLinuxではミキサ〖デバイスの肋年を办磊乖いませんˉ峡不デバイスの * 联买∈マイク/ライン∷や峡不ボリュ〖ムの拇泪は xmixer など戮のツ〖ルで * 乖なって布さいˉ * * デフォルトのデバイス叹は "/dev/dsp" ですˉ茨董恃眶 AUDIODEV に * デバイス叹を回年することで·戮のデバイス叹を蝗脱できますˉ * </JA> * <EN> * @brief Microphone input on Linux/OSS * * Low level I/O functions for microphone input on Linux using OSS API. * Works on kernel driver, OSS/Linux, ALSA OSS compatible device, and * other OSS compatible drivers. This will work on kernel 2.0.x and later. * * This OSS API is used by default on Linux machines. If you want * another API (like ALSA native, esd, portaudio, spAudio), please specify * "--with-mictype=TYPE" options at compilation time to configure script. * * Sound card should support 16bit monaural recording. On Linux, however, * if you unfortunately have a device with only stereo recording support, * Julius will try to get input from the left channel of the stereo input. * * Julius does not alter any mixer device setting at all on Linux. You should * configure the mixer for recording source (mic/line) and recording volume * correctly using other audio tool such as xmixer. * * The default device name is "/dev/dsp", which can be changed by setting * environment variable AUDIODEV. * </EN> * * @author Akinobu LEE * @date Sun Feb 13 16:18:26 2005 * * $Revision: 1.5 $ * *//* * Copyright (c) 1991-2007 Kawahara Lab., Kyoto University * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology * Copyright (c) 2005-2007 Julius project team, Nagoya Institute of Technology * All rights reserved */#include <sent/stddefs.h>#include <sent/adin.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/time.h>#include <fcntl.h>/* sound header */#ifdef HAS_OSS#if defined(HAVE_SYS_SOUNDCARD_H)#include <sys/soundcard.h>#elif defined(HAVE_MACHINE_SOUNDCARD_H)#include <machine/soundcard.h>#endif#endif/// Default device name, can be overridden by AUDIODEV environment variable#define DEFAULT_DEVICE "/dev/dsp"#define FREQALLOWRANGE 200 ///< Acceptable width of sampling frequency#define MAXPOLLINTERVAL 300 ///< Read timeout in msec./** * Maximum Data fragment Length in msec. Input can be delayed to this time. * a maximum of 2^x number smaller than this value will be taken. * You can override this value by specifying environment valuable * "LATENCY_MSEC". * */#define MAX_FRAGMENT_MSEC 50/** * Minimum fragment length in bytes * */#define MIN_FRAGMENT_SIZE 256static int audio_fd; ///< Audio descriptorstatic boolean need_swap; ///< Whether samples need byte swapstatic int frag_size; ///< Actual data fragment sizestatic boolean stereo_rec; ///< TRUE if stereo recording (use left only)/** * Device initialization: check device capability and open for recording. * * @param sfreq [in] required sampling frequency. * @param dummy [in] a dummy data * * @return TRUE on success, FALSE on failure. */booleanadin_oss_standby(int sfreq, void *dummy){#ifndef HAS_OSS jlog("Error: OSS not compiled in\n"); return FALSE;#else int fmt, fmt_can, fmt1, fmt2, rfmt; /* sampling format */ int samplerate; /* 16kHz */ char *defaultdev = DEFAULT_DEVICE; /* default device */ char *devname; int frag; int frag_msec; char *env; /* set device name */ if ((devname = getenv("AUDIODEV")) == NULL) { devname = defaultdev; jlog("Stat: adin_oss: device name = %s\n", devname); } else { jlog("Stat: adin_oss: device name obtained from AUDIODEV: %s\n", devname); } /* open device */ if ((audio_fd = open(devname, O_RDONLY|O_NONBLOCK)) == -1) { jlog("Error: adin_oss: failed to open %s\n", devname); return(FALSE); } /* check whether soundcard can record 16bit data */ /* and set fmt */ if (ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt_can) == -1) { jlog("Error: adin_oss: failed to get formats from audio device\n"); return(FALSE); }#ifdef WORDS_BIGENDIAN fmt1 = AFMT_S16_BE; fmt2 = AFMT_S16_LE;#else fmt1 = AFMT_S16_LE; /* 16bit signed (little endian) */ fmt2 = AFMT_S16_BE; /* (big endian) */#endif /* WORDS_BIGENDIAN */ /* fmt2 needs byte swap */ if (fmt_can & fmt1) { fmt = fmt1; need_swap = FALSE; } else if (fmt_can & fmt2) { fmt = fmt2; need_swap = TRUE; } else { jlog("Error: adin_oss: 16bit recording not supported on this device\n"); return FALSE; }#ifdef DEBUG if (need_swap) { jlog("Stat: adin_oss: samples need swap\n"); } else { jlog("Stat: adin_oss: samples need not swap\n"); }#endif if (close(audio_fd) != 0) return FALSE; /* re-open for recording */ /* open device */ if ((audio_fd = open(devname, O_RDONLY)) == -1) { jlog("Error: adin_oss: failed to open %s", devname); return(FALSE); } /* try to set a small fragment size to minimize delay, */ /* although many devices use static fragment size... */ /* (and smaller fragment causes busy buffering) */ { int arg; int f, f2; /* if environment variable "LATENCY_MSEC" is defined, try to set it as a minimum latency in msec (will be rouneded to 2^x). */ if ((env = getenv("LATENCY_MSEC")) == NULL) { frag_msec = MAX_FRAGMENT_MSEC; } else { frag_msec = atoi(env); } /* get fragment size from MAX_FRAGMENT_MSEC and MIN_FRAGMENT_SIZE */ f = 0; f2 = 1; while (f2 * 1000 / (sfreq * sizeof(SP16)) <= frag_msec || f2 < MIN_FRAGMENT_SIZE) { f++; f2 *= 2; } frag = f - 1; /* set to device */ arg = 0x7fff0000 | frag; if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &arg)) { jlog("Stat: adin_oss: set fragment size to 2^%d=%d bytes (%d msec)\n", frag, 2 << (frag-1), (2 << (frag-1)) * 1000 / (sfreq * sizeof(SP16))); } } /* set format, samplerate, channels */ rfmt = fmt; if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rfmt) == -1) { jlog("Error: adin_oss: failed to get available formats from device\n"); return(FALSE); } if (rfmt != fmt) { jlog("Error: adin_oss: 16bit recording is not supported on this device\n"); return FALSE; } { /* try SNDCTL_DSP_STEREO, SNDCTL_DSP_CHANNELS, monaural, stereo */ int channels; int stereo; boolean ok_p = FALSE; stereo = 0; /* mono */ if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == -1) { /* failed: SNDCTL_DSP_STEREO not supported */ jlog("Stat: adin_oss: sndctl_dsp_stereo not supported, going to try another...\n"); } else { if (stereo != 0) { /* failed to set monaural recording by SNDCTL_DSP_STEREO */ jlog("Stat: adin_oss: failed to set monaural recording by sndctl_dsp_stereo\n"); jlog("Stat: adin_oss: going to try another...\n"); } else { /* succeeded to set monaural recording by SNDCTL_DSP_STEREO */ //jlog("Stat: adin_oss: recording now set to mono\n"); stereo_rec = FALSE; ok_p = TRUE; } } if (! ok_p) { /* not setup yet */ /* try using sndctl_dsp_channels */ channels = 1; if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels) == -1) { /* failed: SNDCTL_DSP_CHANNELS not supported */ jlog("Stat: adin_oss: sndctl_dsp_channels not supported, try another...\n"); } else { if (channels != 1) { /* failed to set monaural recording by SNDCTL_DSP_CHANNELS */ jlog("Stat: adin_oss: failed to set monaural recording by sndctl_dsp_channels\n"); jlog("Stat: adin_oss: going to try another...\n"); } else { /* succeeded to set monaural recording by SNDCTL_DSP_CHANNELS */ //jlog("Stat: adin_oss: recording now set to mono\n"); stereo_rec = FALSE; ok_p = TRUE; } } } if (! ok_p) { /* try using stereo input */ jlog("Warning: adin_oss: failed to setup monaural recording, trying to use the left channel of stereo input\n"); stereo = 1; /* stereo */ if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == -1) { /* failed: SNDCTL_DSP_STEREO not supported */ jlog("Stat: adin_oss: failed to set stereo input using sndctl_dsp_stereo\n"); } else { if (stereo != 1) { /* failed to set stereo recording by SNDCTL_DSP_STEREO */ jlog("Stat: adin_oss: failed to set stereo input using sndctl_dsp_stereo\n"); } else { /* succeeded to set stereo recording by SNDCTL_DSP_STEREO */ jlog("Stat: adin_oss: recording now set to stereo, using left channel\n"); stereo_rec = TRUE; ok_p = TRUE; } } } if (! ok_p) { /* not setup yet */ /* try using stereo input with sndctl_dsp_channels */ channels = 2; if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels) == -1) { /* failed: SNDCTL_DSP_CHANNELS not supported */ jlog("Stat: adin_oss: failed to set stereo input using sndctl_dsp_channels\n"); } else { if (channels != 2) { /* failed to set stereo recording by SNDCTL_DSP_CHANNELS */ jlog("Stat: adin_oss: failed to set stereo input using sndctl_dsp_channels\n"); } else { /* succeeded to set stereo recording by SNDCTL_DSP_CHANNELS */ jlog("Stat: adin_oss: recording now set to stereo, using left channel\n"); stereo_rec = TRUE; ok_p = TRUE; } } } if (! ok_p) { /* all failed */ jlog("Error: adin_oss: failed to setup recording channels\n"); return FALSE; } } samplerate = sfreq; if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &samplerate) == -1) { jlog("Erorr: adin_oss: failed to set sample rate to %dHz\n", sfreq); return(FALSE); } if (samplerate < sfreq - FREQALLOWRANGE || samplerate > sfreq + FREQALLOWRANGE) { jlog("Error: adin_oss: failed to set sampling rate to near %dHz. (%d)\n", sfreq, samplerate); return FALSE; } if (samplerate != sfreq) { jlog("Warning: adin_oss: specified sampling rate was %dHz but set to %dHz, \n", sfreq, samplerate); } jlog("Stat: adin_oss: sampling rate = %dHz\n", samplerate); /* get actual fragment size */ if (ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) == -1) { jlog("Error: adin_oss: failed to get fragment size\n"); return(FALSE); } if (env == NULL) { jlog("Stat: adin_oss: going to set latency to %d msec\n", frag_msec); } else { jlog("Stat: adin_oss: going to set latency to %d msec (from env LATENCY_MSEC)\n", frag_msec); } jlog("Stat: adin_oss: audio I/O Latency = %d msec (fragment size = %d samples)\n", frag_size * 1000/ (sfreq * sizeof(SP16)), frag_size / sizeof(SP16)); return TRUE;#endif /* HAS_OSS */} /** * Start recording. * * @return TRUE on success, FALSE on failure. */booleanadin_oss_begin(){ char buf[4]; /* Read 1 sample (and ignore it) to tell the audio device start recording. (If you knows better way, teach me...) */ if (stereo_rec) { read(audio_fd, buf, 4); } else { read(audio_fd, buf, 2); } return(TRUE);}/** * Stop recording. * * @return TRUE on success, FALSE on failure. */booleanadin_oss_end(){ /* * Not reset device on each end of speech, just let the buffer overrun... * Resetting and restarting of recording device sometimes causes * hawling noises at the next recording. * I don't now why, so take the easy way... :-( */ return TRUE;}/** * @brief Read samples from device * * Try to read @a sampnum samples and returns actual number of recorded * samples currently available. This function will block at most * MAXPOLLINTERVAL msec, until at least one sample can be obtained. * If no data has been obtained after waiting for MAXPOLLINTERVAL msec, * returns 0. * * When stereo input, only left channel will be used. * * @param buf [out] samples obtained in this function * @param sampnum [in] wanted number of samples to be read * * @return actural number of read samples, 0 of no sample has been captured in * MAXPOLLINTERVAL msec, -2 if error. */intadin_oss_read(SP16 *buf, int sampnum){#ifndef HAS_OSS return -2;#else int size,cnt,i; audio_buf_info info; fd_set rfds; struct timeval tv; int status; /* check for incoming samples in device buffer */ /* if there is at least one sample fragment, go next */ /* if not exist, wait for the data to come for at most MAXPOLLINTERVAL msec */ /* if no sample fragment has come in the MAXPOLLINTERVAL period, go next */ FD_ZERO(&rfds); FD_SET(audio_fd, &rfds); tv.tv_sec = 0; tv.tv_usec = MAXPOLLINTERVAL * 1000; status = select(audio_fd+1, &rfds, NULL, NULL, &tv); if (status < 0) { /* select() failed */ jlog("Error: adin_oss: failed to poll device\n"); return(-2); /* error */ } if (FD_ISSET(audio_fd, &rfds)) { /* has some data */ /* get sample num that can be read without blocking */ if (ioctl(audio_fd, SNDCTL_DSP_GETISPACE, &info) == -1) { jlog("Error: adin_oss: failed to get number of samples in the buffer\n"); return(-2); } /* get them as much as possible */ size = sampnum * sizeof(SP16); if (size > info.bytes) size = info.bytes; if (size < frag_size) size = frag_size; size &= ~ 1; /* Force 16bit alignment */ cnt = read(audio_fd, buf, size); if ( cnt < 0 ) { jlog("Error: adin_oss: failed to read samples\n"); return ( -2 ); } cnt /= sizeof(short); if (stereo_rec) { /* remove R channel */ for(i=1;i<cnt;i+=2) buf[(i-1)/2]=buf[i]; cnt/=2; } if (need_swap) swap_sample_bytes(buf, cnt); } else { /* no data after waiting */ jlog("Warning: adin_oss: no data fragment after %d msec?\n", MAXPOLLINTERVAL); cnt = 0; } return(cnt);#endif /* HAS_OSS */}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -