none
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

    2018年2月20日 11:45

回答

  • 完全解決とは言えませんが、再生まではこぎつけました。

    コールバック処理は本件では関係なかったため、ストリーミングという言い方に変更させて頂きます。

    ストリーミング再生をするときには、大きいサイズのデータを分割して、順番に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


    • 回答としてマーク gama55 2018年3月9日 4:26
    • 編集済み gama55 2018年3月9日 4:30 改行位置変更
    2018年3月9日 4:25

すべての返信

  • 質問をするときは実行環境(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
    2018年2月24日 11:11
  • 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

    2018年2月26日 9:56
  • 完全解決とは言えませんが、再生まではこぎつけました。

    コールバック処理は本件では関係なかったため、ストリーミングという言い方に変更させて頂きます。

    ストリーミング再生をするときには、大きいサイズのデータを分割して、順番に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


    • 回答としてマーク gama55 2018年3月9日 4:26
    • 編集済み gama55 2018年3月9日 4:30 改行位置変更
    2018年3月9日 4:25