参考代码: wav音频文件格式解析
代码的思路是编写wave.h,根据wav文件结构定义struct。大佬的执行结果为
然而我在运行代码的时候遇到的问题:
可以看到fmt长度为18,然而还是按照16来处理,导致读取data的时候只读取到后两位。
- 第一次尝试:
首先分析文件:
WAVE PCM soundfile format中可以看到,在fmt的末尾可能出现ExtraParamSize 和 ExtraParams。在此文件中ExtraParamSize = 0,因此没有ExtraParams。
第三行中,02 00 为BlockAlign,10 00 为BitsPerSample,00 00 为ExtraParamsSize。之后就是data。
据此,我往wave.h的struct WAV_FMT的末尾添加了一行:
uint16_t ExtraParamSize;
运行结果:
发现data又往后多移动了两位,奇怪!为什么会这样?
- 第二次尝试:
我灵光一现,将上面那行ExtraParamSize添加到了struct WAV_data的开头。
运行结果:
结果看似正常!然而我的wav文件时长应该是53秒!而且为什么本属于fmt的内容要添加到data的开头呢?还是有问题!
- 第三次尝试
跟大佬聊了一下,认为可能与C语言字节对齐有关。我找到了这个链接,里面提到struct的每个数据类型都会对齐,并且默认按4字节对齐,这导致长度为18的fmt内容被对齐到了16或20,因此data只能显示两个字节。
据此,我在wave.h的开头加入
#pragma pack (2) /*指定按2字节对齐*/
结尾加入
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
运行结果:
一切正常。
- 总结
一开始只是想省事,用struct自动填充文件头信息。后来发现wav文件头长度还不同。fmt的值可能为16,18,20,40等;有些文件头中还有12字节的fact chunk;WAV文件格式解析及处理中还提到一种由Adobe Premiere Pro CC 创建的WAV,包含LIST chunk。因此更好的做法其实是一个字节一个字节读,手动解析,而不是一股脑填进struct。
代码:
wave.h
#ifndef RESOLVE_WAV_WAVE_H
#define RESOLVE_WAV_WAVE_H
#include <stdint.h>
#pragma pack (2) /*指定按2字节对齐*/
// using namespace std;
typedef struct WAV_RIFF {
/* chunk "riff" */
char ChunkID[4]; /* "RIFF" */
/* sub-chunk-size */
uint32_t ChunkSize; /* 36 + Subchunk2Size */
/* sub-chunk-data */
char Format[4]; /* "WAVE" */
} RIFF_t;
typedef struct WAV_FMT {
/* sub-chunk "fmt" */
char Subchunk1ID[4]; /* "fmt " */
/* sub-chunk-size */
uint32_t Subchunk1Size; /* 16 for PCM */
/* sub-chunk-data */
uint16_t AudioFormat; /* PCM = 1*/
uint16_t NumChannels; /* Mono = 1, Stereo = 2, etc. */
uint32_t SampleRate; /* 8000, 44100, etc. */
uint32_t ByteRate; /* = SampleRate * NumChannels * BitsPerSample/8 */
uint16_t BlockAlign; /* = NumChannels * BitsPerSample/8 */
uint16_t BitsPerSample; /* 8bits, 16bits, etc. */
uint16_t ExtraParamSize;
} FMT_t;
typedef struct WAV_data {
/* sub-chunk "data" */
char Subchunk2ID[4]; /* "data" */
/* sub-chunk-size */
uint32_t Subchunk2Size; /* data size */
/* sub-chunk-data */
// Data_block_t block;
} Data_t;
//typedef struct WAV_data_block {
//} Data_block_t;
typedef struct WAV_fotmat {
RIFF_t riff;
FMT_t fmt;
Data_t data;
} Wav;
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
#endif //RESOLVE_WAV_WAVE_H
wav_open.c
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "wave.h"
int main()
{
FILE *fp;
Wav wav;
RIFF_t riff;
FMT_t fmt;
Data_t data;
errno_t err;
err = fopen_s(&fp, "test.wav", "rb");
if (err) {
printf("The file was not opened\n");
}
else {
fread(&wav, 1, sizeof(wav), fp);
fclose(fp);
riff = wav.riff;
fmt = wav.fmt;
data = wav.data;
printf("ChunkID \t%c%c%c%c\n", riff.ChunkID[0], riff.ChunkID[1], riff.ChunkID[2], riff.ChunkID[3]);
printf("ChunkSize \t%d\n", riff.ChunkSize);
printf("Format \t\t%c%c%c%c\n", riff.Format[0], riff.Format[1], riff.Format[2], riff.Format[3]);
printf("\n");
printf("Subchunk1ID \t%c%c%c%c\n", fmt.Subchunk1ID[0], fmt.Subchunk1ID[1], fmt.Subchunk1ID[2], fmt.Subchunk1ID[3]);
printf("Subchunk1Size \t%d\n", fmt.Subchunk1Size);
printf("AudioFormat \t%d\n", fmt.AudioFormat);
printf("NumChannels \t%d\n", fmt.NumChannels);
printf("SampleRate \t%d\n", fmt.SampleRate);
printf("ByteRate \t%d\n", fmt.ByteRate);
printf("BlockAlign \t%d\n", fmt.BlockAlign);
printf("BitsPerSample \t%d\n", fmt.BitsPerSample);
// printf("ExtraParamSize \t%d\n", fmt.ExtraParamSize);
printf("\n");
printf("blockID \t%c%c%c%c\n", data.Subchunk2ID[0], data.Subchunk2ID[1], data.Subchunk2ID[2], data.Subchunk2ID[3]);
printf("blockSize \t%d\n", data.Subchunk2Size);
printf("\n");
if (fmt.ByteRate) {
printf("duration \t%d\n", data.Subchunk2Size / fmt.ByteRate);
}
}
}
运行结果: