2023年2月11日土曜日

HD43517の使い方

 音源ICの使い方一覧へ

HD43517の使い方

注意:このサイトの内容を鵜呑みにし、事故や損失を招いた場合でも当方は一切の責任は負いかねます。自己責任でお願いします。





HD43517は主にCASIOの電子オルガンのCasiotoneシリーズなどに用いられていた波形合成音源LSIです。(LSIですが便宜上ICと呼びます。)

このICは日立のCMOS LSI HD43000シリーズの一つだと思われます。
Casiotone以外に使用されている機器は、今のところ見つからないので、CASIOの特注品である可能性が高いです。そのため、データシートの入手は現時点でできません。

入手には、該当ICが乗ったCasiotoneを分解するのが確実です。

・Casiotone 601
・Casiotone 602
・Casiotone 701
・Casiotone 1000P
・Casiotone MT-70

上記あたりの機種は確実にHD43517を使用しているようです。これ以外のCasiotoneシリーズにも使われているかもしれません。


HD43517単体では音を出すことはできず、AM6012などのDACまたはESK-S10L-0025CなどのR2RラダーDACとアナログシフタ回路が必要です。



***********************************************************
HD43517データシートの入手ができないため、この記事ではICの解析したものをまとめています。
そのため、未知の仕様や本来の使い方とは異なる使い方をしている場合があります。

この記事の内容をもとに実験してみる場合、ICの破損などをはじめとする事故や損害はすべて自己責任です。
***********************************************************

回路解析では、各種Casiotoneのサービスマニュアルをもとに行い、レジスタ解析では「Casiotone 602」の実機のレジスタ書き込みの状態の変化の情報を元に行いました。



・HD43517の特徴

解析により判明した特徴をまとめます。
・5つの正弦波をADSR+SV(スタートレベル)+AL(アタックレベル)+DR(ディレイ)の7種類のエンベロープパラメータで合成(SV、DR、ALは倍音ごとに設定可能)
・5つの正弦波の周波数比は4種類から選択可能(整数倍、非整数倍)
・同時発音数 : 4
・42ピンDIP
・ハードウェアエンベロープ
・最大4つのHD43517を外部チップセレクト回路なしにアドレス空間に配置可能(アドレスbit6~bit3がチップセレクトbitとして割り当て)
・複数のHD43517を1つのDAC回路でまとめることが可能(仕様が複雑そうなので解析を省略)


HD43517は汎用音源ICとしてよりは電子オルガン用として開発されたようです。
回路をできるだけ簡略化できるように工夫がみられます。


・ピン配置

Casiotone-1000P、Casiotone-701のサービスマニュアルを参考にピンを予想しました。
(Casiotone-1000Pサービスマニュアル内の回路図のHD43517のVssとVccは、逆になっているので注意)


ピン番号 名称 I/O 機能
1 DB0 I データピンです。
2 DB1 I データピンです。
3 DB2 I データピンです。
4 DB3 I データピンです。
5 DB4 I データピンです。
6 DB5 I データピンです。
7 DB6 I データピンです。
8 DB7 I データピンです。
9 DAD I/O HD43517を複数接続する時のシリアルデータ入出力端子です。

複数のHD43517のDAD端子同士を接続することで、1つのDAC回路で同時発音数を増やすことができるようです。

使い方、データフォーマットは不明。

未使用時には開放またはプルアップ抵抗を接続します。

MSO端子=H、I-4端子=Hのときのみ、この端子は入力となります。

10 SYNC I/O HD43517を複数接続する時の同期入出力端子です。

複数のHD43517のSYNC端子同士を接続することで、1つのDAC回路で同時発音数を増やすことができるようです。

使い方、タイミングは不明。

未使用時には開放またはプルアップ抵抗を接続します。

MSO端子=H、I-4端子=Hのときのみ、この端子は出力となります。

11 EVD I/O HD43517を複数接続する時のエンベロープデータ入出力端子です。

複数のHD43517のDAD端子同士を接続することで、1つのDAC回路で同時発音数を増やすことができるようです。

使い方、データフォーマットは不明。

未使用時には開放またはプルアップ抵抗を接続します。

MSO端子=H、I-4端子=Hのときのみ、この端子は入力となります。

12 CS I チップセレクト端子です。

この端子がHの期間、「~WE」端子がLの時、
書き込みができます。

13 ~WE I ライト端子です。

この端子がLでかつ、「CS」がHの期間、
DB0~DB7端子の状態が内部レジスタへラッチされます。

14 ~A/D I アドレス / データセレクト端子です。

この端子がLの場合、DB0~DB7はアドレス入力端子となります。

Hの場合、DB0~DB7はデータ入力端子となります。

15 INT1? O 割り込み要求端子です。
詳細不明

基本的に開放しておきます。

サンプリング周波数の1/4の周波数のDuty比50%の矩形パルスを出力します。

入力クロック周波数が4MHzのとき、出力周波数は6.25kHzとなり、S/H端子の出力の立ち下がりから10μs後にこの端子の出力が立ち上がるような位相となります。

16 INT2? O 割り込み要求端子です。
詳細不明

基本的に開放しておきます。

サンプリング周波数の1/64(INT1の1/16)の周波数のDuty比15/16の矩形パルスを出力します。

この端子の出力の立ち上がりとINT1端子の立ち上がりのタイミングが同一となります。

17 I-4 I 詳細不明の入力端子です。

マスター・スレーブ選択入力端子2の可能性が高いです。

Casiotone-602の場合で3つの音源ICのうち、
HD43517-40の I-4 がGNDへ接続、MSOがOPENとなっています。
残り二つのHD43517-33、HD43517-21のI-4 がOPEN、MSOは切り替えできるようになっています。

この端子がHかつ「MSO」端子がHの場合、マスターモードとなります。

この端子はプルアップされています。
18 MSO I マスター・スレーブ選択入力端子1です。

この端子がHかつ「MSO」端子がHの場合、マスターモードとなります。

この端子はプルアップされています。
19 ~RES I リセット端子です。
この端子がLの期間、内部レジスタを初期化します。

20 CLOCK I クロック入力端子です。

標準で4.00MHzを入力します。

21 Vcc - 電源端子です。
+5Vの電源を用意してください。

※Casiotone-1000Pのサービスマニュアル内の回路図の当端子名は「Vss」となっていますが、5V入力されているようなのでVccとしています。
22 DA0 O パラレルサウンドデータ出力端子です。
一応音声データは出力していますが、DACへ接続してない場合が多いです。
23 DA1 O パラレルサウンドデータ出力端子です。
24 DA2 O パラレルサウンドデータ出力端子です。
25 DA3 O パラレルサウンドデータ出力端子です。
26 DA4 O パラレルサウンドデータ出力端子です。
27 DA5 O パラレルサウンドデータ出力端子です。
28 DA6 O パラレルサウンドデータ出力端子です。
29 DA7 O パラレルサウンドデータ出力端子です。
30 DA8 O パラレルサウンドデータ出力端子です。
31 DA9 O パラレルサウンドデータ出力端子です。
32 DA10 O パラレルサウンドデータ出力端子です。
33 DA11 O パラレルサウンドデータ出力端子です。
34 DA12 O パラレルサウンドデータ出力端子です。
35 DAS0 O D/Aシフト出力端子です。
36 DAS1 O D/Aシフト出力端子です。
37 DAS2 O D/Aシフト出力端子です。
38 S/H I/O サンプル・ホールド出力端子です。

スレーブモードの場合はグランドへ接続しているようです。

入力クロックの周波数が4MHzの時、25kHzの矩形波をDuty比50%で出力します。

この端子がHのときDA0~DA12およびDAS0~DAS2の値をサンプルし、
この端子がLのときホールドするような外部回路を接続します。

この端子がHになる約6.6μs前にDA0~DA12およびDAS0~DAS2の値が変化します。(CLK=4MHz時)
39 O-14 詳細不明な出力端子です。
基本的に開放しておきます。

40 Vss2 - グランド端子です。

※Casiotone-1000Pのサービスマニュアル内の回路図の当端子名は「Vcc2」となっていますが、5V入力されているようなのでVss2としています。
41 Vss1 - グランド端子です。

※Casiotone-1000Pのサービスマニュアル内の回路図の当端子名は「Vcc1」となっていますが、5V入力されているようなのでVss1としています。
42 O-15 詳細不明な出力端子です。
基本的に開放しておきます。

音を出力している時、パルス的にHを出力。(要検証)
出力チャンネル数によって波形が変化します。

周波数はサンプリング周波数と同一となります。
1周期を1/4に分け、該当チャンネルが出力されているときにパルスがHとなります。

すべてのチャンネルが出力されているときは常にHとなります。



・HD43517のレジスタ(書き込み専用)

HD43517は数十バイトのレジスタを持っていますが、その正確な数はわかりません。
というのも、このICは複数のHD43517を連結して使用することを想定して設計されており、マスター・スレーブ端子によってアドレス空間内のどこに配置するかを選べるようになっているためです。

最大で4つのHD43517をチップセレクト端子の制御なしにレジスタの書き込みができます。
74139などのデコーダとは違い、同時に複数のHD43517に対してチップセレクト(この場合チップイネーブルというのが正しいでしょうか?)が行えるため、複数のHD43517の発音タイミングを合わせたり、エンベロープなどの共通する値を書き込むときにいちいちICごとに書き込まなくて済むようになっているようです。

HD43517が扱えるアドレス空間$00~$7Fのうち、bit6~bit3の4bitはこのチップイネーブルビットとなります。

残るアドレス3bit分、つまりデータ量で8バイト分が1つのHD43517が持てるレジスタとなるはずです。
しかし、これではPSG音源以上のエンベロープADSR設定や音色設定などに必要なレジスタ容量が明らかにが足りません。

ですのでHD43517は、データ領域の一部をレジスタアドレスとして使用しています。



以下の表はアドレスbit0~bit2の3bit分を示します。
実際のレジスタ名やビット名はわからないので、こちらが勝手に命名しています。
詳しく解析していないため、未知のレジスタや機能、間違いが多数あると思います。

アドレス
(下位3bit分)
レジスタ名 機能
$0 CWM_KEY_OFF 倍音比モードとキーオフレジスタです。
このレジスタに書き込みを行うと対象の内部レジスタの値が更新されます。

データのbit7=Lのとき倍音比選択レジスタへの書き込み、
bit7=Hのときキーオフレジスタへの書き込みとなります。

・データのbit7=Lのとき
データのbit7=Lにした場合、倍音比モードレジスタに対して値を書き込みます。

bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
L不明_3不明_2不明_1不明_0MUTECWM_1CWM_0


MUTE:  (bit2)
ミュート設定ビットです。
このビットをHにするとミュートとなります。


CWM:  (bit1, bit0)
倍音比モード設定ビットです。

CWM_1 CWM_0 倍音モード
L L 整数倍モード①

基音、2倍、3倍、4倍、5倍

L H 非整数倍モード①

基音、1.666倍、2倍、2.666倍、3.333倍

H L 非整数倍モード②

基音、1.4倍、1.8倍、2.2倍、2.6倍

H H 整数倍モード②

1倍+8倍+18倍+28倍、
2倍+10倍+20倍+30倍、
3倍+12倍+22倍+32倍、
4倍+14倍+24倍+34倍、
5倍+15倍+25倍+35倍


HD43517は、整数倍の倍音のほかに2種類の非整数倍の倍音比の音や最大25種の整数倍合成音も出力できます。
これによりオルガン系の音色以外にもベル系の音色の再現をしやすくなっています。


・データのbit7=Hのとき
データのbit7=Hにした場合、キーオフレジスタに対して値を書き込みます。

このレジスタへ書き込みを行うと、指定チャンネルがキーオフします。
bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
H不明_4不明_3不明_2不明_1不明_0KOFF_CH_1KOFF_CH_0


KOFF_CH:  (bit1, bit0)
キーオフ対象チャンネルビットです。
KOFF_CH = 0 ~ 3  (チャンネル1~4)

対象のチャンネルがキーオンされていた場合、キーオフ動作へ移行します。

後述するリリース有効フラグが有効の場合、エンベロープ波形はリリースとなります。
リリースが無効の場合は、音量が0となります。

リリースが有効の状態でキーオフを行いリリースへ移行した後に、さらにこのレジスタに書き込んでキーオフを行うと強制的に発音が停止します。


$1 KEY_ON_CWSW キーオンレジスタとチャンネルごとの各倍音ON/OFF設定レジスタです。
このレジスタに書き込みを行うと対象の内部レジスタの値が更新されます。

データのbit7=Lのときキーオンレジスタへの書き込み、
bit7=Hのときチャンネルごとの各倍音ON/OFF設定レジスタへの書き込みとなります。


・データのbit7=Lのとき
データのbit7=Lにした場合、キーオンレジスタに対して値を書き込みます。

このレジスタへ書き込みを行うと、指定したエンベロープ・リリース条件をもとに指定チャンネルがキーオンします。
bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
L不明_3不明_2不明_1R_ENR_MDKON_CH_1KON_CH_0

不明_1:  (bit4)
このビットをHにするとキーオフに関係なく爆音が出ます。
常にLをセットしておきます。


R_EN:  (bit3)
エンベロープ・リリース有効無効フラグビットです。
このビットをHにするとキーオフ後のリリースが有効となります。


R_MD:  (bit2)
エンベロープ・リリースモード設定ビットです。
このビットをHにすると、
[「倍音2」のディレイ設定値が0以外]かつ
[該当倍音のディレイ設定値が0以外]かつ
[リース有効無効フラグビットがH]
の条件に合う倍音のみリリースが有効となります。
まだ仕様がよくわかりません。

基本的にLにしておくのが無難です。


KON_CH:  (bit1, bit0)
キーオン対象チャンネルビットです。
KON_CH = 0 ~ 3  (チャンネル1~4)

対象のチャンネルがキーオフされていた場合、キーオン動作へ移行します。



・データのbit7=Hのとき
データのbit7=Hにした場合、各倍音ON/OFF設定レジスタに対して値を書き込みます。

このレジスタへ書き込みを行うと、指定チャンネルの各倍音ON/OFF設定値が反映されます。
bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
HOT5OT4OT3OT2OT1OSW_CH_1OSW_CH_0

OT5 ~ OT1:  (bit6 ~ bit2)
このビットをHにすると指定倍音の出力が有効になります。
OT5(「倍音5」) ~ OT1(「基音」) = 0 or 1

OSW_CH:  (bit1, bit0)
各倍音ON/OFF設定対象チャンネルビットです。
OSW_CH = 0 ~ 3  (チャンネル1~4)


$2 STADSR_AL エンベロープ、倍音ごとの音量関連レジスタ群です。
このレジスタに書き込みを行うと対象の内部レジスタの値が更新されます。(キーオン中に変更すると、反映されないパラメータもあるので必ずすべてのチャンネルをキーオフしてからこのレジスタに関する値を変更してください。)

データの上位4bit(bit7~bit4)で対象レジスタを決定します。

bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
機能
L L L L SV_W1_3 SV_W1_2 SV_W1_1 SV_W1_0 基音_エンベロープ・スタートレベル
L L L H SV_W2_3 SV_W2_2 SV_W2_1 SV_W2_0 倍音2_エンベロープ・スタートレベル
L L H L SV_W3_3 SV_W3_2 SV_W3_1 SV_W3_0 倍音3_エンベロープ・スタートレベル
L L H H SV_W4_3 SV_W4_2 SV_W4_1 SV_W4_0 倍音4_エンベロープ・スタートレベル
L H L L SV_W5_3 SV_W5_2 SV_W5_1 SV_W5_0 倍音5_エンベロープ・スタートレベル
L H L H AR_3 AR_2 AR_1 AR_0 エンベロープ・アタックレート
L H H L DR_3 DR_2 DR_1 DR_0 エンベロープ・ディケイレート
L H H H SL_3 SL_2 SL_1 SL_0 エンベロープ・サスティンレベル
H L L L AL_W1_3 AL_W1_2 AL_W1_1 AL_W1_0 基音_エンベロープ・アタックレベル
H L L H AL_W2_3 AL_W2_2 AL_W2_1 AL_W2_0 倍音2_エンベロープ・アタックレベル
H L H L AL_W3_3 AL_W3_2 AL_W3_1 AL_W3_0 倍音3_エンベロープ・アタックレベル
H L H H AL_W4_3 AL_W4_2 AL_W4_1 AL_W4_0 倍音4_エンベロープ・アタックレベル
H H L L AL_W5_3 AL_W5_2 AL_W5_1 AL_W5_0 倍音5_エンベロープ・アタックレベル
H H L H RR_3 RR_2 RR_1 RR_0 エンベロープ・リリースレート
H H H L 不明_3 不明_2 不明_1 不明_0 不明
H H H H 不明_3 不明_2 不明_1 不明_0 不明


・データのbit7~bit4=0x0~0x4のとき
SV_W[N] :  (bit3 ~ bit0)
   [N] : (1~5 : 基音~倍音5)
このレジスタによって倍音ごと発音スタートレベルを決定します。

SV_W[N] = 0 ~ 15 (0x0 ~ 0xF)

設定値を"F"にセットした時の出力レベルを1とした場合、各設定値によるスタート出力振幅は以下のようになります。
設定値 出力電圧振幅比
0 0.000
1 0.008
2 0.010
3 0.016
4 0.021
5 0.031
6 0.042
7 0.063
8 0.083
9 0.125
A 0.167
B 0.250
C 0.333
D 0.500
E 0.667
F 1.000

基本的にはこのレジスタのbit3 ~ bit1の値を増やすと、スタートレベルは2の累乗で増加します。また、bit0をHにするとさらに音量は1.5倍となります。

   SV_W[N]_OUT_LEVEL = 2^(WL-1) * (1 + WDL*0.5)

   :WL = SV_W[N](bit3 ~ bit1)  【SV_W[N](bit3 ~ bit1) = 1~7の時】
   :WDL = SV_W[N](bit0) 

   SV_W[N]_OUT_LEVEL :スタート時電圧振幅値

スタートレベルは、キーオン時の発音レベルです。よくあるエンベロープパラメータADSRの場合、キーオン時は0となっていることが多いですが、HD43517の場合キーオン時の最初のアタック開始時の発音レベルを0以外にできます。

アタック期間では、0レベル~設定したスタートレベルまでのアタックレートが∞となり、設定したスタートレベルに達してからは設定したアタックレートで音量がアタックレベル増加します。

基本的にはSV(スタートレベル) < AL(アタックレベル)
となるようにパラメータをセットします。

音色を保持したまま音量を変更したい場合は、SV_W[N]のbit3 ~ bit1の値を変更すると良さそうです。


・データのbit7~bit4=0x5のとき
AR :  (bit3 ~ bit0)
データのbit7~bit4を0x5にした場合、エンベロープ・アタックレートレジスタに対して値を書き込みます。

AR = 0 ~ 15 (0x0 ~ 0xF)

実測値を元に予想した設定値と大まかなアタック時間の関係を以下の表に示します。
条件 : SV_W[N]=0x0
(入力クロックCLK = 4.0MHz時)
設定値 アタック時間 [ms]
0 0.9※
1 0.9
2 1.9
3 3.8
4 7.5
5 15
6 30
7 60
8 120
9 240
A 480
B 960
C 1900
D 3200
E 4500
F 5800
※200~300μsほど遅延

基本的に1.9sまではこのレジスタのbit3 ~ bit1の値を増やすと、アタック時間は2の累乗で増加します。
レジスタ設定値とアタック時間は次の式で求められます。

   A_time = (2 ^ AR) * 3 * (5 ^ 4) / CLK  [s]  【AR = 1~12の場合】
   A_time = 1.3 * (AR - 12) + (2 ^ 12) * 3 * (5 ^ 4) / CLK  [s]  【AR = 13~15の場合】

   A_time [s] : アタック時間
   CLK [Hz] : 入力クロック(標準4MHz)


・データのbit7~bit4=0x6のとき
DR :  (bit3 ~ bit0)
データのbit7~bit4を0x6にした場合、エンベロープ・ディケイレートレジスタに対して値を書き込みます。

DR = 0 ~ 15 (0x0 ~ 0xF)

実測値を元に予想した設定値と大まかなディケイ時間の関係を以下の表に示します。
条件 : SV_W[N]=0x0, AL_W[N]=0xF, SL=0x0
(入力クロックCLK = 4.0MHz時)
設定値 ディケイ時間 [ms]
0 1200
1 2500
2 4900
3 9800
4 9800
5 9800
6 9800
7 9800
8 4.8
9 9.6
A 19
B 38
C 77
D 150
E 310
F 610

例外が多少ありますが、このレジスタのbit3 ~ bit1の値を増やすと、ディケイ時間は2の累乗で増加します。
レジスタ設定値とディケイ時間は次の式で求められます。

   D_time = (2 ^ (DR+16)) * 3 * (5 ^ 2) / CLK  [s]  【DR = 0~3の場合】
   D_time = (2 ^ DR) * 3 * (5 ^ 2) / CLK  [s]  【DR = 9~15の場合】

   D_time[s]: ディケイ時間
   CLK [Hz]: 入力クロック(標準4MHz)


・データのbit7~bit4=0x7のとき
SL :  (bit3 ~ bit0)
データのbit7~bit4を0x7にした場合、エンベロープ・サスティンレベルレジスタに対して値を書き込みます。

SL = 0 ~ 15 (0x0 ~ 0xF)

設定値を"F"にセットした時の出力レベルを100%とした場合、各設定値によるサスティン出力振幅比は以下のようになります。
条件 : SV_W[N]=0x0, AL_W[N]=0xF
設定値 出力振幅比 [%]
0 0.0
1 0.8
2 1.0
3 1.6
4 2.1
5 3.1
6 4.2
7 6.3
8 8.3
9 12.5
A 16.7
B 25.0
C 33.3
D 50.0
E 66.6
F 100

ディケイ期間が終了し、サスティン期間中の電圧振幅値は、各倍音のAL(アタックレベル)とこのサスティンレベルの積となります。

SV(スタートレベル) >= AL(アタックレベル)
となるように設定し、AL(アタックレベル)の値を倍音ごとに別の値を設定すると、
最大振幅はSVレジスタで、サスティンレベルはALレジスタで操作できます。


・データのbit7~bit4=0x8~0xCのとき
AL_W[N] :  (bit3 ~ bit0)
   [N] : (1~5 : 基音~倍音5)
このレジスタによって倍音ごとアタックレベルを決定します。

AL_W[N]  = 0 ~ 15 (0x0 ~ 0xF)

設定値を"F"にセットした時の出力レベルを1とした場合、各設定値によるアタック出力振幅は以下のようになります。
設定値出力電圧振幅比
00.000
10.008
20.010
30.016
40.021
50.031
60.042
70.063
80.083
90.125
A0.167
B0.250
C0.333
D0.500
E0.667
F1.000

