还是之前的项目,见这篇文章https://segmentfault.com/a/11...,libsox的使用没有成功,还差最后一步,到现在依然没有找到问题。为了尽快完成,我只好改用了FFmpeg提供的库libswresample来完成对音频数据的重采样。还是这个背景,应用场景是对已解码的8K采样率、16bit采样深度、单声道、无文件头的raw格式语音数据进行重采样,将采样率变为16K,其余参数不变。用FFmpeg库做的目前可以用,代码记录如下:
extern "C" {
#ifdef __cplusplus //由于是C++程序调用C的库,必须加这段关于stdint.h的定义,不然会报错
#define __STDC_CONSTANT_MACROS
#ifdef _STDINT_H
#undef _STDINT_H
#endif
#include
#endif
#include "libavutil/opt.h"
#include "libavutil/channel_layout.h"
#include "libavutil/samplefmt.h"
#include "libswresample/swresample.h"
}
bool Resample (short* pWavBuf, int wavLen, short* pWav16k, int wavLen16k) {
//pWavBuf为输入的raw格式语音数据,采样率8K,采样深度16bit;
//wavLen为输入数据的长度,单位为样点数,并非数据长度的字节数
//pWav16k为输出的缓存
//wavLen16k为输出数据的长度,单位依然为样点数
//setting src and dst format
int64_t src_ch_layout = AV_CH_LAYOUT_MONO, dst_ch_layout = AV_CH_LAYOUT_MONO; //声道的类型
int src_rate = 8000, dst_rate = 16000; //采样率
uint8_t **src_data = NULL, uint8_t **dst_data = NULL; //数据缓存
int src_nb_channels = 0, dst_nb_channels = 0; //声道数,在后续通过函数获得
int src_linesize, dst_linesize;
int src_nb_samples = wavLen, dst_nb_samples, max_dst_nb_samples; //采样点数(samples * channels)
enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_S16, dst_sample_fmt = AV_SAMPLE_FMT_S16; //编码格式,此处为16bit有符号数
//context定义上下文
struct SwrContext *swr_ctx;
int ret;
//create resampler context
swr_ctx = swr_alloc_set_opts(NULL, dst_ch_layout, dst_sample_fmt, dst_rate, src_ch_layout, src_sample_fmt, src_rate, 0, NULL); //设置选项
if (!swr_ctx) {
printf("Cannot allocate resampler context!\n");
return false;
}
//initialize resampling context 初始化上下文,每次进行设置后都必须重新初始化上下文使其生效
if ((ret = swr_init(swr_ctx)) < 0) {
printf("Initialize the resampling context failed!\n");
return false;
}
//allocate source sample buffer
src_nb_channels = av_get_channel_layout_nb_channels(src_ch_layout); //声道数
ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize, src_nb_channels, src_nb_samples, src_sample_fmt, 0); //分配空间
if (ret < 0) {
printf("allocate source samples failed!\n");
return false;
}
//compute the number of converted samples and allocate buffer
max_dst_nb_samples = dst_nb_samples = av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP); //计算输出的样本数,向上取整
dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout);
ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize, dst_mb_channels, dst_nb_samples, dst_sample_fmt, 0);
if (ret < 0) {
printf("allocate destination samples failed!\n");
return false;
}
//copy wav data to src_data
memcpy(src_data[0], (char*)pWavBuf, wavLen * sizeof(short));
//compute number of dst samples
dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, src_rate) + src_nb_samples, dst_rate, src_rate, AV_ROUND_UP); //delay的作用我这里可能用不到,网上查到的说法是为了保证能实时处理,给函数处理的时间内新产生的数据分配空间
if (dst_nb_samples > max_dst_nb_samples) {
av_freep(&dst_data[0]);
ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels, dst_nb_samples, dst_sample_fmt, 1);
if (ret < 0) {
printf("allocate destination samples failed!\n");
return false;
}
max_dst_nb_samples = dst_nb_samples;
}
//resample
ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, (const uint8_t **)src_data, src_nb_samples);
if (ret < 0) {
printf("Error while converting.\n");
return false;
}
int dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels, ret, dst_sample_fmt, 1); //buffer size of dst实际转换的输出样本占的空间
if (dst_bufsize < 0) {
printf("Cannot get sample buffer size!\n");
return false;
}
memcpy((char*)pWav16k, dst_data[0], dst_bufsize); //保存转换输出
wavLen16k = ret; //转换的样本数
//flush swr期待后续的输入,会有一部分转换完的数据还留在缓冲区中,需要通过告知swr没有输入了来冲出缓冲区中的数据。方法是将输入的数据置为NULL,长度置为0。
dst_nb_samples -= ret; //剩余长度
ret = swr_convert(swr_ctx, dst_data, dst_nb_samples, NULL, 0);
int rest_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels, ret, dst_sample_fmt, 1);
if (rest_bufsize < 0) {
printf("Can not get sample buffer size!\n");
return false;
}
memcpy((char*)pWav16k + dst_bufsize, dst_data[0], rest_bufsize); //保存剩余数据
wavLen16k += ret; //剩余的样本数
//free
if (src_data) {
av_freep(&src_data[0]);
}
av_freep(&src_data);
if (dst_data) {
av_freep(&dst_data[0]);
}
av_freep(dst_data);
swr_free(&swr_ctx);
return true;
}
另外,C++调用的时候,还需要在libavutil下的commom.h头文件中增加以下定义,否则依然会报错。
#ifndef INT64_C
#define INT64_C(c) (c##LL)
#define UINT64_C(c) (c##ULL)
#endif