花满楼原创
小白:音量设置?按几下音量按键就好啦!
花满楼:这种办法是全局的音量控制方式,现在是直接改音频数据,来做到音量的变化控制。
本文介绍直接更改pcm值,以达到能量控制。
大体的思路是这样的,先解码音频文件,得到pcm文件,再运算pcm文件,最后把pcm文件编码成aac。
重点讲解的部分是pcm文件的运算,解码与编码部分可以参考之前的文章来写代码完成,这里直接用ffmpeg的命令行来完成编解码。
先看整体代码,再做解释:
#include <stdio.h>
#include <stdlib.h>
const char* FFMPEGEXE = "ffmpeg";
const int BUF_LEN = 1024;
const int SAMPLE_RATE = 44100;
const int CHANNELS = 2;
const int BITRATE = 128;
void decode(const char* srcfile, const char* outfile) {
char buf[BUF_LEN] = {0};
sprintf(buf, "%s -i %s -f s16le -ar %d -ac %d -y %s", FFMPEGEXE, srcfile, SAMPLE_RATE, CHANNELS, outfile);
system(buf);
}
void encode(const char* srcfile, const char* outfile) {
char buf[BUF_LEN] = {0};
sprintf(buf, "%s -ar %d -ac %d -f s16le -i %s -ar %d -ac %d -b:a %dK -y %s", FFMPEGEXE, SAMPLE_RATE, CHANNELS, srcfile, SAMPLE_RATE, CHANNELS, BITRATE, outfile);
system(buf);
}
void change_volume(const char* pcmfile, double volume_factor, const char* outfile) {
const int sample_count =1024;
short samples[sample_count];
FILE* src = fopen(pcmfile, "rb");
FILE* out = fopen(outfile, "wb");
if (src && out) {
int cnt = 0;
while (cnt = fread(samples, sizeof(short), sample_count, src)) {
for (int i = 0; i < cnt; i ++) {
samples[i] = (short)(samples[i] * volume_factor);
}
fwrite(samples, sizeof(short), cnt, out);
}
fclose(src);
fclose(out);
}
}
int main(int argc, char *argv[])
{
const char* srcfile = "test.mp3";
const char* pcmfile = "test.pcm";
const char* pcmvolfile = "test_vol.pcm";
const char* outfile = "test.aac";
decode(srcfile, pcmfile);
change_volume(pcmfile, 1.1, pcmvolfile);
encode(pcmvolfile, outfile);
return 0;
}
解码,decode函数,解码为pcm中的s16le的格式,也就是一个short为一个样本。参数ar与ac分别指定采样率与声道数。
小白:pcm不就是未压缩的格式吗,为什么还有s16le这样的格式?
花满楼:pcm是没有未压缩,但还会细化出多种格式,你可以看下avcodec.h里面的定义,比如AV_CODEC_ID_PCM_F32BE、AV_CODEC_ID_PCM_F32LE等等,也可以看下之前讲的“音频之岁月留声”。
改变音量,这里很暴力地,直接把每个样本乘以一个系数。当这个系数超过1之后,削顶失真的问题就会出现。
这里给出一个演示效果。
小于1倍音量时,声音很小,没有问题(1倍音量时跟原文件一样):
![音量对比1]()
1.5倍音量时,声音变大,感觉还可以;3倍音量时,开始失真明显了:
![音量对比2]()
10倍音量的部分波形图是这样的,削顶失真很明显:
![10倍音量时的部分能量图]()
小白:你这些都是图片,我要的声音呢?
花满楼:好吧,我看下有没有上传音频的功能。
可以看出,让声音变小是没有问题的,但如果想放大声音而又避免明显失真,用这个办法的话,就要考虑一个合适的系数,比如不要超过3之类。
小白:你说这是一个简单直接的办法?难道还有不直接的办法能避免失真?
花满楼:还有其它办法来放大音量,比如动态范围控制(DRC)等,这些在以后再作介绍。
编码,把pcm编码成aac,可以使用FFmpeg自带的编码器(FFmpeg3.x才支持),也可以使用faac或fdk-aac等,这里使用的是fdk-aac,先要保证FFmpeg使用了fdk-aac:
![打开fdk-aac]()
在使用命令时,需要指定输入的pcm的格式如ar/ac/f等参数。
原文地址:http://blog.51cto.com/13136504/2058747