電子工作

PSGへの道

ふと、秋月で手にした懐かしいPSGのチップ YMZ294。昔、AY-3-8910をPC−8001のプリンタポートにつけて遊んだっけ。AY-3-8910は、8ビットのI/Oポートが二つ付いていて(これは、後年のFMチップにも引き継がれた)、大きなICだったけれど、YMZ294にはそれが無くて、コンパクト。まぁ、所詮、矩形波とか言いながら、楽器仕立てにできないかなと模索しています。


CPUを選ぶ

独立した装置にするのだから、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も考えてみる予定。


MIDI関係を検討

PSGの制御部は、I/Oポートで素直にできそうですが、ネックになりそうなのは、MIDI周り。

検討して実験しなくちゃいけないのはMIDI入力回路と、シリアル入力のプログラム部分(特にクロックレート)。

まずは、ソフトの検討から始めます。

MIDIのボーレートは31.25kHz。これに近い値を選ぼうということです。

PICでのボーレートは、0-255の1バイトで指定し、計算方法は2種類あります。

  1. Low Speed Mode

    BaudRate = Fosc/(64(X+1)) → X = Fosc/(BaudRate*64) -1

  2. High Speed Mode

    BaudRate = Fosc/(16(X+1)) → X = Fosc/(BaudRate*16) -1

いずれも、Foscは動作クロック、Xはレジスタに設定する値です。

ClockModeValueBaudRateError
20MHzLo931.250kHz0%
20MHzHi3931.250kHz0%
16MHzLo731.250kHz0%
16MHzHi3131.250kHz0%
10MHzLo431.250kHz0%
10MHzHi1931.250kHz0%
8MHzLo331.250kHz0%
8MHzHi1531.250kHz0%
4MHzLo131.250kHz0%
4MHzHi731.250kHz0%

誤差がすべて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用途備考
RA0oymz/D0YMZ294のデータバスへ
RA1oymz/D1同上
RA2oymz/D2同上
RA3oymz/D3同上
RA4oymz/A0YMZ294のA0へ。プルアップ必要
RA5iSWモードスイッチ。入力用ポート1ビット。コンフィグで指定が必要。
RA6-xtalセラロック20MHz
RA7-xtal同上
RB0oymz/CSYMZ294の制御信号
RB1-RXMIDI入力
RB2-TXMIDI出力(?)。USARTを使用しているときは、出力ポートにできない。
LEDをつけても良いかもしれない。
RB3oymz/WRYMZ294の制御信号
RB4oymz/D4YMZ294のデータバスへ
RB5oymz/D5同上
RB6oymz/D6同上
RB7oymz/D7同上

やはり割付がゲテです。せめて28ピン・デバイスにすれば、YMZへのデータポートを一本化できるのですが。モード指定にも、もう少しビット数が欲しいです。外付けのマルチプレクサでも付ければできるのですが、そうすると、配線量も増えるし、ICのコストもかかる。それなら28ピンの方が安いという結論になりそう。28ピンにすればセグメントLEDをつけるとか、液晶をつけるとか、機能追加もできそうだし…

このまま、コンパクトに行くか、方向転換して28ピンデバイスを選ぶか。ちょっと、考えどころですね。


ハードウェアを決めた

迷っていても仕方が無いと、CPUは強引に18ピンのPIC16F648A。クロックはセラロックで20MHz。フォトカプラはPS2501(これは結局使用を断念。PC900Vに代えました)。これは小さな回路で実験したところ使用できそう。負荷抵抗が小さい方が速そうなのですが(データシートでは100Ω)、それでは電流が流れすぎてもったいない。負荷抵抗はとりあえず1kΩにして、様子を見ながら下げようと思います。

当座はブレッドボード上で実験。秋月のライターもしばらく使っていないので、チェックしなければなりません。「PICピカピカ」のボードで色々動かすと。動作はOKのようです。


デバッグ用のMIDI出力

出力確認用のLEDすらないので、デバッグ用にMIDI出力回路を設けます。いろいろと考えたのですが、PICのTXをそのまま出力に使えるのではないかと思いました。

一応の5mA程度の電流ドライブはできるので、相手先でショートでもしない限りは大丈夫でしょう。


ハードウェアを確認する

