车载音频开发(一):从看懂wav开始

背景介绍:随着电车的发展势头迅猛,国内车载音频也成为电车火热宣称的势头,要想深入了解车载音频,那首先还是得从最为普通的音频文件WAV开始。

我们都知道,计算机只能存储数字,声音确实靠不同频率的波组成,那么这些波的数据是如何存储在wav文件中的呢

首先我们用notepad++打开一首简单的wav文件 一堆看不懂的乱码

然后借助插件看起hex格式

这些一个个数字和字母就是该文件的全部内容了,当然,这么看还是很迷,那么我们就用专门的音频软件audition来解析这些数据,看看wav到底长什么样

用audition打开可以看到该wav有两个声道,每个声道对应着一段波形图

我们放大了看,每个波箱图上有非常多的小点,这些点就被称为采样点,只要采样点足够密,那么将这些点按先后顺序连起来,那就成了波形数据

当然,实际的wav文件的内容远不止这些,要像真正读懂wav还要接着往下看

我们先来百度一下有关wav的词条

词条上显示有三个关键词:采样频率,采样位数,声道数

采样频率:简单理解就是一秒钟有多少个小点

采样位数:简单理解就是每个小点在计算机上由多少个 0/1 来记录

声道数:就是这个这个wav有多少个声道 (除了我们大多熟悉的立体音,还有5.1,7.1.4,等其他各种各样的声道数的音源,当我们了解了wav的格式后,想要多少个声道就能有多少个声道)

接下来,我们应该知道的是 采样频率,采样位数,声道数 这些信息我们要如何知道,到底又怎么用,那我们就要去在百度再搜一搜wav文件头

知乎上说的很明白,以下就是文件头的内容

那么知道了这些,作为一个程序员就可以开始展现我们自己的专业啦

先建一个头文件的结构体

cpp 复制代码
    struct WavHeader {
        char chunkId[4];			//"RIFF"
        uint32_t chunkSize;			//totalsize - 8
        char format[4];				//"WAVE"
        char subchunk1Id[4];		//"fmt"
        uint32_t subchunk1Size;		//16:Normal; 18:Non_PCM; 40:Extensible;
        uint16_t audioFormat; 		//wav 格式 1;int型  3:float型  65534:未知
        uint16_t numChannels; 		//声道数 
        uint32_t sampleRate;		//采样率
        uint32_t byteRate;			//比特率:采样率 * 采样位宽
        uint16_t blockAlign;		//采样深度:
        uint16_t bitsPerSample;		//采样位宽:采样深度 * 8
        char subchunk2Id[4];		//"data"
        uint32_t subchunk2Size;		//datasize
    };

通过IO读取文件的数据可得到如下内容

读出了头文件的信息后,剩下的便是读取其所有数据

根据头文件信息我们知道该文件有两声道,采样率是48000,采样位数是16

那接下来我们每16 位也就是每两个字节读一次数据

数据量太大,我们就截取看一部分

这里有正有负,

对应上这里纵坐标为dB 值,那么我把把这些数据转化一下,算出每个数据对应的dB值

cpp 复制代码
memcpy(&WavDate16, buffer.data() + i * headera.numChannels * perdatasize + j * perdatasize, perdatasize);
printf(" %10d , %.2f db ,",  WavDate16, 20 * log10f(abs((float)WavDate16) / 32768));

得到以下结果

接下来我们就还可以完全用代码把所有wav的数据读出来了

以下是优化好的c++代码

