公開日:2025/09/06
title.png

36th Season


#36-1 サウンドジェネレータの設計

 本HPではMML(Music Macro Language)を使って音楽を演奏するソフトウェアを作ってきました。
 Arduinoマイコンボードではハードウェアがギブンなので、マイコンの汎用PWMタイマや割り込み機能などを駆使して、ソフトウェアで工夫しながら実装してきた、のが実態です。
 Cmod A7ボード(FPGA)を使えば、音楽演奏に特化したハードウェアの設計が可能なので、MMLを使った音楽演奏を容易に実現可能なハードウェアを設計して行きたいと思います。

音楽演奏に必要なハードウェア

 本節では音楽演奏に必要なハードウェアに関して考察を進めます。

音楽演奏の出力先

 これまで音楽演奏の出力先として、スピーカまたはパッシブブザーを使ってきました。各々、長所/短所についてまとめてみましょう。

  スピーカ:


  パッシブブザー:

 本ページでは、より手軽に、かつ、充分な音量が得られるパッシブブザーを対象とします。

音階の演奏

 まず、MMLで音楽演奏した以下記事を復習してみてください。

  「#5-8 MMLで音楽を奏でる

 音程は空気振動の周波数で決まります。音楽には12の音階があって、ドレミファソラシ(CDEFGAB)で1オクターブです。「12音階なのに7個しかないじゃん」と言うご指摘に対しては、ドレファソラ(CDFGA)の5音階には「♯(半音上げる)」音階が存在するので、これを加えて7+5=12音階です(下図)。

ar87_keybd.jpg

 音程の周波数はオクターブ4のラ(A)音が440Hzと決まっていて、

と言う関係があります。その他の音階は、例えば、オクターブ4のラ(A)音からオクターブ5のラ(A)音までの間を12等分する周波数となります。

frequency.png

 上式の計算で求めることができますが、べき乗計算が必要であり、ハードウェア化に向きません。ハードウェアで実現する場合には、ランダムロジックによる参照表(※)で実装するのが基本です。

※Verilog-HDLでは、caseステートメントを使って実装するのが定石です。参考コード例としては、こちら(asm_mcvm.v)が近しいです。

 必要なハードウェアは、

です。

音符の長さ

 音符には4分音符、8分音符などがあって、数字の大きい方が音としては短くなります。「分音符」を「分の1音符」と読み替えると解り易いです。
 4分音符は「4分の1」音符と言う意味です。基準となるのが1分音符で、これを全音符と言います。n分音符は、全音符の長さを基準として、その「n分の1」の長さと決まっています(下図)。

  ←長い 短い→  
mark.png

 続いて、一体全体、どれだけ短い音まで演奏できるようにする必要があるのか?って事ですが、今回設計するサウンドジェネレータでは、128分音符𝅘𝅥𝅲を最短かつ最小単位の音符長と決めます。また、最長の音符長は全音符とします。

 必要なハードウェアは、

です。

クリアランス

 ピアノなどの楽器を使って同じ音階を連続して3回演奏すると、例えば「ドドド」の様に3音が分離して聞こえます。ピアノの場合なら、鍵盤を叩いた瞬間が一番音量が大きく、時間の経過と共に音量が小さくなっていくからです(※)。
 パッシブブザーの場合、鳴り始めから鳴り終わりまで一定の音量なので、同じ音階を連続して3回演奏すると×3倍の長さの音階が1回聴こえるだけです(「ドーー」)。これでは締まらないので、最後の数10ms間、音を停止して無音部分を作ることで3音が分離して聞こえる様にしなければなりません。

 このような無音部分をクリアランスと呼ぶことにします。前節で考察した音符長タイマに於いてクリアランス形成信号を出力します。
 なお逆に、クリアランスを無効化して、連続する音が繋がって聞こえるようにしたい場合もあるので、ユーザの指定によってクリアランスを無効化する機能も設けておきたいと思います。

※ピアノの様に鳴り終わりにかけて音量が小さくなっていく、アナログPWMという技術も提案したことがありますが、適切なDA変換器が必要となって、パッシブブザーで鳴らすのに適さないため除外します。

