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

[code language=”cpp”]
#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];
};
[/code]

testApp.mm

[code language=”cpp”]
#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){

}
[/code]

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

  • タッチした場所から円が徐々に拡大していくように変更してみる

testApp.h

[code language=”cpp”]
#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];
};
[/code]

testApp.mm

[code language=”cpp”]
#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){

}
[/code]

音を鳴らす

  • 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

[code language=”cpp”]
#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];
};
[/code]

testApp.mm

[code language=”cpp”]
#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){

}
[/code]

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

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

testApp.h

[code language=”cpp”]
#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];
};
[/code]

testApp.mm

[code language=”cpp”]
#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){

}
[/code]


完成!!