・使用例 - 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の種類とチップセレクトの配置によって変化するので、回路に合わせて変更してください。
PSG・SSG演奏テスト#動く動く#AY_3_8910#YMZ285#YMZ294#YMZ771 pic.twitter.com/Fxk2IcECqN
— oy (@0x6f_0x79) November 21, 2023
・使用例 - 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 件のコメント:
コメントを投稿