電子オルゴールの波形生成

1. ワンチップマイコンとPWMによるアナログ出力

 ATMEL社のワンチップマイコンATtiny2313/ATtiny45をfck= MHzの内蔵クロックで動作させ8ビットPhase Correct PWM機能を用いた場合、

vAV ≒ ( x VH + y VL )/(x + y), ただしx+y= , x=0,2,4,...,
の256段階のアナログ値を表すことができる。

 PWM波形の周期はts= /fck= μsで、その周波数成分はfs=1/ts= Hzおよびその整数倍である。この波形自体を音として聴きとるわけではなく、積分回路などの低域通過型フィルタを通して平均値VAVを得る。

 出力するアナログ値をサンプリング周期ts= μs毎に変化させることにより、任意のアナログ波形を出力することができる。この性能( Hz 8ビット サンプリング)だけを見れば、CDの品質(44.1kHz 16ビット サンプリング)には及ばないものの、電子オルゴールに用いるには十分な品質の信号を作り出す可能性を持っている。

2. 矩形波周期信号と周波数の精度

 カウンタを用いて単純な矩形周期信号を生成する場合は、周波数を高い精度で決めることができる。

 下図はf= Hzの矩形波を生成する例を示している。周期を

T=1/f=1/ = μs
としたい。これを MHzのクロック周期tck= μsで割ると
N=T/tck=1/(f tck) =
となるが、小数点以下を丸め
N'=ROUND(N)=
を用いる。その周期T'は
T'=tck×N'=
となり、Tに対して誤差を含んだものとなっている。T'は
T - tck/2 〜 T' 〜 T + tck/2
の範囲で決めることができ、この場合Tに対して± μs以内の誤差にすることができる。

 周波数に換算すると

1/(1/f + tck/2) 〜 f 〜 1/(1/f - tck/2)
となり、この例では
で、周波数の誤差は %以内となっている。

 いくつかの周波数について、最大誤差は次のとおりである。

ノート周波数 f(Hz)N'周波数の最大誤差(%)

3. サンプリング周期と周期信号

3.1 周波数の精度

 サンプリング周期ts= μsごとに周期波形のアナログ値を出力する場合の周波数の精度について検討する。「私だけの電子オルゴール」では、この方法で減衰矩形波を生成している。

 下図はf= Hzの矩形波を生成する例を示している。周期を

T=1/f=1/ = μs
としたい。これをサンプリング周期ts= μsで割ると
N=T/ts=1/(f ts) =
となるが、小数点以下を丸め
N'=ROUND(N)=
を用いる。その周期T'は
T'=tck×N'=
となり、Tに対して誤差を含んだものとなっている。T'は
T-ts/2 〜 T' 〜 T+ts/2
の範囲で決めることができ、この場合Tに対して± μs以内の誤差にすることができる。

 周波数に換算すると

1/(1/f + ts/2) 〜 f' 〜 1/(1/f - ts/2)
となり、この例では
で、周波数の誤差は約 %以内となっている。

 いくつかの周波数について、最大誤差は次のとおりである。

ノート周波数f(Hz)N'周波数の最大誤差(%)

 半音の周波数の違いが約6%であることを考慮すれば(21/12=1.05946)、高い周波数での誤差は無視しがたい大きさである

 以下は誤差をグラフで示したものである。実際に生成される信号の周波数(赤色)は最大誤差を示す青色の線の範囲となる。

 110Hz〜1760Hzの各音に対する誤差の計算値を以下に示す。青色の線は生じうる最大の誤差である。

3.2 精度の改善

 上の方法では1周期の時間Tをサンプリング周期tsの整数倍T'で近似した。ここでは、M周期の時間MTをサンプリング周期tsの整数倍で近似する方法について検討する。以下、M=256とする。

 下図はf= Hzの矩形波を生成する例を示している。M周期の時間を

としたい。これをサンプリング周期ts= μsで割ると
N=MT/ts=M/(f ts)=
となるが、小数点以下を丸め
N'=ROUND(N)=
を用いる。M周期の時間MT'は
MT'=ts N'= s
となり、MTに対して誤差を含んだものとなっている。MT'は
MT - ts/2 〜 T' 〜 MT + ts/2
の範囲で決めることができ、この場合MTに対して± μs以内の誤差にすることができる。

 周波数に換算すると

1/(1/f + ts/2M) 〜 f' 〜 1/(1/f - ts/2M)
となり、この例では
で、周波数の誤差は %以内となっている。

 M個の個々の波形の開始時刻XnはM倍の値XnM