ハードウェアの動作を段階的に検査します。

  1. 基本的に動いているか。

    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。ドンピシャですね。

  2. MIDIクロックの周波数

    上のほうで計算してあるUSARTの値の設定で果たして正しいクロックが設定されるか確認します。

    プログラムは、単にバッファの空きをポーリングして0x55を書き込む。大まかに言えば矩形波が出るのでこの周波数は、MIDIクロックの1/2、すなわち、15.625kHzのはず。

    RB2にテスタを当て、周波数を測定すると、15.62kHz。うまくいっているようです。

  3. ループバックテストを行う。

    USARTで出力したデータをUSARTで受け取るループバックテストのプログラムを書き、検査します。

  4. YMZとの接続の確認。

    気になっているのは、出力ピンが足りないためにYMZのクリア端子をHIにつっていることです。オーディオ出力は1kΩの負荷でラインに繋げば鳴りそう。当面はオペアンプは不要か?この辺りも試します。


音程の周波数計算

音の高さは二つのレジスタを組にして設定します。都合12ビットの値です。YMZ294に4MHzを入力した場合の計算式は、

tp=4*1000*1000/32/Ft

ここでtpが設定する値。Ftが設定したい周波数です。

この式を元に、A=440Hz、平均律の場合の設定値と、値を設定した場合の実際に出る音の周波数、それから誤差を計算したものが下の表です。誤差はCentつまり、1/100半音単位で表しています。設定値の欄が黄色は、値が大きすぎて設定できないところ、誤差の欄が黄色いのは誤差が10セント、つまり1/10半音を超えているところです。

 このボタンを押すとPICのアセンブラ形式で表示します。プログラムの組み込みなどにご使用ください。

低域の限界は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処理を書くに当たって、MIDIのメッセージストリームについて見直しておきます。要するにシリアルのメッセージストリームで、メッセージの種類を表すSTATUSとそれに付属するDataがあります。STATUSはそのmsbが1、Dataはmsbが0。これで区別が付きます。

これだけなら、簡単なのですが、実は、リアルタイム・メッセージとノン・リアルタイム・メッセージの区分。それからランニング・ステータスという面倒なものもあります。

StatusNameFormat(len)区分
8xNote Off8x kk vv (3)発音中止。kk=キー番号。vv=ヴェロシティChannel Msg
9xNote On9x kk vv (3)発音開始。kk=キー番号。vv=ヴェロシティ。vv=0ならば発音中止
AxAfter Touch(Poly)Ax kk vv (3)キー独立アフタータッチ。kk=キー番号。vv=値。
BxControl ChangeBx cc vv (3)各種制御。cc=制御番号。vv=値。
CxProgram ChangeCx pp (2)音色変更。pp=音色番号。
DxAfter Touch(Channel)Dx vv (2)チャンネルのアフタータッチ。dd=値。
ExPitch BendEx ll hh (3)ピッチベンド。hh ll =ベンド量(64 00:中央値)。
F0System ExclusiveF0 ... F7 (-)特殊用途用メッセージ。可変長。System Common Msg
F1MTC Quater FrameF1 nn (2)Midi Time Code用フレーム
F2Song Position PointerF2 ll hh (3)(シーケンサー)再生位置指定
F3Song SelectF3 ss (2)(シーケンサー)楽曲選択
F4------未定義
F5------未定義
F6Tune RequestF6 (1)チューニング開始指示
F7End Of ExclusiveF0.... F7(-)System Exclusiveの終了
F8Timig ClockF8 (1)(シーケンサー)クロックSystem Realtime Msg
F9---F9 (1)未定義
FAStartFA (1)(シーケンサー)再生開始
FBContinueFB (1)(シーケンサー)再生再開
FCStopFC (1)(シーケンサー)再生停止
FD---FD (1)未定義
FEActive SensingFE (1)接続確認用メッセージ
FFSystem ResetFF (1)システム・リセット

まず、メッセージは、リアルタイム・メッセージとノン・リアルタイム・メッセージに分類されます。上の表ならば、Channnel MessageとSystem Common Messageがノン・リアルタイム・メッセージ。ここで注意するべきことは、ノン・リアルタイム・メッセージの中にリアルタイム・メッセージが割り込む2層構造をとるということです。

