当前位置: 首页 > news >正文

语音处理:wav头格式非44字节场景处理分析

语音处理:wav头格式非44字节场景处理分析

    • 问题来源
    • 46字节wav头处理分析
    • 实践:处理46字节前文C代码修改
    • 相关参考

问题来源

前文《C学习:100行C代码简洁实现wav读写并附demo》使用简单的44字节wav头实现了核心读写功能。

但在实际使用过程中,可能会遇到少部分非44字节头的wav格式文件,这里需要注意。

非44字节头主要分为3种情况:

  1. 46字节(数据为PCM,但chunk_size为18)

  2. Non-PCM DATA,即采用u-law/a-law等量化

  3. Extensible Format,比较少见

情况2和3见《论wav文件格式和wav读写(c代码)》中方法,第一是不要默认按44字节读写wav头,第二是将非data区数据跳过即可。

46字节wav头处理分析

这里结合前文C代码实现讨论pcm data下的46字节wav头处理注意事项。

如何分辨44byte还是46byte字节头?

  • 44字节头的wav文件时,data字段在0x24-0x27地址中。而46字节头时,data字段则通常在0x26-0x29地址中。
  • 46字节头时,fmt格式后的chunk_size为18,而44字节头则默认是16byte。
  • 除此以外,两者头文件格式应无差异,且pcm字段格式的值(format_tag)应为1。

知道46byte字节头了,那要怎么改?

首先,相关默认44字节头的假设就有问题,需要在C代码里做结构体更换,并对文件移位时按实际字节头处理以便得到正确的pcm数据输入。

其次,在补齐结构体里多出来的2字节扩展位后(位置在fmt的末尾,data段前),要注意实现header结构体时,由于44byte能被4整除,结构体设置不会出现内存和数据不对应的问题。而46byte时,由于C代码机制有自动内存对齐,结构体实际内存46byte可能会补成48byte。

最后,通过指定46byte而非sizeof结构体(会出来结果48)读取数据,并对结构体data末尾datalength数据重新解释即可。

小结44byte到46byte的坑点:

  1. 数据段数据读取正确,跳过末尾的元数据
  2. 跳过前面的非data数据,如果有fact等字段数据的话。 (若是audition保存的wav,可以在另存为时,取消包含标记和元数据选项。)
  3. 注意C代码里结构体内部数据单元地址对齐自动增加的内存。 比如46字节大小的结构体,实际会补成48字节,导致读入的数据不符合预期在自动增补字节时,原C代码在读取datalength的时候就会因增补的字节错位,导致数据不对。

实践:处理46字节前文C代码修改

wav_io_basic.h

before

struct wave_header
{char riff[4];           /* "RIFF"                                  */int32_t flength;        /* file length in bytes                    */char wave[4];           /* "WAVE"                                  */char fmt[4];            /* "fmt "                                  */int32_t chunk_size;     /* size of FMT chunk in bytes (usually 16) */int16_t format_tag;     /* 1=PCM, 257=Mu-Law, 258=A-Law, 259=ADPCM */int16_t num_chans;      /* 1=mono, 2=stereo                        */int32_t srate;          /* Sampling rate in samples per second     */int32_t bytes_per_sec;  /* bytes per second = srate*bytes_per_samp */int16_t bytes_per_samp; /* 2=16-bit mono, 4=16-bit stereo          */int16_t bits_per_samp;  /* Number of bits per sample               */char data[4];           /* "data"                                  */int32_t dlength;        /* data length in bytes (filelength - 44)  */
};

after

