PWMDAC_Synthの配列生成マクロ

少し前のブログ記事で、PWMDAC_Synthライブラリにおける音階の作り方を解説しましたが、そのためにどんな関数を使っているか、といった計算式の説明が中心でした。

この計算式は、AVRマイコン上でランタイム(実行時)に動かすためではなく、PC上でのコンパイル時にC++のプリプロセッサで字面の置き換えをして配列を生成するためのものです。

従来は

#define F(x) …
PROGMEM const byte table[] = {F(0), F(1), ... , F(127)};

のように0~127まで書き並べる方法で波形テーブルを作っていましたが、新しいPWMDAC_Synthではよりスマートなこの方法に切り替えました。

#define FX2(f,x) f(x), f(x + 1)
#define FX4(f,x) FX2(f,x), FX2(f,x + 2)
#define FX8(f,x) FX4(f,x), FX4(f,x + 4)
#define FX16(f,x) FX8(f,x), FX8(f,x + 8)
#define FX32(f,x) FX16(f,x),FX16(f,x + 16)
#define FX64(f,x) FX32(f,x),FX32(f,x + 32)
#define FX128(f,x) FX64(f,x),FX64(f,x + 64)
#define ARRAY128(f) {FX128(f,0)}
#define ARRAY256(f) {FX128(f,0),FX128(f,128)}
:
#define PWMDAC_CREATE_WAVETABLE(table, function) PROGMEM const byte table[] = ARRAY256(function)

FXn(f,x) で、n/2 個の要素を2倍に増やし、n 個の要素を持つf(x)の並びを作る、ということを7回繰り返します。たったこれだけで 27 = 128 個のカンマ区切り数値の並びができてしまいます。

これを活用して作ったマクロ ARRAY256(function) や ARRAY128(function) を使えば、その引数 function にマクロ関数名を入れるだけで、関数の結果を使った配列が簡単に作れます。

それをさらにマクロにしてまとめれば、ライブラリを呼び出す側でそのマクロを指定するだけで、インスタンス(実体)を重複なく必要な分だけ作れるようになります。タイマー割り込み処理に欠かせない ISR() (Interrupt Service Routine) の定義もインスタンスとみなして一緒に定義することすら可能になります。

最初の頃はExcelのワークシート関数で配列初期化のソースを作らせていたのですが、このような計算を#defineでC++のプリプロセッサに任せることだってできるのです。

ただし、一つだけ注意点が。
この方法を使ってノイズ波形を作るために random() を使った乱数表を作ろうとすると、なぜかうまくいかないようです。コンパイルして実行したら動きませんでした。
random() は呼び出すたびに値が変わるため、実行時に値を作るようにコンパイルされてしまったのでしょうか…?
このような特殊なデータはExcelに作らせるか、エディタを使って手動で作るしかなさそうです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です