智能客服搭建(4) - 语音流的分贝计算

1. 研究背景

  最近在从事一些语音相关的开发工作,为了对实时语音流的语音相关数据进行计算和量化,做了一些相关的研究。

(1) 思路

  我们平时在电脑上控制音量都是在0-100进行调整,但是这是一个相对的音量感觉,不好进行标准的度量,后来在网上查了一些资料,最终决定使用分贝对声音的大小进行客观的评估。

(2) 分贝的概念

  分贝(decibel,/'dɛsɪ.bɛl/)是量度两个相同单位之数量比例的计量单位,主要用于度量声音强度,常用dB表示。

  “分”(deci-)指十分之一,个位是“贝”(bel),一般只采用分贝。分贝是以美国发明家亚历山大·格雷厄姆·贝尔(Alexander Graham Bell)的名字命名的。

  下面是分贝自测表,可供参考:

分贝值实际效果
0分贝刚能听到的声音
15 分贝以下感觉安静
30 分贝耳语的音量大小
40 分贝冰箱的嗡嗡声
60分贝正常交谈的声音
70分贝相当于走在闹市区
85分贝汽车穿梭的马路上
95分贝摩托车启动声音
100分贝装修电钻的声音
110分贝卡拉OK、大声播放 MP3 的声音
120分贝飞机起飞时的声音
150分贝燃放烟花爆竹的声音

  作为热爱养生的作者,顺便引用一下美国言语听力协会的提醒:长期在夜晚接受 50 分贝的噪音,容易导致心血管疾病;55 分贝,会对儿童学习产生负面影响;60 分贝,让人从睡梦中惊醒;70 分贝,心肌梗死的发病率增加30%左右;超过 110 分贝,可能导致永久性听力损伤。

2. MRCP的语音流处理浅析

(1) 语音能量计算源码

  在之前MRCP Server的开发过程中,阅读了源码中自带的VAD功能,源码/libs/mpf/src/mpf_activity_detector.c中包含了语音能量计算的代码,可以进行参考。

static apr_size_t mpf_activity_detector_level_calculate(const mpf_frame_t *frame)
{
	apr_size_t sum = 0;
	apr_size_t count = frame->codec_frame.size/2;
	const apr_int16_t *cur = frame->codec_frame.buffer;
	const apr_int16_t *end = cur + count;

	for(; cur < end; cur++) {
		if(*cur < 0) {
			sum -= *cur;
		}
		else {
			sum += *cur;
		}
	}

	return sum / count;
}

  以上代码根据frame的信息,对能量进行粗暴的累加,所以对于噪音的环境下,这个算法效果不好。

(2) 自带VAD功能的实现

  讲到这里,顺便提一下,MRCP的端点检测,我们可以看到在调用函数mpf_activity_detector_create进初始化的时候,就会设置静默能量大小在silence_timeout参数中,默认设置的是300。

/** Create activity detector */
MPF_DECLARE(mpf_activity_detector_t*) mpf_activity_detector_create(apr_pool_t *pool)
{
	mpf_activity_detector_t *detector = apr_palloc(pool,sizeof(mpf_activity_detector_t));
	detector->level_threshold = 12; /* 0 .. 255 */
	detector->speech_timeout = 300; /* 0.3 s */
	detector->silence_timeout = 300; /* 0.3 s */
	detector->noinput_timeout = 5000; /* 5 s */
	detector->duration = 0;
	detector->state = DETECTOR_STATE_INACTIVITY;
	return detector;
}

  我们可以通过mpf_activity_detector_silence_timeout_set对VAD能量大小进行修改,我使用了斥巨资购买的主播麦,想通过自己说话测试的方式,试出silence_timeout合适的值,后来放弃了,因为在有噪音的环境非常不稳定,而且在后面我发现,不同的麦克风这个值还不太一样,这就非常的尴尬了。

/** Set timeout required to trigger silence (transition from active to inactive state) */
MPF_DECLARE(void) mpf_activity_detector_silence_timeout_set(mpf_activity_detector_t *detector, apr_size_t silence_timeout)
{
	detector->silence_timeout = silence_timeout;
}

  具体MRCP自带VAD判断的方法在函数mpf_activity_detector_process中,感兴趣的可以自己去查看,总的思路就是设置了4种状态,需要经过这个状态来累计设定的时长,如果满足了,才会切换,否则不予切换,希望详细了解的可以参考文末的一些文章。

3 语音的基本概念

  之前查了很多资料,都是使用SPL进行计算的,写完后测试总是不符合分贝效果的预期,后来进行调试后,感觉实际用的是SL进行计算,下面进行介绍。

(1) 声强与声压

  声强(I):声波平均能流密度的大小。

  声压§:大气压受到声波扰动后产生的变化,即为大气压强的余压,它相当于在大气压强上的叠加一个声波扰动引起的压强变化。

  他们之间的关系如下公式:


I = \frac{p^2}{\rho\nu}

  其中


I: 声强

p: 声压

\rho: 介质密度

\nu: 表示水中声速

(2) 声压级SPL

  声压级定义为将待测声压有效值p(e)与参考声压p(ref)的比值取常用对数,再乘以20,用SPL表示。


SPL = 20\lg\frac{p_e}{p_{ref}}

  其中


p_e: 声压有效值

声压(p_e) = \sqrt{声强(I)*介质密度(\rho)*声速(C)}

\rho: 空气中介质密度1.293

声速: 340m/s

p_{ref}: 基准参考声压,一般在空气中的值为2*10^{-5} (Pa)

  对于数字麦克风计算声压级,看到了一篇帖子,还没有详细进行测试,相关链接放在文末,感兴趣的可以深入了解。

(3) 声源级SL

  声轴上距声源1米处产生的声强相对于参考声强的分贝数。是声纳方程中定量描述声源辐射能力的项,用 SL表示。


SL = 10\lg\frac{I}{I_0}

  其中


I: 表示离声源中心1m处的声强

I_0: 表示参考声强,值为0.67

4. 代码实践

  下面函数实现的功能为获取语音流数据后,对语音数据进行计算的函数:

size_t mpf_activity_detector_level_calculate(const char *data, int dataSize)
{
    size_t sum = 0;
    size_t count = dataSize / 2;
    const short *cur = data;
    const short *end = cur + count;

    for (; cur < end; cur++) {
        if (*cur < 0) {
            sum -= *cur;
        }
        else {
            sum += *cur;
        }
    }

    size_t ret = sum / count;
    printf("------------mpf_activity_detector_level_calculate() I: %ld\n", ret);

    // 声压(p)的平方=声强(i)×介质密度(ρ)×声速(c)
    // 声强单位是:w / m2
    // 密度单位:kg / m3
    // 声速:m / s
    double pe = sqrt((double)(ret * 1.293 * 340));
    double pref = 0.00002;
    
    // SPL = 20LOG(10)[p(e) / p(ref)]
    // 空气中参考声压p(ref)一般取为2*10E-5帕
    int SPL = (int)(20.0 * log10(pe / pref));
    printf("------------mpf_activity_detector_level_calculate() SPL: %d, p(e): %lf, p(ref): %lf\n", SPL, pe, pref);

    // https://zhuanlan.zhihu.com/p/384225760
    // SL = 10LOG(10)[ I / I。]
    // I 离声源中心1m处的声强
    // I。参考声强 0.67
    int SL = (int)(10.0 * log10(ret / 0.67));
    printf("------------mpf_activity_detector_level_calculate() SL: %d\n", SL);

    printf("\n");
    return ret;
}

以下为测试的log info,大家可以有个直观的感受。

------------mpf_activity_detector_level_calculate() I: 285
------------mpf_activity_detector_level_calculate() SPL: 144, p(e): 353.965676, p(ref): 0.000020
------------mpf_activity_detector_level_calculate() SL: 26


------------mpf_activity_detector_level_calculate() I: 3035
------------mpf_activity_detector_level_calculate() SPL: 155, p(e): 1155.095970, p(ref): 0.000020
------------mpf_activity_detector_level_calculate() SL: 36


------------mpf_activity_detector_level_calculate() I: 12894
------------mpf_activity_detector_level_calculate() SPL: 161, p(e): 2380.852847, p(ref): 0.000020
------------mpf_activity_detector_level_calculate() SL: 42


------------mpf_activity_detector_level_calculate() I: 13852
------------mpf_activity_detector_level_calculate() SPL: 161, p(e): 2467.714781, p(ref): 0.000020
------------mpf_activity_detector_level_calculate() SL: 43


------------mpf_activity_detector_level_calculate() I: 17396
------------mpf_activity_detector_level_calculate() SPL: 162, p(e): 2765.434780, p(ref): 0.000020
------------mpf_activity_detector_level_calculate() SL: 44


------------mpf_activity_detector_level_calculate() I: 16933
------------mpf_activity_detector_level_calculate() SPL: 162, p(e): 2728.385138, p(ref): 0.000020
------------mpf_activity_detector_level_calculate() SL: 44

最后作者使用了SL作为分贝数,但是实际测试过程中,感觉比标准的数值要底15左右。

这个问题可能和麦克风也有关系,后面排计划进行优化的时候,再进行文档更新。

欢迎经验丰富的大佬留言指教,一起学习进步。

参考资料

unimrcp-voice-activity语音检测

WebRTC的VAD 过程解读

声压、声强、声源级、谱级

麦克风声强测量的相位一致性

声学相关基础知识

数字麦克风计算声压级

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小爱玄策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值