2014年7月28日月曜日

iPhone で Pure Data を使った音叉を鳴らしてみる

スマホを楽器みたいにして鳴らしてみたくて調べてみたら、PureDataってのを見つけた。

別にスマホに限った技術ではないが、これを使えば、音を生成する部分を抽象的なモデルにして切り出すことができ、これを操作するためのライブラリである libpd を介して、いろんなプラットフォーム上で統一的に操作できるらしい。Android でも iOS でもいけるようだ。

というわけで、試しに Pure Dataを iPhoneで動かしてみる。

お題は音叉とした。iPhoneを膝に当てたら鳴るようにしたら、いかにも本物の音叉っぽいが、単なるボタン押下に留めておいた。実験なので簡単なコードにしたいし、iPhone 買ったばかりで、どの程度の衝撃に耐えられるか分からなくて怖いしな。

■ Pure Data のインストール
PureDataのダウンロードサイトにいくと、Vanilla版ってのがある。そこから mac版をダウンロードして普通にインストール。

音を生成する仕組みをモデル化した「パッチ(patch)」を作成したり、それを鳴らしてみたりできる、Pure Data の開発ツール。

■ fork.pdの作成

・Command+Nで新しい”patch”を作成する。この patch単位で作成した"*.pd"ファイルを、後でプログラムからリソースとして読み込んで操作することになる。

・下図のように、要素を配置する

  1. 左上の[○]は、メニューの Put > Bangから生成する。さらに、この要素上のコンテキストメニューから Propertiesを開いて、Receive Symbolに forkと入力しておく。この文字列でプログラムからレシーバを識別して操作する。
  2. その下の [1 10, 0 3000 2000]は、10msかけて0から1になって、その後、2000ms待ってから、3000msかけて0に戻るというエンベロープの指定で、Put > Messageで生成。
  3. [vline~]は Put > Objectで生成。 ②の指定に基づいたエンベロープを生成。
  4. [osc~ 440]は Put > Object。 440Hzの正弦波発振器。
  5. [*~]も Put > Object。 ④の正弦波を③のエンベロープに整形するオブジェクト。
  6. [dac~]も Put > Object。 デジタル信号をアナログにして、最終的に音を出力するオブジェクト。
要素を配置したら、上から下に連結していく。始点の要素の下側の角(outlet)から、終点の要素の上側の角(inlet)にドラッグすればつながる。

この時点で、①のBangか、②のMessageをクリックすれば、音叉っぽい感じで音が鳴るのを確認できる。

■ libpdのダウンロード
$ git clone git://github.com/libpd/pd-for-ios.git
$ cd pd-for-ios
$ git submodule init
$ git submodule update
pd-for-ios下に、libpd 本体とサンプルプロジェクトが入ってる。

■ プロジェクトの作成
以下の指定でプロジェクトを作成する。
  • Single View Applicationを選択
  • Product Name: Fork
  • Class Prefix: なし
  • Device: iPhone
  • その他、適当

■ プロジェクトの作成
  • libpd/libpd.xcodeprojを、"Add Files To〜"でプロジェクトに追加する。サブプロジェクトになるので、一応、TARGETS に libpd-iosがあるのを確認しておく。
  • プロジェクトの Build Phases > Target Dependencies に libpd-iosを追加。
  • プロジェクトの Build Settings > Search Paths > Header Search Paths に libpd へのパスをrecursive で追加。
  • プロジェクトの Build Settings > Architectures > Architectures の $(ARCHS_STANDARD)を$(ARCHS_STANDARD_32_BIT)に変更(これをやらないとリンカエラーになる)。
  • General > Linked Frameworks and Libraries で、libpd-ios.a を追加
  • fork.pdをインポートする. プロジェクトの Build Phases > Copy Bundle Resources で、fork.pdが含まれていることを確認しておく。
■ AppDelegate.h の編集
ヘッダのインポートと@propertyの追加
#import <UIKit/UIKit.h>
#import "PdAudioController.h"

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic, readonly) PdAudioController *audioController;

@end

■ AppDelegate.m の編集
@implementation の冒頭に以下を追加
@synthesize audioController = _audioController;
・以下のメソッドを編集
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    _audioController = [[PdAudioController alloc] init];
    if ([self.audioController configureAmbientWithSampleRate:44100 numberChannels:2 mixingEnabled:YES] != PdAudioOK) {
        NSLog(@"failed to initialize audio components");
    }
    return YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
    self.audioController.active = YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
    self.audioController.active = NO;
}


■ ViewController.mの編集
・まず、UIKit のインポートの下で、"PdBase.h"をインポートしておく。
#import "PdBase.h"

・次に fork.pd を開くコード。

※ openFile で開いた patchを後で閉じるためには、どこかで保持しておく必要があるが、ここでは持ちっぱなしにしておく。
- (void)viewDidLoad {
    [super viewDidLoad];
    if (![PdBase openFile:@"fork.pd"
                     path:[[NSBundle mainBundle] resourcePath]]) {
        NSLog(@"Failed to open patch!");
    }
}

次にボタンとそのイベントハンドラ
  • Main.storyboard を選択して、画面の真ん中に適当にボタンを置く。Title は “A”とかにしておく。
  • Assistant Editor にして、ViewController.m への Ctrl+ドラッグで Touch Down のイベントハンドラを作る。
  • 中身は以下のような感じ
    - (IBAction)bang:(id)sender {
        [PdBase sendBangToReceiver:@"fork"];
    }
    
これで一通り完成。

■ 動かしてみる

味も素っ気もない画面だが、真ん中の[A]ボタンを押せば、Aの音がポーンとなる。キャプチャ画像は iOSシミュレータだけど、実機の iPhone 5sでも問題なく鳴る。