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