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

//写wav文件
) {

    FILE* fp = fopen(filename, "wb");
    if (fp == NULL) {
        printf("文件打开失败.\n");
        return;
    }
    //修正写入的buffer长度
    totalSampleCount *= sizeof(int16_t)*channels;
    ;
    ;
    ;
    ] = { 'R', 'I', 'F', 'F' };
    uint32_t long_number =  + totalSampleCount;
    fwrite(text, , , fp);
    fwrite(&long_number, , , fp);
    text[] = 'W';
    text[] = 'A';
    text[] = 'V';
    text[] = 'E';
    fwrite(text, , , fp);
    text[] = 'f';
    text[] = 'm';
    text[] = 't';
    text[] = ' ';
    fwrite(text, , , fp);

    long_number = ;
    fwrite(&long_number, , , fp);
    int16_t short_number = FORMAT_PCM;//默认音频格式
    fwrite(&short_number, , , 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);
    ] = { '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");
    )
        ;
    struct stat st;
    ;
    )
        goto doexit;
    file_buf = ();
    if (file_buf != NULL)
    {
        , fd) < )
        {
            fclose(fd);
            ;
        }
        file_buf[st.st_size] = ;
    }

    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)
{
    ;
     * , num_samples = ;
    int16_t *music_buf = (int16_t *) * );
    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[ * ];
            int samples = mp3dec_decode_frame(&dec, buf, music_size, frame_buf, &info);
            if (alloc_samples < (num_samples + samples))
            {
                alloc_samples *= ;
                int16_t* tmp = (int16_t *) * info.channels);
                if (tmp)
                    music_buf = tmp;
            }
            if (music_buf)
                memcpy(music_buf + num_samples*info.channels, frame_buf, samples*info.channels * );
            num_samples += samples;
             || music_size <= (info.frame_bytes + ))
                break;
            buf += info.frame_bytes;
            music_size -= info.frame_bytes;
        }
        if (alloc_samples > num_samples)
        {
            int16_t* tmp = (int16_t *) * 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);
    ;
}
//分割路径函数
void splitpath(const char* path, char* drv, char* dir, char* name, char* ext)
{
    const char* end;
    const char* p;
    const char* s;
    ] && path[] == ':') {
        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;

    ) ;
    ];

    //总音频采样数
    uint32_t totalSampleCount = ;
    //音频采样率
    uint32_t sampleRate = ;
    //通道数
    unsigned ;
    int16_t* wavBuffer = NULL;
    double nLoadTime = bench([&]
    {
        wavBuffer = DecodeMp3ToBuffer(in_file, &sampleRate, &totalSampleCount, &channels);
    });
    std::cout << ) << " 毫秒" << std::endl;

    //保存结果
    double nSaveTime = bench([&]
    {
        ];
        ];
        ];
        ];
        ];
        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 << ) << " 毫秒" << std::endl;
    if (wavBuffer)
    {
        free(wavBuffer);
    }
    getchar();
    std::cout << "按任意键退出程序 \n" << std::endl;
    ;
}

示例具体流程为:

加载mp3(拖放mp3文件到可执行文件上)->解码mp3->保存wav

并对 加载,保存 这2个环节都进行了耗时计算并输出。

若有其他相关问题或者需求也可以邮件联系俺探讨。

邮箱地址是: 
gaozhihan@vip.qq.com

若此博文能帮到您,欢迎扫码小额赞助。

微信:

支付宝:

