iA


SFCゲストレクチャー 2 「デザイン戦略 (デジタルサウンド)」

掲載が遅れてしまいましたが、先日のSFCでのゲストレクチャーの記録、その2。恩師である岩竹徹先生の「デザイン戦略 (デジタルサウンド)」で1コマ、ゲストでレクチャーしました。

内容は、こちらもopenFrameworksの紹介。こちらでは、openFrameworksを使うとこれだけパワフルなことができる、というデモ的な内容が主になります。最後に最近やっているAudio Visualなパフォーマンスのためのプログラムの試作版をデモしました。

こちらの講義も、スライド、サンプルプログラム、また最後に行ったデモのプログラムに関してもダウンロードできるようにしました。


SFC「複雑性の数理」ゲストレクチャー:生成的な形をつくる

懐かしの母校SFCでゲストレクチャーしてきました。井庭崇さん、すばらしい機会を設けていだだき、ありがとうございます!!

講義の中で解説したプログラムのソースコードは全てダウンロード可能です。

openFrameworksやXCodeを持っていない方で、プログラムの実行ファイルのみ確認したい方は、バイナリ版を用意しました。

Windows版は、現在コンパイルでひっかかる部分があり、現在調整中です。すみません!!


目標達成状況と反省の弁

DSC_2486

ニセコに来て早くも一ヶ月。そろそろこの夢の生活も終わり、現実に復帰しなければいけない。ここに着いてすぐに書いたブログのエントリーでいろいろ目標を設定したのだけれど、その達成状況をふりかえってみる。

  • 成績票の提出 – 多摩美と千葉商科大については既に完了。芸大はまだ締切先なので、これから
  • 来学期の授業のアウトラインを考えておく – 正直、まだ内容かたまらず。もう新学期が始まってしまう…
  • TV禁止 – これは、完全にクリア。こちらに来てから、本当に1分もTVを見ていない。強いていえば、入った食堂でつけてあったTVの画面を偶然見たくらい
  • ダイエット – 最初の週に無理して足が痛くなってから、あんまり運動しなくなってしまった。食べ物が美味いので、逆に太ったかも…
  • iPhoneもしくはiPadアプリをリリース – 最低1本、できれば複数のアプリをリリースしたい – iPodに入っている曲をビジュアライズするアプリを近日公開予定!!
  • ブログ更新 – 毎日更新はできなかった。こっちに来てから更新したエントリーは合計15本。2日に1本ペース。

こうして眺めてみると、達成状況はだいたい50%といったところ。環境が良すぎて、なんだかすっかりのんびりムードになってしまった。

考えてみると、大学院を出て就職して何度か転職してフリーランスになって気がつくと15年近く経っているが、こうして1ヶ月も休暇をとったのは初めて。たぶん一生忘れることのない良い思い出になったと思う。ニセコは本当に良い場所だった。また1年がんばって仕事して、来年も来れるといいなあ。あと冬にスキーをしに来ても全然イメージが変わりそう。これもいつか来たい。

ああ、現実に復帰したくない…


夢の生活もそろそろ終わり

DSC_2276

ここ一週間は、あちこち出歩いていて、更新がすっかり停滞してしまった…

この夢の北海道生活も、今週で終わり。2日には帰京します。あー、楽しかったけど、まだまだ滞在したい気分。最後に、勉強の成果のiPhoneアプリの申請をしたい。そして、停滞してしまっている諸々のタスクを消化していかないと。

夏休みの宿題に終われる小学生の気分。


openFrameworksのプロジェクトをXCode4 + Gitでバージョン管理

あ、いかんBlogの更新をサボってしまった。というわけで、コネタ的な更新。

XCode4から、ファイルのバージョン管理システムが統合され、とても便利になった。SubversionだけでなくGitにも対応しているのが嬉しい。そんなわけで、これを機会に自分自身のプロジェクトのバージョン管理をSubversionからGitに移行しようということで試してみた。

通常であれば、XCode4のプロジェクトを新規に作成する際にSource Controlのチェックボックスをチェックするだけ。とても簡単。

XcodeScreenSnapz001.png

