FM音源プログラム (22) -- 全体の構成

話が前後してしまいましたが、FM音源プログラムの全体の構成について説明します。
まず、入力に関するハードウェアは、

  • MIDI/シリアル MIDI の信号入力
  • パネル・スイッチ入力 3 個
  • CPU 負荷測定用のアナログ電圧入力
  • MIDI/シリアル MIDI 切り替えセンス入力
  • 外付け DA コンバータのセンス入力

出力は、

  • マイコン内蔵 DA コンバータのアナログ出力
  • マイコン内蔵 PWM 出力
  • 外付け DA コンバータ用ディジタル出力
  • キャラクLCD モジュール用ディジタル出力
  • CPU 負荷測定用のディジタル出力

などです。
このうち、LCD モジュールへの出力と、パネル・スイッチ入力とはポートを共用していて、プログラムによって入出力を切り替えながら動作させています。
プログラムの全体の構成としては、まず、基本的には、割り込みは内蔵タイマで生成したサンプリング周波数 (fs) の割り込みしか使っていません。
例外として、ATmega 版のプログラムで、外付けシリアル DAC を使う場合に SPI の送信完了割り込みを使っています。
MIDI/シリアル MIDI の UART 受信では、割り込みを使わず、ループの中でフラグ・チェックをして処理しています。
MIDI は 31.25 kbps、シリアル MIDI は 38.4 kbps ですから、シリアルデータの受信レートは、高々 4 kHz に過ぎません。 
それに対して、サンプリング周波数は十数 kHz ですから、fs のレートのループ内で UART のフラグ・チェックをすれば、シリアル受信データを取りこぼすことはありません。
内蔵 DA 、外付け DA どちらの場合でも、DA 出力データには FIFO (リング) バッファを設けています。
fs レートの割り込みルーチンで出力 FIFO からデータを取り出し、DA に出力しています。
メイン・ループの方では、

  1. シリアル受信データがあるかどうかをチェック
  2. データがあれば読み込み、MIDI デコード関数を呼び出す
  3. 出力 FIFO に空きがあるかどうかをチェック
  4. 空きがなければ 1. に戻る
  5. 空きがあれば、前サンプルの計算結果を出力 FIFO に書き込み、現サンプルに対する計算を実行
  6. 1 ms 周期に相当するサンプルであれば EG の計算および LCD リフレッシュを実行
  7. 4 ms 周期に相当するサンプルであれば、ポルタメントLFOSMAF シーケンサの処理を実行
  8. 1. に戻る

という処理の繰り返しになっています。
エンベロープ・ジェネレータの計算周期は 1 ms で、ポルタメント・アキュムレータ更新や LFO 更新、およびデモ曲演奏のための SMAF シーケンサの計算周期は 4 ms となっています。
この 1ms, 4ms の周期は、fs をソフトウェア・タイマでカウントダウンして作り出していますから、fs は勝手な値にはできず、1 kHz の倍数に近い値を選ぶ必要があります。
メインループでの1サンプル分の処理時間は、EG 処理や LFO 処理などを含む場合と、含まない場合とでは変わってきます。
FIFO バッファを導入することにより、1サンプル当りの「平均」処理時間がサンプリング周期より短ければ、出力サンプルが欠落することなく連続的に出力できるようになります。
最も長い変動周期は 4 ms ですから、FIFO バッファの大きさが 4 ms 分のサンプルを格納できる大きさ以上であれば十分です。
ただし、このバッファのためにレイテンシーは増えることになります。 しかも、バッファのデータ量とともにレイテンシーも変動するので、MIDI ノートオン・メッセージの到着から、実際の発音までの時間も変動します。
また、EG の計算は 1 ms ごとに行っているので、MIDI ノートオンを受信しても、次の 1 ms 周期での EG 更新まで発音は開始しません。 つまり、EG による、平均 0.5 ms のレイテンシーがあります。