mp3格式转wav格式 附完整C++算法实现代码的更多相关文章

  1. 声音变调算法PitchShift(模拟汤姆猫) 附完整C++算法实现代码

    上周看到一个变调算法,挺有意思的,原本计划尝试用来润色TTS合成效果的. 实测感觉还需要进一步改进,待有空再思考改进方案. 算法细节原文,移步链接: http://blogs.zynaptiq.com ...

  2. 不用第三方解码码取得图片宽高 附完整C++算法实现代码

    在特定的应用场景下,有时候我们只是想获取图片的宽高, 但不想通过解码图片才取得这个信息. 预先知道图片的宽高信息,进而提速图片加载,预处理等相关操作以提升体验. 在stackoverflow有一篇相关 ...

  3. 不用第三方解码库取得图片宽高 附完整C++算法实现代码

    在特定的应用场景下,有时候我们只是想获取图片的宽高, 但不想通过解码图片才取得这个信息. 预先知道图片的宽高信息,进而提速图片加载,预处理等相关操作以提升体验. 在stackoverflow有一篇相关 ...

  4. 音频算法之小黄人变声 附完整C代码

    前面提及到<大话音频变声原理 附简单示例代码>与<声音变调算法PitchShift(模拟汤姆猫) 附完整C++算法实现代码> 都稍微讲过变声的原理和具体实现. 大家都知道,算法 ...

  5. Android音频: 怎样使用AudioTrack播放一个WAV格式文件?

    翻译 By Long Luo 原文链接:Android Audio: Play a WAV file on an AudioTrack 译者注: 1. 因为这是技术文章,所以有些词句使用原文,表达更准 ...

  6. 微信小程序语音识别开发过程记录 微信小程序silk转mp3 silk转wav 以及ffmpeg使用

    说说最近在开发微信小程序语音识别遇到的问题吧 最先使用微信小程序录音控件可以拿到silk格式,后来微信官方又支持mp3格式了 但是我们拿到这些格式以后,都还不能直接使用,做语音识别,因为目前百度的语音 ...

  7. 使用jave2实现将wav格式的音频转换成mp3格式

    最近需要用到语音合成功能,网上查阅了一番,发现可以使用腾讯云的语音合成API来完成这个功能,但是腾讯云的api返回的是wav格式的音频文件,这个格式的文件有些不通用,因此需要转换成mp3格式的文件. ...

  8. 小程序语音红包中遇到的 语音识别silk转wav格式 如何在线转 或者mp3转wav格式

    公司在开发一个小程序语音红包,现在遇到的问题就是通过微信的小程序文档接口拿到的录音文件要么是silk格式的,要么是mp3格式的 但是呢,如果要调用百度的语音接口,又必须是wav格式的.也就是说通过微信 ...

  9. c# Use NAudio Library to Convert MP3 audio into WAV audio(将Mp3格式转换成Wav格式)

    Have you been in need of converting mp3 audios to wav audios?  If so, the skill in this article prov ...

随机推荐

  1. 联动加入redmine的wik

    <? php error_reporting(E_ERROR); date_default_timezone_set('Asia/Shanghai'); $red_server = " ...

  2. jQuery 插件 Magnify 开发简介(仿 Windows 照片查看器)

    前言 因为一些特殊的业务需求,经过一个多月的蛰伏及思考,我开发了这款 jQuery 图片查看器插件 Magnify,它实现了 Windows 照片查看器的所有功能,比如模态窗的拖拽.调整大小.最大化, ...

  3. java正则表达式总结

    最近工作要使用文件上传解析,上传还好,但是在解析文件的时候,却踩到了好多坑,今天就说说其中的一块吧,正则匹配. 由于上传的文件统一都是csv文件,所以在解析文本的时候,肯定要碰到正则表达式的,先解释一 ...

  4. .net 图片无损压缩

    命名空间: using System.Drawing.Imaging; using System.Drawing; using System.Drawing.Drawing2D; #region Ge ...

  5. django+Python数据库利用Echarts实现网页动态数据显示

    这几天一直在思考前端--服务器--数据库的之间的数据交互,最后决定了用django来做,为什么呢?因为我这只是在开发阶段,所以就用了django自带的web服务器(很方便)而且呢,它还自带了数据库sq ...

  6. 模板引擎(smarty)知识点总结II

    今天咱们继续来学习smarty!!! 知识点1:对于三种变量 常量的引用 有哪三种变量?a.assign赋值 b.系统保留变量(包括:$smarty.get,$smarty.post,$smarty. ...

  7. Java解析word,获取文档中图片位置

    前言(背景介绍): Apache POI是Apache基金会下一个开源的项目,用来处理office系列的文档,能够创建和解析word.excel.ppt格式的文档. 其中对word文档的处理有两个技术 ...

  8. rpm 命令详解

    参考:http://www.cnblogs.com/xiaochaohuashengmi/archive/2011/10/08/2203153.html rpm是由红帽公司开发的软件包管理方式,使用r ...

  9. ELK开机启动 service文件内容

    为了实现ELK的3部分开机启动,可以添加各项服务对应的service文件,再通过systemctl enable XXX实现ELK所有服务开机启动. Elasticsearch elasticsear ...

  10. 常用API接口汇总

    下面列举了100多个国内常用API接口,并按照 笔记.出行.词典.电商.地图.电影.即时通讯.开发者网站.快递查询.旅游.社交.视频.天气.团队协作.图片与图像处理.外卖.消息推送.音乐.云.语义识别 ...