XnM=Xn-1M+N', N0M=0, n=0,1,2,...
で計算し、XnM/Mの小数点以下を丸めて以下のように決定する。
このように決めた開始時刻は、周期T'の各時刻に対して最大±ts/2の誤差を含んでいる。

 このように、この方法では周期の平均値の誤差を1/Mに改善できるが、各々の波形は±ts/2以内のジッタ(時間軸のゆらぎ)を含んだものとなっていることに注意する必要がある。

3.3 実装

 M=28=256とすることにより、XnMに(1/2)×256=(0000000010000000)を加えて丸め処理を行うと、上位バイトにXnが得られる。

4. 任意の波形の生成

4.1 ルックアップテーブル

 これまでは波形を矩形波とし、その周期や周波数、あるいは波形の開始時刻だけを問題にした。ここでは、正弦波や三角波など任意の波形を生成する方法を検討する。

 1周期分の波形データを予めメモリに用意し(ルックアップテーブル)、それを参照して出力する。

 ここでは正弦波のサンプル値として、次のようなNc= 個のデータを用意する。

127*(1-cos 2πn/Nc), n=0, 1, ..., Nc-1

4.2 任意の周波数の波形生成と周波数の精度

 周波数fの正弦波信号v(t)を生成したい。

v(t)=(V/2) (1-cos 2πft)

 下図はf= Hzの正弦波を、サンプリング周期 ts= μsの離散値で表す例を示している。ここでは、周期

T=1/f= μs
の波形を
N' = ROUND(T/ts) =
のデータで表し、その波形を単純に繰り返すものとする。

 このように作られる信号の周期T'は

T'=N' ts=
となり、Tに対して誤差を含んだものとなっている。T'は
T-ts/2 〜 T' 〜 T+ts/2
の範囲で決めることができ、この場合Tに対して± μs以内の誤差にすることができる。

 周波数に換算すると

1/(1/f + ts/2) 〜 f' 〜 1/(1/f - ts/2)
となり、この例では
で、 %以内の誤差を含んでいる。

 いくつかの周波数について、最大誤差は次のとおりである。

ノート周波数f(Hz)N周波数の最大誤差(%)

 半音の周波数の違いが約6%であることを考慮すれば(21/12=1.05946)、高い周波数での誤差は無視しがたい大きさである

 以下は誤差をグラフで示したものである。実際に生成される信号の周波数(赤色)は最大誤差を示す青色の線の範囲となる。

 110Hz〜1760Hzの各音に対する誤差の計算値を以下に示す。青色の線は生じうる最大の誤差である。

4.3 精度の改善

 ルックアップテーブルのNc個のデータは、 T/Nc= μs毎のサンプル値とみなすことができる。しかし、実際のデータ出力はts= μs毎に行われるので、図のように±T/2Nc 以内のジッタ(時間軸のゆらぎ)を含んだものとなる。ジッタは、周波数fが高いほど、そして波形のテーブルサイズNcが大きいほど小さくなる。

 ここでは、Nc= 個の中からステップ幅

Xsw=Nc/(T/ts)= ...
でデータを取り出し、n番目に出力するデータの参照位置Xn
Xn = Xn-1+Xsw, X0=0, n=1, 2, ...
とする。Xnを精度良く更新するために、M倍(M=256)した値Xsw Mを出力したい周波数毎に用意する。
Xsw M = ROUND(M Xsw) = ROUND(M Nc ts f ) =
M倍の参照位置を次のように更新するとともに、
Xn M = Xn-1 M+Xsw M, X0 M=0, n=1, 2, ...
実際の参照位置が 0〜Nc-1 となるように
Xn = ROUND( Xn M/M )%Nc
とする。

 Xsw MはM Nc ts fに対して±1/2以内の誤差とすることができ、作られる信号の周波数をf'とすると、

M Nc ts f - 1/2 〜 Xsw M=M Nc ts f' 〜 M Nc ts f + 1/2
となる。これより
f - 1/(2 M Nc ts) 〜 f' 〜 f + 1/(2 M Nc ts)
となり、この例では、
〜 f' 〜
となる。周波数の誤差は、周波数が高いほど、Mが大きいほど、そして波形のテーブルサイズNcが大きいほど小さくなる。いくつかの周波数について、Xsw Mと誤差は次のとおりである。

ノート周波数f(Hz)Xsw M誤差(%)

4.4 実装

 M=28=256とすることにより、XnMに(1/2)×256=(0000000010000000)を加えて丸め処理を行うと、上位バイトにXnが得られる。

 Ncを2のべき乗(ここではNc=27=128)とすることにより、AND演算でXnのビット7を0にするだけでXn%128のモジュロ演算ができる。

(参考文献)

[1] DTMF Generator, ATMEL Application Note, AVR314.