つまり、90 f8 64 64などという並びがくる可能性があります。これは、ノート・オン・メッセージ(90 64 64)にリアルタイム・クロック(F8)が割り込んだ形です。これを処理できなければなりません。

次の問題はランニング・ステータスです。リアルタイム・メッセージを除外したメッセージの流れの中で、同一のスタータスを持つチャンネル・メッセージが続くとき、後続のチャンネル・メッセージのスタータスは省略できるというものです。

90 64 6490 32 6690 64 0と続いたとき、これは、90 64 6432 6664 0という形で後続の90を省略できます。この結果、転送データ量が減り、リアルタイム応答性が増すという仕掛けです。ステータスの変更を避けるため、ノート・オン・メッセージのヴェロシティが0の場合はノート・オフと見做す(ヴェロシティ64のノート・オフ)という決まりがあります。極端に言えば、ノート・オンとノート・オフだけを扱うならば、最初に一度だけステータスを出せばよい事になります。

細かいことですが、システム・コモン・メッセージのステータスはランニング・ステータスが適用されないこと。システム・コモン・メッセージを跨いでランニング・ステータスは適用されないことに注意が必要です。

コントロール・チェンジの一部の制御番号は、歴史的な経緯を経ながら、特殊な機能が割り当てられています。本来のコントロール・チェンジとして使用されるのは、現在は、0-119番のみとなっています。

では、本来ではないコントロールチェンジの制御番号をまとめてみます。これらは、チャンネル・モード・メッセージと呼ばれています。

CCName
120All Sound Offチャンネルのすべての音を消す。
121Reset All Controllerコントローラを初期化する
122Local Controlキーボードを切り離す
123All Note Offチャンネルの音をノートオフする。(音を消すわけではない)
124Omni Offチャンネル指定を無効にする
125Omni Onチャンネル指定を有効にする
126Mono Mode Onモノ指定
127Mono 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音ポリフォニック。MIDIチャンネルは仮に1チャンネル固定とする。
  2. 発音は後着優先で、発音数が足りない場合には、一番古く発音を始めたものを発音中止して新たに割り当てる。
  3. 発音範囲は0-128で上のほうで作成したテーブルで音程を計算。
  4. 受信メッセージは、基本的にはノートオン、ノートオフのみ。コントロールチェンジの120,122-127を受信するとオール・ノート・オフとする。従って、ピッチベンドやLFO、ポルタメントは処理しない。
  5. エンベロープは使用しないので、ノート・オン/ノート・オフ時に音量を操作するだけのオルガン型エンベロープとなる。
  6. ベロシティは、3ビット右シフト(1/8)して音量として設定。結果が0になった場合は1とする(これは、演奏してみて、あまり音量が小さいようならば、多少の値の下駄を考慮する)

プログラムの大まかな動作は、次の通りです。

  1. シリアル入力を割り込みで受け付ける。
  2. これからをMIDIのチャンネルメッセージを3バイト組に組み立てる。
  3. チャンネルフィルタを通して発音管理へ送る。
  4. 発音管理では、発音数、先着、後着などの管理を行い、実際の発音処理へ送る。
  5. 発音処理では、PSGのレジスタに音程と音量を書き込む。

ちょっと、見積もったところでは、シリアルの受信割り込み内で発音まで行っても間に合いそうです。最終的な処理が出来上がったところで、シミュレータでサイクル数を数え、間に合わないようならば、発音関係は地面でのポーリングに改めます。

