オーディオ再生#

MGLは異なる音源リソースを透過的に扱えるオーディオ再生の機能を備えています。本項では、MGLにおけるオーディオ再生の基本的な使用方法について解説します。

MGLにおけるオーディオ再生の仕組み#

MGLでオーディオ再生を利用するためには、音源リソースとなるオーディオボイスと、そのボイスを再生するためのオーディオソースの2つを利用します。まずはこれらの関係性についてを説明します。

まず、MGLのオーディオ再生に関わる仕組みの概略図を次に示します。

MGLのオーディオ再生の概略図
../_images/audio_diagram.svg

MGL内部処理となっている部分はMGLが隠蔽しているため、アプリケーション側からはあまり意識する必要はありません。ここでは、アプリケーション側から利用可能な3つの要素、すなわち、オーディオボイス、オーディオソース、オーディオレンダラについてを解説します。

オーディオボイス#

オーディオボイスは音の発生源となるオブジェクトです。音声ファイルなどの音源リソースを扱う場合は、このオーディオボイスとして実装します。

オーディオボイスはMGL::Audio::Voiceクラスを継承して実装します。このクラスの役割は、後述するオーディオソースからのサンプリングデータの要求に対し、適切な値を出力することです。

例えば、特定のフォーマットの音声ファイルに対応したい場合、そのファイルの読み込みとデコード処理をオーディオボイス内に実装することで実現します。応用例として、音声波形を動的に生成することで、ソフトウェア音源などの実装も可能になります。

オーディオボイスはボイスキー(MGL::Audio::VoiceKey)と呼ばれるキーと共にあらかじめ登録してから利用します。ボイスキーはそのボイスを表す一意の値であり、登録後はボイスキーを経由してアクセスすることになります。

オーディオボイスはアプリケーション側で実装することを想定していますが、MGLの標準構成には無圧縮のPCMデータに広く用いられている、WAVE形式(RIFF形式)のコンテナに対応したオーディオボイスが含まれています。本項では後にこのボイスを利用する例を解説します。独自にオーディオボイスを作成する場合はオーディオボイスの作成(準備中)も参照してください。

オーディオソース#

オーディオソースはオーディオボイスを再生する役割を担うオブジェクトです。アプリケーションからはMGL::Audio::Sourceクラスによって表現され、ボイスキーを用いてオーディオボイスと関連付けてから使用します。ボイスの再生や停止、音量の調整などはこのクラスに対して指示することになります。

MGL::Audio::Sourceは厳密に言うと、MGL内部で生成・管理される実体への弱参照を行うハンドルクラスです。ソースの実体はオーディオソースインスタンスと呼ばれるもので、MGL内部でボイスからサンプリングデータを取得しているのはこのインスタンスとなります。通常、アプリケーション側からはインスタンスの存在を意識する必要はありません。

オーディオソースの利用方法の詳細についてはオーディオの再生にて解説します。

オーディオレンダラ#

オーディオレンダラは、オーディオデバイスへの出力を担うオブジェクトです。このオブジェクトはプラットフォーム毎の差異を吸収するためにMGL内部の処理からは分離されており、初期化時に登録して使用します。

標準構成で利用する限りでは、独自のオーディオレンダラを準備する必要はありません。Windows向けにはXAudio2による実装が、Apple製品向けにはCore Audioによる実装がそれぞれ用意されており、これらが初期化時に登録されます。

オーディオボイスの登録#

先述の通り、MGLの標準構成にはWAVE形式(RIFF形式)のコンテナに格納されたリニアPCMデータを扱うためのオーディオボイス(以下WAVEボイスと呼称)があらかじめ用意されています。ここでは、このWAVEボイスを例に用いてオーディオボイスの登録方法について解説します。

オーディオボイスの種類#

MGLのオーディオボイスには、読み込み後に内部状態が変化しないStaticボイスと、読み込み後にも内部状態が変化するDynamicボイスの2種類があります。両者の違いは次の通りです。

Staticボイス

読み込み後に内部状態が変化せず、常に全てのサンプリングデータにアクセス可能なボイスです。このボイスは複数のソースによる同時再生が可能です。通常、このボイスは全てのサンプリングデータをメモリ上に展開する必要があるため、初回の読み込み時間とメモリの消費量は音源の長さに比例します。効果音などの尺が短く頻繁に再生される可能性のある音源にはこちらが最適です。

Dynamicボイス

読み込み後も内部状態が変化するボイスです。このボイスは一度に1つのみ再生可能で、複数同時に再生することはできません。その代わり、必要最低限のサンプリングデータのみを保持するように実装することで、読み込み時間やメモリの消費量を大きく節約できます。ストリーミング再生やソフトウェア音源などはこちらで実装することを想定しており、BGMやキャラクターのセリフなどにはこちらが最適です。

標準構成に含まれるWAVEボイスは、Staticボイスとして読み込むMGL::Audio::WaveVoiceと、Dynamicボイスとして読み込むMGL::Audio::WaveStreamVoiceの2つが用意されています。

MGL::Audio::WaveVoice