基本的にはこのレジスタのbit3 ~ bit1の値を増やすと、アタックレベルは2の累乗で増加します。また、bit0をHにするとさらに音量は1.5倍となります。

   AL_W[N] _OUT_LEVEL = 2^(WL-1) * (1 + WDL*0.5)

   :WL = SV_W[N](bit3 ~ bit1)  【AL_W[N] (bit3 ~ bit1) = 1~7の時】
   :WDL = SV_W[N](bit0) 

   AL_W[N] _OUT_LEVEL :アタック期間終了時電圧振幅値

基本的にはSV(スタートレベル) < AL(アタックレベル)
となるようにパラメータをセットします。

音色を保持したまま音量を変更したい場合は、SV_W[N]のbit3 ~ bit1の値を変更すると良さそうです。


・データのbit7~bit4=0xDのとき
RR :  (bit3 ~ bit0)
データのbit7~bit4を0xDにした場合、エンベロープ・リリースレートレジスタに対して値を書き込みます。

RR = 0 ~ 15 (0x0 ~ 0xF)

実測値を元に予想した設定値と大まかなリリース時間の関係を以下の表に示します。
条件 : AL_W[N]=0xF, SL=0xF
(入力クロックCLK = 4.0MHz時)
設定値リリース時間 [ms]
01200
12500
24900
39800
49800
59800
69800
79800
84.8
99.6
A19
B38
C77
D150
E310
F610

例外が多少ありますが、このレジスタのbit3 ~ bit1の値を増やすと、リリース時間は2の累乗で増加します。
レジスタ設定値とリリースレートは次の式で求められます。

   R_time = (2 ^ (RR+16)) * 3 * (5 ^ 2) / CLK  [s]  【RR = 0~3の場合】
   R_time = (2 ^ RR) * 3 * (5 ^ 2) / CLK  [s]  【RR = 9~15の場合】

   R_time[s]: リリース時間
   CLK [Hz]: 入力クロック(標準4MHz)

このレジスタで設定したディケイレートは、エンベロープ0とエンベロープ1に反映されます。


・データのbit7~bit4=0xE,0xFのとき
不明なレジスタです。
通常使用しません。

$3 DELAY 倍音ごとの発音ディレイレジスタ群です。
このレジスタに書き込みを行うと対象の内部レジスタの値が更新されます。(キーオン中に変更すると、反映されないパラメータもあるので必ずすべてのチャンネルをキーオフしてからこのレジスタに関する値を変更してください。)

データのbit6~bit4で対象レジスタを決定します。

bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
機能
X L L L DLY_W1_3 DLY_W1_2 DLY_W1_1 DLY_W1_0 基音_ディレイタイム
X L L H DLY_W2_3 DLY_W2_2 DLY_W2_1 DLY_W2_0 倍音2_ディレイタイム
X L H L DLY_W3_3 DLY_W3_2 DLY_W3_1 DLY_W3_0 倍音3_ディレイタイム
X L H H DLY_W4_3 DLY_W4_2 DLY_W4_1 DLY_W4_0 倍音4_ディレイタイム
X H L L DLY_W5_3 DLY_W5_2 DLY_W5_1 DLY_W5_0 倍音5_ディレイタイム
X H L H 不明_3 不明_2 不明_1 不明_0 不明
X H H L 不明_3 不明_2 不明_1 不明_0 不明
X H H H 不明_3 不明_2 不明_1 不明_0 不明


・データのbit7~bit4=0x0~0x4または0x8~0xCのとき
DLY_W[N] :  (bit3 ~ bit0)
   [N] : (1~5 : 基音~倍音5)
このレジスタによって倍音ごと発音ディレイ時間を決定します。

DLY_W[N]  = 0 ~ 15 (0x0 ~ 0xF)

実測値を元に予想した設定値と大まかなディレイ時間の関係を以下の表に示します。
条件 : SV_W[N]=0xF
(入力クロックCLK = 4.0MHz時)
設定値ディレイ時間 [ms]
00.5
10.9
21.9
33.8
47.5
515
630
760
8120
9240
A480
B960
C1900
D3200
E4500
F5800

基本的に1.9sまではこのレジスタのbit3 ~ bit1の値を増やすと、ディレイ時間は2の累乗で増加します。
レジスタ設定値とディレイ時間は次の式で求められます。

   DLY_time = (2 ^ DLY_W[N]) * 3 * (5 ^ 4) / CLK  [s]  【DLY_W[N] = 1~12の場合】
   DLY_time = 1.3 * (DLY_W[N] - 12) + (2 ^ 12) * 3 * (5 ^ 4) / CLK  [s]  【DLY_W[N] = 13~15の場合】

   DLY_time [s] : ディレイ時間
   CLK [Hz] : 入力クロック(標準4MHz)

データのbit7の値は内部アドレスとして使用しません。
bit7がどちらの値でも書き込み対象アドレスは変化しません。

・データのbit7~bit4=0x5~0x7または0xD~Fのとき
不明なレジスタです。
通常使用しません。

$4 PITCH_C ピッチ変更用のピッチ設定レジスタです。
このレジスタに書き込みを行うと、内部の一時保存レジスタの値が更新されます。
このレジスタの値を更新しただけでは反映されません。$6の「OCT_C_SET」レジスタに書き込むと、このレジスタの値が反映されます。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
TP_7TP_6TP_5TP_4TP_3TP_2TP_1TP_0
※$6の「OCT_C_SET」レジスタのbit3とbit2にそれぞれ「TP_9」と「TP_8」があります。

概要は$6の「OCT_C_SET」レジスタの項を見てください。

$5 PITCH ピッチ設定レジスタです。
このレジスタに書き込みを行うと、内部の一時保存レジスタの値が更新されます。
このレジスタの値を更新しただけでは反映されません。$7の「OCT_SET」レジスタに書き込むと、このレジスタの値が反映されます。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
TP_7TP_6TP_5TP_4TP_3TP_2TP_1TP_0
※$7の「OCT_C_SET」レジスタのbit3とbit2にそれぞれ「TP_9」と「TP_8」があります。

概要は$7の「OCT_SET」レジスタの項を見てください。


$6 OCT_C_SET ピッチ変更用のピッチセットレジスタです。
このレジスタへ書き込みを行うと、発音中の指定チャンネルのピッチが変更されます。

$4の「PITCH_C」レジスタとペアで使用します。必ず「PITCH_C」レジスタへの書き込み後にこのレジスタへ書き込み動作を行ってください。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
不明_0OCT_2OCT_1OCT_0TP_9TP_8TP_CH_1TP_CH_0
※$4の「PITCH_C」レジスタのbit7~bit0に「TP_7」~「TP_0」があります。


不明_0:  (bit7)
不明なビットです。
通常、Lをセットしておきます。


OCT:  (bit6 ~ bit4)
オクターブ設定レジスタです。
OCT = 0 ~ 7


TP:  ($6「OCT_C_SET」のbit3, bit2 、$4「PITCH_C」のbit7 ~ bit0)
ピッチ設定レジスタです。
$4の「PITCH_C」レジスタとセットで使用します。必ず「PITCH_C」レジスタへの書き込み後にこのレジスタへ書き込み動作を行ってください。

TP = 0 ~ 1023 (0x000 ~ 0x3FF)


TP_CH:  (bit1, bit0)
ピッチ変更対象チャンネルビットです。
TP_CH = 0 ~ 3  (チャンネル1~4)


設定値による基音周波数は次の式で求められます。

   f = CLK * (TP + 1) * (2 ^ OCT) *CWMK/ (5 * (2^25))   [Hz]

   f [Hz] : 発音周波数
   CLK [Hz] : 入力クロック(標準4MHz)
   CWMK : 倍音比モード係数(CWM=0,3の時1、CWM=1の時5.998≃6、CWM=2の時4.998≃5)


発音したい基音周波数fによるTPは次の式(近似)で求められます。

   TP = ( f * (5 * (2^25)) / (CLK * (2 ^ OCT) *CWMK) ) - 1


HD43517はピッチレジスとオクターブレジスタが分かれているため、音程テーブルを1オクターブ分作成しておけば容易に音程をセットできるようになっています。


(入力クロックCLK = 4.0MHz時)
音程 CWM=0,3の時TP設定値 CWM=1の時TP設定値 CWM=2の時TP設定値
C 342 (0x156) 456 (0x1C8) 273 (0x111)
C# 362 (0x16A) 483 (0x1E4) 290 (0x122)
D 384 (0x180) 512 (0x200) 307 (0x133)
D# 407 (0x197) 543 (0x21F) 325 (0x145)
E 431 (0x1AF) 575 (0x23F) 345 (0x159)
F 457 (0x1C9) 609 (0x261) 365 (0x16D)
F# 484 (0x1E4) 646 (0x286) 387 (0x183)
G 513 (0x201) 684 (0x2AC) 410 (0x19A)
G# 543 (0x21F) 725 (0x2D5) 434 (0x1B2)
A 576 (0x240) 768 (0x300) 460 (0x1CC)
A# 610 (0x262) 814 (0x32E) 488 (0x1E8)
B 646 (0x286) 862 (0x35E) 517 (0x205)
※440HzをA4とした場合、CWM=0,3の時OCT = 出力したいオクターブ+1、CWM=1の時OCT =出力したいオクターブ-2、CWM=1の時OCT =出力したいオクターブ-1、をセット



このレジスタへ値を書き込むと"発音中"のチャンネルのみピッチが変更されます。
主にピッチベンドやビブラートでの使用を想定しているようです。
キーオン時のピッチセットは$7の「OCT_SET」を使用してください。

$7 OCT_SET ピッチセットレジスタです。
このレジスタへ書き込みを行うと、キーオン前の指定チャンネルのピッチが変更されます。

$5の「PITCH」レジスタとペアで使用します。必ず「PITCH」レジスタへの書き込み後にこのレジスタへ書き込み動作を行ってください。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
不明_0OCT_2OCT_1OCT_0TP_9TP_8TP_CH_1TP_CH_0
※$5の「PITCH」レジスタのbit7~bit0に「TP_7」~「TP_0」があります。

音程の指定方法は$6の「OCT_C_SET」と同じです。


このレジスタへ値を書き込むと"キーオン前"のチャンネルのみピッチが変更されます。
発音中にこのレジスタの値を変更してもピッチは変化しません。
キーオン時のピッチセットとしての使用を想定しているようです。





・タイミング図

HD43517のデータシートが無いので、Casiotone602のタイミングを参考にしました。



アドレス・データの書き込みタイミングです。
アドレスを書き込む場合は14番ピンの「~A/D」をLにします。
データを書き込む場合は「~A/D」をHにします。

HD43517のレジスタへ値をセットする



HD43517は、アドレスバスとデータバスを共用しています。
HD43517へアドレス値を書き込んでからデータ値を書き込むことで、任意の場所の内部レジスタへデータをセットできます。

HD43517のレジスタへデータを書き込むと、百数十μsの待機時間が必要です。

-- HD43517のレジスタへのデータセット手順 --

①CSピンをH、~A/DピンをLにする
②500nsほど待機
③書き込み先アドレスをDB0~DB7へセットし、~WEピンをLにする
④500ns~600nsほど待機
⑤~WEピンをHにする
⑥20μsほど待機
⑦~A/DピンをHにする
⑧500nsほど待機
⑨書き込むデータをDB0~DB7へセットし、~WEピンをLにする
⑩500ns~600nsほど待機
⑪~WEピンをHにする
⑫300nsほど待機
⑬CSピンをLにする





・使い方(制御)

HD43517のレジスタはPSG系音源に比べると多いですが、FM音源ほど多くありません。
ですが、対象レジスタへデータを書き込む際、データの上位数ビットは書き込み先レジスタ選択用となっているため、多少ややこしくなっています。



-- レジスタの種類 --

大雑把に分けると、

・トーン出力・音程設定レジスタ
・エンベロープ系レジスタ

があります。

・トーン出力・音程設定レジスタ

CWM_KEY_OFF($0)
    MUTE($0、DATAbit7=L  : bit2)    ミュート設定ビット、Hでミュート
    CWM_0 ~ CWM_1($0、DATAbit7=L  : bit0~1)    倍音比モード設定ビット
    KOFF_CH_0 ~ KOFF_CH_1($0、DATAbit7=H  : bit0~1)    チャンネル0~1キーオフ

KEY_ON_CWSW($1)
    R_EN($1、DATAbit7=L  : bit3)    エンベロープ・リリース有効無効フラグビット
    R_MD($1、DATAbit7=L  : bit2)    エンベロープ・リリースモード設定ビット
    KON_CH_0 ~ KOFF_CH_1($1、DATAbit7=L  : bit0~1)    チャンネル0~1キーオン
    OT1 ~ OT5($1、DATAbit7=H  : bit2~6)    倍音出力設定
    OSW_CH_0 ~ OSW_CH_1($1、DATAbit7=H  : bit0~1)    チャンネル0~1各倍音ON/OFF設定

PITCH_C($4)、OCT_C_SET($6)、PITCH($5)、OCT_SET($7)
    TP_0 ~ TP_9($4,5 : bit0~7、$6,7 : bit2~3)    ピッチ設定
    OCT_0 ~ OCT_2($6,7 : bit4~6)    オクターブ設定
    TP_CH_0 ~ TP_CH_1($6,7 : bit0~1)    チャンネル0~1ピッチセット

・エンベロープ系レジスタ

STADSR_AL($2)
    SV_W[N]_0 ~ SV_W[N]_3($2、DATAbit7~4=0x0~0x4  bit0~3)    [N](1~5 : 基音~倍音5)スタートレベル
    AR_0 ~ AR_3($2、DATAbit7~4=0x5  bit0~3)    アタックレート
    DR_0 ~ DR_3($2、DATAbit7~4=0x6  bit0~3)    ディケイレート
    SL_0 ~ SL_3($2、DATAbit7~4=0x7  bit0~3)    サスティンレベル
    AL_W[N]_0 ~ AL_W[N]_3($2、DATAbit7~4=0x8~0xC  bit0~3)    [N](1~5 : 基音~倍音5)アタックレベル
    RR_0 ~ RR_3($2、DATAbit7~4=0xD  bit0~3)    リリースレート

DELAY($3)
    DLY_W[N]_0 ~ DLY_W[N]_3($3、DATAbit7~4=0x0~0x4  bit0~3)    [N](1~5 : 基音~倍音5)ディレイ時間



・発音手順


--HD43517のリセット直後からの基本的な発音手順--
①音色を決定するパラメータ群をHD43517へセットする。
②音程設定レジスタにチャンネルを指定して音程をセットする。
③キーオンレジスタへキーオンしたいチャンネルを指定してセットする。

ここでは、3段階のステップに分けていますが、①の音色パラメータ群のセットでは数バイト分のパラメータがあります。音色を変更しない場合、HD43517の起動後に一度だけ書き込めばよいです。


①音色を決定するパラメータ群をHD43517へセットする。

波形合成音源は基本的に各倍音成分の合成比率を時間的にコントロールすることで、音色を作り出しています。
HD43517の場合、5つの倍音成分があるので、PSG系音源と比べるとかなり多様な音色を作り出すことができます。

中でもHD43517は、キーオン後の発音ディレイやスタートレベルなどの機能があり、YM2151などのFM音源では難しいエンベロープ波形を作り出すことができます。
発音ディレイ、スタートレベル、アタックレベルのみ倍音ごと個別に設定できますが、それ以外は共通となります。

残念ながら、これらのエンベロープパラメータをチャンネルごとに変えることはできません。(一応倍音成分のON/OFFはチャンネルごとに出来るので、場合によってはチャンネルごと音色の変更は可能)

解析した段階でエンベロープパラメータは19種類、倍音合成関係パラメータは2種類あり、これらをHD43517へ書き込むことで発音準備が整います。

・エンベロープパラメータ
 --STADSR_AL($2)--
    SV_W1 ~ SV_W5    スタートレベル
    AR    アタックレート
    DR    ディケイレート
    SL    サスティンレベル
    AL_W1 ~ AL_W5    アタックレベル
    RR    エンベロープ・リリース
--DELAY($3)--
    DLY_W1 ~ DLY_W5    ディレイ時間

・倍音合成関係パラメータ
--CWM_KEY_OFF($0)--
    CWM    倍音比モード設定
    OT1 ~ OT5、OSW_CH    チャンネル0~1各倍音ON/OFF設定


②音程設定レジスタにチャンネルを指定して音程をセットする。

HD43517には音程設定レジスタが2種類あり、それぞれキーオン時の音程指定、発音中の音程変更として分けられています。
ピッチベンドやビブラートをしない場合、基本的に片方のレジスタのみピッチデータを書き込めばよいです。

・音程指定パラメータ(キーオン時)
--PITCH($5)、OCT_SET($7)--
    TP    ピッチ設定
    OCT    オクターブ設定
    TP_CH    ピッチ、オクターブセット先チャンネル0~1


③キーオンレジスタへキーオンしたいチャンネルを指定してセットする。

キーオンレジスタに指定チャンネルを書き込むと、キーオンします。
キーオフする場合は、キーオフレジスタへ指定チャンネルを書き込みます。

キーオン時にはリリース設定をビットがあるので、リリースを有効にする場合は注意が必要です。

・キーオン
--KEY_ON_CWSW($1)--
    R_EN    エンベロープ・リリース有効無効フラグビット
    R_MD    エンベロープ・リリースモード設定ビット
    KON_CH    チャンネル0~1キーオン


・使い方(回路)

HD43517は日立のCMOS LSI HD43000シリーズの一つだと思われます。
CMOS ICなので消費電力小さいため、回路次第ですがUSBからの給電も十分可能です。

HD43517のクロック源として基本的に4.00MHzのクリスタルオシレータを使用します。

・DAC周り

HD43517は単体での音声出力はできず、基本的には専用の外付けDAC回路が必要です。
Casiotone 602では、R2RラダーDACや12bit汎用パラレルDAC ICを使用しています。

HD43517の出力は、2'コンプリメント18bit値を「13bit + 0~5bitシフト」の形をパラレルで出力します。
そのため、汎用のDAC ICをそのまま使用する事はできません。


LSBの1bitは使わない場合、基本的には12bitDACと0~5bitアナログシフタを組み合わせた回路となります。






発音部分の基本の回路 DAC ICを使用(CasioTone602の発音部分、一部簡略化)


発音部分の基本の回路 R2RラダーDACの場合(CasioTone602の発音部分)

まずはCasiTone602のDAC周りを例に挙げます。

CasiTone602にはHD43517が3つ使われています。HD43517を複数個連結することでDAC回路をまとめることができるため、CasiTone602のDAC回路は2つしか入っていません。

さらにCasiTone602の2つのDAC回路はそれぞれDAC IC使用バージョンとR2Rラダー抵抗バージョンの2種類となっています。


回路図を見てもらえばわかりますが、どちらの回路も12bitDACと0~5bitアナログシフタ
およびサンプルホールド回路となっています。

12bitDAC回路部分ではAM6012DC相当品(HA17012GB)などのパラレルDAC ICやESK-S10L0025C-1などのR2Rラダー抵抗アレイを使っています。
どちらも入手困難ですので、抵抗単品を選別してR2RラダーDACを組むのが良さそうです。

0~5bitアナログシフタ回路は、アナログスイッチの4066Bと抵抗を使った回路となっており、1段目のアナログスイッチ2つで入力信号を1/1倍または1/8倍、2段目のアナログスイッチ3つで1/1、1/2、1/4倍にして出力します。
その後ローパスフィルタを通してスピーカへ出力されます。


・ローパスフィルタ

HD43517のサンプリングレートfs[Hz]は、

   fs = CLK / 160   [Hz]

HD43517に入力するクロックCLKの周波数が4.0MHzの場合、サンプリング周波数は、

   fs = CLK / 160
      = 4.0M / 160
      =25KHz

となります。

よって、クロックCLKの周波数が4.0MHzの時には、
約12KHz(25/2 KHz)以上の周波数成分をカットする必要があります。
理想LPFであれば12KHz以上でカットできますが、
基本的にアナログLPFのカット特性は理想LPFのカット特性からかけ離れているので、
余裕をもってカットオフ周波数(フィルタ通過後に源信号が-3dBになる周波数)を8~10KHz付近にもつアナログLPF用意してあげます。


サンプルホールド回路までのオペアンプのスルーレートは高めのものを選ぶと良さそうです。

※参考  NJM4558族の標準スルーレート
NJM4558    SR:1V/μs  △
NJM4559    SR:2V/μs  ○
NJM4556A  SR:3V/μs  ○
NJM4560    SR:4V/μs  ○

CasioTone602の場合、NJM4558DDを使用しています。


・HD43517の複数個の相互接続について

HD43517は複数使用した場合にDACの数を省略できる機能があります。
通常、音源ICがN個の場合にはN個のDAC回路が必要になりますが、
HD43517の場合では最大4つ?(3つまでは検証済み)相互接続して1つのDAC回路で済ますことも可能です。
ただし、倍音合成比率は共通となったりする制約があります。

CasioTone602では3つのHD43517が使われており、内2つは常時相互接続して
残り1つは3個相互接続状態と切り離し状態をアナログスイッチで切り替えられるようにしてあります。


相互接続する場合は、9ピンの「DAD」、10ピンの「SYNC」、11ピンの「EVD」端子を
それぞれの同端子に接続します。
マスタとなったHD43517のみデータを受け取ってパラレルサンプルデータを出力します。
倍音合成比率はマスタとなるHD43517に書き込まれたパラメータが反映されます。

マスタ以外のHD43517もパラレルサンプルデータを出力し、
倍音合成比率はパラレルサンプルデータを出力しているHD43517に書き込まれたパラメータが反映されます。
また、38ピンの「S/H」端子はマスタの「S/H」と同期しています。未使用の場合はGNDへ接続するようです。


マスタ・スレーブ設定は17ピンの「I-4」、18ピンの「MSO」で行います。
「I-4」端子と「MSO」端子がHのときマスタモードとなり、「SYNC」端子から同期信号を出力し、
「DAD」端子と「EVD」端子からシリアルデータを受け取ります。

それ以外では「SYNC」端子から同期信号を受け取り、「DAD」端子と「EVD」端子からシリアルデータを出力します。

マスタ・スレーブ設定によってアドレス空間に配置されるHD43517も変わるので注意してください。


例えば、3つのHD43517 ①・②・③を接続したとしましょう。
①、②、③のうちマスタを①に設定します。
①から出力されるパラレルサンプルデータは、①+②+③のサンプルデータで倍音合成比率は①で設定したものです。
②から出力されるパラレルサンプルデータは、②のみのサンプルデータで倍音合成比率は②で設定したものです。
③から出力されるパラレルサンプルデータは、③のみのサンプルデータで倍音合成比率は③で設定したものです。


気づいた方もいると思いますが、HD43517にそれぞれDACを用意してかつHD43517を相互接続することで、1つのチャンネルあたりで音程の同じ2種類の音色の音を出すことができます。

例えば2つのHD43517 ①マスタ・②スレーブにそれぞれDACを用意して相互接続した場合、
総チャンネルは①(ch0~ch3)、②(ch4~ch7)、②'(ch4'~ch7')となり、より重厚なサウンドを作り出せます。

HD43517の相互接続はDAC回路を省くことができ、メリットが大きいように見えますが、注意点があります。
先ほどから説明している通り、倍音合成比率を合わせないといけないということです。


・複数個の相互接続をしない場合

複数個のHD43517を相互接続しない場合は、基本的に9ピンの「DAD」、10ピンの「SYNC」、11ピンの「EVD」端子を開放しておきます。
これらの端子は内部でプルアップされているので、プルアップ抵抗を接続してもかまいません。


9ピンの「DAD」と11ピンの「EVD」端子を開放しておき、10ピンの「SYNC」端子を相互接続した場合、パラレルデータ出力とサンプル・ホールド出力「S/H」が同期します。

サンプル・ホールド回路を共通化できるので、多少回路規模を小さくできます。




・使用例  - MMLで演奏 -

HD43517を1~4個使用し、MMLで演奏するプログラム(Arduino ATmega328p用)を紹介します。

・回路図(HD43517を3つ使用し、相互接続した場合)




・回路図(HD43517を3つ使用し、DACをそれぞれ用意した場合)



基本的に回路構成はCasioTone602のものと同じですが、アナログマルチプレクサを使用したりなどの変更点がいくつかあります。もちろんCasioTone602の回路例で紹介した回路でも音が出ます。



・プログラム(HD43517を1~4個使用、「I-4」と「MSO」端子でアドレス空間上の配置位置を決定)


// HD43517でMML演奏プログラム
//©oy
// https://oykenkyu.blogspot.com/2023/02/HD43517.html

#define XTAL 16000000 //水晶振動子の周波数(MML演奏速度に影響)

#include "avr/io.h"
#include "avr/interrupt.h"

//#define SERIALSPEED 31250 // UARTのボーレート
#define SERIALSPEED 38400 // UARTのボーレート(デバッグ用)

#define CH 16  // HD43517最大チャンネル数(HD43517は1つで4チャンネル分発音できます。)
#define MML_MAX_TR CH // MMLトラック数

//HD43517レジスタ
//bit6~bit4は実アドレス、bit3~bit0は内部レジスタ用アドレス
#define COMP_W_MODE 0x00 //波形合成モード
#define KEY_OFF 0x08 //キーオフ

#define KEY_ON 0x10 //キーオン
#define COMP_W_SW 0x18 //チャンネルごとの各正弦波合成ON/OFF

#define SV_W1_LV 0x20 //スタートレベル:基音音量
#define SV_W2_LV 0x21 //スタートレベル:2つ目の倍音音量
#define SV_W3_LV 0x22 //スタートレベル:3つ目の倍音音量
#define SV_W4_LV 0x23 //スタートレベル:4つ目の倍音音量
#define SV_W5_LV 0x24 //スタートレベル:5つ目の倍音音量
#define AR 0x25 //アタックレート
#define DR 0x26 //ディケイレート
#define SL 0x27 //サスティンレベル
#define AL_W1_LV 0x28 //アタックレベル:基音音量
#define AL_W2_LV 0x29 //アタックレベル:2つ目の倍音音量
#define AL_W3_LV 0x2A //アタックレベル:3つ目の倍音音量
#define AL_W4_LV 0x2B //アタックレベル:4つ目の倍音音量
#define AL_W5_LV 0x2C //アタックレベル:5つ目の倍音音量
#define RR 0x2D //リリースレート

#define W1_DLY 0x30 //基音発音ディレイ
#define W2_DLY 0x31 //2つ目の倍音発音ディレイ
#define W3_DLY 0x32 //3つ目の倍音発音ディレイ
#define W4_DLY 0x33 //4つ目の倍音発音ディレイ
#define W5_DLY 0x34 //5つ目の倍音発音ディレイ

#define PTC_CHG 0x40 //ピッチ変更値指定
#define PTC 0x50 //ピッチ指定
#define OCT_CHG 0x60 //オクターブ変更値セット
#define OCT 0x70 //オクターブセット

// HD43517_チップセレクト用テーブル(少し高速化できる?)
unsigned char hd43517_cs_conv[4] = {0x38,0x58,0x68,0x70};

// HD43517_保存用
unsigned char hd43517_main_vol[CH]; //音量保存用
unsigned char hd43517_main_pan[CH]; //パン保存用
unsigned char hd43517_main_ptc[CH]; //ピッチ保存用
unsigned char hd43517_main_cwm[CH]; //波形合成モード保存用(音程計算で使用)

//hd43517のノートデータへ変換(合成比率モード別)
//HD43517の入力クロック周波数が4.00MHzの時
unsigned int hd43517_note_conv[4][12] = {
  {0x157, 0x16C, 0x182, 0x199, 0x1B1, 0x1CB, 0x1E6, 0x203, 0x222, 0x242, 0x265, 0x289},
  {0x1C8, 0x1E4, 0x200, 0x21F, 0x23F, 0x261, 0x286, 0x2AC, 0x2D5, 0x300, 0x32E, 0x35E},
  {0x111, 0x122, 0x133, 0x145, 0x159, 0x16D, 0x183, 0x19A, 0x1B2, 0x1CC, 0x1E8, 0x205},
  {0x157, 0x16C, 0x182, 0x199, 0x1B1, 0x1CB, 0x1E6, 0x203, 0x222, 0x242, 0x265, 0x289}
};
//hd43517のオクターブデータへ変換(合成比率モード別)
int hd43517_oct_conv[4]= {
  0, 1, -2, 0
};
//HD43517音色パラメータ
//CWM   :   波形合成モード、bit1~bit0の4通り
//CWSW  :   各正弦波合成ON/OFF、bit4~bit0がそれぞれ倍音5~基音に割り当て
//SV-W[N]-L  :  スタートレベル:[N]個目の倍音音量、bit3~bit0
//AL-W[N]-L  :  アタックレベル:[N]個目の倍音音量、bit3~bit0
//AR    :   アタックレート、bit3~bit0
//DR    :   ディケイレート、bit3~bit0
//SL    :   サスティンレベル、bit3~bit0
//RR    :   リリースレート、bit3~bit0
//W[N]-DLY    :  [N]目の倍音発音ディレイ、bit3~bit0
//TL    :   トータル発音レベル(未使用)
//PAN   :   パン(未使用)
//PTC   :   音程、7bit値 64が標準




//パラメータ構造
//CWM, CWSW, reserve, reserve, reserve,
//SV-W1-L, SV-W2-L, SV-W3-L, SV-W4-L, SV-W5-L,
//AL-W1-L, AL-W2-L, AL-W3-L, AL-W4-L, AL-W5-L,
//AR, DR, SL, RR, reserve,
//W1-DLY, W2-DLY, W3-DLY, W4-DLY, W5-DLY,
//TL, PAN, PTC, reserve, reserve,
//reserve, reserve

const unsigned char hd43517_parameter[][32] PROGMEM = {

  //PIPE ORGAN
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x0C, 0x0B, 0x09, 0x0B, 0x0B,
    0x08, 0x0B, 0x0E, 0x00, 0,
    0x01, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //D1A PASON
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x09, 0x0B, 0x00, 0x0B, 0x09,
    0x05, 0x0B, 0x0F, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //TIBIA
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x0C, 0x00, 0x09, 0x0C,
    0x05, 0x0F, 0x0E, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //FLUTE
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x0B, 0x00, 0x00, 0x0D,
    0x07, 0x0D, 0x0F, 0x00, 0,
    0x00, 0x02, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //JAZZ ORGAN1
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x0B, 0x0B, 0x0B, 0x00, 0x00,
    0x05, 0x0D, 0x0F, 0x00, 0,
    0x00, 0x00, 0x01, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //PIANO
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x0D, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x03, 0x01, 0x00, 0x00, 0,
    0x00, 0x00, 0x00, 0x01, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //VIBRA PHONE
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x0C, 0x00, 0x00, 0x0C,
    0x00, 0x0B, 0x00, 0x00, 0x09,
    0x08, 0x01, 0x00, 0x01, 0,
    0x00, 0x01, 0x00, 0x00, 0x01,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //CELESTA
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x0C, 0x0D,
    0x00, 0x00, 0x00, 0x0C, 0x0B,
    0x08, 0x00, 0x00, 0x00, 0,
    0x00, 0x00, 0x00, 0x01, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //OBOE
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x0A, 0x0A, 0x0D, 0x00,
    0x07, 0x0B, 0x0F, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //FUNKY
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x0C, 0x00, 0x0C, 0x0C,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x03, 0x00, 0x00, 0x01, 0,
    0x00, 0x00, 0x00, 0x04, 0x05,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },

  //BRILLIANT OGAN
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x0D, 0x00, 0x00, 0x00, 0x0E,
    0x05, 0x0B, 0x0D, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //WOOD WIND
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x0B, 0x00, 0x00,
    0x0B, 0x00, 0x00, 0x00, 0x0B,
    0x09, 0x03, 0x0F, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //FULL TIBAS
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x09, 0x00, 0x0D, 0x00, 0x0C,
    0x05, 0x0B, 0x0E, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //PICCOLO
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x09, 0x00,
    0x00, 0x00, 0x00, 0x0E, 0x00,
    0x07, 0x0B, 0x0F, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //JAZZ ORGAN2
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x0D, 0x00,
    0x0B, 0x0B, 0x0B, 0x00, 0x00,
    0x07, 0x0D, 0x0F, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //ELEC.PIANO
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x0C, 0x00, 0x00, 0x0D,
    0x00, 0x0C, 0x00, 0x00, 0x00,
    0x03, 0x02, 0x00, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x01,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //MARIMBA
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x0E, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x09,
    0x03, 0x00, 0x00, 0x00, 0,
    0x00, 0x01, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //CHIME
  {
    0x01, 0x1F, 0, 0, 0,
    0x0C, 0x0C, 0x0A, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x03, 0x01, 0x00, 0x01, 0,
    0x01, 0x01, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x2F, 0, 0,
    0, 0
  },
  //BASSOON
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x08, 0x0B, 0x0D, 0x08, 0x00,
    0x03, 0x02, 0x0E, 0x01, 0,
    0x01, 0x01, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //WAH BRASS
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x0C, 0x0E, 0x0C, 0x0A, 0x09,
    0x07, 0x0B, 0x0C, 0x00, 0,
    0x00, 0x04, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //@0_test
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x0F, 0x0E, 0x0D, 0x0C, 0x0B,
    0x00, 0x0C, 0x00, 0x00, 0,
    0x00, 0x02, 0x04, 0x06, 0x08,
    0xFF, 0x40, 0x68, 0, 0,
    0, 0
  },
  //@1_test_bell
  {
    0x02, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x0F, 0x00, 0x00, 0x0D, 0x00,
    0x00, 0x00, 0x00, 0x04, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x68, 0, 0,
    0, 0
  },
 

};


