XAudio2を使ってみる。その2-音の再生-

音データを格納する「バッファ」と、音源となる「ソース」を作成し、音の再生を行います。

バッファの作成

音データのフォーマットはWAVEFORMATEX構造体で記述します。設定するメンバは「チャンネル数」「1サンプルあたりのビット数」「サンプリング周波数」の3つです。その他のメンバは計算で求めることが可能です。

    WAVEFORMATEX format = { 0 };
    format.wFormatTag = WAVE_FORMAT_PCM;
    format.nChannels = 1;                   //チャンネル数
    format.wBitsPerSample = 16;             //1サンプルあたりのビット数
    format.nSamplesPerSec = 44100;          //サンプリングレート
    format.nBlockAlign = format.wBitsPerSample / 8 * format.nChannels;
    format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;

    //1秒分のバッファ
    std::vector< char > data( format.nAvgBytesPerSec * 1 );
  • nChannels
    • チャンネル数を指定。
      • 1:モノラル
      • 2:ステレオ
  • wBitsPerSample
    • 1サンプルあたりのビット数を指定。
      • 8:128を無音とする0〜255の値を使用する
      • 16:0を無音とする-32768〜32767の値を使用する
  • nSamplesPerSec

バッファにはWAVファイルから取得したデータをコピーする必要がありますが、今回はWAVファイルを使わずにプログラムで作った音を再生してみます。

作成する音はsin波の音階「ラ(A)」です。「ラ」は440Hzの周波数で表わされる音です。つまり1秒間に440回の波が必要になります。

44100Hzのバッファに440個の波を作るには、1つの波の波長が44100/440になります。

    //ラを表すsin波(1秒)
    std::vector< char > data( format.nAvgBytesPerSec * 1 );
    short* p = &data[ 0 ];
    for( size_t i = 0; i < data.size() / 2; i ++ )
    {
        float length = format.nSamplesPerSec / 440.0f;
        *p = ( short )( 32767 * sinf( i * 3.1415926535f / length ) );
        p ++;
    }

SourceVoiceの作成

XAudioエンジンからWaveフォーマットを指定してSourceVoiceを作成します。

IXAudio2SourceVoice* source_voice;
    HRESULT hr = xaudio->CreateSourceVoice( &source_voice, &format );
    if( FAILED( hr ) )
        throw "CreateSourceVoice";

XAUDIO2_BUFFER構造体を使ってバッファのバイト数、バッファの先頭アドレスを指定し、SourceVoiceにデータを送信します。(再生キューにデータを追加)

    XAUDIO2_BUFFER buffer = { 0 };
    buffer.AudioBytes = data.size();            //バッファのバイト数
    buffer.pAudioData = ( BYTE* )&data[ 0 ];    //バッファの先頭アドレス
    buffer.Flags = XAUDIO2_END_OF_STREAM;       // tell the source voice not to expect any data after this buffer
    source_voice->SubmitSourceBuffer( &buffer );

再生

SourceVoiceからStartメソッドを呼び出します。

    source_voice->Start();

停止

再生カーソルを停止します。

    source_voice->Stop();

SourcVoiceの破棄

    source_voice->DestroyVoice();

全ソース

以下、1秒間「ラ」の音を再生するためのソースです。

#include <windows.h>
#include <xaudio2.h>
#include <vector>
#include <cmath>
#include <iostream>

IXAudio2* xaudio;
IXAudio2MasteringVoice* mastering_voice;

IXAudio2SourceVoice* source_voice;

void init()
{
    //
    //  Initialize XAudio2
    //
    HRESULT hr;
    if( FAILED( hr = CoInitializeEx( NULL, COINIT_MULTITHREADED ) ) )
        throw "CoInitializeEx";

    UINT32 flags = 0;
#ifdef _DEBUG
    flags |= XAUDIO2_DEBUG_ENGINE;
#endif
    if( FAILED( hr = XAudio2Create( &xaudio, flags ) ) )
        throw "XAudio2Create";

    //
    //  Create a mastering voice
    //
    if( FAILED( hr = xaudio->CreateMasteringVoice( &mastering_voice ) ) )
        throw "CreateMasteringVoice";
}

void cleanup()
{
    //
    //  Cleanup XAudio2
    //
    if( mastering_voice != 0 )
    {
        mastering_voice->DestroyVoice();
        mastering_voice = 0;
    }
    if( xaudio != 0 )
    {
        xaudio->Release();
        xaudio = 0;
    }
    CoUninitialize();
}

int main()
{
    try
    {
        //
        //  XAudioの初期化
        //
        init();

        HRESULT hr;
        //
        //  Waveフォーマットの設定
        //
        WAVEFORMATEX format = { 0 };
        format.wFormatTag = WAVE_FORMAT_PCM;
        format.nChannels = 1;                   //チャンネル数
        format.wBitsPerSample = 16;             //1サンプルあたりのビット数
        format.nSamplesPerSec = 44100;          //サンプリング周波数
        format.nBlockAlign = format.wBitsPerSample / 8 * format.nChannels;
        format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;

        //
        //  SourceVoiceの作成
        //
        if( FAILED( hr = xaudio->CreateSourceVoice( &source_voice, &format ) ) )
            throw "CreateSourceVoice";

        //
        //  適当な音を作成
        //      「ラ」を表すsin波(440Hz、1秒)
        //
        std::vector< BYTE > data( format.nAvgBytesPerSec * 1 );     //バッファ
        short* p = ( short* )&data[ 0 ];
        for( size_t i = 0; i < data.size() / 2; i ++ )
        {
            float length = format.nSamplesPerSec / 440.0f;          //波長
            *p = ( short )( 32767 * sinf( i * 3.1415926535f / (length/2) ) );
            p ++;
        }

        //
        //  SourceVoiceにデータを送信
        //
        XAUDIO2_BUFFER buffer = { 0 };
        buffer.AudioBytes = data.size();            //バッファのバイト数
        buffer.pAudioData = &data[ 0 ];             //バッファの先頭アドレス
        buffer.Flags = XAUDIO2_END_OF_STREAM;       // tell the source voice not to expect any data after this buffer
        source_voice->SubmitSourceBuffer( &buffer );

        //
        //  再生
        //
        source_voice->Start();

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

        //
        //  SourceVoiceの破棄
        //
        source_voice->Stop();
        source_voice->DestroyVoice();
    }
    catch( const char* e )
    {
        std::cout << e;
    }

    //
    //  XAudioの破棄
    //
    cleanup();

    return 0;
}

音階(平均律

ちなみに、「ラ(A)」以外の音階は以下の周波数で表すことができます。

A 440
A#, B♭ 466.164
B, C♭ 493.883
C, B# 523.251
C#, D♭ 554.365
D 587.33
D#, E♭ 622.254
E, F♭ 659.255
F, E# 698.457
F#, G♭ 739.989
G 783.991
G#, A♭ 830.61
A 880.000

これらの値は次の式で求めることができます。

    float a[ 13 ];
    for( int i = 0; i < 13; i ++ )
    {
        float t = powf( 2.0f, 1.0f / 12 );
        a[ i ] = 440.0f * powf( t, ( float )i );
    }

サンプル