两路wav文件读取解析和混音输出并使用WaveOut相关API播放

2023-12-16

wav文件格式简介

wav文件由一个RIFF块(Resource Interchange File Format,资源互换文件格式)组成,其中包含一个"fmt "块和一个"data"块。
RIFF块包含了文件的总体信息,具体如下

字段 大小 (字节) 含义
ChunkID 4 固定的4个字符:“RIFF”
ChunkSize 4 wav文件的总大小-8字节,(不包含ChunkID 和ChunkSize本身的大小)
Format 4 固定的4个字符:“WAVE”

"fmt "块包含了音频数据的格式信息,具体如下:

字段 大小 (字节) 含义
Subchunk1ID 4 固定的4个字符 "fmt ",注意最后一个字符为空格且不可省略
Subchunk1Size 4 fmt 块的大小
AudioFormat 2 编码格式
NumChannels 2 声道数,1:单声道,2:双声道
SampleRate 4 采样率
ByteRate 4 码率,每秒传输的字节数,计算方法:SampleRate * NumChannels * BitsPerSample / 8
BlockAlign 2 块对其,播放时一次性需要处理的字节,计算方法:BitPerSample * NumChannels / 8
BitsPerSample 2 采样位数,一般为8,16,32,64等

"data"块包含了实际的音频数据,具体如下

字段 大小 (字节) 含义
Subchunk2ID 4 固定4个字符:''data"
Subchunk2Size 4 pcm原始音频数据的大小,单位:字节

在这里插入图片描述

wav文件头定义

//文件名:WAVEHeader.h 

//"RIFF"块 
 struct RIFF_CHUNK {
	char chunkID[4];//"RIFF"
	int  chunkSize;//整个文件的大小-8字节 
	char format[4];//"WAVE"
};
//"fmt "块
 struct FMT_CHUNK {
	char chunkID[4];//"fmt ",注意最后一个字节的内容为空格" ",不可省略
	int  chunkSize;
	short audioFormat;
	short numChannels;//单声道:1,立体声:2
	int sampleRate;//采样率
	int byteRate;//码率, SampleRate * NumChannels * BitsPerSample/8 
	short blickAlign;//NumChannels * BitsPerSample
	short bitsPerSample;//8bits=8,16bit=16,以此类推
};
//"data" 块
 struct DATA_CHUNK
{
	char chunkID[4];//"data"
	int  chunkSize;//pcm音频数据大小
};

struct WAVHeader
{
	RIFF_CHUNK riff;
	FMT_CHUNK fmt;
	DATA_CHUNK data;
};

读取wav文件



    ifstream fin("test_s8le.wav", ios::binary);
    if (!fin) {
        cout << "open file failed!" << endl;
        return 1;
    }
    WAVHeader header;
    //读取wav文件头并保存到header对象中
    fin.read((char*) & header, sizeof(header));
    if (strncmp(header.riff.chunkID, "RIFF", 4) != 0 || strncmp(header.riff.format, "WAVE", 4) != 0
        || strncmp(header.fmt.chunkID, "fmt ", 4) != 0 || strncmp(header.data.chunkID, "data", 4) != 0) {
        cout << "file is not a valid WAV file" << endl;
        return 1;
    }
	cout << "audio format:" << header.fmt.audioFormat << endl;
	cout << "channel couts:" << header.fmt.numChannels << endl;
	cout << "sample rate:" << header.fmt.sampleRate << endl;
	cout << "byte rate:" << header.fmt.byteRate << endl;
	cout << "bits per sample:" << header.fmt.bitsPerSample << endl;
	cout << "data size:" << header.data.chunkSize << endl;
    cout <<" ---------" << endl;
    
    char* pcmData = new char[header.data.chunkSize];
    //读取wav文件的pcm数据部分,保存到char 数组中
    fin.read(pcmData, header.data.chunkSize);


