#include<thread>
#include<iostream>
#include<string.h>
#include<windows.h>
#include<conio.h>
#include<mmsystem.h>
#include<stdlib.h>
using namespace std;
#pragma comment(lib,"winmm.lib") //[1]
#define MAXLEN 127
#define XSTART 16
#define YSTART 5
#define LENOFPAGE 146
#define DEPTHOFPAGE 20
short piano_type=1; //2表示亮音钢琴,1是原声钢琴
typedef union{
int i;
char c[10];
}CwithI;
typedef struct{
char name[MAXLEN];
CwithI qusu;
char ver[20];
}HEAD;
void play_sound(char keyboard_key);//播放音频
void play_song(FILE *puzi,short *ystart,HEAD mus_info,short *flag);//播放音乐
void decoding_func(char keyboard_key,char *sound_name,short piano_type);//解码
void gotoxy(int x,int y);//移动光标至指定位置
void print_kuang();//打印框框
void print_pkeys();//打印手动模式下的静态钢琴键盘
void cls_kuang(short,short);//清除框框内指定行的信息
HEAD readhead(FILE *);//读取谱子文件的信息头
void HideCursor();//隐藏光标
void ShowCursor();//显示光标
char begin_page();//初始界面
char exit_page();//退出界面
char manual_page();//手动挡
char auto_page();//自动挡
int main(){
system("mode con cols=180 lines=38"); //[8]
short mode=0;
while(1){ //所有页面的中转站
if(mode==0){
mode = begin_page();
}
if(mode==1){
mode = manual_page();
}
if(mode==2){
mode = auto_page();
}
if(mode==3){
exit_page();
break;
}
fflush(stdin);
}
return 0;
}
char exit_page(){
HideCursor();
char sentences[][MAXLEN]={"PROGRAMME DESIGN",\
"名字 from 院系 in 学校",\
"MUSIC RESOURCE",\
"autopiano.cn",\
"SPECIAL THANKS TO",\
"Professor 老师",\
"T.A. 助教1",\
"T.A. 助教2",\
"室友",\
"github.com/WarpPrism/AutoPiano",\
"runoob.com/cprogramming/c-tutorial.html",\
"",\/*最后一段字符串由于没有下一段字符串来清掉它,会滞留在屏幕上,因此用空字符串来做最后一个*/
"Thank You for Playing My Game!"\
};
print_kuang();
short ystart=YSTART+DEPTHOFPAGE/2-11,line=0,sigofstr=0;
int speed=500;
for(line=YSTART+DEPTHOFPAGE-2;line+(sizeof(sentences)/MAXLEN-2)*2>YSTART;line--){
for(sigofstr=0;sigofstr<sizeof(sentences)/MAXLEN-1;sigofstr++){
if(kbhit()){
speed = 0; //快速跳过制作人员名单
}
if((line+sigofstr*2)<=YSTART+DEPTHOFPAGE-3&&(line+sigofstr*2)>=YSTART+2){
cls_kuang(line+sigofstr*2,-1);
cls_kuang(line+sigofstr*2,1); //上下两行都清除一下
gotoxy(XSTART+LENOFPAGE/2-strlen(sentences[sigofstr])/2,line+sigofstr*2);
cout << sentences[sigofstr];
}
}
Sleep(speed); //滚动字幕的效果
}
for(line=YSTART+DEPTHOFPAGE-3;line>=YSTART+DEPTHOFPAGE/2-1;line--){
cls_kuang(line,1);
gotoxy(XSTART+LENOFPAGE/2-strlen(sentences[sizeof(sentences)/MAXLEN-1])/2,line);
cout << sentences[sizeof(sentences)/MAXLEN-1];
Sleep(speed); //同上一条注释
}
gotoxy(XSTART+LENOFPAGE/2-strlen("请按任意键继续. . .")/2,YSTART+DEPTHOFPAGE-2);
system("PAUSE");
return 0;
}
char begin_page(){
char line_one[]="Welcome to elec-piano!";
char line_two[]="choose your mode:1.manual 2.auto 3.exit";
print_kuang();
gotoxy(XSTART+LENOFPAGE/2-strlen(line_one)/2,YSTART+DEPTHOFPAGE/2-3); //将光标移到方框的正中央并打印文字
cout << line_one;
gotoxy(XSTART+LENOFPAGE/2-strlen(line_two)/2,YSTART+DEPTHOFPAGE/2-1);
cout << line_two;
gotoxy(XSTART+LENOFPAGE/2,YSTART+DEPTHOFPAGE/2+1); //将光标移至中央
char mode[127]={0};
while(1){
cin >> mode; //输入检测
while(getchar()!='\n'){
continue;
}
if(mode[0]>='1'&&mode[0]<='3'&&strlen(mode)==1){
break;
}
else{
cls_kuang(YSTART+DEPTHOFPAGE/2+3,0);
gotoxy(XSTART+LENOFPAGE/2-strlen("Input error!please try again:")/2,YSTART+DEPTHOFPAGE/2+1);
cout << "Input error!please try again:" << endl;
gotoxy(XSTART+LENOFPAGE/2,YSTART+DEPTHOFPAGE/2+3);
}
}
system("CLS");
return (mode[0]-'0');
}
char auto_page(){
FILE *dir,*puzi,*puzi_hx;
char sys_dir_msg[MAXLEN];
char mus_namelist[50][MAXLEN]={0};
short numofsong=0,ystart=YSTART+1,xstart=XSTART+1,lenofsongname=0;
HEAD mus_info,hx_info;
print_kuang();
gotoxy(XSTART+1,ystart);
cout << "Choose your music:";
gotoxy(XSTART+1,++ystart);
dir = _popen("dir .\\songs","r");
while(!feof(dir)){
fscanf(dir,"%s",sys_dir_msg);
if(strstr(sys_dir_msg,".dat")!=NULL){//列出songs文件夹中的dat文件
strcpy(mus_namelist[numofsong],sys_dir_msg);
cout << ++numofsong << "." << sys_dir_msg;
if(strlen(sys_dir_msg)>lenofsongname){
lenofsongname = strlen(sys_dir_msg);
}
gotoxy(xstart,++ystart);
if(ystart==DEPTHOFPAGE+YSTART-1){ //文件过多时换个行继续
xstart += lenofsongname+5;
ystart = YSTART+2;
lenofsongname = 0;
gotoxy(xstart,ystart);
}
}
}
fclose(dir);
CwithI tempci;
while(1){
scanf("%9s",tempci.c);//输入检测
while(getchar()!='\n'){
continue;
}
tempci.c[9] = '\0';
tempci.i = atoi(tempci.c);
if(tempci.i==0||tempci.i>50||tempci.i<0){
cls_kuang(ystart,1);
cout << "Input Error!please try again:";
}
else{
break;
}
}
thread t(play_sound,'+'); //播放一段无声的MP3以加载播放器相关dll
t.detach();
gotoxy(XSTART+1,++ystart);
char path_mode[50]="songs\\%s";
char path[50]={0};
char path_hx[50]={0};
sprintf(path,path_mode,mus_namelist[tempci.i-1]);
strcpy(path_hx,path);
path_hx[strlen(path)-1] = 'x';
path_hx[strlen(path)-2] = 'h';
puzi = fopen(path,"r");
if(puzi==NULL){
cout << "File does not exist! Check your input";
Sleep(2500);
system("cls");
return 0;
}
short hx_flag=1;
puzi_hx = fopen(path_hx,"r");
if(puzi_hx==NULL){
hx_flag = 0;
}
else{
hx_info = readhead(puzi_hx);
}
mus_info = readhead(puzi);
system("cls");
print_kuang();
ystart=YSTART+1;
gotoxy(XSTART+1,ystart);
cout << "now play: " << mus_info.name;
gotoxy(XSTART+1,++ystart);
short flag=0;
if(hx_flag==1){//有和弦时进入此分支
while(1){
thread t1(play_song,puzi,&ystart,mus_info,&flag);
thread t2(play_song,puzi_hx,&ystart,hx_info,&hx_flag);
t2.detach();
t1.join();
if(feof(puzi)||feof(puzi_hx)||flag==2||hx_flag==2){//其中一个谱子放完了自动跳出整个循环
break;
}
}
}
else{
while(1){
thread t1(play_song,puzi,&ystart,mus_info,&flag);
t1.join();
if(feof(puzi)||flag==2){
break;
}
}
}
fclose(puzi);
if(hx_flag!=0){
fclose(puzi_hx);
}
gotoxy(XSTART+1,++ystart);
cls_kuang(ystart,1);
cout << "end" << endl;
cls_kuang(++ystart,1);
system("PAUSE");
system("CLS");
return 0;
}
char manual_page(){
HideCursor();
print_kuang();
print_pkeys();
gotoxy(XSTART+1,YSTART+DEPTHOFPAGE-2);
cout << "press Enter to quit the manual mode!";
thread t(play_sound,'+'); //播放一段无声的MP3以加载播放器相关dll
t.detach();
int i=0;
while(1){
char keyboard_key;
keyboard_key = getch(); //[2]
if(keyboard_key=='\r'){
break; //按回车退出
}
thread t(play_sound,keyboard_key); //[3]
t.detach();
FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); //[4]
}
gotoxy(XSTART+LENOFPAGE/2-strlen("exit")/2,YSTART+DEPTHOFPAGE/2);
cout << "exit!";
ShowCursor();
system("CLS");
return 0;
}
void decoding_func(char keyboard_key,char *sound_name,short piano_type){ //在plays函数中被引用,可有效降低创建线程时传参的复杂程度
char jian[]={'1','!','2','@','3','4','$','5','%','6','^','7','8','*','9','(','0','q','Q',\
'w','W','e','E','r','t','T','y','Y','u','i','I','o','O','p','P','a','s','S','d','D',\
'f','g','G','h','H','j','J','k','l','L','z','Z','x','c','C','v','V','b','B','n','m'}; //所有音名对应的按键
char zhi_zimu[][3]={"C","Cs","D","Ds","E","F","Fs","G","Gs","A","As","B"}; //音名字母部分
char zhi_shuzi[][2]={"0","1","2","3","4","5","6","7"}; //音名数字部分
for(short i=0;i<sizeof(jian);i++){
if(keyboard_key==jian[i]){
short zimu_bianhao=i%12; //字母部分12个一轮回
short shuzi_bianhao=i/12; //数字部分每十二个音+1
char ss_zimu[5],ss_shuzi[2];
strcpy(ss_zimu,zhi_zimu[zimu_bianhao]);
strcpy(ss_shuzi,zhi_shuzi[shuzi_bianhao+piano_type]); //带一个piano_type补正可以拓展音域
strcat(ss_zimu,ss_shuzi);
strcpy(sound_name,ss_zimu);
}
else{
continue;
}
}
}
void play_sound(char keyboard_key){
char sound_name[5]={0}; //音名
char temp_command[MAXLEN]={0}; //mciSendString的命令
if(keyboard_key=='+'){
strcpy(sound_name,"No_sound"); //播放一段无声的声音,用于加载与播放器有关的DLL文件
}
else{
decoding_func(keyboard_key,sound_name,piano_type); //将键盘上的键对应地解码成音名
}
sprintf(temp_command,"open piano\\%s.mp3 alias %s",sound_name,sound_name);
mciSendStringA(temp_command,0,0,0); //[5] //打开音名.mp3
sprintf(temp_command,"play %s",sound_name);
mciSendStringA(temp_command,0,0,0); //播放
Sleep(10000);
sprintf(temp_command,"close %s",sound_name); //关闭
mciSendStringA(temp_command,0,0,0);
return;
}
void play_song(FILE *puzi,short *ystart,HEAD mus_info,short *flag){
char keyboard_key;
keyboard_key = fgetc(puzi);
if(*flag==0){
cout << keyboard_key;
}
if(keyboard_key=='|'||keyboard_key=='\n'){//遇到|和换行符时,由于不对乐曲本身发挥任何作用,因此需要特殊处理
if(keyboard_key=='\n'&&(*flag)==0){
if((*ystart)+1==DEPTHOFPAGE+YSTART-1){
*ystart=YSTART+2;
cls_kuang(*ystart,1);
}
else{
if((*ystart)+2==DEPTHOFPAGE+YSTART-1){
(*ystart)++;
gotoxy(XSTART+1,*ystart);
}
else{
(*ystart)++;
cls_kuang(*ystart,1);
}
}
}
return;
}
else{
Sleep(mus_info.qusu.i);//此处控制曲速,注意:与一般音乐软件使用的曲速单位bpm不同,这里只是单纯的停顿一定毫秒
}
if(kbhit()){
char kbout='\0';
kbout = _getch();
if(kbout=='\r'){ //播放中途按回车键退出
*flag = 2;
return;
}
}
thread t(play_sound,keyboard_key); //单独分出线程来播放声音
t.detach();
return;
}
void gotoxy(int x,int y){ //[6]
_COORD pos;
pos.X=x;
pos.Y=y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos); //设置鼠标位置
}
void print_kuang(){
short start_x=XSTART,start_y=YSTART;
short page_x=LENOFPAGE,page_y=DEPTHOFPAGE;
gotoxy(start_x,start_y); //将鼠标移动到起始点
for(short y=0;y<page_y;y++){
if(y==0||y==page_y-1){
gotoxy(start_x,start_y+y);
for(short x=0;x<=page_x;x++){
cout << "="; //若为第一行或最后一行,则打印page_x个=
}
}
else{
gotoxy(start_x,start_y+y);
cout << '|';
gotoxy(start_x+page_x,start_y+y);
cout << '|'; //每行开头和末尾打印|
}
}
}
void print_pkeys(){
short ystart=YSTART+1;
gotoxy(XSTART+1,ystart);//普通的打印(很蠢)
cout << "_________________________________________________________________________________________________________________________________________________";
for(short i=0;i<6;i++){
gotoxy(XSTART+1,++ystart);
cout << "| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |";
}
gotoxy(XSTART+1,++ystart);
cout << "| |!| |@| | |$| |%| |^| | |*| |(| | |Q| |W| |E| | |T| |Y| | |I| |O| |P| | |S| |D| | |G| |H| |J| | |L| |Z| | |C| |V| |B| | |";
for(short i=0;i<2;i++){
gotoxy(XSTART+1,++ystart);
cout << "| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |";
}
gotoxy(XSTART+1,++ystart);
cout << "| |_| |_| | |_| |_| |_| | |_| |_| | |_| |_| |_| | |_| |_| | |_| |_| |_| | |_| |_| | |_| |_| |_| | |_| |_| | |_| |_| |_| | |";
for(short i=0;i<2;i++){
gotoxy(XSTART+1,++ystart);
cout << "| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |";
}
gotoxy(XSTART+1,++ystart);
cout << "| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | q | w | e | r | t | y | u | i | o | p | a | s | d | f | g | h | j | k | l | z | x | c | v | b | n | m |";
gotoxy(XSTART+1,++ystart);
cout << "| C2| D2| E2| F2| G2| A2| B2| C3| D3| E3| F3| G3| A3| B3| C4| D4| E4| F4| G4| A4| B4| C5| D5| E5| F5| G5| A5| B5| C6| D6| E6| F6| G6| A6| B6| C7|";
gotoxy(XSTART+1,++ystart);
cout << "|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|";
}
void cls_kuang(short line,short mode){
gotoxy(XSTART+1,line);//清除框框内的文字,比system(“cls”)更快
for(short c=0;c<LENOFPAGE-1;c++){
printf(" ");
}
gotoxy(XSTART+1,line+mode);//清除当前行数之外的另一行
for(short c=0;c<LENOFPAGE-1;c++){
printf(" ");
}
gotoxy(XSTART+1,line);//光标回到初始位置
}
void HideCursor(){ //[7]
CONSOLE_CURSOR_INFO cursor; //隐藏光标
cursor.bVisible = FALSE;
cursor.dwSize = sizeof(cursor);
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(handle, &cursor);
}
void ShowCursor(){
CONSOLE_CURSOR_INFO cursor; //显示光标
cursor.bVisible = TRUE;
cursor.dwSize = sizeof(cursor);
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(handle, &cursor);
}
HEAD readhead(FILE *puzi){ //读取曲谱文件的标头,获取曲速,曲名等信息
char flag=0,keyboard_key,count=0;
char tempstr[MAXLEN]={0};
HEAD temphead;
while(!feof(puzi)){
keyboard_key = fgetc(puzi);
if(keyboard_key=='<'){
flag++;
continue;
}
if(keyboard_key=='>'){
count = 0;
flag++;
if(flag==2){
strcpy(temphead.name,tempstr);
}
if(flag==4){
strcpy(temphead.qusu.c,tempstr);
temphead.qusu.i = atoi(temphead.qusu.c);
}
if(flag==6){
strcpy(temphead.ver,tempstr);
keyboard_key = fgetc(puzi);
break;
}
memset(tempstr,'\0',sizeof(tempstr));
}
if(flag%2==1){
tempstr[count++] = keyboard_key;
}
}
return temphead;
}
你好,我是有问必答小助手,非常抱歉,本次您提出的有问必答问题,技术专家团超时未为您做出解答
本次提问扣除的有问必答次数,将会以问答VIP体验卡(1次有问必答机会、商城购买实体图书享受95折优惠)的形式为您补发到账户。
因为有问必答VIP体验卡有效期仅有1天,您在需要使用的时候【私信】联系我,我会为您补发。