Windowsストアアプリで音を鳴らす場合、MediaElementが一般的ですが、画面の遷移がある場合などには少々向きません。
鳴らしたままなどであればやり方もあるんですが、デメリットとして、ボリューム変更などができなくなります。
SharpDXを使用すればC#でもXAudio2を使用可能ですが、今回のはC++/CXで作成したものです。
マイクロソフトのサンプルなどを参考にとりあえずのレベルですが、作成しました。
C#からもWindowsランタイムコンポーネントとして引き込むことで使用できます。
※各種サンプルを公開されている方々とマイクロソフトに感謝を
読み込んでいるヘッダーです
// Microsoft WRL(Windows ランタイム C++ テンプレート ライブラリ) 用ヘッダー
#include <wrl.h>
// XAudio2用ヘッダー
#include <xaudio2.h>
#include <xaudio2fx.h>
// マルチメディア用ヘッダー
#include <mmreg.h>
// media fusion 用ヘッダー
#include <Mfidl.h>
#include <Mfapi.h>
#include <Mfreadwrite.h>
/* 上記に必要なライブラリ
xaudio2.lib
mfcore.lib
mfplat.lib
mfreadwrite.lib
mfuuid.lib
*/
#include "pch.h"
#include "xaudiolib.h"
using namespace Microsoft::WRL;
using namespace Platform;
namespace xaudio2lib
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// サウンドデータ
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BGM管理構造体
struct xaudiolib::ImplSoundData
{
ImplSoundData::ImplSoundData(Platform::String^ filename,xaudiolib^ parent,int index) :
sourceVoice(nullptr),
//playData(data),
isPlaying(false),
isLoop(false),
audioFilePlayerCallBack(parent->m_musicEngine, filename, index)
{
sourceVoice = audioFilePlayerCallBack.GetSourceVoice();
}
~ImplSoundData()
{
if (sourceVoice)
{
sourceVoice->DestroyVoice();
sourceVoice = nullptr;
}
}
IXAudio2SourceVoice* sourceVoice;
bool isPlaying;
bool isLoop;
AudioFilePlayerCallBack audioFilePlayerCallBack;
};
struct xaudiolib::ImplSeData
{
ImplSeData::ImplSeData(Platform::String^ filename,xaudiolib^ parent,int index) :
sourceVoice(nullptr),
//playData(data),
isPlaying(false),
isLoop(false),
audioFilePlayerCallBack(parent->m_seEngine, filename, index)
{
sourceVoice = audioFilePlayerCallBack.GetSourceVoice();
}
~ImplSeData()
{
if (sourceVoice)
{
sourceVoice->DestroyVoice();
sourceVoice = nullptr;
}
}
IXAudio2SourceVoice* sourceVoice;
bool isPlaying;
bool isLoop;
AudioFilePlayerCallBack audioFilePlayerCallBack;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
xaudiolib::xaudiolib(void):m_soudBgmDataList(),m_soudSeDataList()
{
XAudio2Initialize();
}
xaudiolib::~xaudiolib(void)
{
seList.clear();
bgmList.clear();
XAudio2Clear();
}
void xaudiolib::AddBgmFileName(Platform::String^ _fileName)
{
bgmList.push_back(_fileName);
}
void xaudiolib::AddSeFileName(Platform::String^ _fileName)
{
seList.push_back(_fileName);
}
void xaudiolib::Initialize()
{
}
void xaudiolib::XAudio2Initialize()
{
m_musicMasteringVoice = nullptr;
m_audioAvailable = false;
UINT32 flags = 0;
// BGM用XAudio2ポインタ作成
DX::ThrowIfFailed(
XAudio2Create(&m_musicEngine, flags)
);
// BGM用ミキサーの作成
HRESULT hr = m_musicEngine->CreateMasteringVoice(&m_musicMasteringVoice);
if (FAILED(hr))
{
m_audioAvailable = false;
return;
}
// SE用XAudio2ポインタ作成
DX::ThrowIfFailed(
XAudio2Create(&m_seEngine, flags)
);
// SE用ミキサーの作成
hr = m_seEngine->CreateMasteringVoice(&m_seMasteringVoice);
if (FAILED(hr))
{
m_audioAvailable = false;
return;
}
m_audioAvailable = true;
}
void xaudiolib::XAudio2Clear()
{
if(m_audioAvailable)
{
for (size_t index = 0; index < bgmList.size(); ++index)
{
m_soudBgmDataList[index]->audioFilePlayerCallBack.AudioFilePlayerEnd(index);
}
for (size_t index = 0; index < seList.size(); ++index)
{
m_soudSeDataList[index]->audioFilePlayerCallBack.AudioFilePlayerEnd(index);
}
// マスターVoiceの解放
m_musicMasteringVoice->DestroyVoice();
m_musicMasteringVoice = nullptr;
m_seMasteringVoice->DestroyVoice();
m_seMasteringVoice = nullptr;
// m_musicEngine/m_seEngineは「Microsoft::WRL::ComPtr」で自動的に開放
m_audioAvailable = false;
}
}
// サウンドデータを読み込む
void xaudiolib::XAudio2Load()
{
for (size_t index = 0; index < bgmList.size(); ++index)
{
AddBgmSound(bgmList[index], index);
}
for (size_t index = 0; index < seList.size(); ++index)
{
AddSeSound(seList[index], index);
}
}
// BGMデータを登録する
size_t xaudiolib::AddBgmSound(_In_ Platform::String^ soundFileName, _In_ int index)
{
std::shared_ptr<ImplSoundData> implSoundData(new ImplSoundData(soundFileName,this,index));
m_soudBgmDataList.push_back(implSoundData);
return (m_soudBgmDataList.size() - 1);
}
// SEデータを登録する
size_t xaudiolib::AddSeSound(_In_ Platform::String^ seFileName, _In_ int index)
{
std::shared_ptr<ImplSeData> implSeData(new ImplSeData(seFileName,this,index));
m_soudSeDataList.push_back(implSeData);
return (m_soudSeDataList.size() - 1);
}
bool xaudiolib::IsBgmPlay(int bgmIndex, bool bLoop)
{
if(!m_audioAvailable)
{
return false;
}
m_soudBgmDataList[bgmIndex]->audioFilePlayerCallBack.IsPlay(bLoop, bgmIndex);
return true;
}
bool xaudiolib::IsBgmStop(int bgmIndex)
{
if(!m_audioAvailable)
{
return false;
}
m_soudBgmDataList[bgmIndex]->audioFilePlayerCallBack.IsStop();
return true;
}
bool xaudiolib::IsSePlay(int seIndex)
{
if(!m_audioAvailable)
{
return false;
}
m_soudSeDataList[seIndex]->audioFilePlayerCallBack.IsPlay(false, seIndex);
return true;
}
bool xaudiolib::IsSeStop(int seIndex)
{
if(!m_audioAvailable)
{
return false;
}
m_soudSeDataList[seIndex]->audioFilePlayerCallBack.IsStop();
return true;
}
bool xaudiolib::IsBgmPlaying(int bgmIndex)
{
if(!m_audioAvailable)
{
return false;
}
return m_soudBgmDataList[bgmIndex]->audioFilePlayerCallBack.bPlaying;
}
bool xaudiolib::IsSePlaying(int bgmIndex)
{
if(!m_audioAvailable)
{
return false;
}
return m_soudSeDataList[bgmIndex]->audioFilePlayerCallBack.bPlaying;
}
bool xaudiolib::SuspendBgm()
{
if(!m_audioAvailable)
{
return false;
}
this->m_musicEngine->StopEngine();
return true;
}
bool xaudiolib::ResumeBgm()
{
if(!m_audioAvailable)
{
return false;
}
this->m_musicEngine->StartEngine();
return true;
}
bool xaudiolib::SetBgmVolume(int bgmIndex, float volume)
{
if(!m_audioAvailable || m_soudBgmDataList[bgmIndex] == nullptr)
{
return false;
}
m_soudBgmDataList[bgmIndex]->sourceVoice->SetVolume(volume);
return true;
}
bool xaudiolib::GetBgmVolume(int bgmIndex, float *volume)
{
if(!m_audioAvailable || m_soudBgmDataList[bgmIndex] == nullptr)
{
return false;
}
m_soudBgmDataList[bgmIndex]->sourceVoice->GetVolume(volume);
return true;
}
bool xaudiolib::SetSeVolume(int seIndex, float volume)
{
if(!m_audioAvailable)
{
return false;
}
}
bool xaudiolib::GetSeVolume(int seIndex, float* volume)
{
if(!m_audioAvailable)
{
return false;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
AudioFilePlayerCallBack::AudioFilePlayerCallBack(Microsoft::WRL::ComPtr<IXAudio2> pXAudio2, Platform::String^ filename ,int soundIndex)
{
this->mfSourceReader = this->mediaReader.LoadMedia(filename);
this->iSoundIndex = soundIndex;
// Get the Media Foundation media type
ComPtr<IMFMediaType> mfMediaType;
HRESULT hresult = mfSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, &mfMediaType);
if (FAILED(hresult))
throw ref new COMException(hresult, "MFSourceReader->GetCurrentMediaType 取得に失敗");
// Create a WAVEFORMATEX from the media type
WAVEFORMATEX* pWaveFormat;
unsigned int waveFormatLength;
hresult = MFCreateWaveFormatExFromMFMediaType(mfMediaType.Get(), &pWaveFormat, &waveFormatLength);
if (FAILED(hresult))
throw ref new COMException(hresult, "MFCreateWaveFormatExFromMFMediaType 取得に失敗");
// SoourceVoiceを作成
hresult = pXAudio2->CreateSourceVoice(
&pSourceVoice,
pWaveFormat,
XAUDIO2_VOICE_NOPITCH,
1.0f,
this);
// WaveFomatを開放
CoTaskMemFree(pWaveFormat);
bPlaying = false;
}
void AudioFilePlayerCallBack::AudioFilePlayerEnd(int bgmIndex)
{
if(pSourceVoice!=nullptr)
{
this->IsStop();
pSourceVoice->DestroyVoice();
HRESULT hresult = MFShutdown();
if (FAILED(hresult))
throw ref new COMException(hresult, "MFShutdown failure");
}
}
void AudioFilePlayerCallBack::SubmitBuffer()
{
// Get the next block of audio data
int audioBufferLength;
byte * pAudioBuffer = GetNextBlock(&audioBufferLength);
if (pAudioBuffer != nullptr)
{
// Create an XAUDIO2_BUFFER for submitting audio data
XAUDIO2_BUFFER buffer = {0};
buffer.Flags = XAUDIO2_END_OF_STREAM;
buffer.AudioBytes = audioBufferLength;
buffer.pAudioData = pAudioBuffer;
buffer.pContext = pAudioBuffer;
HRESULT hresult = pSourceVoice->SubmitSourceBuffer(&buffer);
if (FAILED(hresult))
throw ref new COMException(hresult, "IXAudio2SourceVoice->SubmitSourceBuffer failure");
}
}
void AudioFilePlayerCallBack::IsPlay(bool bLoop,int iSoundIndex)
{
this->bLoop = bLoop;
this->soundIndex = iSoundIndex;
// 停止処理
this->IsStop();
// 再生バッファ
SubmitBuffer();
SubmitBuffer();
endOfFile = false;
// 再生開始
pSourceVoice->Start(0);
bPlaying = true;
}
void AudioFilePlayerCallBack::IsStop()
{
bPlaying = false;
pSourceVoice->Stop(0);
pSourceVoice->FlushSourceBuffers();
// 再生ポインタを先頭に
this->ReStart();
}
void AudioFilePlayerCallBack::ReStart()
{
if (mfSourceReader == nullptr)
{
return;
}
PROPVARIANT var = {0};
var.vt = VT_I8;
DX::ThrowIfFailed(
mfSourceReader->SetCurrentPosition(GUID_NULL, var)
);
}
IXAudio2SourceVoice* AudioFilePlayerCallBack::GetSourceVoice()
{
return this->pSourceVoice;
}
byte * AudioFilePlayerCallBack::GetNextBlock(int * pAudioBufferLength)
{
// IMFSampleを取得
ComPtr<IMFSample> mfSample;
DWORD flags = 0;
HRESULT hresult = mfSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM,
0, nullptr, &flags, nullptr, &mfSample);
if (FAILED(hresult))
throw ref new COMException(hresult, "MFSourceReader->ReadSample failure");
// ファイルの終わりを確認する
if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
{
endOfFile = true;
*pAudioBufferLength = 0;
return nullptr;
}
// データをバッファへ
ComPtr<IMFMediaBuffer> mfMediaBuffer;
hresult = mfSample->ConvertToContiguousBuffer(&mfMediaBuffer);
if (FAILED(hresult))
throw ref new COMException(hresult, "IMFSample->ConvertToContiguousBuffer failure");
// バッファが勝手に開放されないようにロックしておく
uint8 * pAudioData = nullptr;
DWORD audioDataLength = 0;
hresult = mfMediaBuffer->Lock(&pAudioData, nullptr, &audioDataLength);
if (FAILED(hresult))
throw ref new COMException(hresult, "IMFMediaBuffer->Lock failure");
byte * pAudioBuffer = new byte[audioDataLength];
CopyMemory(pAudioBuffer, pAudioData, audioDataLength);
hresult = mfMediaBuffer->Unlock();
if (FAILED(hresult))
throw ref new COMException(hresult, "IMFMediaBuffer->Unlock failure");
*pAudioBufferLength = audioDataLength;
return pAudioBuffer;
}
// IXAudio2VoiceCallback バッファの終わりに呼び出される
void _stdcall AudioFilePlayerCallBack::OnBufferEnd(void* pContext)
{
// Remember to free the audio buffer!
delete[] pContext;
// Either submit a new buffer or clean up
if(bPlaying)
{
if (!endOfFile)
{
SubmitBuffer();
}
else
{
if(!bLoop)
{
this->IsStop();
}
else
{
this->ReStart();
SubmitBuffer();
SubmitBuffer();
endOfFile = false;
}
}
}
}
};
まだ作り掛&未整理のため読みにくいですがご了承ください。
ファイルの読み込みにはマイクロソフトのサンプルに有った「MediaReader」に少々手を入れて使用しています。
「MediaReader」は転載禁止のため掲載できないため、MSのサンプルをご確認ください。