2023年12月1日金曜日

AY-3-8910(PSG)・SSGの使い方②

 音源ICの使い方一覧へ

AY-3-8910(PSG)・SSGの使い方②

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




・使用例  - MMLで演奏 -

PSG(AY-3-891X)、SSG(YM2149)、SSGC(YM3439)、SSGL(YMZ284)、SSGLP(YMZ294)、SSGP2(YMZ285)、SSGS3(YMZ771)を1~8個使用し、MMLで演奏するプログラム(Arduino ATmega328p用)を紹介します。

PSG(AY-3-8910)、SSGLP(YMZ294)、SSGP2(YMZ285)、SSGS3(YMZ771)で動作確認しました。


・回路図(各音源ICごとの接続例)

手持ちのPSG・SSG系のICがPSG(AY-3-8910)、SSGLP(YMZ294)、SSGP2(YMZ285)、SSGS3(YMZ771)しか無かったため、とりあえずこの4つでテストしています。もちろん「全体の回路例」で紹介した回路でも発音できます。



・注意点
YMZ705(SSGS)やYMZ732(SSGS2)、SSGS3(YMZ771)はSSGが2系統(計6チャネル分)持っています。この2系統はチップセレクトが共通のため、プログラム上で2系統を1つのチップセレクトとして指定する必要があります。

上記回路例の場合のプログラム例(下のプログラム)では、psg_type[IC_NUM]で指定しています。
通常のSSG・PSG(PSG, SSG, SSGC, SSGL, SSGLP, SSGP2等)は0、SSGSやSSGS2は1と2、SSGS3は3と4を指定してください。詳細はプログラム内のコメントを見てください。

例:
上記回路の様にCS0にPSG、CS1にSSGLP、CS2にSSGP2、CS3とCS4にSSGS3を配置した場合

#define IC_NUM 5 //ICの数 3チャンネルで1つ分(ノートとドラムの合計)
unsigned char psg_type[IC_NUM] = {0, 0, 0, 3, 4};

となります。



・MML演奏プログラム(PSGやSSGを1~8個まで使用した場合、Arduino ATmega328p用)


// PSG SSGでMML演奏プログラム
// ©oy
// https://oykenkyu.blogspot.com/2023/07/psg-ssg.html

//00000000000000000000000000000000000000000000000000000000000000
//プログラムの再配布、改変したものを配布する場合は
//「https://oykenkyu.blogspot.com/2023/07/psg-ssg.html
//へのリンクまたはURLを分かりやすいところに貼るもしくは書いて下さい。
//※プログラムソースのコメント欄にも要記載
//00000000000000000000000000000000000000000000000000000000000000

#define XTAL 16000000 // 水晶振動子の周波数(MML演奏速度に影響)
//#define XTAL 20000000  // 水晶振動子の周波数(MML演奏速度に影響)16MHzでもたつく場合20MHzを推奨
//#define XTAL 24000000  // 水晶振動子の周波数(MML演奏速度に影響)24MHzは実験的に用意
#include "avr/io.h"
#include "avr/interrupt.h"

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

#define CH 16          // psg最大チャンネル数(psgは1つで3チャンネル分発音できます。)
#define MML_MAX_TR CH // MMLトラック数
#define DRUM_NUM 5     // ドラム同時発音数
#define IC_NUM 5      // ICの数 3チャンネルで1つ分(ノートとドラムの合計)
#define CH_T CH + DRUM_NUM     // ドラム同時発音数

//音源ICの種類
//3チャンネルごと選択
//SSGS、SSGS2、SSGS3のレジスタマップはSSGやPSGなどと違うので注意
//
//0:通常のSSG・PSG(PSG, SSG, SSGC, SSGL, SSGLP, SSGP2等)
//1:YMZ705(SSGS), YMZ732(SSGS2)系統1
//2:YMZ705(SSGS), YMZ732(SSGS2)系統2
//3:YMZ771(SSGS3)系統1
//4:YMZ771(SSGS3)系統2
//
//系統1と系統2は必ず1、2または3、4の順で連続配置してください
unsigned char psg_type[IC_NUM] = {0, 0, 0, 3, 4};



// MMLチャンネルからPSGのチャンネルへ変換
//#define DRUM_CH 2//下の変換テーブルのチャンネルと被らないよう設定
//char mml_in_tr[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, DRUM_CH, 10, 11, 12, 13, 14, 9};//ドラムのノイズ周波数を考慮しない場合
//char mml_in_tr_drum[8] = {DRUM_CH, 16, 17, 18, 19, 20, 21, 22};////ドラムのノイズ周波数を考慮しない場合(ドラムマップ)
//
#define DRUM_CH 2//下の変換テーブルのチャンネルと被らないよう設定
char mml_in_tr[16] = {0, 1, 3, 4, 6, 7, 9, 10, 12, DRUM_CH, 13, 15, 16, 18, 19, 21};//ドラムのノイズ周波数を考慮する場合
char mml_in_tr_drum[8] = {DRUM_CH, 5, 8, 11, 14, 17, 20, 22};////ドラムのノイズ周波数を考慮する場合(ドラムマップ)


// psg SSGレジスタ
// bit6~bit4は実アドレス、bit3~bit0は内部レジスタ用アドレス
#define TP_L 0x00      // Tone Period L
#define TP_H 0x01      // Tone Period H
#define CH_A_TP_L 0x00 // Channel A Tone Period L
#define CH_A_TP_H 0x01 // Channel A Tone Period H
#define CH_B_TP_L 0x02 // Channel B Tone Period L
#define CH_B_TP_H 0x03 // Channel B Tone Period H
#define CH_C_TP_L 0x04 // Channel C Tone Period L
#define CH_C_TP_H 0x05 // Channel C Tone Period H
#define NOISE_TP 0x06  // Noise Period
#define ENABLE 0x07    //~Enable
#define AMP 0x08       // Amplitude
#define CH_A_AMP 0x08  // Channel A Amplitude
#define CH_B_AMP 0x09  // Channel B Amplitude
#define CH_C_AMP 0x0A  // Channel C Amplitude
#define EP_L 0x0B      // Envelope Period L
#define EP_H 0x0C      // Envelope Period H
#define ESC 0x0D       // Envelope Shape Cycle
#define IO_A 0x0E      // I/O Port A Data Store
#define IO_B 0x0F      // I/O Port B Data Store
#define SSGL_CP 0x0F   // Control Power

// psg_保存用
unsigned char psg_key[IC_NUM];  // key_ON/OFF保存用(~enableレジスタをそのまま保存)
unsigned char psg_env_ch[CH];   // エンベロープ有効無効保存
unsigned char psg_main_vol[CH]; // 音量保存用 0~15
unsigned char psg_main_pan[CH]; // パン保存用
unsigned char psg_main_ptc[CH]; // ピッチ保存用

// ソフトウェアエンベロープ用

volatile int intwr = 0;            // 書き込み中か?
volatile char psg_env_cnt_div[CH_T]; // エンベロープ用カウンタ(分周用)
volatile unsigned char psg_env_cnt[CH_T]; // エンベロープ用カウンタ(出力用)
volatile unsigned char psg_env_s[CH_T];   // エンベロープ状態   0:待機、1:アタック開始待機、2:アタック、3:ディケイ開始待機、4:、ディケイ、5:リリース開始待機、6:リリース、7:終了後の処理
volatile char psg_env_ar[CH_T];           // アタックレート 0~127
volatile char psg_env_dr[CH_T];           // ディケイレート 0~127
volatile char psg_env_sl[CH_T];           // サスティンレベル 0~127
volatile char psg_env_rr[CH_T];           // リリースレート 0~127
volatile char psg_env_rr_vol[CH_T];       // リリース音量保存
volatile unsigned char psg_env_key[CH_T]; // キーオンキーオフ保存

// 除算テーブル
unsigned char div3_tb[24] = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7};
unsigned char div3rem_tb[24] = {0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2};

