PSG Func check program. (ソースコード)


RGKT-NDS-STDY-018(008)


よくよく考えてみたら300行超えてるようなソースを
誰がサンプルコードとしてみてくれるだろうか。
少なくとも俺は、他に最適でシンプルなコードがあったらそちらを参考にすると思う。


最低限の動作確認に必要な箇所と最低限のUIの操作感を考慮すると
このぐらいのコード量になってしまったっというか。言い訳です。ええ。(−−;


なんてこと思ってたら
とりあえず公開しちゃって良いかって気分になってきたんで公開しときます。


ぶっちゃけサンプルとしてじゃなく俺が楽しんで且つ理解するために
勢い任せで書いちゃったのがコード量肥大化の原因な気がします。
もっともっとハードコーディングで利便性よりも関数動作の分かり易さとかを念頭に書くべきだったと
今更ながら後悔してたり。


もうちょっとシンプルなコードを
改めて書くかも知れないです。(´・ω・`)


PSG Func check program. (ソースコード

/*---------------------------------------------------------------------------------
	
	PSG Func Check Program.
	
	Feb 18, 2009 - Feb 23, 2009  1st Release(source code only).
	CODING BY. AYUMI.KAWAI @REGEKATSU
	ADVISER BY. YUKI.INOUE @REGEKATSU
	This program was made from devkitPro Version 1-4-9.
	
	REGEKATSU
	-Console/Homebrew Programming-
	
	this web site.
	http://akiba.geocities.jp/consolehomebrewprogramming/

---------------------------------------------------------------------------------*/
#include <nds.h>
#include <stdio.h>

//def DutyCycle
#define DutyCycle_BaseNum 12.5f
#define DutyCycle_MinNum   0
#define DutyCycle_MaxNum   7

//def VOLUME
typedef enum{
	VOLUME_MIN_NUM =   0,
	VOLUME_MAX_NUM = 127,
	VOLUME_DEF_NUM =  63
}VOLUME_NUM;

//def PAN
typedef enum{
	PAN_MIN_NUM =   0,
	PAN_MAX_NUM = 127,
	PAN_DEF_NUM =  63
}PAN_NUM;

//def OCTAVE
typedef enum{
	OCTAVE_MIN_NUM  =  0,
	OCTAVE_MAX_NUM  =  5,
	OCTAVE_DEF_NUM  =  2,
	OCTAVE_BASE_NUM = 12
}OCTAVE_NUM;
	
typedef enum{
	OCTAVE_UP      =  1,
	OCTAVE_DOWN    = -1,
	OCTAVE_NEUTRAL =  0
}OCTAVE_MODE;

//def NOTE
#define NOTE_MIN_NUM  0
#define NOTE_MAX_NUM 71

typedef enum{
	NOTE_NULL        = -1,
	NOTE_C           =  0,
	NOTE_C_SHARP     =  1,
	NOTE_D           =  2,
	NOTE_D_SHARP     =  3,
	NOTE_E           =  4,
	NOTE_F           =  5,
	NOTE_F_SHARP     =  6,
	NOTE_G           =  7,
	NOTE_G_SHARP     =  8,
	NOTE_A           =  9,
	NOTE_A_SHARP     = 10,
	NOTE_B           = 11,
	NOTE_SHARP_TRUE  =  1,
	NOTE_SHARP_FALSE =  0
}NOTE_DATA;

//def KEY_SELECT INPUT MODE
typedef enum{
	OCTAVE_CHANGE_DISABLE,
	OCTAVE_CHANGE_ENABLE
}OCTAVE_CHANGE_MODE;

//PSG FreqTable. 6 octaves, One dimension 72 arrays.
//平均律を10倍して小数点以下を四捨五入で切り捨てた値をテーブルとして持ち、
//テーブルにあるベース周波数を×8倍し、÷10でNDS用に最適化して、
//その値を周波数値として渡し、soundPlayPSG() 関数にて再生している。

uint16 freq_table[] = {
	
//                  +0,    +1,    +2,    +3,    +4,    +5,    +6,    +7,    +8,    +9,   +10,   +11,
//                   C,    C+,     D,    D+,     E,     F,    F+,     G,    G+,     A,    A+,     B,
/* octave 0 */     654,   693,   734,   778,   824,   873,   925,   980,  1038,  1100,  1165,  1235,
/* octave 1 */    1308,  1386,  1468,  1556,  1648,  1746,  1850,  1960,  2077,  2200,  2331,  2469,
/* octave 2 */    2616,  2772,  2937,  3111,  3296,  3492,  3700,  3920,  4153,  4400,  4662,  4939,
/* octave 3 */    5233,  5444,  5873,  6223,  6593,  6985,  7400,  7840,  8306,  8800,  9323,  9878,
/* octave 4 */   10465, 11087, 11747, 12445, 13185, 13969, 14800, 15680, 16612, 17600, 18647, 19755,
/* octave 5 */   20930, 22175, 23493, 24890, 26370, 27938, 29600, 31360, 33224, 35200, 37293, 39511
	
};

//main routine
int main(void)
{
	
	DutyCycle set_duty_cycle =      DutyCycle_12;                    //set  DutyCycle
	float draw_duty_cycle    = DutyCycle_BaseNum;                    //draw DutyCycle
	
	int cur_octave          = OCTAVE_DEF_NUM;                        //current octave              status
	int now_octave_mode     = OCTAVE_NEUTRAL;                        //now     octave up/down mode status
	OCTAVE_MODE octave_mode =      OCTAVE_UP;                        //KEY_R   octave up/down mode status
	
	int    set_note   =      NOTE_NULL;                              //set note(C-HC)           data
	uint16 set_freq   =              0;                              //set freq(nds optimized)  data
	u8     set_volume = VOLUME_DEF_NUM;                              //set volume(0-127)        data
	u8     set_pan    =    PAN_DEF_NUM;                              //set pan(0-127)           data
	
	OCTAVE_CHANGE_MODE octave_change_mode = OCTAVE_CHANGE_ENABLE;    //KEY_SELECT change octave mode
	
	uint32 kdr_status = 0;                                           //keyDownRepeat status
	int sound_id = 0;                                                //sound channel id(number)
	
	//initialize console
	consoleDemoInit();
	
	//console font color change
	BG_PALETTE_SUB[255] = RGB15(0, 31, 0);
	
	//draw lcd on top
	lcdSwap();
	
	//draw copyright
	iprintf("\x1b[0;0HPSG Func check program.");
	iprintf("\x1b[1;0HFeb 22, 2009");
	iprintf("\x1b[2;0HCODING BY. AYUMI.K @REGEKATSU");
	
	//draw layout
	iprintf("\x1b[5;2HK_L\x1b[6;2H+");             //KEY_L     note sharp
	iprintf("\x1b[5;26HK_R\x1b[6;26HUP  ");        //KEY_R     octave up/down
	iprintf("\x1b[7;6HK_UP\x1b[8;6HE");            //KEY_UP    E
	iprintf("\x1b[9;2HK_LF\x1b[10;2HD");           //KEY_LEFT  D
	iprintf("\x1b[9;10HK_RI\x1b[10;10HF");         //KEY_RIGHT F
	iprintf("\x1b[11;6HK_DN\x1b[12;6HC");          //KEY_DOWN  C
	iprintf("\x1b[7;22HK_X\x1b[8;22HHC");          //KEY_X     HC
	iprintf("\x1b[9;18HK_Y\x1b[10;18HG");          //KEY_Y     G
	iprintf("\x1b[9;26HK_A\x1b[10;26HB");          //KEY_A     B
	iprintf("\x1b[11;22HK_B\x1b[12;22HA");         //KEY_B     A
	printf("\x1b[15;2HK_ST  Duty Cycle  %04.1f%% (%d)", draw_duty_cycle, set_duty_cycle);
	iprintf("\x1b[16;2HK_SL  Change Octave UP/DN");
	iprintf("\x1b[17;3H+K_R      Inc/Dec Oct UP/DN");
	iprintf("\x1b[18;3H+K_UP/DN  Inc/Dec Vol  %03d", set_volume);
	iprintf("\x1b[19;3H+K_LF/RI  L/R Pan  %03d", set_pan);
	iprintf("\x1b[20;2HCurrent Octave Num  %d", cur_octave);
	iprintf("\x1b[21;2HInput Keys State  ");
	iprintf("\x1b[22;2Hfreq_table[  ] == 0000Hz"); //doraw 12 equal temperament)
	
	//set keysSetRepeat
	keysSetRepeat(30, 6);
	
	//initialize sound play
	soundEnable();
	
	//main loop
		while(1) {
			
			//get keys status
			scanKeys();
			kdr_status = keysDownRepeat();
			
			//set base note
			if(keysHeld() & KEY_DOWN){          //note DO
				set_note = NOTE_C;
				printf("\x1b[21;20HC ");
			}else if(keysHeld() & KEY_LEFT){    //note RE
				set_note = NOTE_D;
				printf("\x1b[21;20HD ");
			}else if(keysHeld() & KEY_UP){      //note MI
				set_note = NOTE_E;
				printf("\x1b[21;20HE ");
			}else if(keysHeld() & KEY_RIGHT){   //note FA
				set_note = NOTE_F;
				printf("\x1b[21;20HF ");
			}else if(keysHeld() & KEY_Y){       //note SO
				set_note = NOTE_G;
				printf("\x1b[21;20HG ");
			}else if(keysHeld() & KEY_B){       //note RA
				set_note = NOTE_A;
				printf("\x1b[21;20HA ");
			}else if(keysHeld() & KEY_A){       //note SHI
				set_note = NOTE_B;
				printf("\x1b[21;20HB ");
			}else if(keysHeld() & KEY_X){       //note HighDO
				set_note = NOTE_C + (OCTAVE_BASE_NUM * OCTAVE_UP);
				printf("\x1b[21;20HHC");
			}else{                              //note free
				set_note = NOTE_NULL;
				printf("\x1b[21;20H  ");
			}
			
			//set note sharp
			if(keysHeld() & KEY_L){
				if((set_note == NOTE_E) || (set_note == NOTE_B) || (set_note == NOTE_NULL))
					set_note += NOTE_SHARP_FALSE;
				else
					set_note += NOTE_SHARP_TRUE;
				printf("\x1b[21;22H+L");
			}else{
				printf("\x1b[21;22H  ");
			}
			
			//select button enhancing operation
			if(keysHeld() & KEY_SELECT){
				
				//PSG sound play off
				set_note = NOTE_NULL;
				
				//increment current octave
				if(kdr_status & KEY_R){
					if(cur_octave < OCTAVE_MAX_NUM)
						cur_octave++;
					else if(cur_octave >= OCTAVE_MAX_NUM)
						cur_octave = OCTAVE_MIN_NUM;
					printf("\x1b[20;22H%d", cur_octave);
				}
				
				//increment volume
				else if(kdr_status & KEY_UP){
					if(set_volume < VOLUME_MAX_NUM)
						set_volume++;
					else if(set_volume >= VOLUME_MAX_NUM)
						set_volume = VOLUME_MIN_NUM;
					printf("\x1b[18;26H%03d", set_volume);
				}
				//decrement volume
				else if(kdr_status & KEY_DOWN){
					if(set_volume > VOLUME_MIN_NUM)
						set_volume--;
					else if(set_volume <= VOLUME_MIN_NUM)
						set_volume = VOLUME_MAX_NUM;
					printf("\x1b[18;26H%03d", set_volume);
				}
				
				//increment pan
				else if(kdr_status & KEY_RIGHT){
					if(set_pan < PAN_MAX_NUM)
						set_pan++;
					else if(set_pan >= PAN_MAX_NUM)
						set_pan = PAN_MIN_NUM;
					printf("\x1b[19;22H%03d", set_pan);
				}
				//decrement pan
				else if(kdr_status & KEY_LEFT){
					if(set_pan > PAN_MIN_NUM)
						set_pan--;
					else if(set_pan <= PAN_MIN_NUM)
						set_pan = PAN_MAX_NUM;
					printf("\x1b[19;22H%03d", set_pan);
				}
				
				//セレクトボタン+他キーの組合わせ入力(拡張操作)があった場合、
				//次回セレクト押下までの間、オクターブチェンジを無効にする。
				if(keysDown() & (KEY_R | KEY_UP | KEY_DOWN | KEY_LEFT | KEY_RIGHT))
					octave_change_mode = OCTAVE_CHANGE_DISABLE;
			}
			
			//change octave up down
			else if(keysUp() & KEY_SELECT){
				if(octave_change_mode){     //change octave mode Enable
					if(octave_mode == OCTAVE_UP){        //change octave down
						octave_mode = OCTAVE_DOWN;
						printf("\x1b[6;26HDOWN");
					}else{                               //change octave up
						octave_mode = OCTAVE_UP;
						printf("\x1b[6;26HUP  ");
					}
				}else{                      //change octave mode Disable
					octave_change_mode = OCTAVE_CHANGE_ENABLE;
				}
			}
			
			//set octave up down
			if(keysHeld() & KEY_R){           //set on octave up down
				now_octave_mode = octave_mode;
				printf("\x1b[21;24H+R");
			}else{                            //set off octave up down
				now_octave_mode = OCTAVE_NEUTRAL;
				printf("\x1b[21;24H  ");
			}
			
			//set duty cycle
			if(kdr_status & KEY_START){
				if(set_duty_cycle < 7)
					set_duty_cycle++;
				else if(set_duty_cycle >= 7)
					set_duty_cycle = DutyCycle_12;
			//draw duty cycle
				draw_duty_cycle = (set_duty_cycle == 7)?0.0f:DutyCycle_BaseNum * (set_duty_cycle + 1);
				printf("\x1b[15;20H%04.1f%% (%d)", draw_duty_cycle, set_duty_cycle);
			}
			
			//set note data
			if(set_note != NOTE_NULL){ //is note data
				//1st overflow check
				if((set_note + cur_octave * OCTAVE_BASE_NUM) < NOTE_MIN_NUM)        //note lower overflow
					set_note = set_note + ((cur_octave + 1) * OCTAVE_BASE_NUM);
				else if((set_note + cur_octave * OCTAVE_BASE_NUM) > NOTE_MAX_NUM)   //note upper overflow
					set_note = set_note + ((cur_octave - 1) * OCTAVE_BASE_NUM);
				//2nd overflow check
				else if((set_note + ((cur_octave + now_octave_mode) * OCTAVE_BASE_NUM)) < NOTE_MIN_NUM) //note lower overflow
					set_note = set_note + cur_octave * OCTAVE_BASE_NUM;
				else if((set_note + ((cur_octave + now_octave_mode) * OCTAVE_BASE_NUM)) > NOTE_MAX_NUM) //note upper overflow
					set_note = set_note + cur_octave * OCTAVE_BASE_NUM;
				//note no overflow
				else
					set_note = set_note + ((cur_octave + now_octave_mode) * OCTAVE_BASE_NUM);
			}
			
			//play and draw note data
			if(set_note != NOTE_NULL){ //is note data
				if(keysDown() & (KEY_UP | KEY_DOWN | KEY_LEFT | KEY_RIGHT | KEY_A | KEY_B | KEY_X | KEY_Y | KEY_L | KEY_R | KEY_START)){
					soundPause(sound_id);
					//set freq is optimized for nds
					set_freq = freq_table[set_note] * 8 / 10;
					sound_id = soundPlayPSG(set_duty_cycle, set_freq, set_volume, set_pan);
				}
				//draw 12 equal temperament
				iprintf("\x1b[22;12H[%02d] == %04dHz", set_note, freq_table[set_note] / 10);
			}else{                     //no note data
				soundPause(sound_id);
				iprintf("\x1b[22;12H[  ] == 0000Hz");
			}
				
			swiWaitForVBlank();
			
	}
	
	return 0;
	
}