プログラムのパーツについて検討します。

  1. シリアル受信処理

    通常のシリアル割り込みを行い、フレミング・エラー、オーバーラン・エラーを除去

  2. MIDIメッセージ組み立て処理

    1バイトを受け取り、3バイトのチャンネルメッセージを組み立てる処理。入り口でリアルタイムメッセージを除去する。チャンネルメッセージだけを扱い、ランニングステータスの管理を行う。

    実際にコードを書いてみると意外に小さく書けました。

  3. 発音管理処理

    三つしかない音源を後着優先で管理します。発音(周波数レジスタと音量レジスタ)の組を「スロット」という単位で捉え、これを管理するものです。今回は3音しかないのでベタにスロットの配列を検索した方が速いかもしれませんが、今後の拡張も考えて一種のリンクリストで管理することにしました。

    ノートオン要求があると、未割り当てスロットを確保。無い場合には発音リストの一番古いものをリストから削除して、発音を止め、未割り当てスロットにします。これを発音リストの一番後ろに追加。

    ノートオフ発音中止要求(8x nn nn と 9x nn 00)があると、発音リストの古いほうから検索して、同一のノート番号を持つものを見つけ、発音を止めて、リストから削除。

    オールノートオフでは、発音中のすべてのスロットの発音を止めながらリストから削除。

    追加、削除などの速度が要求されます。いろいろな手法があるのですが、色々試してコードサイズが最も小さくなったリング状のリンクを採用。前方リンクと後方リンクの二つを用意。ルートリンクから、それぞれ、逆周りにリンクします。前方リンクならば、最後のスロットはルートを指すことになります。

    未割り当てスロットは単一の前方リンクで数珠繋ぎ。こちらは最後のリンクが0ならば終わり。取り出すにはエントリを見るだけ。未割り当てリストへの追加位置は常に先頭としました。

  4. 発音処理

    発音管理処理で決められたスロットに対して、周波数レジスターと音量レジスタの書き込みをします。

    ノートオンでは、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. 入力が1-2ビット確保できそうなので、チャンネル切り替えなどに対応したい。

    入力は1ビットとして、スイッチをとりあえず設けておく。将来のソフトのバージョンアップで起動時にモード選択?

    もう一台のPSGを増設できるように、出力として1ビット確保。

  2. 念のためPSGのリセット信号を扱うか?

    取り扱わない。

  3. 電源を検討。

    一応、9V程度のACアダプタと006pの電池切り替えを検討しているのですが、手持ちの3端子5vがあるかどうか。5V電源入力も残したい気持ちもあります。

    手持ちを調べたら78L05があったので、これで9vからドロップすることにします。5vは端子を立てておいて、ジャンパー切り替え。電源スイッチは使用せず、切り替え対応の出力ジャックを使用します。

  4. オーディオ出力を検討。

    一応、簡単なローパスフィルターと、直流カットぐらいは入れたい。

    1μのコンデンサで大丈夫のようです。ケチして電解コンデンサ。ローパスフィルタは割愛しました。

  5. 受信エラー表示のLEDを点けたい。

    これ、シリアル出力を使えば何とかなりそうですが、重いかなとも。

    シリアル出力は将来のためのMIDIアウトとして残して、エラー表示は割愛。電源のLEDを付けたい。


基板が組みあがった

一号機は、結局のところ、秋月のC基板上に手配線で組み立てました。電源、グランド、オーディオ信号以外は、ほとんどを、ウレタン線で配線しました。これは、初めて使ったのですが、線が細いため、非常にコンパクトに仕上がります。拡張コネクタをやめて、2個目のPSG用のソケットを組み込めばよかったかもしれません。

インタフェース・コネクタに、MIDI In/Out、ACアダプタ、フォーンのジャックと、電池スナップを付けて動作確認。電源周りの切り替え(on/offと電池/ACアダプタ)はok。MIDI InにPCのシーケンサを繋いで簡単な曲の演奏もできました。

今回の作業で、初めて使ったウレタン線は、コテを当てると皮膜が溶けるもので、面倒な皮剥きがいりませんし、線が細く皮膜が薄いので非常にコンパクトに配線できます。一方で、皮膜が溶けるまで時間がかかるため、半田付けの時間がやや長くなってしまいました。この点は工夫が必要な気がします。温度の高いコテを使うとか、あらかじめ線材にコテを当てておくとか、を次の機会に試したい。

さて、ジャック類がバラけているので、仮パネルを作るか、あるいは、本格的に箱入れをするか、どちらにしても、このままでは、危ないです。


箱入れ完了

百円ショップで見つけた名刺2枚(=秋月C基板2枚)サイズのスチール缶に組み込みました。

缶に取り付けたのは、次の通り。

  1. MIDI IN/OUTのDIN5 ジャック。
  2. PHONE ジャック2個。一つは電源スイッチを兼ねたオーディオ出力。もう一つは将来の2PSG用のダミー端子。
  3. ACアダプタ用の丸ジャック。これはACアダプタと内臓の006P電池の切り替えを兼ねています。ここにアダプタを繋がなければ内部の電池で動作するというもの。

