LITZHAUS[○TOP][○シンセサイザ技術][○CMU-800関連][○DCB関連][○電子楽器・電子工作等]
[○さるべーじ][○ばいく][●リンクなど][戻る]
2004/7/2

CMU-800プログラムの方針


恥をしのんで…コントロールプログラムの動作の説明

今後CMU-800の駆動をやってみようという人(がいるかは不明ですが)に、「こんなしょぼいプログラムでもMIDI化できるぞ」という応援と「こんなことはやっちゃいけない」という教訓になればと思い、コントロールプログラムの解説です。ただし、このプログラム、一部のMIDIインターフェースで誤動作することを確認しています。MIDIデコード部分は参考にならないかもしれません
inf2
右がプログラムの全体の流れです。MIDIのデータ受信は割り込みを使い最優先で行い、MIDIメッセージのデコードや音源の駆動はメインループで暇な時にやっています。
CVは4051で構成したS/Hで保持しているので、常に値を入れてあげないと電圧が下がってくるのでメインループに入れています。PIT(8253)の設定は、MIDIデコードルーチンのNote-ONだった時に入れています。
MIDIデータ受信
SCIの割込みで受信バッファにデータを積み、すぐにメインループに戻っています(ソースのGET_MIDIルーチン)。MIDIデータは取り逃すと即誤動作に繋がるので、最優先で処理しています。 受信バッファはプログラムによる擬似FIFOで、とりあえず64バイト確保しています。H8のCPUパワーのおかげか、あんまりきっついMIDIデータを送ってないのか、ウチではオーバーフローを起こしていません。また、R5はFIFOリードカウンタ専用、R6はFIFOライトカウンタ専用になっています。FIFO以外のルーチンで使用してないので退避等の処理は一切してません。バッファサイズは2の倍数にして、FIFOカウンタをANDでマスクを掛けているのでのオーバーフローチェックもしてません。エラー処理も、FIFOのオーバーフローか、SCIエラーがでたら強制リセットするだけです。かなり適当です。

MIDIデータデコード1(メッセージの分離)

inf2
すでにバグが潜んでいる事が確からしいMIDIデコードルーチンです。変な所があったら教えていただければ有り難いです。
そもそもMIDIフルスペックを考えてないのでかなりはしょってます。システムエクスクルーシブ開始(F0)受信から終了(F7)受信まで、及び頭がFで始まる、コモン&リアルタイムメッセージは全部無視してます。
MIDIメッセージは1〜3バイトの可変長なので、受信したデータを一度MIDIメッセージバッファに置き、最終バイトを受信後に音源のアサインルーチンに飛びます。

すでにコモン&リアルタイムメッセージは取り除いているので、データの頭に1が立っていたら(80hより大きかったら)1バイト目ですので、メッセージバッファの1バイト目にデータを置き戻ります。それ以外は、2バイト目もしくは3バイト目になります。
このプログラムでは、1バイト目受信後、及び3バイト目受信後に「次は2バイト目だよ」フラグを立てています。そのフラグをみて、メッセージバッファの2バイト目、3バイト目にデータを置いています。プログラムチェンジや、チャンネルプレッシャー等の2バイトデータを使うつもりが無かったので、その部分の評価はしていません。また、ランニングステータスもうまく解釈できると思っています。…が、勘違いなのかもしれません。ランニングステータスって、ステータスバイト無しでデータバイトだけ連続で送られてくるんですよね?違うのかな…


MIDIデータデコード2(ボイスアサイン)
inf3
まず、Note-OFFだった場合、MIDIメッセージバッファをNote-ONのベロシティ0に変更します。これは、MIDIの仕様でNote-ONベロシティ0は、Note-OFFと解釈せよとあるからです。(発音停止がNote-Onで表現できると、発音→停止にランニングステータスが使えるからだと思います。)
ノート番号の補正は、CVを0V=Cにチューニングしたときにあわせています。次に、Note-Onかつ、CMUのチャンネルだった場合それぞれのアサインルーチンに飛びます。

