====== マイク入力をリアルタイムでサウンドスペクトログラムする ====== {{ :of:スクリーンショット_2019-04-20_12.49.22.png?400 |}} ofのExampleにはFFTを利用したものがすでに存在しますが、このページでは周波数解析結果を時系列に 表示するいわゆるサウンドスペクトログラムのサンプルプログラムを掲載しておきます。 * 生データ * FFT処理結果(1024バッファ) * サウンドスペクトログラム(512サンプル分) の三つをそれぞれ fbo を利用して描画しています。必要なFFTクラスは下記のファイルを使ってください。 * {{ :of:fft.zip |}}(FFT.cpp, FFT.h) ofApp.cpp #include "ofApp.h" #include "FFT.h" #define SAMPLING_RATE 44100 //-------------------------------------------------------------- void ofApp::setup(){ ofSetFrameRate(60); fbo.allocate(BUF_FRAME, BUFSIZE, GL_RGB); fbo_raw.allocate(ofGetWidth(), ofGetHeight()); fbo_fft.allocate(ofGetWidth(), ofGetHeight()); int bufferSize = BUFSIZE; soundStream.listDevices(); soundStream.setup(this, // self callback 0, // no output 1, // 1 input ( mono input ) SAMPLING_RATE, // sampling rate[Hz] bufferSize, // buffer size 4); // number of buffer size. for( int i = 0; i < BUF_FRAME; i++ ){ for( int j = 0; j < BUFSIZE; j++){ buf_spectrogram[i][j] = 0; } } gui.setup(); gui.add(gain.set("Gain", 0.5, 0.01, 5.0)); } //-------------------------------------------------------------- void ofApp::update(){ fbo.begin(); ofClear(0); { // Draw Sound Spectrogram int pos_start_x = 0; int pos_start_y = BUFSIZE-1; glBegin(GL_POINTS); for( int i = 0; i < BUF_FRAME; i++ ){ for( int j = 0; j < BUFSIZE; j++){ ofSetColor(255,255,255, 255*buf_spectrogram[i][j]*gain); glVertex2f(pos_start_x+i, pos_start_y-j); } } glEnd(); } fbo.end(); fbo_raw.begin(); ofClear(0); ofBeginShape(); for(int i = 0; i < BUFSIZE; i++ ){ ofVertex(ofGetWidth()*(i/(float)BUFSIZE), ofGetHeight()/2 + sound[i]*100); } ofEndShape(); fbo_raw.end(); fbo_fft.begin(); ofClear(0); ofBeginShape(); for( int i = 0; i < BUFSIZE; i++ ){ ofVertex(ofGetWidth()*(i/(float)BUFSIZE)*2, ofGetHeight()- power[i]-100); } ofEndShape(); ofDrawBitmapString("freq[Hz]", 10, ofGetHeight()-100+20); for( int i = 0; i < BUFSIZE/2; i++ ){ if( i%10 == 0 ){ ofDrawBitmapString( ofToString(i*(int)myfft.getFreqStep(SAMPLING_RATE, BUFSIZE)), 2*ofGetWidth()*(i/(float)BUFSIZE), ofGetHeight()-100+10); } if( i%10 == 0 ){ ofLine(ofGetWidth()*(i/(float)BUFSIZE)*2, ofGetHeight()-100+10, ofGetWidth()*(i/(float)BUFSIZE)*2, ofGetHeight()-100-10); } } fbo_fft.end(); } //-------------------------------------------------------------- void ofApp::draw(){ ofBackground(0); ofSetColor(255); ofDrawBitmapString(ofToString(ofGetFrameRate()), ofGetWidth()-100,10); ofNoFill(); ofSetLineWidth(1.0); // Draw Sound Spectrogram fbo.draw(0,0, ofGetWidth(), ofGetHeight()); // Draw Realtime FFT fbo_fft.draw(0,0); // Draw RawData fbo_raw.draw(0,0); ofDrawBitmapString(ofToString(spectrogram.size()), 20,20); gui.draw(); } void ofApp::keyPressed(int key) { if( key == 's' ){ ofPixels pixels; fbo.readToPixels(pixels); ofSaveImage(pixels, "output.jpg", OF_IMAGE_QUALITY_BEST); } } //-------------------------------------------------------------- void ofApp::audioIn(float * input, int bufferSize, int nChannels){ //lets go through each sample and calculate the root mean square which is a rough way to calculate volume for (int i = 0; i < bufferSize; i++){ sound[i] = input[i]; } float avg_power = 0.0f; myfft.powerSpectrum(0, (int)BUFSIZE/2, sound, BUFSIZE, &magnitude[0], &phase[0], &power[0], &avg_power); for( int i = 511-1; i >= 0; i-- ){ for( int j = 0; j < BUFSIZE; j++ ){ buf_spectrogram[i+1][j] = buf_spectrogram[i][j]; } } for( int j = 0; j < BUFSIZE; j++ ){ buf_spectrogram[0][j] = log10(power[j]); } } ofApp.h #pragma once #include "ofMain.h" #include "ofxGui.h" #include "FFT.h" #define BUF_FRAME 512 #define BUFSIZE 512// 2^11 class ofApp : public ofBaseApp{ public: void setup(); void update(); void draw(); void keyPressed(int key); void audioIn(float *input, int bufferSize, int nChannels); ofSoundStream soundStream; float sound[BUFSIZE]; fft myfft; float magnitude[BUFSIZE]; // 振幅 float phase[BUFSIZE]; // float power[BUFSIZE]; // 振幅×振幅 vector> spectrogram; float buf_spectrogram[BUF_FRAME][BUFSIZE]; double db_min; ofxPanel gui; ofParametergain; ofFbo fbo; ofFbo fbo_raw; ofFbo fbo_fft; };