与《分享用于学习C++图像处理的代码示例》为姊妹篇。
为了便于学习C++音频处理并研究音频算法,
俺写了一个适合初学者学习的小小框架。
麻雀虽小五脏俱全,仅仅考虑单通道处理。
采用Decoder:dr_wav
https://github.com/mackron/dr_libs/blob/master/dr_wav.h
采用Encoder:原本计划采用dr_wav的Encode,但是dr_wav保存的文件头忘记修正音频数据的大小,
采用博主自己实现的代码,仅供学习之用。
dr_wav用于解析wav文件格式.
关于wav格式的解析移步至:
个人习惯,采用int16的处理方式,也可以通过简单的修改,改为float类型。
wav音频样本可以从***上(https://en.wikipedia.org/wiki/WAV)下载。
注:少数wav格式不支持
Format Bitrate () 1 minute (KiB) Sample
11,025 Hz 16 bit PCM 176.4 1292 11k16bitpcm.wav
8,000 Hz 16 bit PCM 128 938 8k16bitpcm.wav
11,025 Hz 8 bit PCM 88.2 646 11k8bitpcm.wav
11,025 Hz µ-Law 88.2 646 11kulaw.wav
8,000 Hz 8 bit PCM 64 469 8k8bitpcm.wav
8,000 Hz µ-Law 64 469 8kulaw.wav
11,025 Hz 4 bit ADPCM 44.1 323 11kadpcm.wav
8,000 Hz 4 bit ADPCM 32 234 8kadpcm.wav
11,025 Hz GSM 06.10 18 132 11kgsm.wav
8,000 Hz MP3 16 kbit/s 16 117 8kmp316.wav
8,000 Hz GSM 06.10 13 103 8kgsm.wav
8,000 Hz Lernout & Hauspie SBC 12 kbit/s 12 88 8ksbc12.wav
8,000 Hz DSP Group Truespeech 9 66 8ktruespeech.wav
8,000 Hz MP3 8 kbit/s 8 60 8kmp38.wav
8,000 Hz Lernout & Hauspie CELP 4.8 35 8kcelp.wav
附带处理耗时计算,示例演示了一个简单的将音频前面一半静音处理,并简单注释了一下部分逻辑。
完整代码:
1 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <stdint.h> 5 #include <time.h> 6 #include <iostream> 7 //采用https://github.com/mackron/dr_libs/blob/master/dr_wav.h 解码 8 #define DR_WAV_IMPLEMENTATION 9 #include "dr_wav.h" 10 11 auto const epoch = clock(); 12 static double now() 13 { 14 return (clock() - epoch); 15 }; 16 17 template <typename FN> 18 static double bench(const FN &fn) 19 { 20 auto took = -now(); 21 return (fn(), took + now()) / 1000; 22 } 23 24 //写wav文件 25 void wavWrite_int16(char* filename, int16_t* buffer, int sampleRate, uint32_t totalSampleCount) { 26 27 FILE* fp = fopen(filename, "wb"); 28 if (fp == NULL) { 29 printf("文件打开失败.\n"); 30 return; 31 } 32 //修正写入的buffer长度 33 totalSampleCount *= sizeof(int16_t); 34 int nbit = 16; 35 int FORMAT_PCM = 1; 36 int nbyte = nbit / 8; 37 char text[4] = { 'R', 'I', 'F', 'F' }; 38 uint32_t long_number = 36 + totalSampleCount; 39 fwrite(text, 1, 4, fp); 40 fwrite(&long_number, 4, 1, fp); 41 text[0] = 'W'; 42 text[1] = 'A'; 43 text[2] = 'V'; 44 text[3] = 'E'; 45 fwrite(text, 1, 4, fp); 46 text[0] = 'f'; 47 text[1] = 'm'; 48 text[2] = 't'; 49 text[3] = ' '; 50 fwrite(text, 1, 4, fp); 51 52 long_number = 16; 53 fwrite(&long_number, 4, 1, fp); 54 int16_t short_number = FORMAT_PCM;//默认音频格式 55 fwrite(&short_number, 2, 1, fp); 56 short_number = 1; // 音频通道数 57 fwrite(&short_number, 2, 1, fp); 58 long_number = sampleRate; // 采样率 59 fwrite(&long_number, 4, 1, fp); 60 long_number = sampleRate * nbyte; // 比特率 61 fwrite(&long_number, 4, 1, fp); 62 short_number = nbyte; // 块对齐 63 fwrite(&short_number, 2, 1, fp); 64 short_number = nbit; // 采样精度 65 fwrite(&short_number, 2, 1, fp); 66 char data[4] = { 'd', 'a', 't', 'a' }; 67 fwrite(data, 1, 4, fp); 68 long_number = totalSampleCount; 69 fwrite(&long_number, 4, 1, fp); 70 fwrite(buffer, totalSampleCount, 1, fp); 71 fclose(fp); 72 } 73 //读取wav文件 74 int16_t* wavRead_int16(char* filename, uint32_t* sampleRate, uint64_t *totalSampleCount) { 75 76 unsigned int channels; 77 int16_t* buffer = drwav_open_and_read_file_s16(filename, &channels, sampleRate, totalSampleCount); 78 if (buffer == NULL) { 79 printf("读取wav文件失败."); 80 } 81 //仅仅处理单通道音频 82 if (channels != 1) 83 { 84 drwav_free(buffer); 85 buffer = NULL; 86 *sampleRate = 0; 87 *totalSampleCount = 0; 88 } 89 return buffer; 90 } 91 92 //分割路径函数 93 void splitpath(const char* path, char* drv, char* dir, char* name, char* ext) 94 { 95 const char* end; 96 const char* p; 97 const char* s; 98 if (path[0] && path[1] == ':') { 99 if (drv) { 100 *drv++ = *path++; 101 *drv++ = *path++; 102 *drv = '\0'; 103 } 104 } 105 else if (drv) 106 *drv = '\0'; 107 for (end = path; *end && *end != ':';) 108 end++; 109 for (p = end; p > path && *--p != '\\' && *p != '/';) 110 if (*p == '.') { 111 end = p; 112 break; 113 } 114 if (ext) 115 for (s = end; (*ext = *s++);) 116 ext++; 117 for (p = end; p > path;) 118 if (*--p == '\\' || *p == '/') { 119 p++; 120 break; 121 } 122 if (name) { 123 for (s = p; s < end;) 124 *name++ = *s++; 125 *name = '\0'; 126 } 127 if (dir) { 128 for (s = path; s < p;) 129 *dir++ = *s++; 130 *dir = '\0'; 131 } 132 } 133 134 int main(int argc, char* argv[]) 135 { 136 std::cout << "Audio Processing " << std::endl; 137 std::cout << "博客:" << std::endl; 138 std::cout << "支持解析单通道wav格式." << std::endl; 139 140 if (argc < 2) return -1; 141 char* in_file = argv[1]; 142 143 //音频采样率 144 uint32_t sampleRate = 0; 145 //总音频采样数 146 uint64_t totalSampleCount = 0; 147 int16_t* wavBuffer = NULL; 148 double nLoadTime = bench([&] 149 { 150 wavBuffer = wavRead_int16(in_file, &sampleRate, &totalSampleCount); 151 }); 152 std::cout << " 加载耗时: " << int(nLoadTime * 1000) << " 毫秒" << std::endl; 153 154 //如果加载成功 155 if (wavBuffer != NULL) 156 { 157 //将前面一般进行静音处理,直接置零即可 158 for (uint64_t i = 0; i < totalSampleCount / 2; i++) 159 { 160 wavBuffer[i] = 0; 161 } 162 } 163 //保存结果 164 double nSaveTime = bench([&] 165 { 166 char drive[3]; 167 char dir[256]; 168 char fname[256]; 169 char ext[256]; 170 char out_file[1024]; 171 splitpath(in_file, drive, dir, fname, ext); 172 sprintf(out_file, "%s%s%s_out%s", drive, dir, fname, ext); 173 wavWrite_int16(out_file, wavBuffer, sampleRate, totalSampleCount); 174 }); 175 std::cout << " 保存耗时: " << int(nSaveTime * 1000) << " 毫秒" << std::endl; 176 177 getchar(); 178 std::cout << "按任意键退出程序 \n" << std::endl; 179 return 0; 180 }