PCM音频数据封装为WAV文件

------------------------------------全系列文章目录------------------------------------

  • PCM(Pulse Code Modulation,脉冲编码调制),PCM音频数据是指经过采样、量化、编码转换成的未压缩数字音频数据。

  • WAV最常见的声音文件格式之一,是微软公司专门为Windows开发的一种标准数字音频文件,该文件能记录各种单声道或立体声的声音信息,并能保证声音不失真

  • WAV格式组成
    在这里插入图片描述

    • RIFF(Resource Interchange File Format,资源交换档案标准),是一种把资料储存在被标记的区块(tagged chunks)中的档案格式(meta-format),不同编码的音频/视频流按照RIFF所定义的存储规则保存,在读取播放时,就可以按照RIFF规则分析文件,正常解析出音频/视频流。
  • 示例代码

    • 按照上述定义,声明如下数据结构
    struct RIFF_t {
        char        ID[4];
        uint32_t    size;
        char        format[4];
    };
    
    struct FMT_t {
        char        ID[4];
        uint32_t    size;
        uint16_t    audio_format;
        uint16_t    channels;
        uint32_t    sample_rate;
        uint32_t    byte_rate;
        uint16_t    block_align;
        uint16_t    bits_per_sample;
    };
    
    struct DATA_t {
        char        ID[4];
        uint32_t    size;
        //void		*data;		//此处因为数据需要从文件中边读取边存储,因此为了方便操作,不声明data
    };
    
    struct PCM_to_WAV_t {
        char        *in_file_name;
        char        *out_file_name;
        FILE        *in_file;
        FILE        *out_file;
        uint16_t    pcm_data;	//保存从PCM文件中读取的数据,因为PCM数据大小必然是16的倍数,所以声明为uint16_t
    
        struct RIFF_t   riff;
        struct FMT_t    fmt;
        struct DATA_t   data;
    };
    
    • 初始化操作
    int PCM_to_WAV_Init(PCM_to_WAV_t *p2w, int argc, char** argv)
    {
        if (argc == 3) {
            p2w->in_file_name        = argv[1];
            p2w->fmt.sample_rate     = 44100;
            p2w->fmt.channels        = 2;
            p2w->fmt.bits_per_sample = 16;
            p2w->out_file_name       = argv[2];
        } else if (argc == 6) {
            p2w->in_file_name        = argv[1];
            p2w->fmt.sample_rate     = atoi(argv[2]);
            p2w->fmt.channels        = atoi(argv[3]);
            p2w->fmt.bits_per_sample = atoi(argv[4]);
            p2w->out_file_name       = argv[5];
        } else {
            printf("$.exe $.pcm $sample_rate $channels $bits_per_sample $.wav\n");
            printf("Example: $.exe in.pcm 44100 2 16 out.wav\n");
            printf("$.exe $.pcm $.wav\n");
            printf("Example: $.exe in.pcm out.wav\n");
            return -1;
        }
    
        p2w->pcm_data = 0;
        p2w->in_file  = NULL;
        p2w->out_file = NULL;
    
        memcpy(p2w->riff.ID, "RIFF", 4);
        memcpy(p2w->riff.format, "WAVE", 4);
        p2w->riff.size = 36;
    
        memcpy(p2w->fmt.ID, "fmt ", 4);
        p2w->fmt.size           = 16;
        p2w->fmt.audio_format   = 1;
        p2w->fmt.byte_rate      = p2w->fmt.sample_rate * p2w->fmt.channels * p2w->fmt.bits_per_sample / 8;
        p2w->fmt.block_align    = p2w->fmt.channels * p2w->fmt.bits_per_sample / 8;
    
        memcpy(p2w->data.ID, "data", 4);
        p2w->data.size = 0;
    
        return 0;
    }
    
    • 转换过程
    int PCM_to_WAV(PCM_to_WAV_t *p2w)
    {
        p2w->in_file  = fopen(p2w->in_file_name, "rb+");
        p2w->out_file = fopen(p2w->out_file_name, "wb+");
        if (p2w->in_file == NULL || p2w->out_file == NULL) {
            printf("open file error\n");
            return -1;
        }
    
        fseek(p2w->out_file, sizeof(struct RIFF_t), 1);
        fwrite(&p2w->fmt, sizeof(struct FMT_t), 1, p2w->out_file);
        fseek(p2w->out_file, sizeof(struct DATA_t), 1);
    
        fread(&p2w->pcm_data, sizeof(uint16_t), 1, p2w->in_file);
        while (! feof(p2w->in_file) ) {
            p2w->data.size += 2;
            fwrite(&p2w->pcm_data, sizeof(uint16_t), 1, p2w->out_file);
            fread(&p2w->pcm_data, sizeof(uint16_t), 1, p2w->in_file);
        }
    
        p2w->riff.size += p2w->data.size;
        rewind(p2w->out_file);
        fwrite(&p2w->riff, sizeof(struct RIFF_t), 1, p2w->out_file);
        fseek(p2w->out_file, sizeof(struct FMT_t), 1);
        fwrite(&p2w->data, sizeof(struct DATA_t), 1, p2w->out_file);
    
        fclose(p2w->in_file);
        fclose(p2w->out_file);
    
        return 0;
    }
    
    • 打印块信息
    void PCM_to_WAV_Info(PCM_to_WAV_t *p2w)
    {
        printf("in file: %s\nout file: %s\n", p2w->in_file_name, p2w->out_file_name);
        printf("RIFF: \n\tID: %4.4s\n\tsize: %d\n\tformat: %4.4s\n",
                    p2w->riff.ID, p2w->riff.size, p2w->riff.format);
        printf("FMT: \n\tID: %4.4s\n\tSize: %d\n\tAudio fmt: %d\n\tChannels: %d\n",
                p2w->fmt.ID, p2w->fmt.size, p2w->fmt.audio_format, p2w->fmt.channels);
        printf("\tsample rate: %d\n\tByte Rate: %d\n\tBlock Align: %d\n\tBits Per Sample: %d\n",
                p2w->fmt.sample_rate, p2w->fmt.byte_rate, p2w->fmt.block_align, p2w->fmt.bits_per_sample);
        printf("DATA: \n\tID: %4.4s\n\tSize: %d\n", p2w->data.ID, p2w->data.size);
    }
    
    • 调用
    int main(int argc, char **argv)
    {
        struct PCM_to_WAV_t pcm2wav;
    
        if (PCM_to_WAV_Init(&pcm2wav, argc, argv) != 0)
            return -1;
        if (PCM_to_WAV(&pcm2wav) != 0)
            return -1;
        PCM_to_WAV_Info(&pcm2wav);
    
        return 0;
    }
    
  • 参考资料

不当之处请在评论区指出
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值