广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++解析wav文件方法介绍
  • 957
分享到

C++解析wav文件方法介绍

2024-04-02 19:04:59 957人浏览 泡泡鱼
摘要

目录一、前言二、接口三、具体步骤四、完整源码一、前言 一开始本来在网上找代码,不过改了好几个都不是很好用。因为很多wav文件的fmt块后面并不是data块,经常还带有其他块,正确的方

一、前言

一开始本来在网上找代码,不过改了好几个都不是很好用。因为很多wav文件的fmt块后面并不是data块,经常还带有其他块,正确的方法应该是按MSDN的方法,找到data块再读取。

二、接口

最后接口如下:

class AudioReader
{
public:
	struct PCM
	{
		int _numChannel;//通道数 1,2 AL_FORMAT_MONO8,AL_FORMAT_STEREO8
		int _bitPerSample;//采样数 8,16 
		byte* _data;
		size_t _size;
		size_t _freq;//采样率
		void Delete() { delete[] _data; }
	}; 
	static bool ReadWAV(string_view path_name, PCM& pcm);
};

三、具体步骤

打开文件,这里就是普通的文件流,按二进制、只读打开文件即可:

ifstream ifs;
if (!g_file->GetFile(path_name, ifs))
{
	debug_err(format("打开文件失败:{}", path_name));
	return false;
}

查找riff块:

uint32_t dwChunkSize;
uint32_t dwChunkPosition;
//查找riff块
FindChunk(ifs, fourccRIFF, dwChunkSize, dwChunkPosition);
uint32_t filetype;
ReadChunkData(ifs, &filetype, sizeof(uint32_t), dwChunkPosition);
if (filetype != fourccWAVE)
{
	debug_err(format("匹配标记失败(fourccWAVE):{}", path_name));
	return false;
}

其中fourccRIFF和fourccWAVE是我们定义的标记,也就是处理了下大小端,如下:

#ifdef DND_ENDIAN_BIG
#define fourccRIFF 'RIFF'
#define fourccDATA 'data'
#define fourccFMT 'fmt '
#define fourccWAVE 'WAVE'
#define fourccXWMA 'XWMA'
#define fourccDPDS 'dpds'
#endif
#ifdef DND_ENDIAN_LITTLE
#define fourccRIFF 'FFIR'
#define fourccDATA 'atad'
#define fourccFMT ' tmf'
#define fourccWAVE 'EVAW'
#define fourccXWMA 'AMWX'
#define fourccDPDS 'sdpd'
#endif

而FindChunk和ReadChunkData两个函数,分别是查找一个块,和读取一个块。代码实现有点长,可以参考后面我给出的完整源码。

接着,查找并读取fmt块,这个块描述了wav文件的音频属性,结构如下(部分字段会用到):

//16字节
struct WAVEFormat 
{
	int16_t audioFormat;
	int16_t numChannels;
	int32_t sampleRate;
	int32_t byteRate;
	int16_t blockAlign;
	int16_t bitsPerSample;
};
//查找fmt块
if (!FindChunk(ifs, fourccFMT, dwChunkSize, dwChunkPosition))
{
	debug_err(format("查找块失败(fourccFMT):{}", path_name));
	return false;
}
//读wave信息
WAVEFormat wave_format;
if (!ReadChunkData(ifs, &wave_format, dwChunkSize, dwChunkPosition))
{
	debug_err(format("读取块失败(wave_format):{}", path_name));
	return false;
};

接下来查找data块,根据返回的大小分配内存:

//查找音频数据
if (!FindChunk(ifs, fourccDATA, dwChunkSize, dwChunkPosition))
{
	debug_err(format("查找块失败(fourccDATA):{}", path_name));
	return false;
};
pcm._data = new byte[dwChunkSize];

然后读取data块,将数据读取到我们分配的内存pcm._data。然后记录下一些重要的字段。由于OpenaAL不能直接播放32位(只8、16)的数据,这里简单返回失败。

