カエルの輪唱 PWM [Arduino]
カエルの輪唱の波形を作り出し、PCMとして再生してみます。
SDカードのWAV再生のようにデータをそのまま再生するわけではなく、和音を計算して波形を作るので、少し計算に時間がかかるかなぁと思い、 8分周のPWM (2048 CPU cycles, 約8kHz弱) としました。
発生する周波数を f とすると、1:1の矩形波を出したいとき、矩形のHigh/Lowが切り替わるまでにかけるループの回数を求める。
16MHz, 8分周のPWM(分周なしで256CPU cycles)の場合には
halfLength = 16000000 / 256 / 8 / 2 / f
となる。(fが0のときは注意)
16MHz, 8分周だと、1秒間音を出すのに 16000000 / 256 / 8 = 7812.5 回のループ(Hz)が必要となる。
if( ( i / halfLength ) % 2 ) 、、
という感じで、ループでカウンタ(i)を増やしていき、カウンタ(i)を halfLength で割ったときの偶奇で矩形のHigh/Lowを決める。
Highのときは、波形を足す(0-255までを4和音とするので、1パートあたり63を足す)
もうすこし、計算をすると、、
if( ( i / halfLength ) % 2 ) 、、
の部分は、
if( ( i / (16000000 / 256 / 8 / 2 / f) ) % 2 )、、
となり
if( ( i / ( 3906.25 / f) ) % 2 )、、
だいたい
if( ( i / ( 4096 / f) ) % 2 )、、
となり、ビットシフトや、And演算子を使うと
if( ( ( i * f ) >> 12 ) & 1 )、、
と、表現できた。また、これならfが0でも大丈夫。
それぞれのパート毎の音色の計算は以前のとおり
カエルの輪唱
http://hello-world.blog.so-net.ne.jp/2011-08-15
ブレッドボードが面倒だったので、ずるをしてデジタル5番をGNDとして使いました。
音を発生するときの振動で接触不良となり、すこし音がかすれてしまいました。
SDカードのWAV再生のようにデータをそのまま再生するわけではなく、和音を計算して波形を作るので、少し計算に時間がかかるかなぁと思い、 8分周のPWM (2048 CPU cycles, 約8kHz弱) としました。
発生する周波数を f とすると、1:1の矩形波を出したいとき、矩形のHigh/Lowが切り替わるまでにかけるループの回数を求める。
16MHz, 8分周のPWM(分周なしで256CPU cycles)の場合には
halfLength = 16000000 / 256 / 8 / 2 / f
となる。(fが0のときは注意)
16MHz, 8分周だと、1秒間音を出すのに 16000000 / 256 / 8 = 7812.5 回のループ(Hz)が必要となる。
if( ( i / halfLength ) % 2 ) 、、
という感じで、ループでカウンタ(i)を増やしていき、カウンタ(i)を halfLength で割ったときの偶奇で矩形のHigh/Lowを決める。
Highのときは、波形を足す(0-255までを4和音とするので、1パートあたり63を足す)
もうすこし、計算をすると、、
if( ( i / halfLength ) % 2 ) 、、
の部分は、
if( ( i / (16000000 / 256 / 8 / 2 / f) ) % 2 )、、
となり
if( ( i / ( 3906.25 / f) ) % 2 )、、
だいたい
if( ( i / ( 4096 / f) ) % 2 )、、
となり、ビットシフトや、And演算子を使うと
if( ( ( i * f ) >> 12 ) & 1 )、、
と、表現できた。また、これならfが0でも大丈夫。
それぞれのパート毎の音色の計算は以前のとおり
カエルの輪唱
http://hello-world.blog.so-net.ne.jp/2011-08-15
ブレッドボードが面倒だったので、ずるをしてデジタル5番をGNDとして使いました。
// カエルの歌 輪唱(PWMバージョン)
#define Do 262
#define Re 294
#define Mi 330
#define Fa 349
#define So 392
#define La 440
#define Ti 494
unsigned int kaeru[] = { // 8分音符(♪)ごとのデータ
Do, 0, Re, 0, Mi, 0, Fa, 0, Mi, 0, Re, 0, Do, 0, 0, 0,
Mi, 0, Fa, 0, So, 0, La, 0, So, 0, Fa, 0, Mi, 0, 0, 0,
Do, 0, 0, 0, Do, 0, 0, 0, Do, 0, 0, 0, Do, 0, 0, 0,
Do, Do, Re, Re, Mi, Mi, Fa, Fa, Mi, 0, Re, 0, Do, 0, 0, 0 };
void setup() {
DDRD |= B00001000; // Arduino pin 3 (ATmega328 PD3, OC2B)をサウンド出力
TCCR2A = _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); // 8bit高速PWM
TCCR2B = _BV(CS21); // 8分周
pinMode( 5, OUTPUT ); digitalWrite( 5, LOW ); // 圧電サウンダの足の都合で5番をGNDにする。
}
void loop() {
unsigned int t, part, f[4];
unsigned long i;
byte pwmVal;
for (t = 0; t < 128; t++) { // 1音ずつ進めていく
for(part = 0; part < 4; part++) f[ part ] = ( ((t>>4)-part+4)>>2==1 ) ? kaeru[t-part*16] : 0;
for(i = 0; i < 2000; i++) { // 1音にかける繰り返し回数 (3906.25で1秒にすると♪=0.5秒)
pwmVal = 0; // PWMの値を初期化
for(part = 0; part < 4; part++) if( ( ( i * f[ part ] ) >> 12 ) & 1 ) pwmVal += 63;
while( !(TIFR2 & _BV(TOV2)) ); // タイマーがオーバーフローするのを待つ
TIFR2 |= _BV(TOV2); // Timer/Counter2 オーバーフロー・フラグ をクリア
OCR2B = pwmVal; // PWMの値を指定
}
}
}
音を発生するときの振動で接触不良となり、すこし音がかすれてしまいました。
ダウンロードは🎥こちら