iCMS

mp3格式转wav格式 附完整C++算法实现代码

好喜欢专业转载优质博客文章的地方哦~

近期偶然间看到一个开源项目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,fp);

text[0] = 'W';

text[1] = 'A';

text[2] = 'V';

text[3] = 'E';

fwrite(text,fp);

text[0] = 'f';

text[1] = 'm';

text[2] = 't';

text[3] = ' ';

fwrite(text,fp);

long_number = 16;

fwrite(&long_number,fp);

int16_t short_number = FORMAT_PCM;//默认音频格式

fwrite(&short_number,2,fp);

short_number = channels; // 音频通道数

fwrite(&short_number,fp);

long_number = sampleRate; // 采样率

fwrite(&long_number,fp);

long_number = sampleRate * nbyte; // 比特率

fwrite(&long_number,fp);

short_number = nbyte; // 块对齐

fwrite(&short_number,fp);

short_number = nbit; // 采样精度

fwrite(&short_number,fp);

char data[4] = { 'd','a','t','a' };

fwrite(data,fp);

long_number = totalSampleCount;

fwrite(&long_number,fp);

fwrite(buffer,totalSampleCount,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,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,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 = '';

}

}

else if (drv)

*drv = '';

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 = '';

}

if (dir) {

for (s = path; s < p;)

*dir++ = *s++;

*dir = '';

}

}

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",fname);

wavWrite_int16(out_file,wavBuffer,sampleRate,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++算法实现代码

支付宝:

mp3格式转wav格式   附完整C++算法实现代码

0

上一篇:

:下一篇

精彩评论

暂无评论...
验证码 换一张
取 消

热门标签