1音の演奏

 1音を演奏するのに必要な情報は、音階(オクターブを含む)と音符長だと判ってきました。まとめてみましょう。

 以上、計16ビットコードで1音を表現する事ができそうです。以降これを演奏コードと呼びます。

楽譜の演奏

 1曲の音楽を演奏しようとするならば、曲の楽譜を読み取って演奏コードに変換し、演奏コードに従った音を順次演奏して行く事になります。
 この様に、複数の音を連続して演奏したい場合、必要なハードウェアはFIFOバッファです。前節の考察で1音を表現するのに必要な演奏コードは16ビットだと判ったので、16ビット×N段のFIFOを持つことが求められます。

 段数Nについては多いに越した事は無いのですが、その分ハードウェアの論理規模が増大します。論理規模を最小化したいなら、N=1段(FIFOバッファ無し)とすることも可能ですが、これでは1音演奏する度にCPUがケアしなければならず、ソフトウェアのオーバヘッドが馬鹿になりません。

 利便性と論理規模との費用対効果を熟慮した結果、N=32段に決めました。

 例えば、4分音符が1分間に120拍と指定された楽譜で4分の4拍子(4/4)なら、1小節の中に4分音符が4拍入ります。仮に1小節内に8分音符が8拍あったとしても、N=32段なら4小節前後のデータをFIFO内に保持することができます。
 実時間で考えても、4分音符が1分間に120拍の楽譜なら、1小節で2秒の演奏時間なので、4小節分なら8秒分の演奏コードをFIFOバッファに書き込むことができます。
 まあ、これだけあれば十分であろう、という判断です。

 必要なハードウェアは、

です。

サウンドジェネレータSNDG1PBユニット

 ここまでの考察により、必要なハードウェアのリストが出そろいました。
 以下、オリジナル16ビットCPUコアMoscoviumに組み込んで使用可能なサウンドジェネレータSNDG1PBユニットの設計を進めて行きます。

ブロック図

 SNDG1PBユニットとしては、1チャンネル(系列)の演奏が可能なユニットとして設計します。FPGAなので、必要数をインスタンスしてI/O端子を割り当てれば、チャンネル数を増やすことは容易です。以下にブロック図を示し、解説を続けます。

a361_sndg1pb.png

※薄黄色の箱はCPUからアクセス可能なI/Oレジスタです。

  ブロック図解説:

I/Oレジスタ表

 以下にI/Oレジスタマップを示します。下表は2チャンネル分で、前半0xf1c0〜0xf1c7がチャンネル0、後半0xf1c8〜0xf1cfがチャンネル1です。

Addr Name RW Bit:15〜8 Bit:7〜0 Memo
0xf1c0 sndgctl0 RW FR
ST
FF
UL
FE
MP
SG
EF
SG
EE
BU
SY
EN
BL
sound generator control
0xf1c2 reserved
0xf1c4 sndgt1280 RW T128[15:0] length of 128th note [us]
0xf1c6 sndgfifo0 RW MSCL
[3:0]
OCTV
[3:0]
NO
CL
LENG[6:0] notes FIFO data
0xf1c8 sndgctl1 RW FR
ST
FF
UL
FE
MP
SG
EF
SG
EE
BU
SY
EN
BL
sound generator control
0xf1ca reserved
0xf1cc sndgt1281 RW T128[15:0] length of 128th note [us]
0xf1ce sndgfifo1 RW MSCL
[3:0]
OCTV
[3:0]
NO
CL
LENG[6:0] notes FIFO data

 BUSC出力のセレクト信号はbcs_sndg_nです。上表の範囲でアサートされるので、SNDG1PBユニット内ではアドレスbadr[3:0]を復号してレジスタを特定しています。このI/Oユニットはバイトアクセス禁止です。必ずワードアクセスしてください。

  I/Oレジスタ仕様:

 2音同時演奏が可能な様に、2チャンネル分のSNDG1PBユニットを搭載しておきました。二和音の演奏とか、あるいはBGMを演奏しながら、別に効果音を1音演奏できるという程度の使い方が可能です。


 サウンドジェネレータの設計が終了した所で、小休止します。次回、少し残っているシステム設計と、実装まで終了したいと思います。


btn_arduino.png

btn_prev.png btn_next.png

home