リズムトリガーは、まずPPIのポートをクリアします。次に受信ノートに対応した太鼓のPPIポートをOnにして戻ります。頭でPPIをクリアしている理由はこれです。リズム音源をトリガする時間を計算するのが面倒だったのでへぼい処理をしているためです。
ちなみに、GM/GS配列などは全然考慮していないので配列はめちゃくちゃです。というより、ノートそのままです。同じ太鼓がいろんなキーで鳴ります。

モノトリガーは、Note-ONだったらそのノートで発音します。Note-Offだったら、発音バッファと比較して発音中ノートと同じであれば止めます。非常にシンプルな後着優先アサインです。また、ノートが変わるときにゲート処理していないのでシングルトリガーになります。

ポリトリガーは、ボイスアサインカウンタをくるくる回し、空きボイスだったら発音します。発音中だったら次を捜しカウンタが一周したらそのボイスで発音します。発音停止は、ボイスを順番にみて受信ノートが発音中だったら止めます。
コードはなんとなく後着優先のローテーション型アサインです。発音は後着優先ですが、発音停止はランダム(カウンタに依存)です。が、さほど違和感ないかと思います。


PITとPPIの設定

音程の周波数は、ベース周波数×2^(ノート/12)という式になります。さらにPITには分周比を入れなくてはいけないため計算が厄介ですので、分周比テーブルを使用しています。低音ほど分周比が高い(数値が大きい)ので、最低音の1オクターブ分のテーブルを用意してそれを元に計算しています。
まず、ノート番号をオクターブとノート(12音階)に分離します。H8には割り算命令があるので簡単です。ノートに対応した分周比をテーブルより持ってきて、オクターブ分ビットシフト(2倍)しています。後は、出てきた値をPITに突っ込んでいるだけです。
アナログシンセでは、生楽器の様に高音部はちょっと高め、低音部ではちょっと低めにチューニングをしたりするのですが、これはそういった処理は一切していません。

CV/GATEは、PPIのポートBからデータを、ポートCからチャンネルとストローブを出しています。ですので、ポートBに出力するビットマップ(CVとGate)をそのままMIDIデコードルーチンで使用し、わざわざ生成しなくていいようにしています。MIDIデコードルーチンの発音バッファの値をそのまま出力PPIに出力できます。
PPIの出力手順は、まずポートB(CV/GATE)に値をセットします。次にポートCのチャンネルをセットします(ストローブは1でマスクします)。次に、ストローブをセット(0でマスク)しポートCから出力します。その後ストローブをリセットし(1でマスク)ポートCから出力します。

バスアクセスのタイミングですが、作例では、特にウェイトいれずに動いてしまったのでそのままです。また、NECタイプの8253がちゃんと反応する事を期待して/IORQは固定し/WRだけで動かしてます。確か、オリジナルのものだと/WRだけだとバスタイミング合わないので駄目だったと思います。(NECなら大丈夫というのも「確かそんなんだった様な気がする」程度なので、通常は/IOREQをコントロールすることをお勧めします)

その他

MIDIチャンネルの設定は、起動時にH8のP8xから順番にビットを立ててPBxから読み込んでいます。本当は、ソフト的にやったほうがスマートでコストもかからないと思いますが、個人的な趣味でロータリースイッチを使っています(これだと、どれが何チャンネルなのかすぐわかりますし、設定方法を忘れる心配も無いので…、でもロータリースイッチ6個分でCPUカードより高いです)。試作の段階では、自照式ドットマトリクスディスプレイと、設定キーを付けて色々な設定が出来る様になっていたのですが、ディスプレイとスイッチをつける穴を開けるのが面倒だったので取っちゃいました。(ただ、ソースには若干痕跡が残ってるかもしれません。)
以上です。開発中は、ローランドのシリアルMIDIドライバをPCに入れ、H8側のSCIクロックをPCにあわせ(38400b/s)行いました。MIDI端子をつけてしまうと、シリアル経由でのファームの流し込みやデバッグが厄介なので、動作が安定するまではシリアルドライバで動かすといいかもしれません。(おかげで、ローランドのUSBインターフェースで誤動作するというバグに気づかなかったのですが。)

このサイトは特に断りのあるコンテンツを除きリンク・転載フリーです。うるさい事は言いません。

しょんぼりかうんたー
(c)1995-2017 LITZHAUS