//YMZ771用アドレス変換テーブル
//0:通常のSSG・PSG(PSG, SSG, SSGC, SSGL, SSGLP, SSGP2等)
//1:YMZ705(SSGS), YMZ732(SSGS2)系統1
//2:YMZ705(SSGS), YMZ732(SSGS2)系統2
//3:YMZ771(SSGS3)系統1
//4:YMZ771(SSGS3)系統2
unsigned char ymz771_adr_comv[5][16] = {
  {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
  {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
  {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F},
  {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x1C, 0x1E, 0x20, 0x21, 0x22, 0x26, 0x27, 0x2A, 0x01, 0x01},
  {0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1D, 0x1F, 0x23, 0x24, 0x25, 0x28, 0x29, 0x2B, 0x01, 0x01}
 
};

// psgのノートデータへ変換
//  3.579Mhz用
/*unsigned int tone_data[128] = {
    855, 807, 762, 719, 679, 641, 605, 571, 539, 1017, 960, 906,
    855, 807, 762, 719, 679, 641, 605, 571, 539, 1017, 960, 906,
    855, 807, 762, 719, 679, 641, 605, 571, 539, 1017, 960, 906,
    855, 807, 762, 719, 679, 641, 605, 571, 539, 1017, 960, 906,
    855, 807, 762, 719, 679, 641, 605, 571, 539, 508, 480, 453,
    427, 404, 381, 359, 339, 320, 302, 285, 269, 254, 240, 226,
    214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,
    107, 101, 95, 90, 85, 80, 76, 71, 67, 64, 60, 57,
    53, 50, 48, 45, 42, 40, 38, 36, 34, 32, 30, 28,
    27, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 14,
    13, 13, 12, 11, 11, 10, 9, 9};*/
// 4.000Mhz用
unsigned int tone_data[128] = {
    3822, 3608, 3405, 3214, 3034, 2863, 2703, 2551, 2408, 2273, 2145, 4050,
    3822, 3608, 3405, 3214, 3034, 2863, 2703, 2551, 2408, 2273, 2145, 4050,
    3822, 3608, 3405, 3214, 3034, 2863, 2703, 2551, 2408, 2273, 2145, 2025,
    1911, 1804, 1703, 1607, 1517, 1432, 1351, 1276, 1204, 1136, 1073, 1012,
    956, 902, 851, 804, 758, 716, 676, 638, 602, 568, 536, 506,
    478, 451, 426, 402, 379, 358, 338, 319, 301, 284, 268, 253,
    239, 225, 213, 201, 190, 179, 169, 159, 150, 142, 134, 127,
    119, 113, 106, 100, 95, 89, 84, 80, 75, 71, 67, 63,
    60, 56, 53, 50, 47, 45, 42, 40, 38, 36, 34, 32,
    30, 28, 27, 25, 24, 22, 21, 20, 19, 18, 17, 16,
    15, 14, 13, 13, 12, 11, 11, 10};

//PSG音量変換用テーブル
const unsigned char PROGMEM psg_vel_conv[128]{
   0,  1,  2,  3,  4,  4,  5,  5,  5,  6,  6,  6,  6,  7,  7,  7,  
   7,  7,  8,  8,  8,  8,  8,  9,  9,  9,  9,  9,  9, 10, 10, 10,
  10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12,
  12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13,
  13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
  14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
  14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
  15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15
};

//PSGパラメータ(簡易MMLの場合、ドラムのサスティンレベルが0以外だとキーオフできない場合あり)
//M,A,D,S,R,N_TP
//
//M       音色 0:矩形波+ノイズ、1:ノイズのみ、2:矩形波のみ
//A       エンベロープ・アタックレート0~127(PSGと互換性なし)
//D       エンベロープ・ディケイレート0~127(PSGと互換性なし)
//S       エンベロープ・サスティンレベル0~15(PSGと互換性なし)
//R       エンベロープ・リリースレート0~127(PSGと互換性なし)
//N_TP    ノイズ周波数 0~31(3チャンネル共通なので注意)
//D_TP    初期音程 0~127:初期音程をセットしない、128~255:-128の音程を初期音程としてセット(ドラムチャンネルでの使用を想定)
//res     予約
//配列0~127はMMLプログラムナンバーの0~127,配列128~255はドラムノートナンバー
const unsigned char PROGMEM psg_parameter[][8] = {
  //1
  {2, 1, 60, 3, 10, 0, 0, 0},
  {2, 1, 60, 3, 10, 0, 0, 0},
  {2, 1, 80, 3, 15, 0, 0, 0},
  {2, 1, 60, 3, 15, 0, 0, 0},
  {2, 1, 60, 3, 10, 0, 0, 0},

  {2, 1, 60, 3, 10, 0, 0, 0},
  {2, 0, 60, 1, 5, 0, 0, 0},
  {2, 0, 60, 1, 5, 0, 0, 0},
  {2, 1, 80, 1, 60, 0, 0, 0},
  {2, 0, 50, 1, 50, 0, 0, 0},
  //11
  {2, 0, 90, 1, 90, 0, 0, 0},
  {2, 1, 90, 1, 20, 0, 0, 0},
  {2, 1, 30, 1, 20, 0, 0, 0},
  {2, 0, 10, 0, 10, 0, 0, 0},
  {2, 0, 90, 1, 90, 0, 0, 0},
 
  {2, 1, 70, 1, 50, 0, 0, 0},
  {2, 1, 10, 14, 5, 0, 0, 0},
  {2, 2, 10, 14, 10, 0, 0, 0},
  {2, 2, 10, 14, 5, 0, 0, 0},
  {2, 2, 10, 14, 20, 0, 0, 0},
  //21
  {2, 2, 10, 14, 10, 0, 0, 0},
  {2, 2, 10, 14, 10, 0, 0, 0},
  {2, 2, 10, 14, 5, 0, 0, 0},
  {2, 2, 10, 14, 5, 0, 0, 0},
  {2, 0, 60, 2, 20, 0, 0, 0},
 
  {2, 0, 60, 2, 20, 0, 0, 0},
  {2, 0, 70, 2, 20, 0, 0, 0},
  {2, 0, 40, 7, 20, 0, 0, 0},
  {2, 1, 80, 2, 20, 0, 0, 0},
  {2, 1, 90, 2, 20, 0, 0, 0},
  //31
  {2, 1, 100, 3, 20, 0, 0, 0},
  {2, 1, 90, 3, 20, 0, 0, 0},
  {2, 2, 80, 6, 20, 0, 0, 0},
  {2, 2, 90, 6, 30, 0, 0, 0},
  {2, 0, 80, 6, 20, 0, 0, 0},
 
  {2, 2, 90, 6, 20, 0, 0, 0},
  {2, 1, 80, 6, 20, 0, 0, 0},
  {2, 1, 80, 6, 20, 0, 0, 0},
  {2, 2, 80, 6, 30, 0, 0, 0},
  {2, 2, 50, 8, 20, 0, 0, 0},
  //41
  {2, 8, 60, 14, 30, 0, 0, 0},
  {2, 8, 60, 13, 30, 0, 0, 0},
  {2, 7, 60, 14, 50, 0, 0, 0},
  {2, 6, 60, 14, 30, 0, 0, 0},
  {2, 8, 30, 14, 60, 0, 0, 0},

  {2, 3, 30, 0, 30, 0, 0, 0},
  {2, 3, 70, 1, 40, 0, 0, 0},
  {2, 1, 50, 0, 30, 0, 0, 0},
  {2, 4, 50, 13, 40, 0, 0, 0},
  {2, 4, 50, 13, 50, 0, 0, 0},
  //51
  {2, 8, 20, 14, 40, 0, 0, 0},
  {2, 6, 20, 14, 40, 0, 0, 0},
  {2, 4, 60, 15, 20, 0, 0, 0},
  {2, 3, 60, 13, 30, 0, 0, 0},
  {2, 3, 30, 14, 30, 0, 0, 0},

  {2, 0, 80, 0, 70, 0, 0, 0},
  {2, 1, 5, 14, 20, 0, 0, 0},
  {2, 1, 20, 14, 20, 0, 0, 0},
  {2, 1, 20, 14, 20, 0, 0, 0},
  {2, 3, 10, 14, 10, 0, 0, 0},
  //61
  {2, 3, 20, 14, 30, 0, 0, 0},
  {2, 0, 40, 14, 20, 0, 0, 0},
  {2, 2, 60, 14, 20, 0, 0, 0},
  {2, 2, 60, 12, 20, 0, 0, 0},
  {2, 4, 30, 14, 20, 0, 0, 0},
 
  {2, 1, 10, 14, 20, 0, 0, 0},
  {2, 1, 0, 15, 20, 0, 0, 0},
  {2, 4, 30, 13, 20, 0, 0, 0},
  {2, 1, 0, 15, 20, 0, 0, 0},
  {2, 1, 20, 14, 20, 0, 0, 0},
  //71
  {2, 1, 20, 14, 20, 0, 0, 0},
  {2, 1, 10, 14, 20, 0, 0, 0},
  {2, 1, 10, 14, 10, 0, 0, 0},
  {2, 2, 10, 14, 20, 0, 0, 0},
  {2, 1, 10, 14, 10, 0, 0, 0},

  {2, 1, 20, 14, 10, 0, 0, 0},
  {2, 1, 20, 12, 20, 0, 0, 0},
  {2, 1, 20, 13, 10, 0, 0, 0},
  {2, 4, 30, 14, 30, 0, 0, 0},
  {2, 1, 10, 14, 10, 0, 0, 0},
  //81
  {2, 0, 0, 15, 0, 0, 0, 0},
  {2, 1, 0, 15, 10, 0, 0, 0},
  {2, 2, 40, 14, 20, 0, 0, 0},
  {2, 1, 60, 13, 20, 0, 0, 0},
  {2, 0, 30, 14, 10, 0, 0, 0},
 
  {2, 3, 30, 14, 30, 0, 0, 0},
  {2, 1, 20, 14, 20, 0, 0, 0},
  {2, 1, 60, 9, 40, 0, 0, 0},
  {2, 1, 40, 13, 70, 0, 0, 0},
  {2, 20, 10, 14, 100, 0, 0, 0},
  //91
  {2, 1, 40, 13, 40, 0, 0, 0},
  {2, 2, 20, 14, 60, 0, 0, 0},
  {2, 30, 80, 9, 100, 0, 0, 0},
  {2, 8, 60, 12, 50, 0, 0, 0},
  {2, 3, 40, 13, 30, 0, 0, 0},
 
  {2, 80, 0, 15, 50, 0, 0, 0},
  {2, 1, 80, 0, 30, 0, 0, 0},
  {2, 10, 40, 14, 50, 0, 0, 0},
  {2, 1, 100, 0, 70, 0, 0, 0},
  {2, 1, 50, 14, 40, 0, 0, 0},
  //101
  {2, 0, 70, 1, 40, 0, 0, 0},
  {2, 80, 0, 15, 80, 0, 0, 0},
  {2, 1, 0, 15, 80, 0, 0, 0},
  {2, 5, 90, 5, 50, 0, 0, 0},
  {2, 1, 80, 2, 50, 0, 0, 0},
 
  {2, 1, 40, 6, 30, 0, 0, 0},
  {2, 1, 60, 1, 20, 0, 0, 0},
  {2, 0, 60, 0, 60, 0, 0, 0},
  {2, 1, 60, 0, 20, 0, 0, 0},
  {2, 2, 20, 14, 20, 0, 0, 0},
  //111
  {2, 1, 20, 14, 20, 0, 0, 0},
  {2, 1, 20, 14, 20, 0, 0, 0},
  {2, 0, 70, 1, 40, 0, 0, 0},
  {2, 0, 20, 0, 10, 0, 0, 0},
  {2, 1, 30, 0, 30, 0, 0, 0},
 
  {2, 0, 10, 0, 10, 0, 0, 0},
  {2, 2, 20, 0, 20, 0, 0, 0},
  {2, 1, 20, 0, 20, 0, 0, 0},
  {0, 1, 20, 0, 20, 0, 0, 0},
  {1, 90, 0, 15, 0, 0, 0, 0},
  //121
  {1, 3, 50, 0, 30, 0, 0, 0},
  {2, 5, 10, 0, 10, 0, 0, 0},
  {1, 5, 0, 0, 30, 18, 0, 0},
  {2, 1, 20, 0, 20, 0, 0, 0},
  {1, 0, 0, 15, 0, 18, 0, 0},

  {0, 100, 0, 15, 100, 2, 0, 0},
  {1, 70, 0, 15, 40, 0, 0, 0},
  {1, 0, 30, 0, 30, 0, 0, 0},

  //以降ドラム用音色
  //C-1~G9
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
 
  //C0(0x0C)
  {1, 0, 0, 0, 0, 0, 0, 0},
  {1, 1, 10, 0, 10, 30, 0, 0},
  {1, 1, 30, 0, 30, 30, 0, 0},
  {1, 0, 2, 0, 2, 0, 0, 0},
  {1, 0, 20, 0, 20, 0, 0, 0},
  {1, 2, 20, 0, 20, 15, 0, 0},
  {1, 2, 20, 0, 20, 20, 0, 0},
  {1, 0, 8, 0, 8, 1, 0, 0},
  {1, 0, 0, 0, 0, 10, 0, 0},
  {1, 0, 15, 0, 15, 0, 0, 0},
  {0, 0, 20, 0, 20, 0, 0x80 | 103, 0},
  {2, 0, 6, 0, 6, 3, 0x80 | 96, 0},
  //C1
  {2, 0, 6, 0, 0, 1, 0x80 | 100, 0},
  {1, 1, 20, 0, 20, 6, 0, 0},
  {1, 2, 30, 0, 30, 6, 0, 0},
  {1, 1, 20, 0, 20, 2, 0, 0},
  {1, 0, 20, 0, 30, 6, 0, 0},
  {1, 1, 30, 0, 30, 31, 0, 0},
  {1, 1, 8, 0, 8, 5, 0, 0},
  {1, 0, 20, 0, 20, 13, 0, 0},
  {1, 0, 10, 0, 10, 4, 0, 0},
  {0, 0, 5, 0, 5, 25, 0x80 | 20, 0},
  {1, 0, 18, 0, 18, 2, 0, 0},
  {0, 1, 6, 0, 6, 30, 0x80 | 20, 0},
  //C2
  {0, 1, 5, 0, 5, 30, 0x80 | 20, 0},
  {1, 1, 10, 0, 10, 15, 0, 0},
  {1, 0, 20, 0, 20, 2, 0, 0},
  {1, 1, 15, 0, 15, 10, 0, 0},
  {1, 0, 20, 0, 20, 6, 0, 0},
  {0, 0, 20, 0, 20, 20, 0x80 | 30, 0},
  {1, 0, 8, 0, 8, 1, 0, 0},
  {0, 0, 20, 0, 20, 17, 0x80 | 32, 0},
  {1, 2, 10, 0, 10, 1, 0, 0},
  {0, 0, 20, 0, 20, 14, 0x80 | 34, 0},
  {1, 0, 30, 0, 30, 1, 0, 0},
  {0, 0, 20, 0, 20, 11, 0x80 | 36, 0},
  //C3
  {0, 0, 20, 0, 20, 8, 0x80 | 38, 0},
  {1, 0, 60, 0, 60, 4, 0, 0},
  {0, 0, 20, 0, 20, 5, 0x80 | 40, 0},
  {1, 3, 40, 0, 40, 3, 0, 0},
  {1, 0, 50, 0, 50, 10, 0, 0},
  {1, 1, 50, 0, 50, 7, 0, 0},
  {1, 2, 10, 0, 10, 0, 0, 0},
  {1, 0, 50, 0, 50, 1, 0, 0},
  {0, 0, 15, 0, 15, 31, 0x80 | 72, 0},
  {1, 0, 60, 0, 60, 0, 0, 0},
  {1, 0, 50, 0, 50, 20, 0, 0},
  {1, 3, 40, 0, 40, 0, 0, 0},
  //C4
  {1, 0, 6, 0, 6, 3, 0, 0},
  {0, 0, 6, 0, 6, 6, 0x80 | 68, 0},
  {1, 0, 6, 0, 6, 3, 0, 0},
  {0, 0, 15, 0, 15, 6, 0x80 | 74, 0},
  {0, 0, 15, 0, 15, 15, 0x80 | 72, 0},
  {0, 0, 30, 0, 30, 10, 0x80 | 74, 0},
  {0, 0, 30, 0, 30, 20, 0x80 | 72, 0},
  {0, 0, 10, 0, 10, 4, 0x80 | 89, 0},
  {0, 0, 10, 0, 10, 7, 0x80 | 84, 0},
  {1, 2, 10, 0, 10, 1, 0, 0},
  {1, 1, 15, 0, 15, 1, 0, 0},
  {2, 1, 10, 0, 8, 5, 0x80 | 96, 0},
  //C5
  {2, 1, 10, 0, 8, 5, 0x80 | 94, 0},
  {1, 1, 4, 0, 4, 5, 0, 0},
  {1, 10, 1, 0, 1, 15, 0, 0},
  {2, 0, 10, 0, 10, 0, 0x80 | 96, 0},
  {2, 0, 8, 0, 8, 0, 0x80 | 84, 0},
  {2, 0, 8, 0, 8, 0, 0x80 | 80, 0},
  {2, 7, 5, 0, 5, 0, 0x80 | 77, 0},
  {2, 7, 5, 0, 5, 0, 0x80 | 54, 0},
  {0, 0, 3, 0, 3, 0, 0x80 | 104, 0},
  {0, 0, 50, 0, 50, 0, 0x80 | 104, 0},
  {1, 2, 15, 0, 15, 15, 0, 0},
  {1, 2, 40, 0, 40, 28, 0, 0},
  //C6
  {1, 10, 20, 0, 20, 8, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  //C7
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  //C8
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  //C9
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {1, 80, 0, 0, 0, 1, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},

};

//---------------演奏したいMMLに合わせて要変更--------------------
// デフォルトオクターブ[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_tone[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_oct[16] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};

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

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

//ドラムチャンネル 状態管理用
char drum_note[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};//0:未発音 1:発音中
char drum_tone_temp = 0;//ドラムチャンネルの一時保存中の音程
char drum_tone_num[16] = {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128};//保存中の音程



// ドラム発音用
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分音符の長さ)
//   ※(音長)の指定で数字のみの場合N分音符、%を付けると直接時間指定Sとなります。
//   例:4分音符+32分音符(system_time_base=480の時)
//   S = ((1/4)+(1/32)) * 4 * system_time_base
//     = 540
//  ・トラック10(ch9、0x09)のドラムパート場合のみ、和音(音長が0)が使えます。
//

unsigned int system_time_base = 480;//%で音長を指定する場合に必要
//動く、動くt128
const unsigned char PROGMEM mml_data[] ="t128@81v50p64r1o2g16.r%540o6a16>a16r16<d16r16o2a12...r32b16.r%780o6c+16d16>a8r4.o2g16.r%540o6a16>a16r16<d16r16o2a12...r32b16.r%780o6e16f+16d8r4.o2g16.r%540o6a16>a16r16<d16r16o2a12...r32b16.r%780o6c+16d16>a8r4.o2g16.r%540o6a16>a16r16<d16r16o2a12...r32b16.r%780o6e16f+16d8r4.o3g16.r%540o6a16>a16r16<d16r16o3a12...r32b16.r%780o6c+16d16>a8r4.>>g16.r%540o6a16>a16r16<d16r16o3a12...r32b16.r1^32g16.r%540o6a16>a16<a16d32r16.o3a12...r32b16.r%780o6a16>a16<a16d16r16e16r4o3g16.r%540o6a16>a16<a16d16r16o3a12...r32b16.r1^32g16.r%540o6a16>a16<a16d32r16.o3a12...r32b16.r%780o6a16>a16<a16d16r16e16r4o3g16.r%540o6a16>a16<a16d16r16o3a12...r32b16.r1^32o7a3...g4.d2>d64a64d64a64d64a64d64a64d64a64d64a64d64a64d64a64>b64d16..c+32c32>b32a+64a64r4>f+8r3...o7a4r4>d64a64d64a64d64a64d64a64d64a64d64a64d64a64d64a64>b64r8...e16.f32f+2e4d8g+32a24..a+24..b24..<c24..c+24..d24..d+24..e24..f24..f+16g16g+16a16a+16b16r16<c+16r16d16r16e16r16f+16r16g+16r16a16r16b16r16<c+32r32d32r32d+32r32e32r32f32r32f+32r32g32r32g+32r32o4e24r%880o7c8r8c8r8c8r8c8r2.>c8r1^4f8r2.a8<c8r8c8r8c8r8c8r2>e8r1^2f8r16f8r16f16r16c4r4<f8r4.f8r4.f8r4.f8r4.f8r4.f8r4f16r16f8r4f8>f8e8c8>f8r1^1^2..c32r32c32r32>e8r16e8r16e8e4r4>>g16.r%540o6a16>a16r16<d16r16o2a12...r32b16.r%780o6c+16d16>a8r4.o2g16.r%540o6a16>a16r16<d16r16o2a12...r32b16.;@58v127p64r1^1^1^1^1o5f+8r%1080c+8r16d8r2d16r16e16r16f+8g8f+8r%1080e8r16d8r1f+8r%1080c+8r16d8r2d16r16e16r16f+8g8f+8r%1080a8r16d8r2d16r16e8d8r4f+16f+16f+16f+16r8f+8r16d8r6...f+16f+16f+8f+16r16f+8d16d8.r4f+16f+16f+16f+16f+16f+16f+8r16d8r8.d16r16e16r16d8r8d16r16e16r16d16r6...f+16f+16f+16f+16r8f+16f+16d16d8r6...f+16f+16f+8f+16r16f+8d16d8.r4f+16f+16f+16f+16f+16f+16f+8d16d8r8.d16r16e16r16d8r8d16r16e16r16d16r12...f+64g64a%1080r16g4f+8d4.r4d8e8f+8g8f+8.r16d8.r16c+8e4d4.r4c+8d16r16e16r16a%1080r16g4f+8d4..r8.c+8d8e8f+8e8r8d4d16r16a4a4..r8.f16r16g8f16r16<c8r8c8r8c8r8>a+8a8g16r16f8g16r16a16r8.a8g8f16r16f16r16a16g8.f16r16f16r16a16g8.f16r16f8c16r16f16r16g16r8.a+8a8f16.r32<c8r8c8r8c8r8>a+8a8g16r16f8g16r16a16r8.a8g8f16r16f16r16a16g8.f16r16f16r16a16g8.f16r16f4..r8.f8g16r16f16r16<c8r8c8r8c8r8>a+8a8g16r16f8g16r16a16r8.a8g8f16r16f16r16a16g8.f16r16f16r16a16g8.f16r16f8c16r16f16r16g16r8.a+8a8f8<c8r8c8r8c8d8>a8g8f16r16g8a+8a8r8a8g8f16r16f8r8a8r8<c8r8>e8r8f8r16f8r16f16r16f8.r16e8r8d8;@34v100p64r1o3g16.r%1140a12...r32b16.r1^32g16.r%1140a12...r32b16.r1^32g16.r%1140a12...r32b16.r1^32g16.r%1140a12...r32b16.r1^32>g16.r%1140a12...r32b16.r1^32g16.r%1140a12...r32b16.r%1620<d16>a16.r32g16.r%1140a12...r32b16.r1^32g16.r%1140a12...r32b16.r1^32g16.r%1140a12...r32b16.r1^32g16.r%1140a12...r32b16.r1^32<c2.r1^4>b2.r1^4e4e4e4e4f+4f+4f+4f+4g8g8g8g8g8g4g8a8a4.r2a+8a+8r8a+16a+16<c8<c8>c8<c8>d8<d8>d8<d8>d8<d8>d8d16c16>a+8<a+8f8>a+8<c8<c8>c8<c8>f8<f8>f8<f8>f8<f8>f8f16g16>a+8<a+8f8>a+8<c8<c4.>d8<d8>d8<d8>d8<d8>d8d16c16>g4<g8>g8<c8<c8>c8<c8>>f8.f8.f8f4r4a+8<a+8f8>a+16a+16<c8<c8>c8<c8>d8<d8>d8<d8>d8<d8>d8d16c16>a+8<a+8f8a+8c8<c8>c8<c8>f8<f8>f8<f8>f8<f8>f8f16g16>a+8<a+8f8>a+8<c8<c8>c8<c+8>c+8<c+8>c+8<d8>d4r8d8>g8<g8f8g8c8<c4c8>>f8.f8.f8f4r8a8<g16.r%1140a12...r32b16.r1^32g16.r%1140a12...r32b16.;@81v80p64r1o6d24...r%330a16>a16r16<d16r8.>a%270r32.a24...r%570<c+16d16>a8r2<d24...r%330a16>a16r16<d16r8.e%270r32.f+24...r%570e16f+16d8r2d24...r%330a16>a16r16<d16r8.>a%270r32.a24...r%570<c+16d16>a8r2<d24...r%330a16>a16r16<d16r8.e%270r32.f+24...r%570e16f+16d8r2d24...r%330a16>a16r16<d16r8.>a%270r32.a24...r%570<c+16d16>a8r2<d24...r%330a16>a16r16<d16r8.e%270r32.f+24...r1^32.d16.r32a16>a16<d32r32d16r16e16r16f+16r16c+12...r32>a16.r12...<a16>a16r16<d16r16e16f+32r32f+16r16g16f+8r8d16.r32a16>a16r16<d16r16e16r16f+16r16c+12...r32>a16.r1^32<d16.r32a16>a16<d32r32d16r16e16r16f+16r16c+12...r32>a16.r12...<a16>a16r16<d16r16e16f+32r32f+16r16g16f+8r8d16.r32a16>a16r16<d16r16e16r16f+16r16c+12...r32>a16.r%1100e24a24<d24a24<d24e24a24e24d24>a24d24r8o3g8r16g8r16g8.r1^8.o6d8c+4d8c+4>a8>f+8a8r1>e8r8e8.r16e8r8e8.r16f+8r8f+8.r16f+8<d8>a8..r32g16.r32g16.r32g16.r32g16.r32g16.r32g8..r32g16.r12...a4.r24<g24<c24r24<c24f24<g24c24>g24f24c24>f24>>a+4r8a+8<c8r8c8r8d8r8d8r8d8r8d8r8>a+8r4a+8<c8r8c8r8c8r4.c8r8c8r8>a+8r4a+8<c8r4.d8r8d8r8d8r8d8r8>g4r8g8<c8r8c8r8<<c8r16c8r16c16r%200>>g24<c24r24<c24f24g24<c24>g24f24c24>f24v50<f8r8f8g8f8f8c8g8f6...r16f8f8f8c8g8f8.r16e8g8f8.r16e8g8c4f8f8v80>>f24g24<c24f24<c24f24g24<c24>g24f24c24>f24v50<f8r8f8r8f8f8r8c+8c+8r8c+8c4f8c64>a64g64f64d64r32.f64d64c64>a64g64r32.>g8r4.<<c2>>f8r16f8r16f16r16<f24v80g24<c24f24<c24f24g24<c24>g24f24c24>f24<d16.r12...a16>a16r16<d16r8.>a12...r32a16.r%540<c+16d16>a8r2<d16.r12...a16>a16r16<d16r8.e12...r32f+16.;@81v60p64r1o5a24...r%1170<c+%270r32.d24...r1^32.>a24...r%1170<c+%270r32.d24...r1^32.f+24...r%1170c+%270r32.d24...r1^32.f+24...r%1170c+%270r32.d24...r1^32.f+24...r%1170c+%270r32.d24...r1^32.f+24...r%1170a%270r32.d24...r1^32.>a16.r12...<a16>a16<d32r32d16r16e16r16>a12...r32<d16.r%540a16>a16r16<d16r16e16r16f+32r16.g16f+8>a16.r12...<a16>a16r16<d16r16e16r16e12...r32f+16.r1^32>a16.r12...<a16>a16<d32r32d16r16e16r16>a12...r32<d16.r%540a16>a16r16<d16r16e16r16f+32r16.g16f+8>a16.r12...<a16>a16r16<d16r16e16r16e12...r32f+%50r1^48..v30>a48r48<e48g+8a2f+32g8..f+16.r32d2r2..v80o3f+4f+16r32f+16r32f+16r32f+16r32f+24r%1840v60o6a4a8a16a16a8g4.d2.r4f+4r2a3.....r%1140>f8r4g8r8<f8<c8>g8r8f8<c8>g16g16r8f8g64g+64g64f+64g64g+64g64f+64g64g+64g64f+64g64g+64g64f+64r4c8>g8r3...f8<c16c16>a+16r8.f4r8f8r4g8r8<f8<c8>g8r8f8<c8>g8r8f8g64g+64g64f+64g64g+64g64f+64g64g+64g64f+64g64g+64g64f+64r8c16r16>a8g8r2>f8r16f8r16f12r24f4o8c64>b64a64g64f64e64d64c64>b96a96g96f96e96d96c96>b96a96g96f96d+96<f8r8<c8>g8r8f8<c8>g8r8f8<c8>g16g16r4g64g+64g64f+64g64g+64g64f+64g64g+64g64f+64g64g+64g64f+64c8a8g8<c8>f8a16g8.f8r8>f8r8e8r8f8r4<f8r8<c8>g8r8f8<c8>g8r8f8<c8>g8r8f8g64g+64g64f+64g64g+64g64f+64g64g+64g64f+64g64g+64g64f+64c8r8>f8r4f8r4<f8r16f16.r16.f16r16f16.r%780>a16.r%1140<c+12...r32d16.r1^32>a16.r%1140<c+12...r32d16.;@25v80p64r1o5d16r3...e12...r32f+16.r1^32d16r3...e12...r32f+16.r%1020d8c+16d16>a4<d16r3...<e12...r32>>b16.r1^32<d16r3...>e12...r32f+16.r%1020<f+8a8f+16e16d8d16r3...<e12...r32>>b16.r1^32<d16r3...e12...r32a16.r1^32d16r3...e12...r32f+16.r1^32d16r3...e12...r32f+16.r1^32d16r3...e12...r32f+16.r1^32d16r3...e12...r32f+16.r1^32>g2r4.g4.r%1500b%660r4b4.v50<<d8e8d8c+8.r16d8a8r32v80>>b32a32f+32b2..f+8a16a16a16a16a8a16a16a2<d8>b4b8b4o7c+4c8c+8r32c+16.c+8r2>>c4f4r8f8r8f8f4.e16f16r8f4.c4f4>e4.<<c8c8>f8r4f8f8f8<f16r16>c4f4r8f8r8f8f8r8f8e4r8f8r8d4d8r4f8r8c8>>f8.f8.f8f8.r6...<<c4f4r8f8r8f8f8r8f8e16f8.r8f8r4.e4>e4.r4<f8<c8r8>f8f8f8r8f4e8g8f8f4r4f4e8f4g4d4f4c4>g4<a8r16a8r16a8a4r4d16r3...e12...r32f+16.r1^32d16r3...e12...r32f+16.;@25v80p64r1o4g16.r%1140<c+12...r32d16.r1^32>g16.r%1140<c+12...r32d16.r%1020>a8a8e4g16.r%1140e12...r32f+16.r1^32g16.r%1140<c+12...r3.....c+8d8c+16>b16a8g16.r%1140e12...r32f+16.r1^32g16.r%1140<c+12...r32>a16.r1^32g16.r%1140<c+12...r32d16.r1^32>g16.r%1140<c+12...r32d16.r1^32>g16.r%1140<c+12...r32d16.r1^32>g16.r%1140<c+12...r32d16.r1^32>e4e4.r8e4e2r%990f+%1170r1^%540f+32e32d32e2>b2<f+2.r8<c+32c32>b32a+32g4g4.g4.a16a16a16a16a16a16a16a16r2a+4.a+8<c2d8c8d4d2>a+4a+8a+8<c4.r4.>a8<c8c4c8.r16>a+4.a+8<c2d8c8d4d4r4>g4g8<c8>e2<c8.c8.c8c4r4>a+8a+8a+8a+8<c8c4c8d8c8d4d4r8d8>a+8a+8a+8a+8r8<c4r8c8r4c8c4c8c8>a+8a+4a+8<c4c8c+8c+8c+8c+4d2c2>g4<c4c8.c8.c8c4r4>g16.r%1140<c+12...r32d16.r1^32>g16.r%1140<c+12...r32d16.;@25v80p64r1o4d16.r%1140a12...r32a16.r1^32d16.r%1140a12...r32b16.r1^32d16.r%1140a12...r32a16.r1^32d16.r%1140a12...r32b16.r1^32d16.r%1140a12...r32a16.r1^32d16.r%1140a12...r32b16.r%520v36r96<d192d+96e8...r%646v80r%314>d16.r%1140a12...r32a16.r1^32d16.r%1140a12...r32b16.r%1020<d16d16d16d16d8r8>d16.r%1140a12...r32a16.r1^32d16.r%1140a12...r32b16.r1^32c%1080r16c4.c2..r8>b3...b1^8.r8<d32c32>b4b4<d8e4e8d1d4.d4.d8d8r8<e16e16r16e8.r2>f4f4g4g4a4a4a4a8g8f4f4g4g4f8r8c16c16f4f4g16r16f4f4g4g4a4a4a4a8g8f4f8g8g4g4a8r16a8r16a8a4r4f4f4g4g4a4a4a4a8g8f4f4g4g4f4f8f4f4g8f4f4g4g8g8g8g8g8g8a2g2e2a8r16a8r16a8a4r4d16.r%1140a12...r32a16.r1^32d16.r%1140a12...r32b16.;@58v80p64r1^2..o4b16.r1^1^1^32<a8r%1080e8r16f+16.r1^32a8r%1080g8r16f+16.r1^32a8r%1080e8r16f+16.r1^32a8r%1080<c+8r16>f+16.r1^12...a16a16a16a16r8a8r16f+8r16>b16.r12...<a16a16a8a16r16a8.f+8.r4a16a16a16a16a16a16a8r16f+8r16<d16.r%1020>f+16r16g16r16f+16r6...a16a16a16a16r8a8r16f+8.>b16.r12...<a16a16a16a16a16r16a8f+16f+8.r4a16a16a16a16a8a8f+32r32f+8r16<d16.r%1020>f+16r16g16r16f+8.r%113v50r%367>a4...r12...g16r8.f+8d%1020r%540v80r1^1<<c+%1080r16>b4a8f+4..r8.>a8a8<d8a8g8r8f+4f+16r16<c+4c+4..r8.>a16r16a+8a16r16<f8r8f8r8f8r8d8c8>a+16r16a8a+16r16<c16r8.>f8d8c16r16c16r16g16f8.c16r16c16r16g16f8.c16r16c8>a16r16<c16r16f16r8.<d8c8>a16.r32<f8r8f8r8f8r8d8c8>a+16r16a8a+16r16<c16r8.>f8d8c16r16c16r16<c16>a+8.a16r16c16r16<c16>a+8.a16r16a4..r8.a8a+16r16a16r16<f8r8f8r8f8r8d8c8>a+16r16a8a+16r16<c16r8.>f8d8c16r16c16r16g16f8.c16r16c16r16g16f8.c16r16c8>a16r16<c16r16f16r8.<d8c8>a8<f8r8f8r8f8g8c8>a8a16r16<c+8d8c+8r8c8>a8d16r16a8r8<c8r8f8r8>g8r8<c8r16c8r16c16r16c8.r1^8.>>b16.;v80p64r1o2d+0>g+0<<g+16r8.<<c16>g+16r16g+16r8.>g+0>d+16r8<g+0>d+16r1^16>g+0<d+0<g+16r8.<<c16>g+16r16g+16r8.>>d+0<g+16r8g+0>d+16r%1080d+0c+0>e0<<f+0>>g+16<c+0<f+16>d+0<f+0>>e16<g+0>e0g+0<e0d+16<d+0>c+0d16r8.>g+0<<g+0>d+16r8.o5c16>g+16r16g+16r8.>>d+0<g+16r8>d+0<g+16r1^16>>g+0<<g+0>d+16r8.o5c16>g+16r16g+16r8.>>d+0<g+16r8>d+0<g+16r1^16>>g+0<d+0b0<g+16r16f+16r16<<c16>g+16>f+16<g+16r8>f+16g+0>b0d+16r8d+0b0<f+16r8.f+16r8.f+16r8.f+16r8.f+16r16>>g+0<d+0b0<g+16r16f+16r16<<c16>g+16>f+16<g+16r8>f+16g+0>b0d+16r8d+0<f+0>b16r1^16b0<g+16r16f+16r16d0>b0d+0<a+16r16f+16r16>b16r16<f+16>d+0<a+0d16>b16r16d+0<a+0d16r16g+0>b16r16<f+16r16>d+0b0<a+0d16r16f+16r16>b16r16<f+16r16>d+0b0<d0a+16r16f+16r16g+0>b16r16<f+16r16>b0<d0>d+0<a+16r16f+16r16>b16r16<f+16a+0>d+0<d16>b16r16<a+0>d+0<d16r16g+0>b16r16<f+16r16d0a+0>b0d+16r16<f+16r16>b16r16<f+16r16>b16r16<f+0>b16r16<g+0>b16r16<f+16r16a+0>d+0b0<d16r16f+16r16>b16r16<f+16a+0d0>d+16b16r16<d0>d+0<a+16r16>b0<g+16r16f+16r16a+0>d+0b0<d16r16f+16r16>b16r16<f+16r16d0>d+0<a+0>b16r16<f+16r16>b0<g+16r16f+16r16d0a+0>b0d+16r16<f+16r16>b16r16<f+16d0>d+0<a+16>b16r16<d0>d+0<a+16r16g+0>b16r16<f+16r16>b16r16<f+16r16>b16r16<f+16r8.a+16r16>b16r16<f+16r8.f+0<<c0o1d0<b16r16b16r16o5c0>>f+0>c+16r16<<g+16r16>f+16r16>b16r16<f+16r8.<<c0o1d0<<f+0>b16r16e0b16e16c+0o5c0>>f+0>e16e16<<g+0>>e16r16<f+16r16>b16r16<f+16r8.>b0<f+0>>d0o5c16r16o2b16r16<f+0<<c0o2c+16r16<<g+16r16>f+16r16>b16r16<f+16r8.>b0>d0o5c0>>f+16r16>e0b16e16e0c+0o5c0>>f+16f+0>e16d+0<a+0d0>e0<<g+16r8.>>b0<a+0>>e0<g0d+0<d16f+16f+16f+16a+0>g0>e0<d+0b0<d16f+16f+16f+16>d+0g0b0<a+0>>e0<<d16f+16f+16f+16a+0>g0>e0<d+0<d0>b16<f+16f+16f+16>g0<a+0>b0<d0>>e0<d+16<f+16f+16f+16>g0b0<a+0>d+0>e0<<d16f+16f+16f+16d0>>e0<g0<a+0>b0d+16<f+16f+16f+16>d+0>e0<b0<a+0>g0<d16f+16f+16f+16>d+0b0>e0<<a+0d0>g16<f+16>g0>e0<d+0<d0a+0>b16<f+16d0>>e0<d+0g0<a+0>b16<f+16>>e0<<a+0d0>b0d+0g16<f+16>g0<a+0>>e0<<d0>b0d+16<f+16>>e0<<d0>b0d+0g0<a+16f+16a+0>g0>e0<b0d+0<d16f+16>>e0<b0d+0g0<a+0d16f+16f+0d0>b16<d0f+0>b16b0<a+0>d+0<d0f+16f+0d0>b16<d0f+0>b32b32<f+0d0>b32b32b0<f+0d0r32>b32<f+0>b0<d0r32>b32r2<g+0>b0<<a+0c+16r16>a+16r16d0f+0>b0d+0<d+16r16a+16r16g+0>b16r16<a+16r16>d+0<f+0>b0<d+0d16r16a+16r16>b0<g+16r16a+16r16>b0<d0>d+0<f+0d+16r16a+16r16>b0<g+16r16a+0d+16r16>b0<d+0d0f+0>d+16r16<a+16r16>b0<g+16r16a+16r16f+0d0d+0>b0d+16r16<a+16r16>b0<g+16r16a+16r16d+0>b0d+0<f+0d16r16a+16r16>b0<g+16r16a+16r16>b0<d+0d0>d+0<f+16r16a+16r16g+0>b16r16<d+0a+16r16>d+0<d0d+0>b0<f+16r16a+16r16g+0>b16r16<a+16r16d+0>b0d+0<f+0d16r16a+16r16g+0>b16r16<a+16r16>d+0<f+0d0d+0>b16r16<a+16r16>b0<g+16r16a+16r16d+0f+0d0>b0d+16r16<a+16r16g+0>b16r16<a+0d+16r16d0f+0>d+0b0<d+16r16a+16r16g+0>b16r16<a+16r16>b0d+0<d+0f+0d16r16a+16r16g+0>b16r16<a+16r16d+0>d+0<d0f+0>b16r16<a+16r16a+0>d+0<d0>b0g+0<f+0d+16r8d+0f+0>g+0<a+0>b0<d0>d+16r8d+0b0<d0f+0a+0d+0>g+16r16<f+0a+0d+0>b0g+0d+0<d16r8.f+0>b16r16<a+16r16>b0<<a+0>g+0<c+16r16>a+16r16d0>b0d+0<f+0d+16r16a+16r16g+0>b16r16<a+16r16>d+0<d+0f+0d0>b16r16<a+16r16g+0>b16r16<a+16r16d0f+0>b0<d+0>d+16r16<a+16r16g+0>b16r16<a+0d+16r16f+0>d+0b0<d+0d16r16a+16r16g+0>b16r16<a+16r16f+0>d+0<d+0>b0<d16r16a+16r16g+0>b16r16<a+16r16d+0f+0d0>b0d+16r16<a+16r16g+0>b16r16<a+0<f16r16>d+0f+0>d+0b0<d16r16a+16r16>b0<g+16r16d+0a+16r16d+0>b0d+0<f+0d16r16a+16r16g+0>b16r16<a+16r16>d+0<d+0f+0>b0<d16r16a+16r16>b0<g+16r16a+16r16d+0d0>d+0<f+0>b16r16<a+16r16>b0<g+16r16a+16r16d0f+0>b0<d+0>d+16r16<a+16r16>b0<g+16r16d+0a+16r16>b0<f+0d0d+0>d+16r16<a+16r16>b0<g+16r16a+16r16f+16r16a+16r16f+0>b16r16<a+16r16f+16r16a+16r16>b0<d0>d+0<d+0a+0f+0>g+16r8<d+0a+0>b0<f+0d0>d+0g+16r8<f+0d0>b0<a+0d+0>d+0g+16r16g+0d+0<a+0>b0<f+0d0d+16r4..>>g+0<d+0<g+16r8.<<c16>g+16r16g+16r8.>>d+0<g+16r8>d+0<g+16r1^16>>g+0<d+0<g+16r8.<<c16>g+16r16g+16r8.>g+0>d+16r8d+0<g+16;";

// タイマー割り込み~~~~~~~~~~~~~~~~~~~~~~~~~
//演奏の時間処理とエンベロープ処理
ISR(TIMER1_COMPA_vect)
{
  char vol_temp;                 // エンベロープ音量計算用

  // 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 * 64.0 * tempo / 625.0); // msをMMLカウンタ値に変換(トラックごとの演奏ずれをできるだけ抑制するため)
  }

  // ソフトウェアエンベロープ用
  for (int env_ch = 0; env_ch < CH_T; env_ch++)
  {
    if ((psg_env_s[env_ch] & 0x01) == 0)
    {
      // エンベロープ処理
      switch (psg_env_s[env_ch])
      {
      case 0:
        // 待機
        continue;
      case 2:
        // アタック
        if (psg_env_cnt[env_ch] >= 15)
        {
          psg_env_s[env_ch] = 3; // ディケイ開始待機へ
        }
        else
        {
          // カウント
          psg_env_cnt_div[env_ch]++;
          if (psg_env_cnt_div[env_ch] >= psg_env_ar[env_ch])
          {
            psg_env_rr_vol[env_ch] = psg_main_vol[env_ch];//リリース時のための音量保存
           
            // 音量の操作
            vol_temp = (int)((int)psg_env_cnt[env_ch] * psg_main_vol[env_ch]) >> 4;
            if (vol_temp <= 0)
            {
              vol_temp = 0;
            }
            if (intwr == 0)
            {
              psg_write(div3_tb[env_ch], AMP + div3rem_tb[env_ch], 0x0F & vol_temp);
              psg_env_cnt_div[env_ch] = 0;
              psg_env_cnt[env_ch]++;
            }
            else
            {
              // メインの方で書き込み中の場合、後で処理
              psg_env_cnt_div[env_ch]--;
            }
          }
        }
        break;
      case 4:
        // ディケイ
        if (psg_env_cnt[env_ch] <= 0)
        {
          psg_env_s[env_ch] = 7; // 終了処理へ
        }
        else if (psg_env_cnt[env_ch] <= psg_env_sl[env_ch])
        {
          //リリース開始まで待機
         
        }
        else
        {
          // カウント
          psg_env_cnt_div[env_ch]++;
          if (psg_env_cnt_div[env_ch] >= psg_env_dr[env_ch])
          {
            psg_env_rr_vol[env_ch] = psg_main_vol[env_ch];//リリース時のための音量保存
           
            if (intwr == 0)
            {
              // 音量の操作
              vol_temp = (int)((int)psg_env_cnt[env_ch] * psg_main_vol[env_ch]) >> 4;
              if (vol_temp <= 0)
              {
                vol_temp = 0;
              }
              psg_write(div3_tb[env_ch], AMP + div3rem_tb[env_ch], 0x0F & vol_temp);
              psg_env_cnt_div[env_ch] = 0;
              psg_env_cnt[env_ch]--;
            }
            else
            {
              // メインの方で書き込み中の場合、後で処理
              psg_env_cnt_div[env_ch]--;
            }
          }
        }
        break;
      case 6:
        // リリース
        if (psg_env_cnt[env_ch] <= 0)
        {
          psg_env_s[env_ch] = 7; // 終了処理へ
        }
        else
        {
          // カウント
          psg_env_cnt_div[env_ch]++;
          if (psg_env_cnt_div[env_ch] >= psg_env_rr[env_ch])
          {
            if (intwr == 0)
            {
              // 音量の操作
              vol_temp = (int)((int)psg_env_cnt[env_ch] * psg_env_rr_vol[env_ch]) >> 4;
              if (vol_temp <= 0)
              {
                vol_temp = 0;
              }
              psg_write(div3_tb[env_ch], AMP + div3rem_tb[env_ch], 0x0F & vol_temp);
              psg_env_cnt_div[env_ch] = 0;
              psg_env_cnt[env_ch]--;
            }
            else
            {
              // メインの方で書き込み中の場合、後で処理
              psg_env_cnt_div[env_ch]--;
            }
          }
        }
        break;
      default:
        break;
      }
    }
    else
    {
      // 準備
      switch (psg_env_s[env_ch])
      {
      case 1:
        // アタック開始待機
        //アタックが0の時、初期音量を上げる
        if (psg_env_ar[env_ch] == 0)
        {
          psg_env_cnt[env_ch] = 15;
        }
        else{
          psg_env_cnt[env_ch] = 0;
        }
        psg_env_cnt_div[env_ch] = 0;
        psg_env_rr_vol[env_ch] = psg_main_vol[env_ch];//リリース時のための音量保存
        if (intwr == 0)
        {
          psg_write(div3_tb[env_ch], AMP + div3rem_tb[env_ch], 0x00);
          psg_env_s[env_ch] = 2; // アタック開始へ
        }
        else
        {
          // メインの方で書き込み中の場合、後で処理
        }
        break;
      case 3:
        // ディケイ開始待機
        // 音量の操作
        vol_temp = (int)((int)psg_env_cnt[env_ch] * psg_main_vol[env_ch]) >> 4;
        if (vol_temp <= 0)
        {
          vol_temp = 0;
        }
        if (intwr == 0)
        {
          psg_write(div3_tb[env_ch], AMP + div3rem_tb[env_ch], 0x0F & vol_temp);
          psg_env_cnt_div[env_ch] = 0;
          psg_env_s[env_ch] = 4; // ディケイ開始待機へ
        }
        else
        {
          // メインの方で書き込み中の場合、後で処理
        }
        break;
      case 5:
        // リリース開始待機
        // 音量の操作
        vol_temp = (int)((int)psg_env_cnt[env_ch] * psg_env_rr_vol[env_ch]) >> 4;
        if (vol_temp <= 0)
        {
          vol_temp = 0;
        }
        if (intwr == 0)
        {
          psg_write(div3_tb[env_ch], AMP + div3rem_tb[env_ch], 0x0F & vol_temp);

          psg_env_cnt_div[env_ch] = 0;
          psg_env_s[env_ch] = 6; // リリース開始
        }
        else
        {
          // メインの方で書き込み中の場合、後で処理
        }

        break;
      case 7:
        // 終了後の処理
        if (intwr == 0)
        {
          psg_env_cnt[env_ch] = 0;
          psg_env_s[env_ch] = 0; // 待機へ
          psg_write(div3_tb[env_ch], AMP + div3rem_tb[env_ch], 0x00);
          break;
        }else
        {
          // メインの方で書き込み中の場合、後で処理
        }
      default:
        break;
      }
    }
  }
}

// セットアップ
void setup()
{
  // デバッグメッセージ用
  Serial.begin(SERIALSPEED);
  // ポート設定
  //  out_put
  DDRD = 0xFC;  // pin2~pin7_psg:D2~D7
  PORTD = 0xFF; // pin2~pin7_psg:D2~D7

  // BC2はH固定の場合
  DDRB = 0x3F;  // pin8~pin9_psg:D0~D1   pin11~pin13_psg:BDIR, BC1, ~RESET
  PORTB = 0x20; // pin8~pin9_psg:D0~D1   pin11~pin13_psg:BDIR, BC1, ~RESET

  // CSは74HC138へ接続、A8はH固定、~A9は74HC138の各~Yへ接続
  DDRC = 0x07;  // pin14~pin17_psg:CS0 ~ CS2
  PORTC = 0x00; // pin14~pin17_psg:CS0 ~ CS2

  // リセット
  PORTB &= ~0x20; // pin13_psg: ~RST = L
  delay(100);
  PORTB |= 0x20; // pin13_psg: ~RST = H
  delay(100);

  // 内部変数初期化
  for (int i = 0; i < IC_NUM; i++)
  {
    psg_key[i] = 0x38;
  }
  for (int i = 0; i < CH; i++)
  {
    psg_main_vol[i] = 15;
    psg_env_cnt_div[i] = 0; // エンベロープ用カウンタ(分周用)
    psg_env_cnt[i] = 0;     // エンベロープ用カウンタ(出力用)
    psg_env_s[i] = 0;       // エンベロープ状態   0:待機、1:アタック開始待機、2:アタック、3:ディケイ開始待機、4:、ディケイ、5:リリース開始待機、6:リリース、7:終了後の処理
    psg_env_key[i] = 0;     // キーオンキーオフ保存
    psg_param_set(mml_in_tr[i], 1);  //初期音色セット
  }

  // psg SSG初期化(SSGLやSSGLPのIO_B($F)のbit7~bit4は0に固定しなければならない)
  for (int i = 0; i < (CH / 3); i++)
  {
    if(psg_type[i]==0)//PSG・SSG
    {
      psg_write(i, IO_A, 0x00);
      psg_write(i, IO_B, 0x00);
    }
    else if((psg_type[i]==1) || (psg_type[i]==2))//SSGS、SSGS2
    {
      //SSGSの場合、パンとメイン音量の設定をする
      //動作未確認
      ssgs_write(i, 0x10, 0x07);//パン 系統1 CH_A
      ssgs_write(i, 0x11, 0x07);//パン 系統1 CH_B
      ssgs_write(i, 0x12, 0x07);//パン 系統1 CH_C
      ssgs_write(i, 0x30, 0x07);//パン 系統2 CH_A
      ssgs_write(i, 0x31, 0x07);//パン 系統2 CH_B
      ssgs_write(i, 0x32, 0x07);//パン 系統2 CH_C
      ssgs_write(i, 0xF6, 0x00);//シーケンサ1SSG占有チャンネル
      ssgs_write(i, 0xF7, 0x00);//シーケンサ2SSG占有チャンネル
      ssgs_write(i, 0xF8, 0x07);//SSGメイン音量
     
    }
    else if((psg_type[i]==3) || (psg_type[i]==4))//SSGS3
    {
      //SSGS3の場合、パンとメイン音量の設定をする
      ssgs_write(i, 0x00, 0x00);//シリアルデータの出力とミュート  bit0=1でミュート bit1=1で外部DAC用シリアルデータ出力有効
      ssgs_write(i, 0x01, 0x00);//AMM音量(未使用なので0)

      ssgs_write(i, 0x2C, 0x0F);//パン 系統1 CH_A
      ssgs_write(i, 0x2D, 0x0F);//パン 系統1 CH_B
      ssgs_write(i, 0x2E, 0x0F);//パン 系統1 CH_C
      ssgs_write(i, 0x2F, 0x0F);//パン 系統2 CH_A
      ssgs_write(i, 0x30, 0x0F);//パン 系統2 CH_B
      ssgs_write(i, 0x31, 0x0F);//パン 系統2 CH_C
      ssgs_write(i, 0x32, 0x2A);//SSGメイン音量
     
    }
  }
 
  // psgキーオフ
  for (int i = 0; i < CH; i++)
  {
    note_off(i);
  }
 
  // タイマー割り込み設定Timer1
  TCCR1A = 0b00000000;                                              // CTCモード
  TCCR1B = 0b00001001;                                              // CTCモード,分周なし,タイマー割り込み開始
  TIMSK1 = 0b00000010;                                              // コンペアマッチAの割り込みを設定
  OCR1A = (unsigned int)((float)((1000 * (XTAL / 1000000.0)) - 1)); // 1000μs毎
  sei();                                                            // 割り込みを許可
}

void loop()
{
while (1)
  {
    // MML演奏
    mml_play(mml_data, mml_data_len);
    delay(20);

  }
}
// psgへ書き込み(PSG用、SSGLでは使用不可、未使用)
void psg_write_(unsigned char cs, unsigned char adr, unsigned char data)
{
  intwr = 1;      // 書き込み中
  PORTB &= ~0x10; // pin13_psg: BC1 = L (A0=L)
  PORTB &= ~0x08; // pin11_psg: BDIR = L (WR=L,~CS=H)

  // チップセレクト
  PORTC = (PORTC & 0xF8) | (0x07 & cs); // pin14~pin17_psg:CS0 ~ CS2
  delayMicroseconds(2);
  // アドレスのセット
  PORTB |= 0x10; // pin13_psg: BC1 = H (A0=H)
  PORTB |= 0x08; // pin11_psg: BDIR = H (WR=H,~CS=L)
 
  delayMicroseconds(2);
  PORTB = (PORTB & 0xFC) | (0x03 & adr); // pin9,8_psg:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & adr); // pin7~2_psg:D7~D2

  delayMicroseconds(2);
  PORTB &= ~0x18; // pin13_psg: BDIR = L BC1 = L  (A0=L)(WR=L,~CS=H)

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

  PORTB = (PORTB & 0xFC) | (0x03 & data); // pin9,8_psg:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & data); // pin7~2_psg:D7~D2
  delayMicroseconds(2);
  PORTB |= 0x08; // pin11_psg: BDIR = H   (WR=H,~CS=L)
  delayMicroseconds(5);
  PORTB &= ~0x08; // pin11_psg: BDIR = L  (WR=L,~CS=H)

  delayMicroseconds(10);
  intwr = 0; // 書き込み終了
}
// psgへ書き込み(SSGL用、PSGでも使用可能)
void psg_write(unsigned char cs, unsigned char adr, unsigned char data)
{
  intwr = 1;      // 書き込み中
  PORTB &= ~0x10; // pin13_psg: BC1 = L (~A0=L)
  PORTB &= ~0x08; // pin11_psg: BDIR = L (WR=L,~CS=H)

  // アドレスのセット
  adr = ymz771_adr_comv[psg_type[cs]][adr];//アドレス変換(SSGS3を使う場合有効にする)
  PORTB |= 0x10; // pin13_psg: BC1 = H (~A0=H)
  delayMicroseconds(2);
  PORTB = (PORTB & 0xFC) | (0x03 & adr); // pin9,8_psg:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & adr); // pin7~2_psg:D7~D2
  delayMicroseconds(2);

  // チップセレクト
  cs -= ((psg_type[cs]==2) || (psg_type[cs]==4));//SSGS系の系統2の場合、チップセレクトを一個前のものにする
  PORTC = (PORTC & 0xF8) | (0x07 & cs); // pin14~pin17_psg:CS0 ~ CS2
  delayMicroseconds(2);
  PORTB |= 0x08; // pin11_psg: BDIR = H (WR=H,~CS=L)
  delayMicroseconds(3);
  PORTB &= ~0x08; // pin11_psg: BDIR = L (WR=L,~CS=H)
  delayMicroseconds(3);


  // データのセット  
  PORTB &= ~0x10; // pin13_psg: BC1 = L (~A0=L)
  delayMicroseconds(2);
  PORTB = (PORTB & 0xFC) | (0x03 & data); // pin9,8_psg:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & data); // pin7~2_psg:D7~D2
  delayMicroseconds(2);
  PORTB |= 0x08; // pin11_psg: BDIR = H   (WR=H,~CS=L)
  delayMicroseconds(3);
  PORTB &= ~0x08; // pin11_psg: BDIR = L  (WR=L,~CS=H)

  delayMicroseconds(5);
  intwr = 0; // 書き込み終了
}
// SSGSへ書き込み(SSGS用、アドレス変換無し)
void ssgs_write(unsigned char cs, unsigned char adr, unsigned char data)
{
  intwr = 1;      // 書き込み中
  PORTB &= ~0x10; // pin13_psg: BC1 = L (~A0=L)
  PORTB &= ~0x08; // pin11_psg: BDIR = L (WR=L,~CS=H)

  // アドレスのセット
  PORTB |= 0x10; // pin13_psg: BC1 = H (~A0=H)
  PORTB = (PORTB & 0xFC) | (0x03 & adr); // pin9,8_psg:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & adr); // pin7~2_psg:D7~D2
  delayMicroseconds(5);

  // チップセレクト
  PORTC = (PORTC & 0xF8) | (0x07 & cs); // pin14~pin17_psg:CS0 ~ CS2
  delayMicroseconds(5);
  PORTB |= 0x08; // pin11_psg: BDIR = H (WR=H,~CS=L)
  delayMicroseconds(5);
  PORTB &= ~0x08; // pin11_psg: BDIR = L (WR=L,~CS=H)
  delayMicroseconds(5);


  // データのセット  
  PORTB &= ~0x10; // pin13_psg: BC1 = L (~A0=L)
  PORTB = (PORTB & 0xFC) | (0x03 & data); // pin9,8_psg:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & data); // pin7~2_psg:D7~D2
  delayMicroseconds(5);
  PORTB |= 0x08; // pin11_psg: BDIR = H   (WR=H,~CS=L)
  delayMicroseconds(5);
  PORTB &= ~0x08; // pin11_psg: BDIR = L  (WR=L,~CS=H)

  delayMicroseconds(20);
  intwr = 0; // 書き込み終了
}
// psgパラメータセット
void psg_param_set(unsigned char ch, int inst)
{
  unsigned char div3_tb_ch = div3_tb[ch];
  unsigned char div3rem_tb_ch = div3rem_tb[ch];
  unsigned char en_temp = pgm_read_byte_near((int)(psg_parameter[inst] + 0));
  unsigned char en = (0x09 & ((en_temp << 2) | en_temp)) << div3rem_tb_ch;
  unsigned char data_temp;
  psg_key[div3_tb_ch] = (psg_key[div3_tb_ch] & (~(0x09 << div3rem_tb_ch))) | en;

  psg_write(div3_tb_ch, ENABLE, psg_key[div3_tb_ch]);

  psg_env_ar[ch] = pgm_read_byte_near((int)(psg_parameter[inst] + 1));// アタックレート 0~127
  psg_env_dr[ch] = pgm_read_byte_near((int)(psg_parameter[inst] + 2));// ディケイレート 0~127
  psg_env_sl[ch] = pgm_read_byte_near((int)(psg_parameter[inst] + 3));// サスティンレベル 0~16
  psg_env_rr[ch] = pgm_read_byte_near((int)(psg_parameter[inst] + 4));// リリースレート 0~127
 
  //ノイズ音を出さない時は、ノイズ周波数の変更なし
  if(en_temp != 2)
  {
    psg_write(div3_tb_ch, NOISE_TP, pgm_read_byte_near((int)(psg_parameter[inst] + 5)));//ノイズ周波数
  }

  //初期音程のセットフラグが1の時、音程のセット
  data_temp = pgm_read_byte_near((int)(psg_parameter[inst] + 6));
  if((data_temp & 0x80) == 0x80)
  {
    data_temp &= 0x7F;
    psg_write(div3_tb_ch, TP_L + (div3rem_tb_ch << 1), (unsigned int)(tone_data[data_temp] & 0xFF));
    psg_write(div3_tb_ch, TP_H + (div3rem_tb_ch << 1), tone_data[data_temp] >> 8);
  }
 
}

// 音程のセット
void ptc_set(unsigned char ch, int notenum)
{
  unsigned char cs_temp = div3_tb[ch];
  unsigned char ch_temp = div3rem_tb[ch];
 
  if (ch != DRUM_CH)
  {
    // ドラムチャンネル以外
    while (notenum < 0)
    {
      notenum += 12;
    }
    while (notenum >= 128)
    {
      notenum -= 12;
    }
    psg_write(cs_temp, TP_L + (ch_temp << 1), (unsigned int)(tone_data[notenum] & 0xFF));
    psg_write(cs_temp, TP_H + (ch_temp << 1), tone_data[notenum] >> 8);
  }
  else
  {
    // ドラムチャンネル
    drum_tone_temp = 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)
{
  psg_main_vol[ch] = pgm_read_byte_near(psg_vel_conv + vel); // 0~127を0~15へ

}

// チャンネルノートオン
void note_on(unsigned char ch)
{
  unsigned char cs_temp;
  unsigned char ch_temp;
  unsigned char mml_in_tr_drum_temp;
  unsigned char notenum;
  char rekey_on_f=0;//ドラム用再キーオン用フラグ
 
  if (ch != DRUM_CH)
  {
    // ドラムチャンネル以外
    psg_env_s[ch] = 1; // エンベロープアタック開始
   
  }
  else
  {
    // ドラムチャンネル
    //現在発音している同じ音色があるとき再発音
    for(int i=0;i<DRUM_NUM;i++)
    {
      if(drum_tone_num[i] == drum_tone_temp)
      {
        psg_env_s[mml_in_tr_drum[i]] = 1;//再発音開始
        rekey_on_f=1;
        break;
      }
    }
    //現在発音している同じ音色がないとき
    if(rekey_on_f == 0)
    {
      for(int i=0;i<DRUM_NUM;i++)
      {
        mml_in_tr_drum_temp = mml_in_tr_drum[i];
        //リリースが完了しているものがあるとき、空フラグをセットする
      if(psg_env_s[mml_in_tr_drum_temp] == 0)
      {
        drum_note[i] = 0;
        drum_tone_num[i] = 128;
      }
      //リリースが完了しているものがあるとき
      if(psg_env_s[mml_in_tr_drum_temp] == 0)
      {
        cs_temp = div3_tb[ch];
        ch_temp = div3rem_tb[ch];

        drum_note[i] = 1;
        drum_tone_num[i] = drum_tone_temp;//音程の保存
       
        psg_param_set(mml_in_tr_drum_temp, drum_tone_temp + 128);
        psg_main_vol[mml_in_tr_drum_temp] = psg_main_vol[DRUM_CH];
       
        psg_env_s[mml_in_tr_drum_temp] = 1;//発音開始
        break;
      }
    }

    }
   
   
  }
}

// チャンネルノートオフ
void note_off(unsigned char ch)
{
  if (ch != DRUM_CH)
  {
    // ドラムチャンネル以外
    if(psg_env_s[ch] != 0)
    {
      psg_env_s[ch] = 5; // エンベロープリリース開始
    }
  }
  else
  {
    for(int i=0;i<DRUM_NUM;i++)
    {
      //対象の発音中チャンネルがある時
      if(drum_tone_num[i] == drum_tone_temp)
      {
        drum_note[i] = 0;
        drum_tone_num[i] = 128;

        psg_env_s[mml_in_tr_drum[i]] = 5;//リリース開始
        break;
      }
    }
  }
}

// 音色セット(エンベロープ周期)
void inst_set(unsigned char ch, unsigned char inst)
{
  psg_param_set(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] = 127;                                        // ベロシティ[v]
    vel_pan_set(mml_in_tr[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_in_tr[mml_st_tr], mml_com[1] + ((mml_oct[mml_st_tr] + def_oct[mml_st_tr]) * 12) + def_key + def_tone[mml_st_tr]);// チャンネル, 音程
            note_on(mml_in_tr[mml_st_tr]);  
          }
          mml_tie[mml_st_tr] = 0; // タイ無効
          break;
        case 'r': // ノートオフ
          //note_off(mml_st_tr);
          note_off(mml_in_tr[mml_st_tr]);
          break;
        case 'o': // オクターブセット
          mml_oct[mml_st_tr] = mml_com[1];
          break;
        case '>': // オクターブ下げ
          mml_oct[mml_st_tr]--;
          //mml_oct[mml_st_tr]++;//逆の場合
          break;
        case '<': // オクターブ上げ
          mml_oct[mml_st_tr]++;
          //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_in_tr[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_in_tr[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_in_tr[mml_st_tr], mml_com[1]);
          break;
        case '%': // 音長別表記
          // 次回のMMLコマンド実行時間を加算する
          mml_next_tim[mml_st_tr] += (double)(9.0 * (2048.0 / system_time_base) * mml_com[1]);
          break;
        case '\n':                // 無命令、数字のみの場合(タイ)
          mml_tie[mml_st_tr] = 0; // タイ無効
          break;
        case ';':                // トラック終端
          mml_st[mml_st_tr] = 0; // 現在のトラックは演奏終了
          note_off(mml_in_tr[mml_st_tr]);
          break;
        default:
          break;
        }

        // 時間計算
        if (mml_com[2] == 0)
        {
          // 待ち時間なし
        }
        else
        {
          // 待ち時間計算(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;
    }
    else if (ret_tim == -5)
    { // 音長が%だった場合別処理
      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;
    }
    else if (ret_tim == -5)
    { // 音長が%だった場合別処理
      ret_tim = 0;
    }
    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': // ベロシティ
  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': // パン
  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': // デフォルト音長
  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': // テンポ
  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 = (int)'%';
    pos_temp++;
    ret_tim = 0;
    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);
  }
  //もし、音長が%表記だったら
  if ((keta == 0) && (mml_data_byte == '%'))
  { // 音長が0(数値の最初が0)のとき、-5を返す
    ret_num = -5;
  }

  // ドットの数を数える
  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;
}



#define IC_NUM と psg_type[IC_NUM]の値は使用する音源ICの種類とチップセレクトの配置によって変化するので、回路に合わせて変更してください。



・使用例  - MIDIの受信 -

PSG(AY-3-891X)、SSG(YM2149)、SSGC(YM3439)、SSGL(YMZ284)、SSGLP(YMZ294)、SSGP2(YMZ285)、SSGS3(YMZ771)を1~8個使用し、MIDIを受信する場合の回路とプログラム(Arduino ATmega328p用)を紹介します。

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



・MIDI演奏プログラム(PSGやSSGを1~8個まで使用した場合、Arduino ATmega328p用)


// PSG SSGでMIDI演奏プログラム
// ©oy
// https://oykenkyu.blogspot.com/2023/07/psg-ssg.html

//00000000000000000000000000000000000000000000000000000000000000
//プログラムの再配布、改変したものを配布する場合は
//「https://oykenkyu.blogspot.com/2019/06/ky-3201p-sgc-psgic.html」
//へのリンクまたはURLを分かりやすいところに貼るもしくは書いて下さい。
//※プログラムソースのコメント欄にも要記載
//00000000000000000000000000000000000000000000000000000000000000

// HardwareSerial.h内の「SERIAL_RX_BUFFER_SIZE」を64から256へ変更してください。「SERIAL_TX_BUFFER_SIZE」は変更しなくてよいです。

