多摩美 - “iTamabi” – iPhoneアプリ開発プロジェクト 2010
openFrameworks for iPhone:サウンドの録音・再生
今日の内容
- サウンドファイルの再生「Buddha Machine」的なアプリを作ってみる
- サウンドの録音と再生を使用した音アプリに挑戦
- 録音した音のデータをインタラクティブに操作する
サウンドファイルの再生
- あらかじめ録音したサウンドファイルを再生するには、ofxOpenALSoundPlayerを使用する – 詳細は過去の授業資料「openFrameworks + iPhone マルチタッチイベントの取得、音アプリを作る」を参照
- 自分で作成した音ファイルと、画面に表示する画像さえあれば「Buddah machine」のようなアプリは作れてしまう
サンプル1:サウンドファイルの再生
- オリジナルな"Buddha Machine"的アプリを制作できる雛形
- サウンドファイル(caf形式)と表示する画像を入れ替えれば、そのままアプリにできるテンプレートを以下に掲載
- 音ファイル(caf形式)は、「プロジェクトのフォルダ」→「bin」→「data」→「mySound.caf」に入れる
- caf形式のフォーマット – リニアPCM、16bit リトルエンディアン符号付き整数、22050Hz
- 表示する画像ファイル(PNG形式)は、「プロジェクトのフォルダ」→「bin」→「data」→「myScreen.png」に入れる
- PNGファイルノフォーマット – PNG 320 x 480 pixel
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); //表示する画面イメージ ofImage image; //OpenALを利用して音を再生するプレーヤー ofxALSoundPlayer synth; };
testApp.mm
#include "testApp.h" //-------------------------------------------------------------- void testApp::setup(){ ofRegisterTouchEvents(this); ofxAccelerometer.setup(); ofxiPhoneAlerts.addListener(this); //画面イメージを読みこみ image.loadImage("myScreen.png"); //ループ再生するcafファイルをロードして、Synthにわりあて synth.loadLoopingSound("mySound.caf"); //読み込んだcafファイルを再生 synth.play(); } //-------------------------------------------------------------- void testApp::update(){ } //-------------------------------------------------------------- void testApp::draw(){ //画面を表示 image.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){ }
サウンドを録音・再生する (サンプリング&プレイバック)
- あらかじめ録音したサウンドファイルを再生するのではなく、iPhoneのマイク入力から音を録音したり、録音した音を再生するには、SoundStreamを使用する必要がある
- SoundStreamは、音のサンプルのデータ自体を数値として配列に記憶し、それを再生することができる機能 → サンプリング&プレイバック
サンプリグ (標本化)とは
- 時間的に連続した信号を一定の間隔をおいて測定することにより、離散的な(連続でない)データとして収集すること
- 時間的に連続した信号 = アナログ信号
- 離散的な信号 = デジタル信号
サンプリング周波数と量子化ビット数
- サンプリング周波数 – 1秒間にいくつのサンプルを使用してサンプリングするか、単位はHz
- 量子化ビットレイト – AD変換の際に信号を何段階の数値で表現するか、単位はbit
iPhoneシミュレータでサンプリング&プレイバックのプレビューを行う際には「Audio MIDI 設定で」下記の設定にする
- サンプリング周波数、44.1KHz
- 量子化ビットレイト – 24bit
Sound Streamの機能
SoundStreamを使用するにはまず ofSoundStreamSetup() で初期設定を行う、通常は、setup() の中で指定
- ofSoundStreamSetup(int nOutputs, int nInputs, ofSimpleApp * OFSA, int sampleRate, int bufferSize, int nBuffers)
- サウンドの録音再生の初期設定を行う
- int nOutput – 再生する音のチャンネル数
- int nInput – 録音する音のチャンネル数
- ofSimpleApp * OFSA – 呼び出し元のofSimpleApp (普通は、this でOK)
- int sampleRate – サンプリングレイト
- int bufferSize – 音をバッファ(格納)するサイズ
Sound Streamに関連するイベント
testApp では、ofSoundStreamSetup() の設定すると音の入出力に関するイベントが発生する
- audioReceived(float * input, int bufferSize, int nChannels)
- 音が入力(録音)される際に発生するイベント
- float * input – 入力された音のサンプルの配列
- int bufferSize – バッファサイズ、一度に格納されるサンプルの長さ
- int nChannels – チャンネル数
- audioRequested(float * output, int bufferSize, int nChannels)
- 音が出力(再生)される際に発生するイベント
- float * output – 音として出力するサウンドのサンプルの配列
- int bufferSize – バッファサイズ、一度に格納されるサンプルの長さ
- int nChannels – チャンネル数
サンプル2:サンプリング&プレイバック
- 音を録音して再生するという「サンプリング&プレイバック」の機能をシンプルに実現してみる
- 画面をタッチ(touchDown)すると録音、タッチしている指を離すと(touchUp)録音した音を再生するように設計
- 録音した音の波形を画面に表示する
testApp.h
#pragma once #include "ofMain.h" #include "ofxiPhone.h" #include "ofxiPhoneExtras.h" //録音・再生する音の長さ #define LENGTH 44100 * 3 class testApp : public ofxiPhoneApp{ public: void setup(); void update(); void draw(); void touchDown(ofTouchEventArgs &touch); void touchMoved(ofTouchEventArgs &touch); void touchUp(ofTouchEventArgs &touch); void touchDoubleTap(ofTouchEventArgs &touch); void audioReceived( float * input, int bufferSize, int nChannels ); void audioRequested( float * output, int bufferSize, int nChannels ); float buffer[LENGTH]; //オーディオバッファ int sampleRate; //サンプリングレイト int recPos; //レコード位置 int playPos; //再生位置 int mode; //現在のモード、0:off, 1:recording, 2:play };
testApp.mm
#include "testApp.h" //-------------------------------------------------------------- void testApp::setup(){ //iPhone基本設定 ofRegisterTouchEvents(this); ofBackground(0, 0, 0); ofSetFrameRate(60); //横位置で使用 ofxiPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT); //サンプリングレイトの設定 sampleRate = 44100; //サウンド録音再生の初期化 ofSoundStreamSetup(1, 1, this, sampleRate, LENGTH, 4); //再生モードに mode = 2; //録音、再生の位置を先頭に recPos = 0; playPos = 0; } //-------------------------------------------------------------- void testApp::update(){ } //-------------------------------------------------------------- void testApp::draw(){ ofSetColor(255, 255, 255); if (mode == 1) { //録音モードの場合、recordingの表示をして、背景を青に ofDrawBitmapString("recording", 10, 20); ofBackground(255, 0, 0); } else if (mode == 2) { //再生モードの場合、playingの表示をして、背景を赤に ofDrawBitmapString("playing", 10, 20); ofBackground(0, 0, 255); } //画面の幅と録音サンプル数の比率を計算 int ratio = LENGTH / ofGetWidth(); //画面の横幅にあわせて、波形を描画 for (int i = 0; i < LENGTH; i+=ratio){ ofLine(i/ratio,ofGetHeight()/2,i/ratio,ofGetHeight()/2+buffer[i]*100.0f); } } //-------------------------------------------------------------- //オーディオ入力の処理 void testApp::audioReceived(float * input, int bufferSize, int nChannels){ //もし録音モードだったら if (mode == 1) { //バッファされたサンプルの数だけ処理 for (int i = 0; i < bufferSize*nChannels; i++){ //録音位置が設定した最大の長さに逹っしていなければ if(recPos<LENGTH){ //バッファにサウンド入力を設定 buffer[recPos] = input[i]; //録音位置を進める recPos++; } else { //もし最大位置を越えていたら、最初に戻る(ループ録音) recPos = 0; } } } } //-------------------------------------------------------------- //オーディオ再生の処理 void testApp::audioRequested(float * output, int bufferSize, int nChannels){ //もし再生モードだったら if (mode == 2) { //バッファされたサンプル数だけ処理 for (int i = 0; i < bufferSize*nChannels; i++) { //再生位置が設定した最大の長さに逹っしていなければ if(playPos<LENGTH){ //バッファに格納したサウンドを再生 output[i] = buffer[playPos]; //再生位置を進める playPos++; } else { //もし最大位置を越えていたら、最初に戻る(ループ再生) playPos = 0; } } } } //-------------------------------------------------------------- void testApp::touchDown(ofTouchEventArgs &touch){ //画面をタッチすると、録音モードへ mode = 1; recPos = 0; } //-------------------------------------------------------------- void testApp::touchMoved(ofTouchEventArgs &touch){ } //-------------------------------------------------------------- void testApp::touchUp(ofTouchEventArgs &touch){ //画面をから指を離すと、再生モードへ mode = 2; playPos = 0; } //-------------------------------------------------------------- void testApp::touchDoubleTap(ofTouchEventArgs &touch){ }
サンプル3:録音した音をインタラクティブに操作する
- 録音した音のピッチや音量を変化できるようにする
- 簡易版"Sampletoy" – 参考:Sampletoy
- 録音したサンプルを再生する際に工夫を加えてみる
- そのままの状態で録音開始
- 画面をタッチすると再生
- 画面をタッチしながら横に移動すると、音のピッチが変化
- 画面をタッチしながら縦に移動すると、音量が変化
testApp.h
#pragma once #include "ofMain.h" #include "ofxiPhone.h" #include "ofxiPhoneExtras.h" //録音・再生する音の長さ #define LENGTH 44100 * 3 class testApp : public ofxiPhoneApp{ public: void setup(); void update(); void draw(); void touchDown(ofTouchEventArgs &touch); void touchMoved(ofTouchEventArgs &touch); void touchUp(ofTouchEventArgs &touch); void touchDoubleTap(ofTouchEventArgs &touch); void audioReceived( float * input, int bufferSize, int nChannels ); void audioRequested( float * output, int bufferSize, int nChannels ); float buffer[LENGTH]; //オーディオバッファ int sampleRate; //サンプリングレイト int recPos; //レコード位置 float playPos; //再生位置 int mode; //現在のモード、0:off, 1:recording, 2:play float audioLevel; //音量 float playSpeed; //再生スピード(ピッチを変更できるようFloat型で) };
testApp.mm
#include "testApp.h" //-------------------------------------------------------------- void testApp::setup(){ //iPhone基本設定 ofRegisterTouchEvents(this); ofBackground(0, 0, 0); ofSetFrameRate(60); //横位置で使用 ofxiPhoneSetOrientation(OFXIPHONE_ORIENTATION_LANDSCAPE_RIGHT); //サンプリングレイトの設定 sampleRate = 44100; //サウンド録音再生の初期化 ofSoundStreamSetup(1, 1, this, sampleRate, LENGTH, 4); //録音モードに mode = 1; //録音、再生の位置を先頭に recPos = 0; playPos = 0; playSpeed = 1.0; } //-------------------------------------------------------------- void testApp::update(){ } //-------------------------------------------------------------- void testApp::draw(){ ofSetColor(255, 255, 255); if (mode == 1) { //録音モードの場合、recordingの表示をして、背景を青に ofDrawBitmapString("recording", 10, 20); ofBackground(255, 0, 0); } else if (mode == 2) { //再生モードの場合、playingの表示をして、背景を赤に ofDrawBitmapString("playing", 10, 20); ofDrawBitmapString("pos = " + ofToString(playSpeed, 3), 10, 40); ofBackground(0, 0, 255); } //画面の幅と録音サンプル数の比率を計算 int ratio = LENGTH / ofGetWidth(); //画面の横幅にあわせて、波形を描画 for (int i = 0; i < LENGTH; i+=ratio){ ofLine(i/ratio,ofGetHeight()/2,i/ratio,ofGetHeight()/2+buffer[i]*100.0f); } } //-------------------------------------------------------------- //オーディオ入力の処理 void testApp::audioReceived(float * input, int bufferSize, int nChannels){ //もし録音モードだったら if (mode == 1) { //バッファされたサンプルの数だけ処理 for (int i = 0; i < bufferSize*nChannels; i++){ //録音位置が設定した最大の長さに逹っしていなければ if(recPos<LENGTH){ //バッファにサウンド入力を設定 buffer[recPos] = input[i]; //録音位置を進める recPos++; }else { //もし最大位置を越えていたら、最初に戻る(ループ再生) recPos = 0; } } } } //-------------------------------------------------------------- //オーディオ再生の処理 void testApp::audioRequested(float * output, int bufferSize, int nChannels){ //もし再生モードだったら if (mode == 2) { //バッファされたサンプル数だけ処理 for (int i = 0; i < bufferSize*nChannels; i++) { //再生位置が設定した最大の長さに逹っしていなければ if(playPos<LENGTH){ //バッファに格納したサウンドを再生 output[i] = buffer[int(playPos)] * audioLevel; //音程にあわせて、再生位置を移動する playPos += playSpeed; } else { //もし最大位置を越えていたら、最初に戻る(ループ再生) playPos = 0; } } } } //-------------------------------------------------------------- void testApp::touchDown(ofTouchEventArgs &touch){ //画面をから指を離すと、再生モードへ mode = 2; playPos = 0; //音量を設定 audioLevel = (ofGetHeight() - touch.y) / ofGetHeight() * 3.0; //音程を設定 playSpeed = touch.x / ofGetWidth() * 2.0; } //-------------------------------------------------------------- void testApp::touchMoved(ofTouchEventArgs &touch){ //音量を設定 audioLevel = (ofGetHeight() - touch.y) / ofGetHeight() * 3.0; //音程を設定 playSpeed = touch.x / ofGetWidth() * 2.0; } //-------------------------------------------------------------- void testApp::touchUp(ofTouchEventArgs &touch){ //画面をタッチすると、録音モードへ mode = 1; recPos = 0; } //-------------------------------------------------------------- void testApp::touchDoubleTap(ofTouchEventArgs &touch){ }
サンプルファイルのダウンロード
今回の授業で紹介した全てのサンプルは、下記のリンクからダウンロード可能です