if (!ReadChunkData(ifs, pcm._data, dwChunkSize, dwChunkPosition))
{
	debug_err(format("读取块失败(pcm数据):{}", path_name));
	pcm.Delete();
	return false;
};
pcm._size = dwChunkSize;
pcm._numChannel = wave_format.numChannels;
pcm._bitPerSample = wave_format.bitsPerSample;
pcm._freq = wave_format.sampleRate;
if (pcm._bitPerSample == 32)
{
	debug_err(format("不支持32位:{}", path_name));
	pcm.Delete();
	return false;
}
return true;

四、完整源码

可以此处获取最新的源码(我将来会添加ogg格式的解析),也可以用下面的:传送门

//.h
class AudioReader
{
public:
	struct PCM
	{
		int _numChannel;//通道数 1,2 AL_FORMAT_MONO8,AL_FORMAT_STEREO8
		int _bitPerSample;//采样数 8,16 
		byte* _data;
		size_t _size;
		size_t _freq;//采样率
		void Delete() { delete[] _data; }
	}; 
	static bool ReadWAV(string_view path_name, PCM& pcm);
};
//16字节
struct WAVEFormat 
{
	int16_t audioFormat;
	int16_t numChannels;
	int32_t sampleRate;
	int32_t byteRate;
	int16_t blockAlign;
	int16_t bitsPerSample;
};
//.cpp
#ifdef DND_ENDIAN_BIG
#define fourccRIFF 'RIFF'
#define fourccDATA 'data'
#define fourccFMT 'fmt '
#define fourccWAVE 'WAVE'
#define fourccXWMA 'XWMA'
#define fourccDPDS 'dpds'
#endif
#ifdef DND_ENDIAN_LITTLE
#define fourccRIFF 'FFIR'
#define fourccDATA 'atad'
#define fourccFMT ' tmf'
#define fourccWAVE 'EVAW'
#define fourccXWMA 'AMWX'
#define fourccDPDS 'sdpd'
#endif
bool FindChunk(ifstream& ifs, uint32_t fourcc, uint32_t& size, uint32_t& pos)
{
	bool ret = true;
	ifs.seekg(0);
	if (ifs.fail())
		return false;
	uint32_t dwChunkType;
	uint32_t dwChunkDataSize;
	uint32_t dwRIFFDataSize = 0;
	uint32_t dwFileType;
	uint32_t bytesRead = 0;
	uint32_t dwOffset = 0;
	while (ret)
	{
		ifs.read((char*)&dwChunkType, sizeof(uint32_t));
		ifs.read((char*)&dwChunkDataSize, sizeof(uint32_t));
		switch (dwChunkType)
		{
		case fourccRIFF:
			dwRIFFDataSize = dwChunkDataSize;
			dwChunkDataSize = 4;
			ifs.read((char*)&dwFileType, sizeof(uint32_t));
			break;
		default:
			ifs.seekg(dwChunkDataSize, std::iOS::cur);
			if (ifs.fail())
				return false;
			break;
		}
		dwOffset += sizeof(uint32_t) * 2;
		if (dwChunkType == fourcc)
		{
			size = dwChunkDataSize;
			pos = dwOffset;
			return true;
		}
		dwOffset += dwChunkDataSize;
		if (bytesRead >= dwRIFFDataSize)
			return false;
	}
	return true;
}
bool ReadChunkData(ifstream& ifs, void* buffer, uint32_t size, uint32_t pos)
{
	ifs.seekg(pos);
	if (ifs.fail())
		return false;
	ifs.read((char*)buffer, size);
	return true;
}
bool AudioReader::ReadWAV(string_view path_name, PCM& pcm)
{
	ifstream ifs;
	if (!g_file->GetFile(path_name, ifs))
	{
		debug_err(format("打开文件失败:{}", path_name));
		return false;
	}
	uint32_t dwChunkSize;
	uint32_t dwChunkPosition;
	//查找riff块
	FindChunk(ifs, fourccRIFF, dwChunkSize, dwChunkPosition);
	uint32_t filetype;
	ReadChunkData(ifs, &filetype, sizeof(uint32_t), dwChunkPosition);
	if (filetype != fourccWAVE)
	{
		debug_err(format("匹配标记失败(fourccWAVE):{}", path_name));
		return false;
	}
	//查找fmt块
	if (!FindChunk(ifs, fourccFMT, dwChunkSize, dwChunkPosition))
	{
		debug_err(format("查找块失败(fourccFMT):{}", path_name));
		return false;
	}
	//读wave信息
	WAVEFormat wave_format;
	if (!ReadChunkData(ifs, &wave_format, dwChunkSize, dwChunkPosition))
	{
		debug_err(format("读取块失败(wave_format):{}", path_name));
		return false;
	};
	//查找音频数据
	if (!FindChunk(ifs, fourccDATA, dwChunkSize, dwChunkPosition))
	{
		debug_err(format("查找块失败(fourccDATA):{}", path_name));
		return false;
	};
	pcm._data = new byte[dwChunkSize];
	if (!ReadChunkData(ifs, pcm._data, dwChunkSize, dwChunkPosition))
	{
		debug_err(format("读取块失败(pcm数据):{}", path_name));
		pcm.Delete();
		return false;
	};
	pcm._size = dwChunkSize;
	pcm._numChannel = wave_format.numChannels;
	pcm._bitPerSample = wave_format.bitsPerSample;
	pcm._freq = wave_format.sampleRate;
	if (pcm._bitPerSample == 32)
	{
		debug_err(format("不支持32位:{}", path_name));
		pcm.Delete();
		return false;
	}
	return true;
}