結局、電源表示用のLEDと、予約していたスイッチは取り付けを見送りました。

ケースの加工は、穴あけだけです。基板やDINジャックを止めるビス穴は3oでこれは、ミニルータに金工用ドリルを付けて、小さい穴から始めて、最後は3oのドリル。ジャック用の大きめの穴は同様に3oまで穴を開けてから、リーマーである程度の大きさまで拡げます。その後、ミニルーターにハイスカッターを取り付けて切削。ステンレススチールにも関わらず快調に作業できました。これは、ハイスカッターの威力に敬服。

内部は窮屈で、006Pの電池がやっと入るぐらい。電池は結局、緩衝材に包んで押し込んだだけです。このケースは蓋がスライド式なので、蓋に部品を取り付けると開かなくなってしまうのが難点ですが、今回のように操作パネルが無い場合には丁度具合が良いようです。

基板の実装は次の通り。

  1. メインのコネクタ
  2. もう一つのPSG用の拡張コネクタ。後から考えれば、ここに2ndPSG用のソケット置けばよかった。
  3. フォトカプラの出力分岐コネクタ。回路図には無いのですが、とりあえず、何かに使えるかと思い出してあります。
  4. 電源ジャンパー。これを外すとレギュレータからの電源5vをカットします。
  5. 電源5v端子。電源ジャンパーを外して、ここに、実験用の5v電源を繋げるように設けました。
  6. セラロックの20MHz。発信子です。これは、なるべくPICの近くに置きます。


純正律の実験 -- 実装の準備 --

ちょっと寄り道をして、このハードを用いて、純正律の実験をするソフトウェアを書こうと思います。

純正律は、和音の周波数比が簡単な整数となり、唸りのない響きが得られる音階です。市販のシンセでも純正律を搭載しているものは多いのですが、原理的に誤差の無いものでは無いようです。ここでは、音程を決める分周比を理論どおりに決めることで、誤差の無い純正律音階を決定します。

純正律の各音の周波数比は簡単な分数で表されるので、この逆数をとり、分母の公倍数を求めれば(通分しているわけです)、設定すべき値の最も小さなセットを計算できます。問題は、PSGの分解能(周波数レジスタのビット数)が低いこと。十分な発音レンジが確保できないかもしれません。また、自由にキーを選ぶことも難しいでしょう。とりあえず、相対音高が大切なので、キーを考えずに計算してみます。

下の表で周波数の逆数の分母の最小公倍数は180となったので、表の値の欄が、PSGの周波数レジスタにセットする最小の組合せとなります。

ファ
周波数比19/85/44/33/25/315/82
逆数18/94/53/42/33/58/151/2
設定値1801601441351201089690

高い方のドが値90となりました。上の方の平均律での設定表から、これは、F6の値=89に近いので、キーはFならば、(実際は多少ずれますが)長調の純正律音階が実現できます。また、このオクターブより低い方ならば、低音の折り返し領域を除けば、原理的に正確な周波数比を保てます。

これは、たまたまのラッキーだったわけですが、相対音高でよければ、PSG以外の分周音源でも同様のことが実現できます。

さて、これで、ダイアトニック(白鍵相当)の値は決まったのですが、黒鍵部分の音程は決まっていません。純正律なので、黒鍵部分はサポートしなくてもいいでは無いかとも、思うのですが、音が出ないのも寂しいし、ということで、何かを割り当てることにします。平均律の音を割り振るのもいいのですが、せっかくだから、多少とも縁のあるものを割り振ってみます。

