yoppa.org


多摩美 - “iTamabi” – iPhoneアプリ開発プロジェクト 2010

openFrameworks + iPhone マルチタッチイベントの取得、音アプリを作る

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

サンプルファイルは以下のリンクよりダウンロードしてください。

マルチタッチイベントの取得

マルチタッチの情報が取得できるイベント

定義 機能
touchDown 画面をタッチした瞬間に実行される
touchMoved 画面をタッチして移動した際に実行される
touchUp タッチしていた画面から離れた瞬間実行される
touchDoubleTap ダブルタップ(素早く2回画面をタップ)した瞬間に実行される

タッチされた情報は、以下のように格納される

  • touch.id – 現在タッチされたポイントのID (0〜4)
  • touch.x – 現在タッチされたポイントのx座標
  • touch.y- 現在タッチされたポイントのy座標
  • とりだしたタッチの座標情報は、配列に格納すると便利
  • 配列 – 簡単な配列の説明「情報のロッカーみたいなもの」

マルチタッチイベントの取得1

  • タッチした全ての場所に円を描く
  • ofPointクラスを利用して、タッチした座標を1つのインスタンス(変数)で管理している
  • [ofPointのインスタンス].x – X座標
  • [ofPointのインスタンス].y – Y座標
  • さらにそのofPointのクラスを配列にして、5つのポイントをtouchLocという配列で一括して管理している
  • touchDownした際に配列toucLocに座標を記録して、drawで記録された座標を中心に円を描いている

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);

  //タッチした座標の配列(5コ)
  ofPoint touchLoc[5];
};

testApp.mm

#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){
  // タッチイベントの登録
  ofRegisterTouchEvents(this);
  // 加速度センサの初期化
  ofxAccelerometer.setup();
  // iPhone警告メッセージの取得
  ofxiPhoneAlerts.addListener(this);

  //背景色を黒に
  ofBackground(0,0,0);
  //アルファ値を使用する
  ofEnableAlphaBlending();
  //円の解像度の設定を32に
  ofSetCircleResolution(32);

  //タッチポイントを初期化、画面の外に
  for (int i=0; i<5; i++) {
    touchLoc[i].set(-100, -100);
  }
}

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

}

//--------------------------------------------------------------
void testApp::draw(){
  //タッチした位置を取得して、円を描く
  for (int i=0; i<5; i++) {
    ofSetColor(31, 127, 255,127);
    ofCircle(touchLoc[i].x, touchLoc[i].y, 60);
    ofSetColor(255, 255, 255);
    ofDrawBitmapString("touch:"
		       + ofToString(i,0)
		       + " = ("
		       + ofToString(touchLoc[i].x,0)
		       + ", "
		       + ofToString(touchLoc[i].y,0)
		       + ")",
		       2, 12*i+12);
  }
}

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

}

//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){
  //タッチした場所を取得
  touchLoc[touch.id].set(touch.x, touch.y);
}

//--------------------------------------------------------------
void testApp::touchMoved(ofTouchEventArgs &touch){
  //タッチしながら動かした場所を取得
  touchLoc[touch.id].set(touch.x, touch.y);
}

//--------------------------------------------------------------
void testApp::touchUp(ofTouchEventArgs &touch){
  //画面から指が離れたら、位置を画面外に
  touchLoc[touch.id].set(-100, -100);
}

//--------------------------------------------------------------
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[5];
  //円のサイズ
  float circleSize[5];
};

testApp.mm

#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){
  // タッチイベントの登録
  ofRegisterTouchEvents(this);
  // 加速度センサの初期化
  ofxAccelerometer.setup();
  // iPhone警告メッセージの取得
  ofxiPhoneAlerts.addListener(this);

  //背景色を黒に
  ofBackground(0,0,0);
  //アルファ値を使用する
  ofEnableAlphaBlending();
  //円の解像度の設定を32に
  ofSetCircleResolution(128);
  ofSetFrameRate(30);

  //タッチポイントを初期化、画面の外に
  for (int i=0; i<5; i++) {
    touchLoc[i].set(-100, -100);
  }
}