到此这篇关于c++解析wav文件方法介绍的文章就介绍到这了,更多相关C++解析wav内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++解析wav文件方法介绍

本文链接: https://www.lsjlt.com/news/166791.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++解析wav文件方法介绍
    目录一、前言二、接口三、具体步骤四、完整源码一、前言 一开始本来在网上找代码,不过改了好几个都不是很好用。因为很多wav文件的fmt块后面并不是data块,经常还带有其他块,正确的方...
    99+
    2022-11-13
  • C++解析obj模型文件方法介绍
    目录一、前言二、中间文件三、使用四、完整代码一、前言 tinyobjloader地址: 传送门 而tinyobjloader库只有一个头文件,可以很方便的读取obj文件。支持材质,不...
    99+
    2022-11-13
  • java读文件乱码解决方法介绍
    使用java读取磁盘文件内容容易出现乱码, 问题是由于java使用的编码和被读取文件的编码不一致导致的。(推荐:java视频教程)假设有一个test.txt的文本文件,文件内容为:“测试java读取中文字符串乱码问题”, 其中包含中文,文件...
    99+
    2020-03-06
    java 乱码
  • Linux文件管理方法介绍
    文件简介 用户通过Shell与linux内核交互。Shell是一个命令行解释工具(是一个软件),它将用户输入的命令转换为内核能够理解的语言(命令)。Linux下,很多工作都是通过命令完成的,学好Linux,首先要掌握常用...
    99+
    2022-06-05
    Linux 文件管理
  • C++将音频PCM数据封装成wav文件的方法
    前言 使用声音设备采集的声音数据通常是PCM数据,直接写入文件是无法播放的,通常的做法是将其封装成wav格式,这样播放器就能够识别且播放了。本文将介绍如何将PCM封装成wav的方法。...
    99+
    2022-11-12
  • java文件下载乱码解决方法介绍
    JAVA文件下载时乱码有两种情况:(推荐:java视频教程)1,下载时中文文件名乱码2,下载时因为路径中包含中文文件名乱码,提示找不到文件解决方法见下面部分代码response.setContentType("multipart/form-...
    99+
    2018-10-22
    java基础 java
  • java读取文件中文乱码解决方法介绍
    Java读取文本文件(例如csv文件、txt文件等),遇到中文就变成乱码。(推荐:java视频教程)读取代码如下:List lines=new ArrayList(); BufferedReader br = new Buffere...
    99+
    2020-12-12
    java基础 java
  • java下载文件名乱码解决方法介绍
    java Web开发下载文件功能(代码如下),文件名如果带有中文,经常会出现乱码现象,需要进行编码。(推荐:java视频教程)String fileName = "测试文件.doc"; try { HttpServletRespo...
    99+
    2016-05-19
    java
  • Android中使用PULL方式解析XML文件深入介绍
    一、基本介绍 Android中极力推荐xmlpull方式解析xml。 xmlpull不仅可用在Android上同样也适用于javase,但在javase环境中需自己获取xmlp...
    99+
    2022-06-06
    xml文件 解析xml XML Android
  • SpringBoot加密配置文件方法介绍
    目录一个简单的SpringBoot项目基于Jasypt的加密集成步骤环境准备引入依赖内容加密密码的传递方式在实践中,项目的某些配置信息是需要进行加密处理的,以减少敏感信息泄露的风险。...
    99+
    2023-01-18
    Spring Boot加密配置文件 Spring Boot配置文件加密
  • 在Vista 预览PDF文件的方法介绍
    之前我们曾介绍过Windows Vista的预览功能,即在用户不打开相应文件的情况下查看文件的具体内容,这是个相当有用的功能,可以大大提高日常工作的效率。Windows Vista预览功能可支持的对象包括图片、音频、视频...
    99+
    2023-05-24
    Vista 预览 PDF 文件 方法
  • hosts文件的介绍以及修改hosts文件的方法
    本篇内容介绍了“hosts文件的介绍以及修改hosts文件的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!(1)什么是Hosts文件? ...
    99+
    2023-06-14
  • 简单介绍使用Python解析并修改XML文档的方法
    问题 你想读取一个XML文档,对它最一些修改,然后将结果写回XML文档。 解决方案 使用 xml.etree.ElementTree 模块可以很容易的处理这些任务。 第一步是以通常的方式来解析这个文档。例...
    99+
    2022-06-04
    简单 文档 方法
  • oracle数据库导入TXT文件方法介绍
    客户端连接数据库导入 安装有oracle客户端,配好监听。 以oracle数据库app用户的表user_svc_info为例<span >CREATE TABLE USER_SVC_INFO( PHONE varcha...
    99+
    2023-05-31
    oracle 数据库 txt文件
  • 介绍GitHub上部分文件下载的方法
    GitHub 是全球最大的代码托管平台之一,它汇聚了无数的开发者和开源项目。对于一些需要使用特定项目代码的用户来说,下载已有的 GitHub 仓库或者项目是一个必备的技能。本文将介绍 GitHub 上部分文件下载的方法。一、前置准备在开始下...
    99+
    2023-10-22
  • Python中删除文件的几种方法介绍
    本篇内容主要讲解“Python中删除文件的几种方法介绍”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Python中删除文件的几种方法介绍”吧!前言很多时候开发者需要删除文件。可能是他错误地创建了...
    99+
    2023-06-07
  • Vista操作系统文件共享方法图文介绍
      我们要把D盘中的名为Vista的文件夹共享的话,首先在该文件夹上点鼠标右键选择“共享”。系统将出现“文件共享”设置向导,首先是“选择要与其共享的用户&rdq...
    99+
    2023-06-02
    Vista 操作系统 文件共享 方法 图文
  • C/C++中文件的随机读写详解及其作用介绍
    目录概述随机读写函数例子指针流成员函数随机访问二进制数据概述 文件的操作方式分为顺序读写和随机读写. 顺序读写指文件的指针只能从头移到尾巴. 随机读写指文件指针可以随意移动, 根据需...
    99+
    2022-11-12
  • C++获取文件大小数值的三种方式介绍
    目录方式一方式二方式三总结方式一 推荐使用此方式获得文件大小! C/C++、Window、Linux 环境都可以使用! 通过结构体struct stat获得。 需要包含头文件:#in...
    99+
    2022-11-13
  • Win8系统Word文件默认为只读文件怎么办 解决方法介绍
    很多Win8的用户使用Word来编辑文本,就是对Word文件进行修改的过程。但是如果Word是只读的,那就意味着不能对word文件进行修改,这是什么原因造成的呢?Win8系统Word文件为默认为只读文件怎么...
    99+
    2022-06-04
    文件 解决方法 默认为
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作