全てのPCMデータをメモリ上に展開するStaticボイスです。容量が小さく、頻繁に利用される音源にはこちらが最適です。

MGL::Audio::WaveStreamVoice

PCMデータを先頭から必要な量だけ逐次読み込むDynamicボイスです。容量が大きく、同時再生を行う必要のない音源にはこちらが最適です。

オーディオボイスの登録方法#

では、実際にオーディオボイスを登録する方法を解説していきます。ここでは、bgm.wavという音声ファイルをBGMとして、se.wavという音声ファイルを効果音として登録するものとします。

まず、両者と関連付けるためのボイスキーを用意します。ここでは例として、MGL::Audio::MakeVoiceKeyを用いてファイル名からボイスキーを生成します。

ボイスキーの定義
// BGMのためのボイスキー
constexpr const auto kBGMVoiceKey = MGL::Audio::MakeVoiceKey("bgm.wav");

// 効果音のためのボイスキー
constexpr const auto kSEVoiceKey = MGL::Audio::MakeVoiceKey("se.wav");

重要

ボイスキーは重複が許されないため、扱うボイスキーの量が増えるほどハッシュ衝突のリスクが高まります。ここでは簡略化のためにプログラム側でボイスキーを生成していますが、実際には衝突チェックを兼ねた生成スクリプトを用意するなど、人の手によらない手段でボイスキーを定義することを推奨します。

ボイスキーが準備できたところで、オーディオボイスの登録処理を行います。オーディオボイスの登録にはMGL::Audio::LoadVoiceを利用します。

MGL::Audio::LoadVoiceは、指定されたクラスの生成から登録までの一連の処理をまとめたテンプレート関数です。テンプレート引数に生成したいオーディオボイスのクラス名を指定し、関数の引数はオーディオボイスのコンストラクタにそのまま渡されます。

オーディオボイスの登録処理の例を次に示します。ここでは、BGMをMGL::Audio::WaveStreamVoiceで、効果音をMGL::Audio::WaveVoiceで読み込みます。

オーディオボイスの登録処理
// BGMの登録
auto bgmVoice = MGL::Audio::LoadVoice<MGL::Audio::WaveStreamVoice>(
                            kBGMVoiceKey,           // ボイスキー
                            "$resource/bgm.wav",    // 読み込むファイルのパス
                            true,                   // デフォルトのループフラグ
                            0);                     // ループポイントのサンプルフレーム
if (bgmVoice.expired())
{
    MGL_TRACE("BGMの登録に失敗");
}

// 効果音の登録
auto seVoice = MGL::Audio::LoadVoice<MGL::Audio::WaveVoice>(
                            kSEVoiceKey,            // ボイスキー
                            "$resource/se.wav");    // 読み込むファイルのパス
if (seVoice.expired())
{
    MGL_TRACE("効果音の登録に失敗");
}

コンストラクタに与える引数は両者とも共通です。第1引数にボイスキーを、第2引数には読み込む音声ファイルのパスを指定します。

第3引数はデフォルトのループフラグです。再生時にループ設定を指定しなかった場合、ここで指定した値に準じた動作を行うようになります。

第4引数はループポイントの設定です。ループ再生の場合に末尾まで再生が完了した後、このポイントまで戻って再生を継続するようになります。ループポイントの単位はサンプルフレームです。

第3引数と第4引数は省略可能で、省略時はそれぞれfalse0が指定されます。この例では効果音はループを行わないものとして、2つの引数を省略しています。

登録に成功した場合、戻り値として生成したオーディオボイスの弱参照ポインタを返します。ソフトウェア音源などの外部から直接操作したいオーディオボイスを生成した場合、この戻り値を必要に応じて保持してください。

オーディオボイスの破棄#

登録したオーディオボイスはアプリケーション終了時に自動で破棄されますが、メモリリソースを空けたい場合などに手動で破棄することも可能です。

オーディオボイスを破棄するにはMGL::Audio::UnloadVoiceを利用し、引数にボイスキーを指定します。この関数の利用例を次に示します。

オーディオボイスの破棄の例
// BGMの破棄
MGL::Audio::UnloadVoice(kBGMVoiceKey);

// 効果音の破棄
MGL::Audio::UnloadVoice(kSEVoiceKey);

オーディオボイスの破棄が要求されると、そのボイスを利用して再生しているオーディオソースは直ちに停止するように動作します。全てのオーディオソースが停止して参照が失われた時点で、オーディオボイスのデストラクタが呼び出されてメモリ上から解放されます。

オーディオソースは専用のスレッドで動作しており、MGL::Audio::UnloadVoiceの呼び出し直後にはまだメモリ上に存在している可能性がある点にはご注意ください。

注釈

オーディオボイスストレージからの削除は破棄要求の時点で行われているため、直後に同じボイスキーを使用した異なる音源を登録することは可能です。

オーディオの再生#

登録したオーディオボイスの再生にはMGL::Audio::Sourceを利用します。このクラスはアプリケーション側からオーディオソースを扱うためのクラスであり、オーディオボイスと関連付けて再生や停止、音量の調整などを行います。

アタッチと再生#

オーディオソースを用いたボイスの再生方法にはいくつかの方法があります。まずは、最も基本的なアタッチして再生する例を次に示します。

アタッチして再生する例
MGL::Audio::Source source;

// BGMのボイスでアタッチ
source.Attach(kBGMVoiceKey, true);

// 再生
source.Play();

オーディオソースのアタッチ処理はMGL::Audio::Source::Attachを用いて行います。

第1引数には関連付けるボイスキーを指定します。ここではBGMのボイスキーを用いています。

第2引数のisAutoRemoveの指定は、再生終了後にオーディオソースを自動で削除するかのフラグです。ここにtrueを指定した場合、再生終了後に内部で管理しているオーディオソースの実体は削除され、このクラスはアタッチしていない状態に戻ります。falseを指定した場合は再生終了後も停止状態で保持され続け、このクラスのアタッチ状態も維持されます。

注釈

MGLにおけるオーディオ再生の仕組みでも述べた通り、MGL::Audio::SourceはMGL内部で管理しているオーディオソースインスタンスと呼ばれる実体への弱参照を行うハンドルクラスです。このため、必要に応じてアタッチ状態を保持したり、操作が不要であればクラスそのものを放棄することも可能です。

再生にはMGL::Audio::Source::Playを使用しています。ここでは引数を省略していますが、この例は次の呼び出しと等価です。

source.Play(0, MGL::Audio::LoopType::ResourceDefault, 1.0f);

第1引数は再生トラックの指定で、オーディオボイスが複数の音源を扱える場合に使用します。標準のWAVEボイスはマルチトラックに対応していないため、ここでは0を指定します。

第2引数はMGL::Audio::LoopTypeによるループの指定です。省略またはLoopType::ResourceDefaultを指定した場合、そのボイスが持つデフォルトの動作を行います。標準のWAVEボイスの場合、デフォルトの動作はコンストラクタの引数で指定しています。

第3引数は再生音量です。この値はスケール値であり、再生時にこの値が乗算されます。

オーディオソースのアタッチ処理は、次のようにコンストラクタで行うことも可能です。

コンストラクタでのアタッチ処理
// BGMのボイスでアタッチ
MGL::Audio::Source source(kBGMVoiceKey, true);

// 再生
source.Play();

コンストラクタの引数は通常のアタッチ処理と同じです。処理の成否はMGL::Audio::Source::IsAttachedにて判別可能です。

また、MGL::Audio::Source::Playでアタッチと再生を同時に行うことも可能です。

アタッチと再生を同時に行う例
MGL::Audio::Source source;

// アタッチして再生
source.Play(kBGMVoiceKey, true);

引数にはMGL::Audio::Source::Attachの引数の後にMGL::Audio::Source::Playの引数を与えます。省略時の値もそれぞれ同等です。

デタッチ処理#

アタッチしたオーディオソースのデタッチ処理にはMGL::Audio::Source::Detachを利用します。

デタッチ処理は自動で行われるため、通常は明示的に呼び出す必要はありません。他のアタッチ処理で上書きした場合の他、クラスが破棄された際にもデストラクタでデタッチ処理が行われます。

デタッチ後にはその再生の制御が行えないことには注意が必要です。例えば、ループ再生時やBGMなどの長尺の音源を再生後、停止命令を与えずにデタッチした場合、その再生は意図しないシーンまで持ち越される危険があります。再生後に制御の必要があるオーディオソースは、デタッチせずに保持し続けるように実装してください。

なお、停止中のオーディオソースをデタッチした場合、再生リストで待機しているソースの実体は即座に削除されます。また、アタッチ時にisAutoRemovefalseに指定していても、デタッチの際にtrue指定時の動作へと上書きされます。

再生音量の制御#

MGLにはマスター音量、ボイス音量、ソース音量の3つが存在しています。それぞれ、次の関数にて制御します。

それぞれの値はスケール値であり、再生時にサンプリングデータに乗算して出力されます。

マスター音量は全ての再生に対して適用される係数です。この値はゲーム全体の音量の調整に使用してください。

ボイス音量はオーディオボイス毎に適用される係数です。例えば、BGMとSEの音量バランスを設定した場合などは、それぞれのボイスに対して設定値を適用することで実現できます。

ソース音量はオーディオソース毎に適用される係数です。ゲーム中の演出的な音量の制御は通常このソース音量にて行います。

ソース音量は時間経過による音量変化にも対応しています。MGL::Audio::Source::SetVolumeの第2引数に変化時間を秒単位で指定することで、現在の音量から指定の音量まで時間経過と共に変化させます。フェードアウトについては特化したMGL::Audio::Source::Fadeoutも用意されています。

フェードインとフェードアウトの例を次に示します。

フェードインの例
// 音量0で再生
source.Play(0, MGL::Audio::LoopType::ResourceDefault, 0.0f);

// 3秒かけて音量 1.0 まで変化させる
source.SetVolume(1.0f, 3.0f);
フェードアウトの例
// 3秒かけてフェードアウト
source.Fadeout(3.0f);

そのソースが音量変化の途中であるかを取得する場合はMGL::Audio::Source::IsFadingを使用してください。