以前有大概介紹了音頻採樣相關的思路,詳情見《簡潔明瞭的插值音頻重採樣算法例子 (附完整C代碼)》。html
音頻方面的開源項目不少不少。git
最知名的莫過於谷歌開源的WebRTC,github
其中的音頻模塊就包含有 算法
AGC自動增益補償(Automatic Gain Control)
自動調麥克風的收音量,使與會者收到必定的音量水平,不會因發言者與麥克風的距離改變時,聲音有忽大忽小聲的缺點。函數
ANS背景噪音抑制(Automatic Noise Suppression)
探測出背景固定頻率的雜音並消除背景噪音。post
AEC是回聲消除器(Acoustic Echo Canceller)
對揚聲器信號與由它產生的多路徑回聲的相關性爲基礎,創建遠端信號的語音模型,利用它對回聲進行估計,並不斷地修改濾波器的係數,使得估計值更加逼近真實的回聲。而後,將回聲估計值從話筒的輸入信號中減去,從而達到消除回聲的目的,AEC還將話筒的輸入與揚聲器過去的值相比較,從而消除延長延遲的屢次反射的聲學回聲。根椐存儲器存放的過去的揚聲器的輸出值的多少,AEC能夠消除各類延遲的回聲。學習
在《音頻增益響度分析 ReplayGain 附完整C代碼示例》也說起到了。優化
不過本文還不是着重於這三個算法,仍是先從採樣算法來。ui
固然有興趣的小夥伴,建議去看下 WebRTC中與signal_processing_library相關的操做算法。spa
有很多優化的思路能夠學習之。
這裏也不展開了。
以前說過採樣能夠採用簡單的插值的方式進行模擬處理,在精度要求不高的狀況下。
可是如果對精度有所要求,那就另論了。
好在前人踩坑,後人走路。
WebRTC中有一個音頻採樣器的類,雖然有必定的使用限制,可是在大多數應用場景下,也夠用了。
WebRTC的代碼是很乾淨,奈何,各個頭文件之間的依賴,實在混亂。
不過稍微耐心,仍是能把代碼理出個七七八八。
稍微花了時間,造福下你們。
將WebRTC中的採樣器代碼單獨抽離出來,
並編寫了C++示例代碼。
完整示例代碼:
#include <cstdio> #include <cstdlib> #include <cstdint> //採用https://github.com/mackron/dr_libs/blob/master/dr_wav.h 解碼 #define DR_WAV_IMPLEMENTATION #include "dr_wav.h" #include "resampler.h" //寫wav文件 void wavWrite_int16(char *filename, int16_t *buffer, size_t sampleRate, size_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 == nullptr) { printf("讀取wav文件失敗."); } //僅僅處理單通道音頻 if (channels != 1) { drwav_free(buffer); buffer = nullptr; *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'; } } int16_t *resampler(int16_t *data_in, size_t totalSampleCount, size_t in_sample_rate, size_t out_sample_rate) { if (data_in == nullptr) return nullptr; if (in_sample_rate == 0) return nullptr; size_t lengthIn = in_sample_rate / 100; size_t maxLen = out_sample_rate / 100; const int channels = 1; Resampler rs; if (rs.Reset(in_sample_rate, out_sample_rate, channels) == -1) return nullptr; size_t outLen = (size_t) (totalSampleCount * out_sample_rate / in_sample_rate); int16_t *data_out = (int16_t *) malloc(outLen * sizeof(int16_t)); if (data_out == nullptr) return nullptr; size_t nCount = (totalSampleCount / lengthIn); size_t nLast = totalSampleCount - (lengthIn * nCount); int16_t *samplesIn = data_in; int16_t *samplesOut = data_out; outLen = 0; for (int i = 0; i < nCount; i++) { rs.Push(samplesIn, lengthIn, samplesOut, maxLen, outLen); samplesIn += lengthIn; samplesOut += outLen; } if (nLast != 0) { const int max_samples = 1920; int16_t samplePatchIn[max_samples] = {0}; int16_t samplePatchOut[max_samples] = {0}; memcpy(samplePatchIn, samplesIn, nLast * sizeof(int16_t)); rs.Push(samplesIn, nLast, samplePatchOut, maxLen, outLen); memcpy(samplesOut, samplePatchOut, (nLast * out_sample_rate / in_sample_rate) * sizeof(int16_t)); } return data_out; } void ResampleTo(char *in_file, char *out_file, size_t out_sample_rate = 16000) { //音頻採樣率 uint32_t sampleRate = 0; //總音頻採樣數 uint64_t inSampleCount = 0; int16_t *inBuffer = wavRead_int16(in_file, &sampleRate, &inSampleCount); //若是加載成功 if (inBuffer != nullptr) { int16_t *outBuffer = resampler(inBuffer, (size_t) inSampleCount, sampleRate, out_sample_rate); if (outBuffer != nullptr) { size_t outSampleCount = (size_t) (inSampleCount * (out_sample_rate * 1.0f / sampleRate)); wavWrite_int16(out_file, outBuffer, out_sample_rate, outSampleCount); free(outBuffer); } free(inBuffer); } } int main(int argc, char *argv[]) { printf("WebRtc Resampler\n"); printf("博客:http://cpuimage.cnblogs.com/\n"); printf("音頻插值重採樣\n"); printf("支持採樣率: 8k、16k、32k、48k、96k\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); ResampleTo(in_file, out_file, 64000); printf("按任意鍵退出程序 \n"); getchar(); return 0; }
項目地址:https://github.com/cpuimage/WebRTC_Resampler
採樣器的代碼很簡單,詳情見resampler.cpp
示例具體流程爲:
加載wav(拖放wav文件到可執行文件上)->重採樣->保存爲_out.wav文件
示例比較簡單,用cmake便可進行編譯示例代碼,詳情見CMakeLists.txt。
如有其餘相關問題或者需求也能夠郵件聯繫俺探討。
郵箱地址是: gaozhihan@vip.qq.com