C语言读取wav文件

参考代码: 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);
        }
    }
}

运行结果:

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值