対象になるのは、ド#レ#ファ#ソ#ラ#の五つの音です。それぞれについて勝手な理屈で、音を割り振ります。

  1. ド#:キーからは遠い音(ドッペル・ドミナントのドミナント)なので、使わないと勝手に決め、レのバリエーションの音を割り振ります。純正律の説明のところに書きましたが、この「レ」の音ではレファラの和音が純正になりません。つまり、「ソシレ」のレとしては正しく響くが、レファラのレとしては音程が高すぎます。そこで、正しく響くために、ドから小全音高いレを用意して、ド#に割り振ることにしました。周波数比は9/10。設定値は、162となります。
  2. レ#:=ミ♭として、同名調の短和音の三度の音を割り振ります。つまり、ドミ♭ソのミ♭にします。短三度ですから、周波数比は6/5。設定値は150です。
  3. ファ#:レファ#ラの「ファ#」を割り振ります。このときの「レ」は「ド#」に割り振った「レ」(ああ、ややこしい)を使います。周波数比は45/35。設定値は128です。コンピュータ的に美しい値です。
  4. ソ#:「ミソ#シ」のソ#を割り振ります。残念ながら、この音を正確に含めようとすると、公倍数がもっと大きくなって、PSGでは実用になりません。結局、設定値が整数にならないので、正しい音程ではありません。周波数比は25/16。設定値は576/5115.2です。
  5. ラ#:この音は何を割り当てるか迷いました。自然7度を聞きたい気もあったのですが、値が大きくずれてしまいました。そこで、ファの4度上のシ♭を割り当てることにしました。設定値を整数にできなかったので、正しい音程ではありません(オクターブの下の方ではOK)。周波数比は16/9。設定値は101.25です。

キーがFとなっているので、ちょっと、面倒かもしれません。F#の音にちょっと音高が低いGを割り振った変則的な鍵盤配置となっていますが、意外に使えるかもしれません。

FのダイアトニックではGm以外はそのまま、和音が使えます。Gmの時にはGの代わりにF#を使うとGmが純正になります。Gの和音もGの代わりにF#を使えば純正になります。

短調、つまり、Dmのキーでも、GmのときにGの代わりにF#を使えば、正しく響きます。属和音のAは純正では無いのですが、属和音だから多少濁ってもよいとすれば、使えないことはありません。

さて、いよいよ、純正律を組み込むことにします。値の計算は、理屈から言うと、最低のオクターブの表だけ持ち、オクターブに合わせて右シフトすればいいのですが、PICの鬼門の除算が必要になります。12で割って商と余りを求めるだけなのですが、除算命令がないので、そこそこ時間がかかります。そこで、オリジナルの平均律版と同様に128音相当のテーブルを持つことにします。幸いプログラムスペースは、まだ空いているので平均律版に機能追加することで対応できそうです。


計算したものが左の表です。

 このボタンを押すとPICのアセンブラ形式で表示します。プログラムの組み込みなどにご使用ください。

表の「計算値」欄が黄色になっているものは、低域の折り返し領域です。また、誤差が10セントを超えるものは誤差欄が黄色になっています。

実用音域は、目論見どおり、C1からA6までをカバーしています。A6がバイオリンの1弦の高い方のAですから、まぁまぁでは無いでしょうか。ここまでで、「理論的な」準備が整いました。


純正律の実験 -- 実装 --

いよいよプログラムの書き直しです。

変更の主な点は次のとおりとなります。

  1. 純正律音程テーブルの組み込み

    上の方で計算したテーブルをそのまま組み込んでいます。

  2. 音程決定処理の変更

    今まで、汎用のテーブル引きを使用していたのですが、テーブルのアドレスが256の倍数の位置になるように固定しました。これは、テーブルの先頭に org ($-1)/.256*.256+.256 を加えて実現できます。

    この結果、テーブル引き(間接ジャンプ)は、記憶していたテーブル上位アドレスをPCLATH(ジャンプアドレスの上位)レジスタに書き込み、MIDIノート番号を左シフトしてPCLに書き込みだけという簡単な形になりました。左シフトする前にキャリーをセットすると上位データ、キャリーをクリアしておけば下位データを得ることができます。

  3. プログラムチェンジでの、平均律/純正律切り替え機能

    起動時は、平均律。プログラム番号1(値は0)ならば平均律、それ以外は純正律とします。スペック上は、プログラム番号1が平均律、プログラム番号2ならば純正律、それ以外の動作は保証しないということにしておきます。

    処理としては、単純にそれぞれのテーブルアドレスの上位をメモリーに記憶するだけです。

    また、これを機会に、MidiRender(メッセージ解析)をメッセージごとのテーブル分岐に改めました。将来に備えての変更です。

  4. MIDI受信チャンネル変更機能

    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のレジスタを見てみます。

