声音的本质是一种能量波,由振动而产生的能量波,通过传输介质传输出去。声音有三个属性:
-
音调(Pitch):声音频率的高低。表示人的听觉分辨一个声音的调子高低的程度。音调主要由声音的频率决定,同时也与声音强度有关
-
音量:人主观上感觉声音的大小,由“振幅”(amplitude)和人离声源的距离决定,振幅越大响度越大,人和声源的距离越小,响度越大。(单位:分贝dB)
-
音色:又称声音的品质,波形决定了声音的音色。声音因不同物体材料的特性而具有不同特性,音色本身是一种抽象的东西,但波形是把这个抽象直观的表现。音色不同,波形则不同。典型的音色波形有方波,锯齿波,正弦波,脉冲波等。不同的音色,通过波形,完全可以分辨的。
波长是决定音调高低;振幅是决定音量高低;波纹是决定音色。
PCM(Pulse Code Modulation,脉冲编码调制)音频数据是未经压缩的音频采样数据裸流,它是由模拟信号经过采样、量化、编码转换成的标准数字音频数据。
描述PCM数据主要有以下6个参数:
- Sample Rate : 采样频率。8kHz(电话)、44.1kHz(CD)、48kHz(DVD)。
- Sample Size : 量化位数,描述数字信号所使用的位数。8位(8bit)代表2的8次方=256,16 位(16bit)则代表2的16次方=65536;采样位数越高,精度越高。
- Number of Channels : 通道个数。常见的音频有立体声(stereo)和单声道(mono)两种类型,立体声包含左声道和右声道。另外还有环绕立体声等其它不太常用的类型。
- Sign : 表示样本数据是否是有符号位,比如用一字节表示的样本数据,有符号的话表示范围为-128 ~ 127,无符号是0 ~ 255。
- Byte Ordering : 字节序。字节序是little-endian还是big-endian。通常均为little-endian。
- Integer Or Floating Point : 整形或浮点型。大多数格式的PCM样本数据使用整形表示,而在一些对精度要求高的应用方面,使用浮点类型表示PCM样本数据。
对于16位,单声道
的音频,采样点幅值为2^15-1
和-2^15
,即32767
和-32768
,当乘以放大倍数后,需要对超出此范围的数据进行溢出处理,但是如果一个音频帧中溢出数据过多,就会造成音频失真,故要合理的动态的选择放大倍数。
下面是两种对16位,单声道
的音频调节音量算法
/*
*samples 音频数据
*numSamples 音频数据长度
*factor 振幅的系数 比如1.2
*/
static int adjustmentVolume(short *samples,int numSamples,float factor)
{
int tmpValue;
if (0 == factor)
{
memset((void *)samples,0,numSamples*sizeof(short));
return numSamples;
}
else if(1.0 == factor)
{
return numSamples;
}
for (int i = 0; i<numSamples; i++)
{
tmpValue = samples[i] * factor; //这样运算出来仍然是整数
if(tmpValue < -32768)
{
tmpValue = -32768;
}
else if (tmpValue > 32767)
{
tmpValue = 32767;
}
samples[i] = tmpValue;
}
return numSamples;
}
/*
*samples 音频数据
*numSamples 音频数据长度
*factor 振幅的系数 比如1.2
*/
int adjustmentVolume(short *samples ,int numSamples, int factor){
const short MIND = -0x8000;
const short MAXD = 0x7FFF;
short data = 0, maxData = 0, minData = 0;
//获取一个音频帧中的最大值`max`和最小值`min`
for (int i = 0; i < numSamples; i++) {
data = samples[i];
maxData = maxData > data ? maxData : data;
minData = minData < data ? minData : data;
}
//根据获取到的最大值和最小值分别计算出在不失真的情况下,允许的放大倍数`maxfactor`和`minfactor`
short maxfactor = maxData != 0 ? MAXD/maxData : 1;
short minfactor = minData != 0 ? MIND/minData : 1;
//取其最小值为允许的放大倍数`allowfactor`
short allowfactor = maxfactor > minfactor ? minfactor : maxfactor;
//选择合适的振幅的系数
factor = factor > allowfactor ? allowfactor : factor;
if (factor == 1) {
return numSamples;
}
else if(0 == factor)
{
memset((void *)samples,0,numSamples*sizeof(short));
return numSamples;
}
//对PCM数据放大
long newData = 0;
for (int i = 0; i < numSamples; i++) {
data = samples[i];
newData = data*factor;
//边界值溢出处理
if (newData < MIND) {
newData = MIND;
}else if (newData > MAXD) {
newData = MAXD;
}
data = newData&0xffff;
samples[i] = data;
}
return numSamples;
}