读取背景音文件

 //读取背景音文件
    ifstream finBg("background_s8le.wav", ios::binary);
    if (!finBg) {
        return 1;
    }

    WAVHeader bgHeader = {};
    finBg.read((char*)&bgHeader, sizeof(header));

	cout << "audio format:" << bgHeader.fmt.audioFormat << endl;
	cout << "channel couts:" << bgHeader.fmt.numChannels << endl;
	cout << "sample rate:" << bgHeader.fmt.sampleRate << endl;
	cout << "byte rate:" << bgHeader.fmt.byteRate << endl;
	cout << "bits per sample:" << bgHeader.fmt.bitsPerSample << endl;
	cout << "data size:" << bgHeader.data.chunkSize << endl;

    char* bgPcmData = new char[bgHeader.data.chunkSize];
    finBg.read(bgPcmData, bgHeader.data.chunkSize);

音频混音

    //----混音start--------------------------
    //音频格式:8bit 8000hz 1channels 
	//使用算法:线性叠加后求平均
	//优点:不会产生溢出,噪音较小;
	//缺点:衰减过大,影响输出音频质量;
    int maxSize = header.data.chunkSize > bgHeader.data.chunkSize ? header.data.chunkSize : bgHeader.data.chunkSize;
    char* targetPcmData = new char[maxSize];
    for (int i = 0; i < maxSize; i++) {
        if (i < header.data.chunkSize && i < bgHeader.data.chunkSize) {
            targetPcmData[i] = (char)(((int16_t)pcmData[i] + (int16_t)bgPcmData[i]) / 2);
        }
        else if (i < header.data.chunkSize) {
            targetPcmData[i] = pcmData[i];
        }
        else {
            targetPcmData[i] = bgPcmData[i];
        }
    }

    //--混音end------------------------------------------------------

使用Windows WaveOut 相关API播放混音后的音频数据

WAVEFORMATEX waveFormat;
    /*
		WAVEFORMATEX是一种数据结构,用于指定波形音频流的数据格式。它包含以下字段:
		wFormatTag:设置波形声音的格式。
		nChannels:设置音频文件的通道数量,对于单声道的声音,此值为1;对于立体声,此值为2。
		nSamplesPerSec:设置每个声道播放和记录时的样本频率。
		nAvgBytesPerSec:设置每秒平均字节数。
		nBlockAlign:设置数据块的对齐方式,即最小数据的原子大小。
		wBitsPerSample:设置每个样本的位数。
		cbSize:设置此结构的大小。
    */
    waveFormat.wFormatTag = WAVE_FORMAT_PCM;
    waveFormat.nChannels = header.fmt.numChannels;
    waveFormat.nSamplesPerSec = header.fmt.sampleRate;
    waveFormat.nBlockAlign = header.fmt.blickAlign;
    waveFormat.wBitsPerSample = header.fmt.bitsPerSample;
    waveFormat.nAvgBytesPerSec =  waveFormat.nSamplesPerSec*waveFormat.wBitsPerSample/8;
    waveFormat.cbSize = 0;

    waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormat, (DWORD_PTR)0, 0, CALLBACK_NULL);
    waveOutHdr.lpData = targetPcmData;
    waveOutHdr.dwBufferLength = maxSize;

    waveOutPrepareHeader(hWaveOut, &waveOutHdr, sizeof(WAVEHDR));
    waveOutWrite(hWaveOut, &waveOutHdr, sizeof(WAVEHDR));

将混音后的数据保存到新的wav文件中

 //输出混音后的数据到wav文件
 
	HWAVEOUT hWaveOut; // waveOut设备句柄
	WAVEHDR waveOutHdr; // waveOut数据块头
	
    ofstream fout("output.wav", ios::binary);
    if (!fout) {
        cout << "output.wav create failed" << endl;
        return 1;
    }
    WAVHeader oheader = header.data.chunkSize > bgHeader.data.chunkSize ? header : bgHeader;
    fout.write((char*)&oheader, sizeof(oheader));
    fout.write(targetPcmData, maxSize);

源码下载

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

