WAV文件提取PCM数据

2023-05-16

关于WAV文件数据格式请参考WAV文件格式详解。
此份代码实现了PCM数据提取以及32位浮点型PCM数据转16位PCM数据格式。

#include <iostream>
#include <fstream>

#include <string>
#include <stdio.h>

/*wav数据格式头*/
typedef struct RiffChunk
{
	char ID[4];     //4字节;RIFF (0x52 49 46 46),以'RIFF'为标识(大端对齐)
	int size;         //4字节;Size是整个文件的长度减去ID和Size的长度
	char Type[4];       //4字节;WAVE(0x57415645),Type是WAVE表示后面需要两个子块:Format区块和Data区块
} RIFF_CHUNK;

/* sub-chunk "fmt" */
typedef struct FormatChunk
{
	char ID[4];     //4字节;fmt (0x45 66 6d),以'fmt '为标识(大端对齐)
	int size;         //4字节;表示该区块数据的长度(不包含ID和Size的长度)
	short AudioFormat;  //2字节;表示Data区块存储的音频数据的格式,PCM音频数据的值为1
	short NumChannels;  //2字节;表示音频数据的声道数,1:单声道,2:双声道
	int SampleRate;   //4字节;表示音频数据的采样率
	int ByteRate;   //4字节;每秒数据字节数 = SampleRate * NumChannels * BitsPerSample / 8
	short BlockAlign;   //2字节;每个采样所需的字节数 = NumChannels * BitsPerSample / 8
	short BitsPerSample;    //2字节;每个采样存储的bit数,8:8bit,16:16bit,32:32bit
} FORMAT_CHUNK;

/* sub-chunk "data" */
typedef struct DataChunk
{
	char ID[4];     //4字节;data(0x64 61 74 61),以'data'为标识
	int  Size;  //4字节;表示音频数据的有效长度(有效长度后面可能有冗余数据),N = ByteRate * seconds
	//往后的数据就是PCM数据了
} DATA_CHUNK;

//wav头格式
typedef struct WavFormat
{
	RIFF_CHUNK mRiffChunk;
	FORMAT_CHUNK mFormatChunk;
	DATA_CHUNK mDataChunk;
} WAV_FORMAT;

//将32位浮点型转成16位
static void pcm32Float2pcm16bit(const char* path, const char* data, const int dataLen)
{
	if (0 != dataLen % 4)
	{
		std::cout << "dataLen:" << dataLen << "不为4的倍数!!" << std::endl;
		return;
	}
	//float的长度要为4byte
	std::cout << "float len:" << sizeof(float) << std::endl;
	const int _16bitDataLen = dataLen / 4;
	short* sample16 = new short[_16bitDataLen];
	const float* _32bitData = (float*)data;
	for (int level1 = 0; level1 < _16bitDataLen; ++level1)
	{
		sample16[level1] = (short)floor((_32bitData[level1]) * 32767);
	}

	std::string name(path);
	name.append("_32BitTo16Bit.pcm");
	std::ofstream outfile(name.c_str(), std::ofstream::binary | std::ofstream::trunc);
	//保存纯16bitpcm数据
	outfile.write((char*)sample16, _16bitDataLen*2);
	outfile.flush();
	outfile.close();

	delete[] sample16;
}

//保存pcm裸数据
static void savePcmData(const char* path, const char* data, const int dataLen)
{
	std::string name(path);
	name.append(".pcm");
	std::ofstream outfile(name.c_str(), std::ofstream::binary | std::ofstream::trunc);
	outfile.write(data, dataLen);
	outfile.flush();
	outfile.close();
}

int main(int argc, char** argv)
{
	if (2 != argc)
	{
		printf("传入参数有误,只需传入文件名即可!!\n");
		for (int level1 = 0; level1 < argc; ++level1)
		{
			printf("%s\n", argv[level1]);
		}
		return 0;
	}

	FILE* fp = NULL;
	WAV_FORMAT wav;
	errno_t err = fopen_s(&fp, argv[1], "rb");
	if (0 != err) {
		printf("can't open audio file\n");
		printf("err:%d\n", err);
		exit(1);
	}
	std::cout << "file name : " << argv[1] << std::endl;

	fread(&wav, 1, sizeof(WAV_FORMAT), fp);

	printf("RiffChunk id: %s\n", std::string(wav.mRiffChunk.ID, 4).c_str());
	printf("RiffChunk size: %d\n", wav.mRiffChunk.size);
	printf("RiffChunk type %s\n", std::string(wav.mRiffChunk.Type, 4).c_str());

	printf("FormatChunk id %s\n", std::string(wav.mFormatChunk.ID, 4).c_str());
	printf("FormatChunk size: %x\n", wav.mFormatChunk.size);
	printf("FormatChunk AudioFormat: %d\n", wav.mFormatChunk.AudioFormat);
	printf("FormatChunk NumChannels: %d\n", wav.mFormatChunk.NumChannels);
	printf("FormatChunk SampleRate: %d\n", wav.mFormatChunk.SampleRate);
	printf("FormatChunk ByteRate: %d\n", wav.mFormatChunk.ByteRate);
	printf("FormatChunk BlockAlign: %d\n", wav.mFormatChunk.BlockAlign);
	printf("FormatChunk BitsPerSample: %d\n", wav.mFormatChunk.BitsPerSample);

	printf("DataChunk id: %s\n", std::string(wav.mDataChunk.ID, 4).c_str());
	printf("DataChunk Size: %d\n", wav.mDataChunk.Size);


	char* buffer = new char[wav.mDataChunk.Size];
	fread(buffer, 1, wav.mDataChunk.Size, fp);

	std::string orgName(argv[1]);
	std::string baseName = orgName.substr(0, orgName.find("."));
	std::cout << "base name:" << baseName << std::endl;

	//保存纯pcm数据
	savePcmData(baseName.c_str(), buffer, wav.mDataChunk.Size);

	//将32位浮点wav转成16位pcm(需要注意,32位并不一定就是浮点型数据)
	if (32 == wav.mFormatChunk.BitsPerSample)
	{
		pcm32Float2pcm16bit(baseName.c_str(), buffer, wav.mDataChunk.Size);
	}

	delete[] buffer;

	fclose(fp);

	//getchar();
	return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

WAV文件提取PCM数据 的相关文章

  • DICOM图像的三维重建算法——面绘制算法和体绘制算法

    对医学影像进行三维重建最常用的算法有两类 xff1a 一类是基于构建物体表面的面绘制算法 xff1b 一类是直接绘制物体三维体素的体绘制算法 本文通过两种最常用的算法 xff0c 总结面绘制和体绘制的基本思想 1 基于面绘制的Marchin

随机推荐