├── data ├── md_settings.laf ├── fourkey_settings.laf └── music │ ├── 4KMode │ ├── VacuumTrack#ADD8E6-[easy].rbq │ ├── VacuumTrack#ADD8E6-[hard].rbq │ ├── VacuumTrack#ADD8E6-[LuNATIC].rbq │ └── A_Fool_Moon_Night.rbq │ └── MuseDashMode │ ├── VacuumTrack#ADD8E6-[easy].rbq │ ├── Chilli_Daddy.rbq │ └── VacuumTrack#ADD8E6-[hard].rbq ├── LICENSE ├── tools └── osu2rbq.cpp ├── include ├── header.h ├── console.h └── song.h ├── readme.md ├── main.cpp ├── music.cpp ├── recorder.cpp ├── settings.cpp ├── md_player.cpp └── fourkey_player.cpp /data/md_settings.laf: -------------------------------------------------------------------------------- 1 | d 1 2 | f 1 3 | j 2 4 | k 2 5 | -------------------------------------------------------------------------------- /data/fourkey_settings.laf: -------------------------------------------------------------------------------- 1 | d 1 2 | f 2 3 | j 3 4 | k 4 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2012 Romain Lespinasse 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /tools/osu2rbq.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | using namespace std; 9 | 10 | struct node 11 | { 12 | int start,end,pos; 13 | node() {} 14 | node(int s, int e, int p) : start(s), end(e), pos(p) {} 15 | }; 16 | 17 | int x,y,startTime,type,sound,endTime; 18 | char s[1000]; 19 | vector lis; 20 | 21 | void change(string str) 22 | { 23 | FILE* fr = fopen((str+".osu").data(), "r"); 24 | FILE* fw = fopen((str+".rbq").data(), "w"); 25 | while(fscanf(fr, "%s", s)) 26 | if(strcmp(s, "[HitObjects]")==0) 27 | break; 28 | 29 | while(fscanf(fr, "%d,%d,%d,%d,%d,%d%s", &x, &y, &startTime, &type, &sound, &endTime, s)!=EOF) 30 | { 31 | if(x==64) x=1; 32 | if(x==192) x=2; 33 | if(x==320) x=3; 34 | if(x==448) x=4; 35 | lis.emplace_back(startTime, endTime, x); 36 | } 37 | fprintf(fw, "%d %d %d\n", lis.size(), lis.back().start+1000, 2); 38 | for(node e : lis) 39 | { 40 | if(e.end) 41 | fprintf(fw, "%d 2 %d %d\n", e.pos, e.start, e.end); 42 | else 43 | fprintf(fw, "%d 1 %d\n", e.pos, e.start); 44 | } 45 | fclose(fr); fclose(fw); 46 | } 47 | void singalMode() 48 | { 49 | string name; 50 | system("cls"); 51 | puts("input the file's name"); 52 | cin>>name; 53 | change(name); 54 | puts("done!"); 55 | _getch(); 56 | } 57 | void autoMode() 58 | { 59 | puts("nothing"); 60 | } 61 | int main() 62 | { 63 | while(1) 64 | { 65 | system("cls"); 66 | puts("1. singal file(s)"); 67 | puts("2. auto change(a)"); 68 | puts("3. quit(q)"); 69 | switch(_getch()) 70 | { 71 | case '1': case 's': case 'S': singalMode(); break; 72 | case '2': case 'a': case 'A': autoMode(); break; 73 | case '3': case 'q': case 'Q': case 27: return 0; 74 | } 75 | } 76 | return 0; 77 | } -------------------------------------------------------------------------------- /include/header.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @details header 3 | */ 4 | #ifndef _MDLP_HEADER 5 | #define _MDLP_HEADER 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | static const int OneSecond = 1000; 19 | static const int MDFallTime = 1500; 20 | static const int FourKeyFallTime = 1000; 21 | static const int Columns = 45; 22 | static const int Lines = 15; 23 | static const double MDSpeed = (double)Columns / MDFallTime; 24 | static const double FourKeySpeed = (double)Lines / FourKeyFallTime; 25 | static const int Status_Time = 200; 26 | 27 | static int start_time; 28 | static int NowTime() 29 | { 30 | return clock()-start_time; 31 | } 32 | 33 | std::pair GetMinuteSecond(int time){ 34 | 35 | time/=1000; 36 | int minute=time/60; 37 | int second=time%60; 38 | std::string min=std::to_string(minute),sec=std::to_string(second); 39 | if(minute<10) min="0"+min; 40 | if(second<10) sec="0"+sec; 41 | return std::make_pair(min,sec); 42 | 43 | } 44 | 45 | static char lower(char c) 46 | { 47 | return (c>=65 and c<=90) ? c+32 : c; 48 | } 49 | 50 | static void Print(std::string s, int times) 51 | { 52 | for(auto v : s) 53 | { 54 | std::cout << v; 55 | if(_kbhit()) times = INT_MAX,_getch(); 56 | Sleep(OneSecond/times); 57 | } 58 | } 59 | 60 | static char WaitForInput() 61 | { 62 | char ch = 0; 63 | while(true) 64 | { 65 | if(!_kbhit()) continue; 66 | else { ch = _getch(); break; } 67 | } 68 | return ch; 69 | } 70 | 71 | static void ClearScreen() 72 | { 73 | COORD coordScreen = {0, 0}; 74 | DWORD cCharsWritten; 75 | CONSOLE_SCREEN_BUFFER_INFO csbi; 76 | DWORD dwConSize; 77 | HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 78 | 79 | GetConsoleScreenBufferInfo(hConsole, &csbi); 80 | dwConSize = csbi.dwSize.X * csbi.dwSize.Y; 81 | FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten); 82 | GetConsoleScreenBufferInfo(hConsole, &csbi); 83 | SetConsoleCursorPosition(hConsole, coordScreen); 84 | } 85 | 86 | #endif // _MPLP_HEADER 87 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ~~大饼~~ 2 | 3 | # Muse Dash Light Play 4 | 5 | Muse Dash Light Play - 一款基于控制台的简约版音游。 6 | 7 | 版本:v1.21 8 | 9 | 界面(MuseDash Mode): 10 | 11 | ``` 12 | Perfect Bad Miss 13 | 7777 7777 7777 14 | ----------------------------------------------- 15 | () < O===========O 16 | () < < 17 | =============================================== 18 | ``` 19 | 20 | 界面(FourKey Mode): 21 | 22 | ``` 23 | Perfect Good bad miss 24 | 7777 7777 7777 7777 25 | ---------------------------- 26 | ~~~~ 27 | | | 28 | | | 29 | | | 30 | | | 31 | | | 32 | ~~~~ 33 | xxxx 34 | xxxx 35 | xxxx 36 | xxxx 37 | xxxx 38 | xxxx 39 | xxxx 40 | xxxx 41 | ====----====----====----==== 42 | ``` 43 | 44 | 使用方法:编译运行 `main.cpp` 45 | 46 | > 如果在游玩时出现程序异常结束的问题,请检查编译选项中是否开启了 `optimize` 优化,如果有,请取消。 47 | > 48 | > 4Key 模式的谱子可以使用 recorder 录制或使用 tools 中的 osu2rbq 将 osu 谱面转为 MDLP 谱面。 49 | > 50 | > rbqv2.0 文件更改了 note 的存储格式,旧版本的 MDLP 无法兼容,请使用 MDLP v1.20 及以上版本,recorder 只支持 rbqv2.0 文件的单 note,如果想要使用 hold 键,敬请期待 MDLP NotesMaker,或者使用 osu2rbq。 51 | 52 | 欢迎游玩,提出建议,或做出贡献(提交 Issues 和 PRs),万分感激 >w<。 53 | 54 | 谱面可以从 [MDLPSongs: MDLP 谱面社区](https://github.com/qingchenling/MDLPSongs) 获取。 55 | 56 | ## 特性 57 | 58 | - **体积轻小** - 使用不到 20KB 的 C++ 代码完成,可以随时编译运行。基于控制台输出,可以兼容大多数环境。 59 | - **易于上手** - 采用经典的音游判定方式。 60 | - **可扩展性强** - ~~因为什么功能都没实现,所以什么功能都有可能实现~~。 61 | 62 | ## 更新日志 63 | 64 | ### main: 65 | v0.04 将 settings 独立进 `main.cpp` 中。 66 | 67 | v0.05 增加谱面列表,支持自定义键位与键数。 68 | 69 | v1.00 **增加了 4Key 模式**。 70 | 71 | v1.01 隐藏了光标。 72 | 73 | v1.10 成功阻止了程序的电摇。 74 | 75 | v1.20 增加了 hold 键。 76 | 77 | v1.21 增加了偏移值。 78 | 79 | ### player 80 | 81 | v0.06 合并了线程 82 | 83 | v1.00 引入了双缓冲。 84 | 85 | v1.01 优化了判定。 86 | 87 | v1.02 增加了实时响应。 88 | 89 | v1.03 调整了判定时间。 90 | 91 | v1.04 增加了 combo 显示。 92 | 93 | v1.05 增加了 hold 键。 94 | 95 | ### recorder: 96 | 97 | v1.11 支持 record 多个谱面。 98 | 99 | v1.12 修复了 record 谱面后没有立即加载的 bug。 100 | 101 | v1.13 兼容了 4Key 录制。 102 | 103 | v1.14 兼容了 rbqv2.0。 104 | 105 | ## TODO 106 | 107 | 最后更新于 2022-10-18 16:40 108 | 109 | - [x] key mode 110 | 111 | - [x] 筛选界面(musedash/xkey) 112 | 113 | - [ ] 完善谱面信息 114 | 115 | - [x] UI优化 116 | 117 | - [ ] 谱面排行榜与评论区 118 | 119 | - [ ] 谱面预览 120 | 121 | - [ ] 谱面难度系统与对应筛选系统 122 | 123 | - [x] 判定实时响应 124 | 125 | - [x] hold 键 126 | -------------------------------------------------------------------------------- /include/console.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONSOLE 2 | #define _CONSOLE 3 | 4 | #include 5 | #include 6 | 7 | class outbuf : public std::streambuf 8 | { 9 | HANDLE h; 10 | char bytes[1024]="error: can't cin"; 11 | public: 12 | outbuf() {} 13 | outbuf(HANDLE h) : h(h) {} 14 | protected: 15 | virtual int_type underflow() override 16 | { 17 | // DWORD readen; 18 | // ReadConsole(h, bytes, 1024, &readen, NULL); 19 | setg(bytes, bytes, bytes+1024); 20 | return *gptr(); 21 | } 22 | virtual int_type overflow(int_type c) override 23 | { 24 | if (c != EOF) 25 | { 26 | DWORD written; 27 | WriteConsole(h, &c, 1, &written, NULL); 28 | } 29 | return c; 30 | } 31 | virtual std::streamsize xsputn(char_type const *s, std::streamsize count) override 32 | { 33 | DWORD written; 34 | WriteConsole(h, s, count, &written, NULL); 35 | return written; 36 | } 37 | }; 38 | 39 | class Console : public std::ostream 40 | { 41 | private: 42 | HANDLE out1,out2; 43 | outbuf ob1,ob2; 44 | void hideCur(HANDLE handle) 45 | { 46 | CONSOLE_CURSOR_INFO CursorInfo; 47 | GetConsoleCursorInfo(handle, &CursorInfo); 48 | CursorInfo.bVisible = false; 49 | SetConsoleCursorInfo(handle, &CursorInfo); 50 | } 51 | void cls(HANDLE handle) 52 | { 53 | COORD coordScreen = {0, 0}; 54 | DWORD cCharsWritten; 55 | CONSOLE_SCREEN_BUFFER_INFO csbi; 56 | DWORD dwConSize; 57 | 58 | GetConsoleScreenBufferInfo(handle, &csbi); 59 | dwConSize = csbi.dwSize.X * csbi.dwSize.Y; 60 | FillConsoleOutputCharacter(handle, TEXT(' '), dwConSize, coordScreen, &cCharsWritten); 61 | GetConsoleScreenBufferInfo(handle, &csbi); 62 | SetConsoleCursorPosition(handle, coordScreen); 63 | } 64 | public: 65 | Console() 66 | { 67 | out1=CreateConsoleScreenBuffer(GENERIC_READ|GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL); 68 | out2=CreateConsoleScreenBuffer(GENERIC_READ|GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL); 69 | ob1=outbuf(out1); ob2=outbuf(out2); 70 | hideCur(out1); hideCur(out2); 71 | } 72 | void open() { update(); } 73 | void update() 74 | { 75 | std::swap(out1, out2); std::swap(ob1, ob2); 76 | SetConsoleActiveScreenBuffer(out1); 77 | rdbuf(&ob2); cls(out2); 78 | } 79 | void close() { SetConsoleActiveScreenBuffer(GetStdHandle(STD_OUTPUT_HANDLE)); } 80 | ~Console() { CloseHandle(out1); CloseHandle(out2); } 81 | }; 82 | 83 | static Console con; 84 | 85 | #endif // _CONSOLE 86 | -------------------------------------------------------------------------------- /data/music/4KMode/VacuumTrack#ADD8E6-[easy].rbq: -------------------------------------------------------------------------------- 1 | 179 134621 2 2 | 3 1 1208 3 | 3 1 1799 4 | 1 2 2390 2981 5 | 2 1 3572 6 | 2 1 4163 7 | 3 2 4754 5345 8 | 3 1 5937 9 | 4 1 6528 10 | 3 2 7119 7710 11 | 2 1 8301 12 | 1 1 8892 13 | 4 1 9483 14 | 2 1 10074 15 | 3 1 10666 16 | 4 1 11257 17 | 2 2 11848 12439 18 | 3 1 13030 19 | 1 1 13621 20 | 2 2 14212 14804 21 | 3 1 15395 22 | 2 1 15986 23 | 3 2 16577 17168 24 | 1 1 17759 25 | 2 1 18350 26 | 4 2 18941 19533 27 | 3 2 19533 20124 28 | 2 1 20124 29 | 1 1 20715 30 | 2 1 21306 31 | 3 1 21897 32 | 4 1 22488 33 | 2 1 23079 34 | 1 1 23671 35 | 2 1 23966 36 | 3 1 24262 37 | 4 1 24557 38 | 2 1 24853 39 | 2 1 25444 40 | 3 1 26035 41 | 1 1 26626 42 | 4 1 27217 43 | 3 1 27808 44 | 2 2 28400 28991 45 | 3 2 28991 29582 46 | 4 1 29582 47 | 2 1 30173 48 | 3 1 30764 49 | 1 1 31355 50 | 3 1 31946 51 | 4 1 32538 52 | 2 2 33129 33720 53 | 3 2 33720 34311 54 | 1 1 34311 55 | 3 1 34902 56 | 4 1 35493 57 | 2 1 36084 58 | 3 1 36675 59 | 1 1 37267 60 | 3 1 37858 61 | 4 1 38744 62 | 3 2 39040 40222 63 | 2 2 39040 40222 64 | 1 2 40222 41405 65 | 4 1 41405 66 | 2 1 41996 67 | 1 1 42587 68 | 2 1 43178 69 | 3 2 43769 44360 70 | 4 1 44360 71 | 2 1 44951 72 | 3 1 45542 73 | 1 1 46134 74 | 1 1 46725 75 | 4 1 47316 76 | 3 1 47907 77 | 1 2 48498 49089 78 | 2 1 49089 79 | 4 2 49680 50272 80 | 3 2 50272 50863 81 | 4 1 50863 82 | 2 1 51454 83 | 1 1 52045 84 | 2 1 52636 85 | 3 1 53227 86 | 2 1 53818 87 | 3 1 54409 88 | 3 1 55001 89 | 2 1 55592 90 | 1 1 56183 91 | 3 1 56774 92 | 2 1 57365 93 | 4 1 57956 94 | 3 1 59139 95 | 2 1 60321 96 | 1 1 61503 97 | 3 1 62685 98 | 4 1 63868 99 | 2 1 65050 100 | 3 1 66232 101 | 1 2 66823 67414 102 | 2 1 67414 103 | 2 1 69779 104 | 3 1 70961 105 | 4 1 72143 106 | 4 1 72735 107 | 1 1 73326 108 | 1 1 73917 109 | 3 1 74508 110 | 2 1 74804 111 | 3 1 75099 112 | 2 1 75395 113 | 3 2 75690 76281 114 | 2 1 76873 115 | 3 1 77464 116 | 1 1 78055 117 | 2 2 78646 79237 118 | 3 1 79237 119 | 4 1 79828 120 | 1 1 80419 121 | 3 1 81010 122 | 4 1 81602 123 | 2 1 82193 124 | 3 1 82784 125 | 4 1 83375 126 | 3 1 83966 127 | 2 1 84557 128 | 1 2 85148 85740 129 | 4 2 85740 86331 130 | 3 1 86331 131 | 2 1 86922 132 | 4 1 87513 133 | 1 1 88104 134 | 3 1 88695 135 | 2 1 89286 136 | 4 1 89877 137 | 3 1 90469 138 | 1 1 91060 139 | 3 1 91651 140 | 4 1 92242 141 | 2 1 92833 142 | 3 1 93424 143 | 4 1 94015 144 | 1 2 94607 95198 145 | 2 2 95198 95789 146 | 3 2 95789 96971 147 | 4 2 96971 98153 148 | 3 2 98153 99336 149 | 2 2 99336 100518 150 | 1 2 100518 101700 151 | 3 2 101700 102882 152 | 2 2 102882 104065 153 | 3 1 104065 154 | 3 1 104360 155 | 1 1 104656 156 | 1 1 104951 157 | 3 2 105247 106429 158 | 2 2 106429 107611 159 | 4 2 107611 108794 160 | 3 2 108794 109976 161 | 1 2 109976 113523 162 | 2 2 113523 114705 163 | 3 2 113523 114705 164 | 1 1 114705 165 | 2 1 115887 166 | 3 1 117070 167 | 4 1 118252 168 | 2 1 119434 169 | 4 1 120616 170 | 3 1 121799 171 | 1 1 122981 172 | 4 1 124163 173 | 2 1 125345 174 | 3 1 126528 175 | 1 1 127710 176 | 4 1 128892 177 | 3 1 130074 178 | 2 1 131257 179 | 1 1 132439 180 | 3 2 133621 138350 181 | -------------------------------------------------------------------------------- /data/music/MuseDashMode/VacuumTrack#ADD8E6-[easy].rbq: -------------------------------------------------------------------------------- 1 | 179 134621 2 2 | 1 1 1208 3 | 1 1 1799 4 | 1 2 2390 2981 5 | 2 1 3572 6 | 2 1 4163 7 | 1 2 4754 5345 8 | 1 1 5937 9 | 2 1 6528 10 | 1 2 7119 7710 11 | 2 1 8301 12 | 1 1 8892 13 | 2 1 9483 14 | 2 1 10074 15 | 1 1 10666 16 | 2 1 11257 17 | 2 2 11848 12439 18 | 1 1 13030 19 | 1 1 13621 20 | 2 2 14212 14804 21 | 1 1 15395 22 | 2 1 15986 23 | 1 2 16577 17168 24 | 1 1 17759 25 | 2 1 18350 26 | 2 2 18941 19533 27 | 1 2 19533 20124 28 | 2 1 20124 29 | 1 1 20715 30 | 2 1 21306 31 | 1 1 21897 32 | 2 1 22488 33 | 2 1 23079 34 | 1 1 23671 35 | 2 1 23966 36 | 1 1 24262 37 | 2 1 24557 38 | 2 1 24853 39 | 2 1 25444 40 | 1 1 26035 41 | 1 1 26626 42 | 2 1 27217 43 | 1 1 27808 44 | 2 2 28400 28991 45 | 1 2 28991 29582 46 | 2 1 29582 47 | 2 1 30173 48 | 1 1 30764 49 | 1 1 31355 50 | 1 1 31946 51 | 2 1 32538 52 | 2 2 33129 33720 53 | 1 2 33720 34311 54 | 1 1 34311 55 | 1 1 34902 56 | 2 1 35493 57 | 2 1 36084 58 | 1 1 36675 59 | 1 1 37267 60 | 1 1 37858 61 | 2 1 38744 62 | 1 2 39040 40222 63 | 2 2 39040 40222 64 | 1 2 40222 41405 65 | 2 1 41405 66 | 2 1 41996 67 | 1 1 42587 68 | 2 1 43178 69 | 1 2 43769 44360 70 | 2 1 44360 71 | 2 1 44951 72 | 1 1 45542 73 | 1 1 46134 74 | 1 1 46725 75 | 2 1 47316 76 | 1 1 47907 77 | 1 2 48498 49089 78 | 2 1 49089 79 | 2 2 49680 50272 80 | 1 2 50272 50863 81 | 2 1 50863 82 | 2 1 51454 83 | 1 1 52045 84 | 2 1 52636 85 | 1 1 53227 86 | 2 1 53818 87 | 1 1 54409 88 | 1 1 55001 89 | 2 1 55592 90 | 1 1 56183 91 | 1 1 56774 92 | 2 1 57365 93 | 2 1 57956 94 | 1 1 59139 95 | 2 1 60321 96 | 1 1 61503 97 | 1 1 62685 98 | 2 1 63868 99 | 2 1 65050 100 | 1 1 66232 101 | 1 2 66823 67414 102 | 2 1 67414 103 | 2 1 69779 104 | 1 1 70961 105 | 2 1 72143 106 | 2 1 72735 107 | 1 1 73326 108 | 1 1 73917 109 | 1 1 74508 110 | 2 1 74804 111 | 1 1 75099 112 | 2 1 75395 113 | 1 2 75690 76281 114 | 2 1 76873 115 | 1 1 77464 116 | 1 1 78055 117 | 2 2 78646 79237 118 | 1 1 79237 119 | 2 1 79828 120 | 1 1 80419 121 | 1 1 81010 122 | 2 1 81602 123 | 2 1 82193 124 | 1 1 82784 125 | 2 1 83375 126 | 1 1 83966 127 | 2 1 84557 128 | 1 2 85148 85740 129 | 2 2 85740 86331 130 | 1 1 86331 131 | 2 1 86922 132 | 2 1 87513 133 | 1 1 88104 134 | 1 1 88695 135 | 2 1 89286 136 | 2 1 89877 137 | 1 1 90469 138 | 1 1 91060 139 | 1 1 91651 140 | 2 1 92242 141 | 2 1 92833 142 | 1 1 93424 143 | 2 1 94015 144 | 1 2 94607 95198 145 | 2 2 95198 95789 146 | 1 2 95789 96971 147 | 2 2 96971 98153 148 | 1 2 98153 99336 149 | 2 2 99336 100518 150 | 1 2 100518 101700 151 | 1 2 101700 102882 152 | 2 2 102882 104065 153 | 1 1 104065 154 | 1 1 104360 155 | 1 1 104656 156 | 1 1 104951 157 | 1 2 105247 106429 158 | 2 2 106429 107611 159 | 2 2 107611 108794 160 | 1 2 108794 109976 161 | 1 2 109976 113523 162 | 2 2 113523 114705 163 | 1 2 113523 114705 164 | 1 1 114705 165 | 2 1 115887 166 | 1 1 117070 167 | 2 1 118252 168 | 2 1 119434 169 | 2 1 120616 170 | 1 1 121799 171 | 1 1 122981 172 | 2 1 124163 173 | 2 1 125345 174 | 1 1 126528 175 | 1 1 127710 176 | 2 1 128892 177 | 1 1 130074 178 | 2 1 131257 179 | 1 1 132439 180 | 1 2 133621 138350 181 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * __ __ _____ __ ______ 3 | * /\ "-./ \ /\ __-. /\ \ /\ == \ 4 | * \ \ \-./\ \ \ \ \/\ \ \ \ \____ \ \ _-/ 5 | * \ \_\ \ \_\ \ \____- \ \_____\ \ \_\ 6 | * \/_/ \/_/ \/____/ \/_____/ \/_/ 7 | * 8 | * @version v1.21 9 | * @date 2022/10/18 10 | */ 11 | #include "include/header.h" 12 | #include "md_player.cpp" 13 | #include "fourkey_player.cpp" 14 | #include "settings.cpp" 15 | #include "recorder.cpp" 16 | using namespace std; 17 | 18 | void PlayMain(); 19 | void HideCursor(); 20 | void CheckFiles(); 21 | 22 | int main() 23 | { 24 | HideCursor(); 25 | ClearScreen(); 26 | CheckFiles(); 27 | setting[0].Prework(0); 28 | setting[1].Prework(1); 29 | Music.MusicPrework(); 30 | while(true) 31 | { 32 | ClearScreen(); 33 | cout << "===============================MDLP Main Menu=============================" << endl; 34 | cout << "1. Play(p)" << endl; 35 | cout << "2. Record(r)" << endl; 36 | cout << "3. Music(m)" <w<", 20); 56 | Sleep(500); 57 | ClearScreen(); 58 | } 59 | 60 | void PlayMain() 61 | { 62 | ClearScreen(); 63 | 64 | cout << "Choose a mode:" << endl; 65 | cout << "1. MuseDash Mode" << endl; 66 | cout << "2. 4K Mode" << endl; 67 | cout << "3. Back" << endl; 68 | 69 | while(true) 70 | { 71 | if(!_kbhit()) continue; 72 | char c = _getch(); 73 | switch(c) 74 | { 75 | case '1': MDPlayerMain(); return void(); 76 | case '2': FourKeyPlayerMain(); return void(); 77 | case '3': case 27: return void(); 78 | } 79 | } 80 | } 81 | 82 | void HideCursor() 83 | { 84 | HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); 85 | CONSOLE_CURSOR_INFO CursorInfo; 86 | GetConsoleCursorInfo(handle, &CursorInfo); 87 | CursorInfo.bVisible = false; 88 | SetConsoleCursorInfo(handle, &CursorInfo); 89 | } 90 | 91 | void CheckFiles() 92 | { 93 | if(access("data",0) == -1) 94 | mkdir("data"); 95 | if(access("data/music",0) == -1) 96 | mkdir("data/music"); 97 | if(access("data/music/MuseDashMode",0) == -1) 98 | mkdir("data/music/MuseDashMode"); 99 | if(access("data/music/4KMode",0) == -1) 100 | mkdir("data/music/4KMode"); 101 | if(access("include/header.h",0)==-1) 102 | puts("Error: 101"); 103 | if(access("include/double_console.h",0)==-1) 104 | puts("Error: 102"); 105 | } 106 | -------------------------------------------------------------------------------- /data/music/MuseDashMode/Chilli_Daddy.rbq: -------------------------------------------------------------------------------- 1 | 226 47441 1 2 | 2 1 137 3 | 2 1 509 4 | 2 1 882 5 | 2 1 1255 6 | 2 1 1627 7 | 2 1 2000 8 | 2 1 2373 9 | 2 1 2745 10 | 2 1 3118 11 | 2 1 3491 12 | 2 1 3863 13 | 2 1 4236 14 | 2 1 4609 15 | 1 1 6099 16 | 1 1 6472 17 | 1 1 6845 18 | 1 1 7217 19 | 1 1 7590 20 | 1 1 7963 21 | 1 1 8335 22 | 1 1 8708 23 | 1 1 9081 24 | 1 1 9453 25 | 1 1 9826 26 | 1 1 10199 27 | 1 1 10571 28 | 2 1 12062 29 | 2 1 12248 30 | 2 1 12435 31 | 2 1 12621 32 | 2 1 12807 33 | 2 1 12994 34 | 2 1 13180 35 | 2 1 13366 36 | 2 1 13553 37 | 2 1 13739 38 | 2 1 13925 39 | 2 1 14112 40 | 2 1 14298 41 | 2 1 14484 42 | 2 1 14671 43 | 2 1 14857 44 | 2 1 15043 45 | 2 1 15230 46 | 2 1 15416 47 | 2 1 15602 48 | 2 1 15789 49 | 2 1 15975 50 | 2 1 16161 51 | 2 1 16348 52 | 2 1 16534 53 | 1 1 18025 54 | 1 1 18211 55 | 1 1 18397 56 | 1 1 18584 57 | 1 1 18770 58 | 1 1 18956 59 | 1 1 19143 60 | 1 1 19329 61 | 1 1 19515 62 | 1 1 19702 63 | 1 1 19888 64 | 1 1 20074 65 | 1 1 20261 66 | 1 1 20447 67 | 1 1 20633 68 | 1 1 20820 69 | 1 1 21006 70 | 1 1 21192 71 | 1 1 21379 72 | 1 1 21565 73 | 1 1 21751 74 | 1 1 21938 75 | 1 1 22124 76 | 1 1 22310 77 | 1 1 22497 78 | 2 1 23987 79 | 2 1 24081 80 | 2 1 24174 81 | 2 1 24267 82 | 2 1 24360 83 | 2 1 24453 84 | 2 1 24546 85 | 2 1 24640 86 | 2 1 24733 87 | 1 1 26969 88 | 1 1 27062 89 | 1 1 27155 90 | 1 1 27248 91 | 1 1 27341 92 | 1 1 27435 93 | 1 1 27528 94 | 1 1 27621 95 | 1 1 27714 96 | 2 1 29950 97 | 2 1 30043 98 | 2 1 30136 99 | 2 1 30230 100 | 2 1 30323 101 | 2 1 30416 102 | 2 1 30509 103 | 2 1 30602 104 | 2 1 30696 105 | 1 1 32932 106 | 1 1 33025 107 | 1 1 33118 108 | 1 1 33211 109 | 1 1 33304 110 | 1 1 33397 111 | 1 1 33491 112 | 1 1 33584 113 | 1 1 33677 114 | 2 1 35913 115 | 2 1 36006 116 | 2 1 36099 117 | 2 1 36192 118 | 2 1 36286 119 | 2 1 36379 120 | 2 1 36472 121 | 2 1 36565 122 | 2 1 36658 123 | 2 1 36751 124 | 2 1 36845 125 | 2 1 36938 126 | 2 1 37031 127 | 2 1 37124 128 | 2 1 37217 129 | 2 1 37310 130 | 1 1 37404 131 | 1 1 37497 132 | 1 1 37590 133 | 1 1 37683 134 | 1 1 37776 135 | 1 1 37869 136 | 1 1 37963 137 | 1 1 38056 138 | 1 1 38149 139 | 1 1 38264 140 | 1 1 38335 141 | 1 1 38428 142 | 1 1 38522 143 | 1 1 38615 144 | 1 1 38708 145 | 1 1 38801 146 | 2 1 38894 147 | 2 1 38987 148 | 2 1 39081 149 | 2 1 39174 150 | 2 1 39267 151 | 2 1 39360 152 | 2 1 39453 153 | 2 1 39546 154 | 2 1 39640 155 | 2 1 39733 156 | 2 1 39826 157 | 2 1 39919 158 | 2 1 40012 159 | 2 1 40105 160 | 2 1 40199 161 | 2 1 40292 162 | 1 1 40385 163 | 1 1 40478 164 | 1 1 40571 165 | 1 1 40664 166 | 1 1 40758 167 | 1 1 40851 168 | 1 1 40944 169 | 1 1 41037 170 | 1 1 41130 171 | 1 1 41223 172 | 1 1 41317 173 | 1 1 41410 174 | 1 1 41503 175 | 1 1 41596 176 | 1 1 41689 177 | 1 1 41782 178 | 2 1 41876 179 | 2 1 41969 180 | 2 1 42062 181 | 2 1 42155 182 | 2 1 42248 183 | 2 1 42341 184 | 2 1 42435 185 | 2 1 42528 186 | 2 1 42621 187 | 2 1 42714 188 | 2 1 42807 189 | 2 1 42900 190 | 2 1 42994 191 | 2 1 43087 192 | 2 1 43180 193 | 2 1 43273 194 | 1 1 43366 195 | 1 1 43459 196 | 1 1 43553 197 | 1 1 43646 198 | 1 1 43739 199 | 1 1 43832 200 | 1 1 43925 201 | 1 1 44018 202 | 1 1 44112 203 | 1 1 44205 204 | 1 1 44298 205 | 1 1 44391 206 | 1 1 44484 207 | 1 1 44577 208 | 1 1 44671 209 | 1 1 44764 210 | 2 1 44857 211 | 2 1 44950 212 | 2 1 45043 213 | 2 1 45136 214 | 2 1 45230 215 | 2 1 45323 216 | 2 1 45416 217 | 2 1 45509 218 | 2 1 45602 219 | 2 1 45696 220 | 2 1 45789 221 | 2 1 45882 222 | 2 1 45975 223 | 2 1 46068 224 | 2 1 46161 225 | 2 1 46255 226 | -------------------------------------------------------------------------------- /music.cpp: -------------------------------------------------------------------------------- 1 | #ifndef _MDLP_MUSIC 2 | #define _MDLP_MUSIC 3 | 4 | #include "include/header.h" 5 | using namespace std; 6 | 7 | class Music 8 | { 9 | private: 10 | vector> list[2]; 11 | 12 | public: 13 | void MusicPrework(); 14 | void MusicMain(); 15 | void Load(int type); 16 | void PrintList(int type); 17 | FILE *ChooseMusic(int type); 18 | } Music; 19 | 20 | void Music::MusicPrework() 21 | { 22 | Load(1), Load(2); 23 | } 24 | 25 | void Music::MusicMain() 26 | { 27 | ClearScreen(); 28 | cout << "=========================MDLP Music Menu=======================" << endl; 29 | cout << "1. MuseDash Mode" << endl; 30 | cout << "2. 4K Mode" << endl; 31 | cout << "3. Back" << endl; 32 | cout << "===============================================================" << endl; 33 | 34 | int type = 1; 35 | while(true) 36 | { 37 | if(!_kbhit()) continue; 38 | char c = _getch(); 39 | if(c == '1') { type = 1; break; } 40 | else if(c == '2') { type = 2; break; } 41 | else if(c == '3') return void(); 42 | } 43 | 44 | PrintList(type); 45 | 46 | cout << "Press 'q' to quit."; 47 | while(true) 48 | { 49 | if(!_kbhit()) continue; 50 | char c = _getch(); 51 | if(c == 'q' or c == 'Q') break; 52 | } 53 | } 54 | 55 | void Music::Load(int type) 56 | { 57 | ClearScreen(); 58 | string type_name = type == 1 ? "MuseDashMode" : "4KMode"; 59 | Print("Loading ", 20); 60 | Print(type_name, 20); 61 | Print(" Music...\n", 20); 62 | type--; 63 | list[type].clear(); 64 | 65 | string path = ".\\data\\music\\" + type_name + "\\"; 66 | long long hFile = 0; 67 | struct _finddata_t fileinfo; 68 | string p; 69 | int cnt = 0; 70 | if((hFile = _findfirst(p.assign(path).append("\\*.rbq").c_str(), &fileinfo)) != -1) 71 | { 72 | do 73 | { 74 | if(!(fileinfo.attrib & _A_SUBDIR)) 75 | { 76 | cnt++; 77 | list[type].emplace_back(cnt, fileinfo.name); 78 | cout << "Loading " << fileinfo.name << endl; 79 | } 80 | } while(_findnext(hFile, &fileinfo) == 0); 81 | _findclose(hFile); 82 | } 83 | 84 | Sleep(OneSecond / 5); 85 | cout << "Loaded!"; 86 | Sleep(OneSecond / 5); 87 | } 88 | 89 | void Music::PrintList(int type) 90 | { 91 | ClearScreen(); 92 | type--; 93 | printf("=============== Music List(total %d) ===============\n", list[type].size()); 94 | 95 | for(int i = 0; i < list[type].size(); i++) 96 | cout << list[type][i].first << " " << list[type][i].second << endl; 97 | 98 | printf("===================================================\n"); 99 | } 100 | 101 | /** 102 | * @param type 1: MuseDash Mode, 2: 4K Mode 103 | */ 104 | FILE *Music::ChooseMusic(int type) 105 | { 106 | begin: 107 | ClearScreen(); 108 | PrintList(type); 109 | 110 | type--; 111 | puts("Choose a music, input 114514 to quit:"); 112 | 113 | int id; cin >> id; 114 | 115 | if(id == 114514) return NULL; 116 | 117 | if(id > list[type].size()) 118 | { 119 | puts("Invalid ID!"); 120 | Sleep(OneSecond); 121 | goto begin; 122 | } 123 | 124 | string type_name = type == 0 ? "MuseDashMode" : "4KMode"; 125 | string path = ".\\data\\music\\"; 126 | path += type_name + "\\" + list[type][id - 1].second; 127 | 128 | return fopen(path.data(), "r"); 129 | } 130 | 131 | #endif // _MDLP_MUSIC 132 | -------------------------------------------------------------------------------- /recorder.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @details A recorder to record the Spectrum of MuseDashLightPlay. 3 | * @author LordLaffey 4 | * @version v1.13 5 | */ 6 | 7 | #include "include/console.h" 8 | #include "include/header.h" 9 | #include "settings.cpp" 10 | #include "music.cpp" 11 | 12 | using namespace std; 13 | 14 | void About(); 15 | void Help(); 16 | void Record(); 17 | void RecordMain(); 18 | void Save(string name, int cnt, int len, int type); 19 | 20 | void RecordMain() 21 | { 22 | 23 | ClearScreen(); 24 | 25 | while(true) 26 | { 27 | ClearScreen(); 28 | cout << "==========================MDLP Spectrum Recorder==========================" << endl; 29 | cout << "1. Start recording(s)" << endl; 30 | cout << "2. About(a)" << endl; 31 | cout << "3. Exit(q)" << endl; 32 | cout << "==========================================================================" << endl; 33 | 34 | char c = WaitForInput(); 35 | switch(lower(c)) 36 | { 37 | case 's': Record(); break; 38 | case 'a': About(); break; 39 | case 'q': case 27: return void(); break; 40 | default: break; 41 | } 42 | } 43 | } 44 | 45 | void About() 46 | { 47 | ClearScreen(); 48 | Print("This is a project to record the Spectrum of MuseDashLightPlay.\n", 20); 49 | Print("This program is written by C++.\n", 20); 50 | Print("And it is coded by LingChen, LordLaffey and Ptilopsis_w.\n", 20); 51 | Print("You can contact us by github:@LordLaffey, @qingchenling, @Ptilopsis_w.\n", 20); 52 | WaitForInput(); 53 | } 54 | 55 | void Save(string name, int cnt, int len, int type) 56 | { 57 | ClearScreen(); 58 | Print("Saveing...\n", 20); 59 | Sleep(OneSecond / 2); 60 | FILE *fr = fopen(name.data(), "r"); 61 | FILE *tmpw = fopen("data/music/tmp.rubbish", "w"); 62 | 63 | if(fr == nullptr) 64 | { 65 | cout << "Error: Cannot open file!" << endl; 66 | return void(); 67 | } 68 | 69 | char s1[1000], s2[1000]; 70 | for(int i = 1; i <= cnt; i++) 71 | { 72 | fscanf(fr, "%s ", s1); 73 | fscanf(fr, "%s\n", s2); 74 | fprintf(tmpw, "%s %s\n", s1, s2); 75 | } 76 | 77 | fclose(fr); 78 | fclose(tmpw); 79 | 80 | FILE *fw = fopen(name.data(), "w"); 81 | FILE *tmpr = fopen("data/music/tmp.rubbish", "r"); 82 | 83 | fprintf(fw, "%d %d %d\n", cnt, len, type); 84 | 85 | for(int i = 1; i <= cnt; i++) 86 | { 87 | fscanf(tmpr, "%s ", s1); 88 | fscanf(tmpr, "%s\n", s2); 89 | fprintf(fw, "%s %d %s\n",s2,1,s1); 90 | } 91 | 92 | fclose(fw); 93 | fclose(tmpr); 94 | 95 | system("del data\\music\\tmp.rubbish"); 96 | 97 | puts("Saved!"); 98 | Sleep(OneSecond / 2); 99 | 100 | puts("Reloading music list..."); 101 | Music.Load(type); 102 | } 103 | 104 | void Record() 105 | { 106 | ClearScreen(); 107 | cout << "Please input the name of the music:" << endl; 108 | string name; 109 | cin >> name; 110 | 111 | ChooseType: 112 | ClearScreen(); 113 | 114 | cout << "Please input the type of the music:" << endl; 115 | cout << "1. MuseDash mode" << endl; 116 | cout << "2. 4K mode" << endl; 117 | 118 | int type; 119 | cin >> type; 120 | string type_name = type == 1 ? "MuseDashMode" : "4KMode"; 121 | type--; 122 | 123 | name = "data/music/" + type_name + "/" + name + ".rbq"; 124 | FILE *fw = fopen(name.data(), "w"); 125 | 126 | if(type != 1 && type != 2) 127 | { 128 | puts("No such type!"); 129 | Sleep(OneSecond / 2); 130 | goto ChooseType; 131 | } 132 | 133 | ClearScreen(); 134 | cout << "Press any key to start recording..." << endl; 135 | 136 | WaitForInput(); 137 | 138 | Print("3...\n", 5); 139 | Print("2...\n", 5); 140 | int start_time = clock(); 141 | Print("1...\n", 5); 142 | cout << "Start!" << endl; 143 | 144 | ClearScreen(); 145 | 146 | int cnt = 0; 147 | while(true) 148 | { 149 | if(!_kbhit()) continue; 150 | char c = _getch(); 151 | if(c == 'q' or c == 'Q') 152 | break; 153 | int key = setting[type].checkKey(c); 154 | if(key != -1) 155 | { 156 | fprintf(fw, "%d ", clock()-start_time); 157 | fprintf(fw, "%d\n", key); 158 | printf("%dms ", clock()-start_time); 159 | printf("%d\n", key); 160 | cnt++; 161 | } 162 | } 163 | 164 | fclose(fw); 165 | Save(name, cnt, clock()-start_time, type); 166 | } 167 | -------------------------------------------------------------------------------- /settings.cpp: -------------------------------------------------------------------------------- 1 | #ifndef _MDLP_SETTING 2 | #define _MDLP_SETTING 3 | 4 | #include "include/header.h" 5 | 6 | using namespace std; 7 | 8 | /** 9 | * @details KeyChecker Class 10 | */ 11 | class Setting{ 12 | 13 | private: 14 | vector > keys; 15 | string path; 16 | void write() 17 | { 18 | FILE* fw = fopen(path.data(),"w"); 19 | for(auto key : keys) 20 | fprintf(fw, "%c %d\n", key.first, key.second); 21 | fclose(fw); 22 | } 23 | void read() 24 | { 25 | FILE* fr = fopen(path.data(),"r"); 26 | keys.clear(); 27 | char c; int x; 28 | while(fscanf(fr, "%c %d\n", &c, &x) != EOF) 29 | keys.emplace_back(c, x); 30 | fclose(fr); 31 | } 32 | public: 33 | void Prework(int typ){ 34 | 35 | path=typ==0?"data/md_settings.laf":"data/fourkey_settings.laf"; 36 | load(); 37 | 38 | } 39 | void load() 40 | { 41 | ClearScreen(); 42 | Print("Loading...\n", 20); 43 | 44 | if(access(path.data(),0) == -1) 45 | { 46 | if(path=="data/md_settings.laf") 47 | keys = {{'d',1}, {'f',1}, {'j',2}, {'k',2}}; 48 | else keys = {{'d',1}, {'f',2}, {'j',3}, {'k',4}}; 49 | write(); 50 | } 51 | 52 | read(); 53 | puts("Completed!"); 54 | Sleep(OneSecond/20); 55 | } 56 | int checkKey(char c) 57 | { 58 | for(auto key : keys) 59 | if(c == key.first or c == key.first-32) 60 | return key.second; 61 | return -1; 62 | } 63 | void printKey() 64 | { 65 | ClearScreen(); 66 | puts("Your key is:"); 67 | for(auto key : keys) 68 | printf("%c %d\n", key.first, key.second); 69 | WaitForInput(); 70 | } 71 | void set(vector> newkeys) 72 | { 73 | keys = newkeys; 74 | write(); 75 | } 76 | 77 | }; 78 | 79 | Setting setting[2]; 80 | 81 | void ResetKey(int); 82 | 83 | void SettingsMain() 84 | { 85 | begin: 86 | 87 | ClearScreen(); 88 | 89 | puts("1. Muse Dash Setting."); 90 | puts("2. 4Key Setting."); 91 | puts("3. Back."); 92 | 93 | int type; 94 | while(1) 95 | { 96 | if(!_kbhit()) continue; 97 | char c = _getch(); 98 | if(c=='1') 99 | {type = 1; break;} 100 | else if(c=='2') 101 | {type = 2; break;} 102 | else if(c=='3') 103 | {goto end; break;} 104 | } 105 | 106 | ClearScreen(); 107 | Print(type==1?"Muse Dash ":"4Key ", 20); 108 | Print("Settings:\n",20); 109 | cout << "1. Reset the keys." << endl; 110 | cout << "2. Print the keys." << endl; 111 | cout << "3. Back.\n" << endl; 112 | 113 | type--; 114 | while(true) 115 | { 116 | if(!_kbhit()) continue; 117 | char c = _getch(); 118 | switch(c) 119 | { 120 | case '1': ResetKey(type+1); goto end; 121 | case '2': setting[type].printKey(); goto end; 122 | case '3': goto begin; break; 123 | default: break; 124 | } 125 | } 126 | 127 | end:; 128 | } 129 | 130 | void ResetKey(int type) 131 | { 132 | type--; 133 | vector> keys; 134 | set st; 135 | 136 | begin: 137 | system("cls"); 138 | puts(type?"4Key Settings:":"Muse Dash Settings:"); 139 | puts("Input a char and a number on a line, you can input many until \"0\" \n"); 140 | 141 | keys.clear(); 142 | st.clear(); 143 | 144 | while(true) 145 | { 146 | char c; int x; 147 | scanf("%c", &c); 148 | if(c == '0') break; 149 | scanf("%d\n", &x); 150 | 151 | c = lower(c); 152 | if(st.count(c)) 153 | { 154 | puts("Error: The key cannot be the same!"); 155 | continue; 156 | } 157 | keys.emplace_back(c, x); 158 | } 159 | 160 | puts("Sure to Save?(y/n)"); 161 | while(true) 162 | { 163 | if(!_kbhit()) continue; 164 | char c = _getch(); 165 | if(c == 'n' or c == 'N') goto begin; 166 | else if(c == 'y' or c == 'Y') break; 167 | } 168 | 169 | puts("Saving..."); 170 | Sleep(OneSecond); 171 | setting[type].set(keys); 172 | puts("The settings has saved!"); 173 | Sleep(OneSecond); 174 | } 175 | 176 | #endif // _MDLP_SETTING 177 | -------------------------------------------------------------------------------- /include/song.h: -------------------------------------------------------------------------------- 1 | #ifndef _SONG 2 | #define _SONG 3 | 4 | #include "header.h" 5 | using namespace std; 6 | 7 | struct Note { 8 | int type; // 1: 短键 2: 长条 9 | int start,end; 10 | int len = 0; 11 | bool hvcheck = false; 12 | Note() {} 13 | Note(int t, int s, int e) : type(t), start(s), end(e) {} 14 | }; 15 | 16 | class Song { 17 | private: 18 | // 判定偏移 19 | static const int Prefect = 50; 20 | static const int Great = 100; 21 | static const int Bad = 150; 22 | struct Track 23 | { 24 | int note_cnt; 25 | int now_note, can_seen; 26 | vector notes; 27 | void reset() 28 | { 29 | now_note = 1; 30 | can_seen = 1; 31 | notes.clear(); 32 | notes.emplace_back(); 33 | } 34 | int run() 35 | { 36 | int miss=0; 37 | while(now_note <= note_cnt and !GetNoteState(notes[now_note].end)) 38 | now_note++,miss++; 39 | while(can_seen <= note_cnt and GetNoteState(notes[can_seen].start) != 5) 40 | can_seen++; 41 | return miss; 42 | } 43 | vector getNotes() 44 | { 45 | vector v; 46 | for(int i=now_note; i Bad) return 0; 62 | else if(abs(now_time-time) <= Prefect) return 1; 63 | else if(abs(now_time-time) <= Great) return 2; 64 | else if(abs(now_time-time) <= Bad) return 3; 65 | else if(time-now_time <= MDFallTime) return 4; 66 | else return 5; 67 | } 68 | public: 69 | atomic perfect_tot, good_tot, bad_tot, miss_tot; 70 | 71 | bool LoadSpectrum(FILE *fr) 72 | { 73 | // 加载文件 74 | cout << "Loading spectrum..." << endl; 75 | if(fr == nullptr) 76 | { 77 | cout << "No spectrum found" << endl; 78 | Sleep(OneSecond); 79 | return false; 80 | } 81 | 82 | // 初始化 83 | int type,readType; 84 | perfect_tot = 0; 85 | good_tot = 0; 86 | bad_tot = 0; 87 | miss_tot = 0; 88 | for(int i=1; i<=trackNum; i++) track[i].reset(); 89 | 90 | fscanf(fr, "%d %d %d", ¬e_cnt, &allTime, &type); 91 | allTime+=OneSecond; 92 | trackNum=type*2; // 转换为轨道数 93 | for(int i = 1; i <= note_cnt; i++) 94 | { 95 | int line,type,start,end; 96 | fscanf(fr, "%d %d %d", &line, &type, &start); 97 | if(type==2) // 滑条 98 | fscanf(fr, "%d", &end); 99 | else 100 | end=start; 101 | 102 | track[line].notes.emplace_back(type, start+OneSecond, end+OneSecond); 103 | } 104 | for(int i=1; i<=trackNum; i++) track[i].note_cnt=track[i].notes.size()-1; 105 | 106 | fclose(fr); 107 | cout << "Spectrum loaded!" << endl; 108 | Sleep(OneSecond); 109 | ClearScreen(); 110 | return true; 111 | } 112 | 113 | /** 114 | * @brief 设置 hold 的初始长度 115 | * @param flag [0|1] MDMode/FKMode 116 | */ 117 | void MakeHolds(bool flag){ 118 | 119 | const double Speed=!flag?MDSpeed:FourKeySpeed; 120 | for(int i = 1; i <= trackNum; i++) 121 | { 122 | for(auto &v : track[i].notes) 123 | { 124 | if(v.type!=2) continue; 125 | v.len = Speed * (v.end-v.start); 126 | } 127 | } 128 | 129 | } 130 | 131 | /** 132 | * @brief 调整当前时间 133 | * @return 是否有 miss 134 | */ 135 | 136 | bool run() 137 | { 138 | int miss=0; 139 | for(int i=1; i<=trackNum; i++) 140 | miss+=track[i].run(); 141 | miss_tot+=miss; 142 | return miss!=0; 143 | } 144 | 145 | /** 146 | * @brief 获取指定轨道应显示的notes 147 | * @param line 轨道号 148 | */ 149 | vector getNotes(int line) { return track[line].getNotes();} 150 | 151 | /** 152 | * @brief 获取指定行行首note的状态 153 | * @param line 轨道号 154 | * @param type [0|1] 键按下/键抬起 155 | * @return 0: Miss, 1: Perfect, 2: Great, 3: Bad, 4: Far, 5: Cannot See 156 | */ 157 | 158 | int getStatus(int line,int type){ 159 | 160 | Note ¬e = track[line].notes[track[line].now_note]; 161 | int lstpress=isPress; 162 | 163 | isPress=(type==1); 164 | if(note.type==1&&type==1) 165 | { 166 | int state = GetNoteState(note.start); 167 | if(state<=2+(trackNum==4)) track[line].now_note++; 168 | return GetNoteState(note.start); 169 | } 170 | else if(note.type==2) 171 | { 172 | if(type==1) 173 | { 174 | int s=GetNoteState(note.start); 175 | if(note.hvcheck) 176 | { 177 | if(NowTime()>note.end) 178 | track[line].now_note++,s=1; 179 | else s=4; 180 | } 181 | else if(!note.hvcheck) 182 | { 183 | if(!lstpress&&s<=2+(trackNum==4)) 184 | note.hvcheck=true; 185 | else s=4; 186 | } 187 | return s; 188 | } 189 | else 190 | { 191 | int e=GetNoteState(note.end); 192 | if(note.hvcheck) 193 | { 194 | track[line].now_note++; 195 | if(e>=1&&e<=2) return e; 196 | else return 0; 197 | } 198 | } 199 | } 200 | 201 | return 5; 202 | 203 | } 204 | 205 | bool isEnd() { return NowTime()>=allTime;} 206 | int GetAllTime() { return allTime;} 207 | }; 208 | 209 | static Song song; 210 | 211 | #endif 212 | -------------------------------------------------------------------------------- /md_player.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file md_player.cpp 3 | * @details The player of MuseDash Mode 4 | * @authors Ptilosis_w, LordLaffey, qingchenling 5 | * @version v1.06 6 | * @date 2022-11-2 7 | */ 8 | 9 | #include "include/console.h" 10 | #include "include/header.h" 11 | #include "include/song.h" 12 | #include "music.cpp" 13 | #include "settings.cpp" 14 | 15 | using namespace std; 16 | 17 | char WaitForInput(); 18 | void MDPrintScreen(); 19 | void MDPlayerMain(); 20 | void MDCheckKeys(); 21 | void MDChangeStatus(int); 22 | void MDMakeHolds(); 23 | bool MDPrework(); 24 | void MDEndwork(); 25 | 26 | HHOOK md_keyboardHook = 0; // 钩子句柄 27 | atomic md_combo; 28 | atomic md_status,md_status_start; 29 | atomic md_quit_flag; 30 | 31 | void MDPlayerMain() 32 | { 33 | ClearScreen(); 34 | if(!MDPrework()) return ; 35 | 36 | cout << "Press any key to start" << endl; 37 | WaitForInput(); 38 | Print("Ready...\n", 9); 39 | Print("GO!!!\n", 6); 40 | ClearScreen(); 41 | 42 | con.open(); 43 | start_time = clock(); 44 | thread print(MDPrintScreen); 45 | thread check(MDCheckKeys); 46 | print.join(); 47 | check.join(); 48 | 49 | con.close(); 50 | 51 | Sleep(OneSecond); 52 | MDEndwork(); 53 | 54 | } 55 | 56 | /** 57 | * @brief 监听按键回调函数 58 | * @param nCode 规定钩子如何处理消息,小于 0 则直接 CallNextHookEx 59 | * @param wParam 消息类型 60 | * @param lParam 指向某个结构体的指针,这里是 KBDLLHOOKSTRUCT(低级键盘输入事件) 61 | */ 62 | LRESULT CALLBACK MD_LowLevelKeyboardProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam) 63 | { 64 | KBDLLHOOKSTRUCT *ks = (KBDLLHOOKSTRUCT*)lParam; // 包含低级键盘输入事件信息 65 | /* 66 | typedef struct tagKBDLLHOOKSTRUCT { 67 | DWORD vkCode; // 按键代号 68 | DWORD scanCode; // 硬件扫描代号,同 vkCode 也可以作为按键的代号。 69 | DWORD flags; // 事件类型,一般按键按下为 0 抬起为 128。 70 | DWORD time; // 消息时间戳 71 | ULONG_PTR dwExtraInfo; // 消息附加信息,一般为 0。 72 | }KBDLLHOOKSTRUCT,*LPKBDLLHOOKSTRUCT,*PKBDLLHOOKSTRUCT; 73 | */ 74 | 75 | if(ks->vkCode == 27) md_quit_flag=true; 76 | int now = setting[0].checkKey(ks->vkCode); 77 | 78 | if(now!=-1) 79 | { 80 | switch(song.getStatus(now, (ks->flags == 0))) 81 | { 82 | case 1: song.perfect_tot++; MDChangeStatus(1); md_combo++; break; 83 | case 2: song.good_tot++; MDChangeStatus(2); md_combo++; break; 84 | case 3: song.miss_tot++; MDChangeStatus(0); md_combo = 0; break; 85 | } 86 | } 87 | 88 | return CallNextHookEx(NULL, nCode, wParam, lParam); 89 | } 90 | 91 | void MDCheckKeys() 92 | { 93 | // 安装钩子 94 | md_keyboardHook = SetWindowsHookEx( 95 | WH_KEYBOARD_LL, // 钩子类型,WH_KEYBOARD_LL 为键盘钩子 96 | MD_LowLevelKeyboardProc, // 指向钩子函数的指针 97 | GetModuleHandleA(NULL), // Dll 句柄 98 | (DWORD)NULL 99 | ); 100 | 101 | if (md_keyboardHook == 0) // 挂钩失败 102 | { 103 | md_quit_flag = true;// make md_printscreen quit 104 | puts("fuck hook"); 105 | return ; 106 | } 107 | 108 | MSG msg; 109 | while(!song.isEnd()) 110 | { 111 | bool tmp = song.run(); 112 | if(tmp) MDChangeStatus(0),md_combo = 0; 113 | if(md_quit_flag) break; 114 | 115 | // 如果消息队列中有消息 116 | if (PeekMessageA( 117 | &msg, // MSG 接收这个消息 118 | NULL, // 检测消息的窗口句柄,NULL:检索当前线程所有窗口消息 119 | (UINT)NULL, // 检查消息范围中第一个消息的值,NULL:检查所有消息(必须和下面的同时为NULL) 120 | (UINT)NULL, // 检查消息范围中最后一个消息的值,NULL:检查所有消息(必须和上面的同时为NULL) 121 | PM_REMOVE // 处理消息的方式,PM_REMOVE:处理后将消息从队列中删除 122 | )) 123 | { 124 | TranslateMessage(&msg); // 把按键消息传递给字符消息 125 | DispatchMessageW(&msg); // 将消息分派给窗口程序 126 | } 127 | else 128 | Sleep(0); //避免CPU全负载运行 129 | } 130 | UnhookWindowsHookEx(md_keyboardHook); // 删除钩子 131 | } 132 | 133 | void MDPrintScreen() 134 | { 135 | static char output[3][105]; 136 | static vector note; 137 | while(!song.isEnd()) 138 | { 139 | if(md_quit_flag) return ; 140 | 141 | con << "Perfect\t\tGreat\t\tMiss" << endl; 142 | con << (int)song.perfect_tot << "\t\t" << (int)song.good_tot << "\t\t" << (int)song.miss_tot << endl; 143 | con << "-----------------------------------------------" << endl; 144 | 145 | memset(output, 0, sizeof(output)); 146 | for(int i = 1; i <= 2; i++) 147 | { 148 | note = song.getNotes(i); 149 | for(auto v:note) 150 | { 151 | if(v.type == 1) 152 | { 153 | int pos = MDSpeed * (v.start-NowTime()) + 2; 154 | if(pos > 0 and pos <= 47) output[i][pos] = '<'; 155 | } 156 | else 157 | { 158 | int spos = MDSpeed * (v.start-NowTime()) + 2; 159 | int epos = MDSpeed * (v.end-NowTime()) + 2; 160 | if(spos > 0 and spos <= 47) 161 | output[i][spos]='O'; 162 | else spos=0; 163 | if(epos > 0 and epos <= 47) 164 | output[i][epos]='O'; 165 | else if(epos>0) epos=48; 166 | for(int j = spos+1; j < epos; j++) 167 | output[i][j]='='; 168 | } 169 | } 170 | } 171 | 172 | // 拼接 173 | for(int i = 1; i <= 47; i++) 174 | { 175 | if(!output[1][i]) output[1][i] = ' '; 176 | if(!output[2][i]) output[2][i] = ' '; 177 | } 178 | output[1][48] = output[2][48] = '\n'; 179 | output[1][1] = output[2][1] = '('; 180 | output[1][2] = output[2][2] = ')'; 181 | strcat(output[1]+1, output[2]+1); 182 | con << output[1]+1; 183 | con << "===============================================" << endl; 184 | 185 | // 输出状态栏 186 | if(md_status != -1 && NowTime() - md_status_start <= Status_Time) 187 | { 188 | if(md_status==0) con << "Miss "; 189 | else if(md_status==1) con << "Perfect"; 190 | else if(md_status==2) con << "Great "; 191 | } 192 | else con << " "; 193 | pair MS = GetMinuteSecond(song.GetAllTime()-NowTime()); 194 | con<=5) 196 | con< fourkey_combo; 25 | atomic fourkey_status,fourkey_status_start; 26 | atomic fourkey_quit_flag; 27 | void FourKeyPlayerMain() 28 | { 29 | ClearScreen(); 30 | if(!FourKeyPrework()) return ; 31 | 32 | cout << "Press any key to start" << endl; 33 | WaitForInput(); 34 | Print("Ready...\n", 9); 35 | Print("GO!!!\n", 6); 36 | ClearScreen(); 37 | 38 | con.open(); 39 | start_time = clock(); 40 | thread print(FourkeyPrintScreen); 41 | thread check(FourKeyCheckKeys); 42 | print.join(); 43 | check.join(); 44 | con.close(); 45 | 46 | Sleep(OneSecond); 47 | FourKeyEndwork(); 48 | 49 | } 50 | 51 | /** 52 | * @brief 监听按键回调函数 53 | * @param nCode 规定钩子如何处理消息,小于 0 则直接 CallNextHookEx 54 | * @param wParam 消息类型 55 | * @param lParam 指向某个结构体的指针,这里是 KBDLLHOOKSTRUCT(低级键盘输入事件) 56 | */ 57 | LRESULT CALLBACK FourKey_LowLevelKeyboardProc(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam) 58 | { 59 | KBDLLHOOKSTRUCT *ks = (KBDLLHOOKSTRUCT*)lParam; // 包含低级键盘输入事件信息 60 | /* 61 | typedef struct tagKBDLLHOOKSTRUCT { 62 | DWORD vkCode; // 按键代号 63 | DWORD scanCode; // 硬件扫描代号,同 vkCode 也可以作为按键的代号。 64 | DWORD flags; // 事件类型,一般按键按下为 0 抬起为 128。 65 | DWORD time; // 消息时间戳 66 | ULONG_PTR dwExtraInfo; // 消息附加信息,一般为 0。 67 | }KBDLLHOOKSTRUCT,*LPKBDLLHOOKSTRUCT,*PKBDLLHOOKSTRUCT; 68 | */ 69 | 70 | if(ks->vkCode == 27) fourkey_quit_flag=true; 71 | int now = setting[1].checkKey(ks->vkCode); 72 | 73 | if(now!=-1) 74 | { 75 | switch(song.getStatus(now, (ks->flags == 0))) 76 | { 77 | case 1: song.perfect_tot++; FourKeyChangeStatus(1); fourkey_combo++; break; 78 | case 2: song.good_tot++; FourKeyChangeStatus(2); fourkey_combo++; break; 79 | case 3: song.bad_tot++; FourKeyChangeStatus(3); fourkey_combo = 0; break; 80 | } 81 | } 82 | 83 | return CallNextHookEx(NULL, nCode, wParam, lParam); 84 | } 85 | 86 | void FourKeyCheckKeys() 87 | { 88 | // 安装钩子 89 | fourkey_keyboardHook = SetWindowsHookEx( 90 | WH_KEYBOARD_LL, // 钩子类型,WH_KEYBOARD_LL 为键盘钩子 91 | FourKey_LowLevelKeyboardProc, // 指向钩子函数的指针 92 | GetModuleHandleA(NULL), // Dll 句柄 93 | (DWORD)NULL 94 | ); 95 | 96 | if (fourkey_keyboardHook == 0) // 挂钩失败 97 | { 98 | fourkey_quit_flag = true;// make md_printscreen quit 99 | puts("fuck hook"); 100 | return ; 101 | } 102 | 103 | MSG msg; 104 | while(!song.isEnd()) 105 | { 106 | bool tmp = song.run(); 107 | if(tmp) FourKeyChangeStatus(0),fourkey_combo = 0; 108 | if(fourkey_quit_flag) break; 109 | 110 | // 如果消息队列中有消息 111 | if (PeekMessageA( 112 | &msg, // MSG 接收这个消息 113 | NULL, // 检测消息的窗口句柄,NULL:检索当前线程所有窗口消息 114 | (UINT)NULL, // 检查消息范围中第一个消息的值,NULL:检查所有消息(必须和下面的同时为NULL) 115 | (UINT)NULL, // 检查消息范围中最后一个消息的值,NULL:检查所有消息(必须和上面的同时为NULL) 116 | PM_REMOVE // 处理消息的方式,PM_REMOVE:处理后将消息从队列中删除 117 | )) 118 | { 119 | TranslateMessage(&msg); // 把按键消息传递给字符消息 120 | DispatchMessageW(&msg); // 将消息分派给窗口程序 121 | } 122 | else 123 | Sleep(0); //避免CPU全负载运行 124 | } 125 | UnhookWindowsHookEx(fourkey_keyboardHook); // 删除钩子 126 | } 127 | 128 | /** 129 | * @brief 屏幕打印线程 130 | */ 131 | void FourkeyPrintScreen() 132 | { 133 | static char output[20][40]; 134 | static char buf[1000]; 135 | static vector note; 136 | 137 | while(!song.isEnd()) 138 | { 139 | if(fourkey_quit_flag) return ; 140 | con << "Perfect\tGood\tBad\tMiss" << endl; 141 | con << (int)song.perfect_tot << "\t" << (int)song.good_tot << "\t" 142 | << (int)song.bad_tot << "\t" << (int)song.miss_tot << endl; 143 | con << "----------------------------" << endl; 144 | memset(output, 0, sizeof(output)); 145 | for(int i = 1; i <= 4; i++) 146 | { 147 | note=song.getNotes(i); 148 | int st=(i-1)*8; 149 | for(auto v:note) 150 | { 151 | if(v.type==1) 152 | { 153 | int pos = 15-FourKeySpeed * (v.start - NowTime())+2; 154 | if(pos < 0 || pos > 15) continue; 155 | for(int j = 1; j<=4; j++) 156 | output[pos][st+j] = 'x'; 157 | } 158 | else 159 | { 160 | int spos = 15-FourKeySpeed * (v.start - NowTime())+2; 161 | int epos = 15-FourKeySpeed * (v.end - NowTime())+2; 162 | if(spos >= 0 and spos <= 15) 163 | { 164 | for(int j = 1; j<=4; j++) 165 | output[spos][st+j] = '~'; 166 | } 167 | else if(spos>15) spos=16; 168 | if(epos >= 0 and epos <= 15) 169 | { 170 | for(int j = 1; j<=4; j++) 171 | output[epos][st+j] = '~'; 172 | } 173 | else if(epos<=15) epos=-1; 174 | for(int j = epos+1; j < spos; j++) 175 | output[j][st+1]='|',output[j][st+4]='|'; 176 | } 177 | } 178 | } 179 | for(int i = 1; i <= 15; i++) 180 | for(int j = 1; j <= 30; j++) 181 | if(!output[i][j]) output[i][j] = ' '; 182 | 183 | memset(buf, 0, sizeof(buf)); 184 | for(int i = 1; i <= 15; i++) 185 | { 186 | output[i][30] = '\n'; 187 | strcat(buf+1, output[i]+1); 188 | } 189 | con << (buf+1); 190 | con << "====----====----====----====" << endl; 191 | if(fourkey_status != -1 && NowTime() - fourkey_status_start <= Status_Time) 192 | { 193 | if(fourkey_status == 0) con << "Miss "; 194 | if(fourkey_status == 1) con << "Perfect"; 195 | else if(fourkey_status == 2) con << "Good "; 196 | else if(fourkey_status == 3) con << "Bad "; 197 | } 198 | else con << " "; 199 | pair MS = GetMinuteSecond(song.GetAllTime()-NowTime()); 200 | con << setw(6) << MS.first << ":" << MS.second; 201 | if(fourkey_combo >= 5) con << setw(11) << "Combo: " << fourkey_combo; 202 | con.update(); 203 | this_thread::sleep_for(chrono::milliseconds(20)); 204 | } 205 | 206 | } 207 | 208 | /** 209 | * @brief 更改实时响应状态 210 | */ 211 | void FourKeyChangeStatus(int status){ 212 | 213 | fourkey_status = status; 214 | fourkey_status_start = NowTime(); 215 | 216 | } 217 | 218 | /** 219 | * @brief To make the Main() function more clear. 220 | * @return 是否成功读取谱面 221 | */ 222 | bool FourKeyPrework(){ 223 | 224 | FILE* fr = Music.ChooseMusic(2); 225 | if(!song.LoadSpectrum(fr)) 226 | { 227 | Sleep(OneSecond); 228 | return false; 229 | } 230 | song.MakeHolds(1); 231 | 232 | fourkey_combo = 0; 233 | fourkey_quit_flag = false; 234 | FourKeyChangeStatus(-1); 235 | return true; 236 | 237 | } 238 | 239 | void FourKeyEndwork(){ 240 | 241 | ClearScreen(); 242 | cout << "Perfect\tGood\tBad\tMiss" << endl; 243 | cout << (int)song.perfect_tot << "\t" << (int)song.good_tot << "\t" 244 | << (int)song.bad_tot << "\t" << (int)song.miss_tot << endl; 245 | cout << "----------------------------" << endl; 246 | cout << "Ended" <