yoppa.org


openFrameworksを、iOS4用にビルドする

openFrameworksを、iPhoneのiOS4用にビルドする方法について解説します。このページでは、下記の環境でテストしています。

Xcodeと、iOS SDKの最新版をダウンロードする

XcodeとiOSX SDKの最新版を、Apple Developper Center (ADC) からダウンロードしてインストールします。iPhone Dev CenterのURL (http://developer.apple.com/iphone/) にアクセスし、登録したADCのアカウントでログインした上で、「iOS SDK 4」のタブを選択します。「Downloads」のメニューより、「Xcode 3.2.3 and iOS SDK 4.0.1」を選択し、ダウンロードします。

ダウンロードしたディスクイメージをダブルクリックしてマウントし、インストーラーを起動して、XCodeとiPhone SDKをインストールします。

FreeImageのライブラリをダウンロード

下記のリンクより、iOS4用にビルドし直されたバージョンのFreeImageのライブラリをダウンロードします。

FreeImageのライブラリを入れ替え

openFrameworksの配布パッケージ内のFreeImageのライブラリを、ダウンロードしたiOS4用のライブラリと入れ替えます。

既存のライブラリを消去します。

  • of_preRelease_v0061_iPhone_FAT-pre3/libs/FreeImage/lib/iphone/libFreeImage_iphone_universal.a


既存のFreeImageのライブラリは消去 (画像をクリックで拡大)

同じフォルダにダウンロードしたライブラリ(2種類)をインストールします。iOS4用のライブラリは、ダウンロードしたフォルダの「FreeImage/lib/iphone/」以下にあります。その中身を以下の場所にコピーします。

  • of_preRelease_v0061_iPhone_FAT-pre3/libs/FreeImage/lib/iphone/libfreeimage-iphone.a
  • of_preRelease_v0061_iPhone_FAT-pre3/libs/FreeImage/lib/iphone/libfreeimage-iphonesimulator.a


ライブラリをiOS4用のものに置き換えた状態 (画像をクリックで拡大)

※ includeフォルダの中身は変更の必要はありません。

openFrameworksのプロジェクトにFreeImageライブラリを追加

まず、XCode 3.2.3で、openFrameworksの既存のプロジェクトファイルをオープンします。左側の「グループとファイル」のリストから、FreeImageのiPhone用ライブラリのフォルダ内にある古いライブラリを選択します。

  • [プロジェクトのアイコン]/libs/core/core libraries/FreeImage/lib/iphone/libFreeImage_iphone_universal.a

ファイルを選択した状態で、Deleteキー、もしくは右クリックで表示されるリストから「削除」を選択して、ファイルを削除します。


プロジェクト内の古いFreeImageライブラリ

次に、ライブラリが格納されていたiphoneフォルダを右クリックして、「追加」→「既存のファイルの追加」を選択します。ファイルの選択画面が表示されるので、iOS4用のライブラリを選択して、「追加」を選びます。

  • of_preRelease_v0061_iPhone_FAT-pre3/libs/FreeImage/lib/iphone/libfreeimage-iphone.a
  • of_preRelease_v0061_iPhone_FAT-pre3/libs/FreeImage/lib/iphone/libfreeimage-iphonesimulator.a


FreeImgeライブラリ内のiphoneフォルダに既存ファイルを追加する


ライブラリをiOS4用のもので入れ替えた状態

この操作は、プロジェクトを新規に作成する際に毎回必要となります。ライブラリーをiOS4用に設定した空のプロジェクトファイルを作成して、新規に「EmptyProject」という名前で保存して再利用すると、次回はその「EmptyProject」をコピーすることで設定済みの状態から初めることが可能です。

Xcodeビルド設定の変更

iOS4に更新した関係で、以前のプロジェクトファイルに設定されていた、ビルドの設定が使えなくなっています。最新版の環境にあわせて、ビルドの設定をする必要があります。設定箇所は2箇所です。

「グループとファイル」のリストの一番上にある、プロジェクトのアイコンを選択した状態で、メニューバーから「情報」を選択し、設定画面が表示されるので、「ビルド」のタブを選択。

  • 「アーキテクチャ」→「ベースSDK」の設定を、「iPhoneデバイス 4.0」に

「グループとファイル」のリストの「ターゲット」の中にある実行ファイルのアイコン(例:emptyExample)を選択した状態で、プロジェクトファイルと同様に変更。

  • 「アーキテクチャ」→「ベースSDK」の設定を、「iPhoneデバイス 4.0」に


ベースSDKの変更

iPhone実機で動かす場合には、ofxiphone-info.plistの設定や、コード署名IDの設定を行います。
あとは、メニューバーの「概要」で、「Simulator」か「Device」かを選択し、「ビルドと実行」ボタンを押して実行します。


SimulatorかDeviceかを選択

追記:iPad (3.2 or 3.2.1) 用にビルドする

iPad用にビルドするには、上記のiOS4の設定と同じ操作をした後で、追加で以下の設定をすることでビルドが可能となります。

「グループとファイル」から、「ターゲット」内にある実行ファイル (例:emptyExample) を選択し、右クリックします。表示されるメニューから「現在のターゲットをiPad用にアップグレード」を選択します。


ターゲットをiPad用にアップグレード

アップグレードの形式を、iPhoneとiPadで共通の1つのユニバーサルアプリケーションにまとめるか、2つの独立したアプリケーションにするかを選択します。この説明では、1つのユニバーサルアプリケーションを選択しています。


アップグレードの形式を選択

最後にメニュバーの「概要」のプルダウンメニューから「アクティブな実行可能ファイル」の中からiPad用の実行可能ファイルを選択します。


実行可能ファイルをiPadで選択

これで設定は完了です。「ビルドして実行」ボタンを押すと、シミュレータ、またはiPad実機でopenFrameworksのアプリケーションが動作します。


Processingで時間を表現する

「時間」を表現する

前回の授業で取り上げた、時計のプログラムで学んだ内容を生かして、時間を表現するプログラムについて考えてみる

  • 場所 (位置) による時間の表現
  • 色・濃度 による時間の表現
  • 数で時間を表現する …etc.

時間の表現の例

  • NOOKA
    • マシュー・ウォルドマンデザインによるシンプルな図形で時間を表現した腕時計
    • http://nooka.com/
  • Tokyoflash
  • Ora Unica
    • Denis Guidoneデザイン、1本の線で時間を表示する
  • Clocks Series by rhizomatics
    • iPhone向けの時計アプリシリーズ、継続的に、多様でユニークな時計アプリをリリースしている
  • Creative Applicationsで紹介されている様々な時計の試み

Processingで時間を表現してみる

Processingで、シンプルなサンプルを作りながら、いろいろな表現の可能性について考えてみましょう。

  • clock01: 場所による時間の表現
  • clock02: 濃度による時間の表現
  • clock03: 色相による時間の表現
  • clock04: 文字と動きによる時間表現
  • clock05: 2進数による時間表現

時間の表現1:場所による時間の表現

  • 図形の場所によって、時間を表現する
  • 時間、分、秒、ミリ秒の値に対応して、ラインの位置を変化させてみる
int ms, s, m, h;
int lastSecond = 0;

void setup() {
    size(640, 480);
    frameRate(60);
    background(0);
}

void draw() {
    ms = millis() % 1000;
    s = second();
    m = minute();
    h = hour();

    fill(0, 4);
    noStroke();
    rect(0, 0, width, height);

    noFill();

    float ms_pos = width / 1000.0 * ms;
    stroke(255);
    line(ms_pos, 0, ms_pos, height / 4);

    translate(0, height / 4);
    stroke(63);
    line(0, 0, width, 0);

    float s_pos = width / 60.0 * s;
    stroke(255);
    line(s_pos, 0, s_pos, height / 4);

    translate(0, height / 4);
    stroke(63);
    line(0, 0, width, 0);

    float m_pos = width / 60.0 * m;
    stroke(255);
    line(m_pos, 0, m_pos, height / 4);

    translate(0, height / 4);
    stroke(63);
    line(0, 0, width, 0);

    float h_pos = width / 24.0 * h;
    stroke(255);
    line(h_pos, 0, h_pos, height / 4);
}

時間の表現2:濃度による時間の表現

  • 図形の濃度 (アルファ値) によって時間を表現する
  • 4つの正方形を横に並べて、その濃度を、時間、分、秒、ミリ秒に対応させてみる
float ms, s, m, h;

void setup() {
    size(800, 200);
    noStroke();
}

void draw() {
    ms = millis() % 1000;
    s = second();
    m = minute();
    h = hour();

    background(0);

    fill(255, 255 * (h / 24.0));
    rect(0, 0, width / 4, height);

    translate(width / 4, 0);
    fill(255, 255 * (m / 60.0));
    rect(0, 0, width / 4, height);

    translate(width / 4, 0);
    fill(255, 255 * (s / 60.0));
    rect(0, 0, width / 4, height);

    translate(width / 4, 0);
    fill(255, 255 * (ms / 1000.0));
    rect(0, 0, width / 4, height);
}

時間の表現3:色相による時間の表現

  • 同じ図形で、今度は透明度はそのまで、色の色相(Hue)を変化させていく
  • colorModeをCSVにして、色相の変化を計算しやすいように
  • あとは色相を、時間、分、秒、ミリ秒に適用させるだけ
float ms, s, m, h;

void setup() {
    size(800, 200);
    colorMode(HSB, 360, 100, 100);
    noStroke();
}

void draw() {
    ms = millis() % 1000;
    s = second();
    m = minute();
    h = hour();

    background(0);

    fill(360 * (h / 24.0), 50, 100);
    rect(0, 0, width / 4, height);

    translate(width / 4, 0);
    fill(360 * (m / 60.0), 50, 100);
    rect(0, 0, width / 4, height);

    translate(width / 4, 0);
    fill(360 * (s / 60.0), 50, 100);
    rect(0, 0, width / 4, height);

    translate(width / 4, 0);
    fill(360 * (ms / 1000.0), 50, 100);
    rect(0, 0, width / 4, height);
}

時間の表現4:文字と動きによる時間表現

  • デジタル時計と、アナログ時計を融合させてみる
  • 時間、分、秒を表示した針が、秒針の角度で回転していく
  • 時間の経過を感じさせるため、余韻を残しながら、消えていくようにする
  • フォントの表示のため、Tools → Create font を利用して、”myFont.vlw” を生成する
PFont font;
int s, m, h;
int lastSecond = 0;

void setup() {
    size(600, 600);

    font = loadFont("myFont.vlw");
    textFont(font);

    frameRate(30);
    smooth();
    background(0);
}

void draw() {
    s = second();
    m = minute();
    h = hour();

    if (lastSecond != s) {
        textAlign(CENTER);
        fill(0, 10);
        noStroke();
        rect(0, 0, width, height);

        fill(255);
        stroke(255);

        pushMatrix();
        translate(width / 2, height / 2);
        rotate(radians(6.0 * s));
        String time = nf(h, 2) + "\n" + nf(m, 2) + "\n" + nf(s, 2);
        text(time, 0, -height / 2.2);
        line(0, 0, 0, -height / 3);
        popMatrix();
    }
    lastSecond = s;
}

時間の表現5:2進数による時間表現

  • 時間の経過を2進数のon/offで表現してみる
  • 現在の時刻をの時間、分、秒をそれぞれ6桁の2進数に変換
    • 例:18:11:49 → 010010001011110001
  • 2進数の0と1を利用して図形を描いていく
  • 時間の経過が蓄積していくように工夫
PFont font;
int s, m, h;
int lastSecond = 0;
int bufferNum = 480;
String[] timeArray = new String[bufferNum];

void setup() {
  size(640, 480);
  frameRate(30);
  background(0);
  noStroke(); h

  for (int i = 0; i < timeArray.length; i++) {
    timeArray[i] = "000000000000000000";
  }
}

void draw() {
  s = second();
  m = minute();
  h = hour();

  if (lastSecond != s) {
    background(0);

    String bs = nf(int(binary(s)), 6);
    String bm = nf(int(binary(m)), 6);
    String bh = nf(int(binary(h)), 6);

    println(nf(h, 2) + ":" + nf(m, 2) + ":" + nf(s, 2) + ", " + bh + bm + bs);

    float rectWidth = width / 18.0;
    float rectHeight = height / float(bufferNum);

    for (int i = timeArray.length; i & gt; 1; i--) {
      timeArray[i - 1] = timeArray[i - 2];
    }
    timeArray[0] = bh + bm + bs;

    for (int j = 0; j < timeArray.length - 1; j++) {
      for (int i = 0; i < 18; i++) {
        if (timeArray[j].charAt(i) == '1') {
          fill(255);
        }
        else {
          fill(0);
        }
        rect(i * rectWidth, j * rectHeight, rectWidth, rectHeight);
      }
    }
  }
  lastSecond = s;
}

サンプルファイルのダウンロード

今回のとりあげた全てのサンプルのソースコードは、下記からダウンロードしてください。


Processingで時計を作る

今日の内容

  • 最終課題のテーマ発表
    • 最終課題に関連した演習
      • 基礎:普通のアナログ時計を作る
        • 時刻の取得
        • 時刻の情報から針の角度を計算する
        • より正確な時計にしていくには?
      • 応用:単純な図形で「時間」を表現する
        • 位置による時間の表現
        • 数による時間の表現
        • 色による時間の表現
        • 形態による時間の表現 …etc

最終課題について

テーマ:Processingを用いて「時間」を表現する

  • Processingを用いて、「時間」を表現するプログラムを作成してください。
    • 時間を表現するといっても、普段身近に接している時計を作成するという意味ではなく、いままで見たことのないような表現手段で「時間」を感じさせる方法を考えてください。
  • 作品の完成度よりは、斬新なアイデアを重視します。
  • 完成したプログラムは下記のWebサーバのアドレスにアップロードすることで提出したこととします。
  • http://www.cuc.ac.jp/~[ログイン名]/proga/final/index.html

時計を作る 1 – アナログ時計を作る

  • 課題の内容と関連して、まずは普通のアナログ時計を作成する
    • 時刻の取得
    • 取得した時刻から、秒針、分針、時針の角度を計算する
  • 時刻を取得するための関数
  • 取得される時刻は、コンピュータのOSに設定された時刻
//秒の取得
second();

//分の取得
minute();

//時の取得
hour();
  • 試しに現在の秒数を取得して、出力してみる
  • println() を利用して、Processingのコンソールのテキストエリアに現在の秒数を表示してみる
void setup() {
  frameRate(30);
}

void draw() {
  int h = hour();
  int m = minute();
  int s = second();
  println("現在の時刻は "+h+":"+m+":"+s);
}
  • 取得した「時」「分」「秒」の値から針の角度を計算する
  • それぞれの1単位あたり、何度針が動くのか計算する (角速度)
    • 時間
      • 12時間で時計を1周する
      • 1時間あたりの移動角度 → 360/12 = 30°
      • 60分で時計を1周する
      • 1分あたりの移動角度 → 360/60 = 6°
      • 60秒で時計を1周する
      • 1秒あたりの移動角度 → 360/60 = 6°
  • 現在の時刻の値にそれぞれの角速度をかけ算すると、現在の角度が算出されるはず
  • 時刻から、針の角度を計算する
    • 例:8時30分20秒
      • 時針:8 x 30 = 240°
      • 分針:30 x 6 = 180°
      • 秒新:20 x 6 = 120°
  • Processingのプロラムで表現してみる
    • 座標系全体を画面の中央に移動 translate(width/2, height/2);
    • 時刻を取得
    • 時間の針
      • pushMatrix();
      • rotate(時間 x 15°);
      • popMatrix();
    • 分の針
      • pushMatrix();
      • rotate(時間 x 6°);
      • popMatrix();
    • 秒の針
      • pushMatrix();
      • rotate(時間 x 6°);
      • popMatrix();
  • Processingの rotate() 関数で用いられる角度の単位に注意する必要あり
  • 角度の単位は「度 (degree)」ではなく「ラジアン (radian)」で指定しなくてはならない
    • Processingでの、degreeからradianへの変換
      • radians( 度 );
    • radianからdegreeへの変換
      • degrees( ラジアン角 );
void setup() {
  size(300,300);
  stroke(255);
  smooth();
  frameRate(30);
}

void draw() {
  background(0);
  float s = second();
  float m = minute();
  float h = hour() % 12;

  translate(width/2, height/2);
  ellipse(0,0,width,height);

  noFill();
  stroke(255);

  //秒針
  pushMatrix();
  rotate(radians(s*(360/60)));
  strokeWeight(1);
  line(0,0,0,-height/2);
  popMatrix();

  //分針
  pushMatrix();
  rotate(radians(m*(360/60)));
  strokeWeight(2);
  line(0,0,0,-height/2);
  popMatrix();

  //時針
  pushMatrix();
  rotate(radians(h*(360/12)));
  strokeWeight(4);
  line(0,0,0,-height/3);
  popMatrix();
}

実際の時計との違い

  • 作成したプログラムの表示と実際のアナログ時計と比べてみる
    • なにか違いはないだろうか?
      • 分が切り替わった瞬間の動き
      • 時間が切り替わった瞬間の動き
  • 実際の時計
    • 時針、分針も常にゆっくりと動いている
    • 時刻が変化した瞬間に動くのではなく、ゆっくりと移動して、気がつくと角度が変化している
  • 実際の分針
    • 秒の影響を受けている
    • 毎秒あたり、1/60の影響 (1分 = 60秒なので)
    • m = m + (s/60);
  • 実際の時針
    • 分の影響を受けている
    • 毎分あたり、1/60の影響 (1時間 = 60分なので)
    • h = h + (m/60);
  • 実際にアナログ時計の動きと同じになるよう、プログラムを改良する
void setup() {
  size(300,300);
  stroke(255);
  smooth();
  frameRate(30);
}

void draw() {
  background(0);

  float s = second();
  float m = minute() + (s/60.0);
  float h = hour()%12 + (m/60.0);

  translate(width/2, height/2);
  ellipse(0,0,width,height);

  noFill();
  stroke(255);

  //秒針
  pushMatrix();
  rotate(radians(s*(360/60)));
  strokeWeight(1);
  line(0,0,0,-height/2);
  popMatrix();

  //分針
  pushMatrix();
  rotate(radians(m*(360/60)));
  strokeWeight(2);
  line(0,0,0,-height/2);
  popMatrix();

  //時針
  pushMatrix();
  rotate(radians(h*(360/12)));
  strokeWeight(4);
  line(0,0,0,-height/3);
  popMatrix();
}
  • 文字盤を追加
    • 角度を変更しながら、一定間隔に目盛を刻んでいく
    • 小さな目盛:6度づつ
    • 大きな目盛:30度づつ
int MARGIN = 20;

void setup() {
  size(300,300);
  stroke(255);
  smooth();
  frameRate(30);
}

void draw() {
  background(0);

  float s = second();
  float m = minute() + (s/60.0);
  float h = hour()%12 + (m/60.0);
  translate(width/2, height/2);
  rotate(radians(180));

  // 文字盤の表示
  pushMatrix();
  fill(128);
  noStroke();
  for(int i=0; i<60; i++){
    rotate(radians(6));
    ellipse(width/2-MARGIN,0,3,3);
  }
  for(int i=0; i<12; i++){
    rotate(radians(30));
    ellipse(width/2-MARGIN,0,10,10);
  }
  popMatrix();
  noFill();
  stroke(255);

  // 秒針
  pushMatrix();
  rotate(radians(s*(360/60)));
  strokeWeight(1);
  line(0,0,0,width/2-MARGIN);
  popMatrix();

  // 分針
  pushMatrix();
  rotate(radians(m*(360/60)));
  strokeWeight(2);
  line(0,0,0,width/2-MARGIN);
  popMatrix();

  // 時針
  pushMatrix();
  rotate(radians(h*(360/12)));
  strokeWeight(4);
  line(0,0,0,width/3-MARGIN);
  popMatrix();
}

アナログ時計 OOP版

int MARGIN = 20;
Clock myClock = new Clock();

void setup() {
  size(300,300);
  stroke(255);
  smooth();
  frameRate(30);
}

void draw() {
  background(0);
  myClock.getTime();
  myClock.draw();
}

class Clock {
  float s, m, h;
  Clock(){
  }

  void getTime(){
    s = second();
    m = minute() + (s/60.0);
    h = hour()%12 + (m/60.0);
  }

  void draw(){
    translate(width/2, height/2);
    rotate(radians(180));
    pushMatrix();
    fill(128);
    noStroke();
    for(int i=0; i<60; i++){
      rotate(radians(6));
      ellipse(width/2-MARGIN,0,3,3);
    }
    for(int i=0; i<12; i++){
      rotate(radians(30));
      ellipse(width/2-MARGIN,0,10,10);
    }
    popMatrix();
    noFill();
    stroke(255);
    pushMatrix();
    rotate(radians(s*(360/60)));
    strokeWeight(1);
    line(0,0,0,width/2-MARGIN);
    popMatrix();
    pushMatrix();
    rotate(radians(m*(360/60)));
    strokeWeight(2);
    line(0,0,0,width/2-MARGIN);
    popMatrix();
    pushMatrix();
    rotate(radians(h*(360/12)));
    strokeWeight(4);
    line(0,0,0,width/3-MARGIN);
    popMatrix();
  }
}

Webアニメーション最終課題について

最終課題について

最終課題テーマ:「気持のよい」インタラクションをつくる

  • インタラクションにおける、気持ちの良さとは何かを、Flashを使用して探求する
    • 気持ちのよい動き
    • 気持のよい反応
    • 気持のよいタイミング
    • 気持のよい変化 …etc.
  • 制作する課題は、以下の条件を満たしていること
    • Flashを使用すること
    • ムービーの閲覧者の何かしらのアクションに反応すること
      • マウスの動き、位置、クリック
      • キーボードからの入力
      • 音 (応用)
      • カメラからの入力 (応用)
    • 動きの表現には、TweenMaxライブラリを使用する
    • 反応するオブジェクトは、単純な図形でも構いませんし、オリジナルのイラストや写真などの使用しても良い

オンラインポートフォリオの作成について

オンラインポートフォリオの制作テンプレート

オンラインポートフォリオの制作にあたっては、下記URLからテンプレートをダウンロードして参考にしてください。テンプレートには、画像、動画、音声、Javaアプレット(Processigの作品) などのメディアを載せる実例も掲載されていますので、適宜参考にしてください。

オンラインポートフォリオのテンプレートをダウンロード (Zip形式、693KB)

今回制作するオンラインポートフォリオは、この授業だけでなく、入学から卒業制作まで制作してきた自身の作品を掲載する重要な記録となります。この授業の講評を終えた後も、更新を続けていくようにしましょう。また、将来的には、このポートフォリオをスタジオの選択や卒業制作の審査などの際に参考資料として提出することもあります。

オンラインポートフォリオに最低載せる内容

  • 表紙 (index.html):
    • 自己紹介:画像、簡単な経歴など自己アピールしたい内容を記述してください。
    • ブックマーク:(もしあれば)自分のWebページ、自身のブログなどへのリンク。自分の興味のある分野のWebページ、好きなアーティストのページなど。
  • 実技系の授業で作成した作品を掲載
    • メディア芸術基礎(情報デッサン / メディアリテラシー):情報デッサンで制作したデッサンの画像
    • イメージリテラシー:制作した映像作品
    • インタラクション:制作したProcessingの作品(Javaアプレット)
  • 個人制作
    • 授業とは関係なく自分自身で制作した作品があれば、積極的にポートフォリオに追加してください
    • イラスト、映像、音楽、
  • 1年生後期に追加する作品
    • サウンド&CGI:制作した音響作品、映像作品
    • クラフト&マテリアル:制作した作品(映像、写真)
  • 2年生以降も制作した作品は全てオンラインポートフォリオに記録していく

メディアに応じた作品の掲載方法

1. 画像 (写真、イラストなど)

大きくわけて、2つの方法があります。

  • Webサーバー内に画像を保存する方法:Webサーバーに画像を格納して、img要素を記述して画像を読み込む
  • 写真共有のWebサービスを利用する方法:Picasa(http://picasaweb.google.com/)、Flickr(http://www.flickr.com/)

Webサーバー内に画像を保存する方法

  • HTML内に、img要素を記述して写真や画像を読み込みます。
  • 例) htmlファイルと同じ階層に「img」というフォルダを作成して、その中に「hoo.jpg」という画像を入れた場合
<img src="img/hoge.jpg" width="画像の幅" height="画像の高さ" alt="画像の説明" />

写真共有サービスを利用する場合 – Picasaウェブアルバムを利用した例

  • 画像をアップロード
    • ヘッダーの右にある「アップロード」ボタンを押す
    • アルバムを選択、または新規に作成 – 写真は「アルバム」という単位で整理されます、授業毎にアルバムを分けたり、時系列にアルバムを作成したりと、適宜整理の方針を決めてアルバムを作成
    • 新規にアルバムを作成する際は、共有の設定を「一般公開」にするように注意
  • ファイルのアップロード画面に遷移するので、画像を選択してアップロード


アップロードボタン


アルバムの選択


アップロード画面

  • 写真へのリンクを取得
    • アルバムからWebに載せたい画像を選択する
    • 選択した写真の右側のメニューの「この写真へのリンク」を押して、リンクの設定を表示
    • 貼りつける画像のサイズを選択
    • 「画像を埋め込み」の欄に表示されているHTMLのタグをコピー
    • 制作するページのHTMLファイルにコピーしたタグをペーストする


画像を埋めこみコードの取得

画像を埋めこんだ例

送信者 Buzz

埋めこみコード

<table style="width:auto;"><tr><td><a href="http://picasaweb.google.com/lh/photo/wMEkwxtiGZRCFpmm2VLeicAS8prLNGVgqURFiaHAmos?feat=embedwebsite"><img src="http://lh4.ggpht.com/_S9tCNvdm3tU/S3OYDHcg9eI/AAAAAAAACuY/nzvPJH3MxaQ/s800/new_portrait.jpeg" /></a></td></tr><tr><td style="font-family:arial,sans-serif; font-size:11px; text-align:right">送信者 <a href="http://picasaweb.google.com/tadokoro/Buzz?authkey=Gv1sRgCJbzqu3dksHo-gE&feat=embedwebsite">Buzz</a></td></tr></table>

2. 動画 (記録映像、映像作品、アニメーションなど)

YouTubeに動画をアップする場合

  • 動画のアップロード
    • YouTubeにGoogleアカウントでログインする (右上のリンク)
    • 検索欄の右にある「アップロード」を選択
    • アップロード終了後、サーバ側で画像の圧縮処理が始まる


アップロードへのリンク

  • 動画を埋め込む
    • アップロードした動画の圧縮が終わったら、埋め込みコードを取得して、動画を貼りつける
    • ログインした後の画面の右上にあるアカウント名のメニューから「マイ動画」を選択
    • 埋め込みたい動画を選択
    • 動画の画面から「埋めこみコード」ボタンを押す
    • 動画のサイズ、インターフェイスの色などを設定
    • 埋め込みコードをコピーして、動画を埋めこみたいHTMLページにペーストする


マイ動画


埋め込みコードボタン


埋め込みコードの取得

動画を埋めこんだ例

埋めこみコード

<object width="560" height="340"><param name="movie" value="http://www.youtube.com/v/bEpXUEvT4g0&hl=ja_JP&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/bEpXUEvT4g0&hl=ja_JP&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"></embed></object>

3. 音 (音楽・音響作品など)

音作品もWebサービスを利用すると便利

SoundCloudを利用する

  • SoundCloud (http://soundcloud.com/) – サウンドを共有することのできるWebサービス
  • ユーザー登録する
    • まずは「Sign Up」ボタンを押して、ユーザ登録をする
    • 必要事項を記入して登録する


Sound Cloud、サインアップ


ユーザー登録画面

  • サウンドファイルのアップロード
    • ヘッダーのメニューの右にある「Upload & Send」ボタンを押す
    • ファイルを選択画面に遷移、「Coose File」ボタンを押してディスク上のサウンドファイルを選択
    • アップロードが開始されると、詳細な情報を設定する画面に遷移する、記入できる項目に情報を記述
    • 共有の設定 – 3. Who is this track for? の設定は「Public (available for everyone)」にすること
    • 全ての設定が完了したら、「Save Track」ボタンを押してトラックを保存


Upload ∧ Sendボタン


オーディオファイルの選択


Track infoの記入


共有の設定

  • サウンドの共有
    • 保存したトラックの波形が表示される
    • 左上にある「Share」ボタンを押す
    • 表示される画面の「The embed code for the player」の下にあるHTMLコードをコピー
    • サウンドを貼りつけたいHTMLファイルの中にコピーしたHTMLコードをペースト


アップロードされたトラック


共有コードの取得

サウンドを埋めこんだ例

SC Test by tadokoro

埋めこみコード

<object height="81" width="100%"> <param name="movie" value="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fsoundcloud.com%2Ftadokoro%2Fsc-test"></param> <param name="allowscriptaccess" value="always"></param> <embed allowscriptaccess="always" height="81" src="http://player.soundcloud.com/player.swf?url=http%3A%2F%2Fsoundcloud.com%2Ftadokoro%2Fsc-test" type="application/x-shockwave-flash" width="100%"></embed> </object>  <span><a href="http://soundcloud.com/tadokoro/sc-test">SC Test</a> by <a href="http://soundcloud.com/tadokoro">tadokoro</a></span> 

Pachubeで地球をセンシング、ネットワークでセンサー情報を共有する

今日の内容

  • Pachubeを使ってみる
    • 世界各地のpachubeの情報をモニターしてみる
    • Processingで、Pachubeの情報をリアルタイムに取得して、視覚化する
    • Arduinoで取得したセンサーの情報を、Pachubeに書き出し
    • センサーの情報をシェアしてみる

Pachubeとは?

  • http://www.pachube.com/
    • インターネットを介してリアルタイムにセンサーやからの情報を共有するプロジェクト
    • 世界中の環境、センサーからのリアルタイムの情報をモニター
    • 自作のデバイスを接続、世界に向けて情報を発信
    • ユーザ登録することで、APIを利用して様々なアプリケーションからPachubeの情報を送受信可能となる
      • Processing, openFrameworks, Java, Ruby, PHP, iPhone ..etc.a

ユーザ登録

  • PachubeのAPIを利用するために、まずはPachubeにユーザ登録する
  • もちろん無料
  • Sign upの登録フォームに必要情報を記入して送信 https://www.pachube.com/signup
  • 登録したemailアドレスに、メールが返送される
  • 登録完了メールの中の「API Key」というアルファベットと数値の文字列が開発の際に必要となるので、控えておく

Webブラウザで情報を取得

  • まずは簡単な方法で情報を取得してみる
    • Pachubeトップページ(http://www.pachube.com/)のマップから、ひとつ場所(ふきだし)を選び、ふきだしのタイトルをクリック – 例えば、iddに設置されたiBioartを選択 – http://www.pachube.com/feeds/2263
    • センサーの設置位置と、現在の状況がリアルタイムに更新されている

  • タイトルの下にあるXMLファイルのURLをクリック (iBioartの場合は、http://www.pachube.com/api/feeds/2263.xml)
  • 文字と数字の羅列が表示される
  • ブラウザから「ソースを見る」を選択する – XML形式のファイルが表示される、これがPachubeからフィードのフォーマット(EEML形式)
<eeml xmlns="http://www.eeml.org/xsd/005" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="5" xsi:schemaLocation="http://www.eeml.org/xsd/005 http://www.eeml.org/xsd/005/005.xsd"> 
  <environment updated="2010-07-11T03:28:16Z" id="2263" creator="http://www.haque.co.uk"> 
    <title>iBioart</title> 
    <feed>http://www.pachube.com/api/feeds/2263.xml</feed> 
    <status>live</status> 
    <description>A Plant connected with Pachube.</description> 
    <website>http://dp.idd.tamabi.ac.jp/bioart/</website> 
    <email>kubotaa@tamabi.ac.jp</email> 
    <location domain="physical" exposure="indoor" disposition="fixed"> 
      <name>Tama Art University, Tokyo</name> 
      <lat>35.6105400074836</lat> 
      <lon>139.351229667664</lon> 
    </location> 
    <data id="0"> 
      <tag>temperature</tag> 
      <value minValue="1.0" maxValue="168.0">83</value> 
    </data> 
    <data id="1"> 
      <tag>humidity</tag> 
      <value minValue="63.0" maxValue="306.0">145</value> 
    </data> 
    <data id="2"> 
      <tag>brightness</tag> 
      <value minValue="0.0" maxValue="942.0">846</value> 
    </data> 
    <data id="3"> 
      <tag>water</tag> 
      <value minValue="0.0" maxValue="842.0">209</value> 
    </data> 
    <data id="4"> 
      <tag>plant1</tag> 
      <value minValue="0.0" maxValue="928.0">353</value> 
    </data> 
    <data id="5"> 
      <tag>plant2</tag> 
      <value minValue="0.0" maxValue="1023.0">401</value> 
    </data> 
    <data id="6"> 
      <tag>plantdiff</tag> 
      <value minValue="-142.0" maxValue="220.0">141</value> 
    </data> 
    <data id="7"> 
      <tag>human</tag> 
      <value minValue="0.0" maxValue="1.0">0</value> 
    </data> 
  </environment> 
</eeml> 
  • このEEML形式のXMLファイルを様々なアプリケーションで解析することで、オリジナルのPachubeモニタを作成できる

Extended Environments Markup Language (EEML)

  • Pachubeでは、センサーや環境の情報を記述XML形式のフォーマットとして、Extended Environments Markup Language (EEML)というものを策定している
  • EEMLに関する詳細な情報は、http://www.eeml.org/ を参照

ProcessingでPachubeの情報を視覚化

  • Processingを仕様して、pachubeのEEMLを解析する
  • EEMLの解析のためのライブラリが公開されているので、これを利用すると便利
  • EEML for Processing (http://www.eeml.org/library/)

EEML for Processingをインストール

  • EEML for Processingのページから、Processing用のライブラリをダウンロード(http://www.eeml.org/library/eeml.zip)
  • Zipファイルを展開した「eeml」フォルダを、「ProcessingのSketchbookの場所/libaries/」以下にコピーする
    • ProcessingのSketchbookの場所は、ProcessingのPreferenceの、「Sketchbook location」で確認

まずは、シンプルに情報を表示

  • 最初のステップとして、遠隔地のセンサーの値を取得してテキストで出力してみる
import eeml.*; //eemlライブラリのインストール

//場所のIDを指定、iBioartは2263
int locationId = 2263;
//配信されているデータの数を指定
int dataNum = 8;
//EEMLからのデータ入力
DataIn dIn;
//データのタグを記録する配列
String[] tag = new String[dataNum];
//データの値を記録する配列
float[] value = new float[dataNum];

void setup() {
  size(400,400);
  //指定した場所から、EEML形式のデータを15秒ごとに受信する
  dIn = new DataIn(this,"http://www.pachube.com/api/feeds/" + locationId + ".xml", "※PachubeのAPI Keyを入力", 15000);
}

void draw() {
  background(0);
  fill(255);
  noStroke();
  //データの数だけ繰り返し
  for(int i=0; i < dataNum; i++){
    //データのタグと値の組み合わせを、テキスト表示
    text(tag[i] + " : " + value[i], 10, i * 20 + 20);
  }
}

//EEMLのデータを受信したら呼び出されるイベント
void onReceiveEEML(DataIn d) {
  //データの数だけ繰り返し
  for(int i=0; i < dataNum; i++) {
    //タグの文字列を取得
    tag[i] = d.getTag(i);
    //センサーの値を取得
    value[i] = d.getValue(i);
  }
}

センサーの変化をグラフ化する

  • センサーの値の変化をグラフで表示してみる
  • このコードを応用すれば、Pachubeの時系列のデータを様々な方法でビジュアライズできるはず
import eeml.*; //eemlライブラリのインストール

//場所のIDを指定、iBioartは2263
int locationId = 2263;
//配信されているデータの数を指定
int dataNum = 8;
//表示するグラフの高さ
int graphHeight = 65;
//隣接するグラフ同士の余白
int graphMargin = 5;
//EEMLからのデータ入力
DataIn dIn;
//データのタグを記録する配列
String[] tag = new String[dataNum];
//データの値を記録する配列
float[] value = new float[dataNum];
//ひとつ前のグラフの座標を記録する配列
float[] lastX = new float[dataNum];
float[] lastY = new float[dataNum];
//データを取得した回数を記録
int count = 0;

void setup() {
  size(800,570);
  smooth();
  //指定した場所から、EEML形式のデータを5秒ごとに受信する
  dIn = new DataIn(this,"http://www.pachube.com/api/feeds/" + locationId + ".xml", "※ PachubeのAPI Keyを入力", 5000);
  background(0);
}

void draw() {
}

//グラフの表示を初期化する
void initGraph() {
  background(0);
  noFill();
  stroke(63);
  //グラフの枠線を描いて、それぞれの値のタグを表示
  for(int i=0; i < dataNum; i++){
    //枠線の表示
    rect(10, i * (graphHeight + graphMargin) + graphMargin, width - 20, graphHeight);
    //データのタグと、最大値、最小値を取得
    String title = dIn.getTag(i) + " (" + dIn.getMinimum(i) + " - " + dIn.getMaximum(i) + ")";
    //取得したタグ、最大値最小値をテキストで表示
    text(title , 12, i * (graphHeight + graphMargin) + 22);
  }
}

//グラフの表示を更新
void showUpdate() {
  stroke(0, 255, 255);
  //データの数だけ繰り返し
  for(int i=0; i < dataNum; i++){
    //表示する座標を計算
    int top = i * (graphHeight + graphMargin) + graphMargin;
    int left = 10;
    int h = graphHeight;
    int w = width - 20;
    //データX座標が表示領域の幅に納まるようにマッピング
    float x = map(count, 0, 100, 10, w);
    //データのY座標が、データの最大値と最小値の範囲に納まるようにマッピング
    float y = map(value[i], dIn.getMinimum(i), dIn.getMaximum(i), top + h, top);
    //ひとつ前の座標から現在の座標まで直線をひく
    if(count > 0){
      line(lastX[i], lastY[i], x, y);
    }
    //現在の値を記録
    lastX[i] = x;
    lastY[i] = y;
  }
}

//EEMLのデータを受信したら呼び出されるイベント
void onReceiveEEML(DataIn d) {
  //データの数だけ繰り返し
  for(int i=0; i < dataNum; i++) {
    //タグの文字列を取得
    tag[i] = d.getTag(i);
    //センサーの値を取得
    value[i] = d.getValue(i);
    println(tag[i] + " : " + value[i]); 
  }
  //もし最初のデータだったら、グラフを初期化する
  if(count == 0) {
    initGraph();
  }
  //グラフの更新
  showUpdate();
  //カウンターを加算
  count++;
  //もし指定したカウントを越えたら、リセット
  if(count > 100) {
    count = 0;
  }
}

Pachubeにデータを配信する

  • Pachubeにユーザ登録をすると、データの受信だけでなく、データの配信をすることも可能
  • 配信したデータは、Pachubeの地図上に表示され、世界中から閲覧可能となる
  • しかし、配信するにはグローバルIPが設定された、外部から閲覧可能なサーバが必要となる
  • 今回は、ローカルなネットワーク(LAN)の中で、擬似的にPachubeのデータ共有を試してみる

Arduino + ブレットボード側の配線

  • 光センサー、温度センサー、湿度センサーなど、変化する値を出力可能なセンサーをブレッドボードに配線
  • ArduinoのAnalog inに接続する
  • 例:照度センサーをとりつけた場合

Funnelライブラリのインストール

  • 今回はArduinoとProcessingの通信にFunnelライブラリを使用
  • Google CodeよりFunnelの最新版をダウンロードする http://code.google.com/p/funnel/
    • 右側にある「Featured downloads: 」より、Funnelの最新版のパッケージ「Funnel-….zip」を選択
  • 「ProcessingのSketchbookの場所/libaries/」以下に「funnel」フォルダを作成
  • Zipファイルを展開、フォルダ「libraries」の中にある「processing」フォルダの中身を、作成した「funnel」フォルダ以下にコピー

Arduino側の準備

  • Arduinoには、StandardFirmataを読み込んでアップロードしておく
    • File > Example > Firmata > StandardFirmarta からロード

Processing側のプログラム

  • Arduinoからの入力を、Funnelを経由して取得し、それをeeml形式で送信するプログラム
import processing.funnel.*; //Funnelライブラリの読み込み
import eeml.*; //EEMLライブラリの読み込み

//Arduinoクラスを定義
Arduino arduino;
//Arduinoから取得した値
float myValue
//Pachubeへ送出する値
DataOut dOut; 

void setup() {
  size(400, 200);
  frameRate(15);
  //Arduinoを初期化
  arduino = new Arduino(this, Arduino.FIRMATA);
  //5210番ポートから、Pachubeへデータを送信
  dOut = new DataOut(this, 5210);
  //データにタグを付ける
  dOut.addData(0,"Brightness");
}

void draw() {
  background(0);
  fill(255);
  noStroke();
  //Arduinoのアナログ入力0番の値を取得
  myValue = arduino.analogPin(0).value;
  //画面上にテキストで表示
  text("Brightness : " + myValue, 10, 20);
}

//データ送出のリクエストで呼びだされるイベント
void onReceiveRequest(DataOut d){
   //Arduinoのアナログ入力0番の値を取得
   myValue = arduino.analogPin(0).value;
   //Pachubeに値を送出
   d.update(0, myValue); 
}
  • プログラムを起動したら、以下のURLにWebブラウザでアクセスしてみる
  • 送出したEEML形式のデータが見えているはず

Pachubeのデータをシェアする

  • 教室内の他の人のEEMLデータを見てみる
  • システム環境設定 > ネットワーク を開く
  • その中に表示されているIPアドレスを控えておき、周囲の人同士で、IPアドレスを教えあう
  • それぞれのURLにアクセス
  • 他の人のアドレスが参照できるはず

[参考] Pachubeデータを世界に公開する

  • ユーザ登録をすると、Pachubeのデータを公開することも可能
  • 公開するには、ArduinoとProcessingの稼動しているPCが、外部から参照可能なIPアドレス(グローバルIP)に接続されていることが必要
  • Pachubeにサインアップした状態で、新規にフィードを生成するページへ http://www.pachube.com/feeds/new
    • Feed Typeを「automatic」に
    • Feed URLを、Arduinoが接続されたPCの「グローバルIP:5210」にする
      • 例:設定されたグローバルIPが、169.254.0.201だったとしたら、Feed URLは http://169.254.0.201:5210 となる
    • 地図をダブルクリックして、データを配信している場所を指定
    • その他配信できる情報は全て記入
    • 「Save Feed」をクリックして配信開始!!

サンプルファイルのダウンロード

授業内でとりあげたコードは下記からダウンロードしてください。


openFrameworks for iPhone:お絵描きアプリを作る

今日の内容

  • ドロー(お絵描き)アプリをつくる
  • 簡単なサンプルから徐々に複雑なサンプルへと進めていきます

ドロー系アプリのサンプル

  • FatTag Deluxe Katsu Edition! http://fffff.at/fattag-deluxe-katsu-edition
    • いろいろな写真を背景にタギング(落書き)することができる
    • スプレーで描いた際のインクの垂れなどをリアルに再現

ドローアプリを作る

ステップ1:タッチした軌跡に円を描く

  • touchDownとtouchMoveした際のX座標とY座標を取得
  • 取得した座標の場所に円を描いていく
  • 円の連なりで線を描いていく

testApp.h

#pragma once

#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 &touch);
    void touchMoved(ofTouchEventArgs &touch);
    void touchUp(ofTouchEventArgs &touch);
    void touchDoubleTap(ofTouchEventArgs &touch);
    
    void lostFocus();
    void gotFocus();
    void gotMemoryWarning();
    void deviceOrientationChanged(int newOrientation);
    
    ofPoint touchLoc; //画面をタッチしている場所
    bool drawing; //ドロー中かどうか
};

testApp.mm

#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){	
	//iPhone基本設定
	ofRegisterTouchEvents(this);
	ofxAccelerometer.setup();
	ofxiPhoneAlerts.addListener(this);
	
    //横位置で起動
    iPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT);

    //画面の基本設定
    ofBackground(255, 255, 255);
    ofSetBackgroundAuto(false);
    drawing = false;
}

//--------------------------------------------------------------
void testApp::update(){

}

//--------------------------------------------------------------
void testApp::draw(){
    //もし現在ドロー中だったら
    if (drawing) {
        //色を黒に
        ofSetColor(0, 0, 0);
        //タッチしている場所に円を描く
        ofCircle(touchLoc.x, touchLoc.y, 10);
    }
}

//--------------------------------------------------------------
void testApp::exit(){

}

//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){
    //ドロー中に
    drawing = true;
    //タッチしている場所を記録
    touchLoc.x = touch.x;
    touchLoc.y = touch.y;
}

//--------------------------------------------------------------
void testApp::touchMoved(ofTouchEventArgs &touch){
    //タッチしている場所を記録
    touchLoc.x = touch.x;
    touchLoc.y = touch.y;
}

//--------------------------------------------------------------
void testApp::touchUp(ofTouchEventArgs &touch){
    //ドローを終了
    drawing = false;
}

//--------------------------------------------------------------
void testApp::touchDoubleTap(ofTouchEventArgs &touch){

}

//--------------------------------------------------------------
void testApp::lostFocus(){

}

//--------------------------------------------------------------
void testApp::gotFocus(){

}

//--------------------------------------------------------------
void testApp::gotMemoryWarning(){

}

//--------------------------------------------------------------
void testApp::deviceOrientationChanged(int newOrientation){

}

ステップ2:線で描く

  • ひとつ前のタッチした座標を記録しておく
  • ひとつ前のタッチした座標と現在の座標を線で結ぶ

testApp.h

#pragma once

#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 &touch);
    void touchMoved(ofTouchEventArgs &touch);
    void touchUp(ofTouchEventArgs &touch);
    void touchDoubleTap(ofTouchEventArgs &touch);
    
    void lostFocus();
    void gotFocus();
    void gotMemoryWarning();
    void deviceOrientationChanged(int newOrientation);

    ofPoint touchLoc; //現在タッチしている場所
    ofPoint lastTouchLoc; //ひとつ前のタッチしている場所
    bool drawing; //ドロー中かどうか
};

testApp.mm

#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){	
    //iPhone基本設定
	ofRegisterTouchEvents(this);
	ofxAccelerometer.setup();
	ofxiPhoneAlerts.addListener(this);
	
    //横位置で起動
    iPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT);
    
    //画面基本設定
    ofSetBackgroundAuto(false);
    ofBackground(255, 255, 255);
    ofSetLineWidth(3);
    ofEnableSmoothing();
    
    drawing = false;
}

