XAudio2学习之流化音频文件

流化是一个过程,在这个过程中,我们只需要在内存中维护一小块内存来播放音频文件。这样我们就可以使用很大的音频文件作为背景音乐,而不占用很大的内存。当我们流化一个音频文件的时候,音频数据是从硬盘上一块一块的读取,而不是将整个文件一次性全部加载。流化是通过异步读取音频数据到硬盘缓冲区队列来完成的。当一个缓冲区填充好数据后,提交给source voice。当source voice播放完成一个缓冲区后,这个缓冲区就可以再次用来读取文件中的数据。通过这种方式循环使用缓冲区,允许我们只加载一部分文件的数据就可以完成对一个很大的音频文件的播放。流化的代码应该放在一个单独的线程中,这样当需要等待长时间的读取或者音频播放操作时可以睡眠。当音频播放完成时,我们可以使用回调触发事件来唤醒线程。

创建一个类继承IXAudio2VoiceCallback接口,并实现。在 void OnBufferEnd( void* ){ SetEvent( hBufferEndEvent ); }方法中设置一个事件,当音频播放需要等待时,线程会自动进入睡眠,当当前buffer播放完成时,SetEvent中的事件被触发从而唤醒线程。

例子的主要实现方法是,在主线程中完成COM初始化,XAudio2引擎和IXAudio2MasteringVoice的创建,并创建线程,把流化文件的回调函数传递给线程,并将文件名通过线程传递给回调。启动线程,会进入回调函数中进行文件解析,读取文件数据,并进行播放。

示例中的缓冲区是循环使用,这样就达到了只维护一小块内存就可以满足播放一个很大的音频文件的要求。

每个缓冲区的大小设置为10240,共5个缓冲区,在一个数组里面。通过维护数组的索引进行循环利用,通过IXAudio2VoiceCallback来保证等待队列不超过5,这样就可以保证等待队列中的数据在播放完成之前不被覆盖。如果数据还没播放完成就失效,那么会导致XAudio2崩溃。

DirectX提供了StreamAudios示例,不过我没有采用它的,代码是我自己重新实现的。DirectX示例:http://download.csdn.net/detail/u011417605/9484060

完整代码:

#pragma once
#include "WaveFile.h"
#include "XAudio2.h"
#include <thread>

#define STREAMBUFFERSIZE 10240//每个缓冲区大小
#define BUFFERNUM 5//缓冲区个数
BYTE streamBuffers[BUFFERNUM][STREAMBUFFERSIZE];//缓冲区数组

class StreamingVoiceContext : public IXAudio2VoiceCallback
{
public:
	HANDLE hBufferEndEvent;
	StreamingVoiceContext() : hBufferEndEvent(CreateEvent(NULL, FALSE, FALSE, NULL)){}
	~StreamingVoiceContext(){ CloseHandle(hBufferEndEvent); }
	void OnStreamEnd() { /*SetEvent(hBufferEndEvent);*/ }
	void OnVoiceProcessingPassEnd() { }
	void OnVoiceProcessingPassStart(UINT32 SamplesRequired) {    }
	//Called when the voice has just finished playing an audio buffer.
	void OnBufferEnd(void * pBufferContext)    { SetEvent(hBufferEndEvent); }
	void OnBufferStart(void * pBufferContext) {    }
	void OnLoopEnd(void * pBufferContext) { /*SetEvent(hBufferEndEvent);*/ }
	void OnVoiceError(void * pBufferContext, HRESULT Error) { }
};

IXAudio2 *pEngine = NULL;
int currentBufferIndex = 0;