ところがoFの場合は用意さているXCodeのプロジェクトファイルをコピーして始める方法が一般的なので、この方法がつかえない。既存のプロジェクトをGitの管理下に移行する必要がある。といっても、手順はさほど複雑ではない。まずいつものようにemptyExampleをフォルダごとコピーする。例えば、emptyExampleGitといった名前のフォルダにする。ターミナルを立ち上げて、このemptyExampleGitフォルダまで移動。例えばこんな感じ。

% cd /Users/tado/Documents/of_preRelease_v007_iphone/apps/myApps/emptyExampleGit/

ここに、gitの環境を構築する。まずは以下のコマンドでGitを初期化して、.gitディレクトリを作成する。

% git init

普通はこのままでも良いのかもしれないが、XCodeの設定ファイルなどはGitで無視されるように設定ファイルを書いたほうがより便利かもしれない。下記のようなファイルをテキストエディタで作成し、「.gitignore」というファイル名で保存する。

# hidden/temp files
.DS_Store
*.swp
*~.nib
 
# Build dir
build/
 
# Xcode project files except for the project file
*.xcodeproj/
!*.xcodeproj/project.pbxproj

もしその他にもGit管理下から外したいファイルがある場合はこのファイルを適宜編集する。これで準備ができたので、カレントディレクトリの内容を全てGitに追加。追加された内容を確認する。

% git add .
% git status

問題ないようであれば、最初のコミットを行う。

% git commit -m "initial commit."

これでこのemptyExampleGitはGitの管理下に入ったはず。このままプロジェクトファイルを起動するとGitでバージョン管理された状態で使用できる。

しかし毎回この操作をするのもちょっと面倒。なので、新規にプロジェクトを作成する際には、このemptyExampleGitプロジェクトのクローンを作成して始めるのが楽ではないかと思う。(※ このやりかたがGitの作法的に正しいかどうか、ちょっと自信なし。問題あるようなら指摘してください…)

クローンを作成するのはXCode4を使えば簡単。まずXCodeを起動して、「Welcome to XCode」の画面を開く。ここで「Connect to a repository」を選択する。

XcodeScreenSnapz002.png

次に表示される画面のlocationに、先程作成したemptyExampleGitディレクトリの場所をフルパスで記入。

XcodeScreenSnapz003.png

プロジェクト名を指定して、バージョン管理システムの種類をGitに指定。あとはプロジェクトの保存場所を聞かれるので、プロジェクト名を新規に決めてoFのappディレクトリの中のサブディレクトリ (※ myAppなど) に保存する。

XcodeScreenSnapz004.png

これでプロジェクトファイルを開くと、Gitでバージョン管理された状態になっているはず。便利!!

XcodeScreenSnapz006.png

オーガナイザーにもきちんと登録されています。

XcodeScreenSnapz007.png

お試しを!


着々と進行中

2sec0.png

前回のiPhoneのiPodから音楽データを読み込む仕組みをつかって、ビジュアライザのアプリを作成中。土台の部分が徐々にできつつある。コーディング作業が俄然楽しくなってきた。複数のモードを切り替えられるようにして、読み込みの部分などのバグが安定したらアプリの申請も視野に入れながら進めていきたい。

今日はニセコアンヌプリのサマーゴンドラに乗って、山の中腹にあるレストハウスでノマドワークしてみた。見晴らしが素晴しく、普段あまり刺激されていない脳の領域が活発化する感じで、作業効率がなかなか良かった。帰りは調子にのって徒歩で下山。途中「熊が出没したので立ち入り禁止」という張り紙などがあり、北海道感満点。


iPodライブラリ内の曲をopenFrameworksで読み込んでサウンドレベルを取得する

picker.png

iPhone開発修行、ちょっと進展したのでご報告。

現在オーディオ・ビジュアル表現のためのアプリを作成したいと考えていて、やはりこういったジャンルはiPhone用のopenFrameworksを利用するほうが効率が良さそう。openFrameworksでサウンドを活用する場合、あらかじめリソースの中に.caf形式のサウンドファイルを入れておいて、それを参照するやり方が一番標準的な方法だ。また、マイク入力を利用してリアルタイムのサウンドストリームを利用することも可能となっている。

