欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[Video and Audio Data Processing]将PCM16LE双声道音频采样数据转换为WAVE格式音频数据

程序员文章站 2022-07-06 10:17:17
...

0. 代码

代码还是用的雷神写的,我几乎每句代码都写了注解,可以在visual studio 2019成功跑通运行。

音源获取地址: https://github.com/leixiaohua1020/simplest_mediadata_test

extern "C"
{
#ifdef __cplusplus
#define __STDC_CONSTANT_MACROS

#endif

}
extern "C" {

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
}







/**
 * Convert PCM16LE raw data to WAVE format
 * @param pcmpath      Input PCM file.
 * @param channels     Channel number of PCM file.
 * @param sample_rate  Sample rate of PCM file.
 * @param wavepath     Output WAVE file.
 */

 //注意有些编译器是64位的,需要把unsigned long修改为unsigned int,
 //说的更详细一点,long占据的字节数还和编译器的数据模型相关,随意在编译器里面加句打印即可check
 //printf("unsigned long a= %d\n,unsigned int =%d", sizeof(unsigned long)
 //,sizeof(unsigned int));


int simplest_pcm16le_to_wave(const char* pcmpath, int channels, 
int sample_rate, const char* wavepath)
{

    //共占4+4+4=12字节
    typedef struct WAVE_HEADER {  //wav文件头
        char         fccID[4];
        unsigned   long    dwSize;
        char         fccType[4];
    }WAVE_HEADER;

    typedef struct WAVE_FMT {   //wav文件头
//共占4+4+2+2+4+4+2+2=24个字节
        char         fccID[4];
        unsigned   long       dwSize;
        unsigned   short     wFormatTag;
        unsigned   short     wChannels;
        unsigned   long       dwSamplesPerSec;
        unsigned   long       dwAvgBytesPerSec;
        unsigned   short     wBlockAlign;
        unsigned   short     uiBitsPerSample;
    }WAVE_FMT;

    typedef struct WAVE_DATA {   // wav文件头,共占4+4=8个字节
        char       fccID[4];
        unsigned long dwSize;
    }WAVE_DATA;


    if (channels == 0 || sample_rate == 0) {
        channels = 2;
        sample_rate = 44100;
    }
    int bits = 16;

    WAVE_HEADER   pcmHEADER;
    WAVE_FMT   pcmFMT;
    WAVE_DATA   pcmDATA;

    unsigned   short   m_pcmData;
    FILE* fp, * fpout;

    fp = fopen(pcmpath, "rb"); //输入文件
    if (fp == NULL) {
        printf("open pcm file error\n");
        return -1;
    }
    fpout = fopen(wavepath, "wb+");//输出文件
    if (fpout == NULL) {
        printf("create wav file error\n");
        return -1;
    }
    //WAVE_HEADER
    memcpy(pcmHEADER.fccID, "RIFF", strlen("RIFF"));
    //	void *memcpy(void *dest, const void *src, size_t n);
    // 它的功能是从src的开始位置拷贝n个字节的数据到dest。
    // 如果dest存在数据,将会被覆盖。memcpy函数的返回值是dest的指针。

    memcpy(pcmHEADER.fccType, "WAVE", strlen("WAVE"));
    //拷贝WAVE字符到pcmHEADER.fccType里面


    fseek(fpout, sizeof(WAVE_HEADER), 1);
    //文件指针从当前位置偏移sizeof(WAVE_HEADER)个字节位置
    //WAVE_FMT
    pcmFMT.dwSamplesPerSec = sample_rate;
    //填充结构体,采样率是44100,即每秒采样次数是44100

    pcmFMT.dwAvgBytesPerSec = pcmFMT.dwSamplesPerSec * sizeof(m_pcmData);
    //每秒采样数据的空间大小,也就是正常播放的话,每秒要读多少数据。用来方便程序评估留多大缓冲区。


    pcmFMT.uiBitsPerSample = bits;
    // 位深度是16位

    memcpy(pcmFMT.fccID, "fmt ", strlen("fmt "));
    //拷贝fmt字符到pcmFMT.fccID里面

    pcmFMT.dwSize = 16;
    //继续填充dwSize

    pcmFMT.wBlockAlign = 2;
    pcmFMT.wChannels = channels;
    //双声道

    pcmFMT.wFormatTag = 1;
    //参考https://blog.csdn.net/yi7900/article/details/7481599
    //表明使用pcm数据	

    fwrite(&pcmFMT, sizeof(WAVE_FMT), 1, fpout);
    //在偏移sizeof(WAVE_HEADER)字节之后,写入WAVE_FMT结构数据

    //WAVE_DATA;
    memcpy(pcmDATA.fccID, "data", strlen("data"));
    //填充WAVE_DATA结构体


    pcmDATA.dwSize = 0;//填充WAVE_DATA结构体
    fseek(fpout, sizeof(WAVE_DATA), SEEK_CUR);
    //从当前位置偏移WAVE_DATA结构体长度

    fread(&m_pcmData, sizeof(unsigned short), 1, fp);
    //读取原始的pcm数据,存到m_pcmData里面,取两个字节的数据,读一次


    while (!feof(fp)) {
        pcmDATA.dwSize += 2;//算的总的 pcm占用的字节数
        fwrite(&m_pcmData, sizeof(unsigned short), 1, fpout);
        //从m_pcmData取出数据,2个字节两个字节的写到输出文件
        fread(&m_pcmData, sizeof(unsigned short), 1, fp);
        //从原始pcm数据再取两个字节数据存到m_pcmData里面
    }

    pcmHEADER.dwSize = 44 + pcmDATA.dwSize;
    //44 = sizeof(WAVE_HEADER) + sizeof(WAVE_FMT) + sizeof(WAVE_DATA)
    //加起来是44个字节

    rewind(fpout);
    //文件内部的位置指针重新指向开头
    fwrite(&pcmHEADER, sizeof(WAVE_HEADER), 1, fpout);
    //把补充完整的pcmHEADER,即wav文件头写到输出
    fseek(fpout, sizeof(WAVE_FMT), SEEK_CUR);
    //SEEK_CUR表明是当前位置,偏移已赋值好的WAVE_FMT位置

    fwrite(&pcmDATA, sizeof(WAVE_DATA), 1, fpout);
    //继续填充已填充完整的WAVE_DATA结构,

    fclose(fp);
    //关闭文件
    fclose(fpout);
    //关闭文件

    return 0;
}



int main()
{
    simplest_pcm16le_to_wave("NocturneNo2inEflat_44.1k_s16le.pcm", 
    2, 44100, "output_nocturne.wav");
	return 0;
}

1. 效果

效果就是生成了wav格式的音频文件,原始文件用cool edit pro还要你选择采样率等信息;

[Video and Audio Data Processing]将PCM16LE双声道音频采样数据转换为WAVE格式音频数据
生成wav之后,你拖进去,就能听到效果了;
[Video and Audio Data Processing]将PCM16LE双声道音频采样数据转换为WAVE格式音频数据

参考链接:

https://blog.csdn.net/leixiaohua1020/article/details/50534316

相关标签: 视音频数据处理