• C代码创建多通道WAV音频文件


    C代码创建多通道WAV音频文件

    算法调试经常需要在PC端进行仿真输出,涉及到多通道,或者每一级算法的输出对比,这时需要多通道的WAV文件生成

    代码

    这里给出的是默认16bit单点数据位长,可以自己修改下,改成自己想要的位长

    /**
     *  @file wav_file_opt.c
     *
     *  @date 2022年07月25日 15:36:57 星期一
     *
     *  @author aron566
     *
     *  @copyright Copyright (c) 2021 aron566 .
     *
     *  @brief wav文件建立接口.
     *
     *  @details None.
     *
     *  @version V1.0
     */
    /** Includes -----------------------------------------------------------------*/
    /* Private includes ----------------------------------------------------------*/
    #include "wav_file_opt.h"
    /** Use C compiler -----------------------------------------------------------*/
    #ifdef __cplusplus ///< use C compiler
    extern "C" {
    #endif
    /** Private macros -----------------------------------------------------------*/
    
    /** Private typedef ----------------------------------------------------------*/
    const static char *Err_Str[81] = {
      [EPERM] = "不允许执行该操作",
      [ENOENT] = "没有此文件或目录",
      [ESRCH] = "没有此进程",
      [EINTR] = "函数中断",
      [EIO] = "I/O 错误",
      [ENXIO] = "没有此设备或地址",
      [E2BIG] = "参数列表太长",
      [ENOEXEC] = "执行格式错误",
      [EBADF] = "文件编号错误",
      [ECHILD] = "没有生成的进程",
      [EAGAIN] = "没有更多进程、没有足够内存或达到最大嵌套级别",
      [ENOMEM] = "没有足够内存",
      [EACCES] = "权限被拒绝",
      [EFAULT] = "地址错误",
      [EBUSY] = "设备或资源忙碌",
      [EEXIST] = "文件已存在",
      [EXDEV] = "跨设备链接",
      [ENODEV] = "没有此设备",
      [ENOTDIR] = "不是目录",
      [EISDIR] = "是目录",
      [EINVAL] = "参数无效",
      [ENFILE] = "系统中打开的文件太多",
      [EMFILE] = "打开的文件太多",
      [ENOTTY] = "不适当的 I/O 控制操作",
      [EFBIG] = "文件太大",
      [ENOSPC] = "设备上没有剩余空间",
      [ESPIPE] = "搜寻无效",
      [EROFS] = "只读文件系统",
      [EMLINK] = "链接太多",
      [EPIPE] = "管道损坏",
      [EDOM] = "数学参数",
      [ERANGE] = "结果太大",
      [EDEADLK] = "会发生资源死锁",
      [EDEADLOCK] = "与 EDEADLK 相同,以便与早期的 Microsoft C 版本兼容",
      [ENAMETOOLONG] = "文件名太长",
      [ENOLCK] = "无可用锁",
      [ENOSYS] = "函数不受支持",
      [ENOTEMPTY] = "目录不为空",
      [EILSEQ] = "非法字节序列",
      [STRUNCATE] = "字符串被截断",
    };
    /** Private constants --------------------------------------------------------*/
    /** Public variables ---------------------------------------------------------*/
    /** Private variables --------------------------------------------------------*/
    const static WAVFILEHEADER_Typedef_t wav_file_header =
    {
      .RiffName = {'R','I','F','F'},
      .nRiffLength = 44,
      .WavName = {'W', 'A', 'V', 'E'},
      .FmtName = {'f', 'm', 't', ' '},
      .nFmtLength = 16,
      .nAudioFormat = 1,
      .nChannleNumber = 2,
      .nSampleRate = 16000,
      .nBytesPerSecond = 16000 * 2 * 16 / 8,
      .nBytesPerSample = 4,
      .nBitsPerSample = 16,
      .DATANAME = {'d', 'a', 't', 'a'},
      .nDataLength = 0,
    };
    /** Private function prototypes ----------------------------------------------*/
    
    /** Private user code --------------------------------------------------------*/
    
    /** Private application code -------------------------------------------------*/
    /*******************************************************************************
    *
    *       Static code
    *
    ********************************************************************************
    */
    
    /** Public application code --------------------------------------------------*/
    /*******************************************************************************
    *
    *       Public code
    *
    ********************************************************************************
    */
    
    /**
     * @brief 关闭wav文件
     *
     * @param instance 句柄
     */
    void wav_file_close(WAV_FILE_Typedef_t *instance)
    {
      instance->wav_file_header.nRiffLength = instance->file_size - 8;
      instance->wav_file_header.nDataLength = instance->file_size - 44;
      fseek(instance->fp, 0, SEEK_SET);
      fwrite(&instance->wav_file_header, sizeof(char), 44, instance->fp);
      fclose(instance->fp);
    }
    
    /**
     * @brief 音频数据写入文件
     *
     * @param instance 句柄
     * @param Channel_Number 通道数 >= 1 <= 8
     * @param ... 通道数据
     */
    void wav_file_put_data(WAV_FILE_Typedef_t *instance, uint8_t Channel_Number, ...)
    {
      static int16_t Audio_Data[8 * WAV_ONCE_WRITE_FRAME_SIZE];
    
      va_list args;
    
      uint32_t index = 0;
    
      for(uint32_t i = 0; i < WAV_ONCE_WRITE_FRAME_SIZE; i++)
      {
        /* args point to the first variable parameter */
        va_start(args, Channel_Number);
        for(uint8_t Channel_Index = 0; Channel_Index < Channel_Number; Channel_Index++)
        {
          Audio_Data[index++] = (va_arg(args, uint16_t *))[i];
        }
      }
      va_end(args);
    
      fwrite(Audio_Data, sizeof(int16_t), index, instance->fp);
      instance->file_size += (index * sizeof(int16_t));
    }
    
    /**
     * @brief Set the wav info object
     *
     * @param record_sec 音频秒数
     * @param nChannleNumber 1单声道 2双声道
     * @param nSampleRate   采样率 点/s
     * @param nBitsPerSample 每次采样得到的样本数据位数值 采样位宽 8/16位/24位/32位
     */
    void wav_file_set_info(WAV_FILE_Typedef_t *instance, uint64_t record_sec, uint16_t nChannleNumber, uint32_t nSampleRate, uint16_t nBitsPerSample)
    {
      /* 设置该时长下文件大小 */
      // instance->file_size = 44 + (record_sec * nChannleNumber * nSampleRate * (nBitsPerSample / 8));
    
      instance->wav_file_header.nChannleNumber = nChannleNumber;
      instance->wav_file_header.nSampleRate = nSampleRate;
      instance->wav_file_header.nBitsPerSample = nBitsPerSample;
    
      instance->wav_file_header.nBytesPerSecond = nSampleRate * nChannleNumber * nBitsPerSample / 8;
      instance->wav_file_header.nBytesPerSample = nChannleNumber * nBitsPerSample / 8;
    }
    
    /**
     * @brief 创建wav文件初始化对象
     *
     * @param instance 句柄
     * @param file_name 文件名
     */
    void wav_file_create(WAV_FILE_Typedef_t *instance, const char *file_name)
    {
      memset(instance, 0, sizeof(WAV_FILE_Typedef_t));
    
      /* 设置文件名 */
      memcpy(instance->file_name, file_name, strlen(file_name));
    
      /* 设置wav文件信息 */
      memcpy(&instance->wav_file_header, &wav_file_header, sizeof(WAVFILEHEADER_Typedef_t));
      instance->file_size = sizeof(WAVFILEHEADER_Typedef_t);
    
      /* 创建wav文件 */
      errno_t err = fopen_s(&instance->fp, instance->file_name, "wb+");
      if(err != 0)
      {
        printf("%s\r\n", Err_Str[err]);
        return;
      }
    
      /* 跳过文件信息头部描述区域 */
      fseek(instance->fp, 44, SEEK_SET);
    }
    
    #ifdef __cplusplus ///
    }
    #endif
    /******************************** End of file *********************************/
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205

    头文件

    /**
     *  @file wav_file_opt.h
     *
     *  @date 2022年07月25日 15:37:10 星期一
     *
     *  @author Copyright (c) 2021 aron566 .
     *
     *  @brief 提供生成wav的接口.
     *
     *  @version V1.0
     */
    #ifndef WAV_FILE_OPT_H
    #define WAV_FILE_OPT_H
    /** Includes -----------------------------------------------------------------*/
    #include  /**< need definition of uint8_t */
    #include  /**< need definition of NULL    */
    #include /**< need definition of BOOL    */
    #include   /**< if need printf             */
    #include 
    #include 
    #include  /**< need variable max value    */
    #include 
    /** Private includes ---------------------------------------------------------*/
    
    /** Use C compiler -----------------------------------------------------------*/
    #ifdef __cplusplus ///< use C compiler
    extern "C" {
    #endif
    /** Private defines ----------------------------------------------------------*/
    #define WAV_ONCE_WRITE_FRAME_SIZE   64//MONO_FRAME_SIZE /**< once write frame size */
    /** Exported typedefines -----------------------------------------------------*/
    /* ref:https://www.cnblogs.com/zoneofmine/p/10850465.html */
    typedef struct WAVFILEHEADER
    {
      /*RIFF头*/
      char RiffName[4];       /**< 文件标识字符串 "RIFF"*/
      uint32_t nRiffLength;    /**< 头后文件长度 = 文件总长 - 8*/
    
      /*数据类型标识符*/
      char WavName[4];        /**< 波形文件标识字符串 "WAVE"*/
    
      /*格式块中的块头*/
      char FmtName[4];        /**< 格式标识字符串 "fmt"*/
      uint32_t nFmtLength;     /**< 头后块长度 16 or 18*/
      /*
          一般为16 或者 18 也有 大于18的数值表示格式块中块数据大小,通常为16,
          为18时表示格式块中块数据有附加信息(即扩展域大小-nAppendMessage)
          主要由一些软件制成的wav格式中含有该2个字节,当大于18时,nFmtLength - 18 (或者用nAppendMessage代替)
          即为扩展域信息数据所占的字节数
      */
    
      /*格式块中的块数据*/
      uint16_t nAudioFormat;   /**< 格式标识 PCM为1 其他压缩>1*/
      uint16_t nChannleNumber; /**< 声道标识 1单声道 2双声道*/
      uint32_t nSampleRate;    /**< 采样率 点/s*/
      uint32_t nBytesPerSecond;/**< 平均字节率 Bytes/s = 采样频率 × 音频通道数 × 每次采样得到的样本位数 / 8*/
      uint16_t nBytesPerSample;/**< 每个采样需要的字节数 Bytes/sample once = 通道数 × 每次采样得到的样本数据位值/8*/
      uint16_t nBitsPerSample; /**< 每次采样得到的样本数据位数值 采样位宽 8位/16位/24位/32位*/
    
      /*
      扩展域数据,nFmtLength >= 18存在
      quint16 nAppendSize;        固定两个字节描述扩展区域数据长度
      AppendMessage[nAppendSize];
        */
    
      /*数据块中的块头*/
      char DATANAME[4];
      uint32_t nDataLength;
    }WAVFILEHEADER_Typedef_t;
    
    /* WAV文件对象 */
    typedef struct
    {
      FILE *fp;
      char file_name[64];
      uint64_t file_size;
      WAVFILEHEADER_Typedef_t wav_file_header;
    }WAV_FILE_Typedef_t;
    /** Exported constants -------------------------------------------------------*/
    
    /** Exported macros-----------------------------------------------------------*/
    /** Exported variables -------------------------------------------------------*/
    /** Exported functions prototypes --------------------------------------------*/
    
    /**
     * @brief 创建wav文件初始化对象
     *
     * @param instance 句柄
     * @param file_name 文件名
     */
    void wav_file_create(WAV_FILE_Typedef_t *instance, const char *file_name);
    
    /**
     * @brief Set the wav info object
     *
     * @param record_sec 音频秒数
     * @param nChannleNumber 1单声道 2双声道,最多8声道
     * @param nSampleRate   采样率 点/s
     * @param nBitsPerSample 每次采样得到的样本数据位数值 采样位宽 8/16位/24位/32位
     */
    void wav_file_set_info(WAV_FILE_Typedef_t *instance, uint64_t record_sec, uint16_t nChannleNumber, uint32_t nSampleRate, uint16_t nBitsPerSample);
    
    /**
     * @brief 音频数据写入文件
     *
     * @param instance 句柄
     * @param Channel_Number 通道数 >= 1 <= 8
     * @param ... 通道数据
     */
    void wav_file_put_data(WAV_FILE_Typedef_t *instance, uint8_t Channel_Number, ...);
    
    /**
     * @brief 关闭wav文件
     *
     * @param instance 句柄
     */
    void wav_file_close(WAV_FILE_Typedef_t *instance);
    
    #ifdef __cplusplus ///
    }
    #endif
    #endif
    /******************************** End of file *********************************/
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124

    使用方式

    int main(void)
    {
    
      /* 初始化wav文件 */
      WAV_FILE_Typedef_t wav_file;
      wav_file_create(&wav_file, "test.wav");
    
      /* 设置通道数 采样率 位长 */
      wav_file_set_info(&wav_file, 0, 3, 16000, 16);
    
    	
    	while(1)
    	{
    		/* 音频处理输出 */
    		
    	    /* 结果写入wav文件 */
    	    wav_file_put_data(&wav_file, 3, result_0, result_l, result_r);
    	}
    	
    	  /* 关闭文件 */
    	wav_file_close(&wav_file);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
  • 相关阅读:
    安全基础二
    Java泛型简介、定义和使用含有泛型的类、定义和使用含有泛型的方法、定义和使用含有泛型的接口、泛型通配符?、受限泛型
    大语言模型LLM分布式训练:PyTorch下的分布式训练(LLM系列06)
    08、http协议和dubbo协议的区别
    因特网的组成,边缘之间的通讯方式,数据交换的方式
    使用Docker快速部署Flink分布式集群
    Pytorch 分布式训练中DP和DDP的原理和用法
    基于 hugging face 预训练模型的实体识别智能标注方案:生成doccano要求json格式
    C# 解决从其他地方迁移项目,引用中大多数包是感叹号的问题
    Node.js学习记录
  • 原文地址:https://blog.csdn.net/weixin_42892101/article/details/126290508