月曜日, 6月 29, 2015

iOS開発再入門 4 pd-for-iosをSwiftで使う

AudioKitやめる

AudioKit、忙しさにかまけているうちに2.1までバージョンが進み、軽い気持ちでアップデートしたらAPIが変わっていて、最新版に合わせて書き直したんだけど音が変に割れてしまうようになったので、使うのをやめた。

Pure Data for iOS

pd-for-iosはご存知Pure DataをiOSで使えるようにしたもので、Pure Dataの資産がそのままiOSで活かせるめちゃめちゃGame changingなライブラリ。デスクトップで使えるPd-extendedとは違うので、gateがないとか諸々注意する点はあるけれども、試した範囲ではパフォーマンスも問題なく、非常に便利。音の生成は全部Pure Dataに任せてしまって、XcodeではUIの部分だけ書けばいい。

  1. githubからcloneなりダウンロードなりで入手。
  2. Xcodeプロジェクトを作成(言語はSwift)し、プロジェクトファイルにFinderからlibpd.xcodeprojをドラッグ&ドロップ。
  3. プロジェクトファイルを選択し「TARGETS」の「Build Settings」にある「Search Paths」の「User Header Search Paths」にlibpdまでのパスを追加(検索窓で「user header」と入力すると早い)。$(SRCROOT)がプロジェクトファイルがあるフォルダを表す変数なので、そこから相対パスで指定するといい。ドロップダウンは「recursive」にする。
  4. 「Build Phases」にある「Link Binary With Libraries」の+ボタンを押して「libpd-ios.a」を追加(赤いままだが問題ないらしい)。

pd-for-iosはObjective-Cで書かれているので、Swiftで使うにはブリッジが必要。(プロジェクト名)-Bridging-Header.hというファイルを作成し、

#import "PdAudioController.h"
#import "PdDispatcher.h"
と書く。

「TARGETS」の「Build Settings」に「Objective-C Bridging Header」という項目があるので、ブリッジングヘッダファイルのパスを追記する。これ忘れがち。

正しく設定するとオブジェクト名やメソッド名がSwiftに変換された形で補完候補に出てくるようになる。enumがそのままだと受け取れないみたいだけど、いったん放置。

クラス変数として

var audioController: PdAudioController!
を宣言し、

AppDelegate.swiftの最初に呼ばれるメソッド内でサンプリングレートなどを設定。

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    self.audioController = PdAudioController()
    self.audioController.configureAmbient(withSampleRate: 44100, numberChannels: 2, mixingEnabled: true)
    
    return true
}

アプリがアクティブでなくなった場合と、

func applicationWillResignActive(application: UIApplication) {
    self.audioController.isActive = false
}
アクティブになった場合の状態を切り替える。
func applicationDidBecomeActive(application: UIApplication) {
    self.audioController.isActive = true
}

ViewController.swiftでは、viewDidLoadメソッド内などで

    let dispatcher = PdDispatcher()
    PdBase.setDelegate(dispatcher)
    PdBase.openFile("dropophone.pd", path: NSBundle.mainBundle().resourcePath)
    PdBase.sendBangToReceiver("start")
とすれば、pd側のstartという名前のreceiveオブジェクトでBangを受け取れる。

お互いのやり取りはここを参考にする。

「readsf~」などでサウンドファイルを読み込む場合は、Xcodeでのリソースパスに準じる。wavじゃないと駄目だったり、afconvertで作ったwavだとヘッダが不正とか言われて再生できなかったのでiTunesで作り直したりして、情弱な感じのはまり方をした。

Dropophoneの音を鳴らす仕組み自体はpdで簡単に実装できた。あとはレイアウトとアニメーションなんだけど、これがまた曲者。

参考

Making Musical Appsという本を読んだ。Safari Books Onlineにもある。

YouTubeでLearning libPD for iOSというチュートリアルがあって、親切。インストールの手順など多少違うので後で確認する。