//---------------演奏したいSMFに合わせて要変更--------------------
//デフォルトオクターブ[ch0,ch1,ch2,…,]
//増やすとオクターブが高くなります。(出力オクターブ = def_oct + (mmlのオクターブ))
char def_oct[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
//char def_oct[16] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};

//デフォルトキー
char def_key = 0;

//--------------------------------------------------------------

//ドラム発音用
unsigned char drum_note_num = 0; //ドラムチャンネルセット保存用(未使用)

// MML演奏用
// ATmega328だとdoubleでも精度はfloatと同様
volatile double mml_time = 0;      // MML演奏用時間カウンタ
volatile double mml_time_ms = 0;   // MML演奏用時間カウンタms
volatile char mml_time_cnt_en = 0; // MML演奏用時間カウント_有効無効(0で無効、1で有効)
volatile int tempo = 120;          //テンポ


//演奏のデータ
//簡易MML
unsigned int mml_data_len = 0xFFFF; //トラック終端「;」が入るため、適当な数値で良い

// mmlのトラック終端には必ず「;」を入れてください。
//使えるコマンド
//「;」             :トラック終端コマンド(トラック終端に必ず入れてください。)
//
//・全チャンネル(トラック)でパラメータを共有
//   「t」+ 数値:テンポを整数で指定
//
//・各チャンネル(トラック)でパラメータの独立指定が可能
//   「v」+ 数値      :ベロシティ(音量)を整数で指定
//   「p」+ 数値      :パン(音量)を整数で指定
//   「@」+ 数値      :音色を整数で指定
//   「o」+ 数値      :デフォルトオクターブを整数で指定
//   「l」+ 数値      :デフォルト音長を整数で指定
//   「<」または「>」  :オクターブを上げる、下げる(「<」で1オクターブ上げる)
//   「^」            :タイ(タイの次の音程コマンドでの発音は無視され、音長のみ取得します。音程の省略も可能です。)
//
//
//・音程コマンド
//   (音程)+(「+」または「-」)+(音長)+(「.」):音程はc,d,e,f,g,a,b,rで指定、+または-で半オクターブ移動、音長は整数で指定、ドットの数で(音調+(音調/2)+(音調/4)+(音調/8)+…)符点音長を追加指定
//   例:
//   「a4」    : 音階Aで4分音符
//   「c+16」  : 音階C#で16分音符
//   「g-1.」  : 音階G♭で符点1分音符(1分音符+2分音符の長さ)
//   「r.」    : 休符で符点デフォルト音長(デフォルト音長の初期値は4分音符です。変更したい場合は「l」コマンドで指定してください。)
//   「r2...」 : 休符で3符点2分音符(複符点2分音符)(2分音符+4分音符+8分音符+16分音符の長さ)
//
//・トラック10(ch9、0x09)のドラムパート場合のみ、和音(音長が0)が使えます。
//

//宵加減テトラゴン
const unsigned char PROGMEM mml_data[] =
"t150l8r1r1r1o4f.d+r16d+f16rdr16da+.a+2^a+.gg+.<c.>eg4..r16g.d+.rg4.d+d+4..r16d+rd+r<cr16>g+.g+c4..r16fr16fr16d+3r24<ee>g+r16d+r.c.c.>g+16r16<fr16d+r16efr16d+r16efr16d+r16efr16f+.f+fr16d+r16e16e16fr16d+r16efr16d+r16efr16c+.d<d+.r16d+.r2r16d+d+16r2.r16d4..r16d4rc+.r2...c.r4r16>g+.<cr.>f+r16f+r16f+f.r16f4a+2b2<c4.rc4.r4>e16f3r2r6>g.ra+r<d+d+rd+4r4f4r4f4r2rd+.g+.g+g.gr16g+>a+.<a+.a+g4rg>a+2g+2r1r1<g2..r<<b2.r4>>c2d2d+2d+2<d+.r16>a+2.g+r16g+r16g+g+4..r16dr16gr16gdr16f+r16f+r1d2g2e2d+2e2.d+4d2g4r16f.c+2c2>b2a+2a4..r16<e2d+.d+.dc+4>e4<<<<d4.>>e4.>c4<d+3r6>c2>b2<d+2>b2b2<e2.e4f+2f+2g4..r16b4e4g4a4a.gr16gb2b.<d.<<d>>>d+.a+.a+a+2d2d.d.de.<g.>e4.e>e<a.<cr16d+cd+>d+<<c>>a.<cr16>dd.<c16>ag+<f.>b.<dff>b<dg+.d+.>f<c.r16d+cf.>g.a+a+g4>g<d+.>g+.<g+g+f+d+4g+4d+d+>b4<g+4>a+.<d+.<d+>>a+4<a+4>a+.<<c16>g+g+a+4f4>b.<b16e>b<ee4.a1^a1r<e2..d+1e1>b2a+2>a1g+2g2f+1f+4r2.b2^b6r1r3br16<f+.ef+2cr16g.f<cr>g4d.d4^d16ar16dr16f+drdda.<d16>a<d16r16>d2g2e2d+2e2.d+4d2g4f4c+2c2>b2a+2a2<e2d+.d+.dc+4>e4<<<<d4.>>e4.>c4<d+3r6>c2>b2<d+2e4>b4b2<e2.e4f+2f+ef+4g4..r16b4e4g4a4a.d+r16gb2b.<d.<<d>>>d+.a+.a+a+2d2d.d.d<d.d2^d.>b<c.e.>g+b4..r16b.g.rb4.gg4..r16grgr<er16c.c>e4..r16ar16ar16g3r24<g+g+cr16>gr.e.e.c16r16ar16gr16g+ar16gr16g+ar16gr16g+ar16a+.a+ar16gr16g+16g+16ar16gr16g+ar16a+r16bbr16<c.c+>dr16cr16c+dr16cr16c+dr16cr16c+dr16cr16c+dr16cr16c+dr16cr16c+d4.c16>b16a+16r32;l16r1r1r1r8.o5d+r8d+8>a+r8<dr8dr<a+2.r1r4>d+r4..d+4r1r1r1r1r2r8..a96a+96b96<c48c+48d12c+48c48>b96a+96a96r4...r3d+96f96g96a96a+96b96<c96c+96d96d+96e24r1r3r48>f+24f48e48d+48d48c+24c8c+24d12d+eff+24g24g+32a32a+32b32<c32c+32d48d+48e96f96f+96g96g+96r8r32>f32r32f8rd+rf6r48gr8>a+a+<f8d+8f8rg8r4r>b.r32a+4r8a+r<g+8rg8reeer8f8rg3r4r6crd+8crd+8rc8rf8f+8rf+8rf+.r32f6r3r8f32f32rfrd+rf6r48g.r.>a+a+<f8d+.r32f8rg.r3r96grf.r.>a+8ra+r<g+6r48g8rg+.r32g8..r8r32d+rd+8rd+8rf8g4r8grf4r8frf8ra+8rf.r32e6r48f6r48g8g+6r48c8rcrd+8.d8rd+rd+2.r4g4.r8g+4..ra+3r24a+.r32a+8rg+.r.g.r32f4..r8.d+8drd+8f3r6r8>a+4<a+8b8.rg8.rf6r48g8r8.c8r>b8r<c.r32d6r48c8rd+32r32d+d+6r48d8.d+8r8d+rd+ra+1^a+.r1r1r4r32d8.r<c6r48>b6r48<c8>b8rg8.a.r32g8.a8.gr<c6r48>b8.<cr>b6r6r24a.r32b8rd6r48arg8.a8rgr<c8.>b8r<cr>b8.r8.bbb6r48g8rera6r6r24d32d32rd6r48a8.f+8g8rg8.a8<c8.>b6r48g8a8.r8.br<c8r>b6r48g8a8.r<d.r32c.r32>b6r48g8rarg8.a8rgr<c8.>b6r48<c8>b8r8d+8..r32d8..r8r32<drd+6r48>b6r48b.r32a6r48g6r48a.r32g6r12b8r8<c4r8>ed+e.r8r32<c8.r>b4r8dd32r32d4r4e8r8<cr>e8r8<c8r8c32r32c32r32>b8..r32a4g8.f+8.f+rg2r4a.r32b.r32<c4..r4rd6r12>b8.ra8.rg6r48<d.r.d.r4.r32>g.r32a+6r48a+.r.a+32a+r32a6r48a+8r<c8r8>f4..r4ra+32r32a32r32<c6r48c8rc8c8r>b6r48<crd4r4>d+6r48d6r48d+8a+4r4f8re.r.f8<c4r4>f+8rg+8.a+8.rb6r12<c+4r8>d+4<c+8.>b8rb.r32a+8rg+8.a+1.r1r1r1r8g+6r48e6r48f+.r32e8.f+8rera6r48g+8ra8g+8..r8r32f+.r32g+6r48>b6r48<f+.r32e8.f+8rera8rg+6r48a.r32g+4r8g+32r32g+32r32g+8re8.c+8f+6r6r24>b32r32b32r32b8r<f+6r48d+.r32e6r48e.r.f+.r32a6r48g+8re8f+6r3a8.g+8.e8f+2r1r1<br4..>f+3r6<<cr4..c8.r4r>gr8gr8gr4ar8ar>d8r8<c8.rc8.>b6r48<c8>b8rg8.a.r32g8.a8rgr<c6r48>b6r48<cr>b8.r8.a8b6r48d6r48arg8.a8rgr<c8.>b8r<cr>b8.r8.bbb6r48g8rera6r6r24d32d32rd6r48a6r48f+8g8rg8.a8<c6r48>b6r48g8a8.r8.br<c8r>b6r48g8a6r12<d8c.r32>b6r48g8rarg8.a8rgr<c8.>b6r48<c8>b6r12d+8..r32d8..r8r32<drd+6r48>b6r48b.r32a6r48g6r48a.r32b6r12b8.r<c3r24>ed+e8r8<c8.r>b4r8dd32r32d4r4e8r8<cr>e8r8<c8r8c32r32c32r32>b8.ra8.rg6r48f+8.f+8g4..r4ra8b8<c2r4d6r12>b8.ra8.rg6r48<d.r.>f+8g2....r1r32gr4..g4r1r1r1r1r1r1r1r1r2.<c+r8cr8c+r2rc+r8cr8c+rc+r8cr8c+rc+r8cr8c+rg8.r4.r32;l8r1r1r1o2a+.a+.a+a+.a+.<fd+.d+.d+d+.d+16d+4d.d.d>b.b16b.r16<c.r16c.r16c.r16c.r16>a+.r16a+.r16<d+4..r16>f.r16f.r16f.r16f.r16g.gr16<c.r16c.r16c>g+g+g+g+a4a4a+.g+.aa+.g+.aa+.g+.aa+a+bba+.g+.aa+.g+.aa+.g+.aa+.<c+.dd+4d+.r16>a+.<d+r16d+3r24>d+d+<d+>b<g>g+g4.rgr16<g.c.r16<cr16>c.rd+e>gg+4..r16g+4..r16br16br16ba+4<cd>d+4<d+>a+g.g.<d+>g+4g+g+aaaaa+a+4a+bbbb<cg16>g.ed+.g.g+g+<g+>g+aa+a+a+g+g.r16gg<g16f+32f32e16d+16>g4g+r16g+r16ag+r16g+r16g+d+.d+.a+<d+4dc>a+<a+g+gd+>a+g+4g+1<g+4.d+4>g+a+g+g2^gg4.b2<f32f+64g32g+32g32f+64f64e64d+64d32c+32c32>b32a+32.a16^a64g+32.f.f.fg.g.gg+4g+<d+16>g+16<ad+16a.a16a16>a+.a+.a+<a+16a+16a16a16a+16a+16>a+16<c16c+r16c+r16c+c+.c16>b<c+d.d.g16f+16d16.c16.>a.f+<d16r2.r16>g16<g16>g.g.<g>g.g.gf+.f+.f+<d+<d+>d+d+e.>e.<e>e.<d+.dd>a<da>g24g24<f24gff16e16c+r16c+16c+.r16c.c.c>br16b4^b16a+a+a+a+ar16a16aaa4a4<d+r16d+r16dc+<c+>c+16<c+16c16>b16c.c16r16cr16c.c.cc.c.c>g<gcf>b.b.b<d+.d+.d+>e.e16<ef+g4dc+>a.r16aaa.r16aa+b.r16b<d+e.e.ec4ccc+4c+c+d.d.dd.d+4^d+16>e.e.<ed.d.dc.c.cc4c4d2^d.c+32.c32.>b32.a+32.r<e.e16eee.e16eed+.d+16d+d+d+<d+>d+<e>d.d16ddar16<d16>ag+g^g32f+32f16r>g<gg..f+32f16r16>f.r16fff.r16f<f>g.r16ggg<g>ggg+.r16g+g+<g+g>g+4g+.r16g+g+g+d+g+aa+.r16a+a+a+.r16a+ba+.r16a+a+a+.r16a+a+b.b.bb.r16bbb2^ba+ag+r1r1r1r1r1r1r1r1r1r1r1b.<e.e>b4bb<c.c.cc.c.cd.dr16ddr16da16f+16d+16da16f+16d16r16ada16<d16d16>a16<d16>a16>g.g.<g>g.g.gf+.f+.f+<d+<d+>d+<d+>e.>e.<e>e.<d+.dd>a<da>g24g24<f24gff16e16c+r16c+16c+.r16c.c.c>br16b4^b16a+a+a+a+ar16a16aaa4a4<d+r16d+r16dc+<c+>c+16<c+16c16>b16c.c16r16cr16c.c.cc.c.c>g<gcf>b.b.b<d+.d+.d+>e.e16<ef+gbdc+>a.r16aaa.r16aa+b.r16b<d+e.e.ec4ccc+4c+c+d.d.e32f+32a32<c32d.>d+4^d+16>e.e.<ed.d.dc.c.<c16.c+32d16.c+32c>gcd2^d.c+32.c32.>b32.a+32.r<g.g.gg.g16g4f+.f+.<f+>d+.d+16d+.r16e.e.ee.r16e.r16g.g.gg.g16g4>a.a.aa.r16a.r16b.br16<e.r16e.r16eccccc+4c+4d.c.c+d.c.c+d.c.c+ddd+d+d.c.c+d.c.c+d.d+.ee.f.f+>g.f.f+g.f.f+g.f.f+g.f.gg.f.f+g.f.f+g^g32f+32f32e32d+32d32r4r32;l16r1r1r1o3a+4.r8a+4.r4r<<<<<a+g+d+>a+gd+>a+gd+r>ga+<d+g8rg+8rg8>b<d+f8d+8f8r8.<<<c+r>d+c>gd+c>gd+c>g<d+8g8rg+8rg8>b<d+f8d+8<cdd+8.d+8.r8d+8.d8.c8>a+8.<f8.d+4r4.>d+4.d+rd+4r1r1r1r4.<<d+>a+g+gd+>a+g+gd+>a+g+gd+>a+r2..<<d+4d+r4..g+8g8f+8f4r4<d8>d8r8c+4r4c+4r4.d+4r4>g+8.gr8g+8b8.b8rb8a+4g+4<d+8.r4rd+8.r4rd+8.r4rd+8.r4r>a+8.r4rb8.r4ra+8.r2.r<d+8.r4rd+8.r4r>a+8.r4ra+8.r4r<d+8.r4rd+8.r4rd+4r8d+4r8d+4<g2g+2a+2r2g+2r1.b4..r2r>>d+6r48d+6^d+192g64g+.r32g6r48d+4rd+2<d+8.dd+.r32d+.r32>d+8.d+32r32>a+8.a+<d+4f4c+8rf+8.f+8f4r4>>d8r<<g8rg8r8.f+8rf+8r2<<<cr8>br8<cr>br1.rd+d+af+d+8r2.>>d+4r2.<<c8d8g8re8>eg8d+4r4<d8r>a8r<d8>g8r4<d32r32d32r8r32ere8g8a8r8b8.r<c8.>b8.g8<d48c32>b48a32g48f+32e48d32c48>b32a48g32f+48e48d48c48>b48a48g48f+48e48>>c8<c32r32cr1<<<f+8g8ad+8.d8c8>b8r4.>b4.<f+f+<a8.g8.g8g8.r4rer<aec>aec>gec>gec>ge<<<dr<d>bf+d>af+d>bf+d>bf+d>b<<<<cg<<c>gec>gec>ge>bge>bgr8<<<<agd>agd>dagd+f+d+>bf+<<<ere>bgd>egd>b<gd>bgd>b<<d+>gd+<g<d+8>g>g<d+4r4<d4r1..>f8rg8.a4a3r2r24a8.f8.r8a8.g8.a8b8.r4rd+2<<d+8.c4r>>d2<<d8.d4r>>d+3r24d+d+d+4..rd+8rd+r4d+4r4d+4r4d+4.<dd+f8rd+8.d8>d+3r6c+8.c+4^c+c+2<<c.r.>a6r48f+.r32d+6r48c6r48>a8f+6r48d+8.c8>a8rf+8.r2..<<a8r2r8d+8r8f+8r8e8r1r8e8r4rg+8.a8br2r8.f+8r2r8e8r8d+8r8e8r2..>f+4.r2r8>>>a1..r4>b8r<<<<<e8re8>>>>>b3r24<brc8r<c8.cr>c8r<c8.cr>d8r<<<g8.g8>>>d8r<<<a8.a8<d8r8>c4<d+8.<c.r.d32r32g32r32>b8.d8d8.r2>>>e2d+8<<<d+d+af+d+8>>>e4r4>e8.r<<d+4>d4r4>g4<<<<c8d8g8re8>eg8d+4r4<d8r>a8r<d8>g8r4<d32r32d32r8r32ere8g8a8r8b8.r<c8.>b8.g8<d48c32>b48a32g48f+32e48d32c48>b32a48g32f+48e48d48c48>b48a48g48f+48e48>>c8<c32r32cr8c8c2c4<<<f+8g8ad+8.d8c8>b8r4.>b4.<f+f+<a8.g8.a8b8.r4rer<aec>aec>gec>gec>ge<<<dr<d>bf+d>af+d>bf+d>bf+d>b<<<<cg<<c>gec>gec>ge>bge>bgr8<<<<agd>agd>dagd+f+d+>bf+<<<ere>bgd>egd>b<gd>bgd>b<<d+>gd+<g<d+8>g>g<<g8g8g8g8<d>g8.r2.d>b<dr<b32d32rd>bgd>bgd>bg<gb8r<c8r>b8d+ga8g8a8<br<dgbge>bge>bge>bge<b8r<c8r>b8d+ga8g8<ef+g8.g8.r8e192f+192g6^g96f+8.e8d8.a8.g4r4.>g4.grg4r1r1r1r1r2.>>g32r2....g32r2....g32r1r;l16r1r1r1.o4fa+<d+dd+f>a+a+a+8r<d+8rd+rd+3r24d+r<gc>g+f>g+g+bb<d>g<gb>bg<ffd+<c>gd+<d+c>gd+g>g<cd+gd+c>g+g<c+d+g>d+g<gabr48g24g<d+>g<d+>g+gcd+g+<cd+>>g+<c>g+<<c>cg+d+g+fcdd>bg<a+grd+>a+<grd+regegd+g+<d+>d+d+g+d+r<c>aec>a<cefr1r1r1r1ra+r1r1r1r1r1>br8br8.<fa+rg+rd+d+r1r1r1r1r1r1r1r1r1r4<<d+8.rd+8r8>b8d+8>b8r<<d+r8>g+8r4b8r4fra+rfrd8r4.<gr>>b<br8>br<<a+32g+32g32f32d32a+32g+32f+32f32a32g32f32d+d>d+>fg+>f<ag+<cd+>a+g+<d+gd+>a+>fg+g+<d+>>g+<<g+>>g+<d+g+<d+>a<d+>d+>a<a<d+>ad+<<d+8.>a+b8<d+d+d+4<f4>c+>g+r48<c+24c+8rc+8c+>g+<c+>g+<fc+>g+8edr<c8rc8>>br8<d8rcr<<d96g96<d32r6r32d4<c8.>b8.<c8>>>g>db<dgrd>d>b<b<dg>>b<dgdec>cre<ce>>b<<f+>bd+>b<b<d+a>b<eb<ed>>eb<e>ebe<ed+>a+d+>ba<fa<dfd>a<da>dg<dfbec>b<<e>b<gbgeggd+>b<f+d+>b>bdb<bgd>b<d>bf+d<d>a+<dg>>a+<e<eee24a24<c24e24a24b24a24e24c24>ecea>aea<eba<d+g<<<d+>>>d+>bb<gr>a<a>>c+ea<c+eg>g<cegec>egcg<e>c>g<cg<cd+c<c>d+c>gd+c>g<<<c>d+c>g<cd+>g<b>b<f+b<ba>>b<ef+b<d+f+>be>b<e>beb<e>e<ge>b<e>be>b<<g+bg+bce>>a<gega<c>>a<a<cegege>f+>b<bf+df+ab<e64f+32.d>bf+<<<d32r32d32r32<d32r32d32r32>>>ec>gc<c>ce<c>c+eb<ec+<e>>ec+<b<d>aed>ad<ged+ef+<f+d+>b<d+>>egb<e>eb<<ggg24e24>b24gd>d>b<eb<d+>cd+<d+cd+c>g<<gd+g32r32>>g<c>c<ba+<e>ad>ad>a<da<f+dada<df+<d>>gdg<d>gede>a+<e<dgd>ebe>a<cfd+a<f>d+>a<cacad+<fc>acfdcd<c>adg+c>g+<fg+f>bg+<dgfefd>bgbgb<f>gg<de>cf<cd+g+d+c>g+<d+g+<c>d+fd+c>g+<a+<a+d>c<c>dg<df>a+dagfe>g<d+>bg+<d+>b<d+bg+f+>bg+<d+g+d+>bg+<f+d+>g+b<f+bd+b<d+>d+<f+d+>>b<d+g+<d+>>d+<d+cd+>a+g+<ca+<c>g+d+c>a+g+<g+<d+c>g+c<c>g+>g+<d+>g+<<d+c>a+g+fd+>a+f<<c+>>e<eaef+ab<c+>aea<c+>ae>ar1r8<<d+r1r1r1r8.c+rg+r2.rer1r1r1.r<<a8.g+8.e8f+3r1r1r6>>>f+r>bar8f+rar8.brab<gr8.>cra<c>g<cea+raar>adra<grf+rdref+f+ef+edr8>f+a<cerf+re>a<de<c>g<g>db<dgrd>d>b<b<dg>>b<dgdec>cre<ce>>b<<f+>bd+>b<b<d+a>b<eb<ed>>eb<e>ebe<ed+>a+d+>ba<fa<dfd>a<da>dg<dfbec>b<<e>b<gbgeggd+>b<f+d+>b>bdb<bgd>b<d>bf+d<d>a+<dg>>a+<e<eee24a24<c24e24a24b24a24e24c24>ecea>aea<eba<d+g<<<d+>>>d+>bb<gr>a<a>>c+ea<c+eg>g<cegec>egcg<e>c>g<cg<cd+c<c>d+c>gd+c>g<<<c>d+c>g<cd+>g<b>b<f+b<ba>>b<ef+b<d+f+>be>b<e>beb<e>e<ge>b<e>be>b<<g+bg+bce>>a<gega<c>>a<a<cegec>af+>b<bf+df+ab<e64f+32.d>bf+<<<d32r32d32r32<d32r32d32r32>>>ec>gc<c>ce<c>c+eb<ec+<e>>ec+<b<d>aed>ad<ged+ef+<f+d+>b<d+>>egb<e>eb<<ggg24e24>b24gd>d>b<eb<d+>cd+<d+cd+c>g<<gd+g32r32>>g<c>c<ba+<e>ad>ad>a<da<f+dada<df+<d>>d8rg8rgrg3r24gr<bec>accd+d+f+>b<b<d+>d+>b<aag<e>bg<ge>bgb>b<egbgec>b<fgb>gb<b<c+d+r48>b24b<g>b<gc>beg<ceg>cec<e>e<c>g<c>aef+f+d+>b<<d>brgdbrgrg+bg+bg<cg>gg<c>gr<ec+>g+ec+eg+ar1r1r1r1g24d24g24<f24r24>f24<c24r24>f+24<c+24>f+24c+24>g24<d24g24r24f24<c24r24>f+24f24f24r12g24<d24g24f+24g24f24<c24>>f+24g24<c+c+<d24g24d24>c24>f24<c24r24>f+24r24f+24<c+24r24>g24<d24g24r24f24c24>g24c24r24c+24f+24c+24<g24d24g24r24f24c24>f24c24>f24f+24<c+24f+24<g32<d+32>g32r<b32>g32r4.;l8r1r1r1o6f.f.ff2>a+.<d+16>a+<d+d+2>g+.<c.>g+b4..r16g4.rd+4.gd+.g.d+d+4g4<d+.c.cc4>g+4f.fr16<d+2^d+>g+2a4rg16r16a+.r1..r16d.c.c+16c+16d.c.c+d.c.c+d.c+.dd+a+16r.d+16r.a+16r.b4b16r2.r16>a+4r4<d<fr>a+a+r4<e4r4.>g+4r4<d+4r4d+r16d+r16d+d+4r4>d+4r4d+4r4d+4r4d+4r4f4r4f4r4g4r4d+4r4g+4r4f4r4f4r4e4r4<c4r4>b4r4>a+3r24a+3r24<a+4<g4r4d+2r1r1r1r1d+.d+4^d+16g4..r16d+2>>a2<a+4r4<d+4f4f.f+r16f+f4r4d.g.d+16r4c.dr2d+4.r>g4.gg2f+4.>f+<f+4.f+g4.gg4f+4f4.ag4ffg.g.gg2g4.rf2e.e.ee4eegr16gr16ga4c+4e.e.ee.e.ed+.d+.d+16r16d+2f+2f+2g1a4a4a4>a4<f+2f+4f+4g2g4g4a.a4^a16a.b.bb.b16bbb.b.bg.g.gg2<d2d4dd>g<d>>a+4<a+4<d4c.>>a.<ac2a.<c.>ag+.<c16>g+4g.g.<d>b4<dgc4>g+4<d+.g+.r>>g4<ga+a+<ddd>g+4.g+f+.g+4^g+16g+.g+16rg+b2a+2<c2d+2c3r6e2e3r6>a4.r1.r<e4.r2rc2.r4>e2^e6r3b2<d4..r16<g+4r4>f+4..r16>b4.r2r<c+2^c+6r3e4r2.r192>c4.r16..r96<<c4.rr32>a4.rr32e4.r16>>b.b4^b16b4.b<cc32r32c.c16r16c.c>g16<cd.g.ga.a.a16a16<a16r.f+f+<<f+32r16.>>>>d16<<<<d32re32>f+32r32>>>d16<g4.gg2f+4.>f+<f+4.f+g4.gg4f+4f4.ag4ffg.g.gg2g4.rf2e.e.ee4eegr16gr16ga4c+4e.e.ee.e.ed+.d+.d+16r16d+2f+2f+2g1a4a4a4>a4<f+2f+4f+4g2g4g4a.a4^a16a.b.bb.b16bbb.b.bg.g.gg2<d2^d.d4^d16d.g16dgg2>f+.f+.<ed+4..r16>b4.rg4.bg.b.gg4b4<g.e.ee4c4>a.ar16<g4.d4>b2b4rb16r16<d.r1r1r1r1r1r1r2....r4..;l8r1r1r1o5a+.<d+r16d+>a+.r4r16g.g.ga+.a+.d+d4.cd2d+.d+4^d+16c4cd+c+.>a+4^a+16a+4<d+4d+.g+.g+d+4d+4d.dr16d+2^d+d+.d+.d+d+4rd+16r16d+.d+.ef.d+.ef.d+.efrf+r>a+.g+.aa+.g+.aa+.g+.aa+.<c+.d>a+4..r.<g16r.g3r2r24<d+c>>g4r4g4r<eer4a+4r4.d+4r4b4r4f+r16f+r16f+f2>a+4r4b4r4<c4r4c4r4d4r4d4r4d+4r4>a+4r4<d+4r4d+4r4c+4r4c+4r4g+4r4g+4r4g+3r24g3r24g4<c+2c+2r1r1r1r1c.r16c4dd+3r24>a+.b.b<c2>g+2<<d+4>>a+4<c+.>g+.g+g+3r6a.<c.>aa.a.ar2<c2>d.d.dd.d4^d16e4.ed+4.d+e.e.ee4d+d+d.d.dd4dde.e.ed+2d.d.dd2c.c.cc4.ccr16cr16>g<c+4>a4<c.c.>bb.<c.cc.c.c16r16c4c4d2d+4.d+e.e.ee4e4ee4ee4e4d4d4d4d4e2e4e4g.g.ge.f+.f+g.g.gg2d+.d+.d+d+2g2<a.r16<dd>>eg16g16g16r.d4g4f.fr<a16ar4f>f.f4^f16f4..r16d.>b.r<gg>b16<d16r16>g+16<d+.r16>g+4<c.<c4^c16>d4>a+4a+<d16<<d.>>ff+.fr16d+4r16d+4^d+16f+4.d+f+4d+4g+2a+4a+4<c4>g+4g+2>b.<e.>b<ee16e16>b4<<d+1^d+.r2.r16>g+2^g+6r3g+2^g+6r3g+2.r4f+4..r16g+2<c+4..r16>b4..r16g+2b2a2...r16a2..r>>b2r64<<<<e4^e32r4c4r4r32>a..r.r64>b16rf+.f+f+4.>f+16r16<<c16>f16r32<g32g.r.g16r16g.g>>ar16<d.d16d16>a.<f+.f+16f+16dr24e24d24<d+4<c.>d16rf+16f+16>d.d.dd.d4^d16e4.ed+4.d+e.e.ee4d+d+d.d.dd4dde.e.ed+2d.d.dd2c.c.cc4.ccr16cr16>g<c+4>a4<c.c.>bb.<c.cc.c.c16r16c4c4d2d+4.d+e.e.ee4e4ee4ee4e4d4d4d4d4e2e4e4g.g.ge.f+.f+g.g.gg2d+.d+.d+d+2g2<a.r4r16>b.>b.a<<d.d.>gc.c.ef+2g.g4^g16e4egf.f4^f16f4g4g.<c.c>g4g4f+.f+r16g4.b4g.g.gg4rg16r16g.g.g+a.g.g+a.g.g+ara+rd.c.c+d.c.c+d6r48d+6r48ee6r48f.f+r1r1gr16g16.r2...r4.;l8r1r1r1o4a+.<d+r16d+>a+.<dr16dd+.d+.d+d+.d+.>b<f.f.>g+b2<c.cr16>gg4g4g.<d+.>gg4g4<c.d+.cc4c4>a+.a+.<c4c3r24c2c.c16r>g+16r16a+.a+.aa+.g+.aa+.g+.aa+4b4>a+4.ra+4.ra+4.ra+.<g+4^g+16d+4..r.<d+16r.d+3r2r24>g+gd4r4d4ra+a+r4<c4r4.c4r4d+.c.r>b.br16b<d+2>g4r4g4r4g+4r4a4r4a+4r4b4r4<c4r4>g4r4g+4r4a+4r4a+4r4a+4r4<d+.r4r16d+4r4d+3r24d+3r6rg+2g+2r1r1r1<<g.b.r2r>>g+.r16g+ra+2g.g+.g+d+4d+4<d+.d+32r32>b.b16>a+4a+4<g+.c+.c+c+4r4>a<d16d.dd.c.dr4d2.>b.b.gb.b4^b16<c>f+16f+16r<c>b4.ab.b.bb4a+aa.a.ab4<c4c+2c.c4^c16>b.b.ba+2a.a.aa4a4<d+r16d+r16d>a4e4g.ar16gg.ar16gg.g.gg4g4b2b4.d+e.b.bb2<c.c.cc4c4>b4b4b4b4<c4c4c+4c+4d.dr16dd.<d+.d+>e.e.ed.d.>b<c.c.cc2d2.>a.r16<de4e4.e4c.c.d+16d+.d+d+4d.c.>aar16<c4r16f.f.dfffeg+.d+16d+d+g+.<<c.r>>f.f16dddg4b>b.<d+.>b4b4.<d+.d+.>b<d+4>b4<d+4d+4g+2f4f4f2a.a.aaaa4<c+3r1.r6>e2r2c4.r2rc+2r2d4.re2ee4c+d+4..r16d+2f2f+2.r4>a2.r4<e1..r4>>b.<<e.ed+4d+d+>c.<f.fe.e.e>d.a.gd.a.aa4r4<<f+.>a4r16>b.b.gb.b4^b16<c>f+16f+16r<c>b4.ab.b.bb4a+aa.a.ab4<c4c+2c.c4^c16>b.b.ba+2a.a.aa4a4<d+r16d+r16d>a4e4g.ar16gg.ar16gg.g.gg4g4b2b4.d+e.b.bb2<c.c.cc4c4>b4b4b4b4<c4c4c+4c+4d.dr16dd.<d+.d+>e.e.ed.d.>b<c.c.cc2d2.r4>g.<g.g>g.<g.d+e.e.cd+2e.er16>bb4b4b.g.bb4b4<e.g.ee4e4d.d.e4eg+4e2e.e16ec16r16d.c.c+d.c.c+d.c.c+d4d+4>d4.rd4.rd.d+.e16r16e.f.f+16r16<dr16cr16c+dr16cr16c+dr16cr16c+dr16cr16c+dr16cr16c+dr16cr16c+d4..rr32;l16r1r1r1r1o5d+2.r8<a+<d+g8rg+8rg8>b<d+f8d+8ffr2>d+4r8a+<d+g8rg+8rg8>b<d+f8d+8cd>d+8.d+8r8.d+8rd8rcr>a+8r<f8rd+8.r4..c4r4c8c8r4a+8r1r1r1r1r1r1r1r1r1r1>g+32g+32rg+rgrg+6r48a+.r.d+d+g+8g.r32g+8ra+.r3r96a+rg.r.d8rdr<c6r48>b8r<c.r32>a+8..r8r32grg8rg8ra+8d+8.r8.d+rd4r8drd8rf8rd+rc+6r48d6r48e.r32d+6r48d+8rd+r>b8.b8rbra+2.r1r1r1r1r1r1r1r1r1r1r1r4<g8rd8.f+.r32e8.f+8.erg6r48f+8.grf+6r6r24f+.r32g8r>b6r48<f+re8.f+8rera8.f8rarg8.r8.<bbb6r48g8rera6r6r24d32d32rd6r48>d8.>b8a+8r<<g8.a8<c8.>b6r48g8a8.r8.>drd+.r.d+8rd.r32g8r8d8c8g6r48e8rf+re8.f+8rera6r48g6r48a.r32g8r8<d+8..r32d8..r8r32<drd+6r48>b6r48b.r32a6r48g6r48a.r32g6r12b8r8<c4r8>ed+e.r8r32<c8.r>b4r8dd32r32d4r4e8r8<cr>e8r8<c8r8c32r32c32r32>b8..r32a4g8.f+8.f+r4r<e4g4a8brc4..r1r>>d6r48d.r.d.r32<<<e8rd8r8.d8>>>g.r32a+.r32<<gr<f4c8c8f4r4>ar8<c8r8.g+8.r4rd4g+8g8f8.d8.r8c4r4>>>d+6r48d8rd+8<<<d4r4>>>f8re6r48fr<<<b4r4d+4r8>a+r<f+4r4f+4r4a+4r8>a+32r32a+32r32a+.r32a+.r32a+.r32a+32r32a+32r32<c4d+4r2>>>>b8.<e8.>b8<e8ee8.>b8r1r1r1r1r1r1r1r1r1r1r1r1b1<c1d2d2>a8r8<d8.rd8.d6r48d8g8rd8.f+.r32e8.f+8.erg6r48f+8.grf+8.r8.f+8g6r48>b6r48<f+re8.f+8rera8.f8rarg8.r8.ddg6r48e8r>br<d+6r6r24>b32b32rb6r48<d6r48>b8a+8r<d8.e8e6r48e6r48e8e8.r8.drd+8rd+6r48d8g6r12d8c.r32g6r48e8rf+re8.f+8rera8.g6r48a8g6r12c8..r32>b8..r8r32<erf+6r48f+6r48>b.r32<e6r48>b6r48<e.r32e6r12>b8.r<e3r24ccc8r8e8.rf+4r8>bb32r32b4r4<c8r8erc8r8e8r8e32r32e32r32e8.rd8.rd6r48d+8.d+8e4<<e4g4a8br>>d+2r2g8.r>a8.r<d6r48>a.r.<d8d2..<<dgb8r<c8r>b8d+ga8g8aar2>g4r8<dgb8r<c8r>b8d+ga8g8ef+>g8.g8r8.g8rf+8rerd8ra8rg8.r4..e4r4e8e8r4<d8r1r1r1r1r4.>gr8fr8gr2rgr8fr8grgr8fr8f+rgr8fr8gr>g8.r4.r32;;l16r1r1r1r8.o7d+8rd+8r>a+<cdd+fgg+r8>a+32r8..a+32r32a+32r8r32a+32r8..>a+<d+g8rg+8rg8>b<d+f8d+8ffd+r8d+r8d+32r32d+32r32d+4r8>a+<d+g8rg+8rg8>b<d+f8d+8cd<c32r8r32c32r8r32c32r32c32r32c32r8r32c32r1r4...cr8>d+r1r1r1r1r2r8.>a+32r32<d+32r32d+32r32>a+32r32<d+32r.<d96<d96r6r>>>a+32r32<d+32r32>b32r8..b32r32b32r32g32r32>b32r32<g32r32g+32r32<<g+96<c+96r6>>>b32r32<d+32r32>b32r32<d+32r8..>g32r32g32r32a+32r32d32r32a+32r.<<<c96d96r12r48>>f32r32d32r32>a+32r32<d+32r32>a+32r8..<c+32r32>a+32r32g32r32>a+32r32<<c+32r32>a+32r32<<g+96<c+96r12r48>>e32r32c+32r32g32r32f32r32d+32r32c+32r8r32>>g+32r32<<c32r32>g32r32d+32r32g32r.<g+32r.>d+32r32<g32r32>g32r32d+32r32g+32r.b32r32<b32r.>g+32r32<c+32r32d+32r32c+32r32d+32r32b32r32<a+48a48g+48g48f+48f48e48d+48d48c+32r8..<c48>>a+48<a+48>a+48<a+48>a+48>d+32r.a+32r32d+32r32a+192<g192>a+192<g64<g+32>g32<g32>d+32<d+32>>g32r32b32r32<d+32r32>b32r.d+192<d+192>d+192<d+192<d+48>d+48<d+48>d+48<d+32r96c48<c48>c48<c48>c48>c32r32d+32r32>g+32r32d+32r32d32r.<<g+192>>g192<<g+192>>g192<<g+192>>g192<<g+64r64g192>c192<g192>c192<g192>c192<g64r64d+192>d+192<d+192>d+192<d+192>d+192<d+64r64>c192<d192>c192<d192>c192<d192>d192<d192>d48g192<c192>g192<c192>g192<c192>d+192<c192>d+48f192a+192f192a+192f192a+64>b96<c96g192c+192g192c+192g192d192g192d+96e96f192c32r24r96a+48<a+48>f48<a+48d48>>a+192<a+192>a+192<a+192>a+192<f64r48>f192<d192>f192<d192>f192<d192r32>f192a+192f192a+192f192a+192r32d192>a+192<d192>a+192<d192>a+192r32<f32r.<<c32<cr32>>>f192b192f192b192f192b192r32f32r32b192<g192>b192<g192>b192<g192r32d32r32f192<d192>f192<d192>d48<d48>d48<d48>d48>g192<g192>g192<g192<g48>g48>a+192<<g192>>a+192<<g192>g48<a+48>c192a+192c192a+192<g+48>g+48<g+48>g+48<g48>>g192<g192>g192<g192<g48>g48>c192<<g192>>c192<<g192>g48<g48>>d+32r.a+48<a+48>a+48<a+48>a+48<a+48c+32r32>a+32r32<g32r32d+32r32c+32r32>a+32r32<d+48<d+48>d+48<d+32r32>>a+32r32d+32r32a+32r32d+32r32<d+192>g192<d+192>g192r24<d+48<d+32r96>f32<f32>f32<f32>>a+32r32g192<a+192>g192<a+192>g192<a+192r32d+32r32f32r32a+32r32.<c32r48r64>a+48<a+48>a+48<a+48>a+48>a+32r32<c+32r32>>a+192<<f192>>a+192<<f192>>a+192<<f192r32>c+32r32<<c+48<c+48>c+48<c+32>g64r64<g64r32c64r32>g64<g64r32.c64>>>a+32<<g64<g64>>a+32r64<<c64>>f32<g64<g64>>d32r32c+192<<c192>>c+192<<c192>>c+96r32<c48<c32rr96>>>d+32r32d+32r32<c32r32>d+32r32<d+32r24r96g48<g48>g+48<g+48>g+48<g+48>g+48<g+48>g+192>b192<g+192>b192r192b192r32<g+48<a48>a48<a192>>b192<<a192>>b192<a48<a48>a192>d+192<a192>d+192r24<a+192>g192<a+192>g192<<a+48>a+48<a+48>a+48<a+32r96>a+48<a+48>a+48<a+32r96>>a+192<g+192>a+192<g+192<g+48>g+48g+192<g+192>g+192<g+192>g+96r32>g+32r32d+32r32a+192<<a+192>>a+192<<a+192>>a+96r.<g48<g48>g48<g48>g32r96>a+192<<g192>>a+192<<g192>>a+96r32d+192<g192>d+192<g192>d+192r64<<g48>>g192<g192>g192<g192>g192<g192r32f32r32>g+32r32a+32r1r32>>dc+c>ba+ag+gf+fed+dc+r1r1r2r8<<<<d32r.>b32r32b<d32r32dgr<<<c>c4>>d+g+8<<d4g>>a+ra+<<d+8>>>bb<<ag+d+>g+<d+rcra+ad+c>a+32r32<f192<d192>f192<d192r8r96>>>a+32<a+.r32a+32r32a+32r32<ff+gg+aa+b<c>>g+<<c+32r.c+32r8r32c+32r.c192d192d+192e192f48e48d+48d32c+32c32>b32a+32a32g+32g32f+48f32e32d+48d48c+48c48>b32a+32a48g+32g32r24<d32r32>g32r32<d32r32d32r32d32r32>g32r32<e32f+32>g32r32a32r32<c32r32c32r32d32r32d32r1r32>g32r8r32<d32r32g48f+48f48<e48d+96d96c+96c96>b96a+96a96r.>e8<<e48d+96d96c+96c96>b48a+48a48g+48g48f+32f32r48>e32r32e32r8r32f+32r32f+32r8r32a32r32a32r8r32d+32r32d+32r8r32d+32r32d+32r8r32g32r32g32r8r32g32r32g32r32e32r32a32r32g32r32f+32r.f+32r32d+32r32f+32r32f32r.a32r32a32r8r32a32r32f32r32f32r32e32r32>b32<b32f32f32c32r.d32r.e32r8g32r8.<e32r32e32r32d+32r8r32d+32r.d+32r32f+32r.>>b32r.<g32r32f+32r8r32>b32r32b32r32a+32r.<<g32r32g32r8r32>d32r32d32r32>a32r.<e32r32e32r8r32g32r32a32r32<e32r.>a32r32a32r8r32a32r32<e32r32>g32r32a32r32g32r32a32r8r32<c32r.>a32r8..d32r32g32r32a32r32<c32r32e32r.e32r32e32r32g48f+48f48<e48d+96d96c+96c96>b96a+96a96r32e32r32e32r32e32r32<e48d+96d96c+96c96>b48a+48a48g+48g48f+32f32r48e32r.g32r.c32r32>b32r8r32<g32r32g32r8r32d+32r32d+32r8r32c32r.f+32r32>b32r32<f+32r32f+32r.>a32r32f+32r32f+32r8r32<d+32r32>b32r8r32>b32r32b32r32<a32r.a32r32<d32r8r32>a32r.b32r.e32r32e32r32e32r32g32r32a32r32b32r32e32r.<g32r32g32r8r32c32r32c32r32>e32r.<e32r32g32r8r32c32r32c32r32>a32r.<d32r32d32r32f+32r.>a32r32a32r32<<br>>>b32r32b32r8r32<d32r32d32r32e32r.<c32r32c32r8r32e32r32e32r32<<c8>>>e32r32e32r8r32<e32r32e32r32d32r32d32r32>b32r32<d32r8r32d32r32d32r32>d8rd+32r8r32d+32r32d+32r32e32r.<e32r32e32r32er>e32r32g32r32<g>e32r32g32r.g32r.<b32r.d+32r.g32r32g32r8r32g32r32g32r32g32r.ga+a+8g8d32r32a32r32<d32r32>g32r32<drd32r32d32r32>a32r32d32r32<d32r32>d32r32<d32r32>a32r32d32r32<d32r8r32>g32r32d32r32<d32r8..d32r32d32r32>d32r32d32r32<d32r.>g32r32g32r8..f32r32<c32r3r96>>a32r32a32r2.r32a32r4...a32r8r32g32r8r32a32r.b32r.g32r32g32r32<<d32r32>>g8rg+8rg+32r8r32g+32r32g+32r32g+8rg+r4a+8ra+r8a+32r32a+32r32a+8rgr4d+8r>br8b32r32<d+32r32d+.r32>b32r32<g+r4f+.r.d+r8b32r32b32r32<f+.r32f+32r32d+r4>>g+8.d+8d+d+32r32d+32r32g+8.d+32e32f8r8d+48e48f8r48d+32r32d+96e96f12^f48f.r32d+96e96f12^f48f.r32d+.r32f.r32<<<e4>c+32r32>b32r32>b32r32<e32r32d96d+96e12r48e32r32e32r32c+r32f+r1r1r1r1r1r1r1r1r1r1r1r1r32>>a<b64<f+64b32r32>b64<f+64b32r.b32r.b32r.b32r8b32rb32r8..cr8>a+32r.<f32r32c32r32>a+32r32<<c32r32>c32r32e32r32c32r8r32e32r.d32rf+32rd8rd32r32d32r8..a8ra32r.g+a32.a+32.b32.<c32.c+32.d32.d+32.eff+gg+^g+64a^a64a+^a+64b<c>>g8.d8r>g3r6<e32r32g32r8r32f+32r32f+32r8r32a32r32a32r8r32d+32r32d+32r8r32d+32r32d+32r8r32g32r32g32r8r32g32r32g32r32e32r32a32r32g32r32f+32r.f+32r32d+32r32f+32r32f32r.a32r32a32r8r32a32r32f32r32f32r32e32r32>b32<b32f32f32c32r.d32r.e32r8g32r8.<e32r32e32r32d+32r8r32d+32r.d+32r32f+32r.>>b32r.<g32r32f+32r8r32>b32r32b32r32a+32r.<g32r32g32r8r32d32r32d32r32>a32r.<e32r32e32r8r32g32r32a32r32<e32r.>a32r32a32r8r32a32r32<e32r32>g32r32a32r32g32r32a32r8r32<c32r.>a32r8..d32r32g32r32a32r32<c32r32e32r.e32r32e32r32g48f+48f48<e48d+96d96c+96c96>b96a+96a96r32e32r32e32r32e32r32<e48d+96d96c+96c96>b48a+48a48g+48g48f+32f32r48e32r.g32r.>d+32r32d+32r8r32d+32r32d+32r8r32d+32r32d+32r8r32<c32r32d+32r32f+32r32>b32r32<f+32r32f+32r.>a32r32f+32r32f+32r8r32<d+32r32>b32r8r32>b32r32b32r32<a32r.a32r32g32r8r32a32r.b32r.e32r32e32r32e32r32g32r32a32r32b32r32e32r.<g32r32g32r8r32c32r32c32r32>e32r.<e32r32g32r8r32c32r32c32r32>a32r.<d32r32d32r32f+32r.>d32r32a32r32<<br>>>b32r32b32r8r32<d32r32d32r32e32r.<c32r32c32r8r32e32r32e32r32<<c8>>>e32r32e32r8r32<e32r32e32r32d32r32d32r32>b32r32b32r8r32<d32r32d32r32>d8rd+32r8r32d+32r32d+32r32e32r.<e32r32e32r32er>e32r32g32r32<g>e32r32g32r.g32r.<b32r.>d+32r.g32r32g32r8r32<g32r32g32r32g32r.ga+a+8g8<d32r32>g32r32g32r32g32r32<drd32r32d32r32>a8.d8.g32r32g32r8r32d32r8..d32r32d32r8r32d32r8..>dgb8r<c8r>b8d+ga8g8aagr8gr8g32r32g32r32g4r8dgb8r<c8r>b8d+ga8g8ef+<e32r8r32e32r8r32e32r32e32r32e32r8r32e32r1r4...er8>gr1r1r1r1r4..<g32r32g32r32<f+32f32e32d+32c+32c32>b32a+32a32g+32g32f+32f32d+32d32c+32g32r8r32f32r8r32g32r32g32r32g32r8r32f32r8r32f+32r.g32r8r32f32r8r32g32r32g32r32>g32g+32a32a+32b<cc+dd+eff+gg+aa+b<cr2r;l16r1r1r1r8.o7d+8rd+8r8.d8rd8>a+2..a+<d+g8rg+8rg8>b<d+f8d+8f8a+r8a+2^a+8.>a+<d+g8rg+8rg8>b<d+f8d+8r8c2^c6r3d8rd8.d+2r8c4..rd+8r8f8g+aa+4.>e8dr8d+8rerf8rd+8re8f8d+c+8rc+8d8rc8rc+8d8rc8rc+8d8>a+<c8rd+rd8d+2^d+6r1r1r1r1r4.r3<f+.r.f+.r.f+8r2>>gr8a+>a+r<a+r<d+8rd+r4c8rd+r2.d8r4.c8r4.d+8c>a+r2.<c8d+4.d+2c+8rc+r8c+r2rc8rcr8cr>b8rbr4a+8a+g+r8a+3r1r1r2.r24<<a+8a8g+8g8f2d1^d4f8d8<c4c8.rd4>gr8.d+4^d+g+d+>g+<d+4>a+gd+8<f2.r4c+r8c+8.c+8c+8.r4rdr8d8.d8r8.c+8.c+8r1>g4.<a8g8.<g8a>c8c8.>b8.<c8>b4r8f+8<g4.arg8.r4ra8r8eeae>b4r4<e8rd8re8f+f+d+d+f+f+>a8<d4a8a8b8gg8g8>a<a8.e4rc4..rc8.c8r>g8a8r8<<d8c8>e8.e8.e4.e8e8g8ed+8rg8g4g8a8b4a8<d8d+8.>b8.f+8a8.g4^gb2a4e8e8<c8>c8e8<c8>d2b4<d4>g2.b8<c8>d2f+gr>f+3r12r48<d8e4g4a8brd+8g4.d+8g8a8g8>>>>a2<a4..r<<<d8g8d4g4d4f8f4.f2e8rc4^cc8.f8.f8f4.d2^d8>g+g8a+8.<c8>a+8.<c8.d8d8.c8.d8f8.e8.f8f+8.f8.d+8d+8.g+8.a+8a+4^a+g+8.f+4^f+f64e64d+8^d+32a+4g+8a+2^a+8<c8r>>g+8.g+8a+a+8r<f8.rc+1r8>>a8<c8.cc8.cc8cc2.r1r1r1r1r4..>>a4.b4..rb2g2r8<c+2..>b8r1r1r2..<<<<<cr8>>>f+4^f+>b3r24br<<<<cr8>>f8rf8<<cr8>>e8rerdr32<cr32>dradra<crf+r8f+rd.r8r32c.r8r32c.r.>b.r.<c.r32>>g4.<a8g8.<g8a>c8c8.>b8.<c8>b4r8f+8<g4.arg8.r4ra8r8eeae>b4<c8d8e8rd8re8f+f+d+d+f+f+>a8<d8f+8a8a8a+8gg8ga>a<a8.e4rec>arb4<c8.>b8rg8a8r8<<d8c8>e8.e8.e8ee8.e8e8g8ed+8rg8g4g8a8b4a8<d8d+8.>b8.f+8a8.g4^gb2a4e8e8<c8>c8e8<c8>d2b4<d4>g2.b8<c8>d2f+gr>f+4^f+er<d8e4g4a8brd+8g4.d+8g8g8g8>>>>a2...r<<<<d2..dgb8r<c8r>b8d+ga8g8a8<dr8d2^d8.>dgb8r<c8r>b8d+ga8g8r8e2^e6r3f+8rf+8.g2r8e4..rg8r8a8<cc+d4.>>g+8f+r8g8rg+ra8rg8rg+8a8gf8rf8d8rc8rc+8d8rc8rc+8d8rd+8re8e8rf8rf+8>g8rf8rf+8g8rf8rf+8g8rf8rf+8g8rf8rf+8g8rf8rf+8g8rf8rf+8<g4r3r96;";
//本来はテンポt166
/////////////////////////////関数

//HD43517へ書き込み
void hd43517_write(unsigned char cs, unsigned char adr, unsigned char data);

//HD43517のレジスタビットへ値をセット
void hd43517_dataset(unsigned char cs, unsigned char adr, unsigned char reg, unsigned char data);
//HD43517のレジスタビットへ値をセット(アドレスと内部アドレス統合)
void hd43517_regset(unsigned char cs, unsigned char reg, unsigned char data);
//hd43517パラメータセット
void hd43517_param_set_all(unsigned char ch, int inst);


//音程のセット
void ptc_set(unsigned char ch, unsigned int notenum);
//メインの音量とパンのセット
void main_vel_pan_set(unsigned char vel, unsigned char pan);
//チャンネルの音量とパンのセット
void vel_pan_set(unsigned char ch, unsigned char vel, unsigned char pan);
//チャンネルノートオン
void note_on(unsigned char ch);
//チャンネルノートオフ
void note_off(unsigned char ch);
//音色セット
void inst_set(unsigned char ch, unsigned char inst);

// MML演奏用関数/////////////////////////////////////
//簡易MMLデータより音を鳴らす
void mml_play(unsigned char *play_data, unsigned int len);
//簡易mmlデータ1バイト取得
int get_mml_data(unsigned char *mml_data, unsigned int len, unsigned int pos);
// mmlコマンド1つ取得
void get_mml_com(unsigned char *mml_data, unsigned int len, int *pos, int *ret_data);
// mmlデータから数値を返す
int get_mml_num(unsigned char *mml_data, unsigned int len, int *pos, int *dp_num);
// mmlデータ配列内のトラック数をカウントする。(「;」の数)
int get_mml_tr_num(unsigned char *mml_data, unsigned int len);
// mmlデータ配列内のトラック先頭位置配列に、トラック数分の先頭位置を入れる
int get_mml_tr_pos(unsigned char *mml_data, unsigned int len, unsigned int tr_max, int *pos_tr);


//タイマー割り込み~~~~~~~~~~~~~~~~~~~~~~~~~
ISR(TIMER1_COMPA_vect)
{
  // MML演奏用
  if (mml_time_cnt_en == 1)
  {
    // MML演奏用時間カウント有効の時、カウント
    mml_time_ms++; // 2msごとのカウント
    // mml_time = mml_time_ms * (9*2*8192.0 * tempo / (120.0 * 2000.0));//下式と等価
    mml_time = mml_time_ms * (3 * 128.0 * tempo / 625.0); // msをMMLカウンタ値に変換(トラックごとの演奏ずれをできるだけ抑制するため)
  }
}

//セットアップ
void setup()
{
  //デバッグメッセージ用
  Serial.begin(SERIALSPEED);
  //ポート設定
  // out_put
  DDRD = 0xFC;  // pin2~pin7_HD43517:D2~D7
  PORTD = 0xFF; // pin2~pin7_HD43517:D2~D7
 
  DDRB = 0x3B;  // pin8~pin9_HD43517:D0~D1   pin11~pin13_HD43517:~WE, A0, ~RST
  PORTB = 0x3B; // pin8~pin9_HD43517:D0~D1   pin11~pin13_HD43517:~WE, A0, ~RST

  DDRC = 0x0F;  // pin14~pin17_HD43517:CS0 ~ CS3
  PORTC = 0x0F; // pin14~pin17_HD43517:CS0 ~ CS3

  //リセット
  PORTB &= ~0x20; // pin13_HD43517: ~RST = L
  delay(100);
  PORTB |= 0x20; // pin13_HD43517: ~RST = H
  delay(100);
 
  // HD43517キーオフ
  for (int i = 0; i < CH; i++)
  {
    note_off(i);
  }
 

  //タイマー割り込み設定Timer1
 
  TCCR1A = 0b00000000;  //CTCモード
  TCCR1B = 0b00001001;  //CTCモード,分周なし,タイマー割り込み開始
  TIMSK1 = 0b00000010;  //コンペアマッチAの割り込みを設定
  OCR1A = (unsigned int)((float)((2000 * (XTAL / 1000000.0)) - 1));// 2000μs毎
  sei();//割り込みを許可
 
}

void loop()
{
  int inst_c=16;//音色
  for (int ch = 0; ch < CH; ch++)
  {
    hd43517_param_set_all(ch, inst_c);//パラメータセット
  }
  while (1)
  {
    // MML演奏
    mml_play(mml_data, mml_data_len);
    delay(20);

    for (int ch = 0; ch < CH; ch++)
    {
      note_off(ch);
    }
   
    inst_c++;
    if(inst_c >= 22){
      inst_c=0;
    }
    for (int ch = 0; ch < CH; ch++)
    {
      hd43517_param_set_all(ch, inst_c);
    }
  }
}
//HD43517へ書き込み
void hd43517_write(unsigned char cs, unsigned char adr, unsigned char data)
{
  PORTB |= 0x10; // pin13_HD43517: A0 = H
  PORTB |= 0x08; // pin11_HD43517: ~WR = H
 
  //チップセレクト
  PORTC = (PORTC & 0xF0) | (0x0F & (1 << cs)); // pin14~pin17_HD43517:CS0 ~ CS3
 
  //アドレスのセット
  PORTB &= ~0x10; // pin13_HD43517: A0 = L
  delayMicroseconds(1);
  PORTB = (PORTB & 0xFC) | (0x03 & adr); // pin9,8_HD43517:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & adr); // pin7~2_HD43517:D7~D2
 
  PORTB &= ~0x08; // pin11_HD43517: ~WR = L
  delayMicroseconds(1);
  PORTB |= 0x08; // pin11_HD43517: ~WR = H

  delayMicroseconds(7);
  //アドレスのセット2回目
  PORTB &= ~0x08; // pin11_HD43517: ~WR = L
  delayMicroseconds(1);
  PORTB |= 0x08; // pin11_HD43517: ~WR = H
  delayMicroseconds(1);
  PORTB |= 0x10; // pin13_HD43517: A0 = H

  //データのセット
  delayMicroseconds(20);

  PORTB = (PORTB & 0xFC) | (0x03 & data); // pin9,8_HD43517:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & data); // pin7~2_HD43517:D7~D2
 
  PORTB &= ~0x08; // pin11_HD43517: ~WR = L
  delayMicroseconds(1);
  PORTB |= 0x08; // pin11_HD43517: ~WR = H

  //データのセット2回目
  delayMicroseconds(7);

  PORTB &= ~0x08; // pin11_HD43517: ~WR = L
  delayMicroseconds(1);
  PORTB |= 0x08; // pin11_HD43517: ~WR = H
 
 
  delayMicroseconds(200);
}

//HD43517のレジスタビットへ値をセット
//csとアドレスチップセレクトを統合
//基本的にこの関数を介してHD43517を操作
void hd43517_dataset(unsigned char cs, unsigned char adr, unsigned char reg, unsigned char data)
{
  //csは4つを1ブロックとする
  //4つのHD43517のcsはアドレスbit6~bit3で指定
  //adrは0x0~0x7までを指定
  //regは0x0~0xFまでを指定
  //hd43517_cs_conv[0x03&cs]は(~(1<<(3+(0x03&cs))) & 0x78)の計算
  if(adr>=0x04)
  {
    //adr>=0x04の場合、データ幅は8bit
    hd43517_write(cs>>2, hd43517_cs_conv[0x03&cs] | (0x07 & adr), data);
  }
  else if(adr>=0x02)
  {
    //0x04>adr>=0x02の場合、データ幅は4bit
    hd43517_write(cs>>2, hd43517_cs_conv[0x03&cs] | (0x07 & adr), (reg<<4) | (0x0F&data));
  }
  else
  {
    //0x02>adr>=0x00の場合、データ幅は7bit
    hd43517_write(cs>>2, hd43517_cs_conv[0x07&cs] | (0x07 & adr), (reg<<4) | (0x3F&data));
  }
}

//HD43517のレジスタビットへ値をセット(アドレスと内部アドレス統合)
//csとアドレスチップセレクトを統合
//defineで定義したレジスタ名で指定してレジスタを操作
//基本的にこの関数を介してHD43517を操作
void hd43517_regset(unsigned char cs, unsigned char reg, unsigned char data)
{
 
  //csは4つを1ブロックとする
  //4つのHD43517のcsはアドレスbit6~bit3で指定
  //reg上位4bitはアドレスで0x0~0x7までを指定
  //reg下位4bitは内部アドレスで0x0~0xFまでを指定
  //hd43517_cs_conv[0x03&cs]は(~(1<<(3+(0x03&cs))) & 0x78)の計算
  if(reg>=0x40)
  {
    //reg>=0x40の場合、データ幅は8bit
    hd43517_write(cs>>2, hd43517_cs_conv[0x03&cs] | (0x07 & (reg>>4)), data);
  }
  else if(reg>=0x20)
  {
    //0x40>reg>=0x20の場合、データ幅は4bit
    hd43517_write(cs>>2, hd43517_cs_conv[0x03&cs] | (0x07 & (reg>>4)), (reg<<4) | (0x0F&data));
  }
  else
  {
    //0x20>reg>=0x00の場合、データ幅は7bit
    hd43517_write(cs>>2, hd43517_cs_conv[0x03&cs] | (0x07 & (reg>>4)), (reg<<4) | (0x7F&data));

  }
}




//hd43517パラメータセット
void hd43517_param_set_all(unsigned char ch, int inst)
{
  //入力データ構造////////////////////////////////
  //CWM, CWSW, reserve, reserve, reserve,
  //SV-W1-L, SV-W2-L, SV-W3-L, SV-W4-L, SV-W5-L,
  //AL-W1-L, AL-W2-L, AL-W3-L, AL-W4-L, AL-W5-L,
  //AR, DR, SL, RR, reserve,
  //W1-DLY, W2-DLY, W3-DLY, W4-DLY, W5-DLY,
  //TL, PAN, PTC, reserve, reserve,
  //reserve, reserve
  ////////////////////////////////////////////////

  //CWM, CWSW, reserve, reserve, reserve,
  hd43517_regset(ch>>2, COMP_W_MODE, pgm_read_byte_near((int)(hd43517_parameter[inst] + 0)));
  hd43517_main_cwm[ch] = 0x03 & pgm_read_byte_near((int)(hd43517_parameter[inst] + 0));
  hd43517_regset(ch>>2, COMP_W_SW, pgm_read_byte_near((int)(hd43517_parameter[inst] + 1))<<2 | 0x00);//ch0
  hd43517_regset(ch>>2, COMP_W_SW, pgm_read_byte_near((int)(hd43517_parameter[inst] + 1))<<2 | 0x01);//ch1
  hd43517_regset(ch>>2, COMP_W_SW, pgm_read_byte_near((int)(hd43517_parameter[inst] + 1))<<2 | 0x02);//ch2
  hd43517_regset(ch>>2, COMP_W_SW, pgm_read_byte_near((int)(hd43517_parameter[inst] + 1))<<2 | 0x03);//ch3
  //SV-W1-L, SV-W2-L, SV-W3-L, SV-W4-L, SV-W5-L,
  hd43517_regset(ch>>2, SV_W1_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 5)));
  hd43517_regset(ch>>2, SV_W2_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 6)));
  hd43517_regset(ch>>2, SV_W3_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 7)));
  hd43517_regset(ch>>2, SV_W4_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 8)));
  hd43517_regset(ch>>2, SV_W5_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 9)));

  //AL-W1-L, AL-W2-L, AL-W3-L, AL-W4-L, AL-W5-L,
  hd43517_regset(ch>>2, AL_W1_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 10)));
  hd43517_regset(ch>>2, AL_W2_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 11)));
  hd43517_regset(ch>>2, AL_W3_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 12)));
  hd43517_regset(ch>>2, AL_W4_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 13)));
  hd43517_regset(ch>>2, AL_W5_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 14)));

  //AR, DR, SL, RR, reserve,
  hd43517_regset(ch>>2, AR, pgm_read_byte_near((int)(hd43517_parameter[inst] + 15)));
  hd43517_regset(ch>>2, DR, pgm_read_byte_near((int)(hd43517_parameter[inst] + 16)));
  hd43517_regset(ch>>2, SL, pgm_read_byte_near((int)(hd43517_parameter[inst] + 17)));
  hd43517_regset(ch>>2, RR, pgm_read_byte_near((int)(hd43517_parameter[inst] + 18)));

  //W1-DLY, W2-DLY, W3-DLY, W4-DLY, W5-DLY,
  hd43517_regset(ch>>2, W1_DLY, pgm_read_byte_near((int)(hd43517_parameter[inst] + 20)));
  hd43517_regset(ch>>2, W2_DLY, pgm_read_byte_near((int)(hd43517_parameter[inst] + 21)));
  hd43517_regset(ch>>2, W3_DLY, pgm_read_byte_near((int)(hd43517_parameter[inst] + 22)));
  hd43517_regset(ch>>2, W4_DLY, pgm_read_byte_near((int)(hd43517_parameter[inst] + 23)));
  hd43517_regset(ch>>2, W5_DLY, pgm_read_byte_near((int)(hd43517_parameter[inst] + 24)));

  //TL, PAN, PTC, reserve, reserve,
  hd43517_main_vol[ch]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 25));
  hd43517_main_pan[ch]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 26));
  hd43517_main_ptc[ch]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 27));

}

