リニューアル [マックな開発]
So-netブログがリニューアルされて、かなり変わりましたね。
カスタマイズもかなりできるようですし。複数のブログも持てるようになったようです。
それとはまったく関係ないですが、Mac BookとMac Book Proが新しくなってました。
Dual USB iBookを使っていたので、やはり白Mac Bookがいいですね。Mac Book Airがいろいろと話題になっていますが、129,000円という値段でMacが使えるというのもあるんですが、デザインは好みでしょうけど、質感が微妙にPoorな感じがするところがいいいんですw。
値段どおりの質感って言ったほうがいいのかもしれませんけど。結構、国産のノートPCだと値段の割には質感がいまいちというのがあるでしょう。あんなんよりは、全然よいです。
Apple MacBook 2.1GHz Core 2 Duo/13.3/1G/120G/24xCombo/Gigabit/BT/DVI MB402J/A
- 出版社/メーカー: アップルコンピュータ
- メディア: エレクトロニクス
Safari 3 パブリックベーター [マックな開発]
そういえば、Safari3のペーター(Windows版)がアップデートされていました。
とりあえず、インストール、というか、Appleアップデーターに現れてたので、アップデートしてみました。
やっと日本語でもちゃんとでるようになりましたね。
手持ちのブラウザ(firefox,Opera,IE7)だとやはり最速ブラウザです。
まぁ、多少機能的には劣るところもありますけども…。
個人的にはマウスジェスチャだけでも実装してほしいです。
CAAudioFileについて [マックな開発]
前回のAUHALを使ってファイルを再生する方法でファイルが終りに達しているかをチェックするところでCAAudioFileのTell()をいうメソッドを使っていました。
しかし、Tell()という再生位置を返すメソッドの動きが期待したようになっていない場合があるようなので、前記事の追記とします。
まず、GetNumberFrames()は、ファイルにあるFrame数(PCMならサンプル数)を返します。Seek()は、ファイルの位置をFrameで指定するものです。これらのメソッドは、ファイルのフォーマットにあるサンプルレートがベースとなっているようです。
Tell()で位置を取得するとき、ファイル読み込み直後または、Seek()で位置を指定した直後は、ファイルのサンプルレートで値を返すようですが、再生すると再生したサンプルレートをベースにサンプル数を足しているようです。
すなわち、ファイルのサンプルレートと再生サンプルレートが異なると、正しい値が得られません。
32kHzのMP3を44.1kHzで最初から最後まで再生するとTell()の返す値は、GetNumberFrames()の返す値に44.1÷32をかけた数にほぼ近い値になります。適当な位置にSeek()してから最後まで再生するとGetNumberFrames()よりも大きい値になります。これはおそらく、Seek()で設定した値(ファイルのサンプルレート)に再生したサンプル数(再生サンプルレート)を足したいるためだと思われます。
あと、MP3などのパケットを使ったのフォーマット(non-PCM)の場合、ファイルを読み込み直後もしくは、Seek(0)で位置を最初にしたときにTell()は0を返さず、マイナスのパケットサイズを返すようです。Tell()の戻り値はUInt64なのでマイナスではなく巨大な数値になります。これについての説明は、CAAudioFile.cppのソースのコメントに書いてあります。なぜ、仕様としてそういう値を返そうとするのかは理解できませんが。
というわけで、現状バグっぽいので、CAAudilFile::Tell()はある限定された条件以外はうまく動かないのではないかということになります。
AUHALを使ってサウンドファイルを再生する [マックな開発]
AUHALでサウンドをキャプチャの仕方についての記事をずいぶん前に書いたんですが、今度は、ファイルを読み込んでAUHALを使って出力させてみたので、概要を書いてみます。PublicUtilityを使います。
ファイルの読み込み
PublicUtilityのフォルダの中にAudioFile-newというフォルダができてました。この中にあるクラスを使うととても簡単にファイルからの再生、録音ができるようです
今回は、CAAudioFileだけを使います。CAAudioFileを使うとフォーマット変換が簡単です。
- CAAudioFileでのファイルの読み込み
- Open()というメソッドを使います。引数がFSRefなので、NSStringからだと変換しなくてはなりません。
とりあえず、
というふうにしてみました(この変換処理はあまり自信がない…)。NSString* filename; CAAudioFile* audioFile; FSRef fsRef; FSPathMakeRef( (UInt8*)[filename cStringUsingEncoding: NSUTF8StringEncoding], &fsRef, NULL); audioFile->Open(fsRef);
- 出力側のフォーマットの設定
- SetClientDataFormat()かSetClientFormat()メソッドでフォーマットを設定できます。AUHALの出力エレメントの入力スコープのフォーマットを設定すればよいです。
CAAudioUnit* audioUnit; CAAudioFile* audioFile; CAStreamBasicDescription asbdInput; //AUHALのフォーマットの取得 audioUnit -> GetFormat(kAudioUnitScope_Input, 0, asbdInput); //ファイルからデータを取り出すときのフォーマットを設定 audioFile -> SetClientFormat(asbdInput);
- コールバックの処理
- 順番的に前後してしまいますがCAAudioFileからの読み込みをするので、ここで説明します。
コールバックの関数引数は、キャプチャーするときと同じものです。こちらが本来の使い方なので、コールバックされたときに引数にちゃんと値がのっかってきます。
OSStatus renderProc(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList * ioData) { CAAudioFile* audioFile=;//inRefConから設定する //ファイルの終りかチェックする //NumberFrameはどこかに保存しておいたほうがいいかも if(audioFile -> Tell() >= audioFile -> GetNumberFrames()){ //再生を止めるか、最初に戻す } //CAAudioFileからデータを取り込む audioFile -> Read(inNumberFrames, ioData); }
AudioUnit関連
これでファイル関連の処理は完了です。audioFileを使わなくなったら、Close()を呼んでdeleteはしますけど。
あとの大まかな処理は、前に書いた記事とおなじですが、ADCにある「TN2091」のドキュメントとも同じです。違うところだけ簡単に説明すると…
- enableIOのところ
- enableIOは、スコープが逆になるようにします。
あと、サンプルレートを設定するところも同じようにエレメント0で設定します。UInt32 enableIO=1; audioUnit -> SetProperty( kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO)); enableIO=0; audioUnit -> SetProperty( kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO));
- コールバックの設定
- キャプチャするときには、「kAudioOutputUnitProperty_SetInputCallback」というプロパティ値に設定しましたが、「kAudioUnitProperty_SetRenderCallback」プロパティ値で設定します(ここだけしか違わないのでコードは書きません)。
あとは、AudioOutputUnitStart()とAudioOutputUnitStop()で再生と停止を制御できます。
※CAAudioUnitの場合、AU()メソッドでAudioUnitを取り出して引数に設定する
まとめ
キャプチャしてくるときもそうですが、単にファイルをからサウンドを再生するだけなら、わざわざこんなことをしなくてもよいです。CAAudioFileを使うとファイルのフォーマットに関わらず、PCMでデータが持って来れるので、再生しながらPCMデータで何かをするというときには、便利だと思います。
Universal Binaryブリーフィング [マックな開発]
今日開催された「Universal Binaryブリーフィング」にいってきました。初台にいったのも久々ですが、かなりかわりましたねぇ。
内容は...前回のネタで書いた予想通り、「Universal Binaryプログラミングガイドライン(第2版)」を熟読していれば、特に必要ないかもという内容でした(開催していただいたアップルさんには申し訳ありませんが...)。
個人的なレベルで要約すると
バイトオーダーには気をつけろ
ネットワークやファイル出力のような、ネイティブ以外にデータを渡すときに気をつける。
Intel Binaryのビルド条件
Intel Binaryを作るには、GCC 4と10.4u.sdkが必要で、すなわちXcode2.2以降を使う。PPCのBinaryを10.2とか下位バージョンに対応するには、SDKROOT_ppcマクロのようにNAME_$(arch)というようなマクロを定義すればよい。これは「Building Universal Binaries」というドキュメントに詳細が載っている。
Accelerateフレームワークを使いなさい
Accelerate.frameworkを使うと、PPCはAltiVec、IntelはSSE系のコードで動くので依存コードにならない。
う〜ん、普通使うかなAccelerateフレームワーク。個人的にはさんざん使ってるけど。でも、よく見ると使えそうな関数もあるので、一度目を通しておくのはよいかもしれません。
他にもいろいろありがたいお話があった(ような気がする)。今日は午前中東京は台風みたいな雨が降っていたせいか、人が少なかった感じがしました。
あとは参加賞のボールペンとなまぬるいエビアンをいただいたので満足です(「えびちゃん」ならもっと嬉しかったかも)。
Xcode2 ってちょっと品質いまいち? [マックな開発]
Cocoa Objective-Cのアプリケーションを開発していて、ビルドやらデバッグやらを頻繁にやっていると、Xcodeがクラッシュすることがあるのです(Objective-Cじゃなくてもなるかもしれないけど)。デバッグが多いときに起きるような気がするのですが、発生手順が特定できずクラッシュレポートに詳しい情報が書けないのです。
デバッガーのUIとGDBとのやりとりが失敗する(タイムアウトになる)という現象もたまにあります。Xcodeを再立ち上げすれば、普通に動くのであまり問題はないです。
他には、使い方が間違っているのかバグなのかはわからないのですが、Static Libraryがリンクされないということがあります。BSD CのStatic Libraryをビルドするプロジェクトを作成して、別のプロジェクト(例えば、Objective-C++)にそのプロジェクトを追加します(.xcodeprojファイルを追加)。それで追加したプロジェクトにあるStatic Libraryを静的リンクをするようにしてビルドします(ちゃんとできます)。
しかし、Static Libraryのプロジェクトのソースをいじってコンパイルしてから、リンクするほうのプロジェクトをビルドしても、新しくなったStatic Libraryをリンクしにいってくれないのです。
最初、これに気がつかずにデバッガーがいかれたと思った(ブレイクポイントとかステップが合わない)のですが、一度Cleanしてからビルドしてから動かしたらうまくいったので、同じようにビルドして確認したらリンクされていなかったようです。
面倒ですが、リンクするほうのプロジェクトにあるどうでもよい小さなソース(例えばmain.c)を強制コンパイル(単ファイルコンパイル)をしてから、ビルドするとうまくいくのでそうしています。ちなみにZeroLinkは両方とも設定していません。
Velocity Engineを使ってチューニング [マックな開発]
Sharkを使ってチューニングをする話は前回しましたが、今作っているソフトがこてこてに信号処理をしているので、Velocity Engineを使うようにチューニングしています。
かつてvecLibといっていたライブラリ群は、Universal BinaryのためAccelerate.frameworkが用意されています。
Accelerate.frameworkでは、vDSP、vForceなどvecLibのライブラリはそのまま使えるようですが、vectorOptsが一部が使えませんでした(全部使えないかはわからない)。
基本的には、vecLibの変わりにAccelerateを使えばそのまま動いて、かつIntelアーキテクチャでも動くようになるようです(実際にIntel Macで動かしたことはないけど)。詳細は「Universal Bynaryプログラミングガイドライン」(日本語(^_^)/)のドキュメントに載ってます(ちょっとだけw)。
それとはべっこの話なんですが、vecLibで書き直したコードが同じように動かなかったのです。ソースをどう見てもバグってなかったのですが、vDSP_vsub()の演算結果がおかしかったのです。
そうしたら、「Accelerate.framework errata」というドキュメントがADCの中に密かに置いてあり、それを読むととあるアーキテクチャでとあるOSバージョンでは、vDSP_vsub()の演算結果が符号が逆転する(すなわち、関数に渡すパラメタが引くほうと引かれるほうが逆になる)ということなのです。
このドキュメントに回避コードが載っているのですが、そのうちのworkaround 1っていうほうのコードは、なんやらアーキテクチャなどをチェックしてパラメタを変えて呼び出しているのですが、このコードではうまく動きませんでした(判別できないもよう)。
workaround 2のほうは、vsmul()を使って-1をかけてからvadd()を呼ぶというかなりベタなコードですが、こちらは動きそうです。
しかしながら、Accelerateを使ってまでしてチューニングしているにもかかわらず、無駄な関数の呼び出しはありえませんので、workaround 2も却下です。
しかたがないので、引数を逆にする関数を作って、一回ためしにvsubを使って演算させた結果から、ちゃんと動くほうの関数のポインタをstaticな変数に代入して、マクロでそのポインタから関数を呼び出すようにしてしまいました(実コードは、頭にきて書いたコードなので、汚くて公開できませんw)。
大体、frameworkになっているんであるならば、とっととアップデートで直してくれればいいのにという気もしますが、現状としてこういう状態があるので直ったとしても、コードは残しておかないとだめでしょうorz。
Sharkを使ってチューニング [マックな開発]
Xcodeで開発している人であれば、1度ぐらいは使ったことがあるかもしれませんが、パフォーマンスツールのShark4を使ってチューニングをしてみようという、お話です。
Shark4は現在バージョンが4.3.2となっており、最新版は、ADCでダウンロードできます。CHUD( Computer Hardware Understanding Development) Toolsというパッケージになります。
Sharkの概要については、「Optimizing Your Application with System Trace in Shark 4」というドキュメント(いつものことだが英語)に書いてあるので、読んでいただくのがよい(ツールの画像もあるので、眺めるだけでもよいかもw)でしょう。
ツールの設定などあまり細かいことはしなくてもよいのですが、PreferencesのSearch Pathの項目に自分のソースが置いてあるフォルダ(親階層でもよい)を指定しておくと、ちゃんとソースが表示されます。
基本的な使い方は「Start」ボタンを押して、サンプリングを開始して、「Stop」でサンプリングを止めます。止めると、TimeProfileやChartが表示されるようになるので、項目を選択して表示させます。
ソースコードの表示の左に「!」がある場合には、そのコードのチューニングのヒントが表示されるので、これを参考にコードを修正していけばよいかと思います。
Sharkはこの手のツールとしてはUIが比較的シンプルですが、チューニングはこれだけでいけるのではないかと思います。
先ほどの「Optimizing Your ...」のドキュメントを読むわかるのですが、CHUD用のコードをソースに埋め込んでおけば、Sharkでイベントを参照できる機能があります。このような機能を使えば、かなりのところまでチューニングができると思います(この機能は使ったことないのですけど)。
Public Utilityを使ってサウンドをキャプチャする [マックな開発]
Public UtilityでCoreAudioを使う話は前回書いたので、サウンドのキャプチャをするにはどうするかというお話です。
CoreAudioのAUHALを使って、デバイスからPCMデータを取ってくる方法については、ADCのテクニカルノートのTN2091に載っています。このTNには、サンプルコードも含まれているので、コピってつなげていけば動くと思います。
このサンプルコードをPublic Utilityを使うように書き換えたとすると...
Audio コンポーネント
ComponentをComponentDescriptionで持ってくる所は、CAComponentとCAComponentDescriptionが使えます。それぞれ、コンストラクタの引数に渡して、インスタンスを作成します。
OpenAComponentは、CAAudioUnit::Open()を呼び出すことで実行されます。
CAAudioUnit::Open()に渡すCAAudioUnitのインスタンスは、事前にnewで作っておきます。
Enabling IO
CAAudioUnitのSetProperty()で設定します。
Audio Deviceの割当
デフォルトのDeviceIDは、CAAudioHardwareSystem::GetDefaultDevice()で持ってきて、これもCAAudioUnitのSetProperty()でデバイスを設定します。
Audio Data Format
CAAudioUnitのGetFormat()でデバイス側のフォーマットをkAudioUnitScope_Inputと1(InputのElement)で取得して、SetFormat()をkAudioUnitScope_Outputと1で設定します(設定というか、デバイスのサンプルレートに合わせるといった感じか?)。
サンプルコードにあるCAStreamBasicDescriptionもPublic Utilityに含まれています。
Channel Mapping
必要があったらやってください(普通はしなくてもよいかな)。
Inputのコールバック
これもCAAudioUnitのSetProperty()でkAudioOutputUnitProperty_SetInputCallbackを設定します。
初期化と実行
CAAudioUnitのInitialize()で初期化して、キャプチャの開始は、AudioOutputUnitStart()にCAAudioUnitのAU()の戻り値を渡します。
コールバック
PCMデータを持ってくるには、CAAudioUnitののRender()を呼び出します。
BufferListですが、CAAudioBufferList::Create()にチャンネル数を渡せば、作成できます。BufferはCAAudioHardwareDevice::GetIOBufferSize()でサイズを取得して、mallocやnew float[]やNSMutableDataなどでメモリを確保します。
CAAudioBufferListにあるmBuffers[]に確保したメモリを設定します。mBuffers[]はCreate()で設定したチャンネル数の個数設定します。
停止と終了処理
AudioOutputUnitStop()にCAAudioUnitのAU()の戻り値を渡して停止して、Uninitialize()を実行したのち、CAAudioUnitのインスタンスを破棄します。
ということで
正直言ってしまうと、サンプルコードと比べてもあまりコードの量も変わらず、Public Utilityを使うご利益はあるんだっけ?という感じもしないでもありません。
前回申し上げた通り、基本的にはCarbon的APIをC++クラス化しただけなので、CoreAudioのAPIとの比較だとあまり変わりはないのですが、データ構造へのアクセスやエラー処理などがCarbonなコードよりは、すっきりしているはずです。
CoreAudioを使う。 [マックな開発]
相変わらず、更新頻度がかなり低いですが、忘れた頃に忘れないように書いておきますw。
まえにサウンドのキャプチャの話をしたんですが、その後サンプルから離脱して、自前で1から書いて、とりあえず動いたので書いておきます。
Public Utility
CoreAudioは、いたって一般的なCarbonのAPIの形式を持っているため、サイズをもらって、メモリを確保して、またAPIに渡して...という儀式的なコードを書くのを低減させてくれるため(?)にPublicUtilityをいうC++のクラス群が開発環境のExamples/CoreAudioにあります。
このクラスたちについての詳しい情報はあまりないのですが、メソッドをみると基本的にはCoreAudioのAPIと類似しており、どこぞのFoundationClassと同じようにw、CのAPIをC++でラップしてあるだけです。
逆にいえば、CoreAudioのAPIのドキュメントを参照しながら、C++のメソッドを探して使えば、いいわけです。
ハードウェア関連のクラス
CoreAudioのAPIでのAudioHardware...のAPI群や kAudioDevice...でアクセスするプロパティ群は、CAAudioHardwareSystemとCAAudioHardwareDeviceのクラスになっています。...Systemのほうは、Staticなメソッドばかりなので、単純にAPIのラッパーとなっているようです。
...Deviceのほうは、「AudioDeviceID」をメンバー変数にもっていて、ほとんどがCarbonでいうところのプロパティへのアクセスが主になっているようです。
キャプチャするためにつかうクラス
いろいろやっていると、ほとんどのクラスを使うはめになるのですがw、最低限使うのは、CAAudioUnitと思われます。CAAudioUnitは、AudioUnit...のAPI群とkAudioUnit...のプロパティ群にアクセスするためのクラスで、このクラスを使うとかなりコーディングは楽になります。
CAStreamBasicDescriptionなどは、構造体を継承したクラスでいわゆるヘルパー的なメソッドがあって便利(そう)ですが、単純なPCMデータのキャプチャでは、サンプルレートとチャンネル数とかぐらいにアクセスできればいいので、使わなくてもなんとかなります。
というわけで
CoreAudioを使おうとしていて、C++を使ってもかまわなければ、PublicUtilityを使ったほうが、かなり楽になるになるのではないかと思います。
いずれにしろ、CoreAudioのAPIリファレンスは読まなくてはならないのですが...