//--------------------------------------------------------------
void testApp::update(){

}

//--------------------------------------------------------------
void testApp::draw(){
    //もしドロー中だったら
    if (drawing) {
        ofSetColor(0, 0, 0);
        //ひとつ前のタッチした座標から現在の座標まで線を描く
        ofLine(lastTouchLoc.x, lastTouchLoc.y, touchLoc.x, touchLoc.y);
    }
}

//--------------------------------------------------------------
void testApp::exit(){

}

//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){
    //タッチしている場所を記録
    touchLoc.x = touch.x;
    touchLoc.y = touch.y;
}

//--------------------------------------------------------------
void testApp::touchMoved(ofTouchEventArgs &touch){
    //ドロー中に
    drawing = true;
    //ひとつ前の座標を記録
    lastTouchLoc = touchLoc;
    //タッチしている場所を記録
    touchLoc.x = touch.x;
    touchLoc.y = touch.y;
}

//--------------------------------------------------------------
void testApp::touchUp(ofTouchEventArgs &touch){
    //ドローを終了
    drawing = false;
}

//--------------------------------------------------------------
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
    ofSetColor(255, 255, 255);
    ofRect(0, 0, ofGetWidth(), ofGetHeight());
}

//--------------------------------------------------------------
void testApp::lostFocus(){

}