ところが、openFrameworksはそのままではiPhone内のiPodライブラリ内の曲のデータは参照できない。せっかくなら、自分のiPhoneに入れた音楽を使用していろいろ面白いビジュアライズをやってみたい。そんなわけで、iOSのSDKを直に利用してiPodライブラリ内のサウンドデータを参照する方法がないものか探ってみた。

iPodライブラリの内容を参照し、曲をピックアップする方法は比較的簡単だった。iOS SKDにはMPMediaPickerControllerというiPodライブラリの曲を選択するためのUIを提供するクラスがある。これを活用するとすぐに高機能な曲目ピッカーが活用できる。openFrameworksでMPMediaPickerControllerを利用するには、以前このブログの「iOSのGUIをopenFrameworksのプロジェクトに追加する」で紹介した方法で、oFにUIViewControllerのサブクラスを追加して、そのUIViewControllerのサブクラスにMPMediaPickerControllerのインスタンスを生成して、delegateしてあげれば良い。MPMediaPickerControllerを利用するには、プロジェクトにMediaPlayer.frameworkを追加する必要がある。また、MPMediaPickerControllerはiOSシミュレータでは動かすことはできない。必ず実機で動かすようにして欲しい。

ここで取得したアイテムはMPMediaItemクラスのインスタンスとして取得される。これを、MPMusicPlayerControllerというプレイヤーのクラスに読みこんでplayしてあげれば、それだけで曲は再生できる。非常に楽ちん。

ところが、MPMusicPlayerControllerは曲の再生はポーズなどのコントロールはできるものの、サウンドデータそのものには全くアクセスできない。ここでしばらく悩んでしまった。いろいろググったところ、素晴しい記事を発見。

詳細はこの2つの記事を参考にしてほしいのだが、ポイントとしては、MPMediaItemからiPodのリソースのURLを取得した上で、普通のオーディオファイルとして書きだしている。

書き出した生のオーディオデータを再生するには、AVAudioPlayerクラスを活用するのが便利なようだ。AVAudioPlayerでサウンドを再生し、そのレベルを取得するメソッドを追加してその値をoFのtestAppから見られるようにしてみた。これで、あとはoFの方でいろいろ工夫すれば、iPodの音楽を活用したオーディオビジュアルなアプリができるはず。

ここまでの内容のソースコードにまとめてみた。MPPikerクラスが、iPodのライブラリを読み込んで再生する部分。

testApp.h

#pragma once

#import <MediaPlayer/MediaPlayer.h>
#import <AVFoundation/AVFoundation.h>

#include "ofMain.h"
#include "ofxiPhone.h"
#include "ofxiPhoneExtras.h"

class testApp : public ofxiPhoneApp {
	
public:
	void setup();
	void update();
	void draw();
	void exit();
	
	void touchDown(ofTouchEventArgs &amp;amp;touch);
	void touchMoved(ofTouchEventArgs &amp;amp;touch);
	void touchUp(ofTouchEventArgs &amp;amp;touch);
	void touchDoubleTap(ofTouchEventArgs &amp;amp;touch);
	void touchCancelled(ofTouchEventArgs &amp;amp;touch);

	void lostFocus();
	void gotFocus();
	void gotMemoryWarning();
	void deviceOrientationChanged(int newOrientation);
};

testApp.mm

#include "testApp.h"
#include "MPPicker.h"

MPPicker *picker;

void testApp::setup(){
    ofSetFrameRate(30);
    ofEnableAlphaBlending();
    ofSetCircleResolution(32);
	ofRegisterTouchEvents(this);
	ofxAccelerometer.setup();
	ofxiPhoneAlerts.addListener(this);
    ofBackground(0, 0, 0);
    picker = [[MPPicker alloc] initWithNibName:@"MPPicker" bundle:nil];
    [ofxiPhoneGetUIWindow() addSubview:picker.view];
}

void testApp::update(){

}

void testApp::draw(){
    //左右のサウンドレベルを取得
    float levelL = [picker getLevelWithChannel:0];
    float levelR = [picker getLevelWithChannel:1];

    //サウンドレベル半径にして円を描画
    ofSetColor(0, 63, 255, 127);
    ofTranslate(ofGetWidth()/3, ofGetHeight()/2);
    ofEllipse(0, 0, levelL * 400.0, levelL * 400.0);
    ofTranslate(ofGetWidth()/3, 0);
    ofEllipse(0, 0, levelR * 400.0, levelR * 400.0);
    
}