//音程のセット
void ptc_set(unsigned char ch, unsigned int notenum)
{
  unsigned char cs_temp = ch >> 2;
  unsigned char ch_temp = ch & 0x03;
  int ch_notenum = notenum + hd43517_main_ptc[ch]-76 + hd43517_oct_conv[hd43517_main_cwm[ch]]*12;
  while(ch_notenum<0){
    ch_notenum+=12;
  }
  while(ch_notenum>=128){
    ch_notenum-=12;
  }
  unsigned int tone = ch_notenum % 12;
  unsigned int oct = ch_notenum / 12;
  unsigned char inst =0;
 
  if (ch != 9)
  { //ドラムチャンネル以外
 
    hd43517_regset(cs_temp, PTC, (unsigned char)((unsigned int)0x00FF & hd43517_note_conv[hd43517_main_cwm[ch]][tone]));
    hd43517_regset(cs_temp, OCT, (((unsigned char)oct<<4) & 0x70) | (((unsigned int)hd43517_note_conv[hd43517_main_cwm[ch]][tone]>>6)&0x0C) | ch_temp);
 
  }
  else
  {
    //ドラムチャンネル
 
  }
}

//メインの音量とパンのセット
void main_vel_pan_set(unsigned char vel, unsigned char pan)
{
  //何もしません

}

