最近调试板子上的3个ADAU1761音频接口,使用pocketsphinx语音库调用alsa库的API接口打开指定的Mic进行录入语音时出现异常,默认情况只能打开第一个ADAU1761音频接口,想打开第二个或第三个ADAU1761音频接口时却出现错误,本文记录一下调试的过程。
移植alsa lib 和alsa utils库后(移植教程见:传送门)可使用以下指令查看声卡设备和PCM设备,具体如下:
root@newboard_project:/# aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: ADAU1761 [ZED ADAU1761], device 0: 43c00000.axi-i2s-adau-hifi adau-hifi-0 []
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: ADAU1761_1 [ZED ADAU1761], device 0: 43c10000.axi-i2s-adau-hifi adau-hifi-0 []
Subdevices: 1/1
Subdevice #0: subdevice #0
card 2: ADAU1761_2 [ZED ADAU1761], device 0: 43c20000.axi-i2s-adau-hifi adau-hifi-0 []
Subdevices: 1/1
Subdevice #0: subdevice #0
root@newboard_project:/# arecord -l
**** List of CAPTURE Hardware Devices ****
card 0: ADAU1761 [ZED ADAU1761], device 0: 43c00000.axi-i2s-adau-hifi adau-hifi-0 []
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: ADAU1761_1 [ZED ADAU1761], device 0: 43c10000.axi-i2s-adau-hifi adau-hifi-0 []
Subdevices: 1/1
Subdevice #0: subdevice #0
card 2: ADAU1761_2 [ZED ADAU1761], device 0: 43c20000.axi-i2s-adau-hifi adau-hifi-0 []
Subdevices: 1/1
Subdevice #0: subdevice #0
root@newboard_project:/# ls -l /dev/snd
crw-rw---- 1 root root 116, 0 Jan 1 00:00 controlC0
crw-rw---- 1 root root 116, 32 Jan 1 00:00 controlC1
crw-rw---- 1 root root 116, 64 Jan 1 00:00 controlC2
crw-rw---- 1 root root 116, 24 Jan 1 00:00 pcmC0D0c
crw-rw---- 1 root root 116, 16 Jan 1 00:00 pcmC0D0p
crw-rw---- 1 root root 116, 56 Jan 1 00:00 pcmC1D0c
crw-rw---- 1 root root 116, 48 Jan 1 00:00 pcmC1D0p
crw-rw---- 1 root root 116, 88 Jan 1 00:00 pcmC2D0c
crw-rw---- 1 root root 116, 80 Jan 1 00:00 pcmC2D0p
crw-rw---- 1 root root 116, 33 Jan 1 00:00 timer
如上,共有三个声卡设备:
card 0: ADAU1761
card 1: ADAU1761_1
card 2: ADAU1761_2
在使用aplay和arecord指令进行播放和录音时可正常选择其中某一个声卡,如:
//使用ADAU1761_1录音
root@newboard_project:/# arecord -D hw:ADAU1761_1 -fS32_LE -d10 -c2 -r48000 s32_
le.wav
Recording WAVE 's32_le.wav' : Signed 32 bit Little Endian, Rate 48000 Hz, Stereo
//使用ADAU1761播放
root@newboard_project:/# aplay -D hw:ADAU1761 -fS32_LE -c2 -r48000 s32_le.wav
Playing WAVE 's32_le.wav' : Signed 32 bit Little Endian, Rate 48000 Hz, Stereo
移植完pocketsphinx库后(见教程:传送门),调用pocketsphinx_continuous指令打开MIC进行语音识别,默认可打开ADAU1761设备,而打开其他两个声卡出现异常,报错:
Failed to set PCM device to 16-bit signed PCM: Invalid argument
FATAL_ERROR: “continuous.c”, line 246: Failed to open audio device
查看pocketsphinx-0.8\src\programs\continuous.c 中源码,为ad_open_dev() API接口打开设备失败。
/*
* Main utterance processing loop:
* for (;;) {
* wait for start of next utterance;
* decode utterance until silence of at least 1 sec observed;
* print utterance result;
* }
*/
static void
recognize_from_microphone()
{
ad_rec_t *ad;
int16 adbuf[4096];
int32 k, ts, rem;
char const *hyp;
char const *uttid;
cont_ad_t *cont;
char word[256];
if ((ad = ad_open_dev(cmd_ln_str_r(config, "-adcdev"),
(int)cmd_ln_float32_r(config, "-samprate"))) == NULL)
E_FATAL("Failed to open audio device\n");
//。。。。。。。省略
}
接着查看ad_open_dev函数(\sphinxbase-0.8\src\libsphinxad\ad_alsa.c文件中),里面调用snd_pcm_open函数打开PCM设备,若输入的dev是NULL,则使用DEFAULT_DEVICE设备。
ad_rec_t *
ad_open_dev(const char *dev, int32 sps)
{
ad_rec_t *handle;
snd_pcm_t *dspH;
int err;
if (dev == NULL)
dev = DEFAULT_DEVICE;
err = snd_pcm_open(&dspH, dev, SND_PCM_STREAM_CAPTURE, 0);
if (err < 0) {
fprintf(stderr,
"Error opening audio device %s for capture: %s\n",
dev, snd_strerror(err));
return NULL;
}
if (setparams(sps, dspH) < 0) {
return NULL;
}
if (setlevels(dev) < 0) {
return NULL;
}
if ((handle = (ad_rec_t *) calloc(1, sizeof(ad_rec_t))) == NULL) {
fprintf(stderr, "calloc(%d) failed\n", (int)sizeof(ad_rec_t));
abort();
}
handle->dspH = dspH;
handle->recording = 0;
handle->sps = sps;
handle->bps = sizeof(int16);
return (handle);
}
DEFAULT_DEVICE的定义:
#define DEFAULT_DEVICE "default"
snd_pcm_open函数的使用介绍见:传送门,查看库的使用介绍,name参数的使用例程,设备名称格式分别使用hw:0,0和plughw:0,0。
默认情况下打开的设备是card 0,默认情况下加载/usr/local/alsa/alsa.conf文件,该文件中有默认打开的声卡参数:
# defaults
#
# show all name hints also for definitions without hint {} section
defaults.namehint.showall off
# show just basic name hints
defaults.namehint.basic on
# show extended name hints
defaults.namehint.extended off
#
defaults.ctl.card 0 //默认声卡0
defaults.pcm.card 0 //默认声卡0
defaults.pcm.device 0 //默认设备0
defaults.pcm.subdevice -1
defaults.pcm.nonblock 1
defaults.pcm.compat 0
defaults.pcm.minperiodtime 5000 # in us
defaults.pcm.ipc_key 5678293
defaults.pcm.ipc_gid audio
defaults.pcm.ipc_perm 0660
defaults.pcm.dmix.max_periods 0
defaults.pcm.dmix.rate 48000
defaults.pcm.dmix.format "unchanged"
defaults.pcm.dmix.card defaults.pcm.card
defaults.pcm.dmix.device defaults.pcm.device
defaults.pcm.dsnoop.card defaults.pcm.card
defaults.pcm.dsnoop.device defaults.pcm.device
defaults.pcm.front.card defaults.pcm.card
defaults.pcm.front.device defaults.pcm.device
defaults.pcm.rear.card defaults.pcm.card
defaults.pcm.rear.device defaults.pcm.device
defaults.pcm.center_lfe.card defaults.pcm.card
defaults.pcm.center_lfe.device defaults.pcm.device
defaults.pcm.side.card defaults.pcm.card
defaults.pcm.side.device defaults.pcm.device
defaults.pcm.surround21.card defaults.pcm.card
defaults.pcm.surround21.device defaults.pcm.device
defaults.pcm.surround40.card defaults.pcm.card
defaults.pcm.surround40.device defaults.pcm.device
defaults.pcm.surround41.card defaults.pcm.card
defaults.pcm.surround41.device defaults.pcm.device
defaults.pcm.surround50.card defaults.pcm.card
defaults.pcm.surround50.device defaults.pcm.device
defaults.pcm.surround51.card defaults.pcm.card
defaults.pcm.surround51.device defaults.pcm.device
defaults.pcm.surround71.card defaults.pcm.card
defaults.pcm.surround71.device defaults.pcm.device
defaults.pcm.iec958.card defaults.pcm.card
defaults.pcm.iec958.device defaults.pcm.device
defaults.pcm.modem.card defaults.pcm.card
defaults.pcm.modem.device defaults.pcm.device
# truncate files via file or tee PCM
defaults.pcm.file_format "raw"
defaults.pcm.file_truncate true
defaults.rawmidi.card 0
defaults.rawmidi.device 0
defaults.rawmidi.subdevice -1
defaults.hwdep.card 0
defaults.hwdep.device 0
defaults.timer.class 2
defaults.timer.sclass 0
defaults.timer.card 0
defaults.timer.device 0
defaults.timer.subdevice 0
若要打开对应的声卡,修改上述参数即可实现加载对应的声卡设备。
查看ALSA库的参数资料,其API使用的是逻辑设备名,如下:
API库使用逻辑设备名而不是设备文件。设备名字可以是真实的硬件名字也可以是插件名字。硬件名字使用hw:i,j这样的格式。其中i是卡号,j是这块声卡上的设备号。第一个声音设备是hw:0 , 0.这个别名默认引用第一块声音设备并且在本文示例中一真会被用到。插件使用另外的唯一名字,比如 plughw:0 , 0:,表示一个插件,这个插件不提供对硬件设备的访问,而是提供像采样率转换这样的软件特性,硬件本身并不支持这样的特性。
ALSA库的资料见:传送门,逻辑设备名的使用例程见:
使用* -adcdev plughw:1,0*参数打开对应声卡,如下打开声卡1:
pocketsphinx_continuous -adcdev plughw:1,0 -hmm tdt_sc_8k/ -lm 6690.lm -dict 6690.dic
成功打开声卡1,连接MIC说话,语音识别结果如下:
使用hw:0 , 0参数打开对应声卡,弹出无效参数的错误:
pocketsphinx_continuous -adcdev hw:1,0 -hmm tdt_sc_8k/ -lm 6690.lm -dict 6690.dic
对比alsa.conf文件中hw和plughw中参数结构如下,具体使用-adcdev hw:1,0打开声卡失败的原因还未查到。
使用pocketsphinx语音库调用alsa库的API接口打开指定的声卡设备主要以下两种方法:
(1)修改alsa.conf文件中的默认声卡参数;(不推荐)
(2)使用* -adcdev plughw:n,0*参数打开对应的声卡n,例如:
pocketsphinx_continuous -adcdev plughw:1,0 -hmm tdt_sc_8k/ -lm 6690.lm -dict 6690.dic