zoukankan      html  css  js  c++  java
  • (转载)mp3格式转wav格式 附完整C++算法实现代码

    转载:https://www.cnblogs.com/cpuimage/p/8282397.html

    近期偶然间看到一个开源项目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("文件打开失败.
    ");
            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 = '';
            }
        }
        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://cpuimage.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 << "按任意键退出程序 
    " << std::endl;
        return 0;
    }
    

      

    示例具体流程为:

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

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

    代码使用c++11标准编译,如果是之前老标准,则会编译报错。

      编译: g++ -o mp3twav mp32wav.cpp  --std=c++11

      执行:./mp3twav output.mp3 

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

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

  • 相关阅读:
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Navicat查询哪些表有指定字段名
  • 原文地址:https://www.cnblogs.com/cyblogs/p/10722345.html
Copyright © 2011-2022 走看看