//--------------------------------------------------------------
void testApp::update(){
  //タッチポイントの数だけくりかえし
  for (int i=0; i<5; i++) {
    //もし円のサイズが0より大きく、画面の高さよりも小さかったら
    if(circleSize[i]>0 && circleSize[i]<ofGetHeight()){
      //円のサイズを増加させる
      circleSize[i]+=3;
    } else {
      //それ以外だったら、円の大きさを0にリセット
      circleSize[i]=0;
    }

  }
}

//--------------------------------------------------------------
void testApp::draw(){
  //タッチした位置を取得して、円を描く
  for (int i=0; i<5; i++) {
    if(circleSize[i]>0 && circleSize[i]<ofGetHeight()){
      ofSetColor(31,127,255,100);
      //拡大する円のサイズを指定して円を描く
      ofCircle(touchLoc[i].x, touchLoc[i].y, circleSize[i]);
      ofSetColor(255, 255,255);
    }
  }
  //ログの出力
  for (int i=0; i<5; i++) {
    ofDrawBitmapString("touch:"
		       + ofToString(i,0)
		       + " = ("
		       + ofToString(touchLoc[i].x,0)
		       + ","
		       + ofToString(touchLoc[i].y,0)
		       + "), size = "
		       + ofToString(circleSize[i], 0),
		       2, 12*i+2);
  }
}

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

}

//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){
  //タッチした場所を取得
  touchLoc[touch.id].set(touch.x, touch.y);
  circleSize[touch.id]=1;
}

//--------------------------------------------------------------
void testApp::touchMoved(ofTouchEventArgs &touch){
  //タッチしながら動かした場所を取得
  touchLoc[touch.id].set(touch.x, touch.y);
}

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

}

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

}

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

}

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

}

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

}

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

}

音を鳴らす

  • iPhoneの音系アプリの紹介 – Bloom、SoundDrop、Rain、VectorRain ..etc
  • openFrameworksを使うと、こうした「音のおもちゃ」が簡単に作れる!!


Bloom


SounDrop


Rain

  • openFrameworks for iPhoneで音を鳴らすには – OpenALを使用する
  • OpenAL – クロスプラットフォームのオーディオAPI
  • 3次元定位オーディオを効率よく表現するように設計されている
  • OpenGLとの相性が良い
  • openFrameworksで、OpenALを利用するプロジェクトには、ofxALSoundPlayerアドオンのファイルが入っている必要がある
  • 「グループとファイル」の「addons」を右クリックして、「追加」→「既存のファイル」を選択
  • ofxALSoundPlayerのフォルダを追加する
  • [oFのフォルダ]/apps/iPhoneSpecificExamples/OpenAlExample/src/ofxALSoundPlayer


ofxALSoundPlayerの場所


アドオンの追加

caf形式のサウンドファイルの準備

  • iPhoneでサウンドファイルを扱うには、Core Audio Format(.caf)という形式に変換する必要がある
  • ターミナルから、”/usr/bin/afconvert”コマンドを使用して、AIFFファイルをCAFファイルに変換することが可能
% afconvert -f caff -d ima4 -d LEI16@22050 -c 1 -o [出力ファイル名].caf [入力ファイル名].wav

OpenALの利用1 – マルチタッチとの組み合せ

  • マルチタッチのサンプルと組み合わせてみる
  • touchDownした瞬間に、音を鳴らす
  • X座標の情報を、音の左右の定位(パン)に利用
  • Y座標の情報を、音程に利用

testApp.h

#pragma once

#include "ofMain.h"
#include "ofxiPhone.h"
#include "ofxiPhoneExtras.h"
#include "ofxALSoundPlayer.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);

  //OpenALを利用して音を再生するプレーヤー(5つ)
  ofxALSoundPlayer synth[5];
  //タッチした座標情報(5つ)
  ofPoint touchLoc[5];
  //円の大きさ情報(5つ)
  float circleSize[5];
};

testApp.mm

