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


推荐阅读
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社区 版权所有