iPhone+OpenAL入門
最近作ったアプリで、独自ピッカーを動かす際に効果音を鳴らそうと思って、AVFoundationで実装したところ、ピッカーをゆっくり動かす分には問題ないのだが、少しでも速く動かすと、再生遅延が起こって音飛びが発生し、最悪の場合再生されなくなってしまった。
そこで、遅延が少ないらしいOpenALで実装してみたので、そのときの手順をまとめてみる。
インポート
#import <OpenAL/al.h> #import <OpenAL/alc.h> #import <AudioToolbox/AudioToolbox.h>
デバイス取得・コンテキストの生成と登録
ALCdevice *device = alcOpenDevice(NULL); if (device) { ALCcontext *context = alcCreateContext(device, NULL); alcMakeContextCurrent(context); }
alcOpenDeviceでデバイスを取得する。引数をNULLにしておくと最適なデバイスを返してくれる(iPhoneの場合、デバイスはひとつしかないのでNULL以外の選択肢はない)。
次にalcCreateContextでコンテキストの生成を行い(戻り値NULLで失敗)、それをalcMakeContextCurrentで登録する。
コンテキストとは、内部状態のことであり、alcMakeContextCurrentで登録を行うと、以後OpenALで行うすべての操作は、そのコンテキストに適用されることになる(複数の設定を使い分けたいときに利用する。通常はひとつで十分)。
音声ファイルのデータを取得する
iPhoneでは容量の関係から、音声ファイルはcaf形式で使用するのが好ましいらしい。変換はターミナル上で行う。変換のコマンドは、検索するとたくさん出てくるが、一応mp3からcafへの変換コマンドを書いておく。音声ファイルがあるところまでターミナルで移動してから、以下のコマンドを実装。
mp3→caf
/usr/bin/afconvert -f caff -d LEI16@44100 -c 1 変換前.mp3 変換後.caf
Bufferの用意
ALuint buffer; alGenBuffers(1, &buffer);
再生したい曲を格納するメモリ(バッファ)を用意する(曲とバッファは1対1の関係)。
alGenBuffersは、一度に複数のBufferを作ることができる。
第一引数は、生成するBufferの数。
Sourceの用意
ALuint source; alGenSources(1, &sources); alSourcei(source, AL_LOOPING, AL_TRUE); alSourcei(source, AL_PITCH, 1.0f); alSourcei(source, AL_GAIN, 0.45f);
再生ソースを用意する。
再生位置、ループ、ピッチやゲインなどを変更したい場合は、ソースに適用する。
alGenBufferと使い方は同じ。
サウンドファイル(cafファイル)の読み込み
void *data; ALenum format; // フォーマット ALsizei size; // ファイルサイズ ALsizei freq; // 周波数 NSBundle *bundle = [NSBundle mainBundle]; CFURLRef fileURL = (__bridge CFURLRef)[NSURL fileURLWithPath:[bundle pathForResource:fileName ofType:type]]; data = MyGetOpenALAudioData(fileURL, &size, &format, &freq); alBufferDataStaticProc(buffer, format, data, size, freq);
サウンドファイル(cafファイル)を読み込み、バッファを作成する。
まず、サウンドの各オーディオデータを取得する。
オーディオデータの取得には、OpenALサンプルoalTouchで使用されているMyGetOpenALAudioData関数を利用すると簡単。
(MyOpenALSupport.hとMyOpenALSupport.mをプロジェクトにコピーし、インポートしておく)
次に、そのオーディオデータを利用してバッファを作成する。
バッファ作成にも、oalTouchで使用されているalBufferDataStaticProc関数を利用すると簡単。
SourceにBufferを適用する
alSourcei(source, AL_BUFFER, buffer);
バッファを再生ソースに割り当てる。
再生
alSourcePlay(source);