热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

wave文件头解析

文章目录概述格式解析格式详解代码字节序概述WaveformAudioFileFormat(WAVE,又或者是因为扩展名而被大众所知的WAV࿰

文章目录

  • 概述
  • 格式解析
    • 格式详解
    • 代码
    • 字节序




概述

Waveform Audio File Format(WAVE,又或者是因为扩展名而被大众所知的WAV),是微软与IBM公司所开发在个人电脑存储音频流的编码格式,在Windows平台的应用软件受到广泛的支持,地位上类似于麦金塔电脑里的AIFF。[2] 此格式属于资源交换文件格式(RIFF)的应用之一,通常会将采用脉冲编码调制的音频资存储在区块中。也是其音乐发烧友中常用的指定规格之一。由于此音频格式未经过压缩,所以在音质方面不会出现失真的情况,但文件的体积因而在众多音频格式中较为大。

格式解析

格式详解


  1. WAV文件格式详解
  2. WAV

代码


  1. 定义上下文结构体

struct StWAVContext
{std::string name;std::ifstream fileHanler;std::vector> chunks; // 所有的chunk集合StAudioFormat format;char* pcmBuf; // pcm bufferuint32_t pcmSize; // pcm数据的大小
};

  1. 获取当前chunk的ID、Size

void getNextChunk(StWAVContext *context, std::string &id, uint32_t *size)
{char chunk[5] = { 0 }; // ID字段 4bytesstd::ifstream &fp = context->fileHanler;// 获取IDfp.read(chunk, 4);id = std::string(chunk);// 获取Sizememset(chunk, 0, 5);fp.read(chunk, 4);memset(size, 0, sizeof(uint32_t));memcpy(size, chunk, sizeof(uint32_t));
}

  1. 循环解析所有chunk,其中对chunkID的处理基于自己的业务需求。

std::string chunkID;uint32_t chunkSize = 0;uint32_t curPos = 0;do{chunkID.clear();getNextChunk(context, chunkID, &chunkSize);context->chunks.push_back(std::make_pair(chunkID, chunkSize));debugf("Find chunk: ID[%s] Size[%ld]", chunkID.c_str(), chunkSize);curPos = context->fileHanler.tellg();if (chunkID.empty()){errorf("Parse nothing.");break;}if (chunkID == "RIFF"){context->fileHanler.seekg(4, context->fileHanler.cur);}else if (chunkID == "fmt "){parseFormatChunk(context, chunkSize);}else if (chunkID == "data"){parseDataChunk(context, chunkSize);break;}else{context->fileHanler.seekg(chunkSize, context->fileHanler.cur);}} while (true);

  1. 以chunkID == "fmt "为例(注意:fmt后还有一个空格,这是因为chunkID占用了4个字节,而fmt仅占用3个字节的原因),解析特定chunk如下:

void parseFormatChunk(StWAVContext *context, uint32_t chunkSize)
{char str_chunk4B[5] = { 0 };char str_chunk2B[3] = { 0 };uint32_t digit_chunk4B = 0;uint16_t digit_chunk2B = 0;std::ifstream &fp = context->fileHanler;// 解析AudioFormatmemset(&str_chunk2B, 0, sizeof(str_chunk2B));fp.read(str_chunk2B, 2);digit_chunk2B = 0;memcpy(&digit_chunk2B, str_chunk2B, sizeof(digit_chunk2B));if (is_bigEndian()){digit_chunk2B = ntohs(digit_chunk2B);}context->format.type = digit_chunk2B;debugf("File[%s] type: %d.", context->name.c_str(), context->format.type);// 解析channelmemset(&str_chunk2B, 0, sizeof(str_chunk2B));fp.read(str_chunk2B, 2);digit_chunk2B = 0;memcpy(&digit_chunk2B, str_chunk2B, sizeof(digit_chunk2B));if (is_bigEndian()){digit_chunk2B = ntohs(digit_chunk2B);}context->format.channels = digit_chunk2B;debugf("File[%s] channels: %d.", context->name.c_str(), context->format.channels);// sample_ratememset(&str_chunk4B, 0, sizeof(str_chunk4B));fp.read(str_chunk4B, 4);digit_chunk4B = 0;memcpy(&digit_chunk4B, str_chunk4B, sizeof(digit_chunk4B));if (is_bigEndian()){digit_chunk4B = ntohl(digit_chunk4B);}context->format.sample_rate = digit_chunk4B;debugf("File[%s] sample_rate: %ld.", context->name.c_str(), context->format.sample_rate);// bytes per secondmemset(&str_chunk4B, 0, sizeof(str_chunk4B));fp.read(str_chunk4B, 4);digit_chunk4B = 0;memcpy(&digit_chunk4B, str_chunk4B, sizeof(digit_chunk4B));if (is_bigEndian()){digit_chunk4B = ntohl(digit_chunk4B);}context->format.bytes_per_second = digit_chunk4B;debugf("File[%s] bytes_per_second: %ld.", context->name.c_str(), context->format.bytes_per_second);// block align sizememset(&str_chunk2B, 0, sizeof(str_chunk2B));fp.read(str_chunk2B, 2);digit_chunk2B = 0;memcpy(&digit_chunk2B, str_chunk2B, sizeof(digit_chunk2B));if (is_bigEndian()){digit_chunk2B = ntohs(digit_chunk2B);}context->format.block_size = digit_chunk2B;debugf("File[%s] block_size: %ld.", context->name.c_str(), context->format.block_size);// bits per samplememset(&str_chunk2B, 0, sizeof(str_chunk2B));fp.read(str_chunk2B, 2);digit_chunk2B = 0;memcpy(&digit_chunk2B, str_chunk2B, sizeof(digit_chunk2B));if (is_bigEndian()){digit_chunk2B = ntohs(digit_chunk2B);}context->format.bits_per_sample = digit_chunk2B;debugf("File[%s] bits_per_sample: %ld.", context->name.c_str(), context->format.bits_per_sample);
}

字节序

在wave的每一个chunk中,涉及到了大量的大小端字节序转换,由此带来一个问题,判断当前主机cpu的字节序,代码如下:

bool is_bigEndian()
{static union { char c; unsigned long mylong; } endian_test = { 0x01 };// bit_endian: 00 00 00 01// little_endian: 01 00 00 00return endian_test.c == 0x00;
}


推荐阅读
  • 使用 `git stash` 可以将当前未提交的修改保存到一个临时存储区,以便在后续恢复工作目录时使用。例如,在处理中间状态时,可以通过 `git stash` 命令将当前的所有未提交更改推送到一个新的储藏中,从而保持工作目录的整洁。此外,本文还将详细介绍如何解决 `git stash pop` 时可能出现的冲突问题,帮助用户高效地管理代码变更。 ... [详细]
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
  • JavaScript XML操作实用工具类:XmlUtilsJS技巧与应用 ... [详细]
  • Presto:高效即席查询引擎的深度解析与应用
    本文深入解析了Presto这一高效的即席查询引擎,详细探讨了其架构设计及其优缺点。Presto通过内存到内存的数据处理方式,显著提升了查询性能,相比传统的MapReduce查询,不仅减少了数据传输的延迟,还提高了查询的准确性和效率。然而,Presto在大规模数据处理和容错机制方面仍存在一定的局限性。本文还介绍了Presto在实际应用中的多种场景,展示了其在大数据分析领域的强大潜力。 ... [详细]
  • 在开发过程中,我最初也依赖于功能全面但操作繁琐的集成开发环境(IDE),如Borland Delphi 和 Microsoft Visual Studio。然而,随着对高效开发的追求,我逐渐转向了更加轻量级和灵活的工具组合。通过 CLIfe,我构建了一个高度定制化的开发环境,不仅提高了代码编写效率,还简化了项目管理流程。这一配置结合了多种强大的命令行工具和插件,使我在日常开发中能够更加得心应手。 ... [详细]
  • 本指南从零开始介绍Scala编程语言的基础知识,重点讲解了Scala解释器REPL(读取-求值-打印-循环)的使用方法。REPL是Scala开发中的重要工具,能够帮助初学者快速理解和实践Scala的基本语法和特性。通过详细的示例和练习,读者将能够熟练掌握Scala的基础概念和编程技巧。 ... [详细]
  • 本文介绍了如何在 Windows 系统上利用 Docker 构建一个包含 NGINX、PHP、MySQL、Redis 和 Elasticsearch 的集成开发环境。通过详细的步骤说明,帮助开发者快速搭建和配置这一复杂的技术栈,提升开发效率和环境一致性。 ... [详细]
  • 本文介绍了如何在iOS平台上使用GLSL着色器将YV12格式的视频帧数据转换为RGB格式,并展示了转换后的图像效果。通过详细的技术实现步骤和代码示例,读者可以轻松掌握这一过程,适用于需要进行视频处理的应用开发。 ... [详细]
  • 蓝桥杯物联网基础教程:通过GPIO输入控制LED5的点亮与熄灭
    本教程详细介绍了如何利用STM32的GPIO接口通过输入信号控制LED5的点亮与熄灭。内容涵盖GPIO的基本配置、按键检测及LED驱动方法,适合具有STM32基础的读者学习和实践。 ... [详细]
  • 本文深入探讨了 Git 与 SVN 的高效使用技巧,旨在帮助开发者轻松应对版本控制中的各种挑战。通过详细解析两种工具的核心功能与最佳实践,读者将能够更好地掌握版本管理的精髓,提高开发效率。 ... [详细]
  • 如何在MySQL中选择合适的表空间以优化性能和管理效率
    在MySQL中,合理选择表空间对于提升表的管理和访问性能至关重要。表空间作为MySQL中用于组织和管理数据的一种机制,能够显著影响数据库的运行效率和维护便利性。通过科学地配置和使用表空间,可以优化存储结构,提高查询速度,简化数据管理流程,从而全面提升系统的整体性能。 ... [详细]
  • 解决lib-flexible安装过程中遇到的错误问题
    在安装 lib-flexible 时,遇到了 `saveError ENOENT: No such file or directory` 错误,具体表现为无法打开 `E:\Github\SDIO\package.json` 文件。解决此问题的关键在于确保项目根目录下存在 `package.json` 文件,并且在正确的项目路径中执行安装命令。建议先检查项目结构,确认文件是否存在,然后再尝试重新安装依赖。 ... [详细]
  • Netty框架中运用Protobuf实现高效通信协议
    在Netty框架中,通过引入Protobuf来实现高效的通信协议。为了使用Protobuf,需要先准备好环境,包括下载并安装Protobuf的代码生成器`protoc`以及相应的源码包。具体资源可从官方下载页面获取,确保版本兼容性以充分发挥其性能优势。此外,配置好开发环境后,可以通过定义`.proto`文件来自动生成Java类,从而简化数据序列化和反序列化的操作,提高通信效率。 ... [详细]
  • 在过去,我曾使用过自建MySQL服务器中的MyISAM和InnoDB存储引擎(也曾尝试过Memory引擎)。今年初,我开始转向阿里云的关系型数据库服务,并深入研究了其高效的压缩存储引擎TokuDB。TokuDB在数据压缩和处理大规模数据集方面表现出色,显著提升了存储效率和查询性能。通过实际应用,我发现TokuDB不仅能够有效减少存储成本,还能显著提高数据处理速度,特别适用于高并发和大数据量的场景。 ... [详细]
  • Shell参数详解与应用
    本文详细介绍了Shell参数的种类及其应用,内容简洁明了,结构清晰。通过深入解析各类参数的功能和使用方法,旨在帮助读者更好地理解和掌握Shell编程技巧,提升实际操作能力。 ... [详细]
author-avatar
马芷靈
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有