PWMDAC_Synthライブラリにおける音程を決める式

PWMDAC_Synthライブラリのソースには、さまざまな計算式が、主にマクロで記述されています。

ここでは、音の高さを決めるマクロ、PHASE_SPEED_OF() について解説します。

#define PHASE_SPEED_OF(note_number) ( \
pow( 2, ((double)note_number - 69)/12 + BitSizeOf(unsigned long) ) \
* PWMDAC_NOTE_A_FREQUENCY * 0xFF * 2 / F_CPU )

これは「位相速度」、すなわち単位時間あたり音波の位相をどれくらい進めるかを計算するマクロです。この式では、位相は最終的には unsigned long の整数値で表わされます。整数値がオーバーフローして一周すると 0 == 2π [rad] の位相ということになります。

単位時間

ここで「単位時間」は、PWMのパルス幅を更新するための割り込みサービスルーチン ISR() が呼ばれる周期です。最高速の位相基準PWM(Phase-correct PWM)の設定にすることにより、カウンタがCPUクロックごとに 0, 1, 2, … ,254, 255, 254, … 2, 1, のように往復しながら繰り返されることで、0 → 254の255個(0xFF個)、255 → 1 の255個、計510個ごとに割り込みがかかります。つまり、

255 * 2 / CPUクロック周波数

ということになります。CPUクロック周波数は F_CPU としてすでに定義済みで、16MHzになっています。これにより

0xFF * 2 / F_CPU

という、割り込み周期(単位時間)を表す式ができました。

音階の周波数

PHASE_SPEED_OF(note_number) の引数 note_number は MIDI のノート番号(0 ~ 127)ですが、これをもとに周波数を決める式が MIDI Tuning Standard として次のように決められています。

2(note_number - 69)/12・440Hz

これは、オクターブが周波数2倍、平均律に基づく半音がその1/12乗(12乗根)であることに由来します。69 は、A=440Hz の音階に対応するノート番号です。

440Hz を PWMDAC_NOTE_A_FREQUENCY として別途 #define し、これを使ってマクロで書くと

pow( 2, ((double)note_number - 69)/12 ) * PWMDAC_NOTE_A_FREQUENCY

のようになります。指数部分は12で割る段階で実数でなければならないので(double)でキャストしています。

周波数を位相速度へ

周波数は1秒当たりのサイクル数なので、これに単位時間を掛けると、その単位時間あたりのサイクル数に変わります。

pow( 2, ((double)note_number - 69)/12 ) * PWMDAC_NOTE_A_FREQUENCY * 0xFF * 2 / F_CPU

単位時間は音波の周期よりもはるかに短いため、サイクル数は1以下の小数になるはずです。これを位相速度にするには unsigned long の最大値+1を掛ける必要があります。unsigned long は 4 バイトなので、4 * 8 = 32 ビット。つまり unsigned long の最大値+1は、232 です。

が、当然のことながらこの数値は大きすぎて unsigned long で表すことはできません。ではどうするか?

ここで音階の周波数計算で使っている pow(2, …) に注目してください。

232 を掛けるということは、pow(2, n) の n に 32 を足すことと同じであることに気づきましたか?

そうです。
指数 n のところにビット数 32 を足すだけでよいのです!

これで、以下のように計算式ができあがりました。

#define BitSizeOf(type) (8 * sizeof(type))
#define PHASE_SPEED_OF(note_number) (pow( 2, ((double)note_number - 69)/12 + BitSizeOf(unsigned long) ) * PWMDAC_NOTE_A_FREQUENCY * 0xFF * 2 / F_CPU )

位相速度の活用

ではこの「位相速度」はどう活用されるか?

位相速度は unsigned long(32ビット)ですが、波形テーブルのインデックスはbyte(8ビット)です。これを変換するには 24 ビット右にシフトする必要があります。

変換できれば、あとはインデックスをもとに波形テーブル(値もインデックスも0~255の範囲)から現在あるべきパルス幅を読み出し、パルス幅を決めるレジスタを設定するだけです。この処理を割り込みサービスルーチンの中で行うことで、一定の単位時間でパルス幅を更新し続けることができるのです。
6重和音の場合は、その数だけこのパルス幅を合算します。
エンベロープでボリュームがコントロールされる場合は、そのボリュームを掛けたりします。

割り込みサービスルーチンでは、あまり時間のかかる処理を行うとそれ以外の処理を行う暇がなくなってしまうので、割り算などクロック数を食うような処理はできません。しかし、クロック数を最小限に抑えれば、6重和音も平気で出せるようになるのです。

電子楽器CAmiDion ソフトウェア更新

電子楽器CAmiDionと、シンセライブラリPWMDAC_Synthのソフトウェアを更新しました。

主な修正点

CAmiDion

  • クラス構造の見直し
  • ソースファイルの分割
  • 新たな#defineパラメータ(同時発音数、A音の周波数など)を追加し、CAmiDionConfig.hへ集約
  • ボタンのチャタリング対策(一定回数以上ボタンOFFが検出されるまで「ボタンが離された」ことにしない)
  • 最新のPWMDAC_Synthに対応

PWMDAC_Synth

  • 処理を見直し、多くの処理を*.cppから*.hに移行
  • 同時発音数、A音の周波数をライブラリを変えることなく#defineで変更できるようにした
  • 波形や位相速度テーブルに設定する値をべた書きせず#defineを駆使してプリプロセッサに計算させるようにした
  • 波形テーブルの実体定義をマクロ化し、必要な波形テーブルだけを実体定義できるようにした
  • ピッチベンド・センシティビティに対応

これに伴い、先ほど、更新したソフトウェアを手持ちの全CAmiDionと、未頒布の基板部品付セットに付属のATMEGA328に反映しました。

現時点で部品付セットの在庫が10セットあります。
欲しいという方はこちらを参照してください。

CAmiDionチャタリング対策後、あのパン屋で演奏してみたら…

なんか職場の飲み会から帰ってくる途中で9月が来てしまったようですが、酔った勢いでブログ書きますw

スケッチ更新しました

CAmiDionのスケッチを大幅にリファクタリングし、ソースファイルの分割を行いました。ソースの整理ができたところで、土曜日(8/29)にはボタンのチャタリング軽減対策を行いました。あと define によるプリプロセッサ定義も CAmiDionConfig.h に分離し、ハードウェア環境に応じてカスタマイズしやすいようにしました。
詳細はOSDNのGitリポジトリ参照。

CAmiDionでは中央付近のボタンをよく使うので、そのボタンから先に劣化し、押しても反応が鈍くなったり、チャタリングで複数回押されたような動作をしてしまうことがありました。これ、結構イラっときますよね。そこでこの影響を抑えて少しでも長持ちさせようと、チャタリング軽減対策に踏み切ったというわけです。チャタリングがうざい!と思ったCAmiDionユーザの方はぜひお試しください。

あのみっくみくなパン屋で演奏

日曜日(8/30)にはパン屋コンピ「ジョバンニと魔女」リリースパーティーというイベントへ行くため、田端の近くにあるあのパン屋へ。

先日夏コミに一般参加したときにyukkyさんのブースで「ジョバンニと魔女」というCDを買ったのですが、よーく見ると参加資格にこれを買った人ってのがある!

2ヶ月ほど前に道に迷ったあげくニアミスで終わって以来、あのパン屋に行けて、またCAmiDionでセッションできそう!ということで参加してきました。コトリ店長に会うのも花見でミク見以来5ヶ月ぶりくらい。パン屋に入れたのはお正月にyukkyさんに連れられて以来実に8ヶ月ぶりくらいでしょうか。
今度は道に迷わず行けました。

昼飯がまだだったのでさっそくパンを食べました(まぁ、パン屋だから当然のごとくそうなりますよねw)
パン屋の中では演奏機材の準備とリハーサルが。さっそくCAmiDionを接続させてもらいました。…が、なんかいきなり暴走することがあるぞ…最初は基板むき出しだからかなーと思っていたのですが、アクリルケース入りの電池式CAmiDionでも再現するので、色々弾いているうちにこんなことが判明。

キーがデフォルトのCのとき、E♭sus4、E♭、Cm のボタンを押すと、LEDが発狂して暴走する!
何やら、チャタリング対策でどっかバグってしまったようだ!
E♭は地雷ボタンか!?

この影響で「魔法のメロディ」でここのコードへ進行するところで「地雷」を踏んでしまい、電源を入れなおさないと復活しないので、ここを避けて演奏してなんとか乗り切りましたww ちょっともどかしいけど仕方ない…
(キーDだと中央から左3つ移動したところがFコードになりますが、このFコードが【まさかの地雷ボタン】になっていたわけです)

楽譜もありましたがあまり見てなくて結局聞いてこれだって思ったコードを押していく感じでしたねー(慣れるとこのほうが早い)

MIDI IN を装備した音楽キーボードを持ち込んでいた人がいたのでCAmiDionをつながせてもらったら、この音源めちゃいい!(しばらくはまってましたw) やっぱりちゃんとしたMIDI音源は音色が違うなぁ…小型のMIDI音源とかないかな…

CAmiDionに興味津々な人が何人かいて説明したりとか、なかなか楽しかったですw

この日は翌日が初音ミク8周年ということで、こんなケーキも。

考えたら、当時8歳だった上の娘も16歳になって初音ミクの設定に追いついたり、自分は娘の3倍の歳になってCAmiDionのボタンの数と同じになっていたりして、何もかもが8の倍数づくしです。プログラマーにとっては区切りのいい数だらけ。

パン屋にはスクリーンまであって、おぐぎんざ商店街を歩く人の目に留まるよう工夫されていました。

このパン屋が話題になるきっかけともなった「恋する縞パン」も2周年なんですね!

しかしながら「恋する縞パン」のうp主である肝心のyukkyさんが残念ながら参加できず、本人そっくりのぬいぐるみが代わりをつとめるような演出が始まったり。

帰ってすぐにデバッグ

さて、帰ってきたら日付が変わってたわけですが(さっきと同じで2日連続だw)
さっそくソースをチェックしたら…チャタリング待ち時間配列(ボタンの数だけある)のインデックスの計算が間違ってたことに気づく!

間違った計算式だとインデックスがこうなります:
0 1 2 3 4 5
8 9 10 11 12 13
16 .. 21
24 .. 29
32 .. 37
40 .. 45
48 .. 53 ← ここで配列サイズ48をはみ出す。E♭ボタンとかまさにここ。
56 57 58 59 60 61 ← B♭とかもここなのでやばかったけど何とか乗り切れた。

早速修正してみたら「地雷除去」に成功!
もう青の真ん中を押しても大丈夫。

前の夜に直して、使用頻度低めのボタンであまり試してなくて気づくのが遅れたのかも…