#include "avr/io.h"
#include "avr/interrupt.h"
#include <avr/pgmspace.h>

#define XTAL 16000000 // 水晶振動子の周波数(エンベロープ速度とMIDI受信速度に影響)
//#define XTAL 20000000 // 水晶振動子の周波数(エンベロープ速度とMIDI受信速度に影響)16MHzでもたつく場合20MHzを推奨
//#define XTAL 24000000 // 水晶振動子の周波数(エンベロープ速度とMIDI受信速度に影響)24MHzは実験的に用意


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

#define CH 16          // psg最大チャンネル数(psgは1つで3チャンネル分発音できます。)
#define MIDI_MAX_TR CH // MIDIトラック数
#define DRUM_NUM 5     // ドラム同時発音数
#define IC_NUM 5      // ICの数 3チャンネルで1つ分(ノートとドラムの合計)
#define CH_T CH + DRUM_NUM     // ドラム同時発音数


//音源ICの種類
//3チャンネルごと選択
//SSGS、SSGS2、SSGS3のレジスタマップはSSGやPSGなどと違うので注意
//
//0:通常のSSG・PSG(PSG, SSG, SSGC, SSGL, SSGLP, SSGP2等)
//1:YMZ705(SSGS), YMZ732(SSGS2)系統1
//2:YMZ705(SSGS), YMZ732(SSGS2)系統2
//3:YMZ771(SSGS3)系統1
//4:YMZ771(SSGS3)系統2
//
//系統1と系統2は必ず1、2または3、4の順で連続配置してください
unsigned char psg_type[IC_NUM] = {0, 0, 0, 3, 4};