void StreamAudioFile(LPWSTR fileName)//线程回调
{
	CWaveFile waveFile;
	HRESULT hr = waveFile.Open(fileName, NULL, WAVEFILE_READ);//加载文件
	if (FAILED(hr))
		return;

	WAVEFORMATEX *waveFormat = waveFile.GetFormat();//获取文件格式

	StreamingVoiceContext pCallBack;
	IXAudio2SourceVoice *pSourceVoice = NULL;
	hr = pEngine->CreateSourceVoice(&pSourceVoice, waveFormat, 0, 1.0f, &pCallBack);//创建源声音,用来提交数据
	if (FAILED(hr))
		return;

	int fileSize = waveFile.GetSize();//获取文件的大小
	int currentPos = 0;
	while (currentPos < fileSize)
	{
		DWORD size = STREAMBUFFERSIZE;
		hr = waveFile.Read(streamBuffers[currentBufferIndex], size, &size);//读取文件内容
		if (FAILED(hr))
			break;
		currentPos += size;//已经播放的数据大小

		XAUDIO2_BUFFER buffer = { 0 };//将读取的文件数据,赋值XAUDIO2_BUFFER
		buffer.AudioBytes = size;
		buffer.pAudioData = streamBuffers[currentBufferIndex];

		hr = pSourceVoice->SubmitSourceBuffer(&buffer);//提交内存数据
		if (FAILED(hr))
			break;

		hr = pSourceVoice->Start(XAUDIO2_COMMIT_NOW);//启动源声音
		if (FAILED(hr))
			break;

		XAUDIO2_VOICE_STATE state;
		pSourceVoice->GetState(&state);//获取状态
		while (state.BuffersQueued > BUFFERNUM - 1)//不让缓冲区的音频数据覆盖
		{
			WaitForSingleObject(pCallBack.hBufferEndEvent, INFINITE);
			pSourceVoice->GetState(&state);
		}
		currentBufferIndex++;//循环利用 缓冲区
		currentBufferIndex %= BUFFERNUM;
	}

	XAUDIO2_VOICE_STATE state;
	while (pSourceVoice->GetState(&state), state.BuffersQueued > 0)//等待队列中的数据播放完成,退出线程
	{
		WaitForSingleObject(pCallBack.hBufferEndEvent, INFINITE);
	}
	pSourceVoice->DestroyVoice();//释放资源
}

 int main(int argc, char *argv[])
 {
	 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);;//com初始化
 	if (FAILED(hr))
 		return 0;

 	hr = XAudio2Create(&pEngine);//创建引擎
 	if (FAILED(hr))
 		return 0;

 	IXAudio2MasteringVoice *pMasterVoice = NULL;
 	hr = pEngine->CreateMasteringVoice(&pMasterVoice);//创建主声音,默认是输出当前扬声器
 	if (FAILED(hr))
 		return 0;

	LPWSTR fileName = L"F:\\桌面\\TestSong.wav";
	std::thread t(StreamAudioFile, fileName);
	t.join();

	fileName = L"F:\\桌面\\Example2.wav";
	std::thread p(StreamAudioFile, fileName);
	p.join();

 	pMasterVoice->DestroyVoice();//释放资源
 	pEngine->Release();//释放资源
 	CoUninitialize();//释放资源
 	return 0;
 }

交流QQ:1245178753

源码下载:http://download.csdn.net/detail/u011417605/9484075

本文地址:http://blog.csdn.net/u011417605/article/details/51087826

时间: 2024-08-27 05:35:24

XAudio2学习之流化音频文件的相关文章

吴裕雄--天生自然 JAVA开发学习:流(Stream)、文件(File)和IO

BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //使用 BufferedReader 在控制台读取字符 import java.io.*; public class BRRead { public static void main(String args[]) throws IOException { char c; // 使用 System.in 创建 BufferedReader Buffe

XAudio2学习之采样率转换

使用IXAduio2SubmixVoice可以对wave文件进行采样率转换,以满足设备对波形音频采样率的要求.在XAudio2学习三之获取音频输出设备信息可以看到音频播放设备是有自己支持播放的wave文件的格式的. 以将2通道/44.1k采样率的wave转为2通道/48k的wave为例: 首先,初始化COM组件: HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);;//com初始化 if (FAILED(hr)) return 0;