void testApp::exit(){ }
void testApp::touchDown(ofTouchEventArgs &amp;amp;touch){ }
void testApp::touchMoved(ofTouchEventArgs &amp;amp;touch){ }
void testApp::touchUp(ofTouchEventArgs &amp;amp;touch){ }
void testApp::touchDoubleTap(ofTouchEventArgs &amp;amp;touch){ }
void testApp::lostFocus(){ }
void testApp::gotFocus(){ }
void testApp::gotMemoryWarning(){ }
void testApp::deviceOrientationChanged(int newOrientation){ }
void testApp::touchCancelled(ofTouchEventArgs&amp;amp; args){ }

MPPicker.h

#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
#import <AVFoundation/AVFoundation.h>

#include "testApp.h"

@interface MPPicker : UIViewController <MPMediaPickerControllerDelegate, UITableViewDelegate, AVAudioPlayerDelegate> {
    testApp *myApp;
    AVAudioPlayer *player;
    IBOutlet UIBarButtonItem *showPickerButton;
    IBOutlet UIBarButtonItem *playButton;
    IBOutlet UIBarButtonItem *pauseButton;
}

-(IBAction)showMediaPicker:(id)sender;
-(IBAction)playMediaItem:(id)sender;
-(IBAction)pauseMediaItem:(id)sender;
-(float)getLevelWithChannel:(int)ch;

@end

MPPicker.mm

#import "MPPicker.h"

@implementation MPPicker

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
    }
    return self;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}


// iPodの曲目のピッカーを表示
-(IBAction)showMediaPicker:(id)sender
{
    MPMediaPickerController *picker = [[MPMediaPickerController alloc] initWithMediaTypes: MPMediaTypeAnyAudio];
    [picker setDelegate: self];
    [picker setAllowsPickingMultipleItems: NO];
    picker.prompt = NSLocalizedString (@"Add songs to play", "Prompt in media item picker");

    // pickerをModalViewに表示
    [self presentModalViewController: picker animated: YES];
    [picker release];
    [player stop];
}

// Pickerで取得したデータをファイルに書き出し
- (void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection
{
    MPMediaItem *item = [mediaItemCollection.items lastObject];
    NSURL *url = [item valueForProperty:MPMediaItemPropertyAssetURL];
    AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:url options:nil];
    
    AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]
                                           initWithAsset:urlAsset
                                           presetName:AVAssetExportPresetPassthrough];
    
    
    NSArray *tracks = [urlAsset tracksWithMediaType:AVMediaTypeAudio];
    AVAssetTrack *track = [tracks objectAtIndex:0];
    id desc = [track.formatDescriptions objectAtIndex:0];
    const AudioStreamBasicDescription *audioDesc = CMAudioFormatDescriptionGetStreamBasicDescription((CMAudioFormatDescriptionRef)desc);
    FourCharCode formatID = audioDesc->mFormatID;
    
    NSString *fileType = nil;
    NSString *ex = nil;
    
    switch (formatID) {
            
        case kAudioFormatLinearPCM:
        {
            UInt32 flags = audioDesc->mFormatFlags;
            if (flags &amp;amp; kAudioFormatFlagIsBigEndian) {
                fileType = @"public.aiff-audio";
                ex = @"aif";
            } else {
                fileType = @"com.microsoft.waveform-audio";
                ex = @"wav";
            }
        }
            break;
            
        case kAudioFormatMPEGLayer3:
            fileType = @"com.apple.quicktime-movie";
            ex = @"mp3";
            break;
            
        case kAudioFormatMPEG4AAC:
            fileType = @"com.apple.m4a-audio";
            ex = @"m4a";
            break;
            
        case kAudioFormatAppleLossless:
            fileType = @"com.apple.m4a-audio";
            ex = @"m4a";
            break;
            
        default:
            break;
    }
    
    exportSession.outputFileType = fileType;
    
    NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString *filePath = [[docDir stringByAppendingPathComponent:[item valueForProperty:MPMediaItemPropertyTitle]] stringByAppendingPathExtension:ex];
    exportSession.outputURL = [NSURL fileURLWithPath:filePath];
        
    [exportSession exportAsynchronouslyWithCompletionHandler:^{
        
        if (exportSession.status == AVAssetExportSessionStatusCompleted) {
            NSLog(@"export session completed");
            [exportSession release];
            player = [[AVAudioPlayer alloc] initWithContentsOfURL:exportSession.outputURL error:nil];
            player.meteringEnabled = YES;
            [player play];
        } else {
            NSLog(@"export session error");
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Export session error\nPlease select again" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
            [alert show];
        }
    }];
    [self dismissModalViewControllerAnimated:YES];
}