//--------------------------------------------------------------
void testApp::gotFocus(){

}

//--------------------------------------------------------------
void testApp::gotMemoryWarning(){

}

//--------------------------------------------------------------
void testApp::deviceOrientationChanged(int newOrientation){

}

ステップ3:線の太さを変化させる

  • 前のタッチした座標から、現在の座標までの移動した距離を測定
  • 移動した距離に応じて線の太さを変化させる
    • 移動距離が長いときは細い線で
    • 移動距離が短いときには太い線で
  • 筆で書いたような効果

testApp.h

#pragma once

#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 &touch);
    void touchMoved(ofTouchEventArgs &touch);
    void touchUp(ofTouchEventArgs &touch);
    void touchDoubleTap(ofTouchEventArgs &touch);
    
    void lostFocus();
    void gotFocus();
    void gotMemoryWarning();
    void deviceOrientationChanged(int newOrientation);

    ofPoint touchLoc; //現在タッチしている場所
    ofPoint lastTouchLoc; //ひとつ前のタッチしている場所
    bool drawing; //ドロー中かどうか
};

testApp.mm

#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){	
    //iPhone基本設定
	ofRegisterTouchEvents(this);
	ofxAccelerometer.setup();
	ofxiPhoneAlerts.addListener(this);
	
    //横位置で起動
    iPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT);
    
    //画面基本設定
    ofSetBackgroundAuto(false);
    ofBackground(255, 255, 255);
    ofEnableSmoothing();
    drawing = false;
}

