一、音频基础知识
-
【通道数】
即声音的通道的数目。常有单声道和立体声之分,单声道的声音只能使用一个喇叭发声(有的也处理成两个喇叭输出同一个声道的声音),立体声可以使两个喇叭都发声(一般左右声道有分工) ,更能感受到空间效果,当然还有更多的通道数。
-
【采样频率】
采样频率即取样频率, 指每秒钟取得声音样本的次数。采样频率越高,声音的质量也就越好,声音的还原也就越真实,但同时它占的资源比较多。由于人耳的分辨率很有限,太高的频率并不能分辨出来。22050 的采样频率是常用的, 44100已是CD音质, 超过48000或96000的采样对人耳已经没有意义。
根据奈奎斯特采样定理,当采样频率fs.max大于信号中最高频率fmax的2倍时(fs.max>2fmax),采样之后的数字信号完整地保留了原始信号中的信息。我们人耳可以听到的声音频率在20Hz-220KHz之间的声波,所以我们做音频采样时一般采用22000 * 2 + 100 = 44100 Hz(多采100Hz的偏差)
-
【采样位数】
采样位数即采样值或取样值(就是将采样样本幅度量化)。它是用来衡量声音波动变化的一个参数,也可以说是声卡的分辨率。它的数值越大,分辨率也就越高,所发出声音的能力越强。
每个采样数据记录的是振幅, 采样精度取决于采样位数的大小: 1 字节(也就是8bit) 只能记录 256 个数, 也就是只能将振幅划分成 256 个等级; 2 字节(也就是16bit) 可以细到 65536 个数, 这已是 CD 标准了; 4 字节(也就是32bit) 能把振幅细分到 4294967296 个等级, 实在是没必要了.
-
【音频播放】
原生的音频格式为pcm,即我们平时的.MP3等音频封装格式文件最终到喇叭播放时已经将其转码为pcm格式的音频数据,pcm的音频数据体积比压缩格式要大很多倍。
二、本地音频转码成pcm输出到文件
extern "C" {
//封装格式
#include "libavformat/avformat.h"
//解码
#include "libavcodec/avcodec.h"
//缩放
#include "libswscale/swscale.h"
//重采样
#include "libswresample/swresample.h"
};
/**
* 将本地音频文件转码为pcm格式并保存到一个新文件上
* input: 本地音频文件路径
* output:转换后的硬盘存储路径
*/
void musicPlayer(const char *input,const char *output){
av_register_all();
AVFormatContext *pFormatCtx = avformat_alloc_context();
//打开音频文件
if (avformat_open_input(&pFormatCtx, input, NULL, NULL) != 0) {
LOGI("%s", "无法打开音频文件");
return;
}
//获取输入文件信息
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
LOGI("%s", "无法获取输入文件信息");
return;
}
//获取音频流索引位置
int i = 0, audio_stream_idx = -1;
for (; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
audio_stream_idx = i;
break;
}
}
//获取解码器
AVCodecContext *codecCtx = pFormatCtx->streams[audio_stream_idx]->codec;
AVCodec *codec = avcodec_find_decoder(codecCtx->codec_id);
//打开解码器
if (avcodec_open2(codecCtx, codec, NULL) < 0) {
LOGI("%s", "无法打开解码器");
return;
}
//压缩数据
AVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));
//解压缩数据
AVFrame *frame = av_frame_alloc();
//frame->16bit 44100 PCM 统一音频采样格式与采样率
SwrContext *swrContext = swr_alloc();
//音频格式 重采样设置参数
AVSampleFormat in_sample = codecCtx->sample_fmt;//原音频的采样位数
//输出采样格式
AVSampleFormat out_sample = AV_SAMPLE_FMT_S16;//16位
int in_sample_rate = codecCtx->sample_rate;// 输入采样率
int out_sample_rate = 44100;//输出采样
//输入声道布局
uint64_t in_ch_layout = codecCtx->channel_layout;
//输出声道布局
uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;//2通道 立体声
/**
* struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate,
int log_offset, void *log_ctx);
*/
swr_alloc_set_opts(swrContext, out_ch_layout, out_sample, out_sample_rate, in_ch_layout, in_sample,
in_sample_rate, 0, NULL);
swr_init(swrContext);
int got_frame = 0;
int ret;
int out_channerl_nb = av_get_channel_layout_nb_channels(out_ch_layout);
LOGE("声道数量%d ", out_channerl_nb);
int count = 0;
//设置音频缓冲区间 16bit 44100 PCM数据
uint8_t *out_buffer = (uint8_t *) av_malloc(2 * 44100);
FILE *fp_pcm = fopen(output, "wb");//输出到文件
while (av_read_frame(pFormatCtx, packet) >= 0) {
ret = avcodec_decode_audio4(codecCtx, frame, &got_frame, packet);
LOGE("正在解码%d", count++);
if (ret < 0) {
LOGE("解码完成");
}
//解码一帧
if (got_frame > 0) {
/**
* int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,
const uint8_t **in , int in_count);
*/
swr_convert(swrContext, &out_buffer, 2 * 44100,
(const uint8_t **) frame->data, frame->nb_samples);
/**
* int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples,
enum AVSampleFormat sample_fmt, int align);
*/
int out_buffer_size = av_samples_get_buffer_size(NULL, out_channerl_nb, frame->nb_samples,
out_sample, 1);
fwrite(out_buffer, 1, out_buffer_size, fp_pcm);//输出到文件
}
}
fclose(fp_pcm);
av_frame_free(&frame);
av_free(out_buffer);
swr_free(&swrContext);
avcodec_close(codecCtx);
avformat_close_input(&pFormatCtx);
}