ASIOを利用してWAVファイルを再生する
(その2)

WAVファイルの再生を開始する

ダイアログの[再生]ボタンを押されたときに、WAVファイルの再生を開始します。

サンプルコードを以下に示します。


/****************************************************************************/
/*!
 *  @brief  WAVファイルを再生する.
 *
 *  @param  [in]    hDlg    ダイアログのウインドウ・ハンドル.
 *
 *  @retval TRUE = OK. / FALSE = NG.
 */
static BOOL     PlaySoundOutput( HWND hDlg )
{
    //! WAVファイルをオープンしていない場合、オープンする.
    if( !IsOpenWaveFile() ){

        //! -1.ダイアログから再生するWAVファイル名を取得する.
        GetWindowText( GetDlgItem( hDlg, IDC_FILENAME ), FileName, sizeof(FileName) - 1 );

        //! -2.再生するWAVファイル名をオープンする.
        if( !ReadOpenWaveFile( FileName ) ){
            MessageBox( hDlg, _T("ファイルをオープンできません"), _T("WAVファイルの再生"), MB_OK );
            return FALSE;
        }
        WaveEnd  = FALSE;
        EndDelay = 0;
    }
    if( !AppWnd ){
        //! オープンしたWAVファイルのオーディオ・フィーマットを取得する.
        WAVEFORMATEX wf;
        GetAudioFormat( &wf );

        //! サウンド出力デバイスをオープンする.
        UINT num = ComboBox_GetCurSel( GetDlgItem( hDlg, IDC_OUTPUT_DEVICE ) );
        if( OpenSoundOutDevice( hDlg, num, &wf ) ){
            //! WAVファイルの読み込み用バッファを確保する.
            if( !DataBuff ){
                DataSize = BufSize * sizeof(WORD) * 32;
                DataBuff = new BYTE[DataSize];
            }
            //! WAVEファイルを読み込む.
            WavBuf[0]  = DataBuff;
            WavBuf[1]  = &DataBuff[DataSize >> 1];
            WavSize[0] = ReadWaveFile( WavBuf[0], DataSize >> 1 );
            WavSize[1] = ReadWaveFile( WavBuf[1], DataSize >> 1 );
            ActivePage = 0;
            ReadCnt    = 0;
        }
        AppWnd = hDlg;
    }
    //! サウンド出力を開始する.
    ASIOStart();

    EnableWindow( GetDlgItem( hDlg, IDC_PLAY          ), FALSE );
    EnableWindow( GetDlgItem( hDlg, IDC_PAUSE         ), TRUE  );
    EnableWindow( GetDlgItem( hDlg, IDC_STOP          ), TRUE  );
    EnableWindow( GetDlgItem( hDlg, IDC_FILENAME      ), FALSE );
    EnableWindow( GetDlgItem( hDlg, IDC_FILE_SELECT   ), FALSE );
    EnableWindow( GetDlgItem( hDlg, IDC_OUTPUT_DEVICE ), FALSE );
    return TRUE;
}
	


/****************************************************************************/
/*!
 *  @brief  ASIOドライバをオープンする.
 *
 *  @param  [in]    hDlg    ダイアログのウインドウ・ハンドル.
 *  @param  [in]    num     オープンするデバイス番号.
 *  @param  [in]    wf      再生するWAVファイルのオーディオ情報構造体のポインタ.
 *
 *  @retval なし.
 */
static BOOL     OpenSoundOutDevice( HWND hDlg, UINT num, WAVEFORMATEX* wf )
{
    if( num >= DevCnt ){
        return FALSE;
    }
    //! オープンするドライバをロードする.
    asioDrivers->loadDriver( DevName[num] );

    do {
        //! ASIOデバイスをオープンする.
        ASIODriverInfo info;
        memset( &info, 0, sizeof(info) );
        info.asioVersion = 0;
        info.sysRef      = hDlg;
        if( ASIOInit( &info ) != ASE_OK ){
            break;
        }
        //! 作成可能なバッファサイズを取得する.
        long minimum;
        long maximum;
        long preferred;
        long granularity;
        if( ASIOGetBufferSize( &minimum, &maximum, &preferred, &granularity ) != ASE_OK ){
            break;
        }
        //! バッファ情報を格納する領域を初期化する.
        long i = 0;
        memset( BufInfo, 0, sizeof(BufInfo) );
        for( ; i < 2; i++ ){
            BufInfo[i].isInput    = ASIOFalse;
            BufInfo[i].channelNum = i;
        }
        BufSize = preferred;

        //! 使用するチャンネルのデータバッファを確保する.
        static ASIOCallbacks cb;
        cb.bufferSwitch         = &bufferSwitch;
        cb.sampleRateDidChange  = NULL;
        cb.asioMessage          = NULL;
        cb.bufferSwitchTimeInfo = NULL;
        ASIOCreateBuffers( BufInfo, i, BufSize, &cb );

        //! サンプルレートを設定する.
        ASIOSetSampleRate( wf->nSamplesPerSec );
        return TRUE;

    }while( FALSE );

    CloseSoundOutputDevice();
    return FALSE;
}
	

