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

news2024/9/27 21:28:47

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1975359.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RabbitMQ的快速入门

目录 前言 1. 安装RabbitMQ 2.基本结构 3. RabbitMQ消息模型 ​​​​​​4. 入门案例 4.1 publisher实现 4.2 consumer实现 4.3 总结 前言 RabbitMQ是一套开源&#xff08;MPL&#xff09;的消息队列服务软件&#xff0c;是由 LShift 提供的一个 Advanced Message Q…

达梦数据库的系统视图v$cachesql

达梦数据库的系统视图v$cachesql 达梦数据库的系统视图V$CACHESQL的主要作用是提供缓冲区中SQL语句的信息&#xff0c;在 ini 参数 USE_PLN_POOL !0 时才统计。通过查询这个视图&#xff0c;用户可以了解SQL语句在缓冲区中的执行情况&#xff0c;包括SQL节点的类型、进入次数、…

滚珠丝杆与丝杆支撑座:稳定性与精度的双重保障

丝杆支撑座是连接滚珠丝杆与电机的轴承&#xff0c;采用优质的轴承能确保支撑座与滚珠丝杆之间的刚性平衡。那么&#xff0c;滚珠丝杆搭连接杆支撑座有哪些优缺点呢&#xff1f; 正常情况下&#xff0c;丝杆支撑座能够提供稳定的支撑力&#xff0c;确保滚珠丝杆在复杂工况下保持…

使用PasteSpider实现类似Jenkins的功能,让你的2G服务器也可以飞起

获取你接触过Jenkins&#xff0c;在我理解就是拉取源码&#xff0c;然后构建成镜像&#xff0c;最后启动容器&#xff01; 这个步骤你在PasteSpider上也可以实现&#xff0c;以下案例使用svn作为源码管理 如果你使用git作为源码管理&#xff0c;道理差不多 以我的代码为例 …

假期BUUCTF小练习3

文章目录 [极客大挑战 2019]BuyFlag[BJDCTF2020]Easy MD5[HCTF 2018]admin第一种方法 直接登录第二种方法 flack session伪造第三种方法Unicode欺骗 [MRCTF2020]你传你&#x1f40e;呢[护网杯 2018]easy_tornadoSSTI注入 [ZJCTF 2019]NiZhuanSiWei [极客大挑战 2019]BuyFlag 一…

好用的AI智能写作助手,创作者必备

随着科技的不断发展&#xff0c;人工智能&#xff08;AI&#xff09;在各个领域都起到了革命性的作用。在写作领域&#xff0c;AI智能写作助手已经成为了创作者们的必备工具。这些智能助手通过强大的自然语言处理能力和深度学习算法&#xff0c;能够帮助创作者们提高写作效率、…

网络安全领域含金量最高的5大赛事,每个网安人的梦!

做网络安全一定要知道的5大赛事&#xff0c;含金量贼高&#xff0c;如果你能拿奖&#xff0c;国内大厂随你挑&#xff0c;几乎是每个有志网安人的梦&#xff01; 一、 DEF CON CTF&#xff08;DEF CON Capture the Flag&#xff09; DEF CON CTF是DEF CON黑帽大会上的一项著名…

grep工具的使用

grep [options]…… pattern [file]…… 工作方式&#xff1a; grep 在一个或者多个文件中搜索字符串模板&#xff0c;如果模板中包括空格&#xff0c;需要使用引号引起来&#xff0c;模 板后的所有字符串会被看作是文件名。 工作结果&#xff1a;如果模板搜索成功&#xf…

算法力扣刷题记录 六十三【回溯章节开篇】

前言 开始回溯章节学习。 在二叉树中预先体会了回溯。那么回溯单独来说是怎么回事&#xff1f; 一、基础知识学习 回溯基础知识参考链接 二、组合问题 2.1题目阅读 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答…

C#--DirectShowLib 关闭自动白平衡和自动曝光时间

