1:計(jì)算分貝 音頻數(shù)據(jù)與大小
首先我們分別累加每個(gè)采樣點(diǎn)的數(shù)值,除以采樣個(gè)數(shù),得到聲音平均能量值。
然后再將其做100與32767之間的等比量化。得到1-100的量化值。
通常情況下,人聲分布在較低的能量范圍,這樣就會使量化后的數(shù)據(jù)大致分布在1-20的較小區(qū)間,不能夠很敏感的感知變化。
所以我們將其做了5倍的放大,當(dāng)然計(jì)算后大于100的值,我們將其賦值100.
//參數(shù)為數(shù)據(jù),采樣個(gè)數(shù)
//返回值為分貝
#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:計(jì)算均方根(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:獲取音頻數(shù)據(jù)最大的振幅(即絕對值最大)(0-32767),除以1000,得到(0-32)。從數(shù)組中獲取相應(yīng)索引所對應(yīng)的分貝值。(提取自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;
? ? }
}
分貝公式

參數(shù):Pref:就是聲音總的振幅最大值;Prms:就是當(dāng)前聲音的振幅值;Lp:就是我們需要的聲音分貝值了。
比如:我們聲音是無符號16bit深度的,那么其每個(gè)采樣點(diǎn)的值應(yīng)該在(0~2^16-1既:0~65535)范圍內(nèi),帶入公式我們可以計(jì)算到(不用除以最大振幅值):20*log(65535)=96.32db,所以根據(jù)這個(gè)我們只要拿到某個(gè)采樣點(diǎn)的振幅值,也就是當(dāng)前聲音采樣點(diǎn)轉(zhuǎn)成16bit后的值就可以計(jì)算出相應(yīng)的分貝值了。那么怎么求聲音采樣點(diǎn)的振幅呢?這是一個(gè)問題,不過也有解決辦法了。
獲取pcm聲音采樣點(diǎn)的振幅:
這里以我項(xiàng)目中用OpenSL來播放FFmpeg重采樣生成的PCM聲音為例,PCM聲音是重采樣為無符號16bit的深度的,然后我們需要得到某一時(shí)間(一般是零點(diǎn)幾毫秒)PCM所在內(nèi)存的地址和PCM聲音的大小,而16bit也就是16bit/8bit=2byte,在c語言中2byte用short int來表示,因此我們可以從PCM所在地址里面按順序取出2個(gè)byte的數(shù)據(jù)然后轉(zhuǎn)化成short int的值就可以拿到當(dāng)前采樣點(diǎn)的振幅了,獲取的方式是用c語言中的memcpy拷貝2個(gè)字節(jié)的數(shù)據(jù)求值就可以了。(注:因?yàn)椴捎命c(diǎn)很密集,如果每個(gè)采用點(diǎn)都計(jì)算一下分貝的話,會消耗一定的性能或者導(dǎo)致聲音播放不連貫,所這里采用取其絕對值和的平均值就可以了,因?yàn)樵谶@段時(shí)間內(nèi),我們看不出任何的區(qū)別。)
/**
* 獲取所有振幅之平均值 計(jì)算db (振幅最大值 2^16-1 = 65535 最大值是 96.32db)
* 16 bit == 2字節(jié) == short int
* 無符號16bit:96.32=20*lg(65535);
*
* @param pcmdata 轉(zhuǎn)換成char類型,才可以按字節(jié)操作
* @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個(gè)字節(jié)的大?。ㄖ担?/p>
sum +=abs(value);//絕對值求和
? ? }
sum = sum / (size /2);//求平均值(2個(gè)字節(jié)表示一個(gè)振幅,所以振幅個(gè)數(shù)為:size/2個(gè))
if(sum >0)
? ? {
db = (int)(20.0*log10(sum));
? ? }
return db;
}