oFでVoIPっぽいもの
こんにちは.1月もそろそろ終わりです. また卒論の季節ですねぇ.どうも1000chです.
大量のマイクを有線でつなげんのめんどくせーなー.ということでiPhoneをワイヤレスマイクっぽく使えないかな?と思いました. てなわけでmac上のoFに対しワイヤレスで音声入力させてみたー.
つくりたいもの
iPhoneのマイクへの入力をUDPでmacに送信,再生するよ.
参考にするサンプル
oFのサンプルを参考に実装してみました.参考にしたのは以下.
- sound/audioInputExample
- sound/audioOutputExample
音声入力をビジュアライズするinput,正弦波をジェネレートして再生するoutput. 音声取得と再生部分のコードを参考にします.
- addons/networkUdpSenderExample
- addons/networkUdpRecieverExample
UDPでの通信サンプル.iPhoneからmacへのデータ送信に使います. 音声通信なのでTCPでなくUDPにしました.
コード
iPhone側
iPhone側.マイク入力をUDPで送信するところまでのコード.
//header #pragma once #include "ofMain.h" #include "ofxiOS.h" #include "ofxiOSExtras.h" #include "ofxNetwork.h" class testApp : public ofxiOSApp{ 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 touchCancelled(ofTouchEventArgs & touch); void lostFocus(); void gotFocus(); void gotMemoryWarning(); void deviceOrientationChanged(int newOrientation); //sound void audioIn(float * input, int bufferSize, int nChannels); int initialBufferSize; int sampleRate; float * buffer; int drawCounter; int bufferCounter; // network ofxUDPManager udpConnection; };
続いて実装.重要なとこ抜粋.
//implement void testApp::setup(){ ofSetVerticalSync(true); ofSetFrameRate(60); //sound initialBufferSize = 512; sampleRate = 44100; buffer = new float[initialBufferSize]; memset(buffer, 0, initialBufferSize * sizeof(float)); ofSoundStreamSetup(0, 1, this, sampleRate, initialBufferSize, 1); //network int port = 12346; udpConnection.Create(); udpConnection.Connect("192.168.1.87", port); udpConnection.SetNonBlocking(true); // draw drawCounter = 0; bufferCounter = 0; } void testApp::audioIn(float *input, int bufferSize, int nChannels){ if(initialBufferSize < bufferSize){ ofLog(OF_LOG_ERROR, "your buffer size was set to %i - but the stream needs a buffer size of %i", initialBufferSize, bufferSize); } // udp messege string message = ""; int minBufferSize = MIN(initialBufferSize, bufferSize); for(int i=0; i<minBufferSize; i++) { cout << input[i] << endl; buffer[i] = input[i]; message += ofToString(input[i])+","; } bufferCounter++; udpConnection.Send(message.c_str(), message.length()); }
なんとこれだけです.注意すべきはaudioIn.ヘッダで定義してあると,デバイスが音声入力を感知するたびにaudioIn関数が呼ばれます.setup内のofSoundStreamSetupで設定したbuffersizeの音声データが利用できます.
そしてその受信したデータをカンマで区切ったstring化.UDPManagerのsendメソッドにより,setup内で指定したhost/portにデータを送れます.
mac側
では次に受信データのパース・再生部分を見てみましょう.
// header #pragma once #include "ofMain.h" #include "ofxNetwork.h" class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); void keyPressed(int key); void keyReleased(int key); void mouseMoved(int x, int y ); void mouseDragged(int x, int y, int button); void mousePressed(int x, int y, int button); void mouseReleased(int x, int y, int button); void windowResized(int w, int h); void dragEvent(ofDragInfo dragInfo); void gotMessage(ofMessage msg); // sound void audioOut(float *input, int bufferSize, int nChannels); ofSoundStream soundStream; float pan; int bufferSize; int sampleRate; float volume; vector<float> lAudio; vector<float> rAudio; // network ofxUDPManager udpConnection; };
続いて実装ファイル.
void testApp::setup(){ ofSetVerticalSync(true); ofSetFrameRate(60); //sound bufferSize = 512; sampleRate = 44100; volume = 0.5f; pan = 0.5; lAudio.assign(bufferSize, 0.0); rAudio.assign(bufferSize, 0.0); soundStream.setup(this, 2, 0, sampleRate, bufferSize, 4); // network udpConnection.Create(); udpConnection.Bind(12345); udpConnection.SetNonBlocking(true); } void testApp::update(){ char udpMessage[100000]; udpConnection.Receive(udpMessage, 100000); string message = udpMessage; if (message != "") { vector<string> stringValue = ofSplitString(message, ","); cout << "recieve message. length:" << stringValue.size() << endl; for(unsigned int i=0;i<stringValue.size();i++){ lAudio[i] = atof(stringValue[i].c_str()); rAudio[i] = atof(stringValue[i].c_str()); } } } void testApp::draw(){ ofPushStyle(); ofSetColor(0); ofSetLineWidth(2); float y1 = ofGetHeight() * 0.5; ofLine(0, y1, ofGetWidth(), y1); int bufferSize = 512; for(int i=0; i<bufferSize; i++){ float p = i / (float)(bufferSize-1); float x = p * ofGetWidth(); float y2 = y1 + lAudio[i] * 200; ofLine(x, y1, x, y2); } ofPopStyle(); } void testApp::audioOut(float * output, int bufferSize, int nChannels){ float leftScale = 1 - pan; float rightScale = pan; for (int i=0; i<bufferSize; i++) { output[i*nChannels ] = lAudio[i] * leftScale * volume; output[i*nChannels + 1] = rAudio[i] * rightScale * volume; } }
ちょっとやること増えました.とはいえdrawはビジュアライズだけなのでなくても可(というかサンプルのコピペ).ポイントはupdateとaudioOutです.
updateではループ毎にUDPManagerがrecieveメソッドを呼んでいます.受信データがあるときのみカンマ区切りをパースし,atofでfloatに直してvectorに格納していきます.
audioOutはbuffersize分のデータを再生するごとに呼ばれます.最初よくわからなかったのですが,引数のoutputにデータを格納するだけで自動的に再生してくれるみたいです.今回はl/rチャンネルのデータをそのまま格納しています.
まとめ
通信に問題がなければ,これでVoIPっぽいものができるはずです. 動かしてみた感じ,ちょっと音質的にどーよ?って感じがします. updateでrecieveが呼ばれるタイミングとbuffersizeが合ってないのかな?と思うのですが,原因はよくわからないです. UDPのrecieveをイベントドリブンにしたコードにだとよりよいのかな.データの圧縮などもやってないので,そこも合わせるとよりよくなるのではないかと思います.
これ使ってなにしようかな笑