简洁明了的插值音频重采样算法例子 (附完整C代码)
近一段时间在图像算法以及音频算法之间来回游走。
经常有一些需求,需要将音频进行采样转码处理。
现有的知名开源库,诸如: 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> #define DR_WAV_IMPLEMENTATION #include "dr_wav.h" #define DR_MP3_IMPLEMENTATION #include "dr_mp3.h" #include "timing.h" void wavWrite_f32(char *filename, float *buffer, int sampleRate, uint32_t totalSampleCount, uint32_t channels) { drwav_data_format format; format.container = drwav_container_riff; format.format = DR_WAVE_FORMAT_IEEE_FLOAT; format.channels = channels; format.sampleRate = (drwav_uint32) sampleRate; format.bitsPerSample = 32; 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, "write file [%s] error.\n", filename); exit(1); } } } float *wavRead_f32(const char *filename, uint32_t *sampleRate, uint64_t *sampleCount, uint32_t *channels) { drwav_uint64 totalSampleCount = 0; float *input = drwav_open_file_and_read_pcm_frames_f32(filename, channels, sampleRate, &totalSampleCount); if (input == NULL) { drmp3_config pConfig; input = drmp3_open_file_and_read_f32(filename, &pConfig, &totalSampleCount); if (input != NULL) { *channels = pConfig.outputChannels; *sampleRate = pConfig.outputSampleRate; } } if (input == NULL) { fprintf(stderr, "read file [%s] error.\n", filename); exit(1); } *sampleCount = totalSampleCount * (*channels); return input; } 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'; } } uint64_t Resample_f32(const float *input, float *output, int inSampleRate, int outSampleRate, uint64_t inputSize, uint32_t channels ) { if (input == NULL) return 0; uint64_t outputSize = inputSize * outSampleRate / inSampleRate; if (output == NULL) return outputSize; double stepDist = ((double) inSampleRate / (double) outSampleRate); const uint64_t fixedFraction = (1LL << 32); const double normFixed = (1.0 / (1LL << 32)); uint64_t step = ((uint64_t) (stepDist * fixedFraction + 0.5)); uint64_t curOffset = 0; for (uint32_t i = 0; i < outputSize; i += 1) { for (uint32_t c = 0; c < channels; c += 1) { *output++ = (float) (input[c] + (input[c + channels] - input[c]) * ( (double) (curOffset >> 32) + ((curOffset & (fixedFraction - 1)) * normFixed) ) ); } curOffset += step; input += (curOffset >> 32) * channels; curOffset &= (fixedFraction - 1); } return outputSize; } uint64_t Resample_s16(const int16_t *input, int16_t *output, int inSampleRate, int outSampleRate, uint64_t inputSize, uint32_t channels ) { if (input == NULL) return 0; uint64_t outputSize = inputSize * outSampleRate / inSampleRate; if (output == NULL) return outputSize; double stepDist = ((double) inSampleRate / (double) outSampleRate); const uint64_t fixedFraction = (1LL << 32); const double normFixed = (1.0 / (1LL << 32)); uint64_t step = ((uint64_t) (stepDist * fixedFraction + 0.5)); uint64_t curOffset = 0; for (uint32_t i = 0; i < outputSize; i += 1) { for (uint32_t c = 0; c < channels; c += 1) { *output++ = (int16_t) (input[c] + (input[c + channels] - input[c]) * ( (double) (curOffset >> 32) + ((curOffset & (fixedFraction - 1)) * normFixed) ) ); } curOffset += step; input += (curOffset >> 32) * channels; curOffset &= (fixedFraction - 1); } return outputSize; } void printUsage() { printf("usage:\n"); printf("./Resampler input.wav 48000\n"); printf("./Resampler input.mp3 16000\n"); printf("or\n"); printf("./Resampler input.wav output.wav 8000\n"); printf("./Resampler input.mp3 output.wav 44100\n"); printf("press any key to exit.\n"); getchar(); } void resampler(char *in_file, char *out_file, uint32_t targetSampleRate) { if (targetSampleRate == 0) { printUsage(); return; } uint32_t sampleRate = 0; uint64_t sampleCount = 0; uint32_t channels = 0; float *input = wavRead_f32(in_file, &sampleRate, &sampleCount, &channels); uint64_t targetSampleCount = Resample_f32(input, 0, sampleRate, targetSampleRate, sampleCount, channels); if (input) { float *output = (float *) malloc(targetSampleCount * sizeof(float)); if (output) { double startTime = now(); Resample_f32(input, output, sampleRate, targetSampleRate, sampleCount / channels, channels); double time_interval = calcElapsed(startTime, now()); printf("time interval: %f ms\n ", (time_interval * 1000)); wavWrite_f32(out_file, output, targetSampleRate, (uint32_t) targetSampleCount, channels); free(output); } free(input); } } int main(int argc, char *argv[]) { printf("Audio Processing\n"); printf("blog:http://cpuimage.cnblogs.com/\n"); printf("Audio Resampler\n"); if (argc < 3) { printUsage(); return -1; } char *in_file = argv[1]; if (argc > 3) { char *out_file = argv[2]; uint32_t targetSampleRate = (uint32_t) atoi(argv[3]); resampler(in_file, out_file, targetSampleRate); } else { int32_t targetSampleRate = (uint32_t) atoi(argv[2]); 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.wav", drive, dir, fname); resampler(in_file, out_file, targetSampleRate); } return 0; } #ifdef __cplusplus } #endif
项目地址:
https://github.com/cpuimage/resampler/
不多注释,代码比较简单,一看就明了。
示例具体流程为:
加载wav(拖放wav文件到可执行文件上)->重采样为原采样的2倍->保存wav
若有其他相关问题或者需求也可以邮件联系俺探讨。
邮箱地址是:
gaozhihan@vip.qq.com