Speex手册(五)——Speex编解码器API(2)和语音处理API(1)

5.4  模式查询

        Speex模式查询类似于speex_encoder_ctl和speex_decoder_ctl函数调用。因为模式是只读的,所以仅能获取一个特别模式的信息。函数调用如下:

                void speex_mode_query(Speexmode *mode, int request, void *ptr);

        其中request可允许的值有(除非特别声明,这些值通过指针ptr返回):

                SPEEX_MODE_FRAME_SIZE  获取模式的帧大小(样本数)

                SPEEX_SUBMODE_BITRATE  通过ptr获取子模式比特率(整数,单位bps)

5.5  封包和带内信号

        有时期望每个数据包(或其他基本存储单元)不止包含一帧,此时可在调用speex_bits_write写数据流之前调用speex_encode N次来实现。若每个包的帧数不是由带外机制决定,则可以包含一个终止码。终止码用5位对代码15(十进制)编码,如表9.2所示。注意对于1.0.2版本,调用speex_bits_write自动插入终止码来填充最后一字节,这样没有任何其他费用并保证包里没有更多帧时Speex能准确检测到。

        可以发送带内信息到另一端。这些信息之前加入一个包含4位信息类型代码的模式14的“伪帧”。表5.1列出了可能的代码,包含他们的意义和信息大小。大多数信息是要发送到另一端编码器或解码器的请求,可以自由接受或无视这些请求。默认的,无视所有带内信息。

                

                                                         表1   带内信号代码

        最后,应用可以用代码13定义常用带内信息,信息字节大小用5位编码,解码器若不知如何解码则可以跳过它。

6  语音处理API(libspeexdsp)

        对于1.2beta3版本,Speex中的非编解码部分单独分离成libspeexdsp库,包括预处理器,声学回声消除器,抖动缓冲器和重采样器。在UNIX环境下,可通过在编译器命令行中添加-lspeexdsp -lm链接到你的程序中。正如libspeex,libspeexdsp调用时可重入的,但不是线程安全的,这意味着可以在多线程中使用,但多线程中对同一状态的调用必须由互斥锁保护。

6.1 预处理器

        调用Speex预处理器之前,首先需要头文件

                #include <speex/speex_preprocess.h>

        然后,创建预处理器状态:

                SpeexPreprocessState *preprocess_state = speex_preprocess_state_init(frame_size,sampling_rate);

推荐frame_size的大小同编码器中的一样,为20ms。

        对于每一输入帧,需要调用:

                speex_preprocess_run(preprocess_state, audio_frame);

其中audio_frame可为输入或输出。若不需要输出处理后的数据,可调用以下函数替代:

                speex_preprocess_eatimate_update(preprocess_state, audio_frame);

这一函数会更新预处理器内部状态变量而无需计算输出音频,节省CPU周期。

        预处理器参数可以改变,通过调用:

                speex_preprocess_ctl(preprocess_state, request, ptr);

调用方法同编码器和译码器中的一样,选项在6.1.1中列出。

        最后,销毁预处理器状态:

                speex_preprocess_state_destroy(preprocess_state);

6.1.1  预处理器选项

        同编解码器一样,预处理选项可通过函数控制。可用选项有:

                SPEEX_PREPROCESS_SET_DENOISE  开启(1)或关闭(2)降噪(spx_int32_t型)

                SPEEX_PREPROCESS_GET_DENOISE  获取降噪状态(spx_int32_t型)

                SPEEX_PREPROCESS_SET_AGC  开启(1)或关闭(2)自动增益控制(AGC)(spx_int32_t型)

                SPEEX_PREPROCESS_GET_AGC  获取AGC状态(spx_int32_t型)

                SPEEX_PREPROCESS_SET_VAD  开启(1)或关闭(2)声音活动检测(VAD)(spx_int32_t型)

                SPEEX_PREPRECESS_GET_VAD  获取VAD状态(spx_int32_t型)

                SPEEX_PREPROCESS_SET_AGC_LEVEL

                SPEEX_PREPROCESS_GET_AGC_LEVEL

                SPEEX_PREPROCESS_SET_DEREVERB  开启(1)或关闭(2)混响消除(spx_int32_t型)

                SPEEX_PREPROCESS_GET_DEREVERB  获取混响消除状态(spx_int32_t型)

                SPEEX_PREPROCESS_SET_DEREVERB_LEVEL  暂时不可用

                SPEEX_PREPROCESS_GET_DEREVERB_LEVEL  暂时不可用

                SPEEX_PREPROCESS_SET_DEREVERB_DECAY  暂时不可用

                SPEEX_PREPROCESS_GET_DEREVERB_DECAY  暂时不可用

                SPEEX_PREPROCESS_SET_PROB_START

                SPEEX_PREPROCESS_GET_PROB_START

                SPEEX_PREPROCESS_SET_PROB_CONTINUE

                SPEEX_PREPROCESS_GET_PROB_CONTINUE

                SPEEX_PREPROCESS_SET_NOISE_SUPPRESS  设置最大噪声衰减,单位dB(负spx_int32_t型)

                SPEEX_PREPROCESS_GET_NOISE_SUPPRESS  获取最大噪声衰减,单位dB(负spx_int32_t型)

                SPEEX_PREPROCESS_SET_ECHO_SUPPRESS  设置最大残余回声衰减,单位dB(负spx_int32_t型)

                SPEEX_PREPROCESS_GET_ECHO_SUPPRESS  获取最带残余回声衰减,单位dB(负spx_int32_t型)

                SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE  近端活动时设置最大残余回声衰减,单位dB(负spx_int32_t型)

                SPEEX_PREPROCESS_GET_ECHO_SUPPRESS_ACTIVE  近端活动时获取最大残余回声衰减,单位dB(负spx_int32_t型)

                SPEEX_PREPROCESS_SET_ECHO_STATE  为残余回声一直设置相关回声消除器(若没有残余回声消除,为指针或NULL)

                SPEEX_PREPROCESS_GET_ECHO_STATE  获取相关回声消除器(指针)

6.2  回声消除

        Speex库现在包含一个可用的声学回声消除算法。使用回声消除器之前,需要包含头文件:

                #include <speex/speex_echo.h>

        然后,创建回声消除器状态:

                SpeexEchoState *echo_state = speex_echo_state_int(frame_size. filter_length);

其中frame_size是你一次想处理的数据大小(样本数),filter_length是你想使用的回声消除滤波器长度(样本数,也叫尾长)。推荐使用20ms帧长(或等于编解码器帧长),确保容易执行FFT(2的指数大小最佳)。推荐尾长近似为房间混响时间的三分之一,例如,在小房间中,混响时间是300ms,则尾长最好为100ms(采样率8000Hz对应800样本数)。

        创建回声消除器状态后,处理音频:

                speex_echo_cancellation(echo_state, input_frame, echo_frame, output_frame);

其中input_frame是麦克风采集到的音频数据(近端),echo_frame是扬声器中播放的音频(远端,待消除),output_frame是回声消除后的数据。

        需要重点考虑的是input_frame和echo_frame之间的关系,在任何时候,近端中存在的回声必须发送到echo_frame作为参考信号。换言之,回声消除器不能消除没有参考信号的回声。另一方面,输入信号和参考回声信号的延时必须足够小,否则回声消除器效果不好甚至无效(译者注:具体参考NLMS算法,这是声学回声消除的一个关键问题,就是近端和远端的同步问题,因为不同步算法收敛慢甚至不收敛。微软的回声消除效果很好,但也对系统版本和硬件有要求,因为我们设置的采样率跟硬件实际采样率还是有一点差别的,具体参见微软相关论文)。在完全同步时,代码可以像这样:

                write_to_soundcard(echo_frame, frame_size);

                read_from_soundcard(input_frame, frame_size);

                speex_echo_cancellation(echo_state, input_frame, echo_frame, output_frame);

        若想更好的回声消除效果,可在预处理器后设置回声消除器(见6.1),即初始化时调用:

                speex_preprocess_ctl(preprocess_state, SPEEX_PREPROCESS_SET_ECHO_STATE, echo_state);

        对于1.2-beta2版本,可用另一种方法代替speex_echo_cancellation()函数。当录音和播放需要同步处理时(如不同线程或使用poll()或select()系统调用),很难确定input_frame对应哪一echo_frame(译者注:即同步问题)。作为替代,回放线程可简单调用:

                speex_echo_playback(echo_state,echo_frame);

每次播放一帧,就调用录音线程处理录音的一帧:

                speex_echo_capture(echo_state, input_frame, output_frame);

在内部,speex_echo_playback()函数简单的将播放帧放在一个buffer中,然后在调用speex_echo_cancel()时由speex_can_capture()调用buffer中数据。使用这种API调用方式的一个副作用是回放帧需要延迟两帧,这是声卡引起的正常延迟。若回放和录音已经完全同步,speex_echo_cancellation()函数能更好地控制回声消除效果。

        最后,回声消除状态需要销毁:

                speex_echo_state_destroy(echo_state);

也可以重置回声消除器,这样就可以重新利用回声消除器而无需再创建一个新的:

                speex_echo_state_reset(echo_state);

6.2.1  故障查找

        回声消除器正常工作可能存在几个问题。其中之一是代码中存在bug(或效果是次优的),其他需要考虑的问题有:

                1)当换一块声卡来录音和播放时,原来的回声消除器无效,除非两块声卡在同样的时钟脉冲源上有相同的采样时钟“锁定”,否则,它们的时钟总会有一些偏移,这将使得回声消除器不收敛(译者注:这就是软件设置的采样率与硬件实际的采样率有些许差别的问题)。

                2)录音和回放之间的延时必须尽可能小。任何信号都是先回放(远端),然后被近端采样录音,过多的延时意味着部分滤波器长度的浪费。最坏情况下,延时比滤波器长度还大,此时不能消除任何回声。

                3)回声尾长(滤波器长度)并不是越大越好。实际上,尾长越长,滤波器收敛越慢。当然,尾长太短也不能消除回声,但更常见的问题是许多人设置了一个很长的尾长然后奇怪为什么没有消除回声。

                4)非线性失真不能被回声消除所使用的线性自适应滤波器建模,因此不能消除非线性失真。可使用好的音频设备并且避免饱和与削波。

        具体可参考Alexey Frunze的《Echo Cancellation Demystified》(http://www.embeddedstar.com/articles/2003/7/article20030720-1.html),解释了回声消除的基本原理。具体算法在不同文章中的描述不同,但回声消除的通用方法就是自适应滤波器。

        在1.2beta2版本后,源代码中包含了一个新的echo_diagnostic.m工具。第一步是在建立是定义DUMP_ECHO_CANCEL_DATA,这将会使回声消除自动保存近端、远端和处理后的信号到文件中(aec_rec.sw、sec_play.sw和aec_out.sw),这正是AEC的输入和输出。使用这一工具时,需要开启倍频程:

                echo_diagnostic('aec_rec.sw', 'aec_play', 'aec_diagnostoc', 1024);

值1024是滤波器长度,可以改变。将会输出一些有用的信息,回声消除音频将保存到aec_diagnostic.sw。如果输出无效(回声没有消除),可能是播放或录音的问题。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值