頓挫中。僕の調べ方が悪いのかもしれませんが、FFTとは無関係なところで悩み中。今日は文字だらけですんません。
プレイバックの問題
ライブラリ「minim」はFFTが簡単に出来るものの、問題はプレイバック部分、特に再生終了イベントを受け取れない(→[Minim] Tell if an AudioPlayer finishes playing?)。minimの作者さん本人が返事をしてますが先の10月にリリースされたバージョンでも改善が無いようです。
再生は勝手に止まるのでいいんです、でもその後、各GUIを初期状態に戻したり、もろもろの後処理が出来ないとすごく気分の悪いツールができあがるのがイヤ。
同じ機能を持つライブラリで「Ess」がありますが、こちらはFFTの結果をリニアで出してきます。例えばベースを22Hzとして220Hz、2,200Hz、、、22×10nの結果を得ることが出来ません。聞き取れない高周波成分の結果をたくさん出されたところで、ねぇ。
再生終了時の処理はメソッドaudioChannelDone(AudioChannel)を上書きすれば可能です。
解決するにはEssをプレイバック用、再生中のサウンドバッファをminim.FFTへ、って合わせ技かな?。
GUIのライブラリのこと
GUIに使用しているライブラリ「controlP5」も不足があるせいかマウスの各状態を得ることが出来ないでいます。レポジトリ上の開発途上な0.40のソース(Controller.java)を見ると、isMousePressed()でマウスの状態を取得できたり、mousePressed() 、mouseReleased()等のオーバーライド可能なメソッドが用意されているようです。でもコンパイルってどうするんだ?
って事で
Prosessing以外でも実現できるものが無いか調べ中。
ActionScript3にも「SoundMixer.computeSpectrum () 」ってのがありますね。
あとは、PureDataとか、vvvvとか、、、
MacOSXのQuartzComposerでもできるんですが、データの書き出し部分をObjective-Cで書かなきゃいけないのはちょっと、、、
続きは頓挫中の「見ちゃいやんバージョン」のソースです。以上の経緯があってまだ一番肝心なデータ書き出し等は実装されていません。すごく中途半端なので役には立たないですよ。
Minimは標準で入っていますが、ほかにcontrolP5が必要です。
mp3,AIFF,wavを開いて再生とスペクトルの表示ができます。
作るのが最終目的じゃなくって、これ使って映像を作りたいんですけどぉ!
/* minim.FFT test
Following code is WORK IN PROGRESS.
Required Libraries: controlP5 and Minim
*/
import controlP5.*;
import ddf.minim.analysis.*;
import ddf.minim.*;
ControlP5 gui;
controlP5.Button btnOpenFile;
controlP5.Button btnSaveResult;
controlP5.Button btnExit;
//controlP5.Textlabel feedbackText;
controlP5.Textlabel lbCurrentTimeMovie;
int currentTimeMovie = 0;
controlP5.Textlabel lbCurrentTimeSound;
int currenttTimeSound = 0;
controlP5.Button btnStop;
controlP5.Button btnStart;
controlP5.Slider slSeek;
int guiOffset = 10;
int fileGuiColumn = 5;
int playbackGuiColumn;
float[] maxHead;
float maxHeadAnimationDelay = 0.95;
Minim minim;
AudioPlayer audio;
FFT fftLog;
int minBandWidth = 22;
int bandStep = 5;
int cue = 0;
PGraphics fftScreen;
int fftScrrenOffsetX = guiOffset;
int fftScrrenOffsetY = fileGuiColumn + 20;
void setup(){
size(520,450);
background(0);
playbackGuiColumn = height - 20;
gui = new ControlP5(this);
//File GUI
btnOpenFile = gui.addButton("openFile",0,guiOffset,fileGuiColumn,100,15);
btnOpenFile.setLabel("Open Sound File");
btnSaveResult = gui.addButton("SatrtAnalaizing",0,guiOffset + 105,fileGuiColumn,100,15);
btnSaveResult.setLabel("Start Analaizing");
btnExit = gui.addButton("quit",0,width - 65,fileGuiColumn,60,15);
btnExit.setColorBackground(color(128,0,0));
btnExit.setColorActive(color(255,0,0));
/*
ControlP5.standard58
ControlP5.standard56
ControlP5.synt24
ControlP5.grixel
*/
//PlayBack GUI
btnStop = gui.addButton("stopAudio",0,guiOffset,playbackGuiColumn,60,15);
btnStop.setLabel("Stop");
btnStart = gui.addButton("playAudio",0,guiOffset+65,playbackGuiColumn,60,15);
btnStart.setLabel("Play");
slSeek = gui.addSlider("seekAudio", 0.0, 100.0, 0.0, guiOffset+130,playbackGuiColumn,250,15);
slSeek.setLabelVisible(false);
//Info
lbCurrentTimeMovie = new controlP5.Textlabel(this,"text",guiOffset + 5,390,300,15,color(255),ControlP5.grixel);
lbCurrentTimeSound = new controlP5.Textlabel(this,"text",guiOffset + 5,410,300,15,color(255),ControlP5.grixel);
disablePlaybackControllers();
//Setup audio
minim = new Minim(this);
//minim.debugOn();
frameRate(30);
//Setup Off-screen for FFT Bar-graphs
fftScreen = createGraphics(512,300,P2D);
fftScreen.beginDraw();
fftScreen.background(0);
fftScreen.noSmooth();
fftScreen.noStroke();
fftScreen.endDraw();
}
void draw(){
if((audio != null) && (audio.isPlaying())){
background(0);
slSeek.setValue((float)audio.position() / (float)audio.length() * 100.0);
updateFftScreen();
image(fftScreen,fftScrrenOffsetX,fftScrrenOffsetY);
updateRealTimeInfo();
currentTimeMovie ++;
}
else if((audio != null) && (!audio.isPlaying())){
}
}
void updateFftScreen(){
fftLog.forward(audio.mix);
int w = int(fftScreen.width/fftLog.avgSize());
fftScreen.beginDraw();
fftScreen.noStroke();
fftScreen.background(0);
for(int i = 0; i < fftLog.avgSize(); i++){
fftScreen.fill(12);
fftScreen.rect(i*w + w/10.0, 0, w/10.0*8, fftScreen.height);
fftScreen.fill(30,30,125);
fftScreen.rect(i*w + w/10.0, fftScreen.height , w/10.0*8, -1 * fftLog.getAvg(i));
fftScreen.fill(0,255,255);
maxHead[i] *= 0.95;
maxHead[i] = max(maxHead[i],fftLog.getAvg(i));
fftScreen.rect(i*w + w/10.0, fftScreen.height - maxHead[i], w/10.0*8, 1);
}
fftScreen.endDraw();
}
void updateRealTimeInfo(){
lbCurrentTimeMovie.setValue("Frame (Code domain) : " + str(currentTimeMovie));
lbCurrentTimeMovie.draw(this);
lbCurrentTimeSound.setValue("Milli-sec. (Playback) : " + str(audio.position()));
lbCurrentTimeSound.draw(this);
}
void clearState(){
for(int i = 0;i< maxHead.length;i++){
maxHead[i] = 0.0;
}
slSeek.setValue(0.0);
currentTimeMovie = 0;
background(0);
updateRealTimeInfo();
}
void quit(){
exit();
}
void openFile(){
String loadPath = selectInput("Select sound file");
if(loadPath == null){
println("No Sound file Selected.");
}
else{
if(audio != null){
audio.pause();
audio.close();
}
println(loadPath);
audio = minim.loadFile(loadPath,2048);
println("Audio Length(milli-sec.):" + audio.length());
//init
fftLog = new FFT(audio.bufferSize(),audio.sampleRate());
fftLog.logAverages(minBandWidth,bandStep);
maxHead = new float[fftLog.avgSize()];
for(int i = 0;i< maxHead.length;i++){
maxHead[i] = 0.0;
}
}
}
void saveResult(){
}
void enablePlaybackControllers(){
}
void disablePlaybackControllers(){
}
void playAudio(){
if((audio != null) && (!audio.isPlaying())){
audio.play(cue);
btnStart.setLabel("Pause");
}
else if((audio != null) && (audio.isPlaying())){
audio.pause();
cue = audio.position();
btnStart.setLabel("Play");
}
}
void stopAudio(){
if(audio != null){
audio.rewind();
audio.pause();
btnStart.setLabel("Play");
clearState();
}
}
void seekAudio(){
/*
if(mousePressed){
if(audio != null){
audio.pause();
cue = (int)(slSeek.value() / 100.0 * audio.length());
btnStart.setLabel("Play");
println("Seek");
}
}
*/
}
void stop(){
if(audio != null){
audio.close();
}
minim.stop();
super.stop();
print("exit");
}
“とどこおるFFT” への1件のフィードバック