音ゲーの出来ない雑魚のブログ

LWJGLとかのことをいうブログ

「moreover lwjgl」 12 oggを流す

速報?ですが、LWJGLのバージョン3.0.0が6/3に正式にリリースされていました。VulkanやらNanoVGやらだけでなく、が追加されており、ただのラッパーではなくなってきているのかな?という感じです。

それでは本題…いままでのOpenGLの流れをぶっちぎってしまって急にOpenALに入って変な感じになってしまいますが…

OpenALクリエイティブテクノロジーというところが作った音声に関するオープンライセンスなライブラリーです。



じつは最新の更新が5年前(古い…)という状態のOpenALですが、OpenAL Softなるものもありまして、これはオープンソースで、更新も5ヶ月前とのことです。LWJGLはOpenAL Softもありますが、OpenALもちゃんと使えるのでそっちを使っています。

そして、そのOpenALを使って今回は、".ogg"ファイルOGG Vorbis)をデコード、再生してみたいと思います。なぜ".mp3"ではなく、".ogg"にしたのかというと、".mp3"はLWJGLで標準的に開くことが出来ませんが、".ogg"はLWJGLでもデコードデータを渡してくれるライブラリー(STB)があるからです。また、".mp3"にはちょっとしたライセンス問題もあるようですので、あまり使いたくないです。

とりあえず、起動したら曲がふつうに再生されるようなソースコードを…

解説(OpenAL/ALC茶色STB青色で)

20行目 : 再生したいoggファイルのパスです。

22,23行目 : 音声を流すデバイスとopenALのコンテキストです。これは、OpenGLの時とおんなじ感じです。

25行目 : 曲のソースと、曲のバッファーを識別する変数を宣言しています。

26行目 : 読み込んだoggファイルの情報を受け取るSTBVorbisInfoの宣言です。

32~40行目 : プログラムの主な流れです。

ここからOpenALの話

43行目 : 音声を流すバイに接続します。

long alcOpenDevice(ByteBuffer device)

OpenGLの時と同じように、引数にnull(ByteBufferの)を指定すれば、標準のデバイスに接続されます。また、その戻り値がNULL(=0)の場合はちゃんとデバイスに接続できなかったということになります。

47行目 : Capabilitiesによって、ALCのメソッド類といった機能がすべて使えるようになります。

49行目 : 指定したデバイスと属性のALCコンテキストを作成します。

long alcCreateContext(long device,IntBuffer arrs)

属性というのは、このコンテキストが生成された時の設定などを入れます。今回は特に何も指定しないので、null(IntBufferで)に指定しています。

50行目 : 作成したコンテキストを現在のコンテキストに指定します。

void alcMakeContextCurrent(long context)

51行目 : ALCのCapabilitiesを使って、OpenALの機能を全て使えるようにします。

ALCALC API)は、OpenALがどの環境でも動くようにするものです。OpenGLで言うと、GLFW的なものかな?

55行目 : 音声のデータ(音源)を入れた「バッファー」(オーディオバッファー)を生成します。

int alGenBuffers()

バッファーは、IDによって区別され、ここでは、IDを返り値にしています。

56行目 : 音声に関する情報が入る「ソース」を生成します。

int alGenSources()

バッファーと同じくIDを返り値にしています。

58行目 : STBVorbisInfomalloc(メモリーに場所をとる)しています。

60行目 : STBVorbisを使って、oggファイルを開き、かつ、デコードしています。

long stb_vorbis_open_filename( . . . )

CharSequenceでファイルのパスを指定し、エラーを受け取るバッファーとAllocation(ふつうはnullでいいのかな?)さえ渡せば、Allocationにデコードした音声を入れておき、それを操作するハンドルが返ってきます。便利ですね~。ちなみに、メモリーから参照するメソッドも用意されてます。

64行目 : 曲データに関する情報(Info)を受け取ります。

void stb_vorbis_get_info(long f,STBVorbisInfo info)

66行目 : Infoから曲のチャンネルを受け取ります。チャンネルは、基本的に2です。

67行目 : Infoから曲の長さを受け取ります。

int stb_vorbis_stream_length_in_samples(long f)

69行目 : 曲の長さの2倍の大きさで、PCMデータ(サンプルデータ)を入れるShortBufferを作ります。2倍になったのは、チャンネルが2だからだと思います。(長さそのままだと半分でぶちっと切れる)

71行目 : デコードしたoggファイルのデータをチャンネルなど踏まえて、インターリービングにサンプルデータのShortBufferに入れます。

ShortBuffer stb_vorbis_get_samples_short_interleaved(long f,int channels,ShortBuffer buffer)

72行目 : デコードしたデータを開放し、STBVorbisを終えます。

void stb_vorbis_close(long f)


74行目 : サンプルデータをバッファーに入れます。

void alBufferData(int buffer,int format,ShortBuffer data,int frequency)

第1引数でバッファーを。第2引数でオーディオファイルの形式(ステレオの16bitか、モノラルの8bitかなど)
第3引数で実際のサンプルデータを。第4引数では、サンプリング周波数を指定します。

76,77行目 : ソースの属性(ALCの方とは違って、ソースの設定としての)を変更します。

void alSourcei(int source,int param,int value)

76行目では、ソースのオーディオバッファーとして、サンプルデータの入ったバッファーを指定しています。
77行目では、ソースを再生するときに、ループをしないように指定しています。

これで、再生までの準備は整いました。
実際にここまでで、OpenAL内はこうなっています。(一例です)
f:id:cakkby:20160607180350p:plain
バッファーは曲のサンプルデータが入っています。そして、それを入れたソースには、様々な属性が付いていることがわかると思います。

また、サンプリング周波数やPCMなどの説明は こちら で…

81行目 : ソースをふつうに再生します。

void alSourcePlay(int source)

今回は、ストリーミングではないので、後から曲をいじることは出来ません。

82~87行目 : ソースがプレイ中かどうかを確認し、プレイ中ならJavaのスレッドで10ミリ秒待ち、また確認します。プレイ中でなくなったら、次に進めます。

82,86行目 : ソースが状態を受け取る関数です。

alGetSourcei(int source,int target)

AL_SOURCE_STATE
ソースの再生に関する情報を受け取れます。AL_PLAYING(再生中)、AL_PAUSED(一時停止中)、AL_STOPPED(停止中)などの状態が出てきます。


89行目 : ソースの再生を止めます。

void alSourceStop(int source)

一度再生中か確認するループがないと、再生した途端に、停止させてしまいます。(このループが良いとはいえませんが…)

93行目 : ソースをメモリーから消します。

void alDeleteSources(int source)

バッファーとのつながりも切れます。

94行目 : バッファーをメモリーから消します。

void alDeleteBuffers(int source)

一度消すと元に戻せません。

95行目 : デバイスを指定のコンテキストのものにします。

long alcGetContextsDevice(long context)

これは、複数デバイスを一度まとめるためです。つまり、今回はあまりいらないです。

96行目 : コンテキストを現在のものから解除します。これをしてからでないと、コンテキストを消せません。

97行目 : コンテキストを消しています。

void alcDestroyContext(long context)

98行目 : デバイスを消しています。

void alcCloseDevice(long device)

おわり





これを再生すれば、自分が好きな曲を流せます。(oggファイルで)
ただ、ストリーミングでもなく、一時停止も音量変化も、そして一番の楽しみの立体音響ですら出来ないので、今度それらもやってみたいと思います。