#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){
  // タッチイベントの登録
  ofRegisterTouchEvents(this);
  // 加速度センサの初期化
  ofxAccelerometer.setup();
  // iPhone警告メッセージの取得
  ofxiPhoneAlerts.addListener(this);

  //背景色を黒に
  ofBackground(0,0,0);
  //アルファ値を使用する
  ofEnableAlphaBlending();
  //円の解像度の設定を32に
  ofSetCircleResolution(128);
  ofSetFrameRate(30);

  //タッチポイントを初期化、画面の外に
  for (int i=0; i<5; i++) {
    touchLoc[i].set(-100, -100);
  }

  //CAFファイルをロードして、Synthにわりあて
  for(int i=0;i<5;i++){
    synth[i].loadSound("glockenspiel.caf");
  }
}

//--------------------------------------------------------------
void testApp::update(){
  //タッチポイントの数だけくりかえし
  for (int i=0; i<5; i++) {
    //もし円のサイズが0より大きく、画面の高さよりも小さかったら
    if(circleSize[i]>0 && circleSize[i]<ofGetHeight()){
      //円のサイズを増加させる
      circleSize[i]+=3;
    } else {
      //円の大きさを0にリセット
      circleSize[i]=0;
    }
  }
}

//--------------------------------------------------------------
void testApp::draw(){
  //タッチした位置を取得して、円を描く
  for (int i=0; i<5; i++) {
    if(circleSize[i]>0 && circleSize[i]<ofGetHeight()){
      ofSetColor(31,127,255,100);
      //拡大する円のサイズを指定して円を描く
      ofCircle(touchLoc[i].x, touchLoc[i].y, circleSize[i]);
      ofSetColor(255, 255,255);
    }
  }
  //ログの出力
  for (int i=0; i<5; i++) {
    ofDrawBitmapString("touch:"
		       + ofToString(i,0)
		       + " = ("
		       + ofToString(touchLoc[i].x,0)
		       + ","
		       + ofToString(touchLoc[i].y,0)
		       + "), size = "
		       + ofToString(circleSize[i], 0),
		       2, 12*i+6);
  }
}

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

}

//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){
  //タッチした場所を取得
  touchLoc[touch.id].set(touch.x, touch.y);
  circleSize[touch.id]=1;
  synth[touch.id].setPitch(0.5 + touch.y / ofGetHeight());
  synth[touch.id].setLocation(touch.x/(float)ofGetWidth()*2-1.0, 0,0);
  synth[touch.id].play();
}

//--------------------------------------------------------------
void testApp::touchMoved(ofTouchEventArgs &touch){
  //タッチしながら動かした場所を取得
  touchLoc[touch.id].set(touch.x, touch.y);
  synth[touch.id].setPitch(0.5 + touch.y / ofGetHeight());
  synth[touch.id].setLocation(touch.x/(float)ofGetWidth()*2-1.0, 0,0);
}

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

}

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

}

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

}

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

}

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

}

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

}

OpenALの利用2 – Bloomっぽいアプリを作る

  • タッチした演奏情報を記録して、くりかえし演奏させてみたい (超簡易シーケンサー?)
  • 次の方法で簡単に実装可能
  • タッチした瞬間に音を鳴らす
  • タッチした座標を中心にして、円が一定速度で拡大していく
  • 円が一定の大きさより大きくなったら、最小のサイズに戻す
  • 最小のサイズに戻す際に、また音を鳴らす
  • 調整のある音階を鳴らしてみたい
  • 音階の比率をあらかじめ定義しておく
  • (純正律の)ドレミファソラシは、下記の比率になる
  • 1 : 9/8 : 5/4 : 4/3 : 3/2 : 5/3 : 15/8
  • タッチした際のY座標からピッチを決めるようにする

testApp.h

#pragma once

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

//最大発音数を定義 (20)
#define NUM_SYNTH 20

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);

  //サウンドプレーヤーの配列
  ofxALSoundPlayer synth[NUM_SYNTH];
  //円の大きさ情報の配列
  float circleSize[NUM_SYNTH];
  //タッチした座標情報の配列
  ofPoint touchLoc[NUM_SYNTH];
  //現在タッチした数(0〜20)
  int currentTouch;
  //音階を生成するための比率
  float pitchRatio[7];
  //演奏した音程の記録
  float noteTable[NUM_SYNTH];
};

