XAudio2のマスターボイスの破棄について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
miya

XAudio2のマスターボイスの破棄について

#1

投稿記事 by miya » 15年前

お世話になっております。

XAudio2を利用したサウンド再生クラスを作っている時に、如何しても理解できないバグが発生してしまったので質問させて頂きます。

XAudio2オブジェクトとマスターボイスのオブジェクトを持ち、機能として初期化と開放のみをするクラスを作りました。
これを継承して、使いまわすことにしたのですが…。
どうしてもマスターボイスを破棄するところでエラーが発生してしまいます。
サウンドの再生自体は出来るので、XAudio2オブジェクト、マスターボイス、ソースボイスの作成は上手くいっていると思います。
エラーの原因が自分には分かりません、どうかご教授願います。

ソース内のライブラリのサイズの関係で他のアップローダーを利用してソースをアップしました。
http://down11.ddo.jp/uploader/download/1262652933.zip
パスワードは「xad」です。
また一部抜粋した物もこちらに記載させて頂きます。
//MyXAudio.h
class XAudio{    
protected:
    //static DWORD                    ReferenceCnt;    //参照カウンタ(使用予定)
    static IXAudio2*                pXAudio2;
    static IXAudio2MasteringVoice*            pMasterVoice;
    
    HRESULT     InitXA(void);

public:
    static void Release(void);
};

//MyXAudio.cpp
#define XA_SAFE_RELEASE(p){ if(p) { (p)->Release(); (p)=NULL; } }

IXAudio2*        XAudio::pXAudio2 = NULL;
IXAudio2MasteringVoice*    XAudio::pMasterVoice = NULL;

HRESULT XAudio::InitXA(void){
    HRESULT hr = S_OK;

    if(pXAudio2 == NULL){
        CoInitializeEx(NULL, COINIT_MULTITHREADED);

        UINT32 flg = 0;
        #ifdef _DEBUG
            flg |= XAUDIO2_DEBUG_ENGINE;
        #endif

        if(FAILED(hr = XAudio2Create(&pXAudio2, flg))){
            MessageBox(NULL, "IXAudio2オブジェクトの作成に失敗。", "ERROR", MB_OK); 
            CoUninitialize();
            return hr;
        }

        if(FAILED(hr = pXAudio2->CreateMasteringVoice(&pMasterVoice))){
            MessageBox(NULL, "マスターボイスの作成に失敗。", "ERROR", MB_OK); 
            XA_SAFE_RELEASE(pXAudio2);
            CoUninitialize();
            return hr;
        }
    }

    return hr;
}

void XAudio::Release(void){
    if(pXAudio2 != NULL){
        if(pMasterVoice != NULL) {
            pMasterVoice->DestroyVoice(); //ここでエラー
            pMasterVoice = NULL;
        }

        XA_SAFE_RELEASE(pXAudio2);
        CoUninitialize();
    }
}

御津凪

Re:XAudio2のマスターボイスの破棄について

#2

投稿記事 by 御津凪 » 15年前

IXAudio2Voice::DestroyVoice
http://msdn.microsoft.com/ja-jp/library ... 85%29.aspx

には、

> 現在このボイスに他のボイスがオーディオを送信している場合、このメソッドは失敗します。

とあります。

なので、中身を見るとマスターボイスを破棄する際、他のソースボイスは全て破棄されていないはずです。


それと、継承関係にあるクラスのデストラクタには必ず virtual をつけないと、
正しくデストラクタが呼ばれませんよ。


それ以外の部分(main 関数の戻り値が int でない事)にも問題ありますが、
この質問の問題ではないので、これについては最近の下記の別トピの記事をご覧ください。
http://www.play21.jp/board/formz.cgi?ac ... 5527#45465

miya

Re:XAudio2のマスターボイスの破棄について

#3

投稿記事 by miya » 15年前

> なので、中身を見るとマスターボイスを破棄する際、他のソースボイスは全て破棄されていないはずです。

XAudioクラスをすべてpublicメンバにし、XAudioクラスを継承したクラスを利用せずエントリーポイント内で以下のようにしてみたのですがやはり…。

XAudio::InitXA();
 ↓
ソースボイス作成
 ↓
再生
 ↓
ソースボイス破棄
 ↓
XAudio::Release();


>それと、継承関係にあるクラスのデストラクタには必ず virtual をつけないと、
正しくデストラクタが呼ばれませんよ。

わかりました。以後気をつけます。

>それ以外の部分(main 関数の戻り値が int でない事)にも問題ありますが

すいません個人的なルールで、試験的なソースにはvoid main(void)を、
完成した、サンプルとして提供するなどのソースにはint main(void)を利用するようにしていまして、
今回は気が動転しててvoidのまま公開してしまいました。
以後気をつけます。

