最近做一个录屏项目,录制的系统声音音量偏小,需要调大
录音的方式采用了AudioRecord,可以拿到音频原始数据,于是想到从音频数据下手
之前没有接触过类似的知识,对这个东西也不懂,参考网上做法,需要使用到一个公式
公式:
A1 = A2 * pow(10, db/20)
A1 和 A2 是两个声音的振幅,A2为原始音频振幅,A1为根据所指定db大小计算出来的调节音量后的音频振幅
频率与振幅
声音有两个基本的物理属性:频率与振幅。声音的振幅就是音量,频率的高低就是指音调,频率用赫兹(Hz)作单位。人耳只能听到20Hz到20khz范围的声音。
代码:
int db = 20;//db为0表示保持音量不变,db为负数表示较低音量,为正数表示提高音量
private double factor = Math.pow(10,(double)db / 20);
//调节PCM数据音量
//pData原始音频byte数组,nLen原始音频byte数组长度,data2转换后新音频byte数组,nBitsPerSample采样率,multiple表示Math.pow()返回值
public int amplifyPCMData(byte[] pData, int nLen, byte[] data2, int nBitsPerSample, float multiple){
int nCur = 0;
if (16 == nBitsPerSample){
while (nCur < nLen){
short volum = getShort(pData, nCur);
float pcmval = volum * multiple;
//volum = (short)(volum * multiple);
//数据溢出处理
if (pcmval < 32767 && pcmval > -32768){
volum = (short)pcmval;
}else if (pcmval > 32767) {
volum = (short)32767;
} else if (pcmval < -32768) {
volum = (short)-32768;
}
data2[nCur] = (byte)( volum & 0xFF);
nCur += 2;
}
}
return 0;
}
private short getShort(byte[] data, int start){
return (short)((data[start] & 0xFF) | (data[start+1] << 8));
}
db
db为0表示保持音量不变,db为负数表示较低音量,为正数表示提高音量,声音调节步长最好是2db
数据溢出处理
每个样本取值范围是有限制的,调节音量时不可能随便增大,比如一个signed 16 bits的样本,值为5000,我们放大10倍,由于有符号位16bits数据取值范围为-32768~32767,5000乘以10得到的50000超过了32767,数据溢出了,最后值可能变为-15536,不是我们期望的。此时我们就需要裁剪了,确保数值在正确范围内
AudioRecord使用示例:
/**
* Thread to capture audio data from internal mic as uncompressed 16bit PCM data
* and write them to the MediaCodec encoder
*/
private class AudioThread extends Thread {
@Override
public void run() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
try {
final int min_buffer_size = AudioRecord.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO,AudioFormat.ENCODING_PCM_16BIT);
int buffer_size = SAMPLES_PER_FRAME * FRAMES_PER_BUFFER;
if (buffer_size < min_buffer_size)
buffer_size = ((min_buffer_size / SAMPLES_PER_FRAME) + 1) * SAMPLES_PER_FRAME * 2;
AudioRecord audioRecord = new AudioRecord(mAudioSource, SAMPLE_RATE,AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, buffer_size);
if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED)
audioRecord = null;
if (audioRecord != null) {
try {
if (mIsCapturing) {
if (DEBUG) Log.v(TAG, "AudioThread:start audio recording");
final ByteBuffer buf = ByteBuffer.allocateDirect(SAMPLES_PER_FRAME);
int readBytes;
audioRecord.startRecording();
try {
for (; mIsCapturing && !mRequestStop && !mIsEOS;) {
// read audio data from internal mic
if (!isPause){
buf.clear();
readBytes = audioRecord.read(buf, SAMPLES_PER_FRAME);
if (readBytes > 0) {
// set audio data to encoder
buf.position(readBytes);
buf.flip();
if (mAudioSource == 99999){
//录制系统声音音量处理
byte[] byteYuan = new byte[buf.remaining()];
buf.get(byteYuan,0,byteYuan.length);
byte[] byteYuanNew = new byte[byteYuan.length];
amplifyPCMData(byteYuan,byteYuan.length,byteYuanNew,16,(float) factor);
buf.clear();
buf.put(byteYuanNew);
buf.position(readBytes);
buf.flip();
}
encode(buf, readBytes, getPTSUs());
frameAvailableSoon();
}
}
}
} finally {
Log.e(TAG,"audio end");
audioRecord.stop();
}
}
} catch (Exception e) {
Log.e(TAG, "AudioRecord failed to startRecording");
if (mExecptionCallback != null){
mExecptionCallback.recordException(e);
}
}finally {
audioRecord.release();
}
} else {
Log.e(TAG, "failed to initialize AudioRecord");
}
} catch (final Exception e) {
Log.e(TAG, "AudioThread#run", e);
}
if (DEBUG) Log.v(TAG, "AudioThread:finished");
}
}
参考:https://www.jianshu.com/p/50c697bec409