n2kd のタイマ(テンポ管理)

n2kd の演奏処理は、外部ハードウェアからの定期的な割込により実行されます。 v1.0 (beta 2e) 時点で対応している割込源となるハードウェアは以下の通りです。

  • PC-98 版
    • OPN(A) の timer_b
    • Sound Blaster 16/98 の PCM 再生機能
    • PIT(システムタイマ)の ch.0
  • PC/AT 互換機版
    • Sound Blaster Pro 及び Sound Blaster 16 の PCM 再生機能
    • PIT(システムタイマ)の ch.0

当初 PC-98 のみをサポートする FM 音源ドライバとして開発が始まった関係で、演奏のテンポは BPM ではなく OPN(A) の timer_b が基準となるよう実装されています。 PIT と Sound Blaster でテンポ管理を行う場合は、割込頻度を timer_b に近づけなくてはなりません。

まず、PIT を 16bit 2 進カウントモードで最大のカウント数である65536回カウントさせても、割込間隔がtimer_b の最も広い場合よりも短くなってしまう問題があります。 これは、与えられているクロックが決まっているので仕方ありませんが、PIT のカウンタの更新が timer_b より結構速いためです。

これに対処するため、より速い頻度で割込を受けて演奏処理はそのうち数回に1回だけ行う機能を足しました。 これにより、timer_b の遅い割込頻度を PIT でも取り扱うことができます。 PIT に設定する値と演奏を行わず待機する回数は、割込間隔があまり短くならない範囲で総当たりで求めました。

Sound Blaster には DMA 転送によって PCM データを再生し、再生したサンプル数が所定の個数になった時に割込を発生させる機能があります。 FM 音源のタイマを発生源とする割込はありません。 どうして AdLib カードは OPL2 の割込信号をバスに結線してくれなかったんですかね……。

Sound Blaster の PCM 再生機能をテンポ管理に使う場合にテンポに影響する項目は、1. サンプルレート、2. サンプル数、3. 割込回数に対する演奏処理の頻度の3つです。 これらも、割込頻度に適当な制限を設けて総当たりで各 timer_b 値に対応する値を求めました。 Sound Blaster のミキサで PCM の音量を0にすれば、任意のメモリを PCM データバッファとして使うことができるので、PCM バッファに関する制約はほぼないようなものになります。 しかし、将来 PCM を使いたくなった時のことを考えて、今回は無音データで埋めた小さいバッファから PCM データを転送するようにしました。 といっても、記載の実装ではテンポによって PCM のサンプルレートと割込間のサンプル数の両方が変わってしまうので、そのままでは PCM 再生処理の実装は相当面倒になるのは間違いなく、ミキサで PCM の音量を0にする実装でも良かったかもしれません。

また、PC/AT 互換機の PIT の ch.0 は IRQ0 の発生源であり、時計を進めたりするのに必要なので、完全に乗っ取ってしまうとシステムの動作に影響があります。 そのため、PC/AT 互換機で PIT の ch.0 に常駐した際には、割込毎にカウント量を積算して適当なタイミングで元の IRQ0 を呼ぶことで、長期的に見た IRQ0 の元の割込ハンドラの実行頻度が保たれるようにしています。


戻るトップに戻る