PSGレジスタマップ
AdrParamB7B6B5B4B3B2B1B0
0x00A 周波数(Lo)TP7-TP0 各チャンネルのTONE周波数を決めます。それぞれ、二つのレジスタをペアを使い、12ビットの精度。
0x01A 周波数(Hi)----TP11-TP8
0x02B 周波数(Lo)TP7-TP0
0x03B 周波数(Hi)----TP11-TP8
0x04C 周波数(Lo)TP7-TP0
0x05C 周波数(Hi)----TP11-TP8
0x06Noise 周波数---NP4-NP0ノイズは5ビットの精度。
0x07Mixer 設定--CBACBA A-Cのチャンネルへの出力を設定。それぞれのビットを0にすると出力。
-NoiseTone
0x08A 音量---ML3-L0 M=0ならば、L3-L0で直接音量を決定。M=1ならば、エンベロープで音量を制御。
0x09B 音量---ML3-L0
0x0AC 音量---ML3-L0
0x0Bエンベロープ周波数(Lo)EP7-EP0 二つのレジスタで都合16ビット。エンベロープ周期を決定。
0x0Cエンベロープ周波数(Hi)EP15-EP8
0x0Dエンベロープ形状----CONTATTALTHOLD CONT=1 繰り返しあり CONT=0 繰り返し無し
ATT=0:下り波形 ATT=1:上り波形
ALT=0:折り返しなし AL=1:折り返しあり
HOLD=0:レベル保持なし HOLD=1:レベル保持
エンベロープ形状
CONTATTALTHOLD
00XX|\____
01XX/|____
1000|\|\|\|\
1001|\____
1010\/\/\/
1011|\| ̄ ̄ ̄ ̄
1100/|/|/|/|
1101/ ̄ ̄ ̄ ̄
1110/\/\/\
1111/|____

エンベロープ形状は、ちょっと面倒なのですが、左の表のとおりです。

大雑把にまとめます。

出力チャンネルは3つ。それぞれのチャンネルはTONENOISEの組合せを出力(ミュートも可)。TONEは各チャンネルに用意されているがNOISEは共通のものが一つだけ。チャンネルの音量は、音量指定、エンベロープ指定のいずれかを選んで制御。音量指定は各チャンネル毎に可能で16段階。エンベロープ指定は、共通の一つのみ。波形と周期を指定できる。

MIDIのコントロールチェンジで制御すると、コントロールチェンジの値が7ビットなのが障害になります。PSGレジスタマップを見ると、問題になりそうなのは、TONEの周期(0x00-0x05)とエンベロープ周期(0x0B、0x0C)。TONEの周期は12ビットなので、二つに分けて送れば、まぁ、我慢できますが、エンベロープ周期は16ビットあるので、3回に分けなければなりません。また、実際にレジスタに格納するまで保留する処理も必要になります。

さて、具体的に、どのように割り振るか、ちょっと、思いつきません。NRPCという便利なものもあるのですが、これだと、メッセージ量が増えてしまうし…


資料

ダウンロード・ファイル
回路図 Pdfファイル 本体基板の回路図です。
ダウンロード・ファイル(ver.0.02)
制御プログラム Hexファイル 3音ポリの制御ソフトです。
純正律対応、EEPROMによるチャンネル切り替えなどに対応しました。
制御プログラム Asmファイル

部品表

