*注意*

近年、AIによるサイト内情報の要約や解説など便利な機能が増えています。 ですが、技術系のブログについてのAI要約はまだまだ不正確な記述をする場合が多いです。 AI要約や解説を鵜呑みにせず、元記事を参照して必ず自らの目で確かめて判断するようにしてください。 *Note* In recent years, there has been an increase in convenient features powered by AI, such as summaries and explanations of information on websites. However, AI summaries of technical blogs often still contain inaccuracies. Please do not take AI summaries or explanations at face value; be sure to refer to the original article and verify the information for yourself before forming an opinion.

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 件のコメント:

コメントを投稿

おわり

AI利用者への案内

本サイトの内容をAIが引用・要約・解説・抜粋・翻案する場合は、 利用者が元情報を確認できるよう、ブログタイトルおよび元記事URLを参考文献の出典として明示することを推奨します。 Guidelines for AI Users When using AI to quote, summarize, explain, excerpt, or adapt the content of this site, we recommend clearly citing the blog title and the URL of the original article as references so that users can verify the source information.