御津凪

Re:XAudio2のマスターボイスの破棄について

#4

投稿記事 by 御津凪 » 15年前

ソースボイスを一切作成せず、マスターボイスのみを生成、破棄してみてください。
それで問題が無ければソースボイスの破棄に問題があるはずです。

あと、
> XAudioクラスをすべてpublicメンバにし、XAudioクラスを継承したクラスを利用せずエントリーポイント内で以下のようにしてみたのですがやはり…。

その記述したコードを見せてくれますか?

アップローダにあった Main.cpp 内では、
void main(void){
    OggXAudio test;

    test.Load("test.ogg");

    test.Play();
    Sleep(10000);
    test.Stop();
    
    XAudio::Release();
}
となっていますが、
このときクラス内にソースボイスを持ち、デストラクタ時に解放するような場合だと、
main 関数から抜ける直前までデストラクタが呼び出されません。
これは、 XAudio::Release 関数がそのデストラクタより前に呼び出されることを意味します。

miya

Re:XAudio2のマスターボイスの破棄について

#5

投稿記事 by miya » 15年前

> ソースボイスを一切作成せず、マスターボイスのみを生成、破棄してみてください。
通りました。
完璧にソースボイスの影響のようです。

そして先ほど通らなかったソースがこちらです。(XAudioクラスはオールパブリックの他変更なしです)
#include "MyXAudio.h"

int main(void){
    XAudio::InitXA();

    OggVorbis_File ovf;
    IXAudio2SourceVoice*    pSourceVoice = NULL;

    ov_fopen("test.ogg", &ovf);
    vorbis_info* info = ov_info( &ovf, -1 );

    WAVEFORMATEX fmt;

    fmt.wFormatTag        = WAVE_FORMAT_PCM;
    fmt.nChannels        = info->channels;
    fmt.nSamplesPerSec    = info->rate;
    fmt.wBitsPerSample    = 16;
    fmt.nBlockAlign        = fmt.wBitsPerSample / 8 * fmt.nChannels;
    fmt.nAvgBytesPerSec    = fmt.nSamplesPerSec * fmt.nBlockAlign;
    fmt.cbSize            = 0;

    XAudio::pXAudio2->CreateSourceVoice(&pSourceVoice, &fmt);

    UINT Size = (UINT)(info->channels * info->rate * info->channels * ov_time_total(&ovf, -1));
    char *data = new char[Size];

    UINT LoadSize = 4096;
    UINT ReadSize = 0;
    UINT Pos      = 0;

    while(1){
        ReadSize = ov_read(&ovf,&data[Pos], LoadSize, 0, 2, 1, NULL);
        if(ReadSize == 0 || Pos + ReadSize >= Size)break;
        
        Pos += ReadSize;
        if(Pos + LoadSize >= Size) LoadSize = Size - Pos;
            
    }
    

    XAUDIO2_BUFFER            SoundBuffer = { 0 };
    SoundBuffer.AudioBytes = Size;
    SoundBuffer.pAudioData = (BYTE*)data;

    pSourceVoice->SubmitSourceBuffer( &SoundBuffer );
    
    pSourceVoice->Start(0);

    MessageBox( 0, "終了します", "", 0 );

    pSourceVoice->Stop(0);
    pSourceVoice->DestroyVoice();

    XAudio::Release();

    return 0;
}

御津凪

Re:XAudio2のマスターボイスの破棄について

#6

投稿記事 by 御津凪 » 15年前

そのコードでこちらで何度か実行してみましたが、エラーは出ませんでした。
(警告は出ますが)

どのようなエラーが出ましたか?

miya

Re:XAudio2のマスターボイスの破棄について

#7

投稿記事 by miya » 15年前

すいません。原因が何となく特定しました…。(詳しい所まではさすがに分からないものでしたが)
報告させていただきます。

> どのようなエラーが出ましたか?
エラーの発生箇所までプログラムが進むと
「○○(プログラム名).exe によってブレークポイントが発生しました」
とだけ表示されるものでした。

で、何が原因だったかと言いますと、DirectX SDKの一部のファイル(恐らくXAudio2に関連した何か)が
何らかの原因で破損していたようで、同バージョンのSDKを再度入れなおした結果コンパイルが通るようになりました。
数日前に書いた別のソース(制作当時、動作していた物)も同様の部分で同じ状態になっていたので「アレ?」と思ったら…まさかと言う感じです。

なお、警告のほうはこちらでも確認できています。

まさか、こんな所に原因が潜んでるとは思わず、自分でも驚いています。
お手数をおかけしてすいませんでした。

御津凪

