openframeworks+ofxshadertoyでオーディオ(音声)データの扱い
openframeworksでサウンドデータをofxshadertoyのシェーダーに渡すやり方は、本家
https://openframeworks.cc
でもあまり詳しく論じられていません。それがわからないと、https://www.shadertoy.com/view/Xds3Rr
とかは、openframeworksで動作しません。
今回、色々試して、とりあえず動いているようなので、手順を記します。
アドオンofxFftの導入
先ず、アドオンofxFftを導入します。
projectGenerator.exeなどのツールを使って、ofxFftを組み込んでください。
ofApp.hの修正
ofApp.hに
enum { SINE, MIC, NOISE };
及び
#include "ofxFft.h"
を追加します。
また、以下の関数と変数を追加します。
void audioReceived(float* input, int bufferSize, int nChannels);
ofxFft *fft;
ofMutex soundMutex;
ofSoundStream sound_stream;
vector<float> middleBins, audioBins;
vector<float> middleBuffer, audioBuffer;
ofTexture mTexture;
ofApp.cppの修正
次に、audioReceivedの処理を以下のようにします。
void ofApp::audioReceived(float* input, int bufferSize, int nChannels) {
if (!audioOn) return;
if (mode == MIC) {
// store input in audioInput buffer
memcpy(&audioBuffer[0], input, sizeof(float) * bufferSize);
float maxValue = 0;
for (int i = 0; i < bufferSize; i++) {
if (abs(audioBuffer[i]) > maxValue) {
maxValue = abs(audioBuffer[i]);
}
}
for (int i = 0; i < bufferSize; i++) {
audioBuffer[i] /= maxValue;
}
}
else if (mode == NOISE) {
for (int i = 0; i < bufferSize; i++)
audioBuffer[i] = ofRandom(-1, 1);
}
else if (mode == SINE) {
for (int i = 0; i < bufferSize; i++)
audioBuffer[i] = sinf(PI * i * mouseX / appWidth);
}
fft->setSignal(&audioBuffer[0]);
float* curFft = fft->getAmplitude();
memcpy(&audioBins[0], curFft, sizeof(float) * fft->getBinSize());
float maxValue = 0;
for (int i = 0; i < fft->getBinSize(); i++) {
if (abs(audioBins[i]) > maxValue) {
maxValue = abs(audioBins[i]);
}
}
for (int i = 0; i < fft->getBinSize(); i++) {
audioBins[i] /= maxValue;
}
soundMutex.lock();
middleBuffer = audioBuffer;
middleBins = audioBins;
soundMutex.unlock();
}
上記はオーディオデータを受け取った場合の処理です。
この受け取ったデータをシェーダーに渡すためにテクスチャーに変換します。
void ofApp::update()内に、
soundMutex.lock();
unsigned char signal[1024];
// for FFT
for (int i = 0; i < 512; i++) {
signal[i] = (unsigned char)(middleBins.at(i) * 255);
}
// for wave
for (int i = 0; i < 512; i++) {
signal[512 + i] = ofMap(middleBuffer.at(i), -1.0, 1.0, 0, 255);
}
soundMutex.unlock();
mTexture.loadData(signal, 512, 2, GL_RED);
を追加し、フレームごとに音声データをmTextureにロードします。
そして以下のようにチャンネル(0-3)とmTextureを指定して
shadertoy[shdrNum].setTexture(<チャンネル>, mTexture);
とすればオーディオデータがシェーダーに渡ります。