//------------------------------------------------
// MML player
//------------------------------------------------
// for v3.5 later
// 全機種対応

// 初期化 / initialize
font("IPAGothic")
int fzenhan
int WX=64
switch deviceType()
	case DEVICE_IPAD_SIMULATOR
	case DEVICE_IPAD
		break
	default
		WX=48
		break
endswitch
int WY=width(WX,,fzenhan)
int xw=1:/* 全角１文字の桁数 / double-width 1 character's columus
if fzenhan then xw=2

// タッチキー位置 / touch key area
int tx=WX-20:// <WX/2にすること
int ty=3
//
// 画面作成 / screen make
int lns=20:// 表示行数 / display lines
if lns>=WY-2 then lns=WY-2
makeScreen()
//
// 全ファイルリストを得る / get all file list
dim str fileList(256):// ()内は扱える最大ファイル数
int allcnt
str ext="^(mml|MML)$"
//
// 初期ファイル検索 / initial file search
int top:// 表示している先頭行要素番号:fileList(top)
// 表示範囲は(top)〜(top+lns-1)
int cur:// カーソル位置:0〜lns-1
int no,pno=-1:// 再生した音の番号
searchFiles(ext)
tcolor(-THWHITE,0,0,255)
tborder(2,200,0,0)
tBackgroundColor(0,0,200,200)
//
dim str soundFname(3)={"01_APIANO_O4C_GRAN.M30","KB_O3C_ROCK.M30","FORGAN2_A2.M44","43_WHSTL.PCM"}
dim int rate(3)={30000,30000,44100,15600}
dim int octave(3)={4,3,2,3}
dim int scale(3)={SCALE_C,SCALE_C,SCALE_A,SCALE_C}
dim int floop(3)={NO,NO,YES,YES}
//
m_init()
int i
for i=1 to 8
	m_assign(i,i)
next
int pan=64:printPanpot(pan)
int vol=64:printVolume(vol)
int snd=0 :printSound(snd)
int fpause=NO
int fmute=NO
repeat
	int top0=top,cur0=cur
	str ext0=ext
	int repCount=1:// 単発再生
	//
	repeat
		int ky=inkey()
		if m_stat(1)=1 then {
			locate(0,WY-3)
			print m_count(1),m_comment(1);
			printCtrlCode(DISP_CTRL_CLEARLINE)
			int pan1=m_pan(1,-1)
			if pan1<>pan then pan=pan1:printPanpot(pan)
			int vol1=m_vol(1,-1)
			if vol1<>vol then vol=vol1:printVolume(vol)
		}
	until ky<>0
	switch ky
		// カーソル移動 / cursor move
		case '8'
			// カーソル上 / cursor up
			if cur=0 then {
				if top>0 then top=top-1
			} else {
				cur=cur-1
			}
			break
		case '2'
			// カーソル下 / cursor down
			if cur=lns-1 then {
				if top+lns<allcnt then top=top+1
			} else {
				if top+cur<allcnt-1 then cur=cur+1
			}
			break
		// ページ単位移動 / page move
		case '4'
			// 前ページ / prev. page
			if allcnt>lns then {
				// ページ切り替えが可能なとき / When paging is enabled
				top=top-(lns-1):// 1行残す / left 1 line
				if top<0 then top=0
			}
			break
		case '6'
			// 次ページ / next page
			if allcnt>lns then {
				// ページ切り替えが可能なとき / When paging is enabled
				top=top+(lns-1):// 1行残す / left 1 line
				if top+lns>allcnt then {
					top=allcnt-lns
				}
			}
			break

		// 再生 / Play
		case 'P'
			if top+cur<>pno then {
				// ファイルが変わるとき
				m_free(1):// 前のを開放する
				m_assign(1,1)
				// ファイルからmmlをトラックに入れ込む
				str fname=fileList(top+cur)
				m_ftrk(1,fname)
//				fileToTrack(fname,1)
			}
			int mr=m_play()
			if (mr<0) then {
				dim str menues(1)
				str title$
				if isLocalizeJapan() then {
					title$="MMLにエラーがあります:"+str$(mr)
					menues={"確認"}
				} else {
					title$="MML is illegal:"+str$(mr)
					menues={"Confirm"}
				}
				selectMenu(title$,menues)
				ky=inkey()
				locate(0,1):print chr$(ky)
			}
			pno=top+cur
			break

		// 一時停止&演奏再開 / pause&continue
		case 'p'
			if (fpause) then m_cont():fpause=NO else m_stop():fpause=YES
			break

		// 単一 / solo
		case 's'
			m_solo(1)
			break

		// 音色変更 / Sound change
		case '@'
			snd=snd+1:if snd>=4 then snd=0
			if (m_tone(0,soundFname(snd),rate(snd),octave(snd),scale(snd),floop(snd))<>0) then {
				locate(0,0):print "音色ファイルが見つからない";soundFname(snd)
			}
			printSound(snd)
			break

		// パンポット左
		case 'L'
			pan=pan+16
			if pan>127 then pan=127
			m_pan(1,pan)
			printPanpot(pan)
			break

		// パンポット右
		case 'R'
			pan=pan-16
			if pan<0 then pan=0
			m_pan(1,pan)
			printPanpot(pan)
			break

		// 音量大
		case 'U'
			vol=vol+16
			if vol>127 then vol=127
			m_vol(1,vol)
			printVolume(vol)
			break

		// 音量小
		case 'D'
			vol=vol-16
			if vol<0 then vol=0
			m_vol(1,vol)
			printVolume(vol)
			break

		// 消音 / mute
		case 'm'
			if (fmute) then m_muteoff():fmute=NO else m_mute():fmute=YES
			break

		// 状態 / State
		case 'S'
			locate(30,WY-4):print "stat=";right$("0000000"+bin$(m_stat(-1)),8)
			break

		default
			break
	endswitch
	if top<>top0 then {
		// ページが変わる / page changed
		printList()
	} else {
		if cur<>cur0 then {
			// カーソル位置が変わった / cursor moved
			printList1(cur0) // 元の位置を反転解除 / non-reverse old position
			printList1(cur)	 // 新しい位置を反転 / revese new position
		}
	}
until ky='!'
snd=2:// 標準音色に戻しておく
m_tone(0,soundFname(snd),rate(snd),octave(snd),scale(snd),floop(snd))
end

func printPanpot(pan;int)
	locate(WX-10,WY-6)
	print "pan=";pan;"  ";
endfunc

func printVolume(vol;int)
	locate(WX-10,WY-5)
	print "vol=";vol;"  ";
endfunc

func printSound(snd;int)
	locate(WX-10,WY-7)
	print "snd=";snd;"  ";
endfunc

//------------------------------------------------
// 関数 / functions
//------------------------------------------------

func fileToTrack(fname;str,trk;int)
int fp
enum
	SZ_BUFF=128:// 文字列化するから最大文字列長以下にすること
endenum
dim char buff(SZ_BUFF)
	fp=fopen(fname,"r")
	while feof(fp)
		int sz=fread(buff,SZ_BUFF,fp)
		m_trk(trk,charStr(buff,sz)):// 文字列化して追加していく
	endwhile
	fclose(fp)
endfunc

func str charStr(buff();char,sz;int)
	int size1
	int cnt=sizeofArray(buff,size1)
	if cnt>sz then cnt=sz:// buffいっぱいに入ってない時はszまで
	str s=""
	int i
	for i=0 to cnt-1
		s=s+chr$(buff(i))
	next
	return(s)
endfunc

func printKeys(x;int,y;int,fdispOnly;int)
	dim int	touchNo(10)	:// タッチエリア番号 / touch area number
	int touchallcnt		:// タッチエリア数   / the number of touch area
	enum
		XC=5
		YC=2
	endenum
	int ky=(y+1)*32+8,kx=4
	int K1X=16*4,K1Y=16*4
	str dev=devicePlatform()
	if (left$(dev,4)<>"iPad") then {
		K1X=16*2
		K1Y=16*2
		kx=2
		ky=(y+1)*16-8
	}
	vpriority(GPAGE0,TPAGE,GPAGE1,GPAGE2,GPAGE3)
	vpage(B_TPAGE+B_GPAGE0+B_GPAGE1+B_GPAGE2+B_GPAGE3)
	apage(GPAGE0)
	dim int keys( XC*YC-1)={'4' ,'8','P'+FKEY_NOT_REPEAT,'2' ,'6' , 'L' ,'U','p'+FKEY_NOT_REPEAT,'D','R'}
	dim str disps(XC*YC-1)={"<<","↑","▶","↓",">>" , "左","大","‖","小","右"}
	if (fdispOnly) then {
		setTouchKeys(kx,ky,XC,YC,K1X,K1Y,keys,disps,"Migu-1M-Regular"):// 再表示のみ
	} else {
		touchallcnt =setTouchKeys(kx,ky,XC,YC,K1X,K1Y,keys,disps,"Migu-1M-Regular",touchNo)
	}
endfunc

func makeScreen()
	// タッチキー / touch key
	int x=0
	int y=1+lns+1
	printKeys(x,y,NO)

	// ファンクションキー / fucntion key
	dim str fnc$(10)
	if isLocalizeJapan() then {
		fnc$={"音色","","","単一","","","","消音","状態","終了"}
	} else {
		fnc$={"Snd","","","Solo","","","Cont","Mute","Stat","End"}
	}
	dim char key(10)={'@',0,0,'s',0,0,0,'m','S','!'}
	int i
	for i=0 to 9
		setFunctionKey(i,fnc$(i),key(i))
	next
	displayFunctionKey(YES,0,9)
endfunc

func messageDisp(mes;str)
	if (YES) then {
		dim str menues(1)
		menues(0)=localizedString("確認","Confirm")
		selectMenu(mes,menues)
	} else {
		locate(0,lns+1)
		if len(mes)=0 then {
			print chr$(DISP_CTRL_CLEARLINE)
		} else {
			print mes
		}
	}
endfunc

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

func printList1(i;int)
	// 1行表示 / 1line display
	locate(0,1+i)
	if top+i<allcnt then {
		if i=cur then tatrb(ATRB_REVERSE):// 選択行 / selected line
		print using "###:";top+i;
		print fileList(top+i);
		//	print lastPathComponent(fileList(top+i));
		if i=cur then tatrb(ATRB_NORMAL)
	}
	print chr$(DISP_CTRL_CLEARLINE):// 行末まで消す / clear to end of line
endfunc

func printList()
// 画面内表示 / display in screen
	int i
	int max=lns
	for i=0 to max-1
		printList1(i)
	next
endfunc

func searchFiles(ext;str)
	// 新しく表示し直すので各種ポインターをリセット
	top=0	:// 表示している先頭行要素番号:fileList(top)
	// 表示範囲は(top)〜(top+lns-1)
	cur=0	:// カーソル位置:0〜lns-1
	//
	// int files(pathes;文字列配列[,basedir;str][,ext;str][,fhave;int][,mode;int][,freg;int][,farea;int])
	allcnt=files(fileList,"",ext,YES,FILES_FILE,YES,FILES_FAREA_ALL):// システム領域も検索対象
	cls()
	printKeys(0,1+lns+1,YES)
	//
	locate(0,0)
	if isLocalizeJapan() then {
		print "拡張子=";ext,,
		print "ファイル数=";allcnt
	} else {
		print "extension=";ext,,
		print allcnt;" files"
	}
	printList()
endfunc

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