📄 adin_mic_linux_alsa.c
字号:
/** * @file adin_mic_linux_alsa.c * @author Akinobu LEE * @date Sun Feb 13 16:18:26 2005 * * <JA> * @brief マイク掐蜗 (Linux/ALSA) * * ALSA API を蝗脱する·マイク掐蜗のための你レベル簇眶ですˉ * 蝗脱には ALSA サウンドドライバ〖がインスト〖ルされていることが涩妥ですˉ * 蝗脱するには configure 箕に "--with-mictype=alsa" を回年して布さいˉ * * サウンドカ〖ドが 16bit モノラル で峡不できることが涩寇ですˉ * * JuliusはLinuxではミキサ〖デバイスの肋年を办磊乖いませんˉ峡不デバイスの * 联买∈マイク/ライン∷や峡不ボリュ〖ムの拇泪は alsamixer など戮のツ〖ルで * 乖なって布さいˉ * * 剩眶サウンドカ〖ドはサポ〖トされていませんˉ剩眶のサウンドカ〖ドが * インスト〖ルされている眷圭·呵介の1つが脱いられますˉ * </JA> * <EN> * @brief Microphone input on Linux/ALSA * * Low level I/O functions for microphone input on Linux using * Advanced Linux Sound Architechture (ALSA) API, developed on version 0.9.x. * If you want to use this API, please specify * "--with-mictype=alsa" options at compilation time to configure script. * * 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 alsamixer. * * Note that sound card should support 16bit monaural recording, and multiple * cards are not supported (in that case the first one will be used). * </EN> * * @sa http://www.alsa-project.org/ * * $Revision: 1.3 $ * *//* * Copyright (c) 1991-2006 Kawahara Lab., Kyoto University * Copyright (c) 2000-2005 Shikano Lab., Nara Institute of Science and Technology * Copyright (c) 2005-2006 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 <fcntl.h>#include <alsa/asoundlib.h>static snd_pcm_t *handle; ///< Audio handlerstatic snd_pcm_hw_params_t *hwparams; ///< Pointer to device hardware parametersstatic char *pcm_name = "hw:0,0"; ///< Name of the PCM devicestatic boolean need_swap; ///< Whether samples need byte swapstatic int latency = 50; ///< Lantency time in msecstatic struct pollfd *ufds; ///< Poll descriptorstatic int count; ///< Poll descriptor count/** * 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_mic_standby(int sfreq, void *dummy){ int err;#if (SND_LIB_MAJOR == 0) int actual_rate; /* sample rate returned by hardware */#else unsigned int actual_rate; /* sample rate returned by hardware */#endif int dir; /* comparison result of exact rate and given rate */ /* allocate hwparam structure */ snd_pcm_hw_params_alloca(&hwparams); /* open device (for resource test, open in non-block mode) */ if ((err = snd_pcm_open(&handle, pcm_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { j_printerr("Error: cannot open PCM device %s (%s)\n", pcm_name, snd_strerror(err)); return(FALSE); } /* set device to non-block mode */ if ((err = snd_pcm_nonblock(handle, 0)) < 0) { j_printerr("Error: cannot set PCM device to block mode\n"); return(FALSE); } /* initialize hwparam structure */ if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0) { j_printerr("Error: cannot initialize PCM device parameter structure (%s)\n", snd_strerror(err)); return(FALSE); } /* set interleaved read/write format */ if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { j_printerr("Error: cannot set PCM device access mode (%s)\n", snd_strerror(err)); return(FALSE); } /* set sample format */#ifdef WORDS_BIGENDIAN /* try big endian, then little endian with byte swap */ if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_BE)) >= 0) { need_swap = FALSE; } else if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE)) >= 0) { need_swap = TRUE; } else { j_printerr("Error: cannot set PCM device format to 16bit-signed (%s)\n", snd_strerror(err)); return(FALSE); }#else /* LITTLE ENDIAN */ /* try little endian, then big endian with byte swap */ if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE)) >= 0) { need_swap = FALSE; } else if ((err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_BE)) >= 0) { need_swap = TRUE; } else { j_printerr("Error: cannot set PCM device format to 16bit-signed (%s)\n", snd_strerror(err)); return(FALSE); }#endif /* set sample rate (if the exact rate is not supported by the hardware, use nearest possible rate */#if (SND_LIB_MAJOR == 0) actual_rate = snd_pcm_hw_params_set_rate_near(handle, hwparams, sfreq, &dir); if (actual_rate < 0) { j_printerr("Error: cannot set PCM device sample rate to %d (%s)\n", sfreq, snd_strerror(actual_rate)); return(FALSE); }#else actual_rate = sfreq; err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &actual_rate, &dir); if (err < 0) { j_printerr("Error: cannot set PCM device sample rate to %d (%s)\n", sfreq, snd_strerror(err)); return(FALSE); }#endif if (actual_rate != sfreq) { j_printerr("Warning: the rate %d Hz is not supported by your PCM hardware.\n ==> Using %d Hz instead.\n", sfreq, actual_rate); } /* set number of channels */ if ((err = snd_pcm_hw_params_set_channels(handle, hwparams, 1)) < 0) { j_printerr("Error: cannot set PCM monoral channel (%s)\n", snd_strerror(err)); return(FALSE); } /* set period size */ {#if (SND_LIB_MAJOR == 0) int periodsize; /* period size (bytes) */ int actual_size; int maxsize, minsize;#else snd_pcm_uframes_t periodsize; /* period size (bytes) */ snd_pcm_uframes_t actual_size; snd_pcm_uframes_t maxsize, minsize;#endif /* get hardware max/min size */ dir = 0;#if (SND_LIB_MAJOR == 0) if ((maxsize = snd_pcm_hw_params_get_period_size_max(hwparams, &dir)) < 0) { j_printerr("Error: cannot get maximum period size\n"); return(FALSE); } if ((minsize = snd_pcm_hw_params_get_period_size_min(hwparams, &dir)) < 0) { j_printerr("Error: cannot get minimum period size\n"); return(FALSE); }#else if ((err = snd_pcm_hw_params_get_period_size_max(hwparams, &maxsize, &dir)) < 0) { j_printerr("Error: cannot get maximum period size\n"); return(FALSE); } if ((err = snd_pcm_hw_params_get_period_size_min(hwparams, &minsize, &dir)) < 0) { j_printerr("Error: cannot get minimum period size\n"); return(FALSE); }#endif /* set apropriate period size */ periodsize = actual_rate * latency / 1000 * sizeof(SP16); if (periodsize < minsize) { j_printerr("Warning: PCM latency of %d ms (%d bytes) too small, use device minimum %d bytes\n", latency, periodsize, minsize); periodsize = minsize; } else if (periodsize > maxsize) { j_printerr("Warning: PCM latency of %d ms (%d bytes) too large, use device maximum %d bytes\n", latency, periodsize, maxsize); periodsize = maxsize; } /* set size (near value will be used) */#if (SND_LIB_MAJOR == 0) actual_size = snd_pcm_hw_params_set_period_size_near(handle, hwparams, periodsize, &dir); if (actual_size < 0) { j_printerr("Error: cannot set PCM record period size to %d (%s)\n", periodsize, snd_strerror(actual_size)); return(FALSE); }#else actual_size = periodsize; err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &actual_size, &dir); if (err < 0) { j_printerr("Error: cannot set PCM record period size to %d (%s)\n", periodsize, snd_strerror(err)); return(FALSE); }#endif if (actual_size != periodsize) { j_printerr("Warning: PCM period size: %d bytes (%d ms) -> %d bytes\n", periodsize, latency, actual_size); } /* set number of periods ( = 2) */ if ((err = snd_pcm_hw_params_set_periods(handle, hwparams, 2, 0)) < 0) { j_printerr("Error: cannot set PCM number of periods to %d (%s)\n", 1, snd_strerror(err)); return(FALSE); } } /* apply the configuration to the PCM device */ if ((err = snd_pcm_hw_params(handle, hwparams)) < 0) { j_printerr("Error: cannot set PCM hardware parameters (%s)\n", snd_strerror(err)); return(FALSE); } /* prepare for recording */ if ((err = snd_pcm_prepare(handle)) < 0) { j_printerr("Error: cannot prepare audio interface (%s)\n", snd_strerror(err)); } /* prepare for polling */ count = snd_pcm_poll_descriptors_count(handle); if (count <= 0) { j_printerr("Error: invalid PCM poll descriptors count\n"); return(FALSE); } ufds = mymalloc(sizeof(struct pollfd) * count); if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) { j_printerr("Error: unable to obtain poll descriptors for PCM recording (%s)\n", snd_strerror(err)); return(FALSE); } return(TRUE);}/** * Error recovery when PCM buffer underrun or suspend. * * @param handle [in] audio handler * @param err [in] error code * * @return 0 on success, otherwise the given errno. */static intxrun_recovery(snd_pcm_t *handle, int err){ if (err == -EPIPE) { /* under-run */ err = snd_pcm_prepare(handle); if (err < 0) j_printerr("Can't recovery from PCM buffer underrun, prepare failed: %s\n", snd_strerror(err)); return 0; } else if (err == -ESTRPIPE) { while ((err = snd_pcm_resume(handle)) == -EAGAIN) sleep(1); /* wait until the suspend flag is released */ if (err < 0) { err = snd_pcm_prepare(handle); if (err < 0) j_printerr("Can't recovery from PCM buffer suspend, prepare failed: %s\n", snd_strerror(err)); } return 0; } return err;}/** * Start recording. * * @return TRUE on success, FALSE on failure. */booleanadin_mic_start(){ int err; snd_pcm_state_t status; /* check hardware status */ while(1) { /* wait till prepared */ status = snd_pcm_state(handle); switch(status) { case SND_PCM_STATE_PREPARED: /* prepared for operation */ if ((err = snd_pcm_start(handle)) < 0) { j_printerr("Error: cannot start PCM (%s)\n", snd_strerror(err)); return (FALSE); } return(TRUE); break; case SND_PCM_STATE_RUNNING: /* capturing the samples of other application */ if ((err = snd_pcm_drop(handle)) < 0) { /* discard the existing samples */ j_printerr("Error: cannot drop PCM (%s)\n", snd_strerror(err)); return (FALSE); } break; case SND_PCM_STATE_XRUN: /* buffer overrun */ if ((err = xrun_recovery(handle, -EPIPE)) < 0) { j_printerr("Error: PCM XRUN recovery failed (%s)\n", snd_strerror(err)); return(FALSE); } break; case SND_PCM_STATE_SUSPENDED: /* suspended by power management system */ if ((err = xrun_recovery(handle, -ESTRPIPE)) < 0) { j_printerr("Error: PCM XRUN recovery failed (%s)\n", snd_strerror(err)); return(FALSE); } break; } } return(TRUE);} /** * Stop recording. * * @return TRUE on success, FALSE on failure. */booleanadin_mic_stop(){ 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 until * at least one sample can be obtained. * * @param buf [out] samples obtained in this function * @param sampnum [in] wanted number of samples to be read * * @return actural number of read samples, -2 if an error occured. */intadin_mic_read(SP16 *buf, int sampnum){ int cnt; snd_pcm_sframes_t avail; while ((avail = snd_pcm_avail_update(handle)) <= 0) { usleep(latency * 1000); } if (avail < sampnum) { cnt = snd_pcm_readi(handle, buf, avail); } else { cnt = snd_pcm_readi(handle, buf, sampnum); } if (cnt < 0) { j_printerr("Error: PCM read failed (%s)\n", snd_strerror(cnt)); return(-2); } if (need_swap) { swap_sample_bytes(buf, cnt); } return(cnt);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -