C++编程:XAudio2 API应用示例

来源:互联网 发布:电脑qq多开器软件 编辑:程序博客网 时间:2024/06/03 19:29

C++编程:XAudio2 API应用示例

XAudio2是一个跨平台的API,在Xbox 360Windows中得到支持。在Xbox 360上, XAudio2作为一个静态库编译到游戏可执行文件中。在Windows上,XAudio2提供一个动态链接库(DLL)。以下例子只使用了其中的一部分功能,并不全面。详情请看微软技术页的XAudio2编程相关(英文)

使用XAudio2来播放未压缩的PCM音频数据的过程并不复杂,主要有以下几个步骤:

  1. 建立XAudio2 引擎

使用XAudio2Create函数,该函数的功能是创建一个XAudio2对象(IXAudio2接口)

原型:HRESULT XAudio2Create(

IXAudio2 **ppXAudio2,//这里返回XAudio2对象的指针

UINT32 Flags,//此处必须为0 

XAUDIO2_PROCESSOR XAudio2Processor//指定所用CPU,默认:XAUDIO2_DEFAULT_PROCESSOR

);

示例:XAudio2Create( &pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR );

 

  1. 使用第1步建立的引擎建立MasteringVoice

使用IXAudio2成员函数CreateMasteringVoice,该函数功能是创建并设置一个MasteringVoice

原型:HRESULT CreateMasteringVoice(

IXAudio2MasteringVoice **ppMasteringVoice,//这里返回MasteringVoice对象指针

UINT32 InputChannels = XAUDIO2_DEFAULT_CHANNELS,//设置声道数,可选

UINT32 InputSampleRate = XAUDIO2_DEFAULT_SAMPLERATE, //设置采样率,可选

UINT32 Flags = 0,//必须是0,可选

LPCWSTR szDeviceId = NULL, //设备,可选,NULL代表全局默认输出设备

const XAUDIO2_EFFECT_CHAIN *pEffectChain = NULL, //音效,可选

AUDIO_STREAM_CATEGORY StreamCategory = AudioCategory_GameEffects//流类型,可选

);

示例:pXAudio2->CreateMasteringVoice(&pMasterVoice);

 

  1. 使用第一步建立的引擎建立SourceVoice(SubmixVoice,以下按SourceVoice举例)

使用IXAudio2成员函数CreateSourceVoice,该函数功能是创建并设置一个SourceVoice

原型:HRESULT CreateSourceVoice(

IXAudio2SourceVoice **ppSourceVoice, //这里返回IXAudio2SourceVoice对象指针

const WAVEFORMATEX *pSourceFormat, //PCM音频格式(下面讲到)

UINT32 Flags = 0,//SourceVoide工作方式,可选

float MaxFrequencyRatio = XAUDIO2_DEFAULT_FREQ_RATIO,//声调,可选,默认为1

IXAudio2VoiceCallback *pCallback = NULL,//回调类指针,可选

const XAUDIO2_VOICE_SENDS *pSendList = NULL,//目标格式设置,可选

const XAUDIO2_EFFECT_CHAIN *pEffectChain = NULL//音效设置,可选

);

示例:pXAudio2->CreateSourceVoice(&pSourceVoice,&format,0,XAUDIO2_DEFAULT_FREQ_RATIO,NULL,NULL,NULL);

 

