PICを使ってアナログシンセ小物やMIDI小物を製作する試みです。
ソフトウェアシンセを作ればという案もあるのですが、それならば、PCでやればいいはず。あえて、スタンダロンにするには、それなりの理由がないと、ただ単に、PC版のソフトウェアシンセを移植するだけになりそうで、ちょっとね。
少なくとも、14ビットコアのPICでは荷が重いので、小物が丁度いい見切りどころかと思っています。
PIC EG I | PIC12F615 | ADSRのEG |
PIC LFO I (製作中) | PIC12F615 | シンプルなLFO |
PIC NOISE I | PIC12F615 | ノイズ発生器 |
8ピンのPICでADSR付きのコンパクトなEGを製作します。
8ピンだと、電源、グランドを除くと、使えるのは6ピン。残ったピンの内訳は、ゲート入力、PWM出力、それからADSRのボリューム(都合4ピン)。これで丁度使い切リます。
具体的にチップを検討すると、CCP(PWM)がついているPIC12F683は、ピン機能がバッティングしてアナログ入力を4つ取れませんでした。ということで断念していたのですが、PIC12F615を入手したところ、CCPの出力を切り替えられる。これで、アナログ入力を4つ使えることになりました。
MICROCHIPのICD2では、電源を除く6端子を使用した場合、一度プログラムを書いてしまうと、二度と書けなるとのこと。これは、内部発振を指定した状態で、MCLRを使用すると、MCLRに高電圧をかけて、書き込み状態になる前にCPUが自走してしまうためらしい。
MICROCHIPのPickit2では、問題なく、何回も書けているので、ライターはPickit2を使っています。
pin | 信号 | i/o | 用途 | 備考 |
---|---|---|---|---|
7 | GP0 | i | AN0 | ATTACK VOLUME |
6 | GP1 | i | AN1 | DECAY VOLUME |
5 | GP2 | i | AN2 | SUSTAIN VOLIME |
4 | GP3 | i | GP3 | GATE信号入力 |
3 | GP4 | i | AN3 | RELEASE VOLUME |
2 | GP5 | o | CCP | PWM出力 |
1 | VDD | VDD | 電源 | |
8 | VSS | VSS | グランド |
ピンの割り当ては左のとおり。要点は、GP5にCCP出力を移して、ADを4つ使用しているところです。
ソフトウェアは、おおまかには、次のとおり。
内部クロックを8MHzにするので、PWMの精度とサンプリング周期とのバランスからパラメータを選択しなければなりません。
PWMを10ビットとした場合は、7.8125kHz。128μS。プログラムでは256ステップ。
PWMを9ビットとした場合は、15.625kHz。64μS。プログラムでは128ステップ。
PWMを8ビットとした場合は、31.25kHz。32μS。プログラムでは64ステップ。
この辺りから、妥協点を見つけたい。サンプリング周期が高いほうが好ましいし、ビット数は多い方が良いわけです。EGは、それほど速度を要求されないので、7.8kHzあれば、十分かもしれません。結局、割り込み内でのプログラムステップ数を見て、多少の余裕を見て判断することになります。
エンベロープの形状は、当面はリニア。それぞれの時間の調整範囲は、すべて同じにしますが、後述のように、等比級数的に扱いたい。調整範囲の決定は、ちょっと、悩ましいところ。イメージとしては、1mSから30Sぐらいを例に考えてみます。
コンピュータ的に切りの良い数字で、長い方を32.768Sとすると、215でおよそ15オクターブ。これも、コンピュータ的にきり良く16オクターブとすると、目盛りの雰囲気は、16分割で、1m/2m/4m/8m/16m/32m/64m/128m/256m/512m/1.024S/2.048S/4.096S/8.192S/16.384S/32.768S/(65.536S)。128段階でボリュームを読み取ると、その中は、さらに8分割。1秒前後のおいしいところが、もっと、細かく欲しい気もしますが…
このあたりは、作って操作してみて再検討の必要があります。といっても、テーブルの変更と、場合によっては、ボリューム変換辺り1回の除算(乗算)で済みそうだし、幸い、地面でゆっくり変換しても害はなさそう。
暫定的に、時間の調整範囲は、1mSから65.536Sの16オクターブ。
テーブルには、サンプリング時間当たりの増分を、一番短い時間のオクターブ分だけ持ちます。時間が長くなるとオクターブ毎にこの値を右に1ビットシフトすればよい事になります。
最短の値は、サンプリング周期を7.8125kHz(=128μS)とすると、128/1000。テーブルのi番目の要素は、オクターブの分割数をnとすれば、次の式で表されます。
これを、16進数で表せばよい事になります。仮に値が4バイトで表されれば、16オクターブ目では、右に16ビットシフトするので、このときの値の長さとしては、2バイト。この場合でも1/1000以上の精度があるので良しとします。
左の表が、サンプリング周波数を7.8125kHzにして、オクターブを16分割した場合のデルタ値の表です。
細かいことを言えば、最大値を0xffffffffで計算するとか、いろいろありますが、折り返し(EGの状態遷移)で、オーバーランを無視する予定なので、実際の値は、結構ラフになってしまうから、このぐらいで良いかと考えています。
アタック、ディケイ、リリースは、この表に従って値が決めています。サスティンは、あえて、リニアにボリュームを反映するようにしました。
とりあえず、ソフトウェアを書いて、動作させました。テストのために、CCP出力には、簡単なローパスフィルターをつけ、AD端子には、10kΩBの可変抵抗器を接続します。ゲートは手で電圧を与えてシンクロで観測したところ、うまく動作しているようです。
割り込み周期は、128μS/256インストラクション。最も時間のかかるディケイ期間での割り込み実行をシミュレータで調べると、43μS/86インストラクション。割り込みの負荷は1/3ぐらいです。
パラメータの変更(可変抵抗器の操作)でエンベロープの途中でもリアルタイムに反応するのは、気持ちがいいです。
ゲートの極性を切り替えたいのですが、スイッチをつけるピンが余っていません。電源投入時の状態で判定することにしました。電源投入時の、ゲートの値をゲートオフ状態とします。ゲート未接続に備えて、ハードの方で使用する極性に応じてプルアップなり、プルダウンしておくことにします。
リトリガーの仕様、つまり、エンベロープがリリースしている最中に再びゲートがオンになった場合の振る舞いは、二種類考えられます。
連続したフレーズを引く場合など、きちんとアタックが出て便利なのですが、反面、リリースが突然切れて不自然になる欠点もあります。デジタルシンセなどでは、これを避けるために、このケースで、ゲートオンしたときから、やや急なリリースをかけ、0に到達してから、遅れてアタックがかかる機能を持つものもあります。
上記の場合と逆で、不自然なリリースを避けられますが、速いフレーズなどを弾くとアタックがはっきり出ない欠点があります。
今回は、とりあえず、(2)の方法をとり、アタックの開始時に出力を0に戻さない仕様としました。
PIC12F615自体は、コアのようなもの。実際にシンセに接続するには、ゲート条件や、出力CVを接続するシンセに合わせなければなりません。
とりあえず、手持ちのKOROG MS50や、RORAND SYSTEM 100Mに混在して使えるようにしたいと思っています。決めなくてはいけない一番大きな点は、電源電圧。これは、多分±15Vで良さそう。
EGの出力電圧を決める参考値。SYSTEM 100Mでは、±10V。KORG MS-50では、おそらく、±5V程度。
GATEは、SYSTEM 100Mが3.0Vが閾値で、MS-50は2.5Vが閾値です(5-0V)。
GATEに通常のCVを入れても動作保証をしたいのですが、ちょっと、荷が重そう。今回は、GATEへのCV入力は考えないことにしました。
結局、回路は二種類作ることにしました。
現在の回路で、入力抵抗が47kと大きくなっているのは、ローランドのGateの決まりに引っ張られたため。また、出力抵抗の1kは、100Ωぐらいまで、落としてもいいかも。EGだから、この辺は厳密でなくても良さそうですが。
今回作ったのは、MS50対応版(ゲート負極性、出力 NORMAL 0-5V/INV -5-0)を作りました。基板は例によって秋月のC基板。パネルは百円ショップで売っていた箱の蓋です。MS50は標準プラグなので、これを三つ(GATE入力、ENVOUT/INV ENVOUT)パネルにつけました。これが、結構高さがあって、基盤とパネルの距離は5pほど必要になってしまいました。
上の画像が全体の様子。レタリングなどしていないもの。右側が実験用の三出力(5/+15/-15)のスイッチング電源。
中の画像は、基板。左のコネクタがGATE入力。上のコネクタがボリューム接続。右には二つのコネクタがあり、上側が電源。下側がエンベロープ出力(ノーマルとインバート)。ICは左がPIC12F615で、右がTL072です。
下の画像が、パネルの裏側。3oのネジ基板を下側に止めるようになっています。実際に基板を止めているのは3本で、もう一本は足の役割。標準プラグが長いので結局、M3の50oのネジを使いました。
実際にKORGのMS50に繋いで遊び、使用感を試しました。
心配していたのは、ヴォリュームレンジの設定の使いやすさです。ADRは1mSから65Sと幅広いのですが、丁度、おいしいところがつまみの真ん中辺りに来ていて、なかなか、使いやすい印象です。
時間の長い方は、粗さが目立つのですが、それでも、効果音などには十分な感じ。逆に短い方の数ミリの部分は、耳で聞いた限りではそれほど差異がありませんでした。短い時間は切り詰めても良かったかもしれません。
おいしい中央付近では分解能を気にしていたのですが、MS50のEGよりも使いやすい感じ。結構、デリケートに調整できます。
SUSTAINも、あえてリニアにしたので、ちょっと気にしていたのですが、特に違和感はありませんでした。
アナログのEGと同様に、発音中につまみをグリグリしても、追従します。SUSTAINを動作中に変更すれば、持続音のレベルが変わるし、発音の終わりに無理矢理、リリースを変更する荒業も可能です。
エンベロープのカーブがリニアなので、アナログEGの減衰カーブと比べてどうか、比較したのですが、リニアの方が使いやすい印象でした。そもそも、シンセ側のコントロール入力が逆LOGがかかっているので、リニアの方が使いやすいのかも。
プログラムのhexと、回路図を掲載します。回路図は、MS50接続用と100M接続用の混在になっており、回路の異なるところは、色わけしてあります。
プログラムのHEXです | eg615_00.hex | 最初のもの |
回路図 | 615eg00.pdf | MS50版/100M版共通に書かれています |
同一回路での、ソフトウェア変更のバリエーションをいくつか考えています。
EGと同様に、PIC12F615を用いて、LFOを作ろうと思います。ハードは殆ど変わらないはずですが、実際は出力のグランドレベルををPWMの中心にするなど、細かい違いが生じそうです。
出力は、PWMで行うことにして、コントロールはボリューム(AD)で。コントロールとしては、周波数と、波形選択。当面はこれだけを決めておきます。ハードを最小化するので、クロックは内部発振器。
未使用の分は、ソフトウェアの容量を見て機能を決めようと思います。現状では、モニターLED(点滅する)とか、外部入力のゲートか、シンクを考えています。もし、ゲートでLFOに立ち上がりエンベロープをかけるならば、立ち上がり時間指定、ディレイ時間指定があると便利そうです。
当面実装する波形の種類は、正弦波、鋸波(立ち上がりと立下り)、矩形波(正相のみと、正負相)、サンプルアンドホールド風階段波形の6種類。このうち、正弦波だけはテーブルで持ち、他は演算で作ることにします。
周波数の範囲は、0.1Hzから20Hzぐらいまで。ボリュームは指数関数的に、つまり、対数目盛りで動作するようにする予定。
内部発振器は8MHzなので、それなりに遅いのが、ちょっと、気になる点です。また、プログラム容量が1kWなので、どこまで、実現できるかも、楽しみの一つ。
内部処理でネックになるのは、周波数用のボリューム値の決め方。ボリュームの対応は指数関数的にするのが狙い。面倒な指数計算を省いてヴォリュームの値から表を引いて値を決めます。左の表がこのとき使用する表です。
調整の範囲は、0.1Hzから20Hzぐらい。比率では、200倍。コンピュータ的に切りの良い数字に直すと256。28ですから、8オクターブとなります。ボリュームを8ビット精度で読み取れば、256段階。オクターブ当たり、32段階となります。これに対応して位相増分を表に書き込んでおきます。表は周波数の最も高い1オクターブ分だけ持ち、オクターブ上がる毎に右シフトして(1/2にして)実際の位相増分を計算します。
テーブルは一番上のオクターブなので、インデックスの0(最初の行)は12.8Hz。サンプリング周波数は7.8125kHz(=128μS)。このとき、位相360度を1として、1回のサンプル当たりの位相増分は、サンプリング間隔を出力の周期で割ったもの。すなわち (1/7812.5)/(1/12.8)→12.8/7812.5となります。
表のほかのインデックスは、2n/32倍したもの。これから、32個のテーブルの値を求めることができます。
値が小さいので、テーブル精度を確保するために、表の16進表記は、5バイト精度で書いてあります。先頭の1倍とは必ず0になっているので、実際のプログラムでは、00を省略して以下の4バイトを格納します。
表を引用する場合には、ADコンバータの上位8ビットを使います。そのうち、下の5ビットでテーブルの要素を指定、上位の3ビットがシフト回数を決めます。シフト回数は、7-(ADvalue>>5)で計算できます。
LFOの出力の中心は、0x80。ところが、こうすると、正側の幅は0x81-0xffの127、負側の幅は、0x00-0x7fの128。正負が非対称になってしまうために、負側の0x00は使用しない方がよい事になります。
波形表で作るサイン波や、矩形波関係は良いのですが、鋸波や、三角波(実装する?)のばあいには、演算がちょっと重くなるので、チョンボして、0x00まで出力してしまうつもりです。
波形の実装方法をまとめておきます。
矩形波1:位相の前半では0x00、位相の後半では0xffを出力。位相の判断は、積算位相値のmsbが1ならば後半とします。
矩形波2:位相の前半では0x80、位相の後半では0xffを出力。位相の判断は、積算位相値のmsbが1ならば後半とします。
鋸波1:段々大きくなって、最後に0に戻る波形。位相積算値をそのまま使います。
鋸波2:最初に最大値になって、段々小さくなっていく波形。位相積算値の1の補数を使います。
三角波:位相積算値を2倍にして、1を超えない場合はそのまま、超えた場合は折り返します。
正弦波:位相積算値で三角関数のテーブルを引きます。テーブルは1周期をそのまま持つ予定。これは、簡単に、任意の別の波形も試せるようにするため。
ランダム:周期毎に乱数で異なる値を出力。周期の先頭で値を更新するために、位相積算のキャリーを保存しておく必要があります。出力のラグ(一種の積分)機能があると面白いのですが、この点は、現在は保留します。
最近、任天堂のDS上で動く、KORG DS-10というのを手に入れて遊んでいるのだけれど、これのLFOには、正弦波が付いていなくて、三角波で代用。使ってみても、三角波で十分な気がします。プログラムサイズを考えると、本当に省略しても良さそうな気がする。出力も逆相は、外側のオペアンプで対応した方がいいのかも(ちょっと、回路が重いかな)。
pin | 信号 | i/o | 用途 | 備考 |
---|---|---|---|---|
7 | GP0 | i | AN0 | 波形選択 |
6 | GP1 | i | AN1 | 周波数指定 |
5 | GP2 | i | GP2 | LED (周期の後半で+5) |
4 | GP3 | i | GP3 | 未使用(プルアップ) |
3 | GP4 | o | GP4 | LED(周期の前半で+5) |
2 | GP5 | o | CCP | PWM出力 |
1 | VDD | VDD | 電源 | |
8 | VSS | VSS | グランド |
プログラムを少し書いてみました。周波数変換のテーブルが128ワード。サイン波のテーブルが512ワード。これは、LFOは当然ですが周波数が遅いので、サンプリングの継ぎ目が気になるため、全波形512段階にして半波長を持ったため。ここは1/4波長にすれば、もっと表を小さく出来るのですが…
この段階で、残りが数ステップ。
ここにきて、シンプルなLFOに方針転換。複雑な機能は、プログラムサイズがもう少しあるCPUに譲ることにします。
LED端子を二つ用意しています。どちらか一方をLED表示に用い、残った方をゲート出力端子に使えます。
ゲートが負極性のMS50用の場合には、GP4にLEDを接続し、電流制限抵抗を通してグランドに落とす形。こうすると、残ったGP2は負極性のゲート出力(0V〜5V)として使えます。
正極性のゲートを得る場合には、GP2にLEDを接続し、電流制限抵抗を通して+5Vに吊る形。残ったGP4からは正極性(+5V〜0V)のゲート信号を得ることが出来ます。
ゲート信号は、シーケンスやサンプル&ホールドのクロックとして利用したり、ランダム波の場合のゲート信号として使用できます。
波形の切り替えは、波形選択のボリュームの小さい方から、次の順に切り替わります。
出力電圧が多少ずれているので、ピッチなど厳密に合わせたい場合は、このモードにしてチューニングしてください。
周期の前半が上り、後半が下りです。
周期の前半が最小値、周期の後半が最大値です。
周期の前半が0、後半が最大値です。
出力回路は、0Vを中心にしてオペアンプで±に振る形。電圧範囲は接続対象のシンセによって異なるのですが、KORG MS−50(±2.5V)に対応して検討するつもりです。また、逆相出力があるとエフェクタなどで便利なので、これも付けたいと思っています。
ROLAND SYSTEM 100Mでは、±5Vなのですが、MS50用の±2.5Vでもそのまま、使えそうなので、MS50用で良しとするつもりです。
PICのPWM出力は 0.0V 〜 5.0V。これを 2.5V 〜 -2.5Vに変換する回路を検討します。
右図の減算回路を使って考えてみました。
式の中で仮に置いたV5は、5Vです。
オペアンプの−端子の電圧は、
(R2/(R1+R2))*V5+(R1/(R1+R2))*VOUT
オペアンプの+端子の電圧は、
(R4/(R3+R4))*VIN
この二つの電圧は等しいとして、
(R2/(R1+R2))*V5+(R1/(R1+R2))*VOUT = (R4/(R3+R4))*VIN
(R1/(R1+R2))*VOUT = (R4/(R3+R4))*VIN - (R2/(R1+R2))*V5
VOUT = (((R1+R2)*R4) / ((R3+R4)*R1) ) * VIN - ( R2/R1 ) * V5
レベルシフト量はV5の項。V5は5Vですが、必要なレベルシフト量は-2.5V。
このことから、R2:R1=1:2。つまり、R1=2*R2となります。
VINの項の係数が増幅率。単純にレベルシフトするだけですから、増幅率は1で、この係数は1になります。従って
(R1+R2)*R4 = (R3+R4)*R1
これに、R1=2*R2を代入して、
3*R2*R4=2*R2*R3 + 2*R2*R4
R2*R4 = 2*R2*R3
R4 = 2*R3
結局、R4:R3=2:1となります。
回路の動作を決定するのは、R1:R2=2:1、R3:R4=1:2という比の値です。R2=10kΩとし、R3は入力インピーダンスを大きくするために1Mオームとすると次のようになります。
R1=20k
R2=10k
R3=1M
R4=2M
レベルシフトの後ろにインバータを入れて、反転出力も設けました。これでOPAMPが二つ、TL072を一つだけで何とか回路を構成できたようです。
LPFは、2.2kΩと0.1μFの4段構成。0.1μFにしたのは、パスコンに使っている積層セラコンを流用したかったからです。本来ならフィルムコンデンサを使うのでしょうが、これでも動作するようです。段数は実際は3段でも問題ありません。
回路の実装では、以下の点に注意して、抵抗を設置してください。
回路がうまく動くためにはオペアンプ周りの抵抗の比が正確で無ければなりません。後述の回路図中の、R1:R2、R3:R4、R5:R6のそれぞれの比が保たれるようにデジタルテスタで実測して抵抗を選んでください。
実際には、それぞれ1:2の比なので、2の方は抵抗を直列に二本繋ぐものとして、デジタルテスタで測って同じ値の三本の抵抗を使ってみました。実測したところでは、ずれがマイナス数ミリボルト程度で、ゲインはほぼ1が保たれました。
また、回路図中には、各電源用の電解コンデンサ(10μF〜47μF程度)、PICやオペアンプに接続する積層セラミックコンデンサ(0.1μF)は省いてあります。適宜設置して下さい。
ゲート(クロック)出力を設ける場合、回路図上では負極性のゲート出力(MS50)対応になっています。正極性にする場合には、ゲート出力をGP4から取り、LEDをGP2として電流制限抵抗を通して+5Vに吊る形にします。通常の使用ではゲート出力は不要と思われます。
波形選択に可変抵抗を用いています。ヒステリシスなどを設けるプログラム上の余裕が無かったので、波形の切り替わりポイントが不安定です。8接点えスイッチを用いた方が使い勝手が良さそうです。
ブレッドボードに組んだ段階で、実際にMS50に繋いで使ってみました。レンジその他は想定どおりで、まぁまぁの使いやすさです。波形の割り当ての都合上、インバート端子の方が使いやすかったです(電気的にもバッファされている)。
波形のキレは、LPFの影響でイマイチ。矩形波の変わり目にポルタメントがかかったりしています。カチッとした矩形波が欲しい場合には、後述のゲート(クロック)を流用すればいいかも。
この段階で実は細かい修正を入れました。
プログラムの残りが数ワードだったので、無理矢理に押し込んだ感じですが、どうにか、入りました。資料の方もこれに伴い変更してあります。
あとは、基板組みとパネル実装が残っています。
プログラムのhexと、回路図を掲載します。
回路図は、MS50接続用で、出力が+2.5V〜-2.5Vとなっていますが、SYSTEM100Mなどでも、問題なく使えると思います(ゲート出力をしない場合)。
プログラムのHEXです | lfo615_00.hex | 09/04/19 更新 |
回路図 | 615lfo00.pdf | 09/04/19 更新 |
PICを使ったホワイトノイズ生成器。
これも、chuckさんや、RJBさんが先行して製作されています。シフトレジスタとXOR演算を使った、とても、コンパクトなプログラムです。
ホワイトノイズの生成は、結局、一様乱数の生成。一様乱数は、いつもなら、合同法などで済ませてしまいます。 耳で聞くので、それほど、乱数の質にはこだわらなくても良いはず。そのかわり、周期性が耳に付くことになりそうです。なにしろ、ずーっと、乱数を生成しているわけですから、天文学的な周期性が要求されます。
ということで、シフト+XORで簡単に実用的なノイズが得られているようなので、この方法で製作を進めます。
CPUは、オリジナルでは、PIC12C509Aだったのですが、これは、お値段が高いし、一度の書き込みしかできません。そこで、PIC10F200-I/Pを使います。これは、秋月で、@70。米粒PICのDIP版です。GPIOしかないけれど、プログラム256W、RAM16B。この用途にはぴったりです。
米粒版PIC10F200に書き込めば、実装上とっても小さなものも製作できそう―― 米粒ノイズ素子です。
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
プログラムも、回路も、ごく単純なのですが、PIC10F200のピン配置が他の8ピンPICと異なるので、ちょっと注意が必要です。米粒版は6ピンなので、これも、ピン配置が異なります。
ピン配置を左の表にまとめました。下側の表は米粒版の場合の接続です。書き込み用のツール接続にははpickit2/ICD2のピン番号を書いておきました。
通常の8ピンPICとピン配置が違うために、通常の8ピンのアダプタが使えません。専用に作るか、ブレッドボードで配線するなどの作業が必要です。
PIC10F200は秋月のライター(V6.70.11以降)で書き込みが出来るようになりました(ただし、配線が特殊なのでソケットに挿してそのまま書けるわけではありません)。
プログラムはChuckさんのものを元にしています。ここで使われているノイズ発生の方法は、24ビットのシフトレジスタを用意し、特定のビット(4箇所)を取り出してそれらのXORをとるという方法。シフトレジスタの右あふれを出力ビットに、シフトレジスタの左からは、XORの演算結果を放り込む。
いくつかのビットのXORを取るということは、結局、それらのパリティを取ることと同じです(理屈は面倒ですね)。ここでは、4つのビットだけを計算しています。
この部分はchuckさんのオリジナルでは、XOR演算を用いて、次のようになっています。
movlw H'00' btfsc SREGH,TAP_A movlw H'01' movwf TAPREG movlw H'00' btfsc SREGH,TAP_B movlw H'01' xorwf TAPREG,F movlw H'00' btfsc SREGL,TAP_C movlw H'01' xorwf TAPREG,F movlw H'00' btfsc SREGL,TAP_D movlw H'01' xorwf TAPREG,F
これを、そのまま頂くのも、何なので、少しは工夫をしようということで、パリティを計算する形に改めました。論理的には全く同じものなのですが…
clrf TAPREG btfsc SREGH,TAP_A incf TAPREG,f btfsc SREGH,TAP_B incf TAPREG,f btfsc SREGL,TAP_C incf TAPREG,f btfsc SREGL,TAP_D incf TAPREG,f
しかし、プログラムが短すぎて、もったいないですね。一緒に組み込んで面白いものが思いつけると、いいのだけれど。
今回は、とりあえず、回路の実装は無し。
学研シンセ、SX-150の方で、SX-150用の実装をします。
プログラム(HEX) | 200noise.hex | 最初のもの |
プログラム(ASM) | 200noise.asm | PIC10F200版。下のchuckさんのオリジナルをご覧下さい。 |
オリジナルのプログラム | Noise Generators | chuckさんが書かれたオリジナルのプログラムのあるページ。 |
ボリューム(ヴァリアブル・レジスターというのかな)で、LFOにおける周波数や、EGでの時間を調節するとき、リニアでは使いにくく、ボリュームを廻すと指数関数的に変化する方が望ましそうです。ボリュームについている目盛りが対数的ということになります。
AD変換器で求めたボリュームの値を元にベキ関数で計算すれば言いだけの話ですが、演算量が多いと、PICではちょっと実現できません。
細かいことを省いて考えると、最終的には 2xを使用すればいいことになります。これだと、普通の音程の周波数計算と同じで、オクターブ毎に2倍になるだけ。計算が大変なのはオクターブの中だけ。xの範囲で言うと0〜1の間だけ、それなりに計算しなければなりません。音程計算の場合には、半音は1/12だったので、半音単位ならば、12個のテーブルを用意すれば十分でした。
では、EGやLFOでは、どのくらいのテーブルが必要になるか、見積もってみましょう。
LFOで、カバーする周波数の範囲を0.1Hz〜20Hzぐらいとすると、およそ、200倍の違い。コンピュータ的に切りよく考えると、256倍で、オクターブで言えば8オクターブ。仮にボリュームを256段階(8ビット)で読み取るとすれば、オクターブ当たり、32段階。指でボリュームを廻しての精度だと256段階は十分な精度ではないでしょうか。
そこで、テーブルは、奢って32エントリー。もし、ボリューム精度128段階(7ビット)ならば、16エントリーで良いことになります。必要ならば、補間を用いてテーブルをさらに1/2にもできそうです。
近似式を用いて計算すれば、テーブルは省けますが、プログラム容量も増えるし、演算時間もかかる。とりわけ乗算のないCPUでは、つらいし、除算を用いるならさらに面倒です。14ビットコアのPICでは、テーブルでの実装の方が無難な気がします。
内部クロック時にMCLRを使うとICD2で再書き込みができないらしいPIC12F615ですが、やはり気になります。
12F615にこだわる理由は、一つだけ。CCPの出力ピンを変更できそうなこと。これができると、ADを4つ使えるので、EGでの標準的なADSRをボリュームに割り当てできます。
Pickit2では電源の制御をしているので大丈夫らしいとのこと。とりあえず、Pickit2で試してみます。
実験すべきは2点。
Pickit2では、問題なく、書けました。
CCP出力割り当ての変更はできたようです。
このとき、ADの四つのポートを使えるかと懸念していたのですが、EG用のプログラムを書いて試したところ、問題なく動作しました。