//チャンネルの音量とパンのセット
void vel_pan_set(unsigned char ch, unsigned char vel, unsigned char pan)
{
  //HD43517はチャンネル音量設定ができません
 
}

//チャンネルノートオン
void note_on(unsigned char ch)
{
  unsigned char cs_temp = ch >> 2;
  unsigned char ch_temp = ch & 0x03;
  if (ch != 9)
  {
    //ドラムチャンネル以外
    //hd43517_regset(cs_temp, KEY_ON, 0x08 | ch_temp);//リリース有効
    hd43517_regset(cs_temp, KEY_ON, 0x00 | ch_temp);//リリース無効
  }
  else
  {
    //ドラムチャンネルはなし
  }
}

//チャンネルノートオフ
void note_off(unsigned char ch)
{
  unsigned char cs_temp = ch >> 2;
  unsigned char ch_temp = ch & 0x03;
  if (ch != 9)
  { //ドラムチャンネル以外
    hd43517_regset(cs_temp, KEY_OFF, 0x08 | ch_temp);
  }
  else
  {
    //ドラムチャンネルはなし
  }
}

//音色セット
void inst_set(unsigned char ch, unsigned char inst)
{
  hd43517_param_set_all(ch , inst);
}

// MML演奏用関数/////////////////////////////////////

//簡易MMLデータより音を鳴らす
void mml_play(unsigned char *play_data, unsigned int len)
{

  int mml_com[4] = {0, 0, 0, 0}; // mmlコマンド取得用[コード, 数値1, 時間, 数値2(音長表現のドットの数)]

  int mml_st[MML_MAX_TR];          // mml演奏状態(0でmml読み取り終了、1でmml読み取り中)、チャンネルごとに保存
  char mml_tie[MML_MAX_TR];        //タイ有効無効フラグ[^]
  char mml_oct[MML_MAX_TR];        //オクターブ[o]
  char mml_def_dly[MML_MAX_TR];    //デフォルト音調[l]
  char mml_pan[MML_MAX_TR];        //パン[p]
  char mml_vel[MML_MAX_TR];        //ベロシティ[v]
  double mml_next_tim[MML_MAX_TR]; //次のMMLコマンド実行時間(絶対時間)
  int mml_st_sum = 0;              //トラック演奏状態チェック用(フラグを加算して0になったらすべてのトラックの演奏が終了)

  ////////////////トラック数の取得と各トラック先頭位置の取得//////
  unsigned int tr_pos[MML_MAX_TR];                                         //トラック先頭位置保存用(先頭位置)
  int tr_num = get_mml_tr_pos(mml_data, mml_data_len, MML_MAX_TR, tr_pos); //各トラック先頭位置をtr_posに入れ、トラック数を返す

  //トラックごとの配列初期設定
  for (int mml_setup = 0; mml_setup < tr_num; mml_setup++)
  {
    mml_st[mml_setup] = 1;                                          // mml演奏状態フラグ(0でmml読み取り終了、1でmml読み取り中)
    mml_tie[mml_setup] = 0;                                         //タイ状態有効無効フラグ
    mml_oct[mml_setup] = 4;                                         //オクターブ
    mml_def_dly[mml_setup] = 4;                                     //デフォルト音調(四分音符)
    mml_next_tim[mml_setup] = 0;                                    //最初のMMLコマンドは無条件で読み取る。
    mml_pan[mml_setup] = 64;                                        //パン[p]
    mml_vel[mml_setup] = 60;                                        //ベロシティ[v]
    vel_pan_set(mml_setup, mml_vel[mml_setup], mml_pan[mml_setup]); //パンと音量の仮設定
  }
  // Serial.print("tr_num:");//デバッグ用
  // Serial.print(tr_num);//デバッグ用
  // Serial.print("\r\n");//デバッグ用

  mml_time_ms = 0;     // MML演奏用時間msカウンタ初期化
  mml_time_cnt_en = 1; // MML演奏用時間カウント_開始
  while (1)
  { //すべてのトラックが演奏終了のとき、mml演奏関数終了

    mml_st_sum = 0; //トラック演奏状態チェック用初期化
    //トラックごと処理

    for (int mml_st_tr = 0; mml_st_tr < tr_num; mml_st_tr++)
    {
      // Serial.print("mml_st[mml_st_tr]:");//デバッグ用
      // Serial.print(mml_st[mml_st_tr]);//デバッグ用
      // Serial.print("\r\n");//デバッグ用
      if (mml_st[mml_st_tr] == 0)
      { //演奏が終了したトラックは無視
        continue;
      }

      // Serial.print("mml_time:");//デバッグ用
      // Serial.print(mml_time);//デバッグ用
      // Serial.print("mml_next_tim[mml_st_tr]:");//デバッグ用
      // Serial.print(mml_next_tim[mml_st_tr]);//デバッグ用
      // Serial.print("\r\n");//デバッグ用

      if ((unsigned long)mml_next_tim[mml_st_tr] <= (unsigned long)mml_time)
      { //次回読み取り時間カウンタが時間カウンタの値以上になったらMMLコードを読み取る。
        ////////////////////////////

        // mmlコマンド取得(pos自動インクリメント)
        get_mml_com(play_data, len, (tr_pos + mml_st_tr), mml_com); //返しは、[コード, 数値1, 時間, 数値2(音長表現のドットの数)]の配列ポインタ
        if (mml_com[2] == -2)
        { // MMLの音調が省略されていたらデフォルト音長をセット
          mml_com[2] = mml_def_dly[mml_st_tr];
        }
        // MMLコマンド表示(多少不安定になります)//デバッグ用
        /*Serial.print("ch:");
        Serial.print(mml_st_tr);
        Serial.print("_pos:");
        Serial.print(tr_pos[mml_st_tr]);
        Serial.print("_");
        Serial.write(mml_com[0]);
        Serial.print(mml_com[1]);
        Serial.print("_dlytim:");
        Serial.print(mml_com[2]);
        Serial.print("_dot:");
        Serial.print(mml_com[3]);
        Serial.print("\r\n");*/

        switch (mml_com[0])
        {
        case 'n': //ノートオン
          if (mml_tie[mml_st_tr] == 0)
          {
            note_off(mml_st_tr);
            ptc_set(mml_st_tr, mml_com[1] +((mml_oct[mml_st_tr] + def_oct[mml_st_tr])*12) + def_key);//チャンネル, 音程
            note_on(mml_st_tr);//ノートオン
          }
          mml_tie[mml_st_tr] = 0; //タイ無効
          break;
        case 'r': //ノートオフ
          note_off(mml_st_tr);
          break;
        case 'o': //オクターブセット
          mml_oct[mml_st_tr] = mml_com[1];
          break;
        case '>': //オクターブ下げ
          mml_oct[mml_st_tr]--;
          break;
        case '<': //オクターブ上げ
          mml_oct[mml_st_tr]++;
          break;
        case '^':                 //タイ
          mml_tie[mml_st_tr] = 1; //タイ有効
          break;
        case 'l':                              //デフォルト音長
          mml_def_dly[mml_st_tr] = mml_com[1]; //音長
          break;
        case 'v': //ベロシティ
          mml_vel[mml_st_tr] = mml_com[1];
          vel_pan_set(mml_st_tr, mml_vel[mml_st_tr], mml_pan[mml_st_tr]); //チャンネル, 音量, パン
          break;
        case 'p': //パン
          mml_pan[mml_st_tr] = mml_com[1];
          vel_pan_set(mml_st_tr, mml_vel[mml_st_tr], mml_pan[mml_st_tr]); //チャンネル, 音量, パン
          break;
        case 't': //テンポ
          tempo = mml_com[1];
          break;
        case '@': //音色変更
          //inst_set(mml_st_tr, mml_com[1]);
          break;
        case '\n':                //無命令、数字のみの場合(タイ)
          mml_tie[mml_st_tr] = 0; //タイ無効
          break;
        case ';':                //トラック終端
          mml_st[mml_st_tr] = 0; //現在のトラックは演奏終了
          break;
        default:
          break;
        }

        //時間計算
        if (mml_com[2] == 0)
        {
          //待ち時間なし
        }
        else
        {
          // delay(500);
          //待ち時間計算(ms)
          double dly_tim_dot_sum = 0;
          for (int d_time = 0; d_time <= mml_com[3]; d_time++) // mmlのドットの数分ディレイを入れる
          {
            dly_tim_dot_sum += (double)(9.0 * 8192.0 / (mml_com[2] * pow(2, d_time)));
            // delay((int)((2000.0 / (mml_com[2]*pow(2, d_time))) * (120.0 / tempo)));
          }
          //次回のMMLコマンド実行時間を加算する
          mml_next_tim[mml_st_tr] += dly_tim_dot_sum;
        }
        ////////////////////////////
      }

      mml_st_sum += mml_st[mml_st_tr]; //トラック演奏状態チェック用(フラグを加算して0になったらすべてのトラックの演奏が終了)
     
      // Serial.print("mml_st_sum:");//デバッグ用
      // Serial.print(mml_st_sum);//デバッグ用
      // Serial.print("\r\n");//デバッグ用
    }
    //トラック演奏状態チェック
    if (mml_st_sum == 0)
    {
      //すべてのトラックが演奏終了のとき、mml演奏関数終了
      break; // mml演奏関数終了
    }
  }
  mml_time_cnt_en = 0; // MML演奏用時間カウント_停止
}
//簡易mmlデータ1バイト取得
int get_mml_data(unsigned char *mml_data, unsigned int len, unsigned int pos)
{
  // posは0~(len - 1)までを指定
  //データ範囲外で -1 を返す。

  if (pos >= len)
  {
    return -1;
  }
  // return mml_data[pos];    //MMLデータをSRAMに配置した時
  return pgm_read_byte_near(mml_data + pos); // MMLデータをPROGMEMに配置した時
}

// mmlコマンド1つ取得
//返しは、[コード, 数値1, 時間, 数値2(音長表現のドットの数)]の配列ポインタ
void get_mml_com(unsigned char *mml_data, unsigned int len, int *pos, int *ret_data)
{
  unsigned int pos_temp = *pos; //読み取り位置を記憶
  unsigned char temp_get_mml_byte;

  int ret_code = 0;
  int ret_num1 = 0;
  int ret_tim = 0;
  int ret_num2 = 0;

  switch (get_mml_data(mml_data, len, pos_temp))
  {
  case '0': //数字の場合、音長のみとして扱う
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
    ret_code = (int)'\n';
    ret_tim = get_mml_num(mml_data, len, &pos_temp, &ret_num2); // mmlデータ配列, mmlデータ配列の長さ, 読み取り位置のポインタ, ドットの数戻り値用ポインタ
    break;
  case 'b': //
    ret_num1 += 2;
  case 'a': //
    ret_num1 += 2;
  case 'g': //
    ret_num1 += 2;
  case 'f': //
    ret_num1 += 1;
  case 'e': //
    ret_num1 += 2;
  case 'd': //
    ret_num1 += 2;
  case 'c': //
    if (get_mml_data(mml_data, len, pos_temp + 1) == '+')
    {
      pos_temp++;
      ret_num1 += 1; //半音上げ
    }
    else if (get_mml_data(mml_data, len, pos_temp + 1) == '-')
    {
      pos_temp++;
      ret_num1 -= 1; //半音下げ
    }
    pos_temp++;
    ret_code = 'n';
    ret_tim = get_mml_num(mml_data, len, &pos_temp, &ret_num2); // mmlデータ配列, mmlデータ配列の長さ, 読み取り位置のポインタ, ドットの数戻り値用ポインタ
    if (ret_tim == 0)
    { //音長が省略されていたら
      ret_tim = -2;
    }
    else if (ret_tim == -3)
    { //音長が0だった場合、和音(ドラムのみ)
      ret_tim = 0;
    }
    break;
  case 'r': //ノートオフ
    ret_code = 'r';
    pos_temp++;
    ret_tim = get_mml_num(mml_data, len, &pos_temp, &ret_num2); // mmlデータ配列, mmlデータ配列の長さ, 読み取り位置のポインタ, ドットの数戻り値用ポインタ
    if (ret_tim == 0)
    { //音長が省略されていたら
      ret_tim = -2;
    }
    break;
  case '<': //
    ret_code = (int)'<';
    pos_temp++;
    break;
  case '>': //
    ret_code = (int)'>';
    pos_temp++;
    break;
  case '^': //タイ
    ret_code = (int)'^';
    pos_temp++;
    break;
  case 'o': //音程
    ret_code = (int)'o';
    pos_temp++;
    ret_num1 = get_mml_num(mml_data, len, &pos_temp, &ret_num2); // mmlデータ配列, mmlデータ配列の長さ, 読み取り位置のポインタ, ドットの数戻り値用ポインタ
    break;
  case 'v': //ベロシティ
    ret_code = (int)'v';
    pos_temp++;
    ret_num1 = get_mml_num(mml_data, len, &pos_temp, &ret_num2); // mmlデータ配列, mmlデータ配列の長さ, 読み取り位置のポインタ, ドットの数戻り値用ポインタ
    break;
  case 'p': //ベロシティ
    ret_code = (int)'p';
    pos_temp++;
    ret_num1 = get_mml_num(mml_data, len, &pos_temp, &ret_num2); // mmlデータ配列, mmlデータ配列の長さ, 読み取り位置のポインタ, ドットの数戻り値用ポインタ
    break;
  case 'l': //デフォルト音長
    ret_code = (int)'l';
    pos_temp++;
    ret_num1 = get_mml_num(mml_data, len, &pos_temp, &ret_num2); // mmlデータ配列, mmlデータ配列の長さ, 読み取り位置のポインタ, ドットの数戻り値用ポインタ
    break;
  case '[': //ループ開始(使用不可)
    pos_temp++;
    break;
  case ']': //ループ終了(使用不可)
    pos_temp++;
    break;
  case 't': //テンポ
    ret_code = (int)'t';
    pos_temp++;
    ret_num1 = get_mml_num(mml_data, len, &pos_temp, &ret_num2); // mmlデータ配列, mmlデータ配列の長さ, 読み取り位置のポインタ, ドットの数戻り値用ポインタ
    break;
  case '@': //音色
    ret_code = (int)'@';
    pos_temp++;
    ret_num1 = get_mml_num(mml_data, len, &pos_temp, &ret_num2); // mmlデータ配列, mmlデータ配列の長さ, 読み取り位置のポインタ, ドットの数戻り値用ポインタ
    break;
  case ';': //終端
    ret_code = ';';
    // pos_temp=len;
    break;
  default: //未定義コマンドが来るとストップ
    break;
  }

  *pos = pos_temp; //戻し用現在の読み取り位置更新
  ret_data[0] = ret_code;
  ret_data[1] = ret_num1;
  ret_data[2] = ret_tim;
  ret_data[3] = ret_num2;
}