其中format这样设置:(位数为bits,声道数为channels,采样率为hz

WAVEFORMATEX format;

format.wFormatTag = WAVE_FORMAT_PCM;//PCM格式

format.wBitsPerSample = bits;//位数

format.nChannels = channels;//声道数

format.nSamplesPerSec = hz;//采样率

format.nBlockAlign = bits*channels/8;//数据块调整

format.nAvgBytesPerSec = format.nBlockAlign*hz;//平均传输速率

format.cbSize = 0;//附加信息

 

  1. 呈交音频数据

使用IXAudio2SourceVoice的成员函数SubmitSourceBuffer,该函数功能是呈交一个XAUDIO2_BUFFER

原型:HRESULT SubmitSourceBuffer(

const XAUDIO2_BUFFER *pBuffer,// 结构体XAUDIO2_BUFFER的指针(下面讲到)

const XAUDIO2_BUFFER_WMA *pBufferWMA = NULL//wma格式Buffer的指针,可选,默认NULL

);

示例:pSourceVoice->SubmitSourceBuffer(&XAudio2Buffer,NULL);

 

其中XAudio2Buffer这样设置:

XAUDIO2_BUFFER XAudio2Buffer;

XAudio2Buffer.Flags = 0;//可以设为0XAUDIO2_END_OF_STREAM,当设为后者时,将使XAudio2播放完该数据块后自动停止,不再播放下一个数据块

XAudio2Buffer.AudioBytes = BufferSize;// 音频数据的长度,按字节算

XAudio2Buffer.pAudioData = pBuffer;//具体音频数据的地址,unsigned char pBuffer[]

XAudio2Buffer.PlayBegin = 0;//起始播放地址

XAudio2Buffer.PlayLength = 0;//播放长度,0为整数据块

XAudio2Buffer.LoopBegin = 0;//循环起始位置

XAudio2Buffer.LoopLength = 0;//循环长度,按字节算

XAudio2Buffer.LoopCount = 0;//循环次数,0为不循环,255为无限循环

XAudio2Buffer.pContext = NULL;//这里的pContext用来标识该数据块,供回调用,可以是NULL

 

  1. 继续呈交数据和播放数据:

播放呈交的数据使用IXAudio2SourceVoice的成员函数Start,该函数功能是开始播放。

原型:HRESULT Start(

UINT32 Flags,//必须是0

UINT32 OperationSet = XAUDIO2_COMMIT_NOW//使用XAUDIO2_COMMIT_NOW将立即生效,使用XAUDIO2_COMMIT_ALL将挂起,等待其它的数值的OperationSet的处理完

);

示例:pSourceVoice->Start(0, XAUDIO2_COMMIT_NOW);

 

5步做完之后,XAudio2将一块接一块地播放呈交的数据块。我们只需不断重复第四步,就能不断地播放音频数据了。需要注意的是,在XAudio2播放完某个XAudio2Buffer之前,该XAudio2Buffer以及XAudio2Buffer.pAudioData所指向的内存不能被修改或删除,否则将发生错误。但是某个XAudio2Buffer一旦被播放完,就能被修改了。为此,我们可以创建一个数组XAUDIO2_BUFFER []来循环呈交和更新数据。那怎么知道XAudio2到底播放了几个XAudio2Buffer呢,可以使用IXAudio2SourceVoice的成员函数GetState

原型:GetState(

XAUDIO2_VOICE_STATE *pVoiceState,// 这里返回XAUDIO2_VOICE_STATE结构体指针

[optional]  UINT32 Flags//获取方式,可选,默认0.设为XAUDIO2_VOICE_NOSAMPLESPLAYED将只获取挂起(包括正在播放)XAudio2Buffer数量,速度较快。注意:DirectX SDK版本没有此参数

);

XAUDIO2_VOICE_STATE包含三个成员:

void * pCurrentBufferContext//对应XAUDIO2_BUFFER中的pContext

UINT32 BuffersQueued//挂起(包括正在播放)XAudio2Buffer数量

UINT64 SamplesPlayed//已播放的样本数

示例: pSourceVoice->GetState(&state);

 

  1. 暂停和停止播放

暂停播放使用IXAudio2SourceVoice的成员函数:Stop

原型:HRESULT Stop(

UINT32 Flags,// 设为0XAUDIO2_PLAY_TAILS,后者代表等待音效放完

UINT32 OperationSet = XAUDIO2_COMMIT_NOW// XAUDIO2_COMMIT_NOW立即生效

);

如果设定XAUDIO2_PLAY_TAILS,应在音效输出完成后设定0,Stop一次。

暂停后再次调用Start将在暂停的位置开始播放。

 

如果要完全停止,还需要使用IXAudio2SourceVoice的成员函数FlushSourceBuffers,该函数功能是清除挂起的XAudio2Buffer队列。

原型:HRESULT FlushSourceBuffers();

说明:该函数使用后要到XAudio2播放完一个XAudio2Buffer才生效,建议在回调中使用。使用该函数后,XAudio2Buffer队列计数将置0

 

  1. IXAudio2SourceVoice的其他功能:设置声调使用SetFrequencyRatio函数

原型:HRESULT SetFrequencyRatio(

float Ratio,//1.0为正常声调,>1.0为高声调快放,<1.0为低声调慢放

UINT32 OperationSet = XAUDIO2_COMMIT_NOW

);

IXAudio2SourceVoice继承自IXAudio2Voice,所以还有许多IXAudio2Voice的功能,比如设置音量用SetVolume等。

 

注意:以上IXAudio2SourceVoice的成员函数中, StopGetState FlushSourceBuffers可以在回调中使用

释放相关实例的顺序与创建他们的顺序相反。需要包含头文件Xaudio2.hObjbase.h以及链接ole32.lib(而不是Microsoft网站上的Xaudio2.lib)


 

下面给出我写的一个Speaker类供大家参考,该类使用XAudio2来播放PCM数据,在Win7x64系统中使用VS2010DirectX SDKx64/clr(Speaker中没有使用托管类)编译测试通过

其公开成员函数ReadData采用阻塞的方法来读取音频数据,一旦塞满缓冲区将自动开始播放,播放多少就读取多少,SetFormat设置PCM音频格式(不设置的话,默认是16位、2声道、44100采样率)SetBufferSize设置缓冲块数和每块大小(不设置的话,默认是4块,每块40KB)

 

#include <Windows.h>

#include <xaudio2.h>

#include "Objbase.h"

#pragma comment(lib,"Ole32.lib")

 

#define DEF_MaxBufferCount4

#define DEF_StreamingBufferSize40960

 

class VoiceCallBack;

class Speaker;

 

 

class VoiceCallBack:public IXAudio2VoiceCallback

{//回调类

public:HANDLE hBufferEndEvent;

   VoiceCallBack():hBufferEndEvent(CreateEvent(NULL,FALSE,FALSE,NULL))

{count =lastcontext = currentcontext= 0; }

   ~VoiceCallBack(){CloseHandle(hBufferEndEvent);}

   Speaker *speaker;

   XAUDIO2_VOICE_STATE state;

public:int count;

   int lastcontext;

   int currentcontext;

   //播放一个数据块前后触发事件

   void OnBufferEnd(void *pBufferContext);

   //不需要的方法只保留声明

   void OnBufferStart(void *pBufferContext){}

   void OnVoiceProcessingPassEnd(){}

   void OnVoiceProcessingPassStart(UINT32 SamplesRequired){}

   void OnStreamEnd(){}

   void OnLoopEnd(void *pBufferContext){}

   void OnVoiceError(void *pBufferContext,HRESULT Error){}

};

 

class Speaker

{public:

//XAudio2数据

IXAudio2* pXAudio2;//XAudio2引擎

IXAudio2MasteringVoice* pMasterVoice;//MsterVoice

VoiceCallBack voiceCallBack;//回调处理

XAUDIO2_BUFFER *pXAudio2Buffer;//封装的用来呈交的音频缓冲数据块

WAVEFORMATEX WaveFormat;//音频格式

IXAudio2SourceVoice *pSourceVoice;//源音处理接口

int MaxBufferCount;//最大缓冲数据块数

int StreamingBufferSize;//缓冲数据块大小

unsigned char **ppAudioData;//音频缓冲数据

bool IsPlaying;//是否在播放

 

HRESULT State;//状态,0为正常,<0为错误,>0为信息或警告

const wchar_t *pState;//状态内容详细描述

//跟外部读取交换数据

int ReadingBufferNumber;//正在读取的Buffer

int WritingBufferNumber;//正在写入的Buffer

int UnReadingBufferCount;//未呈交Buffer数量

int UnProcessedBufferCount;//呈交但未处理的Buffer数量

int WritingPosition;//当前块准备写入位置

 

 

//控制函数部分

public:Speaker();//默认构造函数

public:~Speaker();//析构函数

private:void reset();//重置交换数据参数

private:HRESULT SetState(HRESULT state);//设置简单的状态信息

public:void StopPlaying();//停止播放

public:void Pause();//暂停

public:void StartPlaying();//开始播放

public:void SetFormat(short bits,short channels,int hz);//更改PCM格式

public:void SetBufferSize(int MaxBufferCount,int StreamingBufferSize);//分配音频数据缓冲区

public:static XAUDIO2_BUFFER MakeXAudio2Buffer(const BYTE *pBuffer,int BufferSize);//生成XAudio2缓冲区

public:static WAVEFORMATEX MakePCMSourceFormat(short bits,short channels,int hz);//生成PCM格式信息

public:void ReadData(unsigned char *pBuffer,int length);//pBuffer指向的内存复制length字节的数据到缓冲区,不复制完不返回

public:int ReadDataFrom(unsigned char *pBuffer,int length);//pBuffer指向的内存复制length字节的数据,立即返回实际复制的数据字节数

};

 

void VoiceCallBack::OnBufferEnd(void *pBufferContext)

{

if(pBufferContext == NULL)

{

speaker->pSourceVoice->GetState(&state);

speaker->UnProcessedBufferCount=state.BuffersQueued;

}

else

{

currentcontext = *((int *) pBufferContext)+1;

if(currentcontext > lastcontext)

count += currentcontext - lastcontext;

else  if(currentcontext < lastcontext)

count += speaker->MaxBufferCount + currentcontext - lastcontext;

lastcontext = currentcontext;

speaker->UnProcessedBufferCount -= count;

}

count = 0;

while(speaker->UnReadingBufferCount)//提交

{

speaker->pSourceVoice->SubmitSourceBuffer(&speaker->pXAudio2Buffer[speaker->ReadingBufferNumber],NULL);

speaker->ReadingBufferNumber = (1 + speaker->ReadingBufferNumber) % speaker->MaxBufferCount;

speaker->UnProcessedBufferCount++;

speaker->UnReadingBufferCount --;

}

SetEvent(hBufferEndEvent);

 }

 

Speaker::Speaker()

{

//初始化

IsPlaying = false;

MaxBufferCount = 0;

StreamingBufferSize = 0;

voiceCallBack.speaker = this;

reset();

//建立引擎

if(FAILED(SetState(XAudio2Create( &pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR ))))goto END;

//建立MasteringVoice

if(FAILED(SetState(pXAudio2->CreateMasteringVoice(&pMasterVoice))))goto END;

//分配缓冲内存并设置XAudio2Buffer

SetBufferSize(DEF_MaxBufferCount,DEF_StreamingBufferSize);//设置默认缓冲

//设置pcm格式

WaveFormat = Speaker::MakePCMSourceFormat(16,2,44100);

//建立SourceVoice

SetState(pXAudio2->CreateSourceVoice(&this->pSourceVoice,&this->WaveFormat,0,XAUDIO2_DEFAULT_FREQ_RATIO,&this->voiceCallBack,NULL,NULL));

END:;

};

Speaker::~Speaker()

{

SetBufferSize(0,0);//停止播放并释放缓冲区

if(pSourceVoice != NULL)pSourceVoice->DestroyVoice();

if(pMasterVoice != NULL)pMasterVoice->DestroyVoice();

if(pXAudio2 != NULL)pXAudio2->StopEngine();

}

void Speaker::reset()

{

SetState(0);

ReadingBufferNumber = 0;

UnReadingBufferCount = 0;

UnProcessedBufferCount = 0;

WritingBufferNumber = 0;

WritingPosition = 0;

voiceCallBack.count = 0;

voiceCallBack.lastcontext = 0;

}

void Speaker::StopPlaying()

{

if(pSourceVoice == NULL)return;

XAUDIO2_VOICE_STATE state;

if(IsPlaying)

{

IsPlaying = false;

pSourceVoice->GetState(&state);

while(state.BuffersQueued)

{

if(FAILED(SetState(pSourceVoice->Stop(0,XAUDIO2_COMMIT_NOW))))return;

if(FAILED(SetState(pSourceVoice->FlushSourceBuffers())))return;

Sleep(1);

pSourceVoice->GetState(&state);

}

}

reset();

}

void Speaker::Pause()

{

if(pSourceVoice == NULL)return;

if(IsPlaying)

{

SetState(pSourceVoice->Stop(0,XAUDIO2_COMMIT_NOW));

if(FAILED(State))return;

IsPlaying = false;

}

}

void Speaker::StartPlaying()

{

SetState(pSourceVoice->Start(0,XAUDIO2_COMMIT_NOW));

if(FAILED(State))return;

this->IsPlaying = true;

}

void  Speaker::SetFormat(short bits,short channels,int hz)

{

if(WaveFormat.wBitsPerSample == bits && WaveFormat.nChannels == channels && WaveFormat.nSamplesPerSec == hz)

return;

StopPlaying();

if(pSourceVoice)pSourceVoice->DestroyVoice();

pSourceVoice = NULL;

WaveFormat = MakePCMSourceFormat(bits,channels,hz);

if(FAILED(SetState(pXAudio2->CreateSourceVoice(&pSourceVoice,&WaveFormat,0,XAUDIO2_DEFAULT_FREQ_RATIO,&this->voiceCallBack,NULL,NULL))))pSourceVoice = NULL;

}

void Speaker::SetBufferSize(int MaxBufferCount,int StreamingBufferSize)

{

if (this->MaxBufferCount!=MaxBufferCount || this->StreamingBufferSize!=StreamingBufferSize)

{

this->StopPlaying();

for(int i=0;i<this->MaxBufferCount;++i)

{

if(this->StreamingBufferSize)//删除块

{

delete [] this->ppAudioData[i];

if(this->pXAudio2Buffer[i].pContext)

delete this->pXAudio2Buffer[i].pContext;

}

}

if(this->MaxBufferCount)//删除所有

{

delete [] this->ppAudioData;

delete [] this->pXAudio2Buffer;

}

this->MaxBufferCount=MaxBufferCount;//赋值

this->StreamingBufferSize=StreamingBufferSize;

voiceCallBack.lastcontext = 0;

 

if(MaxBufferCount)//创建

{

reset();

this->ppAudioData = new unsigned char *[MaxBufferCount];

this->pXAudio2Buffer = new XAUDIO2_BUFFER[MaxBufferCount];

if (StreamingBufferSize)

{

this->pXAudio2Buffer = new XAUDIO2_BUFFER[MaxBufferCount];

for(int i=0;i<MaxBufferCount;++i)

{

this->ppAudioData[i] = new unsigned char[StreamingBufferSize];

this->pXAudio2Buffer[i] = Speaker::MakeXAudio2Buffer(ppAudioData[i],StreamingBufferSize);

*((int*)this->pXAudio2Buffer[i].pContext) = i;

}

}

}

}

}

XAUDIO2_BUFFER Speaker::MakeXAudio2Buffer(const BYTE *pBuffer,int BufferSize)

{

XAUDIO2_BUFFER XAudio2Buffer;

XAudio2Buffer.AudioBytes = BufferSize;

XAudio2Buffer.Flags = 0;

XAudio2Buffer.LoopBegin = 0;

XAudio2Buffer.LoopCount = 0;

XAudio2Buffer.LoopLength = 0;

XAudio2Buffer.pAudioData = pBuffer;

XAudio2Buffer.pContext = (void*)new int[1];

XAudio2Buffer.PlayBegin = 0;

XAudio2Buffer.PlayLength = 0;

return XAudio2Buffer;

}

WAVEFORMATEX Speaker::MakePCMSourceFormat(short bits,short channels,int hz)

{

WAVEFORMATEX format;

format.wFormatTag = WAVE_FORMAT_PCM;//PCM格式

format.wBitsPerSample = bits;//位数

format.nChannels = channels;//声道数

format.nSamplesPerSec = hz;//采样率

format.nBlockAlign = bits*channels/8;//数据块调整

format.nAvgBytesPerSec = format.nBlockAlign*hz;//平均传输速率

format.cbSize = 0;//附加信息

return format;

}

void Speaker::ReadData(unsigned char *pBuffer,int length)

{

int i=0;

while(length>i)//不读完不退出循环

{

while(UnProcessedBufferCount + UnReadingBufferCount

 >= MaxBufferCount-1)

{

if(!IsPlaying)

{

StartPlaying();

Sleep(0);

continue;

}

voiceCallBack.OnBufferEnd(NULL);

Sleep(10);

 

}

if(StreamingBufferSize > length-i+WritingPosition)

{

while(i<length)

{

this->ppAudioData[WritingBufferNumber][WritingPosition] = pBuffer[i];

++WritingPosition;

++i;

}

break;

}

while(StreamingBufferSize>WritingPosition)

{

this->ppAudioData[WritingBufferNumber][WritingPosition] = pBuffer[i];

++WritingPosition;

++i;

}

WritingPosition = 0;

WritingBufferNumber = (1+WritingBufferNumber)%MaxBufferCount;

++UnReadingBufferCount;

}

}

int Speaker::ReadDataFrom(unsigned char *pBuffer,int length)//pBuffer指向的内存复制length字节的数据,立即返回实际复制的数据字节数

{

int i=0;

//XAUDIO2_VOICE_STATE state;

//pSourceVoice->GetState(&state);

if(pBuffer==nullptr || !length)

return 0;

while(UnProcessedBufferCount + UnReadingBufferCount < this->MaxBufferCount)

{

if(this->StreamingBufferSize > length-i+WritingPosition)

{

while(i<length)

{

this->ppAudioData[WritingBufferNumber][WritingPosition] = pBuffer[i];

++WritingPosition;

++i;

}

return length;

}

while(WritingPosition<StreamingBufferSize)

{

this->ppAudioData[WritingBufferNumber][WritingPosition] = pBuffer[i];

++WritingPosition;

++i;

}

WritingPosition = 0;

WritingBufferNumber = (1+WritingBufferNumber)%MaxBufferCount;

++UnReadingBufferCount;

++UnProcessedBufferCount;

}

voiceCallBack.OnBufferEnd(NULL);//督促XAudio2快点放完

return i;

}

HRESULT Speaker::SetState(HRESULT state)

{

this->State = state;

switch(state)

{

case 0:pState = L"正常.";break;

case XAUDIO2_E_INVALID_CALL:pState = L"函数无效调用";break;

case XAUDIO2_E_XMA_DECODER_ERROR:pState = L"XMA设备损坏";break;

case XAUDIO2_E_XAPO_CREATION_FAILED:pState = L"XAPO效果初始化失败";break;

case XAUDIO2_E_DEVICE_INVALIDATED:pState = L"音频设备不可用";break;

default:if(state>0)pState = L"未知信息";

else pState = L"未知错误";

}

return state;

}

声明:不知道此文章原始出处是哪里,所以没有注明转载地址。网上可以搜到很多这篇文章相关的文档或者引用,如果原作者需要我注明地址的话,请联系我。

0 0
原创粉丝点击