cpp 复制代码
#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>
#include <cmath>

    using namespace std;

    struct WavHeader {
        char chunkId[4];			//"RIFF"
        uint32_t chunkSize;			//totalsize - 8
        char format[4];				//"WAVE"
        char subchunk1Id[4];		//"fmt"
        uint32_t subchunk1Size;		//16:Normal; 18:Non_PCM; 40:Extensible;
    };

    struct type40_header {
        uint16_t audioFormat; 		//wav 格式 1;int型  3:float型  65534:未知
        uint16_t numChannels; 		//声道数 
        uint32_t sampleRate;		//采样率
        uint32_t byteRate;			//比特率:采样率 * 采样位宽
        uint16_t blockAlign;		//采样深度:
        uint16_t bitsPerSample;		//采样位宽:采样深度 * 8
        uint16_t cbSize;
        uint16_t wValidBitsPerSample;
        uint32_t dwChannelMask;
        char SubFormat[4];
        char ckID[4];
        uint32_t cksize;
        uint32_t dwSampleLength;
    };

    struct data_header {
        char subchunk2Id[4];		//"data"
        uint32_t subchunk2Size;		//datasize
    };

    //查看头部数据
    int header_check(const char* filename)
    {
        WavHeader header = {};
        type40_header headera = {};
        data_header headerb = {};

        ifstream inputFile(filename, ios::binary);
        if (!inputFile.is_open()) {
            cerr << "无法打开文件" << endl;
            return -1;
        }

        inputFile.read(reinterpret_cast<char*>(&header), sizeof(header));
        inputFile.read(reinterpret_cast<char*>(&headera), header.subchunk1Size);
        inputFile.read(reinterpret_cast<char*>(&headerb), sizeof(headerb));


        //printf("\ntotalsize : \t%u \n", header.chunkSize + 8);
         printf("chunkId       : ,%c%c%c%c\n", header.chunkId[0], header.chunkId[1], header.chunkId[2], header.chunkId[3]);
        cout << "chunkSize     : ," << header.chunkSize << endl;
         printf("format        : ,%c%c%c%c\n", header.format[0], header.format[1], header.format[2], header.format[3]);
         printf("subchunk1Id   : ,%c%c%c%c\n", header.subchunk1Id[0], header.subchunk1Id[1], header.subchunk1Id[2], header.subchunk1Id[3]);
        cout << "subchunk1Size : ," << header.subchunk1Size << endl;
        cout << "audioFormat   : ," << headera.audioFormat << endl;
        cout << "numChannels   : ," << headera.numChannels << endl;
        cout << "sampleRate    : ," << headera.sampleRate << endl;
        cout << "byteRate      : ," << headera.byteRate << endl;
        cout << "blockAlign    : ," << headera.blockAlign << endl;
        cout << "bitsPerSample : ," << headera.bitsPerSample << endl;


         printf("subchunk2Id   : ,%c%c%c%c\n", headerb.subchunk2Id[0], headerb.subchunk2Id[1], headerb.subchunk2Id[2], headerb.subchunk2Id[3]);
        cout << "subchunk2Size : ," << headerb.subchunk2Size << endl;


        inputFile.close();
        return 0;
    }

    int data_check(const char* filename)
    {
        WavHeader header = {};
        type40_header headera = {};
        data_header headerb = {};

        ifstream inputFile(filename, ios::binary);
        if (!inputFile.is_open()) {
            cerr << "无法打开文件" << endl;
            return -1;
        }

        inputFile.read(reinterpret_cast<char*>(&header), sizeof(header));
        inputFile.read(reinterpret_cast<char*>(&headera), header.subchunk1Size);
        inputFile.read(reinterpret_cast<char*>(&headerb), sizeof(headerb));

        const size_t dataSize = headerb.subchunk2Size;
        vector<char> buffer(dataSize);

        inputFile.read(buffer.data(), dataSize);
        uint16_t perdatasize = headera.bitsPerSample / 8;
        float wavdate;
        int32_t WavDate = 0;
        int16_t WavDate16 = 0;
        printf(",");
        for (int j = 0; j < headera.numChannels; j++) {
            printf("channel[%d] , ,", j + 1);
        }
        printf("\n");
        printf("data,");
        for (int j = 0; j < headera.numChannels; j++) {
            printf("Frequency , Response ,");
        }
        printf("\n");
        for (size_t i = 0; i < dataSize / perdatasize/ headera.numChannels; i++) {
            //for (size_t i = 0; i < 100; i++) {
                printf("data[%8d] : ,",(unsigned int)i);
                for (size_t j = 0; j < headera.numChannels; j++) {

                        if (16 == headera.bitsPerSample)
                        {
                            memcpy(&WavDate16, buffer.data() + i * headera.numChannels * perdatasize + j * perdatasize, perdatasize);
                            printf(" %10d , %.2f db ,",  WavDate16, 20 * log10f(abs((float)WavDate16) / 32768));
                        }
                        else if (24 == headera.bitsPerSample)
                        {
                            memcpy(&WavDate, buffer.data() + i * headera.numChannels * perdatasize + j * perdatasize, perdatasize);
                            printf(" %10d , %.2f db ,", (WavDate << 8) / 256, 20 * log10f(abs((float)(WavDate << 8)) / 256 / 8388608));
                        }
                        else if (32 == headera.bitsPerSample)
                        {
                            if (1 != headera.audioFormat) {
                                memcpy(&wavdate, buffer.data() + i * headera.numChannels * perdatasize + j * perdatasize, perdatasize);
                                printf(" %.6f , %.2f db ,", wavdate, 20 * log10f(abs(wavdate)));
                            }
                            else {
                                memcpy(&WavDate, buffer.data() + i * headera.numChannels * perdatasize + j * perdatasize, perdatasize);
                                printf(" %16d , %.2f db ,", WavDate, 20 * log10f(abs((float)WavDate) / 2147483648));
                            }

                        }
                    
                }
                cout << endl;
            }
        buffer.clear();

        inputFile.close();
        return 0;
    }



    int main(int argc, const char* argv[]) {

        int wavnum = argc - 1;

        if (1 == wavnum) {

            header_check(argv[1]);
            printf("\n");
            data_check(argv[1]);
        }

        return 0;
    }
相关推荐
聆风吟º13 小时前
【C++藏宝阁】C++介绍:从发展历程到现代应用
开发语言·c++·应用领域·发展历程·起源
艾莉丝努力练剑13 小时前
艾莉丝努力练剑的2025年度总结
java·大数据·linux·开发语言·c++·人工智能·python
枫叶丹415 小时前
【Qt开发】Qt系统(一)-> 定时器 QTimerEvent 和 QTimer
c语言·开发语言·数据库·c++·qt·系统架构
xu_yule17 小时前
算法基础(数论)—费马小定理
c++·算法·裴蜀定理·欧拉定理·费马小定理·同余方程·扩展欧几里得定理
宇宙超级无敌暴龙战士21 小时前
旮旯c语言三个任务
c++·c
BanyeBirth21 小时前
C++差分数组(二维)
开发语言·c++·算法
Cherry的跨界思维21 小时前
【AI测试全栈:质量模型】4、新AI测试金字塔:从单元到社会的四层测试策略落地指南
人工智能·单元测试·集成测试·ai测试·全栈ai·全栈ai测试·社会测试
Fcy6481 天前
C++ map和multimap的使用
开发语言·c++·stl
CC.GG1 天前
【C++】STL容器----unordered_map和unordered_set的使用
java·数据库·c++
lengjingzju1 天前
基于IMake的 GCC 编译与链接选项深度解析:构建高效、安全、可调试的现代软件
c++·安全·性能优化·软件构建·开源软件