两路wav文件读取解析和混音输出并使用WaveOut相关API播放 的相关文章

  • 从实体获取单列

    如何从查询中获取单个列而不是整个对象 我可以这样做来获取整个对象 但我想要的只是名称 IList
  • 通过另一个列表更新列表(linq)

    我有类 Data 的对象列表 如下所示 class Data int code string name DateTime date update 我还有另一个课程列表 例如 class RefCodes int old code int n
  • 当其源是 https uri 时如何使 wpf MediaElement 播放

    在 wpf 独立应用程序 exe 中 我在主窗口中包含了 MediaElement
  • C++中的类要具备什么条件才能成为容器?

    我是 C 编程新手 偶然发现了这个术语containers举例如下vector deque map etc 一个企业的最低要求应该是什么class应该满足被称为container in C 我将从 范围 这个概念开始 Range 只有两个方
  • MSMQ接收和删除

    是否有任何选项可以在读取消息后将其从 MSMQ 中删除 比如 接收 删除可以作为原子操作运行吗 听起来您想查看下一条消息 然后在处理完成后接收它 Message message Queue Peek Queue ReceiveById me
  • 如何查明 .exe 是否正在 C++ 中运行?

    给定进程名称 例如 程序 exe C 标准库没有这样的支持 您需要一个操作系统 API 来执行此操作 如果这是 Windows 那么您将使用 CreateToolhelp32Snapshot 然后使用 Process32First 和 Pr
  • 以下 PLINQ 代码没有改进

    我没有看到使用以下代码的处理速度有任何改进 IEnumerable
  • 从时间列表中查找最接近的时间

    所以 这是场景 我有一个带有创建时间的文件 我想从该文件的创建时间最接近或相等的时间列表中选择一个时间 完成此操作的最佳方法是什么 var closestTime listOfTimes OrderBy t gt Math Abs t fi
  • C 类型命名约定,_t 或 ALLCAPS

    我一直想知道是否有任何命名约定 例如何时对类型使用全部大写以及何时追加 t 什么时候不使用任何东西 我知道当时 K R 发布了各种有关如何使用 C 的文档 但我找不到任何相关内容 在 C 标准库类型中 t看起来漂亮占主导地位 time t
  • 为什么 std::function 不是有效的模板参数,而函数指针却是?

    我已经定义了名为的类模板CallBackAtInit其唯一目的是在初始化时调用函数 构造函数 该函数在模板参数中指定 问题是模板不接受std function作为参数 但它们接受函数指针 为什么 这是我的代码 include
  • 如何增加ofstream的缓冲区大小

    我想增加 C 程序的缓冲区大小 以便它不会过于频繁地写入 默认缓冲区是 8192 字节 我尝试使用 pubsetbuf 将其增加到 200K 原始代码 ofstream fq fastq1 cstr ios out fastq1 is a
  • 如何设置消息队列的所有者?

    System Messaging MessageQueue 类不提供设置队列所有权的方法 如何以编程方式设置 MSMQ 消息队列的所有者 简短的答案是 p invoke 对 windows api 函数的调用MQSetQueueSecuri
  • 如果在代码中添加元素,“FindName”将不起作用

    在 WPF 应用程序中 如果在 XAML 中声明 ContentControl
  • 如何使用 C# 查询远程 MS ACCESS .mdb 数据库

    我正在尝试使用 C 查询 mote MS ACCESS 数据库 mdb 文件 将文件复制到本地计算机时可以成功查询它 我只想远程放置文件 所以我的客户端程序不包含原始数据 static string m path http www xyz
  • WinForms - 加载表单时如何使用 PaintEventArgs 运行函数?

    我试图理解图形 在 Graphics FromImage 文档中 它有这样的示例 private void FromImageImage PaintEventArgs e Create image Image imageFile Image
  • 在 mvc4 中创建通用 mvc 视图

    我以前也提过类似的问题 没有得到答案 如何创建一个通用的 mvc4 视图 该视图可以显示传递给它的模型列表或单个模型 模型可以是个人 组织或团体 无论传递给它的是什么 如果您正在寻找类似的东西 model MyViewModel
  • 用数组或向量实现多维数组

    我想使用单个数组或向量实现多维数组 可以像通常的多维数组一样访问它 例如 a 1 2 3 我陷入困境的是如何实施 操作员 如果数组的维数为 1 则 a 1 应该返回位于索引 1 处的元素 但是如果维数大于一怎么办 对于嵌套向量 例如 3 维
  • 将日期时间显示为 MM/dd/yyyy HH:mm 格式 C#

    在数据库中 日期时间以 MM dd yyyy HH mm ss 格式存储 但是 我想以 MM dd yyyy HH mm 格式显示日期时间 我通过使用 String Format 进行了尝试 txtCampaignStartDate Tex
  • 不使用放置 new 返回的指针时的 C++ 严格别名

    这可能会导致未定义的行为吗 uint8 t storage 4 We assume storage is properly aligned here int32 t intPtr new void storage int32 t 4 I k
  • 在 C 中使用 #define 没有任何价值

    If a define没有任何价值地使用 例如 define COMMAND SPI 默认值是0吗 不 它的评估结果为零 从字面上看 该符号被替换为空 然而 一旦你有了 define FOO 预处理器条件 ifdef FOO现在将是真的 另

