XAudio2 (一)

最近在写游戏菜单,想做一个可以手动设置游戏场景音乐,音效,及音量的菜单,看许多书上用的都是DXMusic,DirectSound,可自从DirectX 9.0c之后,DXMusic和DirectSound就不再存在了,反而被XAudio给取代了。在网上查找资料,书上查找相关信息,也是甚至更少,迫不得已只能去官网和官方给的样例慢慢琢磨。

XAudio2官方介绍:

https://msdn.microsoft.com/zh-cn/library/windows/desktop/ee415737

或者看看这位大哥的博客作为参考也行:
XAudio2 (一)_第1张图片

XAudio2系统中的声音

在XAudio2系统中共有三种声音,分别是:source voice(源声音),master voice(主声音),
sbumix(混合音)。

Source voice(源声音):也就是从磁盘文件中所采集到的音频数据——最初始的状态(在这个系统中)

Submix voice(混合声音):也就是将多个源声音进行混合——类似于游戏场景中背景音乐和音效的混合

Master voice(主声音):这是重中之重,将音频数据最后写到咱们的音频输出设备上——播放声音

想要播放声音大概分为以下几步:
1、初始化XAudio2。
2、进行音频文件的查找。
3、将波形文件的数据读入到内存中
4、创建源声音
5、将缓冲区内的音频提交给源声音
6、最终播放声音

初始化XAudio2:

1、先使用XAudio2Create函数创建一个XAudio2的对象

函数原型:

HRESULT XAudio2Create(
         IXAudio2 **ppXAudio2,      //需要实例化的对象
         //指该实例化对象的行为,一般设置为0或者XAUDIO2_DEBUG_ENGINE宏
         UINT32 Flags = 0,          
         //让CPU分配一个线程给该音频系统,不做特别要求可设置为XAUDIO2_DEFAULT_PROCESSOR
         XAUDIO2_PROCESSOR XAudio2Processor = XAUDIO2_DEFAULT_PROCESSOR
)
IXAudio2 *pXAudio2 = NULL;

    HRESULT hr;

    //创建示列
    if (FAILED(hr=XAudio2Create(&pXAudio2,0,XAUDIO2_DEFAULT_PROCESSOR)))
    {
        ::MessageBox(NULL, L"XAudio2 Create Failed", L"Error", NULL);
        return hr;
    }

2、创建主声音,很关键,能不能播放声音就看它是否存在。
利用CreateMasteringVoice函数来创建主声音
函数原型:

HRESULT CreateMasteringVoice(
         IXAudio2MasteringVoice **ppMasteringVoice, //实例化对象
         UINT32 InputChannels = XAUDIO2_DEFAULT_CHANNELS,// //输入音频中声音期望的声道
         UINT32 InputSampleRate = XAUDIO2_DEFAULT_SAMPLERATE,//音频的样本率
         UINT32 Flags = 0,     //指定声音行为,默认为0
         UINT32 DeviceIndex = 0,   //音频设备索引,默认音频设备=0
         const XAUDIO2_EFFECT_CHAIN *pEffectChain = NULL //音频的效果链
)
IXAudio2MasteringVoice *pMasterVoice = NULL;
//后面的参数由于声明的时候已经初始化,不做特别要求,可以不用填,默认
    if (FAILED(hr=pXAudio2->CreateMasteringVoice(&pMasterVoice)))
    {
        ::MessageBox(NULL, L"The Mastering voice is created failed", L"Error", NULL);
        return false;
    }

查找音频文件:

看官方例子:
定义一个辅助查找文件函数Find MediaFile(WCHAR* strDestPath, int cchDest,LPCWSTR strFilename))
各个参数分别表示文件路径,最大的路径存放容量,音频文件名

读音频文件:

这时就需要官方给的DX Wave型类了。
类的头文件定义如下:
class WaveFile
{
public:
    WAVEFORMATEX*   m_pwfx;                     //pointer to WAVEFORMATEX structure 波形文件格式的结构指针
    HMMIO           m_hmmio;                    //a I/O handle for the WAVE 波形文件操作的句柄
    MMCKINFO        m_ck;                       //Multimedia RIFF chunk 多媒体资源文件交换格式块
    MMCKINFO        m_ckRiff;                   //use in opening a wave file 用于使用打开一个波形文件
    DWORD           m_dwSize;                   //size of the wave file 波形文件的大小,用于存放在内存中
    MMIOINFO        m_mmioinfoOut;              //
    DWORD           m_dwFlags;
    BOOL            m_bIsReadingFromMemory;
    BYTE*           m_pdData;
    BYTE*           m_pdDataCur;
    ULONG           m_ulDataSize;
    CHAR*           m_pResourceBuffer;

protected:
    HRESULT ReadMMIO();
    HRESULT WriteMMIO(WAVEFORMATEX* pwfxDest);

public:
    WaveFile();
    ~WaveFile();

    HRESULT Open(LPWSTR strFileName, WAVEFORMATEX *pwfx, DWORD dwFlags);
    HRESULT OpenFromMemory(BYTE* pbData, ULONG ulDataSize, WAVEFORMATEX *pwfx, DWORD dwFlags);
    HRESULT Close();

