1. 程式人生 > >mp3格式轉wav格式 附完整C++算法實現代碼

mp3格式轉wav格式 附完整C++算法實現代碼

結果 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++算法實現代碼