Re:XAudio2のマスターボイスの破棄について

#8

投稿記事 by 御津凪 » 15年前

エラーを確認して、 SDK に問題があるかどうかを特定しようとしていましたが、
miya さんが先に気づき、解決されたようで何よりです。

> エラーの発生箇所までプログラムが進むと
> 「○○(プログラム名).exe によってブレークポイントが発生しました」
> とだけ表示されるものでした。

ランタイムエラーのダイアログさえ出ないということは、明らかに何らか(恐らく DLL)の破損ですね。

ちなみに警告に意味は分かりますか?

miya

Re:XAudio2のマスターボイスの破棄について

#9

投稿記事 by miya » 15年前

本当に御津凪さんにはお世話になりました。

ちなみに、こちらで確認している警告は

warning LNK4098: defaultlib 'LIBCMT' は他のライブラリの使用と競合しています。/NODEFAULTLIB:library を使用してください。

というもので、書いてあるとおり、/MTで作られたライブラリをそれ以外の指定で利用しているため
出ているようです。
実際、コード作成を/MTにする、又は特定のライブラリの無視に追加してしまえば消えたので、
リリースでビルドするときに/MTを指定する事にしました。

後、warningとしては出ていませんが

ogg_static.lib(framing.obj) : MSIL .netmodule または /GL を伴ってコンパイルされたモジュールが
見つかりました。/LTCG を使用して再開始してください。リンカのパフォーマンスを向上させるためには、
コマンドラインに /LTCG を追加してください。

というものが出ています。
こちらはMSDNを読んでも、今一「/LTCG」の項目が理解できていない状態です。
しかし指定されたとおり、リンカのコマンドラインに「/LTCG」を追加すれば回避できるようなので、
ひとまずこのまま利用したいと思います。

これ以外の警告がそちらの環境で出ていましたら、教えていただくと助かります。

後、余談になりますがマスターボイスの破棄のタイミングですが、
現在の利用中のソースボイスをカウンタで数えてやり、
すべてソースボイスが破棄された(カウンタが0になった)時に一緒に破棄するようにしました。
まだコピーコンストラクタや=演算子の時のカウント数の問題は残っていますが、
ひとまずこれで何とかなりそうです。

御津凪

Re:XAudio2のマスターボイスの破棄について

#10

投稿記事 by 御津凪 » 15年前

コンパイル・リンク時の警告はそれで正解ですね。
ようは元となっているライブラリ(ここでは ogg_static.lib)をコンパイルしたときの設定と一緒にしなさい
ということなんですけどね。
つまり、デバッグ版とリリース版、もしくは各コード生成オプションごとにライブラリを使用する必要があるわけです。


それとは別に、実行時にも警告が出ていますね。
XAudio2 では、デバッグエンジンを使用したときに、動作に影響(あるいは支障)がある可能性のとき、
デバッグ出力上に出力されます。
今回では、

XAUDIO2: WARNING: Voice at 0x******** starved: no more source buffers are available, but no end-of-stream marker was received
(一応アドレス値は置き換えています)

というものが出ています。
こちらのほうはお気づきでしたでしょうか?

要約すると、
「最後のサウンドバッファを消費しましたが、 最後のバッファにはエンドマーカーが付いていませんでした」
です。
音声の終端が存在するサウンドバッファを送る時は XAUDIO2_END_OF_STREAM フラグをつける必要があります。
(ちなみに、つけなくても動作自体には全く支障はありません)

miya

Re:XAudio2のマスターボイスの破棄について

#11

投稿記事 by miya » 15年前

デバッグ出力に情報を吐き出してくれるとは…指摘されるまで気づきませんでした。
「~を読み込みました」で埋め尽くされていたため、スクロールしてようやく確認できました。

SDKのサンプルの初期化時に、デバッグ用のフラグを設定していたので、
そっくりそのまま書いたは良かったものの、結局何してるんだろうなぁ…という感じだったのですが
これでスッキリしました!

確かに、こちらでコメントに記載したソースには、すべて読み込んでいるのに、
見事にフラグ付け忘れてますね…気をつけます。

後、出力される事が分かったおかげで、今現在のソースの問題も見えました。

XAUDIO2: WARNING: Called Stop() on a voice that was already stopped; ignoring call

停止状態で停止させようとするなって…当然ですよね。
Start()した回数だけStop()を呼び出さないといけない、とリファレンスにも書かれてますし…。

しかし、これで大分作る込めそうな気配がします。
DirectX Audioと違い中々ネット上でも書籍でもまだ資料が少なくて、
経験の浅い私には難しい面もありますが、自身の知識向上のためにも頑張ってみます。

閉鎖

“C言語何でも質問掲示板” へ戻る