两路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播放 的相关文章

随机推荐

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

    写在前面 不知不觉又到了学期的末尾 不知道各位计算机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文件中