サウンド出力を行うASIOドライバをロードする

asioDrivers::loadDriver()を呼び出して、使用するASIOドライバをロードします。


    bool asioDrivers::loadDriver( char* name )
	

引数説明
name

ロードするASIOドライバ名のポインタ。
getDriverNames()により、リストアップされたドライバ名から選択します。

戻り値がtrueならば、ドライバのロードは成功です。

ASIOドライバをオープンする

ASIOInit()を呼び出して、使用するASIOドライバをオープンします。


    ASIOError ASIOInit( ASIODriverInfo* info )
	

引数説明
info

ドライバ情報を取得する構造体(ASIODriverInfo構造体)のポインタ。

戻り値がASE_OKならば、ドライバのオープンは成功です。

ASIODriverInfo構造体は以下のとおりです。

メンバ名説明
asioVersion

ホストバージョンを指定します。
関数の戻りで、インプリメンテーションバージョンがセットされます。

driverVersion

関数の戻りで、ドライバ・バージョンがセットされます。

name[32]

関数の戻りで、ドライバ名の文字列がセットされます。

errorMessage[124]

関数の戻りで、エラー内容を表す文字列がセットされます。

sysRef

アプリケーションのウインドウハンドルを指定します。

作成可能なバッファサイズを取得する

ASIOGetBufferSize()を呼び出して作成可能なストリームバッファのサイズを取得します。


    ASIOError ASIOGetBufferSize(long *minSize,
                                long *maxSize,
                                long *preferredSize,
                                long *granularity);
	

引数説明
minSize

最小バッファサイズを格納する領域のポインタ。

maxSize

最大バッファサイズを格納する領域のポインタ。

preferredSize

推奨バッファサイズを格納する領域のポインタ。

granularity

ASIOGetBufferSize()からの戻り値がASE_OKであれば、バッファサイズの取得は成功です。
入出力が無い場合、戻り値がASE_NotPresentとなります。

使用するチャンネルのデータバッファを確保する

ASIOCreateBuffers()を呼び出して各チャンネルで使用するデータバッファを確保します。


    ASIOError ASIOCreateBuffers( ASIOBufferInfo *bufferInfos,
                                 long numChannels,
                                 long bufferSize,
                                 ASIOCallbacks *callbacks)
	

引数説明
bufferInfos

作成するバッファの設定を記述した構造体配列のポインタ。
構造体のメンバ isInput と channelNum に使用するチャンネルの情報をセットして呼び出します。

numChannels

使用するチャンネル数。(bufferInfosで指定した配列の個数。)

bufferSize

確保するバッファのサイズ。ASIOGetBufferSize()にて取得した範囲を超えないようにします。

callbacks

コールバック関数のポインタテーブル(ASIOCallbacks構造体)のポインタ。

戻り値がASE_OKであれば、データバッファの作成は成功です。

ASIOBufferInfo構造体は以下のとおりです。

メンバ名説明
isInput

使用するチャンネルの入力/出力を次の値で指定します。
 - ASIOTrue
   入力チャンネル。
 - ASIOFalse
   出力チャンネル。

channelNum

使用するチャンネル番号を指定します。

buffers[2]

ドライバで確保したバッファのアドレスが格納されます。

ASIOCallbacks構造体は以下のとおりです。

メンバ名説明
bufferSwitch

データ入出力のコールバック。

sampleRateDidChange

サンプルレート変更時のコールバック。

asioMessage

ドライバからの要求を受け付けるコールバック。

bufferSwitchTimeInfo

データ入出力のコールバック (タイムコード付き)。

各コールバック関数は以下のとおりです。

bufferSwitch()は、ASIOドライバに対するデータ入出力を行うタイミングでコールバックされます。


    void (*bufferSwitch)( long doubleBufferIndex, ASIOBool directProcess )
	

引数説明
doubleBufferIndex

処理するバッファのインデックス番号。(0 または 1)

directProcess

 - ASIOTrue
   コールバック内での処理が可能。
 - ASIOFalse
   コールバック内での処理は不可。

sampleRateDidChange()は、デジタル入力等で、サンプルレートが現在のレートと変わったときにコールバックされます。


    void (*sampleRateDidChange)( ASIOSampleRate sRate )
	

引数説明
sRate

変更後のサンプルレート。(サンプルレートが不明な場合、0がセットされます。)

asioMessage()は、ドライバからの要求を受け付けるコールバック関数です。


    long (*asioMessage)( long selector, long value, void* message, double* opt )
	

引数説明
selector