struct wave_header
{char riff[4];           /* "RIFF"                                  */int32_t flength;        /* file length in bytes                    */char wave[4];           /* "WAVE"                                  */char fmt[4];            /* "fmt "                                  */int32_t chunk_size;     /* size of FMT chunk in bytes (usually 16) */int16_t format_tag;     /* 1=PCM, 257=Mu-Law, 258=A-Law, 259=ADPCM */int16_t num_chans;      /* 1=mono, 2=stereo                        */int32_t srate;          /* Sampling rate in samples per second     */int32_t bytes_per_sec;  /* bytes per second = srate*bytes_per_samp */int16_t bytes_per_samp; /* 2=16-bit mono, 4=16-bit stereo          */int16_t bits_per_samp;  /* Number of bits per sample               */// newint16_t reserved0; // for 46byte and format_tag=1 is pcm, extension byte, chunk_size: 16->18char data[4];           /* "data" if reserved0 exits, data offset: 38                                 */int32_t dlength;        /* data length in bytes (filelength - 46), 以为dlength offset is 42,但实际dlength前面自动补两字节便于四字节对齐,故dlength地址是44,from 46->48  */
};

wav_io_basic.c

before

// old 1
const int header_size_com = 44;/* Read function */
int WavGetHeader(FILE *fp, struct wave_header *wavh)
{ // load the header from the fileif ((int)fread(wavh, 1, sizeof(struct wave_header), fp) != header_size_com) {perror("Error loading header in read mode");return -11;}// after loading the header, we reach the data segmentreturn 0;
}// old 2wavh->chunk_size = 16; // default size of fmt definition

after

// new 1
const int header_size_com = 46;/* Read function */
int WavGetHeader(FILE *fp, struct wave_header *wavh)
{ // load the header from the fileif ((int)fread(wavh, 1, header_size_com, fp) != header_size_com) {perror("Error loading header in read mode");return -11;}// after loading the header, we reach the data segmentif (wavh->chunk_size == 18) { // means 44byte->46byteint *val = (int *)(&wavh->data[0] + 4);wavh->dlength = *val;}return 0;
}// new 2wavh->chunk_size = 18; // default size of fmt definition

以上代码修改完成后,可以正常读取46byte字节头的wav文件。

相关参考

  1. https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
  2. https://stackoverflow.com/questions/19991405/how-can-i-detect-whether-a-wav-file-has-a-44-or-46-byte-header
  3. https://blog.argcv.com/articles/6850.c
  4. https://blog.csdn.net/book_bbyuan/article/details/110188074
http://www.dtcms.com/a/490127.html

相关文章:

  • git 安全证书 【git】
  • leetcode 168. Excel 表列名称 python
  • LabVIEW 高速采集系统性能优化
  • 忻州建设公司网站临汾做网站的公司
  • 书籍学习|基于SprinBoot+vue的书籍学习平台(源码+数据库+文档)
  • PNNX + TorchScript + 手动修改后处理逻辑,最终输出适配 NCNN官方 yolov8.cpp
  • 记录小程序真机bug,而模拟器无法复现
  • 【iOS取证篇】浅谈iPhone手机的失窃设备保护功能
  • Qoder 全栈Ai工具
  • 数据库—数据库设计 多表查询 事务
  • php网站开发如何赚钱新发地网站建设
  • 多合一建网站数码电子产品网站建设策划书
  • Spring Boot + Kafka 全面实战案例
  • MATLAB基于GWO-BP神经网络对某拨叉件锻造金属流动性的参数分析
  • 建网站教学视频wordpress外汇
  • Ubuntu-8卡H20服务器升级nvidia驱动+cuda版本
  • 2.6 代码注释与编码规
  • html css js网页制作成品——饮料官网html+css+js 4页网页设计(4页)附源码
  • Langchain流式自定义生成器函数
  • 基于单片机的智能收银机模拟系统设计
  • ssh连接本地虚拟机
  • PyInstaller多模块项目打包指南
  • access 网站后台汕头自助建站
  • 从图纸到三维模型:智能装配指导的突破之路
  • 「JMM+Java锁+AQS」 知识图谱
  • 【广州公共资源交易-注册安全分析报告-无验证方式导致安全隐患】
  • C++ 学习日记
  • 晶晨S905L3SB芯片_安卓9.0_高安版_支持外置WIFI_线刷固件包
  • 4G5G 移动代理实战:什么时候必须用移动 IP?
  • 【OpenHarmony】传感器轻量级服务模块架构