2007年06月07日

LFOとEGの設計とテスト

ddstest1.c に LFOとEGを組み込んでみた。→ ddstest2.c(修正版:ddstest2a.c
EG サンプル↓


LFO と EG は高い頻度で動かす必要がない(と思った)ので、↓のように、1/16 の頻度に落としてCPUを節約している。(あと負荷を分散させるよう工夫もしている)
ただ、LFO もサンプリングレートで動かしてやれば、2オペレータのFM音源にもなるかも知れない。(参考:JO-MIDI-FM)
今は、VCFをどう入れられるかが興味の対象なのだが、一段落したらFM音源化にもチャレンジしてみたい。


static void generate_snd() {
static uint8_t cnt;
:
cnt++;
if (cnt >= 16) {
do_lfo();
cnt = 0;
} else if (cnt == 8) {
do_eg();
}
}


LFO の本体はこんな具合。VCO を 正弦波専用に単純化して、VCOのパラメータを変更するコード。

static void do_lfo() {
uint8_t pos;
int8_t rev = 0;
int8_t v;
int16_t v2;
lfo.wave_pos += lfo.wave_inc;
pos = lfo.wave_pos >> 8;
if (pos >= 128) {
rev = 1;
pos = 255 - pos;
}
if (pos >= 64) {
pos = 127 - pos;
}
v = __LPM(sintab+pos);
if (rev) v = -v;
v2 = (int16_t)v * lfo.intensity;
vco.lfo_inc = fmulsu16(v2, vco.wave_inc)>>4;
}


EG はちょっと長いけれども、全部載せるとこんなかんじ。


static void do_eg() {
uint8_t state = eg.state;
if (state == EG_NOTEOFF) {
eg.intval = (0 << 8);
}
if (state == EG_NOTEON) {
eg.sustain_cnt = 0;
if (eg.attack == 0) {
eg.intval = eg.sustain_lvl;
eg.state = state = EG_SUSTAIN;
} else {
eg.intval = 0;
eg.state = state = EG_ATTACK;
}
}
if (state == EG_ATTACK) {
if ((uint32_t)eg.intval + eg.attack >=
(255<<8)) { // overflow
eg.intval = (255 << 8);
if (eg.sustain_lvl == 0) {
eg.state = EG_RELEASE;
} else {
eg.state = EG_DECAY;
}
} else {
eg.intval += eg.attack;
}
} else if (state == EG_DECAY) {
eg.intval = fmul16(eg.intval, eg.decay);
if (eg.intval <= eg.sustain_lvl) {
eg.intval = eg.sustain_lvl;
if (eg.sustain_time == 0) {
eg.state = EG_RELEASE;
} else {
eg.state = EG_SUSTAIN;
}
}
} else if (state == EG_SUSTAIN) {
eg.sustain_cnt ++;
if (eg.sustain_cnt == eg.sustain_time) {
eg.state = EG_RELEASE;
}
} else if (state == EG_RELEASE) {
eg.intval = fmul16(eg.intval, eg.release);
if (eg.intval < (1 << 8)) {
eg.intval = 0;
eg.state = EG_NOTEOFF;
}
}
eg.out = eg.intval << 8;
}


内部的には、16bit で値を計算して、上位8bit だけを出力としている。
ATTACK は、(計算が楽なので)線形で立ち上げることにした。DECAYは、減衰曲線で sustain_lvlになるまで減衰させる。SUSTAIN が終われば、RELEASEで減衰させるが、一応 DECAY と別のパラメータにした。
特別な処理としては、ATTACK のパラメータが 0 なら、SUSTAIN から入る。とか sustain_lvl が 0 なら ATTACK - RELEASE になるとか。ちなみに、CASE 文を使わないのは、(今使っている) avr-gcc(version 3.4) ではコード量が大きくなってしまうから。(CASE 文を使わないくせがついてしまった)

あと、fmul16()とかfmulsu16()とか出てくるが、実機では、アセンブラ化するつもりで関数化している。

おわりに

EG が付いたので、なんだか楽器らしくなってきて音を鳴らすのが楽しくなってきた。デジタルフィルタの VCF を作れば、一応コンポーネントは揃うことになる。もう一息なのだが、VCF は計算量が多く実機とすりあわせていかないといけないので、作るのはひとまずおいておいて ... 実機のコードと それにあわせた評価環境を作ろうと思う。
今のコードは、簡単なUIとサウンドジェネレータが一体になっているので、これを2つに分ける。Linux や Windows でもテストしたいので、プログラム間は、TCP/IP で通信する予定。

追記:
Linux/Windows の両方でバグがあったので、ddstest2.c を修正して、ddstest2a.c にアップデートしました。
posted by すz at 20:42| Comment(0) | TrackBack(0) | I2CSND
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス: [必須入力]

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/30760395
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック