2024年7月18日木曜日

出入り検知器をPCB化する(その10)メロディ生成4

 次はテンポの処理である。

単音の場合は適当に遅延を挟むことでメロディを演奏することが出来たが和音の場合は各音符の長さが同一ではないため単純な遅延では処理できない。 そこで32分音符の長さを基準とした割り込みとソフトのカウンタで処理する事とした。

実際にはスタッカートやテヌートも処理したいので音符の長さを12分割した間隔で割り込みをかけ発音を制御する。最高のレートだとBPM=200の場合は約3ms毎に割り込む事になる。

割込みにはTimer6(TMR2)を使った。250KHzのクロックをプリスケーラで16分周するとポストスケーラを使わなくても丁度良い具合になる。 BPM=40で244分周、BPM=200で49分周と、8ビットの分周器に丁度収まるスケールだ。

ここまで出来たら、あとは譜面の記法を決めればよい。TeraTermで操作できるよう譜面は全て文字で表記するようにした。前に書いたように音の高さはMIDIの音階と同じコードを使うがメモリーを節約するため16進数で表記する。音の長さは次の様にした。

音符は音の高さ2文字と長さ1文字の3文字で表す(休符は音階=0とする)。更にチャネル指定を「:c:」で、BPMを「%hh」、音階のオフセットを「$±h」で表す。1音を3文字で表すよう数値は16進標記とした。3文字の間にはスペースや幾つかの区切り記号を挿入することが出来る。

譜面は改行で終わる1行の文字列である。譜面を最後まで読み込んだら内部でチャネル(音源)ごとの楽譜に変換し、32分音符の1/12の時間間隔の割込みに従って同時進行で演奏(音をON/OFF)する。

次のような4和音の譜面を1オクターブ上げてBPM=100で演奏してみた(この譜面はプログラム中にデータとして記述して分かり易いよう改行を入れている部分であるが、実際は「"」や改行を外し1行にする必要がある)。

       "%64$+c"
    ":1:43E 41E 46E 48G 4dG 4aE_48G 4dG 4aE 46E 48E 43E 413"
    ":3:3EE 3cE 3eE 45E     45E_45E     46E 43E 43E 3cE 3c3"
    ":2:3aE 39E 3aE 3cE     41E_3cE     41E 3eE 40E 37E 393"
    ":4:37E 35E 37E 35E     32E_35E     3aE 37E 30E 34E 353"


 これを解釈し内部で使う音源ごとの楽譜に変換したものは次のようなものである(各音源の最初の16バイトの16進ダンプ)。

  --1-- 54 43 54 41 54 46 74 48  74 4d 54 4a 74 48 74 4d
  --2-- 54 3a 54 39 54 3a 54 3c  54 41 54 3c 54 41 54 3e
  --3-- 54 3e 54 3c 54 3e 54 45  54 45 54 45 54 46 54 43
  --4-- 54 37 54 35 54 37 54 35  54 32 54 35 54 3a 54 37

これを演奏するとこのような音になる。

(その後)

3連符を表現したくて割込み頻度を3倍にして、32分音符の間に3×12回割り込むようにした。これで音符の長さを表すパラメータが3倍となり3で割り切れるようになる。表記も改訂した。

この機能を使って3連符のあるこんな演奏ができる。我ながら上出来である。 

(さらにその後)

音源3,4はパルス幅変調ができるので、試してみた。パルス幅を50%、25%、12.5%、…と半分ずつにしていくと、50%~6.25%までは音質が変わり、それを超えると音量が低下する。これは主旋律を目立たせる技として使えそうである。

(さらにその後)

オシロスコープを使って割り込み処理にどの程度時間を取られているか観測してみたところ約2msかかっていた。一方200BPMの時32分音符に費やされる時間は約37ms。実際にはそれを12分割して管理していたが、さらに3連符処理のためその1/3倍としている。つまり割込み処理を1ms以下で終える必要がある。一方実際には処理にその倍の時間がかかっており100BPM以上の演奏には追従できない事が分かる。 これを解決するためCPUクロック(Fosc)を4倍に引き上げて4MHzとした。これで1回の割込み処理が500μs程度で終わり時間に余裕が生まれた(実際には36回(或いはその倍数)に1回起こる音符の切り替え処理時にもっと多くの時間がかかるが聞いていても分からない)。またクロックの変更に伴って影響を受けるUARTの通信速度や音の高さに関係するパラメータを変更した。幸いにも音符の高さを表すパラメータはクロックを細工する事で再計算せずに当初の音域をカバーする事が出来た。詳しくは後述する。

(つづく)



0 件のコメント:

コメントを投稿