热门标签 | 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;
}


推荐阅读
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 我们有(据我所知)星型模式SQL数据库中的数据文件。该数据库有5个不同的文件,扩展名为 ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
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社区 版权所有