セレクタ・コード

value

セレクタに付随するパラメータ。

message

セレクタに付随するメッセージ・パラメータ。

opt

セレクタに付随するオプション・パラメータ。

戻り値はセレクタによって異なります。未サポートのセレクタの場合0を返します。

セレクタ・コードは以下のとおりです。

セレクタ・コード説明
kAsioSelectorSupported

アプリケーションが要求されたセレクタをサポートしているか調べます。
 - パラメータ
   調査するセレクタ・コード。
 - 戻り値
   サポートしている場合1を、していない場合0を返します。

kAsioEngineVersion

アプリケーションのASIOインプリメンテーション・バージョンを問い合わせます。
 - パラメータ
   ありません。
 - 戻り値
   ASIOインプリメンテーション・バージョン(2以上)を返します。

kAsioResetRequest

ドライバからのリセット要求。
 - パラメータ
   ありません。
 - 戻り値
   1を返します。

kAsioBufferSizeChange

バッファサイズの変更が出来たことを通知します。
(バッファサイズの変更が出来ない場合、ドライバからkAsioResetRequestが送られます。)
 - パラメータ
   新しいバッファサイズ。
 - 戻り値
   受け付ける場合は1を、受け付けない場合は0を返します。

kAsioResyncRequest

タイムスタンプが不正になるなど、同期が取れなくなったことによる、ドライバからのリセット要求。
 - パラメータ
   ありません。
 - 戻り値
   受け付ける場合は1を、受け付けない場合は0を返します。

kAsioLatenciesChanged

ドライバがレイテンシを変更したことを通知します。
 - パラメータ
   ありません。
 - 戻り値
   1を返します。

kAsioSupportsTimeInfo

bufferSwitchTimeInfo()コールバックをサポートしているか問い合わせます。
 - パラメータ
   ありません。
 - 戻り値
   サポートしている場合1を、していない場合0を返します。

kAsioSupportsTimeCode

bufferSwitchTimeInfo()コールバックがタイムコードの読み込みをサポートしているか問い合わせます。
 - パラメータ
   ありません。
 - 戻り値
   サポートしている場合1を、していない場合0を返します。

bufferSwitchTimeInfo()は、ASIOドライバに対するデータ入出力を行うタイミングでコールバックされます。
ドライバとアプリケーションで同期を取るために、タイムコードの受け渡しを行います。


    ASIOTime* (*bufferSwitchTimeInfo)( ASIOTime* params,
                                       long doubleBufferIndex,
                                       ASIOBool directProcess )
	

引数説明
params

ASIOTime構造体のポインタ。

doubleBufferIndex

処理するバッファのインデックス番号。(0 または 1)

directProcess

 - ASIOTrue
   コールバック内での処理が可能。
 - ASIOFalse
   コールバック内での処理は不可。

戻り値は、アプリケーションからドライバに通知するタイムコード(ASIOTime構造体)のポインタを返します。

ASIOTime構造体は以下のとおりです。

メンバ名説明
reserved[4]

未使用。(ALL 0)

timeInfo

時間情報(AsioTimeInfo構造体)。

timeCode

タイムコード(ASIOTimeCode構造体)。

AsioTimeInfo構造体は以下のとおりです。

メンバ名説明
speed

再生速度 (1. = nominal)

systemTime

システム時間(ナノ秒)。
Windowsの場合 timeGetTime() で取得される時間。

samplePosition

ASIOStart()からのサンプル数。

sampleRate

サンプルレート。

flags

 - kSystemTimeValid
   メンバ(systemTime)は有効なデータ。
 - kSamplePositionValid
   メンバ(samplePosition)は有効なデータ。
 - kSampleRateValid
   メンバ(sampleRate)は有効なデータ。
 - kSpeedValid
   メンバ(speed)は有効なデータ。
 - kSampleRateChanged
   サンプルレートが変更されました。
 - kClockSourceChanged
   クロックソースが変更されました。

ASIOTimeCode構造体は以下のとおりです。

メンバ名説明
speed

再生速度の分数。

timeCodeSamples

タイムコード (サンプル数)。

flags

 - kTcValid
   タイムコードは有効なデータ。
 - kTcRunning
   正方向再生(?)
 - kTcReverse
   逆方向再生(?)
 - kTcOnspeed
   早送り(?)
 - kTcStill
   静止(?)
 - kTcSpeedValid
   メンバ(speed)は有効なデータ。

future[64]

未使用。(ALL 0)

ASIOでのサウンド出力を開始する

ASIOStart()を呼び出して、ASIOでのサウンド入出力を開始します。


    ASIOError ASIOStart( void )
	

戻り値がASE_OKならば、サウンド出力が開始されます。



ASIOは Steinberg Media Technologies AG社の商標です。
Windowsは米国Microsoft Corporationの登録商標です。