近期偶然间看到一个开源项目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. LintCode-丑数

    设计一个算法.找出仅仅含素因子3,5,7 的第 k 大的数. 符合条件的数如:3.5.7,9,15...... 您在真实的面试中是否遇到过这个题? Yes 例子 假设k=4, 返回 9 挑战 要求时间 ...

  2. Maven构建真正的J2EE项目

    今天同事问起我眼下用Maven构建的多模块项目架构和曾经用Eclipse创建的Web项目的问题.以下将讲一下使用maven搭建多模块的J2ee项目,以及採用这样的方式搭建项目对日后项目的水平拆分和垂直 ...

  3. MySQL 删除数据库中反复数据(以部分数据为准)

    delete from zqzrdp where tel  in (select min(dpxx_id) from  zqzrdp  group by tel  having count(tel)& ...

  4. tomcat加入系统服务+开机自启

    1.首先将tomcat/bin 下的catalina.sh复制到目录/etc/init.d中,并修改名称为tomcat [root@iZ2318 ~]# sudo cp /usr/local/tomc ...

  5. 关于模式识别中的domain generalization 和 domain adaptation

    今晚听了李文博士的报告"Domain Generalization and Adaptation using Low-Rank Examplar Classifiers",讲的很精 ...

  6. JavaSe:UncaughtExceptionHandler

    Java中,出现对于异常的处理,通常是使用try{}catch()来捕获处理的.但是在使用trycatch时,未必会将所有的异常都捕获到,所以方法声明时可以有throws xxxException. ...

  7. nginx日常维护常用命令

    http://www.jb51.net/article/47750.htm 一.简明nginx常用命令 1. 启动 Nginx poechant@ubuntu:sudo ./sbin/nginx 2. ...

  8. 《编程人生:15位软件先驱访谈录》【PDF】下载

    <编程人生:15位软件先驱访谈录>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382231 内容简介 本书适合所有程序员,也适合 ...

  9. 小白的Python之路 day2 字符编码和转码

    字符编码和转码 详细文章: http://www.cnblogs.com/yuanchenqi/articles/5956943.html http://www.diveintopython3.net ...

  10. 三菱Q系列PLC的io分配

    1.系统基本配置 2.存储卡配置 3.外部IO标号 4.主基板IO模块的IO号分配 5.扩展基板IO口标号 6.标准配置实例 7. 一.输入采样阶段 在输入采样阶段,可编程逻辑控制器以扫描方式依次地读 ...