maxiEnvelope

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){ 
}