[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还要你选择采样率等信息;
生成wav之后,你拖进去,就能听到效果了;
参考链接:
https://blog.csdn.net/leixiaohua1020/article/details/50534316