一、前言 因项目需要需关闭相机自身的自动白平衡和自动曝光时间&#xff0c;统统设定为恒定值&#xff0c;开始实验了opencvCvSharp&#xff0c;但是不起作用&#xff0c;故实验了更底层的 DirectShowLib &#xff0c;可成功关闭相机自带的自动白平衡和自动曝光时间&#xff0c…

用script实现的一个简易计算机

目录 一、计算器原理 二、源码 三、结果展示 四、不足与改进 一、计算器原理 首先我们列出想象中简易计算器的样子&#xff1a; 虽然画的磕碜但是应该不影响下面的操作hh 首先想到用表格形式在网页中表示它&#xff0c;是一个5行4列的表格&#xff0c;接着要实现计算器的…

ShardingSphere中的ShardingJDBC常见分片算法的实现

文章目录 ShardingJDBC快速入门修改雪花算法和分表策略核心概念分片算法简单INLINE分片算法STANDARD标准分片算法COMPLEX_INLINE复杂分片算法CLASS_BASED自定义分片算法HINT_INLINE强制分片算法 注意事项 ShardingJDBC Git地址 快速入门 现在我存在两个数据库&#xff0c;并…

【传知代码】Flan-T5 使用指南(论文复现)

当今&#xff0c;自然语言处理技术正在以前所未有的速度和精度发展。在这个领域中&#xff0c;Flan-T5作为一种新兴的预训练语言模型&#xff0c;正吸引着广泛的关注和应用。Flan-T5不仅仅是一个强大的文本生成工具&#xff0c;它还能通过提供高效的语义理解和多任务学习能力&a…

【java计算机毕设】智慧校园管理系统MySQL springboot vue HTML maven寒暑假小组设计项目源码作业带文档安装环境

目录 1项目功能 2项目介绍 3项目地址 1项目功能 【java计算机毕设】智慧校园管理系统MySQL springboot vue HTML maven寒暑假小组设计项目源码作业带文档安装环境 2项目介绍 系统功能&#xff1a; 智慧校园管理系统包括管理员、用户、老师三种角色。 管理员功能包括个人中心…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 团队关系判定(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题…

大语言模型学习笔记 LangChain简述

一、简述 LangChain是一个由大型语言模型 (LLM) 驱动的应用程序开发框架。LangChain 旨在为开发人员提供一系列功能&#xff0c;利用大型语言模型简化应用程序的创建和管理。 LangChain 可充当几乎所有 LLM 的通用接口&#xff0c;提供集中式开发环境来构建 LLM 应用程序并将其…

二十天刷leetcode【hot100】算法- day1[前端Typescript]

哈希表 1. 两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。你…

适用于个人使用的十大数据恢复工具:综合指南

有许多数据恢复工具和软件可用于帮助恢复丢失或损坏的文件。通过了解您的需求并考虑这里探讨的工具&#xff0c;您将能够选择最佳的数据恢复软件&#xff0c;并希望找回您丢失的宝藏。在本综合指南中&#xff0c;我们将探索个人使用的十大数据恢复工具&#xff0c;重点介绍它们…

ESP32人脸识别开发- 基础介绍(一)

一、ESP32人脸识别的方案介绍 目前ESP32和ESP32S3都是支持的&#xff0c;官方推的开发板有两种&#xff0c;一种 ESP-EYE ,没有LCD 另一种是ESP32S3-EYE,有带LCD屏 二、ESP32人脸识别选用ESP32的优势 ESP32S3带AI 加速功能&#xff0c;在人脸识别的速度是比ESP32快了不少 | S…

鸿蒙 IM 即时通讯开发实践,融云 IM HarmonyOS NEXT 版

融云完成针对“纯血鸿蒙”操作系统的 SDK 研发&#xff0c;HarmonyOS NEXT 版融云 IM SDK 已上线&#xff0c;开发者可在“鸿蒙生态伙伴 SDK 市场”查询使用。 发挥 20 年通信行业技术积累和领创品牌效应&#xff0c;融云为社交、娱乐、游戏、电商、出行、医疗等各行业提供专业…