近期偶然间看到一个开源项目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. js通用方法检測浏览器是否已安装指定插件(IE与非IE通用)

    /* * 检測是否已安装指定插件 * * pluginName 插件名称 */ function checkPlugins(pluginName) { var np = navigator.plugi ...

  2. 腾讯云数据库团队:MySQL语句复制(SBR)的缺陷列举

    作者介绍: 赵伟 腾讯云TDSQL数据库开发者 MySQL (这里的MySQL是指广义的mysql,包括oracle,mysql,percona,mariadb等)的Statement Based R ...

  3. Jenkins in OpenCASCADE

    Jenkins in OpenCASCADE eryar@163.com Abstract. Jenkins是一个开源软件项目,是基于Java开发的一个持续集成工具,用于监控持续复制的工作,旨在提供一 ...

  4. 破解iframe微信推文(图片)防盗链

    $.ajaxPrefilter(function(options) { if(options.crossDomain && jQuery.support.cors) { var htt ...

  5. Java I/O---RandomAccessFile类(随机访问文件的读取和写入)

    1.JDK API中RandomAccessFile类的描述 此类的实例支持对随机访问文件的读取和写入.随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组.存在指向该隐含数组的光标或索引 ...

  6. scala写算法-用小根堆解决topK

    topK问题是指从大量数据中获取最大(或最小)的k个数,比如从全校学生中寻找成绩最高的500名学生等等. 本问题可采用小根堆解决.思路是先把源数据中的前k个数放入堆中,然后构建堆,使其保持堆序(可以简 ...

  7. Nginx的 HTTP 499 状态码处理

    1.前言 今天在处理一个客户问题,遇到Nginx access log中出现大量的499状态码.实际场景是:客户的域名通过cname解析到我们的Nginx反向代理集群上来,客户的Web服务是由一个负载 ...

  8. [置顶] Xamarin Android安装教程(2016最新亲测安装版)

    写这篇安装教程前要说的几句话 之前很多人想用Vs来开发Android项目,苦于这个环境的安装.的确这并不是一件简单的事情,并不是开发者都能在花一上午能装好,如果你花了一天时间,第一个Xamarin   ...

  9. js scrollTop 事件

    代码: window.onscroll = function() { var t = document.documentElement.scrollTop || document.body.scrol ...

  10. Java后端程序员都做些什么?

    这个问题来自于QQ网友,一句两句说不清楚,索性写个文章. 我刚开始做Web开发的时候,根本没有前端,后端之说. 原因很简单,那个时候服务器端的代码就是一切:接受浏览器的请求,实现业务逻辑,访问数据库, ...