经常有人问我如何计算录音时音量大小。iOS平台是有api可以直接调用的,但是Android平台上没有比较好的办法,因此我们就不得不自己计算了。


之所以有计算音量这个需求,是因为很多应用希望根据音量的大小实现一些动画效果。因此,从这个需求出发,只要能根据说话时声音的大小,获取到的音量值有变化即可,而不必过分纠缠于到底范围多少才是准确的。因为计算的方法有很多种,不同的方法计算出来的值肯定是不同的,但是只要能反映出大小变化,我们的目的就达到了。


下面我以Android录音为例,介绍一下其中的一种计算方法。大家可以根据自己的需要重新计算,或者是对我这个计算出来的值做一些数学变换,从而满足自己的需要。


需要提前说明的是,网上也有类似的一些计算方法,但是千万不能照搬过来,因为这个和录音的编码和录音数据的类型是有关系的。


录音的编码主要有两种:8位pcm和16位pcm。8位pcm用一个字节表示语音的一个点,16位pcm用两个字节,也就是一个short来表示语音的一个点。需要特别注意的是,如果你用的16位pcm编码,而取录音数据用的是byte的话,需要自己将两个bye转换成一个short。将两个byte转换成一个short,有小端和大端两种,一般默认情况都是小端,但是有的开源库,比如lamemp3需要的就是大端,这个要根据不同的情况进行不同的处理。


下面以Android为例,介绍一下用平均值计算音量的方法。


private double calculateVolume(short[] buffer){

       double sumVolume = 0.0;

       double avgVolume = 0.0;

       double volume = 0.0;

       for(short b : buffer){

               sumVolume += Math.abs(b);

       }

       avgVolume = sumVolume / buffer.length;

       volume = Math.log10(1 + avgVolume) * 10;

       return volume;

}


这个方法传递的是short类型的数据,所以录音的编码肯定是16位pcm,这样可以直接计算而不需要转换了。相信大家都听过声波这个东西,大家用音频编辑软件Adobe audition 打开一段声音:


101959596.png


从这里我们可以看到,声音是高低起伏变化的,有波峰波谷,说白了就是有正有负。因此在计算的时候,我们需要先求绝对值,要不然就上下抵消。求完绝对值然后进行累加,再除以整个数据的长度,就得到了这段语音数据的平均值了。


但是这样直接计算出来的结果比较大,不利于我们使用,因此对它进行了取对数再乘以10:


volume = Math.log10(1 + avgVolume) * 10;


这些可以根据自己的需要进行运算,我这边只是一个简单的例子。


还有一个特别需要注意的问题是:如果你录音的编码是16为pcm,而录音数据数据是byte,需要将两个byte转为一个short进行处理,建议用小端的方式。


private doublecalculateVolume(byte[] buffer){

       double sumVolume = 0.0;

       double avgVolume = 0.0;

       double volume = 0.0;

       for(int i = 0; i < buffer.length; i+=2){

               int v1 = buffer[i] & 0xFF;

               int v2 = buffer[i + 1] & 0xFF;

               int temp = v1 + (v2 << 8);// 小端

               if (temp >= 0x8000) {

                       temp = 0xffff - temp;

               }

       sumVolume += Math.abs(temp);

       }

       avgVolume = sumVolume / buffer.length / 2;

       volume = Math.log10(1 + avgVolume) * 10;

       return volume;

}


关于用小端的方式,将两个byte转为一个short的那些移位运算,应该不难,这里不做过多的解释了。


以上就是用平均值的方法计算音量大小的方法,但是在iOS中,你自己不需要计算了,直接调用系统的api即可。


iOS获取录音是系统音量的步骤:


1、在开始录音之前,要设置获取系统音量的属性


   // 设置获取音量大小的属性

   UInt32 enabledLevelMeter = true;

   AudioQueueSetProperty(_audioQueue, kAudioQueueProperty_EnableLevelMetering, &enabledLevelMeter, sizeof(UInt32));


2、调用系统api获取音量,此方法可以在 缓冲器的回调函数中调用,也可以自己用一个定时器来调用


   AudioQueueLevelMeterState levelMeter;

   UInt32 levelMeterSize = sizeof(AudioQueueLevelMeterState);

   AudioQueueGetProperty(_audioQueue, kAudioQueueProperty_CurrentLevelMeterDB, &levelMeter, &levelMeterSize);

   double volume = levelMeter.mAveragePower


当然,你如果不想用系统的音量值,也可以像上面那样自己计算,这取决于你的需要。


最后我把android的获取录音音量大小的demo上传到附件中了,大家可以参考。