java 流操作对文件的分割和合并的实例详解_java - JAVA

文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 java 流操作对文件的分割和合并的实例详解 学习文件的输入输出流,自己做一个小的示例,对文件进行分割和合并. 下面是代码: package com.dufy.file; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import jav

【音视频连载-007】基础学习篇-SDL 播放 PCM 音频文件(上)

音视频学习入门技术文章连载: 技术开发故事会连载 [音视频连载-001]基础学习篇-SDL 介绍以及工程配置 [音视频连载-002]基础学习篇-SDL 创建窗口并显示颜色 [音视频连载-003]基础学习篇-SDL 消息循环和事件响应 [音视频连载-004]基础学习篇-SDL 加载图片并显示 [音视频连载-005]基础学习篇-SDL 加载 YUV 文件并显示 [音视频连载-006]基础学习篇-SDL 播放 YUV 视频文件]) 在前面的文章中已经能够利用 SDL 去播放 YUV 视频文件了,接下来

UNIX环境编程学习笔记(13)——文件I/O之标准I/O流

lienhua342014-09-29 1 标准 I/O 流 之前学习的都是不带缓冲的 I/O 操作函数,直接针对文件描述符的,每调用一次函数可能都会触发一次系统调用,单次调用可能比较快捷.但是,对于需要频繁进行 I/O 操作的程序,频繁触发系统调用产生的消耗太大. 标准 I/O 库提供了带缓冲的 I/O 操作函数,这些函数围绕着一种叫做流(stream)的东西进行.当使用标准 I/O 库打开或创建一个文件时,系统提供了一个流与这个文件相关联.通过流的读入和输出完成所需要的 I/O操作. 标准

C++学习47 文件的概念 文件流类与文件流对象 文件的打开与关闭

迄今为止,我们讨论的输入输出是以系统指定的标准设备(输入设备为键盘,输出设备为显示器)为对象的.在实际应用中,常以磁盘文件作为对象.即从磁盘文件读取数据,将数据输出到磁盘文件.磁盘是计算机的外部存储器,它能够长期保留信息,能读能写,可以刷新重写,方便携带,因而得到广泛使用. 文件(file)是程序设计中一个重要的概念.所谓“文件”,一般指存储在外部介质上数据的集合.一批数据是以文件的形式存放在外部介质(如磁盘.光盘和U盘)上的.操 作系统是以文件为单位对数据进行管理的,也就是说,如果想找存在外部

哔哩哔哩视频与音频文件流合并

ffmpeg下载地址:http://www.ffmpeg.org/download.html 这里下载Windows版本的:ffmpeg-4.2.2-win64-static.zip 解压后bin目录下的内容(假设在D盘的bin路径下): public class App { // ffmpeg.exe全路径 private static final String FFMPEG_PATH = "D:/bin/ffmpeg.exe"; public static void main(St

PHP 将amr音频文件转换为mp3格式

说下整体思路 1.服务器安装ffmpeg 2.使用ffmpeg -i 指令来转换amr为mp3格式(这个到时候写在PHP代码中,使用exec函数执行即可) 3.在网页端使用HTML5的audio标签来播放mp3文件 下面是操作细节: 一.服务器安装ffmpeg以cenos为例 此处参考:http://my.oschina.NET/ethan09/blog/372435 需要特别注意的是,在下面的方法中,amrnb和amrwb的安装到make环节会请求3gp的一个网址,一般是请求不到的,可以用cr

XAudio2学习之IXAudio2VoiceCallback回调

使用IXAudio2VoiceCallback回调的好处是,在音频数据播放完的时候,可以通知外部程序进行其他操作. 使用IXAudio2VoiceCallback需要继承此接口,然后重新实现,因为内部所有函数都是纯虚函数.所有函数中使用比较多的是OnStreamEnd函数,当音频数据播放完成的时候,OnStreamEnd会触发一个事件. 继承实现IXAudio2VoiceCallback接口: class VoiceCallback : public IXAudio2VoiceCallback