还是之前的项目,见这篇文章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

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