iPhone Core Audio Programming 第2回 AUGraphを使ってPlay Thru
iPhone Core Audio Programming 2回目
前回、これまでGetting Started〜で書いてきたことがiPhoneでも通用すると書きました。
あと、Core Audioはいろいろありすぎてわけわからん!などと言われているようですが、たしかにそうだし、サンプルコードがほとんど無いというのはありますが、実は結構簡単です。
まずはそれを証明しましょう。
そこでまずは、というかいきなりAUGraphを使ってみます。Audio Queueより簡単です。
(僕はAudio QueueはCore Audioで一番難しいと思うのですが、どうでしょう)
AUGraphとは何か
AUGraphについては
Getting Started With Audio Unit 第06回.AUGraphとは何か
Getting Started With Audio Unit 第16回.AUGraph SubGraphを使う
で説明してあります。なんだか分からない方は先に一読ください。その方が今後の時間節約になると思います。
簡単に説明すると、AUGraphはAudio Unitを複数組み合わせて一つの処理を実現するための仕組みです。
Audio Unitとはオーディオ処理で必要となる機能を持つプラグインです。プラグインといっても必ずしもVSTみたいなエフェクターではなくて、音の出力、ミックス、変換、生成など、オーディオ処理で必要となるほぼ全ての機能を持てます。
*Audio UnitについてもGetting Started With Audio Unitの1〜5回ぐらいまでを読んでおいてください。
Core Audioはこのようにシステム自体がプラグイン・アーキテクチャーを採用しています。
AVAudioPlayerとかAudio QueueとかOpenALとかいろいろありますが、最終的には全部Audio Unitを使っていると考えてよいです(後述のRemote IO Unitを使って音を出力します)。
さて、話を戻すと、AUGraphを使うわけですが、iPhone Core Audioには音を生成するジェネレーター(kAudioUnitType_Generator)に該当するAudio Unitがありません。そこで今回はマイク入力を使います。つまり、マイク入力をそのまま出力するAUGraphを作ります。
Remote IO Unit
マイク入力、スピーカーからの出力にはRemote IO Unitを使います。
OS X Core AudioのDefault Output Unitと同じと考えてもらっていいと思います。
Remote IO Unitには IOというぐらいですから、InputとOutputがあります。
そしてそれぞれは「バス」(Bus)で区別します。Inputは1でOutputは0です。以降、このバスの1とか0をバスナンバーと呼びます。今回はこれだけ分かればよいです。詳細は次回解説します。
以降、便宜的にInputをRemote Input、OutputをRemote Outputと呼びます。
さっそく、Remote InputとRemote Outputを繫いでAUGraphを作ってみます。
PlayThruという名前でView-Based Applicationをテンプレートにプロジェクトを作って、AudioToolBox.frameworkをリンク、PlayThruViewController.hに
#import <AudioToolbox/AudioToolbox.h>
#import <AudioUnit/AudioUnit.h>
を追加、viewDidLoadを以下のように実装します。ViewControllerにオーディオのロジックを書くな!と言われそうですが、クラス化はひとまず置いておいて、手っ取り早く動かしましょう。
ちなみに、AudioUnit.frameworkはリンクしません。するとビルドエラーが出ます(なにこれ)。
- (void)viewDidLoad {
AUGraph mAuGraph;
AUNode remoteIONode;
AudioUnit remoteIOUnit;
NewAUGraph(&mAuGraph);
AUGraphOpen(mAuGraph);
AudioComponentDescription cd;
cd.componentType = kAudioUnitType_Output;
cd.componentSubType = kAudioUnitSubType_RemoteIO;
cd.componentManufacturer = kAudioUnitManufacturer_Apple;
cd.componentFlags = cd.componentFlagsMask = 0;
AUGraphAddNode(mAuGraph, &cd, &remoteIONode);
AUGraphNodeInfo(mAuGraph, remoteIONode, NULL, &remoteIOUnit);
//マイク入力をオンにする
UInt32 flag = 1;
AudioUnitSetProperty(remoteIOUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input, //Remote InputのInput
1, //Remote Input
&flag,
sizeof(UInt32));
AUGraphConnectNodeInput(mAuGraph,
remoteIONode, 1, //Remote Inputと
remoteIONode, 0 //Remote Outputを接続
);
AUGraphInitialize(mAuGraph);
AUGraphStart(mAuGraph);
}
カテゴリ(タイプ)がkAudioUnitType_OutputでサブタイプがkAudioUnitSubType_RemoteIOのAudioComponentDescriptionを作って、AUGraphAddNoteでRemote IO UnitをAUGraphに追加、Remote InputとRemote Outputを接続しているだけです。
途中、AudioUnitSetProperty(kAudioOutputUnitProperty_EnableIO)という呪文がありますが、これはマイクを使う!という指示です。Remote InputのInputという謎のコメントがありますが、Remote Inputはマイク”入力”を”出力”するのですから、当然Input/Outputがあるわけです。とりあえず今は呪文と思ってください。
これら以外はGetting Started With Audio Unit 第06回.AUGraphとは何か
とほとんど同じコードだというのが分かると思います。
AUGraphNewNodeがdeprecatedになったのでAUGraphAddNodeに変更になっていたりしますが。
ヘッドフォンを装着して、ビルド実行してみてください。マイク入力が聞こえます。
が、が、片方のチャンネルしか聞こえません。
マイク入力がモノラルなのに、その後はステレオになっているので片方しか聞こえないのですが、これを解決するにはRemote IO Unitの詳細とAudioStreamDescriptionを解説しないといけないので、次回やります。
ちなみに、ヘッドフォンをしてない場合は面白くて、受話部分から音が出ます。
これは、ヘッドフォンをしていない場合、スピーカーを使うと、スピーカーとマイクが近い位置にあるので、フィードバック(ハウリング)してしまうため、こういう仕様になっています。これはAudio Sessionを使うと変更できます(Audio Sessionについてはたぶん第4回目ぐらいにやります。)。
次回はちゃんと両チャンネルから音が出るように修正していきます。あと、今回のコードは処理の開始・停止すらないので、これも実装します。
Core AudioのAPIはほぼ全部OSStatus型を返します。これはエラーが発生した場合に0以外が帰ります。今回は全部無視していますが、気になる人は
OSStatus err = AUGraphNodeInfo(mAuGraph, remoteIONode, NULL, &remoteIOUnit); if(err)NSLog(@"errorが発生した! %d",err);
という感じでチェックしてください。
App Storeにあるレコーダーはモニタリングができないものが大半ですが、AUGraphを使えば結構簡単にできます。この入力をどうにか加工すればRjDjみたいなものを作るのも可能ですね。
もちろん、AUGraphを使わなくてもマイク入力を取得できますが、これも先の回でやります。
続きは次回。
オトカメラは以上の機能を使ってモニタリングに対応した、音も記録するカメラアプリケーションです。
モニタリングに加えて、録音レベルの調整にも対応しています。
App Storeで購入いただけますので、よろしくお願いします!

ただ、3GSで不具合が見つかったので、3GSの方は少々お待ちください。