随机推荐

  • 期末备考 |《数学物理方法》期末备考资料包来啦!

    写在前面 不知不觉又到了学期的末尾 不知道各位计算机er的 专业课复习得怎么样了呢 为了帮助大家更好地 备战期末 从今天开始 岛主将持续为大家更新 计算机期末备考资料 为同学们的绩点护航 今天岛主为大家带来的是 数学物理方法 期末备考资料包
  • Solidity之旅(十)OOP-抽象合约

    抽象合约 abstractcontract 前文在讲合约继承的基类构造函数的参数时 有提到抽象合约 也就是说 如果派生合约未能给其继承的基合约指定构造函数参数时 那么 该派生合约必须声明为抽象合约 abstractcontract 我们知道
  • Graylog 中日志级别及其对应的数字

    在 Graylog 中 日志级别 level 通常使用数字表示 数字越低表示日志级别越高 以下是常见的日志级别及其对应的数字表示 DEBUG 调试 对应数字 7 INFO 信息 对应数字 6 NOTICE 通知 对应数字 5 WARN 警告
  • ERP、SAP、MES 三者之间的区别是什么?

    ERP SAP MES之间有什么区别 SAP 思爱普 是ERP系统与企业管理解决方案 提供商 而ERP和MES是两个用途不一样的 管理系统 也就是说 SAP是一家厂商 提供包含ERP在内的管理系统 SAP搞清楚了 那么 ERP和MES 呢
  • GoLong的学习之路,进阶,Viper(yaml等配置文件的管理)

    本来有今天是继续接着上一章写微服务的 但是这几天有朋友说 再写Web框架的时候 遇到一个问题 就是很多的中间件 redis 微信 mysql mq 的配置信息写的太杂了 很不好管理 希望我能写一篇有管理配置文件的 所以这篇就放到今天写吧 微
  • 【抄作业】ImportError :cannot import name xxxxxx ,原博主Activewaste

    前情介绍 网上关于这种问题的解决方案一大堆 但是绝大多数都是不适用 或者说解决不了问题 我根据别人所遇到的和我自己遇到的 对这个问题整理了一下 希望能解决这个问题 问题分析 一 缺少这个module或者func或者package 缺少pyt
  • 波奇学Linux:环境变量,本地变量和内建命令

    Windows下的环境变量 echo PATH 查看指令搜索命令路径 在bash命令行输入的指令 系统根据PATH中的路径查询 增加PATH指令 PATH等于上面的路径 表示不同路径分割符 home boki lesson13代表新的路径
  • 基于java中SSM框架实现门诊药品管理系统演示【附项目源码+论文说明】

    基于java中SSM框架实现门诊药品管理系统演示 摘要 21世纪的今天 随着社会的不断发展与进步 人们对于信息科学化的认识 已由低层次向高层次发展 由原来的感性认识向理性认识提高 管理工作的重要性已逐渐被人们所认识 科学化的管理 使信息存储
  • 数说CS | 不招学硕?拟录取人数持续增长?北大软件与微电子学院为何如此热门?

    写在前面 北京大学软件与微电子学院 软件工程学科评估为A类 招收哪些专业 保研录取情况如何 今天 岛主就带你 深度揭秘北京大学软件与微电子学院 01 院校介绍 北京大学软件与微电子学院成立于2002年3月 如今已形成了一个学院 北京大学软件
  • 数说CS | 拟录取名额上涨,开设九推?上岸复旦大学计算机科学与技术学院更轻松了吗?

    写在前面 复旦大学计算机科学技术学院 学科评估为A类 招收哪些专业 保研录取情况如何 今天 岛主就带你 深度揭秘复旦大学计算机科学技术学院 01 院校介绍 复旦大学计算机学科创建于 中国计算机事业的起步期 始于 1956 年自主建造的国内第
  • 工业级路由器在货运物流仓储管理中的应用

    工业级路由器在货运物流仓储管理中扮演着重要的角色 为整个物流系统提供了稳定可靠的网络连接和数据传输支持 下面将从以下几个方面介绍工业级路由器在货运物流仓储管理中的应用 实时监控和追踪 工业级路由器通过与各种传感器 监控设备和物联网设备的连接
  • 人工智能自然语言处理:语言之美,算法之智

    导言 自然语言处理 Natural Language Processing NLP 是人工智能领域中备受关注的分支 致力于让计算机能够理解 处理和生成人类语言 本文将深入研究人工智能在自然语言处理领域的关键技术 应用场景以及未来发展趋势 1
  • 学长休学一年强势回归,截胡了我的保研名额……

    写在前面 保研是一场持久的战役 从评定保研资格到申请梦校offer 每一步都需要保研er费尽九牛二虎之力 其中 最怕的便是半路杀出个程咬金 让一切的努力化为乌有 算到了加分刺客 算到了名额变动 独独没想到 被上届休学归来的学长姐挤占了保研名
  • 人工智能计算机视觉:解析现状与未来趋势

    导言 随着人工智能的迅速发展 计算机视觉技术逐渐成为引领创新的关键领域 本文将深入探讨人工智能在计算机视觉方面的最新进展 关键挑战以及未来可能的趋势 1 简介 计算机视觉是人工智能的一个重要分支 其目标是使机器具备类似于人类视觉的能力 这一
  • C++函数模板与类模板

    目录 C 模板定义 函数模板 类模板 类模板的定义 模板的优缺点 模板的优点 模板的缺点 C 模板定义 C 模板允许程序员在通用编程中创建可重用的代码 这种编程技术基于模板的编
  • echarts环形饼图

    效果示例 代码汇总 pieCharts let data const providerResult name 智诺 value 23 name 海康 value 5 name 大华 value 5 name 云科 value 23 name
  • 开考在即?四六级押题卷免费送!

    距12月16日四六级考试 还有 1个多月 的时间啦 在这短短一月时间里 只有 考前押题和历年真题 才能在短时间内帮助到你们 所以 岛主给你们准备了 今年 12月四六级绝密押题卷 还包含 历年真题卷 答案详解 没有时间复习 想考前突击一下的同
  • 在openEuler上安装openGauss2023年12月最新openGauss5.0.0LTS版本全图片解析

    先说环境 虚拟机 openEuler22 03 LTS ip 192 168 88 129 普通用户 yirc99 和 root用户 主机win10 要安装的数据库 openGauss 5 0 0 LTS 在下面的文章中可能会出现命令不存在
  • 迅为IMX6UL核心板在便携式医疗设备中的应用方案

    在科技日益发展的今天 便携式医疗设备变得越来越受欢迎 这些小巧 轻便的设备 例如IMX6UL核心板 为医疗行业带来了巨大的变革 它们不仅便于携带 而且集成了多种功能 满足了人们对健康管理的各种需求 便携式医疗设备在当今社会越来越受到欢迎 这
  • 两路wav文件读取解析和混音输出并使用WaveOut相关API播放

    目录 wav文件格式简介 wav文件头定义 读取wav文件 读取背景音文件 音频混音 使用Windows WaveOut 相关API播放混音后的音频数据 将混音后的数据保存到新的wav文件中