testApp.mm

#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){
  // タッチイベントの登録
  ofRegisterTouchEvents(this);
  // 加速度センサの初期化
  ofxAccelerometer.setup();
  // iPhone警告メッセージの取得
  ofxiPhoneAlerts.addListener(this);

  //背景色を黒に
  ofBackground(0,0,0);
  //アルファ値を使用する
  ofEnableAlphaBlending();
  //円の解像度の設定を32に
  ofSetCircleResolution(128);
  ofSetFrameRate(30);

  //タッチポイントを初期化、画面の外に
  for (int i=0; i<NUM_SYNTH; i++) {
    touchLoc[i].set(-100, -100);
    synth[i].loadSound("glockenspiel.caf");
  }

  //現在タッチされた数
  currentTouch = 0;

  //音階を生成
  pitchRatio[0]= 1.0;
  pitchRatio[1]= 9.0/8.0;
  pitchRatio[2]= 5.0/4.0;
  pitchRatio[3]= 4.0/3.0;
  pitchRatio[4]= 3.0/2.0;
  pitchRatio[5]= 5.0/3.0;
  pitchRatio[6]= 15.0/8.0;
}

//--------------------------------------------------------------
void testApp::update(){
  //タッチポイントの数だけくりかえし
  for (int i=0; i<NUM_SYNTH; i++) {
    //もし円のサイズが0より大きく、画面の高さよりも小さかったら
    if(circleSize[i]>0 && circleSize[i]<ofGetHeight()){
      //円のサイズを増加させる
      circleSize[i]+=3;
    } else {
      if(touchLoc[i].x > 0 && touchLoc[i].y > 0){
	//円の大きさを1にリセットして、サウンドを再度鳴らす
	//記録されている音が順番にパターンとして生成される
	circleSize[i]=1;
	synth[i].setPitch(noteTable[i]);
	synth[i].setLocation(touchLoc[i].x/(float)ofGetWidth()*2-1.0, 0,0);
	synth[currentTouch].setVolume(0.2);
	synth[i].play();
      }
    }
  }
}

//--------------------------------------------------------------
void testApp::draw(){
  //タッチした位置を取得して、円を描く
  for (int i=0; i<NUM_SYNTH; i++) {
    if(circleSize[i]>0 && circleSize[i]<ofGetHeight()){
      ofSetColor(31,127,255,200);
      ofNoFill();
      ofSetLineWidth(4);
      //拡大する円のサイズを指定して円を描く
      ofCircle(touchLoc[i].x, touchLoc[i].y, circleSize[i]);
      ofSetColor(255, 255,255);
    }
  }
}

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

}

//--------------------------------------------------------------
void testApp::touchDown(ofTouchEventArgs &touch){
  //タッチした場所を取得
  touchLoc[currentTouch].set(touch.x, touch.y);
  circleSize[currentTouch]=1;
  //タッチしたY座標から、ピッチを選択
  noteTable[currentTouch] = pitchRatio[int(7.0*touch.y/ofGetHeight())] * 0.5;
  synth[currentTouch].setPitch(noteTable[currentTouch]);
  //パンを設定
  synth[currentTouch].setLocation(touch.x/(float)ofGetWidth()*2-1.0, 0,0);
  //音量を設定
  synth[currentTouch].setVolume(0.2);
  //音を鳴らす
  synth[currentTouch].play();
  currentTouch++;
  if (currentTouch>NUM_SYNTH-1) {
    currentTouch = 0;
  }
}

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

}

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

}

//--------------------------------------------------------------
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
  //ダブルタップでリセット
  for (int i=0; i<NUM_SYNTH; i++) {
    circleSize[i]=0;
    touchLoc[i].set(-100, -100);
  }
}

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

}

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

}

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

}

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

}


完成!!