トップ回答者
XAudio2でのxWMA形式コールバック再生時のXAUDIO2_BUFFER_WMAの設定値

質問
-
お世話になります。
XAudio2を使用し、xWMA形式ファイルの再生プログラムを作成しています。
SubmitSourceBuffer()をする際に、xWMA形式の場合はXAUDIO2_BUFFER_WMA構造体に
値を設定して渡す必要があると思いますが、コールバック時にどのような値を渡せばよいか
分からないです。
コールバックを設定しないで再生する場合は、下記(※1)を設定していますが、
コールバック設定時に同じ値を設定するとエラー(※2)が発生してしまいます。XAUDIO2_BUFFER_WMA構造体にどのような値を設定すればよいか
ご存知の方、または参考サイトがある場合はご教示頂きたいです。よろしくお願いします。
■コールバックではない場合のXAUDIO2_BUFFER_WMA設定値(※1)
XAUDIO2_BUFFER_WMA xbw = { 0 };
mmckinfo.ckid = mmioFOURCC('d', 'p', 'd', 's');
mmioDescend(hmmio, &mmckinfo, &riffchunk, MMIO_FINDCHUNK);
xbw.nPacketCount = mmckinfo.cksize / sizeof(UINT32);
xbw.aDecodedPacketCumulativeBytes = new UINT32[nPacketCount];■発生するエラー(※2)
XAUDIO2: ERROR: AudioBytes not aligned to the WMA format's block size
XAUDIO2: ERROR: AudioBytes must equal number WMA packets submitted multiplied by the WMA format's block size
XAUDIO2: ERROR: Invalid WMA source buffer■IXAudio2SourceVoice::SubmitSourceBuffer
https://msdn.microsoft.com/ja-jp/library/bb694569(v=vs.85).aspx■ソースボイスのコールバック再生
https://msdn.microsoft.com/ja-jp/library/bb669171(v=vs.85).aspx■開発環境
Visual Studio 2017 pro
DirectX(June 2010)
WindowsSDK 10.0.16299.0
XAudio2 v2.7- 移動 立花楓Microsoft employee, Moderator 2018年2月21日 0:15 Windows クライアント開発 > Windows クライアント開発
回答
-
完全解決とは言えませんが、再生まではこぎつけました。
コールバック処理は本件では関係なかったため、ストリーミングという言い方に変更させて頂きます。
ストリーミング再生をするときには、大きいサイズのデータを分割して、順番にSubmitSourceBuffer()しますが、その際にXAUDIO2_BUFFER_WMAのPacketCountも分割して設定する必要があります。
また、XAUDIO2_BUFFERのAudioBytesも、PacketCountの分割数とWAVEFORMATEXのnBlockAlignに応じて設定する必要があります。
XAUDIO2_BUFFER_WMAのpDecodedPacketCumulativeBytesは、dpdsチャンクから読み込んだ値を設定します。下記にソースを記載しました(※1)
今回はパケットの送信数を48に設定しましたが、根拠があるわけではなく、再生が安定した数字が48だったので設定してあります(数字が小さいと音が途切れたり、加速したりしました)再生するファイルによっては違うかもしれません。おそらく、パケットの送信頻度によっても変わると思います。
PacketCountは346でした。
パケットを送信する毎に、PacketCountから送信したパケット数を減算していき、最終パケットを送る際に、パケットが送信数48に満たない場合は、あまりのパケット数を送ります(今回は10)参考サイト(※2)の以下の文の意味がよく分からないため、ソースに反映できていないです。そのため、不具合がでる可能性があります。
>さらに、一度に数パケットずつ xWMA ファイルをストリーミングする場合、
>アプリケーションは現在送信済みのパケットのすべてのエントリから、
>前のパケットの pDecodedPacketCumulativeBytes[PacketCount-1] を減算する必要があります。処理の流れとしてはソース(※1)の「初回バッファ送信」でデータを送った後に、IXAudio2SourceVoice::GetStateで取得したBuffersQueuedでキューの数を監視し、BUFF_MAX以下の場合は、「次回バッファ送信」を呼び出す感じになります。
■ソース抜粋(※1)
#define BUFF_MAX 4 const UINT32 SEND_PACKET_NUM = 48;//パケット送信数 UINT32 sendPacketCnt = 0; ○初回バッファ送信 buf = NULL; cnt = 0; buflen = SEND_PACKET_NUM * pwfx.Format.nBlockAlign; buf = new unsigned char[buflen * BUFF_MAX]; ptr = buf; cnt = (cnt + 1) % (BUFF_MAX+1); readlen = mmioRead(hmmio, (HPSTR)ptr, buflen); if (readlen <= 0) { return FALSE; } memset(&bufinfo, 0x00, sizeof(bufinfo)); bufinfo.Flags = ((UINT32)readlen >= buflen) ? 0 : XAUDIO2_END_OF_STREAM; bufinfo.AudioBytes = readlen; bufinfo.pAudioData = ptr; bufinfo.PlayBegin = 0; XAUDIO2_BUFFER_WMA xbw = { 0 }; xbw.PacketCount = SEND_PACKET_NUM; sendPacketCnt += SEND_PACKET_NUM; xbw.pDecodedPacketCumulativeBytes = aDecodedPacketCumulativeBytes; hr = pSourceVoice->SubmitSourceBuffer(&bufinfo, &xbw); ○次回バッファ送信 ptr = buf + buflen * cnt; cnt = (cnt + 1) % (BUFF_MAX+1); readlen = mmioRead(hmmio, (HPSTR)ptr, buflen); if (readlen <= 0) return nullptr; if ((UINT32)readlen >= buflen) { bufinfo.Flags = 0; } else { bufinfo.Flags = XAUDIO2_END_OF_STREAM; } bufinfo.AudioBytes = readlen; bufinfo.pAudioData = ptr; XAUDIO2_BUFFER_WMA xbw = { 0 }; if (sendPacketCnt == nPacketCount) { sendPacketCnt = 0; } if ((sendPacketCnt + SEND_PACKET_NUM) > nPacketCount) { xbw.PacketCount += (nPacketCount - sendPacketCnt); sendPacketCnt += (nPacketCount - sendPacketCnt); } else { xbw.PacketCount = SEND_PACKET_NUM; sendPacketCnt += SEND_PACKET_NUM; } xbw.pDecodedPacketCumulativeBytes = aDecodedPacketCumulativeBytes; hr = pSourceVoice->SubmitSourceBuffer(&buffer, &xbw);
■使用したxWMAの情報
wFormatTag = 65534
nChannels = 2
nSamplesPerSec = 48000
nAvgBytesPerSec = 6000
nBlockAlign = 1008
wBitsPerSample = 16
cbSize = 22
Samples = {wValidBitsPerSample=16 wSamplesPerBlock=16 wReserved=16 }
dwChannelMask = 3
datachunk.cksize = 348768
■参考サイト(※2)
https://msdn.microsoft.com/ja-jp/library/cc308076%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
すべての返信
-
質問をするときは実行環境(OSバージョンなど)やプロジェクト種別に関してもきちんと書くようにしてください。
ちなみに、レガシーなWindows Multimedia APIを使っていることから、UWPではなくデスクトップアプリ開発だと思われますが、Windows SDK 10にはXAudio2 v2.9用のヘッダーが含まれており、またWindows 10にはXAudio2 v2.9が標準インストールされています。このXAudio2 v2.9はxWMAに対応しています。もしWindows 10をターゲットにする(Windows SDK 10を使う)場合は、旧DirectX SDKのXAudio2 v2.7を利用する必要はありません。
IXAudio2SourceVoice::SubmitSourceBuffer()の第1引数にXAUDIO2_BUFFERを渡す際、XAUDIO2_BUFFER::AudioBytesにどのような値を設定していますか?
また、IXAudio2VoiceCallbackを使ったイベント駆動方式のコールバック再生では、通例OnBufferEnd()内で次のバッファのためのSubmitSourceBuffer()を呼び出しますが、OnBufferEnd()は別スレッドで実行されます。その点を理解・考慮して、きちんとスレッドセーフになるよう実装していますか?
なお、MicrosoftのChuck Walbourn氏によるライブラリDirectXTK (DirectX Tool Kit) には、Audio配下にXAudio2を使った上位ライブラリも実装されています。DXTKはDirectXを利用したアプリ開発全般に関する参考にもなります。
- 編集済み sygh 2018年2月24日 17:54
-
sygh様、ご返信ありがとうございます。
すみません。実行環境はWindows 10 pro(1709 16299.192)、プロジェクト種別は
ご推察の通りデスクトップアプリ開発となります。
当方事情でWindows7~10での動作が必要なためXAudio2 v2.7で開発しています。
>IXAudio2SourceVoice::SubmitSourceBuffer()の第1引数にXAUDIO2_BUFFERを渡す際、XAUDIO2_BUFFER::AudioBytesにどのような値を設定していますか?
XAUDIO2_BUFFER::AudioBytesには下記(※1)のようにmmioReadで一定サイズの
データを読み込み設定しています。
>きちんとスレッドセーフになるよう実装していますか?
すみません。この辺りよく分かっていないのですが、参考サイト様(※2)のような感じで、
イベントを発行して、WaitForSingleObjectで待ってデータを読み込む感じにしています。
現状は、WaitForSingleObjectで待つ部分を、音再生前に_beginthreadexで作成したスレッドで行う感じに
なっているのですが、まずい実装なのかもよく分かってないです。
返答がずれていたらすみません。
>DXTKはDirectXを利用したアプリ開発全般に関する参考にもなります。
DirectXTKの情報ありがとうございます。
XAudio2実装部を確認してみます。
■コード抜粋(初回のデータを送る部分)(※1)#define BUFF_MAX 4 chunk.ckid = mmioFOURCC('d', 'a', 't', 'a'); mret = mmioDescend(hmmio, &chunk, &riff_chunk, MMIO_FINDCHUNK); buf = NULL; cnt = 0; buflen = pwfx.Format.nAvgBytesPerSec / sizeof(UINT32); buf = new unsigned char[buflen * (BUFF_MAX+1)]; ptr = buf; cnt = (cnt + 1) % (BUFF_MAX+1); readlen = mmioRead(hmmio, (HPSTR)ptr, buflen); if (readlen <= 0) { return FALSE; } memset(&bufinfo, 0x00, sizeof(bufinfo)); bufinfo.Flags = ((UINT32)readlen >= buflen) ? 0 : XAUDIO2_END_OF_STREAM; bufinfo.AudioBytes = readlen; bufinfo.pAudioData = ptr; bufinfo.PlayBegin = 0;
■参考サイト様(※2)
https://qiita.com/tobira-code/items/39936c4e2b1168fb79ce
-
完全解決とは言えませんが、再生まではこぎつけました。
コールバック処理は本件では関係なかったため、ストリーミングという言い方に変更させて頂きます。
ストリーミング再生をするときには、大きいサイズのデータを分割して、順番にSubmitSourceBuffer()しますが、その際にXAUDIO2_BUFFER_WMAのPacketCountも分割して設定する必要があります。
また、XAUDIO2_BUFFERのAudioBytesも、PacketCountの分割数とWAVEFORMATEXのnBlockAlignに応じて設定する必要があります。
XAUDIO2_BUFFER_WMAのpDecodedPacketCumulativeBytesは、dpdsチャンクから読み込んだ値を設定します。下記にソースを記載しました(※1)
今回はパケットの送信数を48に設定しましたが、根拠があるわけではなく、再生が安定した数字が48だったので設定してあります(数字が小さいと音が途切れたり、加速したりしました)再生するファイルによっては違うかもしれません。おそらく、パケットの送信頻度によっても変わると思います。
PacketCountは346でした。
パケットを送信する毎に、PacketCountから送信したパケット数を減算していき、最終パケットを送る際に、パケットが送信数48に満たない場合は、あまりのパケット数を送ります(今回は10)参考サイト(※2)の以下の文の意味がよく分からないため、ソースに反映できていないです。そのため、不具合がでる可能性があります。
>さらに、一度に数パケットずつ xWMA ファイルをストリーミングする場合、
>アプリケーションは現在送信済みのパケットのすべてのエントリから、
>前のパケットの pDecodedPacketCumulativeBytes[PacketCount-1] を減算する必要があります。処理の流れとしてはソース(※1)の「初回バッファ送信」でデータを送った後に、IXAudio2SourceVoice::GetStateで取得したBuffersQueuedでキューの数を監視し、BUFF_MAX以下の場合は、「次回バッファ送信」を呼び出す感じになります。
■ソース抜粋(※1)
#define BUFF_MAX 4 const UINT32 SEND_PACKET_NUM = 48;//パケット送信数 UINT32 sendPacketCnt = 0; ○初回バッファ送信 buf = NULL; cnt = 0; buflen = SEND_PACKET_NUM * pwfx.Format.nBlockAlign; buf = new unsigned char[buflen * BUFF_MAX]; ptr = buf; cnt = (cnt + 1) % (BUFF_MAX+1); readlen = mmioRead(hmmio, (HPSTR)ptr, buflen); if (readlen <= 0) { return FALSE; } memset(&bufinfo, 0x00, sizeof(bufinfo)); bufinfo.Flags = ((UINT32)readlen >= buflen) ? 0 : XAUDIO2_END_OF_STREAM; bufinfo.AudioBytes = readlen; bufinfo.pAudioData = ptr; bufinfo.PlayBegin = 0; XAUDIO2_BUFFER_WMA xbw = { 0 }; xbw.PacketCount = SEND_PACKET_NUM; sendPacketCnt += SEND_PACKET_NUM; xbw.pDecodedPacketCumulativeBytes = aDecodedPacketCumulativeBytes; hr = pSourceVoice->SubmitSourceBuffer(&bufinfo, &xbw); ○次回バッファ送信 ptr = buf + buflen * cnt; cnt = (cnt + 1) % (BUFF_MAX+1); readlen = mmioRead(hmmio, (HPSTR)ptr, buflen); if (readlen <= 0) return nullptr; if ((UINT32)readlen >= buflen) { bufinfo.Flags = 0; } else { bufinfo.Flags = XAUDIO2_END_OF_STREAM; } bufinfo.AudioBytes = readlen; bufinfo.pAudioData = ptr; XAUDIO2_BUFFER_WMA xbw = { 0 }; if (sendPacketCnt == nPacketCount) { sendPacketCnt = 0; } if ((sendPacketCnt + SEND_PACKET_NUM) > nPacketCount) { xbw.PacketCount += (nPacketCount - sendPacketCnt); sendPacketCnt += (nPacketCount - sendPacketCnt); } else { xbw.PacketCount = SEND_PACKET_NUM; sendPacketCnt += SEND_PACKET_NUM; } xbw.pDecodedPacketCumulativeBytes = aDecodedPacketCumulativeBytes; hr = pSourceVoice->SubmitSourceBuffer(&buffer, &xbw);
■使用したxWMAの情報
wFormatTag = 65534
nChannels = 2
nSamplesPerSec = 48000
nAvgBytesPerSec = 6000
nBlockAlign = 1008
wBitsPerSample = 16
cbSize = 22
Samples = {wValidBitsPerSample=16 wSamplesPerBlock=16 wReserved=16 }
dwChannelMask = 3
datachunk.cksize = 348768
■参考サイト(※2)
https://msdn.microsoft.com/ja-jp/library/cc308076%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396