mp3格式轉wav格式 附完整C++算法實現代碼
阿新 • • 發佈:2018-01-14
結果 lsa orm 掃碼 sampler mp3文件 采樣率 mp3 完整
近期偶然間看到一個開源項目minimp3
Minimalistic MP3 decoder single header library
項目地址:
https://github.com/lieff/minimp3
單文件頭的最小mp3解碼器。
一直很想抽時間好好看上一看。
最好的學習方式就是寫個實用性的工程項目。
例如實現mp3轉wav格式。
嗯,這篇博文就是這麽來的。
閱讀了下minimp3的源碼,有一兩處小bug,
這個解碼算法可以進一步提速優化的地方還有不少。
後面有時間,再好好庖丁解牛。
基於這個庫,實現mp3轉wav的代碼行數不到300行。
小巧而簡潔,算是簡單的拋磚引玉了。
個人習慣,很少寫註釋,
所以盡可能把代碼寫得清晰易懂,當然也有犯懶的時候。
完整代碼:
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_DEPRECATE 1 #define _CRT_NONSTDC_NO_DEPRECATE 1 #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <time.h> #include <iostream> // ref:https://github.com/lieff/minimp3/blob/master/minimp3.h#define MINIMP3_IMPLEMENTATION #include "minimp3.h" #include <sys/stat.h> auto const epoch = clock(); static double now() { return (clock() - epoch); }; template <typename FN> static double bench(const FN &fn) { auto took = -now(); return (fn(), took + now()) / 1000; } //寫wav文件void wavWrite_int16(char* filename, int16_t* buffer, int sampleRate, uint32_t totalSampleCount, int channels = 1) { FILE* fp = fopen(filename, "wb"); if (fp == NULL) { printf("文件打開失敗.\n"); return; } //修正寫入的buffer長度 totalSampleCount *= sizeof(int16_t)*channels; int nbit = 16; int FORMAT_PCM = 1; int nbyte = nbit / 8; char text[4] = { ‘R‘, ‘I‘, ‘F‘, ‘F‘ }; uint32_t long_number = 36 + totalSampleCount; fwrite(text, 1, 4, fp); fwrite(&long_number, 4, 1, fp); text[0] = ‘W‘; text[1] = ‘A‘; text[2] = ‘V‘; text[3] = ‘E‘; fwrite(text, 1, 4, fp); text[0] = ‘f‘; text[1] = ‘m‘; text[2] = ‘t‘; text[3] = ‘ ‘; fwrite(text, 1, 4, fp); long_number = 16; fwrite(&long_number, 4, 1, fp); int16_t short_number = FORMAT_PCM;//默認音頻格式 fwrite(&short_number, 2, 1, fp); short_number = channels; // 音頻通道數 fwrite(&short_number, 2, 1, fp); long_number = sampleRate; // 采樣率 fwrite(&long_number, 4, 1, fp); long_number = sampleRate * nbyte; // 比特率 fwrite(&long_number, 4, 1, fp); short_number = nbyte; // 塊對齊 fwrite(&short_number, 2, 1, fp); short_number = nbit; // 采樣精度 fwrite(&short_number, 2, 1, fp); char data[4] = { ‘d‘, ‘a‘, ‘t‘, ‘a‘ }; fwrite(data, 1, 4, fp); long_number = totalSampleCount; fwrite(&long_number, 4, 1, fp); fwrite(buffer, totalSampleCount, 1, fp); fclose(fp); } //讀取文件buffer char *getFileBuffer(const char *fname, int *size) { FILE * fd = fopen(fname, "rb"); if (fd == 0) return 0; struct stat st; char *file_buf = 0; if (fstat(fileno(fd), &st) < 0) goto doexit; file_buf = (char *)malloc(st.st_size + 1); if (file_buf != NULL) { if (fread(file_buf, st.st_size, 1, fd) < 1) { fclose(fd); return 0; } file_buf[st.st_size] = 0; } if (size) *size = st.st_size; doexit: fclose(fd); return file_buf; } //mp3解碼 int16_t* DecodeMp3ToBuffer(char* filename, uint32_t *sampleRate, uint32_t *totalSampleCount, unsigned int *channels) { int music_size = 0; int alloc_samples = 1024 * 1024, num_samples = 0; int16_t *music_buf = (int16_t *)malloc(alloc_samples * 2 * 2); unsigned char *file_buf = (unsigned char *)getFileBuffer(filename, &music_size); if (file_buf != NULL) { unsigned char *buf = file_buf; mp3dec_frame_info_t info; mp3dec_t dec; mp3dec_init(&dec); for (;;) { int16_t frame_buf[2 * 1152]; int samples = mp3dec_decode_frame(&dec, buf, music_size, frame_buf, &info); if (alloc_samples < (num_samples + samples)) { alloc_samples *= 2; int16_t* tmp = (int16_t *)realloc(music_buf, alloc_samples * 2 * info.channels); if (tmp) music_buf = tmp; } if (music_buf) memcpy(music_buf + num_samples*info.channels, frame_buf, samples*info.channels * 2); num_samples += samples; if (info.frame_bytes <= 0 || music_size <= (info.frame_bytes + 4)) break; buf += info.frame_bytes; music_size -= info.frame_bytes; } if (alloc_samples > num_samples) { int16_t* tmp = (int16_t *)realloc(music_buf, num_samples * 2 * info.channels); if (tmp) music_buf = tmp; } if (sampleRate) *sampleRate = info.hz; if (channels) *channels = info.channels; if (num_samples) *totalSampleCount = num_samples; free(file_buf); return music_buf; } if (music_buf) free(music_buf); return 0; } //分割路徑函數 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‘; } } int main(int argc, char* argv[]) { std::cout << "Audio Processing " << std::endl; std::cout << "博客:http://tntmonks.cnblogs.com/" << std::endl; std::cout << "mp3 轉 wav." << std::endl; if (argc < 2) return -1; char* in_file = argv[1]; //總音頻采樣數 uint32_t totalSampleCount = 0; //音頻采樣率 uint32_t sampleRate = 0; //通道數 unsigned int channels = 0; int16_t* wavBuffer = NULL; double nLoadTime = bench([&] { wavBuffer = DecodeMp3ToBuffer(in_file, &sampleRate, &totalSampleCount, &channels); }); std::cout << " 加載耗時: " << int(nLoadTime * 1000) << " 毫秒" << std::endl; //保存結果 double nSaveTime = bench([&] { 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.wav", drive, dir, fname); wavWrite_int16(out_file, wavBuffer, sampleRate, totalSampleCount, channels); }); std::cout << " 保存耗時: " << int(nSaveTime * 1000) << " 毫秒" << std::endl; if (wavBuffer) { free(wavBuffer); } getchar(); std::cout << "按任意鍵退出程序 \n" << std::endl; return 0; }
示例具體流程為:
加載mp3(拖放mp3文件到可執行文件上)->解碼mp3->保存wav
並對 加載,保存 這2個環節都進行了耗時計算並輸出。
若有其他相關問題或者需求也可以郵件聯系俺探討。
郵箱地址是:
[email protected]
若此博文能幫到您,歡迎掃碼小額贊助。
微信:
支付寶:
mp3格式轉wav格式 附完整C++算法實現代碼