//--------------------------------------------------------------
void testApp::update(){

}

//--------------------------------------------------------------
void testApp::draw(){
    //ドロー中だったら
    if (drawing) {
        //前のタッチした座標と現在の座標の距離を算出
        float dist = ofDist(lastTouchLoc.x, lastTouchLoc.y, touchLoc.x, touchLoc.y);
        //線の太さを決定
        float lineWidth = 14 - dist * 0.5;
        //4ピクセルよりは細くならないように
        if (lineWidth < 4) {
            lineWidth = 4;
        }
        ofSetColor(0, 0, 0);
        //算出した太さで円を描く
        ofCircle(touchLoc.x, touchLoc.y, lineWidth);
    }
}

//--------------------------------------------------------------
void testApp::exit(){
    
}

//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){
    //タッチしている場所を記録
    touchLoc.x = touch.x;
    touchLoc.y = touch.y;
}

//--------------------------------------------------------------
void testApp::touchMoved(ofTouchEventArgs &touch){
    //ドロー中に
    drawing = true;
    //ひとつ前の座標を記録
    lastTouchLoc = touchLoc;
    //タッチしている場所を記録
    touchLoc.x = touch.x;
    touchLoc.y = touch.y;
}

//--------------------------------------------------------------
void testApp::touchUp(ofTouchEventArgs &touch){
    //ドローを終了
    drawing = false;
}

//--------------------------------------------------------------
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
    //ダブルタップで画面をリセット
    drawing = false;
    ofSetColor(255, 255, 255);
    ofRect(0, 0, ofGetWidth(), ofGetHeight());
}

//--------------------------------------------------------------
void testApp::lostFocus(){

}

//--------------------------------------------------------------
void testApp::gotFocus(){

}

//--------------------------------------------------------------
void testApp::gotMemoryWarning(){

}

//--------------------------------------------------------------
void testApp::deviceOrientationChanged(int newOrientation){

}

ステップ4:よりなめらかな線に

  • 線が細くなった際に、線が切れぎれになってしまう問題に対処
  • 座標を一気に移動するのではなく、補完していくことで、なめらかな線にしている

testApp.h

#pragma once

#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 &touch);
    void touchMoved(ofTouchEventArgs &touch);
    void touchUp(ofTouchEventArgs &touch);
    void touchDoubleTap(ofTouchEventArgs &touch);
    
    void lostFocus();
    void gotFocus();
    void gotMemoryWarning();
    void deviceOrientationChanged(int newOrientation);

    ofPoint touchLoc; //現在タッチしている場所
    ofPoint lastTouchLoc; //ひとつ前のタッチしている場所
    bool drawing; //ドロー中かどうか
    float interpolate; //補完の割合

};

testApp.mm

#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){	
    //iPhone基本設定
	ofRegisterTouchEvents(this);
	ofxAccelerometer.setup();
	ofxiPhoneAlerts.addListener(this);
	
    //横位置で起動
    iPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT);
    
    //画面基本設定
    ofSetBackgroundAuto(false);
    ofBackground(255, 255, 255);
    ofEnableSmoothing();
    drawing = false;
    
    //補完の割合を設定
    interpolate = 0.2;
}

//--------------------------------------------------------------
void testApp::update(){

}

//--------------------------------------------------------------
void testApp::draw(){
    if (drawing) {
        
        //前のタッチした座標と現在の座標の距離を算出
        float dist = ofDist(lastTouchLoc.x, lastTouchLoc.y, touchLoc.x, touchLoc.y);
        //線の太さを決定
        float lineWidth = 16 - dist * 0.2;
        //6ピクセルよりは細くならないように
        if (lineWidth < 6) {
            lineWidth = 6;
        }
        ofSetColor(0, 0, 0);
        //座標を一定の割合で補完している
        lastTouchLoc += (touchLoc - lastTouchLoc) * interpolate;
        //円を描く
        ofFill();
        ofCircle(lastTouchLoc.x, lastTouchLoc.y, lineWidth);
        ofNoFill();
        ofCircle(lastTouchLoc.x, lastTouchLoc.y, lineWidth);
    }
}

//--------------------------------------------------------------
void testApp::exit(){

}

//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){
    //タッチしている場所を記録
    touchLoc.x = touch.x;
    touchLoc.y = touch.y;
    //ひとつ前の座標を記録
    lastTouchLoc = touchLoc;
}

//--------------------------------------------------------------
void testApp::touchMoved(ofTouchEventArgs &touch){
    //ドロー中に
    drawing = true;
    
    //タッチしている場所を記録
    touchLoc.x = touch.x;
    touchLoc.y = touch.y;
}

//--------------------------------------------------------------
void testApp::touchUp(ofTouchEventArgs &touch){
    //ドローを終了
    drawing = false;
}

//--------------------------------------------------------------
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
    //ダブルタップで画面をリセット
    drawing = false;
    ofSetColor(255, 255, 255);
    ofRect(0, 0, ofGetWidth(), ofGetHeight());
}

//--------------------------------------------------------------
void testApp::lostFocus(){

}

//--------------------------------------------------------------
void testApp::gotFocus(){

}

//--------------------------------------------------------------
void testApp::gotMemoryWarning(){

}

//--------------------------------------------------------------
void testApp::deviceOrientationChanged(int newOrientation){

}

ステップ5:文字で描く

  • フォントを読みこんで、文字によって線を描く

testApp.h

#pragma once

#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 &touch);
    void touchMoved(ofTouchEventArgs &touch);
    void touchUp(ofTouchEventArgs &touch);
    void touchDoubleTap(ofTouchEventArgs &touch);
    
    void lostFocus();
    void gotFocus();
    void gotMemoryWarning();
    void deviceOrientationChanged(int newOrientation);
    
    ofPoint touchLoc;
    ofPoint lastTouchLoc;
    float interpolate;
    bool drawing;
    ofTrueTypeFont myFont;
    string letters;
    float size;
    int counter;
};

testApp.mm

#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){	
	//iPhone基本設定
	ofRegisterTouchEvents(this);
	ofxAccelerometer.setup();
	ofxiPhoneAlerts.addListener(this);
	
    //横位置で起動
    iPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT);
    
    //画面基本設定
    ofSetBackgroundAuto(false);
    ofBackground(255, 255, 255);
    ofEnableSmoothing();
    drawing = false;
    
    //表示する文字列を設定
    letters = "hello iphone!!";
    counter = 0;
    interpolate = 0.2;
}

//--------------------------------------------------------------
void testApp::update(){
    
}

//--------------------------------------------------------------
void testApp::draw(){
    //文字で線を描く
    float dist = ofDist(lastTouchLoc.x, lastTouchLoc.y, touchLoc.x, touchLoc.y);
    float fontSize = dist;
    if (fontSize < 12) {
        fontSize = 12;
    }
    myFont.loadFont("Georgia.ttf", dist);
    ofSetColor(0, 0, 0);
    string newLetter = letters.substr(counter, 1);
    size = myFont.stringWidth(newLetter);

    
    if (dist > size) {
        float angle = atan2(touchLoc.x - lastTouchLoc.x, lastTouchLoc.y - touchLoc.y);            
        
        ofPushMatrix();
        ofTranslate(touchLoc.x, touchLoc.y);
        ofRotateZ(angle * 180.0 / PI - 90);
        myFont.drawString(newLetter, 0, 0);
        ofPopMatrix();
        
        counter++;
        if (counter > letters.size()) {
            counter = 0;
        }
        lastTouchLoc = touchLoc;
    }
    
}

//--------------------------------------------------------------
void testApp::exit(){
    
}

//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){
    //タッチしている場所を記録
    touchLoc.x = touch.x;
    touchLoc.y = touch.y;
    //ひとつ前の座標を記録
    lastTouchLoc = touchLoc;
}

//--------------------------------------------------------------
void testApp::touchMoved(ofTouchEventArgs &touch){
    //ドロー中に
    drawing = true;
    
    //タッチしている場所を記録
    touchLoc.x = touch.x;
    touchLoc.y = touch.y;
}

//--------------------------------------------------------------
void testApp::touchUp(ofTouchEventArgs &touch){
    //ドローを終了
    drawing = false;
}

//--------------------------------------------------------------
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
    //ダブルタップで画面をリセット
    drawing = false;
    ofSetColor(255, 255, 255);
    ofRect(0, 0, ofGetWidth(), ofGetHeight());
}

//--------------------------------------------------------------
void testApp::lostFocus(){
    
}

//--------------------------------------------------------------
void testApp::gotFocus(){
    
}

//--------------------------------------------------------------
void testApp::gotMemoryWarning(){
    
}

//--------------------------------------------------------------
void testApp::deviceOrientationChanged(int newOrientation){
    
}

サンプルファイルのダウンロード

今回の授業で紹介したプログラムのソースコードは以下からダウンロードしてください。


ActionScript応用:Tweenライブラリの利用(2)

今日の内容

  • 先週に引き続き、Tweenライブラリについて学んでいきます
  • ユーザのアクションに反応する、Tweenを使用した様々な動きを試していきます

触れると拡大する物体

  • マウスで触れると(ROLL_OVER)、拡大します
  • マウスを離すと(ROLL_OUT)、もとの大きさに戻ります
  • Easingの関数や、拡大・縮小に要する時間をいろいろ変化させて、動きのニュアンスの違いを試してみましょう
import com.greensock.*;
import com.greensock.easing.*;

this.mc.addEventListener(MouseEvent.ROLL_OVER, rollOverHandler);
this.mc.addEventListener(MouseEvent.ROLL_OUT, rollOutHandler);

function rollOverHandler(e:MouseEvent):void {
	TweenLite.to(this.mc, 1, {scaleX:4.0, scaleY:4.0, ease:Elastic.easeOut});
}

function rollOutHandler(e:MouseEvent):void {
	TweenLite.to(this.mc, 1, {scaleX:1.0, scaleY:1.0, ease:Elastic.easeOut});
}
https://yoppa.org/wp-content/uploads/2010/07/01_touchEaseing.swf,550,400

触ろうとすると、逃げていく物体

  • マウスで触れようとすると、スルリすり抜けて逃げていきます
  • ランダムな値を指定するには、Math.random() という命令を使用します、Math.random() は、0〜1の範囲でランダムな値を生成します
import com.greensock.*;
import com.greensock.easing.*;

this.mc.addEventListener(MouseEvent.ROLL_OVER, rollOverHandler);

function rollOverHandler(e:MouseEvent):void {
	TweenLite.to(this.mc, 1, {x:Math.random()*550, y:Math.random()*400, rotation:Math.random()*360, ease:Elastic.easeOut});
}
https://yoppa.org/wp-content/uploads/2010/07/02_touchEaseing.swf,550,400

触れると色が変化する

  • 色を変化させるには、「tint」というプロパティを変化させます
  • tintを有効にするには、プラグインを読みこむ必要があります。冒頭で「import com.greensock.plugins.*」と指定します。
import com.greensock.*;
import com.greensock.easing.*;
import com.greensock.plugins.*;

TweenPlugin.activate([TintPlugin]);

this.mc.addEventListener(MouseEvent.ROLL_OVER, rollOverHandler);
this.mc.addEventListener(MouseEvent.ROLL_OUT, rollOutHandler);

function rollOverHandler(e:MouseEvent):void {
    var color:Number = Math.random() * 0xffffff;
    TweenLite.to(this.mc, 1, {scaleX:4.0, scaleY:4.0, tint:color, ease:Elastic.easeOut});
}

function rollOutHandler(e:MouseEvent):void {
    var color:Number = Math.random() * 0xffffff;
    TweenLite.to(this.mc, 1, {scaleX:1.0, scaleY:1.0, tint:color, ease:Elastic.easeOut});
}
https://yoppa.org/wp-content/uploads/2010/07/03_touchEaseingColor.swf,550,400

触れるとぼやける

  • 色の変化だけでなく、様々なエフェクトを利用可能です
  • プラグインを利用して、ぼかし(blur)をかけてみます。
import com.greensock.*;
import com.greensock.easing.*;
import com.greensock.plugins.*;

TweenPlugin.activate([TintPlugin]);

this.mc.addEventListener(MouseEvent.ROLL_OVER, rollOverHandler);
this.mc.addEventListener(MouseEvent.ROLL_OUT, rollOutHandler);

function rollOverHandler(e:MouseEvent):void {
    TweenMax.to(this.mc, 1, {scaleX:4.0, scaleY:4.0, blurFilter:{blurX:50, blurY:50}, ease:Elastic.easeOut});
}

function rollOutHandler(e:MouseEvent):void {
    TweenMax.to(this.mc, 1, {scaleX:1.0, scaleY:1.0, blurFilter:{blurX:0, blurY:0}, ease:Elastic.easeOut});
}
https://yoppa.org/wp-content/uploads/2010/07/04_touchEaseingBlur.swf,550,400

触れると光る

  • glow という効果を使用すると、全体が光っているような効果を生みだします
import com.greensock.*;
import com.greensock.easing.*;
import com.greensock.plugins.*;

TweenPlugin.activate([TintPlugin]);

this.mc.addEventListener(MouseEvent.ROLL_OVER, rollOverHandler);
this.mc.addEventListener(MouseEvent.ROLL_OUT, rollOutHandler);

function rollOverHandler(e:MouseEvent):void {
    TweenMax.to(this.mc, 1, {scaleX:4.0, scaleY:4.0, glowFilter:{color:0x91e600, alpha:1, blurX:30, blurY:30}, ease:Elastic.easeOut});
}

function rollOutHandler(e:MouseEvent):void {
    TweenMax.to(this.mc, 1, {scaleX:1.0, scaleY:1.0, glowFilter:{alpha:0, blurX:0, blurY:0}, ease:Elastic.easeOut});
}
https://yoppa.org/wp-content/uploads/2010/07/04_touchEaseingGlow.swf,550,400

ArduinoとProcessingの連携3:「植物シンセ」を作る

今日の内容

  • Arduinoからのデータを、Procssingを使用して「可聴化」する
  • データを音に変換する方法 – Processing minimライブラリについて
  • サウンドの生成
  • サウンドファイルを再生
  • 可変抵抗の値でサウンドをコントロール
  • 植物にとりつけたタッチセンサーで、音を鳴らす – 「植物シンセ」簡易版

Minimライブラリについて

  • Minimライブラリ – Processingで音を扱うためのライブラリ、Java Sound APIを使用している
  • 音に関する様々な機能が利用できる
    • サウンドの再生 – WAV, AIFF, AU, SND, MP3形式のサウンドファイルを読み込んで再生する
    • 録音:入力された音を、ディスクに直接、またはメモリー上のバッファーに録音可能
    • オーディオ入力:モノラル、ステレオのオーディオ入力の値を取得
    • オーディオ出力:モノラル、ステレオでサウンドを出力
    • 音響合成:音響合成のためのシンプルなインタフェイスを提供
    • エフェクト:サウンドエフェクトのためのインタフェイスを提供
    • FFT:高速フーリエ変換で、リアルタイムにスペクトル解析が可能
  • 今日の授業では、シンプルなサイン波の生成と、サウンドファイルの再生を使用

Minimライブラリを使用してみる

サイン波の生成

  • ステレオのサイン波を生成する
  • マウスのX座標で周波数を変化させる
  • マウスのY座標で音量を変化させる
import ddf.minim.*;
import ddf.minim.signals.*;

//Minimクラスのインスタンスminimを定義
Minim minim;
//オーディオをoutに出力する
AudioOutput out;
//サイン波の波形をsineに生成
SineWave sine;

void setup() {
    size(800, 400);
    //Minimクラスをインスタンス化
    minim = new Minim(this);
    //モノラルのオーディオ出力として、outを準備
    out = minim.getLineOut(Minim.MONO);
    //440Hz、音量1.0でサイン波を出力
    sine = new SineWave(440, 1.0, out.sampleRate());
    //ポルタメント(音程変化のなめらかさ)を200msecに
    sine.portamento(200);
    //生成したサイン波をオーディオ出力に追加
    out.addSignal(sine);
}

void draw() {
    background(0);
}

void mouseMoved() {
    //マウスのX座標を周波数に設定
    //X座標の(0, width)を、周波数(40, 2000)にマップ
    float freq = map(mouseX, 0, width, 40, 2000);
    sine.setFreq(freq);
    //マウスのY座標を音量に設定
    //Y座標の(0, height)を、周波数(1.0, 0.0)にマップ
    float amp = map(mouseY, 0, height, 1.0, 0.0);
    sine.setAmp(amp);
}

void stop() {
    //プログラムの終了処理
    out.close();
    minim.stop();
    super.stop();
}

波形の表示

  • 波形を表示する
import ddf.minim.*;
import ddf.minim.signals.*;

Minim minim;
AudioOutput out;
SineWave sine;

void setup() {
    size(800, 400);
    frameRate(60);
    smooth();
    strokeWeight(2);
  
    minim = new Minim(this);
    out = minim.getLineOut(Minim.MONO);
    sine = new SineWave(440, 1.0, out.sampleRate());
    sine.portamento(200);
    out.addSignal(sine);
}

void draw() {
    //波形を表示
    background(0);
    stroke(0,255,0);
    //Y座標の原点を画面の中心に移動
    translate(0, height/2);
    //バッファーに格納されたサンプル数だけくりかえし
    for(int i = 0; i < out.bufferSize() - 1; i++) {
	//サンプル数から、画面の幅いっぱいに波形を表示するようにマッピング
	float x = map(i, 0, out.bufferSize(), 0, width);
	//画面の高さいっぱになるように、サンプルの値をマッピング
	float y = map(out.mix.get(i), 0, 1.0, 0, height/2);
	//値をプロット
	point(x,  y);
    }
}

void mouseMoved() {
    float freq = map(mouseX, 0, width, 20, 1000);
    sine.setFreq(freq);
    float amp = map(mouseY, 0, height, 1, 0);
    sine.setAmp(amp);
}

void stop() {
    out.close();
    minim.stop();
    super.stop();
}

複数の音を鳴らす

  • 2種類のsin波を生成し、同時に鳴らす
  • 波形の表示を工夫してみる – 1つめのSin波のレベルをX軸に、2つめのSin波のレベルをY軸にして波形をプロットとするとどうなるか?
  • リサージュ図形 – 互いに直交する二つの単振動を順序対として得られる点の軌跡が描く平面図形
import ddf.minim.*;
import ddf.minim.signals.*;

Minim minim;
AudioOutput out;
//2つのサイン波を定義
SineWave sine1, sine2; 

void setup() {
    size(600, 600);
    frameRate(30);
    smooth();
    strokeWeight(2);
  
    minim = new Minim(this);
    //ステレオで出力
    out = minim.getLineOut(Minim.STEREO);
  
    //1つめのサイン波
    sine1 = new SineWave(440, 0.8, out.sampleRate());
    sine1.portamento(200);
    //左から出力
    sine1.setPan(-1);
    //オーディオ出力に追加
    out.addSignal(sine1);
  
    //2つめのサイン波
    sine2 = new SineWave(440, 0.8, out.sampleRate());
    sine2.portamento(200);
    //右から出力
    sine2.setPan(1);
    //オーディオ出力に追加
    out.addSignal(sine2);

    background(0);
}

void draw() {
    //背景を黒にフェードさせる
    fill(0, 0, 0, 15);
    noStroke();
    rect(0, 0, width, height);
  
    //色の設定
    noFill();
    stroke(0,255,0);
    //画面の中心へ原点を移動
    translate(width/2, height/2);
    //波形を描画
    for(int i = 0; i < out.bufferSize() - 1; i++) {
	//X座標を右チャンネンル、Y座標を左チャンネルにする
	point(out.right.get(i) * width/2,  out.left.get(i) * height/2);
    }
}

void mouseMoved() {
    float freq1 = map(mouseX, 0, width, 20, 1000);
    sine1.setFreq(freq1);
   
    float freq2 = map(mouseY, 0, height, 20, 1000);
    sine2.setFreq(freq2);
}

void stop() {
    out.close();
    minim.stop();
    super.stop();
}

サウンドファイルの読み込み、再生

  • サウンドファイルを読み込んで再生する
import ddf.minim.*;

Minim minim;
//サウンドファイルのプレーヤーを定義
AudioPlayer player;

void setup()
{
    size(800, 400, P3D);
    minim = new Minim(this);
    //サウンドファイルを読み込む
    player = minim.loadFile("anton.aif", 2048);
    //サウンドファイルを再生
    player.play();
}

void draw() {
    //波形を描く
    background(0);
    stroke(0,255,0);
    //左チャンネルを上半分に描く
    //y座標の原点を移動
    translate(0, height/4);
    for(int i = 0; i < player.bufferSize() - 1; i++) {
	//サンプルの値を画面にあわせてマッピング
	float x = map(i, 0, player.bufferSize(), 0, width);
	float y = map(player.left.get(i), 0, 1, 0, height/4);
	point(x,  y);
    }
    //右チャンネルを上半分に
    //y座標の原点を移動
    translate(0, height/2);
    for(int i = 0; i < player.bufferSize() - 1; i++) {
	//サンプルの値を画面にあわせてマッピング
	float x = map(i, 0, player.bufferSize(), 0, width);
	float y = map(player.right.get(i), 0, 1, 0, height/4);
	point(x, y);
    }
}

//マウスボタンを押すとサンプルを再生
void mousePressed() {
    //サウンドを巻き戻し
    player.rewind();
    //サウンドを再生する
    player.play();
}

void stop() {
    player.close();
    minim.stop();
    super.stop();
}

ArduinoからProcessing+Minimをコントロールしてみる

センサーの値でSin波をコントロール

  • 「複数の音を鳴らす」のサンプルを改造
  • 2つのアナログ入力の値を、2つのSin波のそれぞれの周波数に対応させてみる
  • リサージュ図形の表示の部分はそのまま

Arduino + ブレッドボード配線図

Arduino側コード

//アナログ入力の数を定義する
#define NUM 2

//アナログ入力の値を格納する配列
int val[NUM]; 

void setup() {
  //シリアル通信の開始
  Serial.begin(9600);
}

void loop() {
  //アナログ入力の数だけ繰り返し
  for(int i=0; i<NUM; i++){
    //アナログ入力の値を読み込み
    val[i] = analogRead(i);
  }

  //Processingからの読み込み完了通知を待つ
  if(Serial.available() > 0){
    //アナログ入力の数だけ繰り返し
    for(int i=0; i<NUM; i++){
      //上位 8bit を送出
      Serial.print(val[i] >> 8, BYTE);
      //下位 8bit を送出
      Serial.print(val[i] & 255, BYTE);
    }
    //合図用データを読み込んでバッファ領域を空にする
    Serial.read(); 
  }
}

Processing側コード

import processing.opengl.*;
import ddf.minim.*;
import ddf.minim.signals.*;
import processing.serial.*;

Minim minim;
AudioOutput out;
SineWave sine1, sine2;

//アナログ入力の数を指定
int NUM = 2;
//Serialクラスのインスタンス
Serial myPort;
//Serialより読み込んだデータを格納する配列
int[] val = new int[NUM]; 

void setup() {
    size(600, 600, OPENGL);
    smooth();
    strokeWeight(2);

    //ポートの名前を取得
    String portName = Serial.list()[1];
    //Serialクラスをインスタンス化
    myPort = new Serial(this, portName, 9600);
 
    minim = new Minim(this);
    out = minim.getLineOut(Minim.STEREO);

    sine1 = new SineWave(440, 0.8, out.sampleRate());
    sine1.portamento(200);
    sine1.setPan(-1);
    out.addSignal(sine1);

    sine2 = new SineWave(440, 0.8, out.sampleRate());
    sine2.portamento(200);
    sine2.setPan(1);
    out.addSignal(sine2);
    background(0);
}

void draw() {
    fill(0, 0, 0, 15);
    noStroke();
    rect(0, 0, width, height);

    noFill();
    stroke(0,255,0);
    translate(width/2, height/2);
    for(int i = 0; i < out.bufferSize() - 1; i++) {
	float x = map(i, 0, out.bufferSize(), 0, width);
	point(out.right.get(i) * width/2,  out.left.get(i) * height/2);
    }
}

//シリアル入力を検出した際に発生するイベント
void serialEvent(Serial p) {
    //もし、バッファーにアナログ入力の数の2倍(上位8bitと下位8bit)あれば
    if(myPort.available() > NUM * 2 - 1) {
	//入力の数だけ繰り返し
	for(int i=0; i<NUM; i++) {
	    //上位8bitを取得
	    int val_high = myPort.read();
	    //下位8bitを取得
	    int val_low = myPort.read();
	    //2つのbit列を合成
	    val[i] = ( val_high << 8 ) + val_low;
	    //もし全ての入力を処理したら
	    if(i == NUM-1) {
		//読み込み完了の合図を送る
		myPort.write(255);
	    }
	}
    }

    //取得したシリアルの値を周波数にマッピング
    //サイン波1
    float freq1 = map(val[0], 0, 1023, 20, 1000);
    sine1.setFreq(freq1);
    //サイン波2
    float freq2 = map(val[1], 0, 1023, 20, 1000);
    sine2.setFreq(freq2);
}

//マウスが押されたら通信開始
void mousePressed() {
    //バッファ領域を空にする
    myPort.clear();
    //合図用データを送る
    myPort.write(255);
}

void stop() {
    out.close();
    minim.stop();
    super.stop();
}

「植物シンセ」タッチセンサーでサウンドファイルを再生

まずはスイッチで実験

  • タクトスイッチで簡単な回路を作成して実験する

Arduino+ブレッドボード回路図

Arduino側コード

//スイッチの入力ピンを定義
const int buttonPin = 2;
//ボタンの状態(初期値:0)
int buttonState = 0;

void setup() {
    //シリアル通信開始
    Serial.begin(9600);
    //スイッチの入力を定義
    pinMode(buttonPin, INPUT);     
}

void loop(){
    //デジタル入力の状態を取得
    buttonState = digitalRead(buttonPin);
    //もしスイッチが押されていたら
    if (buttonState == HIGH) {  
	//シリアルから値255を出力
	Serial.print(255, BYTE);
    }
}

Processing側コード

import ddf.minim.*;
import processing.serial.*;

Minim minim;
AudioPlayer player;

Serial myPort;
int val;

void setup() {
    size(800, 400, P3D);
    //ポートの名前を取得
    String portName = Serial.list()[1];
    //Serialクラスをインスタンス化
    myPort = new Serial(this, portName, 9600);

    minim = new Minim(this);
    player = minim.loadFile("anton.aif", 2048);
}

void draw() {
    //波形を描く
    background(0);
    stroke(0,255,0);
    for(int i = 0; i < player.bufferSize() - 1; i++) {
	float x = map(i, 0, player.bufferSize(), 0, width);
	point(x,  height / 4 + player.left.get(i) * height/4);
    }
    for(int i = 0; i < player.bufferSize() - 1; i++) {
	float x = map(i, 0, player.bufferSize(), 0, width);
	point(x,  height / 4 * 3 + player.right.get(i) * height/4);
    }
}

//シリアル入力を検出した際に発生するイベント
void serialEvent(Serial p) {
    //ポートの状態を確認
    if(myPort.available() > 1) {
	//シリアルからの入力値を取得
	val = myPort.read();
    }
    //もし値が0より大きく、まだ音を再生していなかったら
    if(val > 0 && player.isPlaying() == false){
	//再生位置を巻き戻して、サウンドを再生
	player.rewind();
	player.play();
    }
}

void stop() {
    player.close();
    minim.stop();
    super.stop();
}

植物にとりつけたタッチセンサーをスイッチにしてみる

  • 矢坂先生の前回と前々回の講義を参考にする
  • タッチセンサーでスイッチを切り替えるArduinoのプログラムを利用

Arduino+ブレッドボード回路図

  • 8番、9番のデジタル入力にタッチセンサを接続
  • 8番ピンの手前に1MΩの抵抗を入れる

Arduino側コード

#define POWER 8
#define METAL 9

int filter = 0; 
int counter = 0;

void setup(){
    Serial.begin(9600);
    pinMode(POWER,OUTPUT); 
    pinMode(METAL,INPUT);
}

void loop() {
    counter = 0;
    digitalWrite(POWER, HIGH);
    while (digitalRead(METAL)!=HIGH){
	counter++; 	 	 
    }
    delay(1);
    digitalWrite(POWER, LOW);
    filter += (counter - filter) / 2;
    if(filter > 5){
	Serial.print(255, BYTE);
    } 
}

Processing側コード

import ddf.minim.*;
import processing.serial.*;

Minim minim;
AudioPlayer player;

Serial myPort;
int val;

void setup() {
    size(800, 400, P3D);
    //ポートの名前を取得
    String portName = Serial.list()[1];
    //Serialクラスをインスタンス化
    myPort = new Serial(this, portName, 9600);

    minim = new Minim(this);
    player = minim.loadFile("anton.aif", 2048);
}

void draw() {
    //波形を描く
    background(0);
    stroke(0,255,0);
    for(int i = 0; i < player.bufferSize() - 1; i++) {
	float x = map(i, 0, player.bufferSize(), 0, width);
	point(x,  height / 4 + player.left.get(i) * height/4);
    }
    for(int i = 0; i < player.bufferSize() - 1; i++) {
	float x = map(i, 0, player.bufferSize(), 0, width);
	point(x,  height / 4 * 3 + player.right.get(i) * height/4);
    }
}

//シリアル入力を検出した際に発生するイベント
void serialEvent(Serial p) {
    //ポートの状態を確認
    if(myPort.available() > 1) {
	//シリアルからの入力値を取得
	val = myPort.read();
    }
    //もし値が0より大きく、まだ音を再生していなかったら
    if(val > 0 && player.isPlaying() == false){
	//再生位置を巻き戻して、サウンドを再生
	player.rewind();
	player.play();
    }
}

void stop() {
    player.close();
    minim.stop();
    super.stop();
}

音をランダムに再生する (Processing側コードを変更)

import ddf.minim.*;
import processing.serial.*;

Minim minim;
AudioPlayer player;

Serial myPort;
int val;

String [] soundfile = new String[8];

void setup() {
  size(800, 400, P3D);
  soundfile[0] = "anton.aif";
  soundfile[1] = "cello-f2.aif";
  soundfile[2] = "cherokee.aif";
  soundfile[3] = "drumLoop.aif";
  soundfile[4] = "jongly.aif";
  soundfile[5] = "rainstick.aif";
  soundfile[6] = "sho0630.aif";
  soundfile[7] = "vibes-a1.aif";
  //ポートの名前を取得
  String portName = Serial.list()[1];
  //Serialクラスをインスタンス化
  myPort = new Serial(this, portName, 9600);
  minim = new Minim(this);
  player = minim.loadFile(soundfile[int(random(7))], 2048);
}

void draw() {
  //波形を描く
  background(0);
  stroke(0,255,0);
  for(int i = 0; i < player.bufferSize() - 1; i++) {
    float x = map(i, 0, player.bufferSize(), 0, width);
    point(x,  height / 4 + player.left.get(i) * height/4);
  }
  for(int i = 0; i < player.bufferSize() - 1; i++) {
    float x = map(i, 0, player.bufferSize(), 0, width);
    point(x,  height / 4 * 3 + player.right.get(i) * height/4);
  }
}

//シリアル入力を検出した際に発生するイベント
void serialEvent(Serial p) {
  //ポートの状態を確認
  if(myPort.available() > 1) {
    //シリアルからの入力値を取得
    val = myPort.read();
  }
  //もし値が0より大きく、まだ音を再生していなかったら
  //if(val > 0 && player.isPlaying() == false){
  if(val > 0){
    //再生位置を巻き戻して、サウンドを再生
    player = minim.loadFile(soundfile[int(random(7))], 2048);
    player.rewind();
    player.play();
  }
}

void stop() {
  player.close();
  minim.stop();
  super.stop();
}

openFrameworks for iPhone:画像ファイルを読み込む、アニメーションを作成する

今日の内容

  • openFrameworksに画像ファイルを読み込む
  • 画像を連続して読み込んで、手書きアニメーションに挑戦!!

画像ファイルを読み込む

  • openFrameworksで、画像を扱うには、ofImageクラスを使用する
  • ofImage – freeImageライブラリをopenFrameworksで使用しやすいようにしたクラス
  • 主要な画像フォーマットに対応(PNG, JPEG, TIFF, BMP, GIF など)
  • ofImageを利用して読み込む画像ファイルは、[プロジェクトのフォルダ]/bin/data/ 以下に配置する

画像を読み込む手順

  • ofImageのインスタンスを生成
    • 例) ofImage myImage;
    • ofImageクラスのインスタンスmyImageを生成
  • 画像ファイルを読み込み
    • 例) myImage.loadImage("hoge.png");
    • bin/data フォルダ内にある、hoge.pngファイルを読み込む
  • 画像を表示
    • 例) myImage.draw(0, 0);
    • 座標(0, 0)を左上にして、画像を表示

画像を1枚読み込んで表示する

testApp.h

#pragma once

#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 &touch);
    void touchMoved(ofTouchEventArgs &touch);
    void touchUp(ofTouchEventArgs &touch);
    void touchDoubleTap(ofTouchEventArgs &touch);
    
    void lostFocus();
    void gotFocus();
    void gotMemoryWarning();
    void deviceOrientationChanged(int newOrientation);
    
    //ofImageクラスのインスタンスmyImageを生成
    ofImage myImage;
    
};

testApp.mm

#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){	
    //iPhone初期設定
	ofRegisterTouchEvents(this);
	ofxAccelerometer.setup();
	ofxiPhoneAlerts.addListener(this);
	ofBackground(127,127,127);
    
    //画像ファイルの読み込み
    myImage.loadImage("myImage.png");
}

//--------------------------------------------------------------
void testApp::update(){

}

//--------------------------------------------------------------
void testApp::draw(){
    //読み込んだ画像ファイルを座標(0, 0)を基準点にして描画
    myImage.draw(0, 0);
}

//--------------------------------------------------------------
void testApp::exit(){

}

//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){

}

//--------------------------------------------------------------
void testApp::touchMoved(ofTouchEventArgs &touch){

}

//--------------------------------------------------------------
void testApp::touchUp(ofTouchEventArgs &touch){

}

//--------------------------------------------------------------
void testApp::touchDoubleTap(ofTouchEventArgs &touch){

}

//--------------------------------------------------------------
void testApp::lostFocus(){

}

//--------------------------------------------------------------
void testApp::gotFocus(){

}

//--------------------------------------------------------------
void testApp::gotMemoryWarning(){

}

//--------------------------------------------------------------
void testApp::deviceOrientationChanged(int newOrientation){

}

複数の画像を読み込んで順番に表示する

testApp.h

#pragma once

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

#define FRAMENUM 12 //読み込む画像の枚数

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

	void lostFocus();
	void gotFocus();
	void gotMemoryWarning();
	void deviceOrientationChanged(int newOrientation);
    
    //ofImageのインスタンスの配列を生成
    ofImage myImage[FRAMENUM];
    //現在のフレーム数を記録する変数
    int currentFrame;
};

testApp.mm

#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){	
    //iPhone初期設定
	ofRegisterTouchEvents(this);
	ofxAccelerometer.setup();
	ofxiPhoneAlerts.addListener(this);
	ofBackground(255, 255, 255);
    ofSetFrameRate(24);
    
    //連番がふられた画像を順番に読み込み
    for (int i = 0; i < FRAMENUM; i++) {
        //ファイル名を一時的に格納する文字列
        char char1[32];
        //連番のファイル名を生成
        sprintf(char1, "images/myAnim%04d.png", i+1);
        //画像をofImageのインスタンスの配列に読み込み
        myImage[i].loadImage(char1);
    }
    //再生フレームを0から始める
    currentFrame = 0;
}

//--------------------------------------------------------------
void testApp::update(){

}

//--------------------------------------------------------------
void testApp::draw(){
    //現在のフレームの画像を表示
    myImage[currentFrame].draw(0, 0);
    
    //フレーム数をひとつ進める
    currentFrame++;
    //もし指定した枚数よりフレーム数が大きくなったら
    //フレーム数をリセット
    if (currentFrame > FRAMENUM - 1) {
        currentFrame = 0;
    }
}

//--------------------------------------------------------------
void testApp::exit(){

}

//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){

}

//--------------------------------------------------------------
void testApp::touchMoved(ofTouchEventArgs &touch){

}

//--------------------------------------------------------------
void testApp::touchUp(ofTouchEventArgs &touch){

}

//--------------------------------------------------------------
void testApp::touchDoubleTap(ofTouchEventArgs &touch){

}

//--------------------------------------------------------------
void testApp::lostFocus(){

}

//--------------------------------------------------------------
void testApp::gotFocus(){

}

//--------------------------------------------------------------
void testApp::gotMemoryWarning(){

}

//--------------------------------------------------------------
void testApp::deviceOrientationChanged(int newOrientation){

}

テンプレートをもとにオリジナルのアニメーションを作成してみる

  • 作成する画像のフォーマットは、320 x 480のPNGファイル
  • 作成した画像は順番に、myAnim[番号].png という名前で保存していく、番号は0詰めで4桁で。
    • myAnim0001.png、myAnim0002.png、myAnim0003
  • 作成した枚数で、testApp.h で定義されている定数"FRAMENUM"を変更する
    • #define FRAMENUM 枚数
  • まずは2枚から
  • うまくいったら、枚数を増やしてみる

サンプルファイルのダウンロード

今回の授業で紹介したプログラムのソースコードは以下からダウンロードしてください。