// 曲をキャンセルした時
- (void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker {
	[self dismissModalViewControllerAnimated:YES];
}

// 曲を再生
-(IBAction)playMediaItem:(id)sender
{
    [player play];
}

// 曲をポーズ
-(IBAction)pauseMediaItem:(id)sender
{
    [player pause];
}

// チャンネルを指定して、再生レベルを取得
-(float)getLevelWithChannel:(int)ch
{
    if(player.playing){
        [player updateMeters];
        float db = [player averagePowerForChannel:ch];
        float power = pow(10, (0.05 * db));
        return power;
    } else {
        return nil;
    }
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
    myApp = (testApp*)ofGetAppPtr();
    player.delegate = self;
}


- (void)viewDidUnload
{
    [super viewDidUnload];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    //return (interfaceOrientation == UIInterfaceOrientationPortrait);
    return NO;
}

@end

Box2Dと組みあわせて、物理シミュレーションを使用したビジュアライザーや、ofImagePickerを使用してカメラ入力の画像を音でモジュレーションするアプリなどいろいろ夢が膨らむ。もう少しoFとの連携を強化したものを、addonにして配布したら感謝されるんだろうか? ofxMPMediaPickerとか。

プロジェクトをダウンロード

下記のリンクよりプロジェクトファイル一式がダウンロードできます。of_preRelease_v007_iphone、XCode 4.1 + iOS SDK 3.2で作成しています。


徐々に復活

new_pachview.png

ようやく足の調子も復活してきた。ちょっと足が痛いだけで、気力も萎えてしまうものだ。なんだか、ここ数日あまり生産性が上がらなかった。

徐々に復活してきたので、またいろいろ勉強再開していきたい。とりあえずは「iOS開発におけるパターンによるオートマティズム」学習のまとめとして、以前作っていたPachubeのモニタアプリを、木下さんの書籍のパターンで再作成してみた。複数のフィードも登録できるように。きちんとパターンが構築されたプロジェクトは、改変や拡張が容易であるということを実感。しかしなぜかXMLのパースのバグがあり、いまだに解決できず。ダメプログラマ…

そろそろ、より表現を重視したアプリに移行せねば。休暇が終わってしまう…


勉強勉強

iOS開発におけるパターンによるオートマティズム

前回の続き。いろいろAppleのサンプルを漁りながら、アプリを作ろうとするものの、まだ理解できない部分も多い。もっと根本的な理解が必要なのかもと思っていたところに、とても素晴しい本を献本していただいた。

木下誠さん著の「iOS開発におけるパターンによるオートマティズム」という書籍。いままで、なんとなく理解していたつもりになっていたMVCパターンを使用したアプリ構築のための様々なノウハウが凝縮されている。Objective-Cについてはある程度理解したのだが、いざアプリを構築しようとした時にどこから手をつけて良いのか途方に暮れてしまうようなレベルの開発者(例えば自分)にちょうと良いレベルの内容。アプリの構造やその実現のためのパターンがたくさん紹介されているので、実践的でとても勉強になりそうだ。

ビジュアライザーはとりあえずお預けにして、しばらくまだ基礎固めをしていこうかと。

足の調子は相変らず…


足痛い

オフシーズンのさびれたスキー場、なんだか和む…

なんだか本格的に足が痛むようになってしまった… 今日はせっかく良い天気なのに、むー。足が痛いと集中力も途切れがち。開発も遅々として進まず。まあ、しばらくは安静にしてます。