月別アーカイブ: 2009年11月

iPhone Core Audio プログラミング

ちょっと記事にするには乗り遅れた感がありますが、「iPhone Core Audio プログラミング」という、iPhoneのオーディオプログラミングに特化した本が発売されています。今はもうだいたいどこの本屋さんいっても置いてありますね。詳しい内容については筆者の永野さんのサイトのページ「iPhone Core Audioプログラミング」(書籍) 発売へ。

iPhoneのオーディオのAPIについては、これ一冊に日本語でほぼ全て書いてあります。日本語です!iPhone Dev Centerにも「Core Audio 概要」とか「Audio Session Programming Guide」とかの日本語訳はありますが、Apple純正という安心感はあるものの翻訳なので、ネイティブな日本人が書いた文章というのはやっぱり分かりやすいです。もう僕がiPhoneの開発ネタを書く事もないかなぁなんて思ったり。

基本的にはAPIの解説になるので、本の最後の章にアプリのサンプルはいくつかありますが、実際に自分好みのオーディオアプリを作り始めてみると壁にぶち当たると思います(主にパフォーマンスで)。が、まあそれはトライアンドエラーで頑張りましょうってことで。作るアプリの内容とか機種とかによっても引っかかる部分が違いますし。なぜか新しい機種の方が負荷に弱かったりするときもあるんですよねぇ。

オーディオのプログラミングっていうと、オーディオの再生とか録音とかシンセサイズとかってところに興味がいってしまいがちだと思いますが、iPhoneに関していえば最も重要なのはAudio Sessionです!ちょっとでもiPhoneでオーディオ鳴らすなら、Chapter4と7は熟読してください!知っていてあえて使わないのと、何となくめんどくさいから読まないで知らずに使えないのとでは大きな差があります。まぁ、App Storeに出さずに自分のデバイスだけで動かせればいいっていうなら話は別ですけどね。他のChapterは気になるところだけピックアップして読めば良いと思います。

値段は4,200円と、他の入門書と比べてやや高いですが、それだけの価値はあると思います。僕はオーディオメインでプログラミングを勉強してますが、やっぱりその中でも興味があるところに偏って調べていたりするので、読んでみるといろいろ知らないところがたくさんあります。今後iPhoneのオーディオで、忘れていたり、何か新たに調べたくなったときに、役立ってくれる事は確実です。

Grand Central Dispatch その7 おまけ

さらに今更ながら、ASCII.jpさんの「マルチコア時代の新機軸! Snow LeopardのGCD」に、わりと一般向けの説明から始まって、コードを使用したプログラマ向けの説明までしているページがありました。とりあえずここらへんで基本知識を得てから、いろいろ試した方が良かったのかもしれません。

その記事を見ていて改めて発見したのは、dispatch_get_main_queueで、メインスレッドで実行されるキューが取得できるという事です。なるほど、これがあればperformSelectorOnMainThreadを呼ばなくてもディスパッチで呼び出しが完結できます。前回のタイマーとかはメインキュー使う方が良い気がします。

でも、こういった解説記事の中でディスパッチソースにまで触れられているものってあんまり見かけないですね。ディスパッチさえあればアプリの基本的な仕組みが作れてしまうみたいな。ということで、ソース系でもうひとつ調べてみました。

やってみたかったのは、ディスパッチでバッチ処理的な事をさせておいている途中で、キャンセルしたいって言う事です。すくなくとも僕の認識では、dispatch_asyncでブロックをキューに渡してしまうとキャンセルできない様なので、これをやりたいとおもって見つけたのがディスパッチソースの中のDISPATCH_SOURCE_TYPE_DATA_ADDというやつです。

それを使って書いてみたソースがこちらです。今回は自分でキャンセルしてみたいので、Cocoa Applicationでプロジェクトを作ってAppDelegateを以下のように変更し、Interface Builderでウィンドウにボタンをひとつ作ってAppDelegateのcancelにつなげてください。

//
// AppDelegate.h
//
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <nsapplicationDelegate> {
    NSWindow *window;
    dispatch_source_t _source;
}
@property (assign) IBOutlet NSWindow *window;
- (IBAction)cancel:(id)sender;
@end
//
// AppDelegate.m
//
#import "AppDelegate.h"
@implementation AppDelegate
@synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    dispatch_queue_t queue = dispatch_queue_create("test", NULL);
    _source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue);
    
    __block int count = 0;
    
    dispatch_source_set_event_handler(_source, ^{
        count++;
        printf("call %d\n", count);
        usleep(100000);
        dispatch_source_merge_data(_source, 1);
    });
    
    dispatch_source_set_cancel_handler(_source, ^{
        printf("end\n");
        dispatch_release(_source);
        _source = NULL;
        dispatch_release(queue);
        exit(0);
    });
    
    dispatch_source_merge_data(_source, 1);
    dispatch_resume(_source);
}
- (IBAction)cancel:(id)sender
{
    dispatch_source_cancel(_source);
}
@end

実行すると、event handlerのブロックが繰り返し呼ばれてログにカウントが表示されます。キャンセルボタンを押すと終了します。

DATA_ADDの使い方としては、dispatch_source_merge_data()の2つ目の引数で1以上の値を与えて呼ぶと、dispatch_source_set_event_handlerで登録したブロックが呼び出されるという感じになります。このサンプルでは、event_handlerのブロックの中から直接merge_dataを呼び出して処理を繰り返すようにしています。

merge_dataに0を渡してもブロックが呼ばれない事からも分かるように、たぶん本来はこれで処理自体をするものではなく、こちらのページにのっているように、dispatch_applyとかでやっている処理の進捗の量をmerge_dataの引数で受け取ってインジケータに表示するような用途に使うものだと思われます。