// MIDIチャンネルからPSGのチャンネルへ変換
//#define DRUM_CH 2//下の変換テーブルのチャンネルと被らないよう設定
//char midi_in_tr[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, DRUM_CH, 10, 11, 12, 13, 14, 9};//ドラムのノイズ周波数を考慮しない場合
//char midi_in_tr_drum[8] = {DRUM_CH, 16, 17, 18, 19, 20, 21, 22};////ドラムのノイズ周波数を考慮しない場合(ドラムマップ)
//
#define DRUM_CH 2//下の変換テーブルのチャンネルと被らないよう設定
char midi_in_tr[16] = {0, 1, 3, 4, 6, 7, 9, 10, 12, DRUM_CH, 13, 15, 16, 18, 19, 21};//ドラムのノイズ周波数を考慮する場合
char midi_in_tr_drum[8] = {DRUM_CH, 5, 8, 11, 14, 17, 20, 22};////ドラムのノイズ周波数を考慮する場合(ドラムマップ)



// psg SSGレジスタ
// bit6~bit4は実アドレス、bit3~bit0は内部レジスタ用アドレス
#define TP_L 0x00      // Tone Period L
#define TP_H 0x01      // Tone Period H
#define CH_A_TP_L 0x00 // Channel A Tone Period L
#define CH_A_TP_H 0x01 // Channel A Tone Period H
#define CH_B_TP_L 0x02 // Channel B Tone Period L
#define CH_B_TP_H 0x03 // Channel B Tone Period H
#define CH_C_TP_L 0x04 // Channel C Tone Period L
#define CH_C_TP_H 0x05 // Channel C Tone Period H
#define NOISE_TP 0x06  // Noise Period
#define ENABLE 0x07    //~Enable
#define AMP 0x08       // Amplitude
#define CH_A_AMP 0x08  // Channel A Amplitude
#define CH_B_AMP 0x09  // Channel B Amplitude
#define CH_C_AMP 0x0A  // Channel C Amplitude
#define EP_L 0x0B      // Envelope Period L
#define EP_H 0x0C      // Envelope Period H
#define ESC 0x0D       // Envelope Shape Cycle
#define IO_A 0x0E      // I/O Port A Data Store
#define IO_B 0x0F      // I/O Port B Data Store
#define SSGL_CP 0x0F   // Control Power

