当前位置: 首页 > news >正文

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

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

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • DUILib 创建自定义文本编辑控件
  • 【Python】模块
  • 《Windows API每日一练》24.1 WinSock简介
  • Java中的notify()与notifyAll()区别
  • 探展2024世界人工智能大会之合合信息扫描黑科技~
  • Harmony学习(四)(应用程序框架基础)
  • 电影票房数据的获取,可以控制数量,并导出表格或csv
  • filebeat + logstash使用笔记
  • 优化PyCharm:让IDE响应速度飞起来
  • 一键生成视频并批量上传视频抖音、bilibili、腾讯(已打包)
  • 【vulnhub】The Ether: Evil Science靶机
  • 浦语提示词工程实践
  • 从文本到图像:深度解析向量嵌入在机器学习中的应用
  • 面试经验|问题
  • Linux配置go程序为service后台开机自启动
  • Google 是如何开发 Web 框架的
  • (ckeditor+ckfinder用法)Jquery,js获取ckeditor值
  • 「面试题」如何实现一个圣杯布局?
  • 【笔记】你不知道的JS读书笔记——Promise
  • 78. Subsets
  • java中的hashCode
  • js学习笔记
  • spring boot 整合mybatis 无法输出sql的问题
  • vue.js框架原理浅析
  • 前端
  • 思维导图—你不知道的JavaScript中卷
  • 以太坊客户端Geth命令参数详解
  • 你对linux中grep命令知道多少?
  • MyCAT水平分库
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • # 数仓建模:如何构建主题宽表模型?
  • # 数论-逆元
  • #define
  • #LLM入门|Prompt#1.7_文本拓展_Expanding
  • #stm32整理(一)flash读写
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (c语言)strcpy函数用法
  • (M)unity2D敌人的创建、人物属性设置,遇敌掉血
  • (定时器/计数器)中断系统(详解与使用)
  • (附源码)计算机毕业设计ssm基于B_S的汽车售后服务管理系统
  • (数据大屏)(Hadoop)基于SSM框架的学院校友管理系统的设计与实现+文档
  • (一)基于IDEA的JAVA基础10
  • (转)从零实现3D图像引擎:(8)参数化直线与3D平面函数库
  • (转)母版页和相对路径
  • (自用)仿写程序
  • .NET 回调、接口回调、 委托
  • @Query中countQuery的介绍
  • [3300万人的聊天室] 作为产品的上游公司该如何?
  • [C# 开发技巧]如何使不符合要求的元素等于离它最近的一个元素
  • [datastore@cyberfear.com].Elbie、[thekeyishere@cock.li].Elbie勒索病毒数据怎么处理|数据解密恢复
  • [flink]部署模式
  • [flutter]一键将YAPI生成的api.json文件转为需要的Dart Model类的脚本
  • [Java、Android面试]_05_内存泄漏和内存溢出
  • [leetcode] 四数之和 M
  • [Linux内核驱动]模块参数