// mmlデータから数値を返す
// mmlデータ配列, mmlデータ配列の長さ, 読み取り位置のポインタ, ドットの数戻り値用ポインタ
int get_mml_num(unsigned char *mml_data, unsigned int len, int *pos, int *dp_num)
{
  //※読み取り位置は配列外になっても加算されます。
  unsigned int pos_temp = *pos; //読み取り位置を記憶
  int keta = 0;                 //読み取る数値の桁数(0で数字以外,1で1桁,2で2桁)の読み取り位置インクリメント用
  int ret_num = 0;              //戻り値用数字
  int ret_dp_num = 0;           //ドットの数戻り値用
  int mml_data_byte = get_mml_data(mml_data, len, pos_temp);

  //数値読み取り
  while ((mml_data_byte >= '0') && (mml_data_byte <= '9'))
  {
    // ret_num += (mml_data_byte - '0')*pow(10, keta);//数字と桁数の乗算値を加算
    if ((keta == 0) && (mml_data_byte == '0'))
    { //音長が0(数値の最初が0)のとき、-3を返す
      ret_num = -3;
    }
    else
    {
      ret_num = ret_num * 10 + (mml_data_byte - '0');
    }

    keta++;
    mml_data_byte = get_mml_data(mml_data, len, pos_temp + keta);
  }

  //ドットの数を数える
  mml_data_byte = get_mml_data(mml_data, len, pos_temp + keta);
  while (mml_data_byte == '.')
  {
    ret_dp_num++;
    mml_data_byte = get_mml_data(mml_data, len, pos_temp + keta + ret_dp_num);
  }

  *pos += keta + ret_dp_num; //現在の読み取り位置に読み取った数分、加算
  *dp_num = ret_dp_num;      //ドットの数書き換え
  return ret_num;
}

// mmlデータ配列内のトラック数をカウントする。(「;」の数)
int get_mml_tr_num(unsigned char *mml_data, unsigned int len)
{
  int get_data_temp = 0;
  unsigned int tr_cnt = 0; //トラック数カウント

  for (int c_tr = 0; c_tr < len; c_tr++)
  {

    get_data_temp = get_mml_data(mml_data, len, c_tr);
    if (get_data_temp == ';')
    {
      tr_cnt++; //「;」の数カウント
    }
    else if ((get_data_temp == 0) || (get_data_temp == -1))
    {
      //終端判定
      //文字列配列終端(0x00)か配列サイズ外でデータ終了
      break;
    }
  }
  return tr_cnt;
}

// mmlデータ配列内のトラック先頭位置配列に、トラック数分の先頭位置を入れる
// mmlデータ配列, mmlデータ配列の長さ, 取得トラック数, トラック先頭位置配列の戻り値用ポインタ
//戻り値は、先頭位置取得トラック数
int get_mml_tr_pos(unsigned char *mml_data, unsigned int len, unsigned int tr_max, int *pos_tr)
{
  int get_data_temp = 0;
  unsigned int tr_cnt = 0;          //トラック数カウント
  unsigned int get_temp_tr_pos = 0; //トラック先頭アドレス一時保存

  for (int c_tr = 0; c_tr < len; c_tr++)
  {

    get_data_temp = get_mml_data(mml_data, len, c_tr);
    if (get_data_temp == ';')
    {
      pos_tr[tr_cnt] = get_temp_tr_pos; //前回の「;」の次のアドレスを入れる
      get_temp_tr_pos = c_tr + 1;
      tr_cnt++; //「;」の数カウント
    }
    if ((get_data_temp == 0) || (get_data_temp == -1) || (tr_max <= tr_cnt))
    {
      //終端判定
      //文字列配列終端(0x00)か配列サイズ外でデータ終了
      break;
    }
  }
  return tr_cnt;
}






演奏例の動画では、3つのHD43517を相互接続しているため、すべてのチャンネルが同じ音色かつ同じ音量となっており、あまり聞きやすくないです。
やはり、HD43517ごとにDACを用意した方がよいかもしれませんね。



・使用例  - MIDIの受信 -

HD43517を1~4個使用し、MIDIを受信する場合の回路とプログラム(Arduino ATmega328p用)を紹介します。

回路はArduinoのD0ピンにMIDI受信回路を追加するだけです。HD43517まわりはMMLでの使用例と同じなので、各自お好きな方で回路を組んでください。



・プログラム(HD43517を1~4個使用、「I-4」と「MSO」端子でアドレス空間上の配置位置を決定)


//HD43517でMIDI演奏プログラム
//©oy
//https://oykenkyu.blogspot.com/2023/02/hd43517.html


#include "avr/io.h"
#include "avr/interrupt.h"

#define SERIALSPEED 31250 // UARTのボーレート(MIDIのボーレートは31250bps)
//#define SERIALSPEED 38400 // UARTのボーレート(MIDIのボーレートは31250bps)

#define CH 16  // HD43517最大チャンネル数(HD43517は1つで8チャンネル分発音できます。)
#define MIDI_MAX_TR CH // MIDIトラック数

//HD43517レジスタ
//bit6~bit4は実アドレス、bit3~bit0は内部レジスタ用アドレス
#define COMP_W_MODE 0x00 //波形合成モード
#define KEY_OFF 0x08 //キーオフ

#define KEY_ON 0x10 //キーオン
#define COMP_W_SW 0x18 //チャンネルごとの各正弦波合成ON/OFF

#define SV_W1_LV 0x20 //スタートレベル:基音音量
#define SV_W2_LV 0x21 //スタートレベル:2つ目の倍音音量
#define SV_W3_LV 0x22 //スタートレベル:3つ目の倍音音量
#define SV_W4_LV 0x23 //スタートレベル:4つ目の倍音音量
#define SV_W5_LV 0x24 //スタートレベル:5つ目の倍音音量
#define AR 0x25 //アタックレート
#define DR 0x26 //ディケイレート
#define SL 0x27 //サスティンレベル
#define AL_W1_LV 0x28 //アタックレベル:基音音量
#define AL_W2_LV 0x29 //アタックレベル:2つ目の倍音音量
#define AL_W3_LV 0x2A //アタックレベル:3つ目の倍音音量
#define AL_W4_LV 0x2B //アタックレベル:4つ目の倍音音量
#define AL_W5_LV 0x2C //アタックレベル:5つ目の倍音音量
#define RR 0x2D //リリースレート

#define W1_DLY 0x30 //基音発音ディレイ
#define W2_DLY 0x31 //2つ目の倍音発音ディレイ
#define W3_DLY 0x32 //3つ目の倍音発音ディレイ
#define W4_DLY 0x33 //4つ目の倍音発音ディレイ
#define W5_DLY 0x34 //5つ目の倍音発音ディレイ

#define PTC_CHG 0x40 //ピッチ変更値指定
#define PTC 0x50 //ピッチ指定
#define OCT_CHG 0x60 //オクターブ変更値セット
#define OCT 0x70 //オクターブセット


// HD43517_チップセレクト用テーブル(少し高速化できる?)
unsigned char hd43517_cs_conv[4] = {0x38,0x58,0x68,0x70};

// HD43517_保存用
unsigned char hd43517_main_vol[CH]; //音量保存用
unsigned char hd43517_main_pan[CH]; //パン保存用
unsigned char hd43517_main_ptc[CH]; //ピッチ保存用
unsigned char hd43517_main_cwm[CH]; //波形合成モード保存用(音程計算で使用)

//HD43517のノートデータへ変換(合成比率モード別)
//HD43517の入力クロック周波数が4.00MHzの時
unsigned int hd43517_note_conv[4][12] = {
  {0x157, 0x16C, 0x182, 0x199, 0x1B1, 0x1CB, 0x1E6, 0x203, 0x222, 0x242, 0x265, 0x289},
  {0x1C8, 0x1E4, 0x200, 0x21F, 0x23F, 0x261, 0x286, 0x2AC, 0x2D5, 0x300, 0x32E, 0x35E},
  {0x111, 0x122, 0x133, 0x145, 0x159, 0x16D, 0x183, 0x19A, 0x1B2, 0x1CC, 0x1E8, 0x205},
  {0x157, 0x16C, 0x182, 0x199, 0x1B1, 0x1CB, 0x1E6, 0x203, 0x222, 0x242, 0x265, 0x289}
};
//HD43517のオクターブデータへ変換(合成比率モード別)
int hd43517_oct_conv[4]= {
  0, 1, -2, 0
};

//スタートレベル保存(音量計算用)
unsigned char hd43517_sv_temp[16][5];
//アタックレベル保存(音量計算用)
unsigned char hd43517_al_temp[16][5];

//メイン音量一時保存
unsigned char main_vol_temp=0x7F;



//HD43517音色パラメータ
//CWM   :   波形合成モード、bit1~bit0の4通り
//CWSW  :   各正弦波合成ON/OFF、bit4~bit0がそれぞれ倍音5~基音に割り当て
//SV-W[N]-L  :  スタートレベル:[N]個目の倍音音量、bit3~bit0
//AL-W[N]-L  :  アタックレベル:[N]個目の倍音音量、bit3~bit0
//AR    :   アタックレート、bit3~bit0
//DR    :   ディケイレート、bit3~bit0
//SL    :   サスティンレベル、bit3~bit0
//RR    :   リリースレート、bit3~bit0
//W[N]-DLY    :  [N]目の倍音発音ディレイ、bit3~bit0
//TL    :   トータル発音レベル(未使用)
//PAN   :   パン(未使用)
//PTC   :   音程、7bit値 64が標準





//パラメータ構造
//CWM, CWSW, reserve, reserve, reserve,
//SV-W1-L, SV-W2-L, SV-W3-L, SV-W4-L, SV-W5-L,
//AL-W1-L, AL-W2-L, AL-W3-L, AL-W4-L, AL-W5-L,
//AR, DR, SL, RR, reserve,
//W1-DLY, W2-DLY, W3-DLY, W4-DLY, W5-DLY,
//TL, PAN, PTC, reserve, reserve,
//reserve, reserve


const unsigned char hd43517_parameter_num = 22;//音色の数

const unsigned char hd43517_parameter[][32] PROGMEM = {
  //0 PIPE ORGAN
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x0C, 0x0B, 0x09, 0x0B, 0x0B,
    0x08, 0x0B, 0x0E, 0x00, 0,
    0x01, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //1 D1A PASON
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x09, 0x0B, 0x00, 0x0B, 0x09,
    0x05, 0x0B, 0x0F, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //2 TIBIA
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x0C, 0x00, 0x09, 0x0C,
    0x05, 0x0F, 0x0E, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //3 FLUTE
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x0B, 0x00, 0x00, 0x0D,
    0x07, 0x0D, 0x0F, 0x00, 0,
    0x00, 0x02, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //4 JAZZ ORGAN1
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x0B, 0x0B, 0x0B, 0x00, 0x00,
    0x05, 0x0D, 0x0F, 0x00, 0,
    0x00, 0x00, 0x01, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //5 PIANO
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x0D, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x03, 0x01, 0x00, 0x00, 0,
    0x00, 0x00, 0x00, 0x01, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //6 VIBRA PHONE
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x0C, 0x00, 0x00, 0x0C,
    0x00, 0x0B, 0x00, 0x00, 0x09,
    0x08, 0x01, 0x00, 0x01, 0,
    0x00, 0x01, 0x00, 0x00, 0x01,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //7 CELESTA
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x0C, 0x0D,
    0x00, 0x00, 0x00, 0x0C, 0x0B,
    0x08, 0x00, 0x00, 0x00, 0,
    0x00, 0x00, 0x00, 0x01, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //8 OBOE
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x0A, 0x0A, 0x0D, 0x00,
    0x07, 0x0B, 0x0F, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //9 FUNKY
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x0C, 0x00, 0x0C, 0x0C,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x03, 0x00, 0x00, 0x01, 0,
    0x00, 0x00, 0x00, 0x04, 0x05,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //10 BRILLIANT OGAN
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x0D, 0x00, 0x00, 0x00, 0x0E,
    0x05, 0x0B, 0x0D, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //11 WOOD WIND
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x0B, 0x00, 0x00,
    0x0B, 0x00, 0x00, 0x00, 0x0B,
    0x09, 0x03, 0x0F, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //12 FULL TIBAS
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x09, 0x00, 0x0D, 0x00, 0x0C,
    0x05, 0x0B, 0x0E, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //13 PICCOLO
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x09, 0x00,
    0x00, 0x00, 0x00, 0x0E, 0x00,
    0x07, 0x0B, 0x0F, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //14 JAZZ ORGAN2
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x0D, 0x00,
    0x0B, 0x0B, 0x0B, 0x00, 0x00,
    0x07, 0x0D, 0x0F, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //15 ELEC.PIANO
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x0C, 0x00, 0x00, 0x0D,
    0x00, 0x0C, 0x00, 0x00, 0x00,
    0x03, 0x02, 0x00, 0x00, 0,
    0x00, 0x00, 0x00, 0x00, 0x01,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //16 MARIMBA
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x0E, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x09,
    0x03, 0x00, 0x00, 0x00, 0,
    0x00, 0x01, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //17 CHIME
  {
    0x01, 0x1F, 0, 0, 0,
    0x0C, 0x0C, 0x0A, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x03, 0x01, 0x00, 0x01, 0,
    0x01, 0x01, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x2F, 0, 0,
    0, 0
  },
  //18 BASSOON
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x08, 0x0B, 0x0D, 0x08, 0x00,
    0x03, 0x02, 0x0E, 0x01, 0,
    0x01, 0x01, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //19 WAH BRASS
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x0C, 0x0E, 0x0C, 0x0A, 0x09,
    0x07, 0x0B, 0x0C, 0x00, 0,
    0x00, 0x04, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x40, 0, 0,
    0, 0
  },
  //20 @0_test
  {
    0x00, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x0F, 0x0E, 0x0D, 0x0C, 0x0B,
    0x00, 0x0C, 0x00, 0x00, 0,
    0x00, 0x02, 0x04, 0x06, 0x08,
    0xFF, 0x40, 0x68, 0, 0,
    0, 0
  },
  //21 @1_test_bell
  {
    0x02, 0x1F, 0, 0, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0x0F, 0x00, 0x00, 0x0D, 0x00,
    0x00, 0x00, 0x00, 0x04, 0,
    0x00, 0x00, 0x00, 0x00, 0x00,
    0xFF, 0x40, 0x68, 0, 0,
    0, 0
  },
 

};



//デフォルトトーン[ch0,ch1,ch2,…,]
//増やすと半音高くなります。(出力音程 = def_tone + (midiの音程))
char def_tone[CH] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

//デフォルトキー
char def_key = 0;

//MIDIチャンネルからHD43517のチャンネルへ変換
char hd43517_tr[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 15, 10, 11, 12, 13, 14, 9};

//--------------------------------------------------------------

//ドラム発音用
unsigned char drum_note_num = 0; //ドラムチャンネルセット保存用

//MIDI受信用
char midi_main_vel[16] = {127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127};//メインベロシティ(HD43517では未使用)(ノートベロシティと乗算して使用)
char midi_pan[16] = {64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64};//パン
char midi_vel[16] = {127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127};//ベロシティ

unsigned char midi_buf[256]; //受信バッファ
int ex_mess_en=0;            //midi_read()で使用
unsigned int dat_ph=0;                //midi_read()で使用
unsigned char read_buf_h;    //midi_read()で使用
unsigned int stop_byte=0;             //midi_read()で使用
unsigned char read_buf=0;    //midi_read()で使用
unsigned char running_ch=0x90;//ランニングステータスチャンネル

unsigned char midiprog[16];    //MIDIプログラムチェンジ保存
unsigned char sas_en = 0;      //サスティン有効フラグ


/////////////////////////////関数

//HD43517へ書き込み
void hd43517_write(unsigned char cs, unsigned char adr, unsigned char data);

//HD43517のレジスタビットへ値をセット
void hd43517_dataset(unsigned char cs, unsigned char adr, unsigned char reg, unsigned char data);
//HD43517のレジスタビットへ値をセット(アドレスと内部アドレス統合)
void hd43517_regset(unsigned char cs, unsigned char reg, unsigned char data);
//hd43517パラメータセット
void hd43517_param_set_all(unsigned char ch, int inst);

//音程のセット
void ptc_set(unsigned char ch, unsigned int notenum);
//メインの音量とパンのセット
void main_vel_pan_set(unsigned char vel, unsigned char pan);
//チャンネルの音量とパンのセット
void vel_pan_set(unsigned char ch, unsigned char vel, unsigned char pan);
//チャンネルノートオン
void note_on(unsigned char ch);
//チャンネルノートオフ
void note_off(unsigned char ch);
//音色セット
void inst_set(unsigned char ch, unsigned char inst);

//MIDI受信関係
//1バイトずつmidi_readを実行し、1グループのmidiメッセージを受信し終えるとmidi_comを実行
void midi_read(char read_buf);
//MIDIデータ入力処理
inline void midi_com(unsigned char *in_midi_mess);


//セットアップ
void setup()
{
 
  Serial.begin(SERIALSPEED); //シリアル通信開始(MIDI受信)
 
  //ポート設定
  // out_put
  DDRD = 0xFC;  // pin2~pin7_HD43517:D2~D7
  PORTD = 0xFF; // pin2~pin7_HD43517:D2~D7
 
  DDRB = 0x3B;  // pin8~pin9_HD43517:D0~D1   pin11~pin13_HD43517:~WE, A0, ~RST
  PORTB = 0x3B; // pin8~pin9_HD43517:D0~D1   pin11~pin13_HD43517:~WE, A0, ~RST

  DDRC = 0x0F;  // pin14~pin17_HD43517:CS0 ~ CS3
  PORTC = 0x0F; // pin14~pin17_HD43517:CS0 ~ CS3

  //リセット
  PORTB &= ~0x20; // pin13_HD43517: ~RST = L
  delay(100);
  PORTB |= 0x20; // pin13_HD43517: ~RST = H
  delay(100);
 
  // HD43517キーオフ
  for (int i = 0; i < CH; i++)
  {
    note_off(i);
  }
 
  //パラメータ初期化
  for (int ch = 0; ch < CH; ch++)
  {
    hd43517_param_set_all(ch, 3);//パラメータセット
  }
 
}

void loop()
{
  while (1)
  {
    if (Serial.available() > 0)
    {
      midi_read(Serial.read());//イベント処理
    }
  }
}

//HD43517へ書き込み
void hd43517_write(unsigned char cs, unsigned char adr, unsigned char data)
{
  PORTB |= 0x10; // pin13_HD43517: A0 = H
  PORTB |= 0x08; // pin11_HD43517: ~WR = H
 
  //チップセレクト
  PORTC = (PORTC & 0xF0) | (0x0F & (1 << cs)); // pin14~pin17_HD43517:CS0 ~ CS3
 
  //アドレスのセット
  PORTB &= ~0x10; // pin13_HD43517: A0 = L
  delayMicroseconds(1);
  PORTB = (PORTB & 0xFC) | (0x03 & adr); // pin9,8_HD43517:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & adr); // pin7~2_HD43517:D7~D2
 
  PORTB &= ~0x08; // pin11_HD43517: ~WR = L
  delayMicroseconds(1);
  PORTB |= 0x08; // pin11_HD43517: ~WR = H

  delayMicroseconds(7);
  //アドレスのセット2回目
  PORTB &= ~0x08; // pin11_HD43517: ~WR = L
  delayMicroseconds(1);
  PORTB |= 0x08; // pin11_HD43517: ~WR = H
  delayMicroseconds(1);
  PORTB |= 0x10; // pin13_HD43517: A0 = H

  //データのセット
  delayMicroseconds(20);

  PORTB = (PORTB & 0xFC) | (0x03 & data); // pin9,8_HD43517:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & data); // pin7~2_HD43517:D7~D2
 
  PORTB &= ~0x08; // pin11_HD43517: ~WR = L
  delayMicroseconds(1);
  PORTB |= 0x08; // pin11_HD43517: ~WR = H

  //データのセット2回目
  delayMicroseconds(7);

  PORTB &= ~0x08; // pin11_HD43517: ~WR = L
  delayMicroseconds(1);
  PORTB |= 0x08; // pin11_HD43517: ~WR = H
 
 
  delayMicroseconds(200);
}

