多摩美 - “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
[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);
//表示する画面イメージ
ofImage image;
//OpenALを利用して音を再生するプレーヤー
ofxALSoundPlayer synth;
};
[/code]
testApp.mm
[code language=”cpp”]
#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){
}
[/code]
サウンドを録音・再生する (サンプリング&プレイバック)
- あらかじめ録音したサウンドファイルを再生するのではなく、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
[code language=”cpp”]
#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
};
[/code]
testApp.mm
[code language=”cpp”]
#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){
}
[/code]
サンプル3:録音した音をインタラクティブに操作する
- 録音した音のピッチや音量を変化できるようにする
- 簡易版"Sampletoy" – 参考:Sampletoy
- 録音したサンプルを再生する際に工夫を加えてみる
- そのままの状態で録音開始
- 画面をタッチすると再生
- 画面をタッチしながら横に移動すると、音のピッチが変化
- 画面をタッチしながら縦に移動すると、音量が変化
testApp.h
[code language=”cpp”]
#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型で)
};
[/code]
testApp.mm
[code language=”cpp”]
#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){
}
[/code]
サンプルファイルのダウンロード
今回の授業で紹介した全てのサンプルは、下記のリンクからダウンロード可能です