maximilianには二つのエンベロープが存在する
そのうちの一つmaxiEnvelopeは最大1000個までのアンカーポイントを打てて、
それぞれの間をリニアにつなぐシンプルなエンベロープである。
もう一つのmaxEnvはadsrを使ったもののようである。
ソースを見る
class maxiEnvelope { double period; double output; double startval; double currentval; double nextval; int isPlaying; public: double line(int numberofsegments,double segments[100]); void trigger(int index,double amp); int valindex; double amplitude; };
//I like this. double maxiEnvelope::line(int numberofsegments,double segments[1000]) { if (isPlaying==1) {//only make a sound once you've been triggered period=2./(segments[valindex+1]*0.004); nextval=segments[valindex+2]; currentval=segments[valindex]; if (currentval-amplitude > 0.0000001 && valindex < numberofsegments) { amplitude += ((currentval-startval)/(maxiSettings::sampleRate/period)); } else if (currentval-amplitude < -0.0000001 && valindex < numberofsegments) { amplitude -= (((currentval-startval)*(-1))/(maxiSettings::sampleRate/period)); } else if (valindex >numberofsegments-1) { valindex=numberofsegments-2; } else { valindex=valindex+2; startval=currentval; } output=amplitude; } else { output=0; } return(output); } //and this void maxiEnvelope::trigger(int index, double amp) { isPlaying=1;//ok the envelope is being used now. valindex=index; amplitude=amp; }
関数を読み解く
関数は二つあるが、triggerはセットされたエンベロープをスタート・リセットさせるだけなので
lineを読み解く。
lineのsegmentsには、その配列の個数と値とステップ数を交互に入れたものを渡す。
{値、ステップ数、値、ステップ数、、、}
という具合だ。
値もステップ数も、前の値に関係なく絶対値だ。
ステップ数は秒でもミリ秒でもなく、
サンプリングレート等を考えて求める必要がある。
いまいち、step数が直感的でなく分かりづらいので、コードを追ってみたい。
5行目の部分
period=2./(segments[valindex+1]*0.004);
ここは、前の値からの増加期間を計算しているようだ。
9行目、11行目と関わってくる
amplitude += ((currentval-startval)/(maxiSettings::sampleRate/period));
サンプリングレートをperiodで割っている。
9,11行目の分子の部分が44100となると1秒、22050となると0.5秒で
目的の値になるのがわかるので、ここの値を求めてみたい。
実際にstep数を代入して計算してみる
maxSetting::sampleRateは44100とする
segments[valindex+1] = 1000の場合
period = 2./(1000*0.004);
period = 0.5
44100/0.5
=88200となる
つまり88200/44100=2で、2秒ということになる。
—
segments[valindex+1] = 500の場合
period = 2./(500*0.004);
period = 1.0
44100/1.0
=44100となる
つまり44100/44100=1で、1秒ということになる。
で
segments[valindex+1] = 250の場合
0.5秒
segments[valindex+1] = 2000の場合
4秒
となり
まとめると
2000の時4秒
1000の時2秒
500の時1秒
250の時0.5秒
で500で1秒と考えれば良さそう。
ただ気持ち悪い場合は0.004の部分を0.002にすれば
ミリ秒で考えられそうですね。
使ってみる
適当なキーを押すとトリガーになって、円が描画されます。
#pragma once #include "ofMain.h" #include "ofxMaxim.h" class testApp : public ofBaseApp { public: ~testApp();/* destructor is very useful */ 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); void audioRequested (float * input, int bufferSize, int nChannels); /* output method */ void audioReceived (float * input, int bufferSize, int nChannels); /* input method */ int initialBufferSize; /* buffer size */ int sampleRate; /* stick you maximilian stuff below */ double sample; ofxMaxiEnvelope envelope; };
#include "testApp.h" double env[8]={20,0, 0,500, 250,1000, 40,250}; //------------------------------------------------------------- testApp::~testApp() { } //-------------------------------------------------------------- void testApp::setup(){ ofEnableAlphaBlending(); ofSetupScreen(); ofBackground(0, 0, 0); ofSetVerticalSync(true); sampleRate = 44100; /* Sampling Rate */ initialBufferSize = 512; /* Buffer Size. you have to fill this buffer with sound*/ ofSoundStreamSetup(2,0,this, sampleRate, initialBufferSize, 4);/* Call this last ! */ } //-------------------------------------------------------------- void testApp::update(){ } //-------------------------------------------------------------- void testApp::draw(){ ofSetColor(255, 255, 255,255); ofCircle(ofGetWidth()/2, ofGetHeight()/2, sample); } //-------------------------------------------------------------- void testApp::audioRequested (float * output, int bufferSize, int nChannels){ for (int i = 0; i < bufferSize; i++){ sample = envelope.line(8, env); } } //-------------------------------------------------------------- void testApp::audioReceived (float * input, int bufferSize, int nChannels){ for (int i = 0; i < bufferSize; i++){} } //-------------------------------------------------------------- void testApp::keyPressed(int key){ envelope.trigger(0, env[0]); } //-------------------------------------------------------------- void testApp::keyReleased(int key){ } //-------------------------------------------------------------- void testApp::mouseMoved(int x, int y ){ } //-------------------------------------------------------------- void testApp::mouseDragged(int x, int y, int button){ } //-------------------------------------------------------------- void testApp::mousePressed(int x, int y, int button){ } //-------------------------------------------------------------- void testApp::mouseReleased(int x, int y, int button){ } //-------------------------------------------------------------- void testApp::windowResized(int w, int h){ } //-------------------------------------------------------------- void testApp::gotMessage(ofMessage msg){ } //-------------------------------------------------------------- void testApp::dragEvent(ofDragInfo dragInfo){ }