名称個数
PIC16F648A1CPU。初期化処理を書き換えれば16F88なども使えるはず。
YMZ2941PSG。秋月で発振子と組で500円。
4MHz発信子1秋月のセットのもの
PC9101フォトカプラ。MIDI入力用。鈴商で入手。
セラロック20MHz1PICのクロック発振用。PICのすぐ近くにおいてください。秋月で入手。
小信号ダイオード1適当なもので十分。
78L051三端子レギュレータ。100mA取れれば十分です。
LED1パワー表示用のLED。適当で結構です。今回は結局実装しませんでした。
抵抗100kΩ1オーディオ出力用。
抵抗10kΩ3スイッチのプルアップ用:適当で結構です。
PSGのA0プルアップ用:PICの該当出力端子がオープンドレインなので必要です。
オーディオ出力用:1kΩをつけていたのですが、電流節約のため10kΩにしました。
抵抗4.7kΩ2フォトカプラの出力プルアップ:これも出力がオープンドレイン(?)なので必要。
フォトカプラのE端子プルアップ:要らないかもしれませんし、値も適当で…。手持ちの都合でこの値。
抵抗1kΩ1LEDの電流制限用。図面上は値を指定しませんでした。LEDが点灯すれば良いので、大きめの値にすれば電流の節約。
抵抗220Ω3MIDIの入出力にお約束で付けるものです。
電解コンデンサ47μF1レギュレータの出力安定用。もっと小さくても大丈夫。
電解コンデンサ10μF1レギュレータの入力安定用。無くても動きます。
電解コンデンサ1μF1オーディオ出力の直流カット用。極性は+がPSG側で、−が出力側。本当は無極性電解コンデンサか、フィルムコンデンサを使った方が良いです。
積層セラミックコンデンサ0.1μF3お約束の「パスコン」。PIC/YMZ294/PC910の電源とグランドピンの間になるべく近く配線します。
基板1秋月のC基板を使いましたが何でもいいです。
ICソケット18p2PICとYMZ用。特にPICはソフトウェアを変更する可能性があるので、必ずソケットに。
ICソケット8p1PC910用
PHONE JACK1オーディオ出力端子。パワースイッチを兼ねるので3接点のもの。
DIN5P ジャック2MIDI IN/OUT用。今回のソフトウェアではOUTは実際には何の機能もありませんから、実装しなくても良いです。その場合は1個だけ。
DCジャック1ACアダプタ用。内部の電池との切り替えを兼ねているので3接点のもの。
006P電池スナップ1電池の頭につけて、線が2本出るものです。
ケース1今回は百円ショップのスチール缶を使いましたが、入れば何でもいいですよね。
006P電池1電源用電池。
ACアダプタ19V以上あれば動作するはずです。コネクタの極性があるのでご注意ください(センターマイナスとセンタープラス)。秋月のものはセンタープラスなので、回路図はこれに従っていますが、楽器用はセンターマイナスが多いです。

回路図の簡単な説明。

  1. コネクタCN1がインタフェース用。CN2がもう一台PSGを拡張するための予備コネクタです。従って当面はCN2は不要です。
  2. MIDIコネクタは端子面(半田付けしない方)から見たものです。
  3. 電源関係

    CN2の電源関係は、ややこしいのですが、ACアダプタ用のDCジャックが3端子のものを使用して、電池の006Pと自動切換えにしています。ACアダプタを繋がなければ、グランドが006Pの「−」端子に繋がるようになります。

    フォン端子は、三端子のものを使って電源スイッチ兼用にしています。これは、ジャックが差し込むと二つのグランド端子がショートすることを利用して電源からのグランドを回路に接続するやり方です。

    どちらにしても、購入したジャックに依存するので、この辺は臨機応変な対応が必要ですので、ご注意ください。

  4. 5v電源

    5V電源は、三端子レギュレータから作っています。ここで使っているのは、78L05ですが、7805とは端子が違っています。ご注意ください。電源についている電解コンデンサは、電源近くに配置します。

    容量は、10μから47μ程度で大差は無いと思います。入力側は不要かもしれません。また、レギュレータのチップによっては、小さなコンデンサが必要な場合もあります。ご使用になるレギュレータのデータシートで付加コンデンサの必要性、容量などをご確認の上、回路をアレンジしてください。(TIのμPA78L05ならば0.33μと0.1μ)

  5. パスコン

    図面の1ページ目右下にあるコンデンサは、積層セラミックの0.1μのもので、ICの電源とグランドのすぐそばに置かないと効果がありません。一応、PC-910、PIC16F648A、YMZ294のそれぞれにひとつずつ配置してください。発信器には不要かと思います。

バグや、今後の仕様/回路変更などで変わる予定です。一応、個人の使用は自由ですが、商用利用の際はご連絡ください。内容に関する質問、バグのご指摘など、アルゴ算法堂のブログページのPSGに関する記事にコメントの形でお書きください。

メニューへ戻る
アルゴ算法堂へ戻る