美文网首页
PCM 分贝获取

PCM 分贝获取

作者: woo_5857 | 来源:发表于2019-12-02 13:00 被阅读0次

1:计算分贝 音频数据与大小

首先我们分别累加每个采样点的数值,除以采样个数,得到声音平均能量值。

然后再将其做100与32767之间的等比量化。得到1-100的量化值。

通常情况下,人声分布在较低的能量范围,这样就会使量化后的数据大致分布在1-20的较小区间,不能够很敏感的感知变化。

所以我们将其做了5倍的放大,当然计算后大于100的值,我们将其赋值100.

//参数为数据,采样个数

//返回值为分贝

#define VOLUMEMAX  32767

intSimpleCalculate_DB(short* pcmData,intsample)

{

    signedshortret =0;

    if(sample >0){

        intsum =0;

        signedshort* pos = (signedshort*)pcmData;

        for(inti =0; i < sample; i++){

            sum +=abs(*pos);

            pos++;

        }

        ret = sum *500.0/ (sample *VOLUMEMAX);

        if(ret >=100){

            ret =100;

        }

   }

   returnret;

}

2:计算均方根(RMS) 即能量值

static const float kMaxSquaredLevel = 32768 * 32768;

constexpr float kMinLevel = 30.f;

voidProcess(constint16_t* data,size_tlength)

{

    floatsum_square_ =0;

    size_tsample_count_ =0;

    for(size_ti =0; i < length; ++i) {

        sum_square_ += data[i] * data[i];

    }

    sample_count_ += length;.

    floatrms = sum_square_ / (sample_count_ * kMaxSquaredLevel);

    //20log_10(x^0.5) = 10log_10(x)

    rms =10* log10(rms);

    if(rms < -kMinLevel)

        rms = -kMinLevel;

    rms = -rms;

    returnstatic_cast<int>(rms +0.5);

}

3:获取音频数据最大的振幅(即绝对值最大)(0-32767),除以1000,得到(0-32)。从数组中获取相应索引所对应的分贝值。(提取自webrtc)

const int8_t permutation[33] =

    {0,1,2,3,4,4,5,5,5,5,6,6,6,6,6,7,7,7,7,8,8,8,9,9,9,9,9,9,9,9,9,9,9};

int16_t WebRtcSpl_MaxAbsValueW16C(const int16_t* vector, size_t length)

{

    size_ti =0;

    intabsolute =0, maximum =0;

    for(i =0; i < length; i++) {

        absolute =abs((int)vector[i]);

        if(absolute > maximum) {

            maximum = absolute;

        }

    }

    if(maximum >32767) {

        maximum =32767;

    }

    return(int16_t)maximum;

}

voidComputeLevel(constint16_t* data,size_tlength)

{

    int16_t_absMax =0;

    int16_t_count =0;

    int8_t_currentLevel =0;

    int16_tabsValue(0);

    absValue = WebRtcSpl_MaxAbsValueW16(data,length);

    if(absValue> _absMax)

        _absMax =absValue;

    if(_count++ ==10) {

        _count =0;

        int32_tposition = _absMax/1000;

        if((position ==0) && (_absMax >250)){

            position =1;

        }

        _currentLevel = permutation[position];

        _absMax >>=2;

    }

}

分贝公式

参数:Pref:就是声音总的振幅最大值;Prms:就是当前声音的振幅值;Lp:就是我们需要的声音分贝值了。

比如:我们声音是无符号16bit深度的,那么其每个采样点的值应该在(0~2^16-1既:0~65535)范围内,带入公式我们可以计算到(不用除以最大振幅值):20*log(65535)=96.32db,所以根据这个我们只要拿到某个采样点的振幅值,也就是当前声音采样点转成16bit后的值就可以计算出相应的分贝值了。那么怎么求声音采样点的振幅呢?这是一个问题,不过也有解决办法了。

获取pcm声音采样点的振幅:

这里以我项目中用OpenSL来播放FFmpeg重采样生成的PCM声音为例,PCM声音是重采样为无符号16bit的深度的,然后我们需要得到某一时间(一般是零点几毫秒)PCM所在内存的地址和PCM声音的大小,而16bit也就是16bit/8bit=2byte,在c语言中2byte用short int来表示,因此我们可以从PCM所在地址里面按顺序取出2个byte的数据然后转化成short int的值就可以拿到当前采样点的振幅了,获取的方式是用c语言中的memcpy拷贝2个字节的数据求值就可以了。(注:因为采用点很密集,如果每个采用点都计算一下分贝的话,会消耗一定的性能或者导致声音播放不连贯,所这里采用取其绝对值和的平均值就可以了,因为在这段时间内,我们看不出任何的区别。)

/**

* 获取所有振幅之平均值 计算db (振幅最大值 2^16-1 = 65535 最大值是 96.32db)

* 16 bit == 2字节 == short int

* 无符号16bit:96.32=20*lg(65535);

*

* @param pcmdata 转换成char类型,才可以按字节操作

* @param size pcmdata的大小

* @return

*/

intAudio::getPcmDB(constunsignedchar*pcmdata,size_tsize) {

intdb =0;

shortintvalue =0;

doublesum =0;

for(inti =0; i < size; i +=2)

    {

memcpy(&value, pcmdata+i,2);//获取2个字节的大小(值)

sum +=abs(value);//绝对值求和

    }

sum = sum / (size /2);//求平均值(2个字节表示一个振幅,所以振幅个数为:size/2个)

if(sum >0)

    {

db = (int)(20.0*log10(sum));

    }

return db;

}

相关文章

网友评论

      本文标题:PCM 分贝获取

      本文链接:https://www.haomeiwen.com/subject/igzxgctx.html