//HD43517のレジスタビットへ値をセット
//csとアドレスチップセレクトを統合
//基本的にこの関数を介してHD43517を操作
void hd43517_dataset(unsigned char cs, unsigned char adr, unsigned char reg, unsigned char data)
{
  //csは4つを1ブロックとする
  //4つのHD43517のcsはアドレスbit6~bit3で指定
  //adrは0x0~0x7までを指定
  //regは0x0~0xFまでを指定
  //hd43517_cs_conv[0x03&cs]は(~(1<<(3+(0x03&cs))) & 0x78)の計算
  if(adr>=0x04)
  {
    //adr>=0x04の場合、データ幅は8bit
    hd43517_write(cs>>2, hd43517_cs_conv[0x03&cs] | (0x07 & adr), data);
  }
  else if(adr>=0x02)
  {
    //0x04>adr>=0x02の場合、データ幅は4bit
    hd43517_write(cs>>2, hd43517_cs_conv[0x03&cs] | (0x07 & adr), (reg<<4) | (0x0F&data));
  }
  else
  {
    //0x02>adr>=0x00の場合、データ幅は7bit
    hd43517_write(cs>>2, hd43517_cs_conv[0x07&cs] | (0x07 & adr), (reg<<4) | (0x3F&data));
  }
}

//HD43517のレジスタビットへ値をセット(アドレスと内部アドレス統合)
//csとアドレスチップセレクトを統合
//defineで定義したレジスタ名で指定してレジスタを操作
//基本的にこの関数を介してHD43517を操作
void hd43517_regset(unsigned char cs, unsigned char reg, unsigned char data)
{

  /*
  Serial.print("hd43517_cs:");//デバッグ用
    Serial.print(cs);//デバッグ用
    Serial.print("  adr1:");//デバッグ用
    Serial.print(reg>>4,HEX);//デバッグ用
    Serial.print("  adr2:");//デバッグ用
    Serial.print(reg&0xF,HEX);//デバッグ用
    Serial.print("  data:");//デバッグ用
    Serial.print(data,HEX);//デバッグ用
    Serial.print("\r\n");//デバッグ用*/
   
  //csは4つを1ブロックとする
  //4つのHD43517のcsはアドレスbit6~bit3で指定
  //reg上位4bitはアドレスで0x0~0x7までを指定
  //reg下位4bitは内部アドレスで0x0~0xFまでを指定
  //hd43517_cs_conv[0x03&cs]は(~(1<<(3+(0x03&cs))) & 0x78)の計算
  if(reg>=0x40)
  {
    //reg>=0x40の場合、データ幅は8bit
    hd43517_write(cs>>2, hd43517_cs_conv[0x03&cs] | (0x07 & (reg>>4)), data);
  }
  else if(reg>=0x20)
  {
    //0x40>reg>=0x20の場合、データ幅は4bit
    hd43517_write(cs>>2, hd43517_cs_conv[0x03&cs] | (0x07 & (reg>>4)), (reg<<4) | (0x0F&data));
  }
  else
  {
    //0x20>reg>=0x00の場合、データ幅は7bit
    hd43517_write(cs>>2, hd43517_cs_conv[0x03&cs] | (0x07 & (reg>>4)), (reg<<4) | (0x7F&data));

  }
}

//hd43517パラメータセット
void hd43517_param_set_all(unsigned char ch, int inst)
{
  //入力データ構造////////////////////////////////
  //CWM, CWSW, reserve, reserve, reserve,
  //SV-W1-L, SV-W2-L, SV-W3-L, SV-W4-L, SV-W5-L,
  //AL-W1-L, AL-W2-L, AL-W3-L, AL-W4-L, AL-W5-L,
  //AR, DR, SL, RR, reserve,
  //W1-DLY, W2-DLY, W3-DLY, W4-DLY, W5-DLY,
  //TL, PAN, PTC, reserve, reserve,
  //reserve, reserve
  ////////////////////////////////////////////////

  //CWM, CWSW, reserve, reserve, reserve,
  hd43517_regset(ch>>2, COMP_W_MODE, pgm_read_byte_near((int)(hd43517_parameter[inst] + 0)));
  hd43517_main_cwm[ch] = 0x03 & pgm_read_byte_near((int)(hd43517_parameter[inst] + 0));
  hd43517_regset(ch>>2, COMP_W_SW, pgm_read_byte_near((int)(hd43517_parameter[inst] + 1))<<2 | 0x00);//ch0
  hd43517_regset(ch>>2, COMP_W_SW, pgm_read_byte_near((int)(hd43517_parameter[inst] + 1))<<2 | 0x01);//ch1
  hd43517_regset(ch>>2, COMP_W_SW, pgm_read_byte_near((int)(hd43517_parameter[inst] + 1))<<2 | 0x02);//ch2
  hd43517_regset(ch>>2, COMP_W_SW, pgm_read_byte_near((int)(hd43517_parameter[inst] + 1))<<2 | 0x03);//ch3

  //SV-W1-L, SV-W2-L, SV-W3-L, SV-W4-L, SV-W5-L,
  hd43517_regset(ch>>2, SV_W1_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 5)));
  hd43517_regset(ch>>2, SV_W2_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 6)));
  hd43517_regset(ch>>2, SV_W3_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 7)));
  hd43517_regset(ch>>2, SV_W4_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 8)));
  hd43517_regset(ch>>2, SV_W5_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 9)));

  //AL-W1-L, AL-W2-L, AL-W3-L, AL-W4-L, AL-W5-L,
  hd43517_regset(ch>>2, AL_W1_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 10)));
  hd43517_regset(ch>>2, AL_W2_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 11)));
  hd43517_regset(ch>>2, AL_W3_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 12)));
  hd43517_regset(ch>>2, AL_W4_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 13)));
  hd43517_regset(ch>>2, AL_W5_LV, pgm_read_byte_near((int)(hd43517_parameter[inst] + 14)));

  /*//いずれかのチャンネルがキーオン中にパラメータの変更をすると、パラメータが正常に反映されないため、音量制御はしない。以下実験用
  //SV-W1-L, SV-W2-L, SV-W3-L, SV-W4-L, SV-W5-L,
  hd43517_sv_temp[ch][0]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 5));
  hd43517_sv_temp[ch][1]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 6));
  hd43517_sv_temp[ch][2]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 7));
  hd43517_sv_temp[ch][3]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 8));
  hd43517_sv_temp[ch][4]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 9));
  //AL-W1-L, AL-W2-L, AL-W3-L, AL-W4-L, AL-W5-L,
  hd43517_al_temp[ch][0]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 10));
  hd43517_al_temp[ch][1]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 11));
  hd43517_al_temp[ch][2]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 12));
  hd43517_al_temp[ch][3]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 13));
  hd43517_al_temp[ch][4]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 14));
  */

  //AR, DR, SL, RR, reserve,
  hd43517_regset(ch>>2, AR, pgm_read_byte_near((int)(hd43517_parameter[inst] + 15)));
  hd43517_regset(ch>>2, DR, pgm_read_byte_near((int)(hd43517_parameter[inst] + 16)));
  hd43517_regset(ch>>2, SL, pgm_read_byte_near((int)(hd43517_parameter[inst] + 17)));
  hd43517_regset(ch>>2, RR, pgm_read_byte_near((int)(hd43517_parameter[inst] + 18)));

  //W1-DLY, W2-DLY, W3-DLY, W4-DLY, W5-DLY,
  hd43517_regset(ch>>2, W1_DLY, pgm_read_byte_near((int)(hd43517_parameter[inst] + 20)));
  hd43517_regset(ch>>2, W2_DLY, pgm_read_byte_near((int)(hd43517_parameter[inst] + 21)));
  hd43517_regset(ch>>2, W3_DLY, pgm_read_byte_near((int)(hd43517_parameter[inst] + 22)));
  hd43517_regset(ch>>2, W4_DLY, pgm_read_byte_near((int)(hd43517_parameter[inst] + 23)));
  hd43517_regset(ch>>2, W5_DLY, pgm_read_byte_near((int)(hd43517_parameter[inst] + 24)));

  //TL, PAN, PTC, reserve, reserve,
  hd43517_main_vol[ch]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 25));
  hd43517_main_pan[ch]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 26));
  hd43517_main_ptc[ch]=pgm_read_byte_near((int)(hd43517_parameter[inst] + 27));

}

//音程のセット
void ptc_set(unsigned char ch, unsigned int notenum)
{
  unsigned char cs_temp = ch >> 2;
  unsigned char ch_temp = ch & 0x03;
  int ch_notenum = notenum + hd43517_main_ptc[ch]-76 + hd43517_oct_conv[hd43517_main_cwm[ch]]*12;
  while(ch_notenum<0){
    ch_notenum+=12;
  }
  while(ch_notenum>=128){
    ch_notenum-=12;
  }
  unsigned int tone = ch_notenum % 12;
  unsigned int oct = ch_notenum / 12;
  unsigned char inst =0;
 
  if (ch != 9)
  { //ドラムチャンネル以外
 
    hd43517_regset(cs_temp, PTC, (unsigned char)((unsigned int)0x00FF & hd43517_note_conv[hd43517_main_cwm[ch]][tone]));
    hd43517_regset(cs_temp, OCT, (((unsigned char)oct<<4) & 0x70) | (((unsigned int)hd43517_note_conv[hd43517_main_cwm[ch]][tone]>>6)&0x0C) | ch_temp);
 
  }
  else
  {
    //ドラムチャンネル
 
  }
}

//メインの音量とパンのセット
void main_vel_pan_set(unsigned char vel, unsigned char pan)
{
  main_vol_temp = vel;
}

//チャンネルの音量とパンのセット
void vel_pan_set(unsigned char ch, unsigned char vel, unsigned char pan)
{
  char set_vel_temp = 0;
  return;//いずれかのチャンネルがキーオン中にパラメータの変更をすると、パラメータが正常に反映されないため、音量制御はしない。以下実験用

  //音量計算
  vel = (char)((127 - (char)((int)((int)vel * (int)main_vol_temp) >> 7))>>4);
 

  set_vel_temp = (char)hd43517_sv_temp[ch][0] - vel;
  if(set_vel_temp < 0){
    set_vel_temp =0;
  }
  hd43517_regset(ch>>2, SV_W1_LV, set_vel_temp);
  set_vel_temp = (char)hd43517_sv_temp[ch][1] - vel;
  if(set_vel_temp < 0){
    set_vel_temp =0;
  }
  hd43517_regset(ch>>2, SV_W2_LV, set_vel_temp);
  set_vel_temp = (char)hd43517_sv_temp[ch][2] - vel;
  if(set_vel_temp < 0){
    set_vel_temp =0;
  }
  hd43517_regset(ch>>2, SV_W3_LV, set_vel_temp);
  set_vel_temp = (char)hd43517_sv_temp[ch][3] - vel;
  if(set_vel_temp < 0){
    set_vel_temp =0;
  }
  hd43517_regset(ch>>2, SV_W4_LV, set_vel_temp);
  set_vel_temp = (char)hd43517_sv_temp[ch][4] - vel;
  if(set_vel_temp < 0){
    set_vel_temp =0;
  }
  hd43517_regset(ch>>2, SV_W5_LV, set_vel_temp);
 

  set_vel_temp = (char)hd43517_al_temp[ch][0] - vel;
  if(set_vel_temp < 0){
    set_vel_temp =0;
  }
  hd43517_regset(ch>>2, AL_W1_LV, set_vel_temp);
  set_vel_temp = (char)hd43517_al_temp[ch][1] - vel;
  if(set_vel_temp < 0){
    set_vel_temp =0;
  }
  hd43517_regset(ch>>2, AL_W2_LV, set_vel_temp);
  set_vel_temp = (char)hd43517_al_temp[ch][2] - vel;
  if(set_vel_temp < 0){
    set_vel_temp =0;
  }
  hd43517_regset(ch>>2, AL_W3_LV, set_vel_temp);
  set_vel_temp = (char)hd43517_al_temp[ch][3] - vel;
  if(set_vel_temp < 0){
    set_vel_temp =0;
  }
  hd43517_regset(ch>>2, AL_W4_LV, set_vel_temp);
  set_vel_temp = (char)hd43517_al_temp[ch][4] - vel;
  if(set_vel_temp < 0){
    set_vel_temp =0;
  }
  hd43517_regset(ch>>2, AL_W5_LV, set_vel_temp);
 

}

//チャンネルノートオン
void note_on(unsigned char ch)
{
  unsigned char cs_temp = ch >> 2;
  unsigned char ch_temp = ch & 0x03;
  if (ch != 9)
  {
    //ドラムチャンネル以外
    hd43517_regset(cs_temp, KEY_ON, 0x08 | ch_temp);//リリース有効
    //hd43517_regset(cs_temp, KEY_ON, 0x00 | ch_temp);//リリース無効
  }
  else
  {
    //ドラムチャンネルはなし
  }
}

//チャンネルノートオフ
void note_off(unsigned char ch)
{
  unsigned char cs_temp = ch >> 2;
  unsigned char ch_temp = ch & 0x03;
  if (ch != 9)
  { //ドラムチャンネル以外
    hd43517_regset(cs_temp, KEY_OFF, 0x08 | ch_temp);
  }
  else
  {
    //ドラムチャンネルはなし
  }
}

//音色セット
void inst_set(unsigned char ch, unsigned char inst)
{
  hd43517_param_set_all(ch , inst);
}



//MIDI受信用関数/////////////////////////////
//1バイトずつmidi_readを実行し、1グループのmidiメッセージを受信し終えるとmidi_comを実行
void midi_read(char read_buf)
{
  if (ex_mess_en == 1)
  {
    if ((unsigned char)read_buf == 0xF7)
    { //エクスクルーシブ・メッセージ終了
      ex_mess_en = 0;
      stop_byte = dat_ph;
    }
  }
  else if (((read_buf >> 7) & 0x01) == 1)
  {
    //データ始まり検出したら
    dat_ph = 0;
    ex_mess_en = 0;
    read_buf_h = 0xF0 & read_buf;
   
    if (((0x80 <= (unsigned char)read_buf_h) && (0xB0 >= (unsigned char)read_buf_h)) || ((unsigned char)read_buf_h == 0xE0))
    {
      //3バイト読み取り
      running_ch = read_buf; //ランニングステータスチャンネルセット
      stop_byte = 2;
    }
    else if (((unsigned char)read_buf_h == 0xC0) || ((unsigned char)read_buf_h == 0xD0))
    {
      //2バイト読み取り
      running_ch = read_buf;
      stop_byte = 1;
    }
    else
    {
      switch ((unsigned char)read_buf)
      {
        case 0xF0: //エクスクルーシブ・メッセージ
        ex_mess_en = 1;
        stop_byte = 254;
        //Serial.print("ex_mess_en\r\n");//デバッグ用*/
        break;
        case 0xF1: //MIDIタイムコード
        case 0xF3: //ソング・セレクト
        stop_byte = 1;
        break;
        case 0xF2: //ソング・ポジション・ポインター
        stop_byte = 2;
        break;
        case 0xF6: //チューン・リクエスト
        case 0xF7: //エンド・オブ・エクスクルーシブ
        case 0xF8: //タイミング・クロック(MIDIクロック)
        case 0xFA: //スタート
        case 0xFB: //コンティニュー
        case 0xFC: //ストップ
        case 0xFF: //システム・リセット
        stop_byte = 0;
        break;
        case 0xFE: //アクティブ・センシングc
        stop_byte = 0;
        break;
      }
    }
  }
  else if (dat_ph == 0)
  {
    //ランニングステータス//////
    stop_byte = 2;
    midi_buf[0] = running_ch;
    dat_ph++;
    //////////////////////////
  }
  midi_buf[dat_ph] = read_buf; //受信データ格納
  if (dat_ph >= stop_byte)
  {
    //指定バイト数受信完了
 
    stop_byte = 3;
    ex_mess_en = 0;
    if((midi_buf[0]==0xFE) && (dat_ph == 0))
    {
      dat_ph = 0;//アクティブセンシングは処理しない
    }
    else
    {
      midi_com(midi_buf);
    }
   
    dat_ph = 0;
    midi_buf[0] = 0;
    return;
  }
  if ((((midi_buf[0] >> 7) & 0x01) == 1) || (ex_mess_en == 1))
  {
    //1バイト目がメッセージ以外読み取りしない
    dat_ph++;
  }
}


//MIDIデータ入力処理
inline void midi_com(unsigned char *in_midi_mess)
{
  unsigned char in_midi_data_buf_h0 = in_midi_mess[0] >> 4;   //上位4ビットのみを右へ4ビットシフト
  unsigned char in_midi_data_buf_l0 = in_midi_mess[0] & 0x0f; //下位4ビットのみ チャンネル
  unsigned char in_midi_data_buf_1 = in_midi_mess[1];
  unsigned char in_midi_data_buf_2 = in_midi_mess[2];

  unsigned char cs_temp;
  unsigned char ch_temp;


  //ドラムパート
  if (in_midi_data_buf_l0 == 0x09)
  {
    drum_note_num=in_midi_mess[1];
    if (in_midi_data_buf_h0 == 0x08){
      //ノートオフ
      //何もしません
    }
    else if (in_midi_data_buf_h0 == 0x09){
      if (in_midi_mess[2] == 0){
        //ノートオフ
        //何もしません
      }
      else{
        //ノートオン
        //何もしません
      }
    }
    return;
  }
  //80
  if (in_midi_data_buf_h0 == 0x08)
  {
    //ノートオフ
    if(in_midi_data_buf_l0 < MIDI_MAX_TR){
      note_off(hd43517_tr[in_midi_data_buf_l0]);
    }
  }
  //90
  else if (in_midi_data_buf_h0 == 0x09)
  {
    if (in_midi_mess[2] == 0)
    {
      //ノートオフ
      if(in_midi_data_buf_l0 < MIDI_MAX_TR){
        note_off(hd43517_tr[in_midi_data_buf_l0]);
      }
    }
    else
    {
      midi_vel[hd43517_tr[in_midi_data_buf_l0]] = in_midi_data_buf_2;//ベロシティ保存
      //ノートオン
      if(in_midi_data_buf_l0 < MIDI_MAX_TR){
        ptc_set(hd43517_tr[in_midi_data_buf_l0], in_midi_mess[1] +(def_tone[in_midi_data_buf_l0]) + def_key);//チャンネル, 音程
        vel_pan_set(hd43517_tr[in_midi_data_buf_l0], ((int)((int)midi_vel[in_midi_data_buf_l0] * (int)midi_main_vel[in_midi_data_buf_l0] )>>7), midi_pan[in_midi_data_buf_l0]);//チャンネル, 音量, パン
        note_on(hd43517_tr[in_midi_data_buf_l0]);//ノートオン
      }
    }
  }
  else if (in_midi_data_buf_h0 == 0x0A)
  {
    //ポリフォニックキープレッシャー
    //何もしない
  }
  else if (in_midi_data_buf_h0 == 0x0B)
  {
    //コントロールチェンジ
    switch (in_midi_mess[1])
    {
    case 0x07: //チャンネルボリューム
      midi_main_vel[in_midi_data_buf_l0] = in_midi_mess[2];
      break;
    case 0x0A: //パン
      midi_pan[in_midi_data_buf_l0] = in_midi_mess[2];
      break;
    case 0x78:
    case 0x79:
      for (int i = 0; i < MIDI_MAX_TR; i++)
      {
        note_off(i);
      }
      break;
    case 0x40: //サスティン
      if (in_midi_mess[2] == 0x7F)
      {
        sas_en = 1;
      }
      else
      {
        sas_en = 0;
      }
      break;
    default:
      break;
    }
  }
  else if (in_midi_data_buf_h0 == 0x0C)
  {
    //プログラムチェンジ
    midiprog[in_midi_data_buf_l0]=in_midi_mess[1];
    if(midiprog[in_midi_data_buf_l0] < hd43517_parameter_num){
      hd43517_param_set_all(in_midi_data_buf_l0 , midiprog[in_midi_data_buf_l0]);
    }
   
  }
  else if (in_midi_data_buf_h0 == 0x0E)
  {
    //ピッチベンド
    //何もしない
  }
  else if (in_midi_data_buf_h0 == 0x0F)
  {
    //システムメッセージ
    if (in_midi_mess[0] == 0xFE)
    {
      //アクティブセンシング
    }
    if (in_midi_mess[0] == 0xF0)
    {
      //エクスクルーシブ・メッセージ
     
    }
  }
}



MIDIのプログラムチェンジで上記プログラム内にあるHD43517のパラメータをチェンジ出来ます。
パラメータは配列としてまとめています。音色を作成したい場合はその配列にパラメータを追加してください。

現状音量制御はできません。音量を変更したい場合は、すべてのチャンネルがキーオフした状態でSVとALレジスタへ
書き込む必要があります。





質問と回答   [2023/11/27追記]

Q:HD43517が故障しているCasiotone501を修理をしたいのですが、日本国外でHD43517を入手することはできますか?


A:日本国外でHD43517を見つけるのは難しいかもしれません。
日本国内ですと、中古のCasiotoneが低価格で売られているのですが、日本国外となると、怪しいサイトで購入するしか無さそうです。(たぶん偽物や故障品だと思います)


ちなみにですが、Casiotone501のサービスマニュアルが手に入らないので回路は不明ですが、HD43517が2個同時に壊れている可能性は低いです。
もしかすると、HD43517を用意する必要がないかもしれません。

HD43517はそれぞれにDACを付ける場合とプライマリセカンダリモードにして1つのDACを使用する2種類のモードが用意されています。

Casiotone501の場合は不明ですが、恐らくプライマリセカンダリモードで1つのDACの場合かと思われます。
この場合、DACが接続されているプライマリのHD43517が故障すると、正常に発音されません。
試しにプライマリのHD43517とセカンダリのHD43517を交換して音が4ポリ分音が鳴れば、片方が故障していると特定できます。

もし、それでも正常な発音がしなければ後段のDAC回路が故障している可能性が高いです。


古いシンセサイザの主な故障は、はんだのクラックや電解コンデンサの劣化、トランジスタやダイオード、アナログスイッチやオペアンプの不良などです。
まずは、はんだのクラックが無いか丁寧に探して怪しい部分は追いはんだをすると良いでしょう。
電解コンデンサは劣化しているか見た目ではわからないので、一通り交換した方が良いです。特に低漏れ電流品(他のとスリーブ色が違うもの、オレンジ色等)は、交換をおすすめします。
※電源回路基板にあるRIFAコンデンサは劣化により発火する危険があるので、速やかに別のXコンデンサに交換してください。


半導体部品もまれに故障します。
手に入れやすい部品(アナログスイッチやオペアンプ)などを交換していきます。
経験上、電源回路のツェナーダイオードやトランジスタが割と故障している場合が多いです。電圧を測って異常な電圧が出ていないかチェックしましょう。
あとは、音量調節用の可変抵抗が中途半端に故障していると正常な音が出ないこともあります。

HD43517に入力するクロックが安定しているか確認してみることぐらいでしょう。

ここまでで、治らなければHD43517やDAC ICが故障している可能性が高いです。

HD43517が故障していた場合は中古のCasiotoneを手に入れて部品取りするしかないでしょう。
HD43517が入っているCasiotoneはいくつかあるので、日本国外でも探すとあるかもしれません。


Casiotone501の基板の高解像度な画像をいただければ故障個所を推定できるかもしれません。



0 件のコメント:

コメントを投稿