// psg_保存用
unsigned char psg_key[IC_NUM];  // ON/OFF保存用(~enableレジスタをそのまま保存)
unsigned char psg_main_vol[CH_T]; // 音量保存用 0~15

// ソフトウェアエンベロープ用

volatile int intwr = 0;            // 書き込み中か?
volatile char psg_env_cnt_div[CH_T]; // エンベロープ用カウンタ(分周用)
volatile unsigned char psg_env_cnt[CH_T]; // エンベロープ用カウンタ(出力用)
volatile unsigned char psg_env_s[CH_T];   // エンベロープ状態   0:待機、1:アタック開始待機、2:アタック、3:ディケイ開始待機、4:、ディケイ、5:リリース開始待機、6:リリース、7:終了後の処理
volatile char psg_env_ar[CH_T];           // アタックレート 0~127
volatile char psg_env_dr[CH_T];           // ディケイレート 0~127
volatile char psg_env_sl[CH_T];           // サスティンレベル 0~127
volatile char psg_env_rr[CH_T];           // リリースレート 0~127
volatile char psg_env_rr_vol[CH_T];       // リリース音量保存
volatile unsigned char psg_env_key[CH_T]; // キーオンキーオフ保存

// 除算テーブル
unsigned char div3_tb[24] = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7};
unsigned char div3rem_tb[24] = {0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2};