    HRESULT Read(BYTE *pBuffer, DWORD dwSizeToRead, DWORD *pdwSizeRead);
    HRESULT Write(UINT nSizeToWrite, BYTE *pbData, UINT* pnSizeWrote);

    DWORD GetSize();
    HRESULT ResetFile();
    WAVEFORMATEX *GetFormat()
    {
        return m_pwfx;
    }
};

如何进行读操作?

先将文件进行打开,分析信息,上一步是查找文件是否存在

    WaveFile wav;
    if (FAILED(hr = wav.Open(L"XXX.wav", NULL, WAVEFILE_READ)))
    {
        return hr;
    }

再获取波形文件的相关信息:

    //获取格式
    WAVEFORMATEX *pwfx = wav.GetFormat();

    //计算这个文件共占多少字节
    DWORD cbWaveSize = wav.GetSize();

    //用于存放文件的数据
    BYTE *pbWaveData = new BYTE[cbWaveSize];

最后再进行读文件操作:

if (FAILED(hr = wav.Read(pbWaveData, cbWaveSize, &cbWaveSize)))
    {
        SAFE_DELETE_ARRAY(pbWaveData);
        return hr;
    }

创建源声音:

通过利用CreateSourceVoice()函数来进行创建
函数原型:
HRESULT CreateSourceVoice(
         IXAudio2SourceVoice **ppSourceVoice,  //实例对象
         const WAVEFORMATEX *pSourceFormat,   //音频文件格式8,16,20,32位
         UINT32 Flags = 0,                   //源声音的行为
         //音频的最大频率允许
         float MaxFrequencyRatio = XAUDIO2_DEFAULT_FREQ_RATIO,
         //向客户提供的回调指针
         IXAudio2VoiceCallback *pCallback = NULL,
         //描述目的地声音为源声音的指针
         const XAUDIO2_VOICE_SENDS *pSendList = NULL,
         const XAUDIO2_EFFECT_CHAIN *pEffectChain = NULL  //效果链
)

一般这样创建:

IXAudio2SourceVoice *pSourceVoice;
    if (FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, pwfx)))
    {
        ::MessageBox(NULL, L"The Source Voice is Created failed", L"Error", MB_OK);
        return FALSE;
    }

将缓冲区内的音频提交给源声音:

//首先定义音频缓冲区
XAUDIO2_BUFFER buffer = { 0 };
//设置音频缓冲区的内容,至少要设置这个三个,不然就播放不了声音,有一次就是忽略到这点导致一直播放不了
    buffer.pAudioData = pbWaveData;
    buffer.Flags = XAUDIO2_END_OF_STREAM;       //不要这个缓冲区之后的任何数据
    buffer.AudioBytes = cbWaveSize;

完整的XAUDIO2_BUFFER:

typedef struct XAUDIO2_BUFFER
{
    UINT32 Flags;                       // 行为方式,要还说不要缓冲区后面的数据
    UINT32 AudioBytes;                  // 音频数据的大小
    const BYTE* pAudioData;             // 音频数据指针
    UINT32 PlayBegin;                   // 缓冲区应该播放第一个对象
    UINT32 PlayLength;                  // 播放区域的长度,为0就默认为整个缓冲区

    UINT32 LoopBegin;                   // 循环播放该音频
    UINT32 LoopLength;                  // 循环区域的长度,为0就默认整个缓冲区

    UINT32 LoopCount;                   //  循环播放的次数,如果为0,那么上面连个参数LoopBegin和LoopLength也要为0,这个设定才成功
    void* pContext;                     //传回客户端的值
} XAUDIO2_BUFFER;

接下来利用SubmitSourceBuffer()函数来提交缓冲区的内容

函数原型:

HRESULT SubmitSourceBuffer(
         const XAUDIO2_BUFFER *pBuffer, //该指针指向一个XAUDIO2_BUFFER的队列
         const XAUDIO2_BUFFER_WMA *pBufferWMA = NULL //提交额外缓冲的指针
)

用法:

if (FAILED(hr = pSourceVoice->SubmitSourceBuffer(&buffer)))
    {
        ::MessageBox(NULL, L"Submit an XAudio2_Buffer Failed", L"Error", MB_OK);
        return FALSE;
    }

最后激动的时刻,就是播放声音了

利用源声音的Start()函数,将声音传Master voice(主声音),主声音将音频数据传给我们的音频设备,方能播放声音了。

函数原型:

HRESULT Start(
         UINT32 Flags,           //控制声音是否开始,0代表开始
         UINT32 OperationSet = XAUDIO2_COMMIT_NOW //官方称为part of a deferred batch(延期批的部分)
)
        if (FAILED(hr = pSourceVoice->Start(0)))
        {
            return FALSE;
        }

最终就可以听见声音了。

源码下载:

http://download.csdn.net/download/qq_33248019/10030436

你可能感兴趣的:(开启Directx9.0之旅)