您的位置:首页 > 编程语言

简洁明了的插值音频重采样算法例子 (附完整C代码)

2018-03-26 22:00 483 查看
近一段时间在图像算法以及音频算法之间来回游走。

经常有一些需求,需要将音频进行采样转码处理。

现有的知名开源库,诸如: webrtc , sox等,

代码阅读起来实在闹心。

而音频重采样其实也就是插值算法。

与图像方面的插值算法没有太大的区别。

基于双线性插值的思路。

博主简单实现一个简洁的重采样算法,

用在对采样音质要求不高的情况下,也是够用了。

编解码库采用dr_wav

https://github.com/mackron/dr_libs/blob/master/dr_wav.h

近期有点强迫症,纯c实现。

贴上完整代码:

#ifdef __cplusplus
extern "C" {
#endif

#define  _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
//采用https://github.com/mackron/dr_libs/blob/master/dr_wav.h 解码
#define DR_WAV_IMPLEMENTATION

#include "dr_wav.h"

void resampler(char *in_file, char *out_file);

//写wav文件
void wavWrite_int16(char *filename, int16_t *buffer, int sampleRate, uint32_t totalSampleCount) {
drwav_data_format format;
format.container = drwav_container_riff;     // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64.
format.format = DR_WAVE_FORMAT_PCM;          // <-- Any of the DR_WAVE_FORMAT_* codes.
format.channels = 1;
format.sampleRate = (drwav_uint32) sampleRate;
format.bitsPerSample = 16;
drwav *pWav = drwav_open_file_write(filename, &format);
if (pWav) {
drwav_uint64 samplesWritten = drwav_write(pWav, totalSampleCount, buffer);
drwav_uninit(pWav);
if (samplesWritten != totalSampleCount) {
fprintf(stderr, "ERROR\n");
exit(1);
}
}
}

//读取wav文件
int16_t *wavRead_int16(char *filename, uint32_t *sampleRate, uint64_t *totalSampleCount) {
unsigned int channels;
int16_t *buffer = drwav_open_and_read_file_s16(filename, &channels, sampleRate, totalSampleCount);
if (buffer == NULL) {
printf("读取wav文件失败.");
}
//仅仅处理单通道音频
if (channels != 1) {
drwav_free(buffer);
buffer = NULL;
*sampleRate = 0;
*totalSampleCount = 0;
}
return buffer;
}

//分割路径函数
void splitpath(const char *path, char *drv, char *dir, char *name, char *ext) {
const char *end;
const char *p;
const char *s;
if (path[0] && path[1] == ':') {
if (drv) {
*drv++ = *path++;
*drv++ = *path++;
*drv = '\0';
}
} else if (drv)
*drv = '\0';
for (end = path; *end && *end != ':';)
end++;
for (p = end; p > path && *--p != '\\' && *p != '/';)
if (*p == '.') {
end = p;
break;
}
if (ext)
for (s = end; (*ext = *s++);)
ext++;
for (p = end; p > path;)
if (*--p == '\\' || *p == '/') {
p++;
break;
}
if (name) {
for (s = p; s < end;)
*name++ = *s++;
*name = '\0';
}
if (dir) {
for (s = path; s < p;)
*dir++ = *s++;
*dir = '\0';
}
}

void resampleData(const int16_t *sourceData, int32_t sampleRate, uint32_t srcSize, int16_t *destinationData,
int32_t newSampleRate) {
if (sampleRate == newSampleRate) {
memcpy(destinationData, sourceData, srcSize * sizeof(int16_t));
return;
}
uint32_t last_pos = srcSize - 1;
uint32_t dstSize = (uint32_t) (srcSize * ((float) newSampleRate / sampleRate));
for (uint32_t idx = 0; idx < dstSize; idx++) {
float index = ((float) idx * sampleRate) / (newSampleRate);
uint32_t p1 = (uint32_t) index;
float coef = index - p1;
uint32_t p2 = (p1 == last_pos) ? last_pos : p1 + 1;
destinationData[idx] = (int16_t) ((1.0f - coef) * sourceData[p1] + coef * sourceData[p2]);
}
}

void resampler(char *in_file, char *out_file) {
//音频采样率
uint32_t in_sampleRate = 0;
//总音频采样数
uint64_t totalSampleCount = 0;
int16_t *data_in = wavRead_int16(in_file, &in_sampleRate, &totalSampleCount);
uint32_t out_sampleRate = in_sampleRate * 2;
uint32_t out_size = (uint32_t) (totalSampleCount * ((float) out_sampleRate / in_sampleRate));
int16_t *data_out = (int16_t *) malloc(out_size * sizeof(int16_t));
//如果加载成功
if (data_in != NULL && data_out != NULL) {
resampleData(data_in, in_sampleRate, (uint32_t) totalSampleCount, data_out, out_sampleRate);
wavWrite_int16(out_file, data_out, out_sampleRate, (uint32_t) out_size);
free(data_in);
free(data_out);
} else {
if (data_in) free(data_in);
if (data_out) free(data_out);
}
}

int main(int argc, char *argv[]) {
printf("Audio Processing\n");
printf("博客:http://cpuimage.cnblogs.com/\n");
printf("音频插值重采样\n");
if (argc < 2)
return -1;

char *in_file = argv[1];
char drive[3];
char dir[256];
char fname[256];
char ext[256];
char out_file[1024];
splitpath(in_file, drive, dir, fname, ext);
sprintf(out_file, "%s%s%s_out%s", drive, dir, fname, ext);
resampler(in_file, out_file);
getchar();
printf("按任意键退出程序 \n");
return 0;
}

#ifdef __cplusplus
}
#endif


不多注释,代码比较简单,一看就明了。

示例具体流程为:

加载wav(拖放wav文件到可执行文件上)->重采样为原采样的2倍->保存wav

若有其他相关问题或者需求也可以邮件联系俺探讨。

邮箱地址是:
gaozhihan@vip.qq.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: