背景介绍:随着电车的发展势头迅猛,国内车载音频也成为电车火热宣称的势头,要想深入了解车载音频,那首先还是得从最为普通的音频文件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 <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; }