车载音频开发(一):从看懂wav开始
创始人
2024-11-06 06:41:04

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

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

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

        然后借助插件看起hex格式

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

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

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

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

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

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

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

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

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

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

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

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

先建一个头文件的结构体

    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值

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

 得到以下结果

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

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

#include  #include  #include  #include  #include       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(&header), sizeof(header));         inputFile.read(reinterpret_cast(&headera), header.subchunk1Size);         inputFile.read(reinterpret_cast(&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(&header), sizeof(header));         inputFile.read(reinterpret_cast(&headera), header.subchunk1Size);         inputFile.read(reinterpret_cast(&headerb), sizeof(headerb));          const size_t dataSize = headerb.subchunk2Size;         vector 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;     } 

相关内容

热门资讯

裸辞做“一人公司”,我后悔了 去年这个时候,一位以色列程序员正在东南亚旅行。他顺手把一个在脑子里转了很久的想法做成了产品,一个让任...
南京建成国内首个Pre-6G试... 4月21日,2026全球6G技术与产业生态大会在南京开幕。全息互动技术展台前,一名远在北京的工作人员...
超梵求职受邀参加“2025抖音... 超梵求职受邀参加“2025抖音巨量引擎成人教育行业生态大会”,探讨分享优质内容传播,服务万千学员。 ...
摩托罗拉Razr 2026(R... IT之家 4 月 22 日消息,摩托罗拉宣布新一代 Razr 折叠手机将于 4 月 29 日在美国发...
库克卸任,特纳斯领航:苹果新纪... 苹果首席执行官蒂姆·库克将卸任,硬件工程主管约翰·特纳斯将接任,苹果公司今天宣布此事。 库克将在夏季...