C语言读写WAV文件

代码放在这里了呢,求star⭐

WAV格式遵循RIFF格式

由于我们需要的是它的第二层的fmt块和data

并且WAV文件前12字节的内容是RIFF <文件大小-8> WAVE

所以我们可以跳过它们进行读取


引入相关的库

#include <stdio.h>
#include <string.h>

定义结构体

typedef struct _CHUNK_{
    long start; // 当前块的起始位置(输入用)
    char id[4]; // 块的ID
    unsigned int size; // 块的数据大小
    void* buffer; // 块的数据(输出用)
} CHUNK,*rCHUNK;

以及…fmt

typedef struct _FMT_{
	// 音频格式(PCM是1,IEEE float是3,0xFFFE必须在扩展快内指定音频格式)
    unsigned short AudioFormat;
    // 声道数
    unsigned short NumChannels;
    // 采样率
    unsigned int SampleRate;
    // 每秒字节数
    unsigned int ByteRate;
    // 块大小(单次采样的总字节数)
    unsigned short BlockAlign;
    // 每个采样的比特数
    unsigned short BitPerSample;
    // 扩展快大小(一般是22)
    unsigned short cbSize;
    unsigned short VBPS;
    unsigned int ChannelMask;
    unsigned char GUID[16];
} FMT,*rFMT;

读取FMT

int ReadFMT(rCHUNK ck,rFMT fmt,FILE* fp){
    if(!ck || !fmt) return -1;
    fseek(fp,ck->start+8,SEEK_SET);
    fread(&fmt->AudioFormat,2,1,fp);
    fread(&fmt->NumChannels,2,1,fp);
    fread(&fmt->SampleRate,4,1,fp);
    fread(&fmt->ByteRate,4,1,fp);
    fread(&fmt->BlockAlign,2,1,fp);
    fread(&fmt->BitPerSample,2,1,fp);
    // 读取扩展快
    if(ck->size > 16){
        fread(&fmt->cbSize,2,1,fp);
        fread(&fmt->VBPS,2,1,fp);
        fread(&fmt->ChannelMask,4,1,fp);
        fread(fmt->GUID,16,1,fp);
    }
    // 重新计算部分参数
    fmt->BlockAlign = fmt->BitPerSample*fmt->NumChannels;
    fmt->ByteRate = fmt->BlockAlign*fmt->SampleRate;
    return 0;
}

初始化块

int InitChunk(rCHUNK ck,long start,char* id,unsigned int size,void* data){
    if (!ck || !id) return -1;
    ck->start = start;
    memcpy(ck->id,id,4);
    ck->size = size;
    ck->buffer = data;
    return 0;
}

初始化FMT(不考虑扩展块)

int InitFMT(rFMT fmt,unsigned short AF,unsigned short NC,unsigned int SR,unsigned short BPS){
    if(!fmt) return -1;
    fmt->AudioFormat = AF;
    fmt->NumChannels = NC;
    fmt->SampleRate = SR;
    fmt->BitPerSample = BPS;

    fmt->BlockAlign = BPS * NC / 8;
    fmt->ByteRate = fmt->BlockAlign * fmt->SampleRate;
    return 0;
}

上面这是FMT块的数据
现在要初始化FMT块

int InitFMTChunk(rCHUNK ck,rFMT fmt,unsigned int fmt_size){
    if(!ck || !fmt) return -1;
    InitChunk(ck,12,"fmt ",fmt_size,fmt);
    return 0;
}

读取一个块

int LoadChunk(rCHUNK now,rCHUNK next,FILE* fp){
    if(!now||!next||!fp)return -1;
    // 定位到当前块
    if(fseek(fp,now->start,SEEK_SET)) return 1;
    // 获取下一个块的起始位置
    next->start = now->start + 8 + now->size;
    // 跳转到下一块
    if(fseek(fp,next->start,SEEK_SET)) return 1;
    // 读取块信息
    if(!fread((next->id),4,1,fp)) return 2;
    if(!fread(&(next->size),4,1,fp)) return 2;
    return 0;
}

WAV文件RIFF块下的子块里面不仅有fmt和data块
还有其他的比如fact块和LIST块

所以我们要循环读取并比较块的ID

int LoadWAV(FILE* fp,rCHUNK fmt,rCHUNK data){
    CHUNK start,tmp;
    InitChunk(&start,0,"RIFF",4,NULL);
    // 循环读取
    LoadChunk(&start,&tmp,fp);
    do{
        if(!memcmp(tmp.id,"fmt ",4)){
            InitChunk(fmt,tmp.start,tmp.id,tmp.size,NULL);
        }else
        if(!memcmp(tmp.id,"data",4)){
            InitChunk(data,tmp.start,tmp.id,tmp.size,NULL);
        }
    }while(!LoadChunk(&tmp,&tmp,fp));
    return 0;
}

最后是保存WAV文件

int SaveWAV(FILE* fp,rCHUNK fmt,rCHUNK data){
    fwrite("RIFF",4,1,fp);
    fwrite("\0\0\0\0",4,1,fp);
    fwrite("WAVE",4,1,fp);

    fwrite(fmt->id,4,1,fp);
    fwrite(&fmt->size,4,1,fp);
    fwrite(fmt->buffer,fmt->size,1,fp);

    fwrite(data->id,4,1,fp);
    fwrite(&data->size,4,1,fp);
    fwrite(data->buffer,data->size,1,fp);

    unsigned int riff_size = ftell(fp) - 8;
    fseek(fp,4,SEEK_SET);
    fwrite(&riff_size,4,1,fp);
    return 0;
}

非常简单
这篇文章就结束了,记得给仓库点一个star,谢谢

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值