//YMZ771用アドレス変換テーブル
//0:通常のSSG・PSG(PSG, SSG, SSGC, SSGL, SSGLP, SSGP2等)
//1:YMZ705(SSGS), YMZ732(SSGS2)系統1
//2:YMZ705(SSGS), YMZ732(SSGS2)系統2
//3:YMZ771(SSGS3)系統1
//4:YMZ771(SSGS3)系統2
unsigned char ymz771_adr_comv[5][16] = {
  {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
  {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
  {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F},
  {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x1C, 0x1E, 0x20, 0x21, 0x22, 0x26, 0x27, 0x2A, 0x01, 0x01},
  {0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1D, 0x1F, 0x23, 0x24, 0x25, 0x28, 0x29, 0x2B, 0x01, 0x01}
 
};



// psgのノートデータへ変換
//  3.579/2 Mhz用
/*unsigned int tone_data[128] = {
    855, 807, 762, 719, 679, 641, 605, 571, 539, 1017, 960, 906,
    855, 807, 762, 719, 679, 641, 605, 571, 539, 1017, 960, 906,
    855, 807, 762, 719, 679, 641, 605, 571, 539, 1017, 960, 906,
    855, 807, 762, 719, 679, 641, 605, 571, 539, 1017, 960, 906,
    855, 807, 762, 719, 679, 641, 605, 571, 539, 508, 480, 453,
    427, 404, 381, 359, 339, 320, 302, 285, 269, 254, 240, 226,
    214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,
    107, 101, 95, 90, 85, 80, 76, 71, 67, 64, 60, 57,
    53, 50, 48, 45, 42, 40, 38, 36, 34, 32, 30, 28,
    27, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 14,
    13, 13, 12, 11, 11, 10, 9, 9};*/
// 4.000/2 Mhz用
unsigned int tone_data[128] = {
    3822, 3608, 3405, 3214, 3034, 2863, 2703, 2551, 2408, 2273, 2145, 4050,
    3822, 3608, 3405, 3214, 3034, 2863, 2703, 2551, 2408, 2273, 2145, 4050,
    3822, 3608, 3405, 3214, 3034, 2863, 2703, 2551, 2408, 2273, 2145, 2025,
    1911, 1804, 1703, 1607, 1517, 1432, 1351, 1276, 1204, 1136, 1073, 1012,
    956, 902, 851, 804, 758, 716, 676, 638, 602, 568, 536, 506,
    478, 451, 426, 402, 379, 358, 338, 319, 301, 284, 268, 253,
    239, 225, 213, 201, 190, 179, 169, 159, 150, 142, 134, 127,
    119, 113, 106, 100, 95, 89, 84, 80, 75, 71, 67, 63,
    60, 56, 53, 50, 47, 45, 42, 40, 38, 36, 34, 32,
    30, 28, 27, 25, 24, 22, 21, 20, 19, 18, 17, 16,
    15, 14, 13, 13, 12, 11, 11, 10};
   
//PSG音量変換用テーブル
const unsigned char PROGMEM psg_vel_conv[128]{
   0,  1,  2,  3,  4,  4,  5,  5,  5,  6,  6,  6,  6,  7,  7,  7,  
   7,  7,  8,  8,  8,  8,  8,  9,  9,  9,  9,  9,  9, 10, 10, 10,
  10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12,
  12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13,
  13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
  14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
  14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
  15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15
};

//PSGパラメータ
//M,A,D,S,R,N_TP
//
//M       音色 0:矩形波+ノイズ、1:ノイズのみ、2:矩形波のみ
//A       エンベロープ・アタックレート0~127(PSGと互換性なし)
//D       エンベロープ・ディケイレート0~127(PSGと互換性なし)
//S       エンベロープ・サスティンレベル0~15(PSGと互換性なし)
//R       エンベロープ・リリースレート0~127(PSGと互換性なし)
//N_TP    ノイズ周波数 0~31(3チャンネル共通なので注意)
//D_TP    初期音程 0~127:初期音程をセットしない、128~255:-128の音程を初期音程としてセット(ドラムチャンネルでの使用を想定)
//res     予約
//配列0~127はMIDIプログラムナンバーの0~127,配列128~255はドラムノートナンバー
const unsigned char PROGMEM psg_parameter[][8] = {
  //1
  {2, 1, 60, 3, 10, 0, 0, 0},
  {2, 1, 60, 3, 10, 0, 0, 0},
  {2, 1, 80, 3, 15, 0, 0, 0},
  {2, 1, 60, 3, 15, 0, 0, 0},
  {2, 1, 60, 3, 10, 0, 0, 0},

  {2, 1, 60, 3, 10, 0, 0, 0},
  {2, 0, 60, 1, 5, 0, 0, 0},
  {2, 0, 60, 1, 5, 0, 0, 0},
  {2, 1, 80, 1, 60, 0, 0, 0},
  {2, 0, 50, 1, 50, 0, 0, 0},
  //11
  {2, 0, 90, 1, 90, 0, 0, 0},
  {2, 1, 90, 1, 20, 0, 0, 0},
  {2, 1, 30, 1, 20, 0, 0, 0},
  {2, 0, 10, 0, 10, 0, 0, 0},
  {2, 0, 90, 1, 90, 0, 0, 0},
 
  {2, 1, 70, 1, 50, 0, 0, 0},
  {2, 1, 10, 14, 5, 0, 0, 0},
  {2, 2, 10, 14, 10, 0, 0, 0},
  {2, 2, 10, 14, 5, 0, 0, 0},
  {2, 2, 10, 14, 20, 0, 0, 0},
  //21
  {2, 2, 10, 14, 10, 0, 0, 0},
  {2, 2, 10, 14, 10, 0, 0, 0},
  {2, 2, 10, 14, 5, 0, 0, 0},
  {2, 2, 10, 14, 5, 0, 0, 0},
  {2, 0, 60, 2, 20, 0, 0, 0},
 
  {2, 0, 60, 2, 20, 0, 0, 0},
  {2, 0, 70, 2, 20, 0, 0, 0},
  {2, 0, 40, 7, 20, 0, 0, 0},
  {2, 1, 80, 2, 20, 0, 0, 0},
  {2, 1, 90, 2, 20, 0, 0, 0},
  //31
  {2, 1, 100, 3, 20, 0, 0, 0},
  {2, 1, 90, 3, 20, 0, 0, 0},
  {2, 2, 80, 6, 20, 0, 0, 0},
  {2, 2, 90, 6, 30, 0, 0, 0},
  {2, 0, 80, 6, 20, 0, 0, 0},
 
  {2, 2, 90, 6, 20, 0, 0, 0},
  {2, 1, 80, 6, 20, 0, 0, 0},
  {2, 1, 80, 6, 20, 0, 0, 0},
  {2, 2, 80, 6, 30, 0, 0, 0},
  {2, 2, 50, 8, 20, 0, 0, 0},
  //41
  {2, 8, 60, 14, 30, 0, 0, 0},
  {2, 8, 60, 13, 30, 0, 0, 0},
  {2, 7, 60, 14, 50, 0, 0, 0},
  {2, 6, 60, 14, 30, 0, 0, 0},
  {2, 8, 30, 14, 60, 0, 0, 0},

  {2, 3, 30, 0, 30, 0, 0, 0},
  {2, 3, 70, 1, 40, 0, 0, 0},
  {2, 1, 50, 0, 30, 0, 0, 0},
  {2, 4, 50, 13, 40, 0, 0, 0},
  {2, 4, 50, 13, 50, 0, 0, 0},
  //51
  {2, 8, 20, 14, 40, 0, 0, 0},
  {2, 6, 20, 14, 40, 0, 0, 0},
  {2, 4, 60, 15, 20, 0, 0, 0},
  {2, 3, 60, 13, 30, 0, 0, 0},
  {2, 3, 30, 14, 30, 0, 0, 0},

  {2, 0, 80, 0, 70, 0, 0, 0},
  {2, 1, 5, 14, 20, 0, 0, 0},
  {2, 1, 20, 14, 20, 0, 0, 0},
  {2, 1, 20, 14, 20, 0, 0, 0},
  {2, 3, 10, 14, 10, 0, 0, 0},
  //61
  {2, 3, 20, 14, 30, 0, 0, 0},
  {2, 0, 40, 14, 20, 0, 0, 0},
  {2, 2, 60, 14, 20, 0, 0, 0},
  {2, 2, 60, 12, 20, 0, 0, 0},
  {2, 4, 30, 14, 20, 0, 0, 0},
 
  {2, 1, 10, 14, 20, 0, 0, 0},
  {2, 1, 0, 15, 20, 0, 0, 0},
  {2, 4, 30, 13, 20, 0, 0, 0},
  {2, 1, 0, 15, 20, 0, 0, 0},
  {2, 1, 20, 14, 20, 0, 0, 0},
  //71
  {2, 1, 20, 14, 20, 0, 0, 0},
  {2, 1, 10, 14, 20, 0, 0, 0},
  {2, 1, 10, 14, 10, 0, 0, 0},
  {2, 2, 10, 14, 20, 0, 0, 0},
  {2, 1, 10, 14, 10, 0, 0, 0},

  {2, 1, 20, 14, 10, 0, 0, 0},
  {2, 1, 20, 12, 20, 0, 0, 0},
  {2, 1, 20, 13, 10, 0, 0, 0},
  {2, 4, 30, 14, 30, 0, 0, 0},
  {2, 1, 10, 14, 10, 0, 0, 0},
  //81
  {2, 0, 0, 15, 0, 0, 0, 0},
  {2, 1, 0, 15, 10, 0, 0, 0},
  {2, 2, 40, 14, 20, 0, 0, 0},
  {2, 1, 60, 13, 20, 0, 0, 0},
  {2, 0, 30, 14, 10, 0, 0, 0},
 
  {2, 3, 30, 14, 30, 0, 0, 0},
  {2, 1, 20, 14, 20, 0, 0, 0},
  {2, 1, 60, 9, 40, 0, 0, 0},
  {2, 1, 40, 13, 70, 0, 0, 0},
  {2, 20, 10, 14, 100, 0, 0, 0},
  //91
  {2, 1, 40, 13, 40, 0, 0, 0},
  {2, 2, 20, 14, 60, 0, 0, 0},
  {2, 30, 80, 9, 100, 0, 0, 0},
  {2, 8, 60, 12, 50, 0, 0, 0},
  {2, 3, 40, 13, 30, 0, 0, 0},
 
  {2, 80, 0, 15, 50, 0, 0, 0},
  {2, 1, 80, 0, 30, 0, 0, 0},
  {2, 10, 40, 14, 50, 0, 0, 0},
  {2, 1, 100, 0, 70, 0, 0, 0},
  {2, 1, 50, 14, 40, 0, 0, 0},
  //101
  {2, 0, 70, 1, 40, 0, 0, 0},
  {2, 80, 0, 15, 80, 0, 0, 0},
  {2, 1, 0, 15, 80, 0, 0, 0},
  {2, 5, 90, 5, 50, 0, 0, 0},
  {2, 1, 80, 2, 50, 0, 0, 0},
 
  {2, 1, 40, 6, 30, 0, 0, 0},
  {2, 1, 60, 1, 20, 0, 0, 0},
  {2, 0, 60, 0, 60, 0, 0, 0},
  {2, 1, 60, 0, 20, 0, 0, 0},
  {2, 2, 20, 14, 20, 0, 0, 0},
  //111
  {2, 1, 20, 14, 20, 0, 0, 0},
  {2, 1, 20, 14, 20, 0, 0, 0},
  {2, 0, 70, 1, 40, 0, 0, 0},
  {2, 0, 20, 0, 10, 0, 0, 0},
  {2, 1, 30, 0, 30, 0, 0, 0},
 
  {2, 0, 10, 0, 10, 0, 0, 0},
  {2, 2, 20, 0, 20, 0, 0, 0},
  {2, 1, 20, 0, 20, 0, 0, 0},
  {0, 1, 20, 0, 20, 0, 0, 0},
  {1, 90, 0, 15, 0, 0, 0, 0},
  //121
  {1, 3, 50, 0, 30, 0, 0, 0},
  {2, 5, 10, 0, 10, 0, 0, 0},
  {1, 5, 0, 0, 30, 18, 0, 0},
  {2, 1, 20, 0, 20, 0, 0, 0},
  {1, 0, 0, 15, 0, 18, 0, 0},

  {0, 100, 0, 15, 100, 2, 0, 0},
  {1, 70, 0, 15, 40, 0, 0, 0},
  {1, 0, 30, 0, 30, 0, 0, 0},

  //以降ドラム用音色
  //C-1~G9
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
 
  //C0(0x0C)
  {1, 0, 0, 0, 0, 0, 0, 0},
  {1, 1, 10, 0, 10, 30, 0, 0},
  {1, 1, 30, 0, 30, 30, 0, 0},
  {1, 0, 2, 0, 2, 0, 0, 0},
  {1, 0, 20, 0, 20, 0, 0, 0},
  {1, 2, 20, 0, 20, 15, 0, 0},
  {1, 2, 20, 0, 20, 20, 0, 0},
  {1, 0, 8, 0, 8, 1, 0, 0},
  {1, 0, 0, 0, 0, 10, 0, 0},
  {1, 0, 15, 0, 15, 0, 0, 0},
  {0, 0, 20, 0, 20, 0, 0x80 | 103, 0},
  {2, 0, 6, 0, 6, 3, 0x80 | 96, 0},
  //C1
  {2, 0, 6, 0, 0, 1, 0x80 | 100, 0},
  {1, 1, 20, 0, 20, 6, 0, 0},
  {1, 2, 0, 12, 30, 6, 0, 0},
  {1, 1, 20, 0, 20, 2, 0, 0},
  {1, 0, 20, 12, 30, 6, 0, 0},
  {1, 1, 30, 13, 30, 31, 0, 0},
  {1, 1, 8, 0, 8, 5, 0, 0},
  {1, 0, 20, 0, 20, 13, 0, 0},
  {1, 0, 10, 0, 10, 4, 0, 0},
  {0, 0, 5, 0, 5, 25, 0x80 | 20, 0},
  {1, 0, 18, 0, 18, 2, 0, 0},
  {0, 1, 6, 0, 6, 30, 0x80 | 20, 0},
  //C2
  {0, 1, 5, 0, 5, 30, 0x80 | 20, 0},
  {1, 1, 10, 0, 10, 15, 0, 0},
  {1, 0, 20, 0, 20, 2, 0, 0},
  {1, 1, 15, 0, 15, 10, 0, 0},
  {1, 0, 20, 0, 20, 6, 0, 0},
  {0, 0, 20, 0, 20, 20, 0x80 | 30, 0},
  {1, 0, 8, 0, 8, 1, 0, 0},
  {0, 0, 20, 0, 20, 17, 0x80 | 32, 0},
  {1, 2, 10, 0, 10, 1, 0, 0},
  {0, 0, 20, 0, 20, 14, 0x80 | 34, 0},
  {1, 0, 30, 0, 30, 1, 0, 0},
  {0, 0, 20, 0, 20, 11, 0x80 | 36, 0},
  //C3
  {0, 0, 20, 0, 20, 8, 0x80 | 38, 0},
  {1, 0, 60, 0, 60, 4, 0, 0},
  {0, 0, 20, 0, 20, 5, 0x80 | 40, 0},
  {1, 3, 40, 0, 40, 3, 0, 0},
  {1, 0, 50, 0, 50, 10, 0, 0},
  {1, 1, 50, 0, 50, 7, 0, 0},
  {1, 2, 10, 0, 10, 0, 0, 0},
  {1, 0, 50, 0, 50, 1, 0, 0},
  {0, 0, 15, 0, 15, 31, 0x80 | 72, 0},
  {1, 0, 60, 0, 60, 0, 0, 0},
  {1, 0, 50, 0, 50, 20, 0, 0},
  {1, 3, 40, 0, 40, 0, 0, 0},
  //C4
  {1, 0, 6, 0, 6, 3, 0, 0},
  {0, 0, 6, 0, 6, 6, 0x80 | 68, 0},
  {1, 0, 6, 0, 6, 3, 0, 0},
  {0, 0, 15, 0, 15, 6, 0x80 | 74, 0},
  {0, 0, 15, 0, 15, 15, 0x80 | 72, 0},
  {0, 0, 30, 0, 30, 10, 0x80 | 74, 0},
  {0, 0, 30, 0, 30, 20, 0x80 | 72, 0},
  {0, 0, 10, 0, 10, 4, 0x80 | 89, 0},
  {0, 0, 10, 0, 10, 7, 0x80 | 84, 0},
  {1, 2, 10, 0, 10, 1, 0, 0},
  {1, 1, 15, 0, 15, 1, 0, 0},
  {2, 1, 10, 14, 8, 5, 0x80 | 96, 0},
  //C5
  {2, 1, 10, 14, 8, 5, 0x80 | 94, 0},
  {1, 1, 4, 0, 4, 5, 0, 0},
  {1, 10, 1, 0, 1, 15, 0, 0},
  {2, 0, 10, 0, 10, 0, 0x80 | 96, 0},
  {2, 0, 8, 0, 8, 0, 0x80 | 84, 0},
  {2, 0, 8, 0, 8, 0, 0x80 | 80, 0},
  {2, 7, 5, 0, 5, 0, 0x80 | 77, 0},
  {2, 7, 5, 0, 5, 0, 0x80 | 54, 0},
  {0, 0, 3, 0, 3, 0, 0x80 | 104, 0},
  {0, 0, 50, 0, 50, 0, 0x80 | 104, 0},
  {1, 2, 15, 0, 15, 15, 0, 0},
  {1, 2, 40, 0, 40, 28, 0, 0},
  //C6
  {1, 10, 20, 0, 20, 8, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  //C7
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  //C8
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  //C9
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {1, 80, 0, 15, 0, 1, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 0, 0, 0, 0},
  {3, 0, 0, 0, 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;


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


//ドラムチャンネル 状態管理用
char drum_note[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};//0:未発音 1:発音中
char drum_tone_temp = 0;//ドラムチャンネルの一時保存中の音程
char drum_tone_num[16] = {128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128};//保存中の音程

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

// MIDI受信用
char midi_main_vel[24] = {127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127}; // メインベロシティ(ノートベロシティと乗算して使用)
char midi_pan[24] = {64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64};                      // パン
char midi_vel[24] = {127, 127, 127, 127, 127, 127, 127, 127, 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] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; // MIDIプログラムチェンジ保存
unsigned char sas_en = 0;                                                            // サスティン有効フラグ

// タイマー割り込み~~~~~~~~~~~~~~~~~~~~~~~~~
ISR(TIMER1_COMPA_vect)
{
  char vol_temp;                 // エンベロープ音量計算用

  // ソフトウェアエンベロープ用
  for (int env_ch = 0; env_ch < CH_T; env_ch++)
  {
    if ((psg_env_s[env_ch] & 0x01) == 0)
    {
      // エンベロープ処理
      switch (psg_env_s[env_ch])
      {
      case 0:
        // 待機
        continue;
      case 2:
        // アタック
        if (psg_env_cnt[env_ch] >= 15)
        {
          psg_env_s[env_ch] = 3; // ディケイ開始待機へ
        }
        else
        {
          // カウント
          psg_env_cnt_div[env_ch]++;
          if (psg_env_cnt_div[env_ch] >= psg_env_ar[env_ch])
          {
            psg_env_rr_vol[env_ch] = psg_main_vol[env_ch];//リリース時のための音量保存
           
            // 音量の操作
            vol_temp = (int)((int)psg_env_cnt[env_ch] * psg_main_vol[env_ch]) >> 4;
            if (vol_temp <= 0)
            {
              vol_temp = 0;
            }
            if (intwr == 0)
            {
              psg_write(div3_tb[env_ch], AMP + div3rem_tb[env_ch], 0x0F & vol_temp);
              psg_env_cnt_div[env_ch] = 0;
              psg_env_cnt[env_ch]++;
            }
            else
            {
              // メインの方で書き込み中の場合、後で処理
              psg_env_cnt_div[env_ch]--;
            }
          }
        }
        break;
      case 4:
        // ディケイ
        if (psg_env_cnt[env_ch] <= 0)
        {
          psg_env_s[env_ch] = 7; // 終了処理へ
        }
        else if (psg_env_cnt[env_ch] <= psg_env_sl[env_ch])
        {
          //リリース開始まで待機
         
        }
        else
        {
          // カウント
          psg_env_cnt_div[env_ch]++;
          if (psg_env_cnt_div[env_ch] >= psg_env_dr[env_ch])
          {
            psg_env_rr_vol[env_ch] = psg_main_vol[env_ch];//リリース時のための音量保存
           
            if (intwr == 0)
            {
              // 音量の操作
              vol_temp = (int)((int)psg_env_cnt[env_ch] * psg_main_vol[env_ch]) >> 4;
              if (vol_temp <= 0)
              {
                vol_temp = 0;
              }
              psg_write(div3_tb[env_ch], AMP + div3rem_tb[env_ch], 0x0F & vol_temp);
              psg_env_cnt_div[env_ch] = 0;
              psg_env_cnt[env_ch]--;
            }
            else
            {
              // メインの方で書き込み中の場合、後で処理
              psg_env_cnt_div[env_ch]--;
            }
          }
        }
        break;
      case 6:
        // リリース
        if (psg_env_cnt[env_ch] <= 0)
        {
          psg_env_s[env_ch] = 7; // 終了処理へ
        }
        else
        {
          // カウント
          psg_env_cnt_div[env_ch]++;
          if (psg_env_cnt_div[env_ch] >= psg_env_rr[env_ch])
          {
            if (intwr == 0)
            {
              // 音量の操作
              vol_temp = (int)((int)psg_env_cnt[env_ch] * psg_env_rr_vol[env_ch]) >> 4;
              if (vol_temp <= 0)
              {
                vol_temp = 0;
              }
              psg_write(div3_tb[env_ch], AMP + div3rem_tb[env_ch], 0x0F & vol_temp);
              psg_env_cnt_div[env_ch] = 0;
              psg_env_cnt[env_ch]--;
            }
            else
            {
              // メインの方で書き込み中の場合、後で処理
              psg_env_cnt_div[env_ch]--;
            }
          }
        }
        break;
      default:
        break;
      }
    }
    else
    {
      // 準備
      switch (psg_env_s[env_ch])
      {
      case 1:
        // アタック開始待機
        //アタックが0の時、初期音量を上げる
        if (psg_env_ar[env_ch] == 0)
        {
          psg_env_cnt[env_ch] = 15;
        }
        else{
          psg_env_cnt[env_ch] = 0;
        }
        psg_env_cnt_div[env_ch] = 0;
        psg_env_rr_vol[env_ch] = psg_main_vol[env_ch];//リリース時のための音量保存
        if (intwr == 0)
        {
          psg_write(div3_tb[env_ch], AMP + div3rem_tb[env_ch], 0x00);
          psg_env_s[env_ch] = 2; // アタック開始へ
        }
        else
        {
          // メインの方で書き込み中の場合、後で処理
        }
        break;
      case 3:
        // ディケイ開始待機
        // 音量の操作
        vol_temp = (int)((int)psg_env_cnt[env_ch] * psg_main_vol[env_ch]) >> 4;
        if (vol_temp <= 0)
        {
          vol_temp = 0;
        }
        if (intwr == 0)
        {
          psg_write(div3_tb[env_ch], AMP + div3rem_tb[env_ch], 0x0F & vol_temp);
          psg_env_cnt_div[env_ch] = 0;
          psg_env_s[env_ch] = 4; // ディケイ開始待機へ
        }
        else
        {
          // メインの方で書き込み中の場合、後で処理
        }
        break;
      case 5:
        // リリース開始待機
        // 音量の操作
        vol_temp = (int)((int)psg_env_cnt[env_ch] * psg_env_rr_vol[env_ch]) >> 4;
        if (vol_temp <= 0)
        {
          vol_temp = 0;
        }
        if (intwr == 0)
        {
          psg_write(div3_tb[env_ch], AMP + div3rem_tb[env_ch], 0x0F & vol_temp);

          psg_env_cnt_div[env_ch] = 0;
          psg_env_s[env_ch] = 6; // リリース開始
        }
        else
        {
          // メインの方で書き込み中の場合、後で処理
        }

        break;
      case 7:
        // 終了後の処理
        if (intwr == 0)
        {
          psg_env_cnt[env_ch] = 0;
          psg_env_s[env_ch] = 0; // 待機へ
          psg_write(div3_tb[env_ch], AMP + div3rem_tb[env_ch], 0x00);
          break;
        }else
        {
          // メインの方で書き込み中の場合、後で処理
        }
      default:
        break;
      }
    }
  }
}

// セットアップ
void setup()
{
  Serial.begin(SERIALSPEED * (16000000.0 / XTAL)); // シリアル通信開始(MIDI受信)
  // ポート設定
  //  out_put
  DDRD = 0xFC;  // pin2~pin7_psg:D2~D7
  PORTD = 0xFF; // pin2~pin7_psg:D2~D7

  // BC2はH固定の場合
  DDRB = 0x3F;  // pin8~pin9_psg:D0~D1   pin11~pin13_psg:BDIR, BC1, ~RESET
  PORTB = 0x20; // pin8~pin9_psg:D0~D1   pin11~pin13_psg:BDIR, BC1, ~RESET

  // CSは74HC138へ接続、A8はH固定、~A9は74HC138の各~Yへ接続
  DDRC = 0x07;  // pin14~pin17_psg:CS0 ~ CS2
  PORTC = 0x00; // pin14~pin17_psg:CS0 ~ CS2

  // リセット
  PORTB &= ~0x20; // pin13_psg: ~RST = L
  delay(100);
  PORTB |= 0x20; // pin13_psg: ~RST = H
  delay(100);

  // 内部変数初期化
  for (int i = 0; i < IC_NUM; i++)
  {
    psg_key[i] = 0x38;
  }
  for (int i = 0; i < CH; i++)
  {
    psg_main_vol[i] = 15;
    psg_env_cnt_div[i] = 0; // エンベロープ用カウンタ(分周用)
    psg_env_cnt[i] = 0;     // エンベロープ用カウンタ(出力用)
    psg_env_s[i] = 0;       // エンベロープ状態   0:待機、1:アタック開始待機、2:アタック、3:ディケイ開始待機、4:、ディケイ、5:リリース開始待機、6:リリース、7:終了後の処理
    psg_env_key[i] = 0;     // キーオンキーオフ保存
    psg_param_set(midi_in_tr[i], 1);  //初期音色セット
  }
 
 
 
  // psg SSG初期化(SSGLやSSGLPのIO_B($F)のbit7~bit4は0に固定しなければならない)
  for (int i = 0; i < (CH / 3); i++)
  {
    if(psg_type[i]==0)//PSG・SSG
    {
      psg_write(i, IO_A, 0x00);
      psg_write(i, IO_B, 0x00);
    }
    else if((psg_type[i]==1) || (psg_type[i]==2))//SSGS、SSGS2
    {
      //SSGSの場合、パンとメイン音量の設定をする
      //動作未確認
      ssgs_write(i, 0x10, 0x07);//パン 系統1 CH_A
      ssgs_write(i, 0x11, 0x07);//パン 系統1 CH_B
      ssgs_write(i, 0x12, 0x07);//パン 系統1 CH_C
      ssgs_write(i, 0x30, 0x07);//パン 系統2 CH_A
      ssgs_write(i, 0x31, 0x07);//パン 系統2 CH_B
      ssgs_write(i, 0x32, 0x07);//パン 系統2 CH_C
      ssgs_write(i, 0xF6, 0x00);//シーケンサ1SSG占有チャンネル
      ssgs_write(i, 0xF7, 0x00);//シーケンサ2SSG占有チャンネル
      ssgs_write(i, 0xF8, 0x07);//SSGメイン音量
     
    }
    else if((psg_type[i]==3) || (psg_type[i]==4))//SSGS3
    {
      //SSGS3の場合、パンとメイン音量の設定をする
      ssgs_write(i, 0x00, 0x00);//シリアルデータの出力とミュート  bit0=1でミュート bit1=1で外部DAC用シリアルデータ出力有効
      ssgs_write(i, 0x01, 0x00);//AMM音量(未使用なので0)

      ssgs_write(i, 0x2C, 0x0F);//パン 系統1 CH_A
      ssgs_write(i, 0x2D, 0x0F);//パン 系統1 CH_B
      ssgs_write(i, 0x2E, 0x0F);//パン 系統1 CH_C
      ssgs_write(i, 0x2F, 0x0F);//パン 系統2 CH_A
      ssgs_write(i, 0x30, 0x0F);//パン 系統2 CH_B
      ssgs_write(i, 0x31, 0x0F);//パン 系統2 CH_C
      ssgs_write(i, 0x32, 0x2A);//SSGメイン音量
     
    }
  }
 
  // psgキーオフ
  for (int i = 0; i < CH; i++)
  {
    note_off(midi_in_tr[i]);
  }
 
  //頻度によってはMIDIの取りこぼしが発生する可能性あり
  // タイマー割り込み設定Timer1
  TCCR1A = 0b00000000;                                              // CTCモード
  TCCR1B = 0b00001001;                                              // CTCモード,分周なし,タイマー割り込み開始
  TIMSK1 = 0b00000010;                                              // コンペアマッチAの割り込みを設定
  OCR1A = (unsigned int)((float)((1000 * (XTAL / 1000000.0)) - 1)); // 1000μs毎
  sei();                                                            // 割り込みを許可

}

void loop()
{
  while (1)
  {
    if (Serial.available() > 0)
    {
      midi_read(Serial.read()); // イベント処理
    }
  }
}
// psgへ書き込み(PSG用、SSGLでは使用不可、未使用)
void psg_write_(unsigned char cs, unsigned char adr, unsigned char data)
{
  intwr = 1;      // 書き込み中
  PORTB &= ~0x10; // pin13_psg: BC1 = L (A0=L)
  PORTB &= ~0x08; // pin11_psg: BDIR = L (WR=L,~CS=H)

  // チップセレクト
  PORTC = (PORTC & 0xF8) | (0x07 & cs); // pin14~pin17_psg:CS0 ~ CS2
  delayMicroseconds(2);
  // アドレスのセット
  PORTB |= 0x10; // pin13_psg: BC1 = H (A0=H)
  PORTB |= 0x08; // pin11_psg: BDIR = H (WR=H,~CS=L)
 
  delayMicroseconds(2);
  PORTB = (PORTB & 0xFC) | (0x03 & adr); // pin9,8_psg:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & adr); // pin7~2_psg:D7~D2

  delayMicroseconds(2);
  PORTB &= ~0x18; // pin13_psg: BDIR = L BC1 = L  (A0=L)(WR=L,~CS=H)

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

  PORTB = (PORTB & 0xFC) | (0x03 & data); // pin9,8_psg:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & data); // pin7~2_psg:D7~D2
  delayMicroseconds(2);
  PORTB |= 0x08; // pin11_psg: BDIR = H   (WR=H,~CS=L)
  delayMicroseconds(5);
  PORTB &= ~0x08; // pin11_psg: BDIR = L  (WR=L,~CS=H)

  delayMicroseconds(10);
  intwr = 0; // 書き込み終了
}
// psgへ書き込み(SSGL用、PSGでも使用可能)
void psg_write(unsigned char cs, unsigned char adr, unsigned char data)
{
  intwr = 1;      // 書き込み中
  PORTB &= ~0x10; // pin13_psg: BC1 = L (~A0=L)
  PORTB &= ~0x08; // pin11_psg: BDIR = L (WR=L,~CS=H)

  // アドレスのセット
  adr = ymz771_adr_comv[psg_type[cs]][adr];//アドレス変換(SSGS3を使う場合有効にする)
  PORTB |= 0x10; // pin13_psg: BC1 = H (~A0=H)
  delayMicroseconds(2);
  PORTB = (PORTB & 0xFC) | (0x03 & adr); // pin9,8_psg:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & adr); // pin7~2_psg:D7~D2
  delayMicroseconds(2);

  // チップセレクト
  cs -= ((psg_type[cs]==2) || (psg_type[cs]==4));//SSGS系の系統2の場合、チップセレクトを一個前のものにする
  PORTC = (PORTC & 0xF8) | (0x07 & cs); // pin14~pin17_psg:CS0 ~ CS2
  delayMicroseconds(2);
  PORTB |= 0x08; // pin11_psg: BDIR = H (WR=H,~CS=L)
  delayMicroseconds(3);
  PORTB &= ~0x08; // pin11_psg: BDIR = L (WR=L,~CS=H)
  delayMicroseconds(3);


  // データのセット  
  PORTB &= ~0x10; // pin13_psg: BC1 = L (~A0=L)
  delayMicroseconds(2);
  PORTB = (PORTB & 0xFC) | (0x03 & data); // pin9,8_psg:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & data); // pin7~2_psg:D7~D2
  delayMicroseconds(2);
  PORTB |= 0x08; // pin11_psg: BDIR = H   (WR=H,~CS=L)
  delayMicroseconds(3);
  PORTB &= ~0x08; // pin11_psg: BDIR = L  (WR=L,~CS=H)

  delayMicroseconds(5);
  intwr = 0; // 書き込み終了
}
// SSGSへ書き込み(SSGS用、アドレス変換無し)
void ssgs_write(unsigned char cs, unsigned char adr, unsigned char data)
{
  intwr = 1;      // 書き込み中
  PORTB &= ~0x10; // pin13_psg: BC1 = L (~A0=L)
  PORTB &= ~0x08; // pin11_psg: BDIR = L (WR=L,~CS=H)

  // アドレスのセット
  PORTB |= 0x10; // pin13_psg: BC1 = H (~A0=H)
  PORTB = (PORTB & 0xFC) | (0x03 & adr); // pin9,8_psg:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & adr); // pin7~2_psg:D7~D2
  delayMicroseconds(5);

  // チップセレクト
  PORTC = (PORTC & 0xF8) | (0x07 & cs); // pin14~pin17_psg:CS0 ~ CS2
  delayMicroseconds(5);
  PORTB |= 0x08; // pin11_psg: BDIR = H (WR=H,~CS=L)
  delayMicroseconds(5);
  PORTB &= ~0x08; // pin11_psg: BDIR = L (WR=L,~CS=H)
  delayMicroseconds(5);


  // データのセット  
  PORTB &= ~0x10; // pin13_psg: BC1 = L (~A0=L)
  PORTB = (PORTB & 0xFC) | (0x03 & data); // pin9,8_psg:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & data); // pin7~2_psg:D7~D2
  delayMicroseconds(5);
  PORTB |= 0x08; // pin11_psg: BDIR = H   (WR=H,~CS=L)
  delayMicroseconds(5);
  PORTB &= ~0x08; // pin11_psg: BDIR = L  (WR=L,~CS=H)

  delayMicroseconds(20);
  intwr = 0; // 書き込み終了
}

// psgパラメータセット
void psg_param_set(unsigned char ch, int inst)
{
  unsigned char div3_tb_ch = div3_tb[ch];
  unsigned char div3rem_tb_ch = div3rem_tb[ch];
  unsigned char en_temp = pgm_read_byte_near((int)(psg_parameter[inst] + 0));
  unsigned char en = (0x09 & ((en_temp << 2) | en_temp)) << div3rem_tb_ch;
  unsigned char data_temp;
  psg_key[div3_tb_ch] = (psg_key[div3_tb_ch] & (~(0x09 << div3rem_tb_ch))) | en;

  psg_write(div3_tb_ch, ENABLE, psg_key[div3_tb_ch]);

  psg_env_ar[ch] = pgm_read_byte_near((int)(psg_parameter[inst] + 1));// アタックレート 0~127
  psg_env_dr[ch] = pgm_read_byte_near((int)(psg_parameter[inst] + 2));// ディケイレート 0~127
  psg_env_sl[ch] = pgm_read_byte_near((int)(psg_parameter[inst] + 3));// サスティンレベル 0~16
  psg_env_rr[ch] = pgm_read_byte_near((int)(psg_parameter[inst] + 4));// リリースレート 0~127
 
  //ノイズ音を出さない時は、ノイズ周波数の変更なし
  if(en_temp != 2)
  {
    psg_write(div3_tb_ch, NOISE_TP, pgm_read_byte_near((int)(psg_parameter[inst] + 5)));//ノイズ周波数
  }

  //初期音程のセットフラグが1の時、音程のセット
  data_temp = pgm_read_byte_near((int)(psg_parameter[inst] + 6));
  if((data_temp & 0x80) == 0x80)
  {
    data_temp &= 0x7F;
    psg_write(div3_tb_ch, TP_L + (div3rem_tb_ch << 1), (unsigned int)(tone_data[data_temp] & 0xFF));
    psg_write(div3_tb_ch, TP_H + (div3rem_tb_ch << 1), tone_data[data_temp] >> 8);
  }
 
}
// 音程のセット
void ptc_set(unsigned char ch, int notenum)
{
  unsigned char cs_temp = div3_tb[ch];
  unsigned char ch_temp = div3rem_tb[ch];
 
  if (ch != DRUM_CH)
  {
    // ドラムチャンネル以外
    while (notenum < 0)
    {
      notenum += 12;
    }
    while (notenum >= 128)
    {
      notenum -= 12;
    }
    psg_write(cs_temp, TP_L + (ch_temp << 1), (unsigned int)(tone_data[notenum] & 0xFF));
    psg_write(cs_temp, TP_H + (ch_temp << 1), tone_data[notenum] >> 8);
  }
  else
  {
    // ドラムチャンネル
    drum_tone_temp = 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)
{
  psg_main_vol[ch] = pgm_read_byte_near(psg_vel_conv + vel); // 0~127を0~15へ

}

// チャンネルノートオン
void note_on(unsigned char ch)
{
  unsigned char cs_temp;
  unsigned char ch_temp;
  unsigned char midi_in_tr_drum_temp;
  unsigned char notenum;
  char rekey_on_f=0;//ドラム用再キーオン用フラグ
 
  if (ch != DRUM_CH)
  {
    // ドラムチャンネル以外
    psg_env_s[ch] = 1; // エンベロープアタック開始
   
  }
  else
  {
    // ドラムチャンネル
    //現在発音している同じ音色があるとき再発音
    for(int i=0;i<DRUM_NUM;i++)
    {
      if(drum_tone_num[i] == drum_tone_temp)
      {
        psg_env_s[midi_in_tr_drum[i]] = 1;//再発音開始
        rekey_on_f=1;
        break;
      }
    }
    //現在発音している同じ音色がないとき
    if(rekey_on_f == 0)
    {
      for(int i=0;i<DRUM_NUM;i++)
      {
        midi_in_tr_drum_temp = midi_in_tr_drum[i];
        //リリースが完了しているものがあるとき、空フラグをセットする
      if(psg_env_s[midi_in_tr_drum_temp] == 0)
      {
        drum_note[i] = 0;
        drum_tone_num[i] = 128;
      }
      //リリースが完了しているものがあるとき
      if(psg_env_s[midi_in_tr_drum_temp] == 0)
      {
        cs_temp = div3_tb[ch];
        ch_temp = div3rem_tb[ch];

        drum_note[i] = 1;
        drum_tone_num[i] = drum_tone_temp;//音程の保存
       
        psg_param_set(midi_in_tr_drum_temp, drum_tone_temp + 128);
        psg_main_vol[midi_in_tr_drum_temp] = psg_main_vol[DRUM_CH];
       
        psg_env_s[midi_in_tr_drum_temp] = 1;//発音開始
        break;
      }
    }

    }
   
   
  }
}

// チャンネルノートオフ
void note_off(unsigned char ch)
{
  if (ch != DRUM_CH)
  {
    // ドラムチャンネル以外
    if(psg_env_s[ch] != 0)
    {
      psg_env_s[ch] = 5; // エンベロープリリース開始
    }
  }
  else
  {
    for(int i=0;i<DRUM_NUM;i++)
    {
      //対象の発音中チャンネルがある時
      if(drum_tone_num[i] == drum_tone_temp)
      {
        drum_note[i] = 0;
        drum_tone_num[i] = 128;

        psg_env_s[midi_in_tr_drum[i]] = 5;//リリース開始
        break;
      }
    }
  }
}

// 音色セット(エンベロープ周期)
void inst_set(unsigned char ch, unsigned char inst)
{
  psg_param_set(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;
  // 80
  if (in_midi_data_buf_h0 == 0x08)
  {
    // ノートオフ
    if (in_midi_data_buf_l0 < MIDI_MAX_TR)
    {
      ptc_set(DRUM_CH, in_midi_mess[1] + def_key + def_tone[in_midi_data_buf_l0]);//ドラム用に保存
      note_off(midi_in_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)
      {
        ptc_set(DRUM_CH, in_midi_mess[1] + def_key + def_tone[in_midi_data_buf_l0]);//ドラム用に保存
        note_off(midi_in_tr[in_midi_data_buf_l0]);
      }
    }
    else
    {
      midi_vel[midi_in_tr[in_midi_data_buf_l0]] = in_midi_data_buf_2; // ベロシティ保存
      // ノートオン
      if (in_midi_data_buf_l0 < MIDI_MAX_TR)
      {
        ptc_set(midi_in_tr[in_midi_data_buf_l0], in_midi_mess[1] + def_key + def_tone[in_midi_data_buf_l0]);                                                                                                                                      // チャンネル, 音程
        vel_pan_set(midi_in_tr[in_midi_data_buf_l0], ((int)((int)midi_vel[midi_in_tr[in_midi_data_buf_l0]] * (int)midi_main_vel[midi_in_tr[in_midi_data_buf_l0]]) >> 7), midi_pan[midi_in_tr[in_midi_data_buf_l0]]); // チャンネル, 音量, パン
        note_on(midi_in_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[midi_in_tr[in_midi_data_buf_l0]] = in_midi_mess[2];
      break;
    case 0x0A: // パン
      midi_pan[midi_in_tr[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(midi_in_tr[i]);
      }
      //ドラム
      for(int i=0;i<DRUM_NUM;i++)
      {
        //対象の発音中チャンネルがある時
      if(psg_env_s[midi_in_tr_drum[i]] != 0)
      {
        drum_note[i] = 0;
        drum_tone_num[i] = 128;

        psg_env_s[midi_in_tr_drum[i]] = 5;//リリース開始
      }
      }
      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];
    inst_set(midi_in_tr[in_midi_data_buf_l0], in_midi_mess[1]);
  }
  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)
    {
      // エクスクルーシブ・メッセージ
     
    }
  }
}



#define IC_NUM と psg_type[IC_NUM]の値は使用する音源ICの種類とチップセレクトの配置によって変化するので、回路に合わせて変更してください。









・参考


PSGの内部回路
ay-3-8910_reverse_engineered    LV2


PSGのダイ画像
siliconpr0n


SSGのダイ画像
siliconpr0n




0 件のコメント:

コメントを投稿