ふと、秋月で手にした懐かしいPSGのチップ YMZ294。昔、AY-3-8910をPC−8001のプリンタポートにつけて遊んだっけ。AY-3-8910は、8ビットのI/Oポートが二つ付いていて(これは、後年のFMチップにも引き継がれた)、大きなICだったけれど、YMZ294にはそれが無くて、コンパクト。まぁ、所詮、矩形波とか言いながら、楽器仕立てにできないかなと模索しています。
独立した装置にするのだから、CPUを選ばなくちゃいけません。汎用のCPU(ARMとかH8とか)を使えば簡単なのだけれど、ここは、キッチュにPICを使いたいと思います。
YMZ294のインタフェースが8ビットポート+制御線だから、8ビットポートのあるもの。もちろん、8ピンのチップで、シフトレジスタという奇手も考えられますが、ICが一つ増えちゃうし、配線量も増えるので却下。やはり、18ピンのものから選ぶことになります。
一応、MIDI入力を考えているので、シリアル入力も必要。USART無しのチップを使って、ソフトでシリアルというのも面白そうなんだけれど、割り込みはEG制御などで結構忙しく使いたい。ここは安易にUSART付きの石かな。となると、古典的なPIC16F84Aは対象外。
MIDIコントロールと割り切れば、ヴォリュームでパラメータ制御などなしにして、ADCは不要。手持ちのPIC艦隊から捜すと、PIC16F648Aが丁度、該当します。在庫を掃く意味でもこれに決定。
電源は5V。MIDI入力一つ。クロックは、ちょっと迷うところで、内蔵の4MHz、YMZ付属の4MHz、セラロックの20MHzのどれにするか。速いほうがいいんだけど、ちょっと回路が増えるし…とりあえず、CPU20MHz、YMZ4MHzで始めて、余裕があれば、CPUも4MHzも考えてみる予定。
PSGの制御部は、I/Oポートで素直にできそうですが、ネックになりそうなのは、MIDI周り。
検討して実験しなくちゃいけないのはMIDI入力回路と、シリアル入力のプログラム部分(特にクロックレート)。
まずは、ソフトの検討から始めます。
MIDIのボーレートは31.25kHz。これに近い値を選ぼうということです。
PICでのボーレートは、0-255の1バイトで指定し、計算方法は2種類あります。
BaudRate = Fosc/(64(X+1)) → X = Fosc/(BaudRate*64) -1
BaudRate = Fosc/(16(X+1)) → X = Fosc/(BaudRate*16) -1
いずれも、Foscは動作クロック、Xはレジスタに設定する値です。
Clock | Mode | Value | BaudRate | Error |
---|---|---|---|---|
20MHz | Lo | 9 | 31.250kHz | 0% |
20MHz | Hi | 39 | 31.250kHz | 0% |
16MHz | Lo | 7 | 31.250kHz | 0% |
16MHz | Hi | 31 | 31.250kHz | 0% |
10MHz | Lo | 4 | 31.250kHz | 0% |
10MHz | Hi | 19 | 31.250kHz | 0% |
8MHz | Lo | 3 | 31.250kHz | 0% |
8MHz | Hi | 15 | 31.250kHz | 0% |
4MHz | Lo | 1 | 31.250kHz | 0% |
4MHz | Hi | 7 | 31.250kHz | 0% |
誤差がすべて0%というすばらしい結果でした。ちょっと考えてみたら、MIDIのボーレート31.250kHzは、1MHzの1/32。つまり、MHz単位でのクロックを分周すれば通常はぴったりになるように決めてあったわけですね。半端なクロックを選ばなければOK。
MIDIのメッセージは、実は、リアルタイム層とメッセージ層(?)の2層構造。つまり、ノート・メッセージなどの間にクロックなどが割り込む構造です。このためシーケンスなどを扱う場合は、この2層を別の空間で扱う必要があり、面倒。今回は、リアルタイムメッセージは、まったく無視するか、あるいは、クロックだけタイムキーパーとしてこっそりモニタするつもり。
もう一つの難関は、SYSEX。あまりにも自由度が高すぎて扱いにくいメッセージですが、幸いIDが取れないのだから、扱う必要は無い(規格上は扱えない)と決めます。このため音色の制御はコントロールチェンジを利用する予定(GSとかXG風)。EEPROMがあるので、音色をここに保存してプログラムチェンジに対応したい。
どうせおもちゃレベルなので、楽器システム自体は数種類を試したいし、可能ならば、一緒にROM焼してモード切替などできればベスト。
MIDIチャンネルは固定でも良いし、起動時にSWなどを読んでも良いでしょう。
MIDI回路はコネクタからフォトカプラを介して、CPUのRXに入力。フォトカプラの選定が一つのポイントです。手持ちのものから選ぶならば、PC900V、PC910がロジック出力機能を備えていて無難。トランジスタが裸のタイプでは、PS2501が使えるかもしれません。冒険して、PS2501を使ってみたいところなのですが…
また、CPUのポート割りで、ちょっと、問題があります。大まかに言えば、PIC16F648Aでは、RAとRBの二つのパラレルポートがあるのですが、シリアル用のポート(RX/TX)は、RB1/RB2と兼用。つまり、RBはYMZ294とのインタフェースには使えない。他方、RAを見るとRA5が入力専用ポート。さらに外部クロックもRA6/RA7。つまり、YMZ294とは素直に結線できません。結局、RA0-RA3/RB4-RB7のように上下ニブルを生き別れにすることになりそう。不吉です。
RX端子の性格はシュミット・トリガー。ロジックの判別はLowが1.0v以下、Highが4.0v以上になっています。PS2501は大丈夫な気がします。レスポンスの方が問題かもしれません。
不本意ながらも、CPU周りの結線が決まってしまいました。
信号 | i/o | 用途 | 備考 |
---|---|---|---|
RA0 | o | ymz/D0 | YMZ294のデータバスへ |
RA1 | o | ymz/D1 | 同上 |
RA2 | o | ymz/D2 | 同上 |
RA3 | o | ymz/D3 | 同上 |
RA4 | o | ymz/A0 | YMZ294のA0へ。プルアップ必要 |
RA5 | i | SW | モードスイッチ。入力用ポート1ビット。コンフィグで指定が必要。 |
RA6 | - | xtal | セラロック20MHz |
RA7 | - | xtal | 同上 |
RB0 | o | ymz/CS | YMZ294の制御信号 |
RB1 | - | RX | MIDI入力 |
RB2 | - | TX | MIDI出力(?)。USARTを使用しているときは、出力ポートにできない。 LEDをつけても良いかもしれない。 |
RB3 | o | ymz/WR | YMZ294の制御信号 |
RB4 | o | ymz/D4 | YMZ294のデータバスへ |
RB5 | o | ymz/D5 | 同上 |
RB6 | o | ymz/D6 | 同上 |
RB7 | o | ymz/D7 | 同上 |
やはり割付がゲテです。せめて28ピン・デバイスにすれば、YMZへのデータポートを一本化できるのですが。モード指定にも、もう少しビット数が欲しいです。外付けのマルチプレクサでも付ければできるのですが、そうすると、配線量も増えるし、ICのコストもかかる。それなら28ピンの方が安いという結論になりそう。28ピンにすればセグメントLEDをつけるとか、液晶をつけるとか、機能追加もできそうだし…
このまま、コンパクトに行くか、方向転換して28ピンデバイスを選ぶか。ちょっと、考えどころですね。
迷っていても仕方が無いと、CPUは強引に18ピンのPIC16F648A。クロックはセラロックで20MHz。フォトカプラはPS2501(これは結局使用を断念。PC900Vに代えました)。これは小さな回路で実験したところ使用できそう。負荷抵抗が小さい方が速そうなのですが(データシートでは100Ω)、それでは電流が流れすぎてもったいない。負荷抵抗はとりあえず1kΩにして、様子を見ながら下げようと思います。
当座はブレッドボード上で実験。秋月のライターもしばらく使っていないので、チェックしなければなりません。「PICピカピカ」のボードで色々動かすと。動作はOKのようです。
出力確認用のLEDすらないので、デバッグ用にMIDI出力回路を設けます。いろいろと考えたのですが、PICのTXをそのまま出力に使えるのではないかと思いました。
一応の5mA程度の電流ドライブはできるので、相手先でショートでもしない限りは大丈夫でしょう。
ハードウェアの動作を段階的に検査します。
16F648Aに5v/Gndそれと、20MHzのセラロックを繋げば動くはず。確認用にPORTB4(10pin)に220Ωの抵抗を介してLEDを繋ぎます。LEDの反対側はグランドに落としておく。
使用したセラロックは秋月で購入したコンデンサ内蔵の20MHzのもの(村田製作所CSTLS20M0X51)。内蔵コンデンサの容量がデータシートを見ても判りませんでした(各種用意と書いてある)。秋月のホームページではPICマイコン等に最適と書いてあるので、大丈夫だとは思うのですが、まずは、実機確認です。
この状態で次のような、テストプログラムを作成し、タイマー0をプリスケーラ最大(×X256)とし、フリーラン。割り込み処理の中で16分周して、都度、PORTB4を反転します。
PICにプログラムを焼きこんで電源を入れると、うまく点滅しました。周波数の予想は、20M/(4*256*256*16*2)→2.3841...Hz。テスターで周波数を計ると2.384Hz。ドンピシャですね。
上のほうで計算してあるUSARTの値の設定で果たして正しいクロックが設定されるか確認します。
プログラムは、単にバッファの空きをポーリングして0x55を書き込む。大まかに言えば矩形波が出るのでこの周波数は、MIDIクロックの1/2、すなわち、15.625kHzのはず。
RB2にテスタを当て、周波数を測定すると、15.62kHz。うまくいっているようです。
USARTで出力したデータをUSARTで受け取るループバックテストのプログラムを書き、検査します。
うまく動きました。
MIDIのコネクタは使用せずに出力回路と入力回路を結びます。とりあえずフォトカプラはPS2501。あまり、調子がよろしくありません。負荷抵抗が大きいと遅いし、負荷抵抗が小さいと電圧振幅が狭い。予備実験ではうまくいくと思ったんですが…。とりあえず、深追いせずにフォトカプラを代えることにしました。
フォトカプラをシャープのPC900Vに変更しすると、即動作。さすが推奨品です。
今回の計画ではMIDIOUTは無いのですが、つけておくとデバッグ情報の出力など、便利なもの。5mのケーブルを通して戻しましたが、うまく動作しています。
気になっているのは、出力ピンが足りないためにYMZのクリア端子をHIにつっていることです。オーディオ出力は1kΩの負荷でラインに繋げば鳴りそう。当面はオペアンプは不要か?この辺りも試します。
テスターで測ると3.999MHz。きちんと発振しているようです。
現在の回路では、YMZのバスがRB7-RB4/RA3-R0と生き別れになっていて面倒なのですが…
簡単な発音プログラムを書いて検査します。440Hzで鳴りっ放しのものです。
ここでちょっと、引っかかってしまいました。出力をパワードスピーカーに繋ぐと確かにラの音がします。電子チューナーで測るとほぼ440Hz。しかし、テスターで周波数を測ると125kHz。何か間違いではないかとポートの割り当てを変えてみたり、書き込みタイミングを変えてみたりと試行錯誤しても結果は変わりません。仕方なく、シンクロで出力波形を観測すると、125kHzがキャリヤのように見えます。440Hzの矩形波のオンオフのオンの部分に短冊のように125kHzの切れ目が入っています。
おそらく、これは仕様だということで、そのまま元の回路に戻しました。125kHzは可聴範囲外ですからいいんですが、気持ち悪いです。広域アンプとツィータの組合せなどでは、まずそう。気に入らないのですが、手持ちの石では、このように動作します。
想像するに、この125kHzは入力クロック4Mの1/32の値。トーン周波数レジスタ設定値の最小単位に相当します。和音を入力するとシンクロの波形から想像するとトーンのミックスは時分割で行っているような雰囲気です。
気になっていたリセットはしなくても大丈夫のようです。出力もパワードスピーカーに直接繋いでも音が出ました。
YmzWrite: ;adr out movf ymz_adr,w andlw 0x0f ;sure movwf porta ;a0->0 & low nible ; clrf portb ;write/cs -> 0 ; movlw b'00001001' movwf portb ;write/cs -> 1 ;data out movf ymz_data,w andlw 0x0f iorlw b'00010000' movwf porta ;a0->1 & low nible ; movf ymz_data,w andlw 0xf0 movwf portb ;write/cs ->0 & hi nible ; iorlw b'00001001' movwf portb ;write/cs ->1 ; return
上がYMZのレジスタアクセス処理。ymg_adrにレジスタ番号、ymg_dataにデータを格納してからCALLします。今回の回路ではYMZのデータバスがRB7-RB4/RA3-RA0に生き別れになっているので長くなっています。RA4がYMZのA0、RB0がCS、RB3がWRITEに接続されています。
WRITEとCSのタイミングは同時でも動くようで、秋月のサンプルもそうなっていました。実際、WRITE/CSをショートさせても動作します(ということは1ビット余るが今回は自粛)。アドレスの設定はアドレスの上位ニブルが常にゼロなので若干簡略化しています。タイミングですが、信号のずれはデータシートから数十nS程度。20MHzのPICでは1命令当たり200nSかかるので余裕で(?)待ち時間は不要です。
音の高さは二つのレジスタを組にして設定します。都合12ビットの値です。YMZ294に4MHzを入力した場合の計算式は、
tp=4*1000*1000/32/Ft
ここでtpが設定する値。Ftが設定したい周波数です。
この式を元に、A=440Hz、平均律の場合の設定値と、値を設定した場合の実際に出る音の周波数、それから誤差を計算したものが下の表です。誤差はCentつまり、1/100半音単位で表しています。設定値の欄が黄色は、値が大きすぎて設定できないところ、誤差の欄が黄色いのは誤差が10セント、つまり1/10半音を超えているところです。
低域の限界はB0(23)。ベースの4弦Eがそのちょっと上のE1(28)。チェロに合わせて弦を伸ばしたベースの下限がC1(24)だから、丁度いい具合です。また、音程の誤差も低域は少なく、下から見て始めて誤差が1セント(1/100半音)を超えるのがD#3。
他方、高域は音程が高くなるほど誤差が増えます。下から見て始めて誤差が10セントを超えるのがC#7(97)。しかし、音楽的な使用を考えると、バイオリンの2弦がA4(69)--ウクレレの1弦--だから、その2オクターブ上のA6(93)ぐらいが上限とすれば、なんとか誤差10セントをクリアしています。誤差5セント以内でと考えると、下から見て最初に出てくるのがG#5(80)。オクターブも下がることになり、相当に苦しいです。
この表をそのままインプリメントすると、実用的には、C1からC7までのおよそ6オクターブ。ただし、一番上のオクターブは、やや、怪しい。かろうじて弦のベースからバイオリンまでカバーしていることになります。実用域より上は、音程を折り返すか、あるいは、音痴でもいいからそのまま載せるか、どちらにしても大差ないような気がします。
LFOやピッチベンドを実装すると、その精度によってですが、表を使っている限り表のサイズは膨大なものになります(乗算が速ければ、ある程度演算でガバーできるのですが)。そこで表の圧縮を考えてみます。
単純には、オクターブ高くなると設定値を1/2すればよいことを利用して、低い音域、例えば、ここでは、C-1-B-1を表で持ち、オクターブ毎に1ビット右シフトすれば、値が求まります。この場合の計算例が以下の表です。
大まかな音域は最初の表と変わらないのですが、高域を見ると随分ずれがあることがわかります。結構つらいかもしれません。
いずれにせよ、ピッチベンドにしても、LFOにしても、例えば、A4(69)付近では半音間の設定値の違いは16しかありません。つまり、この音域ではピッチベンドは4ビット+符号が限度。ビブラートをたっぷり聞かせる、バイオリンの1弦のA5(81)では、なんと、8ステップの違いです。やはりレジスタが12ビットというのは、かなりきつい制限です。
MIDI処理を書くに当たって、MIDIのメッセージストリームについて見直しておきます。要するにシリアルのメッセージストリームで、メッセージの種類を表すSTATUSとそれに付属するDataがあります。STATUSはそのmsbが1、Dataはmsbが0。これで区別が付きます。
これだけなら、簡単なのですが、実は、リアルタイム・メッセージとノン・リアルタイム・メッセージの区分。それからランニング・ステータスという面倒なものもあります。
Status | Name | Format(len) | 区分 | |
---|---|---|---|---|
8x | Note Off | 8x kk vv (3) | 発音中止。kk=キー番号。vv=ヴェロシティ | Channel Msg |
9x | Note On | 9x kk vv (3) | 発音開始。kk=キー番号。vv=ヴェロシティ。vv=0ならば発音中止 | |
Ax | After Touch(Poly) | Ax kk vv (3) | キー独立アフタータッチ。kk=キー番号。vv=値。 | |
Bx | Control Change | Bx cc vv (3) | 各種制御。cc=制御番号。vv=値。 | |
Cx | Program Change | Cx pp (2) | 音色変更。pp=音色番号。 | |
Dx | After Touch(Channel) | Dx vv (2) | チャンネルのアフタータッチ。dd=値。 | |
Ex | Pitch Bend | Ex ll hh (3) | ピッチベンド。hh ll =ベンド量(64 00:中央値)。 | |
F0 | System Exclusive | F0 ... F7 (-) | 特殊用途用メッセージ。可変長。 | System Common Msg |
F1 | MTC Quater Frame | F1 nn (2) | Midi Time Code用フレーム | |
F2 | Song Position Pointer | F2 ll hh (3) | (シーケンサー)再生位置指定 | |
F3 | Song Select | F3 ss (2) | (シーケンサー)楽曲選択 | |
F4 | --- | --- | 未定義 | |
F5 | --- | --- | 未定義 | |
F6 | Tune Request | F6 (1) | チューニング開始指示 | |
F7 | End Of Exclusive | F0.... F7(-) | System Exclusiveの終了 | |
F8 | Timig Clock | F8 (1) | (シーケンサー)クロック | System Realtime Msg |
F9 | --- | F9 (1) | 未定義 | |
FA | Start | FA (1) | (シーケンサー)再生開始 | |
FB | Continue | FB (1) | (シーケンサー)再生再開 | |
FC | Stop | FC (1) | (シーケンサー)再生停止 | |
FD | --- | FD (1) | 未定義 | |
FE | Active Sensing | FE (1) | 接続確認用メッセージ | |
FF | System Reset | FF (1) | システム・リセット |
まず、メッセージは、リアルタイム・メッセージとノン・リアルタイム・メッセージに分類されます。上の表ならば、Channnel MessageとSystem Common Messageがノン・リアルタイム・メッセージ。ここで注意するべきことは、ノン・リアルタイム・メッセージの中にリアルタイム・メッセージが割り込む2層構造をとるということです。
つまり、90 f8 64 64などという並びがくる可能性があります。これは、ノート・オン・メッセージ(90 64 64)にリアルタイム・クロック(F8)が割り込んだ形です。これを処理できなければなりません。
次の問題はランニング・ステータスです。リアルタイム・メッセージを除外したメッセージの流れの中で、同一のスタータスを持つチャンネル・メッセージが続くとき、後続のチャンネル・メッセージのスタータスは省略できるというものです。
90 64 64、90 32 66、90 64 0と続いたとき、これは、90 64 64、32 66、64 0という形で後続の90を省略できます。この結果、転送データ量が減り、リアルタイム応答性が増すという仕掛けです。ステータスの変更を避けるため、ノート・オン・メッセージのヴェロシティが0の場合はノート・オフと見做す(ヴェロシティ64のノート・オフ)という決まりがあります。極端に言えば、ノート・オンとノート・オフだけを扱うならば、最初に一度だけステータスを出せばよい事になります。
細かいことですが、システム・コモン・メッセージのステータスはランニング・ステータスが適用されないこと。システム・コモン・メッセージを跨いでランニング・ステータスは適用されないことに注意が必要です。
コントロール・チェンジの一部の制御番号は、歴史的な経緯を経ながら、特殊な機能が割り当てられています。本来のコントロール・チェンジとして使用されるのは、現在は、0-119番のみとなっています。
では、本来ではないコントロールチェンジの制御番号をまとめてみます。これらは、チャンネル・モード・メッセージと呼ばれています。
CC | Name | |
---|---|---|
120 | All Sound Off | チャンネルのすべての音を消す。 |
121 | Reset All Controller | コントローラを初期化する |
122 | Local Control | キーボードを切り離す |
123 | All Note Off | チャンネルの音をノートオフする。(音を消すわけではない) |
124 | Omni Off | チャンネル指定を無効にする |
125 | Omni On | チャンネル指定を有効にする |
126 | Mono Mode On | モノ指定 |
127 | Mono Mode Off | ポリ指定 |
まず、124-127は、機器そのものの動作を規定するメッセージですが、この規定に沿って動作する楽器は非常に稀です。特に現在のマルチチャンネルの音源では、実情に合わないようで、それなりに解釈を与えられて動作するようです。これらを受信した場合は、少なくとも、(慣習的に)ノート・オフ処理を行うようです。
122はローカル・コントロール。キーボードつきの楽器の場合に、キーボードを切り離して、発音しなくする機能です。今回はキーボードが無いので関係ありません。
121はコントローラの値を保持している内部情報を初期値にします。シーケンサーの開始時などに送られる可能性があります。
120と122はよく似ているのですが、少し異なります。All Note Offは、発音中の音にノート・オフ処理を行います。ノート・オフですから、音のリリース部分は残るわけで、音が消えるわけではありません。これに対してAll Sound Offは、音そのものを止めます。実際にはノート・オフ処理をしてから消音を行うことになるでしょう。両方とも、指定のチャンネルに対してのみ処理を行い、さらに、MIDIの規格によれば「キーボードの音には作用しない」、つまり、MIDI受信した音だけ処理するのが好ましいとされています。
この部分は発音管理に関係があるので、一応、心に留めて置きます。
System Exclusiveメッセージは、今回は扱いませんが、可変長のメッセージなので、面倒な部分が多いです。表中では、F0 .... F7と書きましたが、規定上は、F0で始まり、次のノン・リアルタイム・メッセージが出る直前までで、必ずしもF7は必要としません。実際に初期の頃には、最後のF7が付かない機種もありました。言い換えるとF7は区切りを表す何もしないメッセージということになります。ここもちょっと留意する必要があります。
メッセージ処理全体では、不必要なメッセージでも、(バッファに入れなくても)処理は行うこと、リアルタイムでない未定義命令の長さは不明なのでそれなりに動作するようにするなどの配慮が必要です。また、多少のメッセージの誤りを受信しても処理そのものは破綻しないことも重要なポイントです。
いよいよ最初のソフトウェアの製作です。
大まかな仕様は次の通り。
プログラムの大まかな動作は、次の通りです。
ちょっと、見積もったところでは、シリアルの受信割り込み内で発音まで行っても間に合いそうです。最終的な処理が出来上がったところで、シミュレータでサイクル数を数え、間に合わないようならば、発音関係は地面でのポーリングに改めます。
プログラムのパーツについて検討します。
通常のシリアル割り込みを行い、フレミング・エラー、オーバーラン・エラーを除去
1バイトを受け取り、3バイトのチャンネルメッセージを組み立てる処理。入り口でリアルタイムメッセージを除去する。チャンネルメッセージだけを扱い、ランニングステータスの管理を行う。
実際にコードを書いてみると意外に小さく書けました。
三つしかない音源を後着優先で管理します。発音(周波数レジスタと音量レジスタ)の組を「スロット」という単位で捉え、これを管理するものです。今回は3音しかないのでベタにスロットの配列を検索した方が速いかもしれませんが、今後の拡張も考えて一種のリンクリストで管理することにしました。
ノートオン要求があると、未割り当てスロットを確保。無い場合には発音リストの一番古いものをリストから削除して、発音を止め、未割り当てスロットにします。これを発音リストの一番後ろに追加。
ノートオフ発音中止要求(8x nn nn と 9x nn 00)があると、発音リストの古いほうから検索して、同一のノート番号を持つものを見つけ、発音を止めて、リストから削除。
オールノートオフでは、発音中のすべてのスロットの発音を止めながらリストから削除。
追加、削除などの速度が要求されます。いろいろな手法があるのですが、色々試してコードサイズが最も小さくなったリング状のリンクを採用。前方リンクと後方リンクの二つを用意。ルートリンクから、それぞれ、逆周りにリンクします。前方リンクならば、最後のスロットはルートを指すことになります。
未割り当てスロットは単一の前方リンクで数珠繋ぎ。こちらは最後のリンクが0ならば終わり。取り出すにはエントリを見るだけ。未割り当てリストへの追加位置は常に先頭としました。
発音管理処理で決められたスロットに対して、周波数レジスターと音量レジスタの書き込みをします。
ノートオンでは、MIDIノート番号から128エントリのテーブルを引いて、周波数を書き込み。ベロシティを音量に変換して、これも書き込みます。テーブルの位置を256の倍数にすればテーブル引きが速くなるのですが、とりあえずは、今回は汎用アドレスで処理します。
ノートオフは、音量を0にするだけ。
大まかなデバッグはMicrochip社のMPLABのシミュレータで行いましたが、シリアル入力データのテキストファイルを用意すると、その内容を読み込んで割り込みのシミュレートまでしてくれるので、とても便利ですが、実際のところ、なかなか、思うように動作しませんでした。デバッグタブの「Limitation」では「Serial I/O is not simulated」と書いてあるのですが、UART I/Oのタブはあります。うまく動くときもあるので、なんとか、PC上でデバッグ終了。
ブレッドボード上の回路に実装して、PCのシーケンサーから実行すると、動いているようです。音源制御も結局、割り込みの中で処理しましたが、速度的には問題が無いようです。MIDIの1バイト320μSに対して、シミュレータでの割り込み処理の最長時間でも50μS程度。MIDIメッセージのバッファリングも省いてしまいました。
プログラムサイズは、音程テーブルを除いて358ワード。音程テーブル(256ワード)を含めると614ワードでかなり余裕があります。
そろそろブレッドボードもやめようと思うので、それまでに、決めなければならないことが数点。
入力は1ビットとして、スイッチをとりあえず設けておく。将来のソフトのバージョンアップで起動時にモード選択?
もう一台のPSGを増設できるように、出力として1ビット確保。
取り扱わない。
一応、9V程度のACアダプタと006pの電池切り替えを検討しているのですが、手持ちの3端子5vがあるかどうか。5V電源入力も残したい気持ちもあります。
手持ちを調べたら78L05があったので、これで9vからドロップすることにします。5vは端子を立てておいて、ジャンパー切り替え。電源スイッチは使用せず、切り替え対応の出力ジャックを使用します。
一応、簡単なローパスフィルターと、直流カットぐらいは入れたい。
1μのコンデンサで大丈夫のようです。ケチして電解コンデンサ。ローパスフィルタは割愛しました。
これ、シリアル出力を使えば何とかなりそうですが、重いかなとも。
シリアル出力は将来のためのMIDIアウトとして残して、エラー表示は割愛。電源のLEDを付けたい。
一号機は、結局のところ、秋月のC基板上に手配線で組み立てました。電源、グランド、オーディオ信号以外は、ほとんどを、ウレタン線で配線しました。これは、初めて使ったのですが、線が細いため、非常にコンパクトに仕上がります。拡張コネクタをやめて、2個目のPSG用のソケットを組み込めばよかったかもしれません。
インタフェース・コネクタに、MIDI In/Out、ACアダプタ、フォーンのジャックと、電池スナップを付けて動作確認。電源周りの切り替え(on/offと電池/ACアダプタ)はok。MIDI InにPCのシーケンサを繋いで簡単な曲の演奏もできました。
今回の作業で、初めて使ったウレタン線は、コテを当てると皮膜が溶けるもので、面倒な皮剥きがいりませんし、線が細く皮膜が薄いので非常にコンパクトに配線できます。一方で、皮膜が溶けるまで時間がかかるため、半田付けの時間がやや長くなってしまいました。この点は工夫が必要な気がします。温度の高いコテを使うとか、あらかじめ線材にコテを当てておくとか、を次の機会に試したい。
さて、ジャック類がバラけているので、仮パネルを作るか、あるいは、本格的に箱入れをするか、どちらにしても、このままでは、危ないです。
百円ショップで見つけた名刺2枚(=秋月C基板2枚)サイズのスチール缶に組み込みました。
缶に取り付けたのは、次の通り。
結局、電源表示用のLEDと、予約していたスイッチは取り付けを見送りました。
ケースの加工は、穴あけだけです。基板やDINジャックを止めるビス穴は3oでこれは、ミニルータに金工用ドリルを付けて、小さい穴から始めて、最後は3oのドリル。ジャック用の大きめの穴は同様に3oまで穴を開けてから、リーマーである程度の大きさまで拡げます。その後、ミニルーターにハイスカッターを取り付けて切削。ステンレススチールにも関わらず快調に作業できました。これは、ハイスカッターの威力に敬服。
内部は窮屈で、006Pの電池がやっと入るぐらい。電池は結局、緩衝材に包んで押し込んだだけです。このケースは蓋がスライド式なので、蓋に部品を取り付けると開かなくなってしまうのが難点ですが、今回のように操作パネルが無い場合には丁度具合が良いようです。
基板の実装は次の通り。
ちょっと寄り道をして、このハードを用いて、純正律の実験をするソフトウェアを書こうと思います。
純正律は、和音の周波数比が簡単な整数となり、唸りのない響きが得られる音階です。市販のシンセでも純正律を搭載しているものは多いのですが、原理的に誤差の無いものでは無いようです。ここでは、音程を決める分周比を理論どおりに決めることで、誤差の無い純正律音階を決定します。
純正律の各音の周波数比は簡単な分数で表されるので、この逆数をとり、分母の公倍数を求めれば(通分しているわけです)、設定すべき値の最も小さなセットを計算できます。問題は、PSGの分解能(周波数レジスタのビット数)が低いこと。十分な発音レンジが確保できないかもしれません。また、自由にキーを選ぶことも難しいでしょう。とりあえず、相対音高が大切なので、キーを考えずに計算してみます。
下の表で周波数の逆数の分母の最小公倍数は180となったので、表の値の欄が、PSGの周波数レジスタにセットする最小の組合せとなります。
ド | レ | ミ | ファ | ソ | ラ | シ | ド | |
---|---|---|---|---|---|---|---|---|
周波数比 | 1 | 9/8 | 5/4 | 4/3 | 3/2 | 5/3 | 15/8 | 2 |
逆数 | 1 | 8/9 | 4/5 | 3/4 | 2/3 | 3/5 | 8/15 | 1/2 |
設定値 | 180 | 160 | 144 | 135 | 120 | 108 | 96 | 90 |
高い方のドが値90となりました。上の方の平均律での設定表から、これは、F6の値=89に近いので、キーはFならば、(実際は多少ずれますが)長調の純正律音階が実現できます。また、このオクターブより低い方ならば、低音の折り返し領域を除けば、原理的に正確な周波数比を保てます。
これは、たまたまのラッキーだったわけですが、相対音高でよければ、PSG以外の分周音源でも同様のことが実現できます。
さて、これで、ダイアトニック(白鍵相当)の値は決まったのですが、黒鍵部分の音程は決まっていません。純正律なので、黒鍵部分はサポートしなくてもいいでは無いかとも、思うのですが、音が出ないのも寂しいし、ということで、何かを割り当てることにします。平均律の音を割り振るのもいいのですが、せっかくだから、多少とも縁のあるものを割り振ってみます。
対象になるのは、ド#レ#ファ#ソ#ラ#の五つの音です。それぞれについて勝手な理屈で、音を割り振ります。
キーがFとなっているので、ちょっと、面倒かもしれません。F#の音にちょっと音高が低いGを割り振った変則的な鍵盤配置となっていますが、意外に使えるかもしれません。
FのダイアトニックではGm以外はそのまま、和音が使えます。Gmの時にはGの代わりにF#を使うとGmが純正になります。Gの和音もGの代わりにF#を使えば純正になります。
短調、つまり、Dmのキーでも、GmのときにGの代わりにF#を使えば、正しく響きます。属和音のAは純正では無いのですが、属和音だから多少濁ってもよいとすれば、使えないことはありません。
さて、いよいよ、純正律を組み込むことにします。値の計算は、理屈から言うと、最低のオクターブの表だけ持ち、オクターブに合わせて右シフトすればいいのですが、PICの鬼門の除算が必要になります。12で割って商と余りを求めるだけなのですが、除算命令がないので、そこそこ時間がかかります。そこで、オリジナルの平均律版と同様に128音相当のテーブルを持つことにします。幸いプログラムスペースは、まだ空いているので平均律版に機能追加することで対応できそうです。
計算したものが左の表です。
表の「計算値」欄が黄色になっているものは、低域の折り返し領域です。また、誤差が10セントを超えるものは誤差欄が黄色になっています。
実用音域は、目論見どおり、C1からA6までをカバーしています。A6がバイオリンの1弦の高い方のAですから、まぁまぁでは無いでしょうか。ここまでで、「理論的な」準備が整いました。
いよいよプログラムの書き直しです。
変更の主な点は次のとおりとなります。
上の方で計算したテーブルをそのまま組み込んでいます。
今まで、汎用のテーブル引きを使用していたのですが、テーブルのアドレスが256の倍数の位置になるように固定しました。これは、テーブルの先頭に org ($-1)/.256*.256+.256 を加えて実現できます。
この結果、テーブル引き(間接ジャンプ)は、記憶していたテーブル上位アドレスをPCLATH(ジャンプアドレスの上位)レジスタに書き込み、MIDIノート番号を左シフトしてPCLに書き込みだけという簡単な形になりました。左シフトする前にキャリーをセットすると上位データ、キャリーをクリアしておけば下位データを得ることができます。
起動時は、平均律。プログラム番号1(値は0)ならば平均律、それ以外は純正律とします。スペック上は、プログラム番号1が平均律、プログラム番号2ならば純正律、それ以外の動作は保証しないということにしておきます。
処理としては、単純にそれぞれのテーブルアドレスの上位をメモリーに記憶するだけです。
また、これを機会に、MidiRender(メッセージ解析)をメッセージごとのテーブル分岐に改めました。将来に備えての変更です。
EEPROMの0番地の1バイトでMIDIチャンネルを指定できるようにしました。これでプログラムの変更をしなくても大丈夫。EEPROMの0番地の下の4ビット(下位ニブル)でチャンネルを指定します。0ならば1チャンネル、1ならば2チャンネルといった具合です。秋月のライターならば、EEPROMの変更と書き込みなどができるので、ここで変更して書き込んでください。
作成したプログラムのHEXファイルとASMファイルを掲載しました(Ver.0.02)。
純正律を使用する場合、キーがFであること、G♭がGの低い方のバリエーションに割り振られていることに注意してください。その他、細かいことは、上の方にゴタゴタと書いてあります。
いくつかの和声進行を試したのですが、劇的には変わりませんでした。つまり、夢見るほど美しくはなりませんでした。矩形波でなく、もっと倍音が少ない音の方が効果がありそうです。
それでも、「唸り」に関しては、当たり前ですが、大幅に改善されています。特に密集音形(クローズ)での変調感は全くありません。
レ(G)の低い方の音の使って、Gmの響きを試しました。変調感(みょうな唸り)は無くなりますが、レの音が曲の中では下がって聞こえて、気持ちが悪いです。
効果音シンセの前に、全体の処理を見直すことにしました。大きな目的は二つ。一つは、MIDIの機能を強化するために、MIDIメッセージの処理を割り込みの外側に出すこと。もう一つは、これによって生じた時間的な余裕を利用して、これからの、プログラム変更を可能な限り、プログラムチェンジで切り替えられるようにすることです。
また、現在のポリ3音機構のうち、平均律の処理は、RJBさんからご要望のあったピッチベンドなどを組み込む予定です(精度は低いのですが)。純正律の方は現在のまま、残す方向で検討しています。
プログラム全体は、リロケータブル形式に改める予定。
本格的な効果音機能を製作する前に、外部のMIDIシーケンサからPSG内部のレジスタを触って実験できるようにしようと考えました。まず、PSGのレジスタを見てみます。
Adr | Param | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 | |
---|---|---|---|---|---|---|---|---|---|---|
0x00 | A 周波数(Lo) | TP7-TP0 | 各チャンネルのTONE周波数を決めます。それぞれ、二つのレジスタをペアを使い、12ビットの精度。 | |||||||
0x01 | A 周波数(Hi) | - | - | - | - | TP11-TP8 | ||||
0x02 | B 周波数(Lo) | TP7-TP0 | ||||||||
0x03 | B 周波数(Hi) | - | - | - | - | TP11-TP8 | ||||
0x04 | C 周波数(Lo) | TP7-TP0 | ||||||||
0x05 | C 周波数(Hi) | - | - | - | - | TP11-TP8 | ||||
0x06 | Noise 周波数 | - | - | - | NP4-NP0 | ノイズは5ビットの精度。 | ||||
0x07 | Mixer 設定 | - | - | C | B | A | C | B | A | A-Cのチャンネルへの出力を設定。それぞれのビットを0にすると出力。 |
- | Noise | Tone | ||||||||
0x08 | A 音量 | - | - | - | M | L3-L0 | M=0ならば、L3-L0で直接音量を決定。M=1ならば、エンベロープで音量を制御。 | |||
0x09 | B 音量 | - | - | - | M | L3-L0 | ||||
0x0A | C 音量 | - | - | - | M | L3-L0 | ||||
0x0B | エンベロープ周波数(Lo) | EP7-EP0 | 二つのレジスタで都合16ビット。エンベロープ周期を決定。 | |||||||
0x0C | エンベロープ周波数(Hi) | EP15-EP8 | ||||||||
0x0D | エンベロープ形状 | - | - | - | - | CONT | ATT | ALT | HOLD | CONT=1 繰り返しあり CONT=0 繰り返し無し ATT=0:下り波形 ATT=1:上り波形 ALT=0:折り返しなし AL=1:折り返しあり HOLD=0:レベル保持なし HOLD=1:レベル保持 |
CONT | ATT | ALT | HOLD | |
---|---|---|---|---|
0 | 0 | X | X | |\____ |
0 | 1 | X | X | /|____ |
1 | 0 | 0 | 0 | |\|\|\|\ |
1 | 0 | 0 | 1 | |\____ |
1 | 0 | 1 | 0 | \/\/\/ |
1 | 0 | 1 | 1 | |\| ̄ ̄ ̄ ̄ |
1 | 1 | 0 | 0 | /|/|/|/| |
1 | 1 | 0 | 1 | / ̄ ̄ ̄ ̄ |
1 | 1 | 1 | 0 | /\/\/\ |
1 | 1 | 1 | 1 | /|____ |
エンベロープ形状は、ちょっと面倒なのですが、左の表のとおりです。
大雑把にまとめます。
出力チャンネルは3つ。それぞれのチャンネルはTONEとNOISEの組合せを出力(ミュートも可)。TONEは各チャンネルに用意されているがNOISEは共通のものが一つだけ。チャンネルの音量は、音量指定、エンベロープ指定のいずれかを選んで制御。音量指定は各チャンネル毎に可能で16段階。エンベロープ指定は、共通の一つのみ。波形と周期を指定できる。
MIDIのコントロールチェンジで制御すると、コントロールチェンジの値が7ビットなのが障害になります。PSGレジスタマップを見ると、問題になりそうなのは、TONEの周期(0x00-0x05)とエンベロープ周期(0x0B、0x0C)。TONEの周期は12ビットなので、二つに分けて送れば、まぁ、我慢できますが、エンベロープ周期は16ビットあるので、3回に分けなければなりません。また、実際にレジスタに格納するまで保留する処理も必要になります。
さて、具体的に、どのように割り振るか、ちょっと、思いつきません。NRPCという便利なものもあるのですが、これだと、メッセージ量が増えてしまうし…
ダウンロード・ファイル | |
---|---|
回路図 Pdfファイル | 本体基板の回路図です。 |
ダウンロード・ファイル(ver.0.02) | |
制御プログラム Hexファイル | 3音ポリの制御ソフトです。 純正律対応、EEPROMによるチャンネル切り替えなどに対応しました。 |
制御プログラム Asmファイル |
部品表
名称 | 個数 | |
---|---|---|
PIC16F648A | 1 | CPU。初期化処理を書き換えれば16F88なども使えるはず。 |
YMZ294 | 1 | PSG。秋月で発振子と組で500円。 |
4MHz発信子 | 1 | 秋月のセットのもの |
PC910 | 1 | フォトカプラ。MIDI入力用。鈴商で入手。 |
セラロック20MHz | 1 | PICのクロック発振用。PICのすぐ近くにおいてください。秋月で入手。 |
小信号ダイオード | 1 | 適当なもので十分。 |
78L05 | 1 | 三端子レギュレータ。100mA取れれば十分です。 |
LED | 1 | パワー表示用のLED。適当で結構です。今回は結局実装しませんでした。 |
抵抗100kΩ | 1 | オーディオ出力用。 |
抵抗10kΩ | 3 | スイッチのプルアップ用:適当で結構です。 PSGのA0プルアップ用:PICの該当出力端子がオープンドレインなので必要です。 オーディオ出力用:1kΩをつけていたのですが、電流節約のため10kΩにしました。 |
抵抗4.7kΩ | 2 | フォトカプラの出力プルアップ:これも出力がオープンドレイン(?)なので必要。 フォトカプラのE端子プルアップ:要らないかもしれませんし、値も適当で…。手持ちの都合でこの値。 |
抵抗1kΩ | 1 | LEDの電流制限用。図面上は値を指定しませんでした。LEDが点灯すれば良いので、大きめの値にすれば電流の節約。 |
抵抗220Ω | 3 | MIDIの入出力にお約束で付けるものです。 |
電解コンデンサ47μF | 1 | レギュレータの出力安定用。もっと小さくても大丈夫。 |
電解コンデンサ10μF | 1 | レギュレータの入力安定用。無くても動きます。 |
電解コンデンサ1μF | 1 | オーディオ出力の直流カット用。極性は+がPSG側で、−が出力側。本当は無極性電解コンデンサか、フィルムコンデンサを使った方が良いです。 |
積層セラミックコンデンサ0.1μF | 3 | お約束の「パスコン」。PIC/YMZ294/PC910の電源とグランドピンの間になるべく近く配線します。 |
基板 | 1 | 秋月のC基板を使いましたが何でもいいです。 |
ICソケット18p | 2 | PICとYMZ用。特にPICはソフトウェアを変更する可能性があるので、必ずソケットに。 |
ICソケット8p | 1 | PC910用 |
PHONE JACK | 1 | オーディオ出力端子。パワースイッチを兼ねるので3接点のもの。 |
DIN5P ジャック | 2 | MIDI IN/OUT用。今回のソフトウェアではOUTは実際には何の機能もありませんから、実装しなくても良いです。その場合は1個だけ。 |
DCジャック | 1 | ACアダプタ用。内部の電池との切り替えを兼ねているので3接点のもの。 |
006P電池スナップ | 1 | 電池の頭につけて、線が2本出るものです。 |
ケース | 1 | 今回は百円ショップのスチール缶を使いましたが、入れば何でもいいですよね。 |
006P電池 | 1 | 電源用電池。 |
ACアダプタ | 1 | 9V以上あれば動作するはずです。コネクタの極性があるのでご注意ください(センターマイナスとセンタープラス)。秋月のものはセンタープラスなので、回路図はこれに従っていますが、楽器用はセンターマイナスが多いです。 |
回路図の簡単な説明。
CN2の電源関係は、ややこしいのですが、ACアダプタ用のDCジャックが3端子のものを使用して、電池の006Pと自動切換えにしています。ACアダプタを繋がなければ、グランドが006Pの「−」端子に繋がるようになります。
フォン端子は、三端子のものを使って電源スイッチ兼用にしています。これは、ジャックが差し込むと二つのグランド端子がショートすることを利用して電源からのグランドを回路に接続するやり方です。
どちらにしても、購入したジャックに依存するので、この辺は臨機応変な対応が必要ですので、ご注意ください。
5V電源は、三端子レギュレータから作っています。ここで使っているのは、78L05ですが、7805とは端子が違っています。ご注意ください。電源についている電解コンデンサは、電源近くに配置します。
容量は、10μから47μ程度で大差は無いと思います。入力側は不要かもしれません。また、レギュレータのチップによっては、小さなコンデンサが必要な場合もあります。ご使用になるレギュレータのデータシートで付加コンデンサの必要性、容量などをご確認の上、回路をアレンジしてください。(TIのμPA78L05ならば0.33μと0.1μ)
図面の1ページ目右下にあるコンデンサは、積層セラミックの0.1μのもので、ICの電源とグランドのすぐそばに置かないと効果がありません。一応、PC-910、PIC16F648A、YMZ294のそれぞれにひとつずつ配置してください。発信器には不要かと思います。
バグや、今後の仕様/回路変更などで変わる予定です。一応、個人の使用は自由ですが、商用利用の際はご連絡ください。内容に関する質問、バグのご指摘など、アルゴ算法堂のブログページのPSGに関する記事にコメントの形でお書きください。