多摩美 - “iTamabi” – iPhoneアプリ開発プロジェクト 2010
openFrameworks + iPhone マルチタッチイベントの取得、音アプリを作る
サンプルファイルのダウンロード
サンプルファイルは以下のリンクよりダウンロードしてください。
- サンプルファイル iTamabi100517.zip (Zip形式 347KB)
マルチタッチイベントの取得
マルチタッチの情報が取得できるイベント
定義 | 機能 |
---|---|
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を使うと、こうした「音のおもちゃ」が簡単に作れる!!
- openFrameworks for iPhoneで音を鳴らすには – OpenALを使用する
- OpenAL – クロスプラットフォームのオーディオAPI
- 3次元定位オーディオを効率よく表現するように設計されている
- OpenGLとの相性が良い
- openFrameworksで、OpenALを利用するプロジェクトには、ofxALSoundPlayerアドオンのファイルが入っている必要がある
- 「グループとファイル」の「addons」を右クリックして、「追加」→「既存のファイル」を選択
- ofxALSoundPlayerのフォルダを追加する
- [oFのフォルダ]/apps/iPhoneSpecificExamples/OpenAlExample/src/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
- FreeSoundプロジェクトから、好きな音を探してきて、CAFファイルに変換してみる
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){ }