354 Commits

Author SHA1 Message Date
EillesWan
fc138f1dbf 新的转换算法测试,更好的声音适配 2024-01-08 00:08:52 +08:00
EillesWan
0b0328bc44 更新乐器对照表 2023-10-22 12:37:53 +08:00
EillesWan
72dfdfeb34 改进Channel的应用,减少内存占用,优化结构生成,提高MIDI兼容,修改API结构 2023-10-02 18:24:09 +08:00
EillesWan
9a580132e5 新增midi对应钢琴键盘的音符表 2023-09-30 02:27:51 +08:00
EillesWan
29380d4151 优化生成结构 2023-09-24 12:44:00 +08:00
EillesWan
76eff25a1d 文档全面升级,部分架构更新 2023-09-24 00:38:31 +08:00
EillesWan
7b60d3f9ea 新增红石包子 2023-09-10 19:26:42 +08:00
EillesWan
483e45dcc2 成了吗?如成! 2023-09-10 19:13:39 +08:00
EillesWan
627a26f64a 支持mcstructure结构导出旧版指令 2023-09-10 11:43:39 +08:00
EillesWan
357cb18c5b 提交文档,增加作者署名 2023-08-12 15:10:42 +08:00
EillesWan
83c9750db3 重大更新:优化算法+红石指令音乐生成 2023-08-12 12:39:30 +08:00
EillesWan
5c86a28b44 okay 2023-07-31 23:25:45 +09:00
EillesWan
70674ec6f7 增加对于不正规Midi兼容性的支持,避免转换错误的情况发生。 2023-07-31 22:28:14 +09:00
EillesWan
f73c1be944 更高效的算法管理与兼容性和代码格式更新,详见样例代码;同时新增实验算法,在其中尝试下次更新的内容 2023-07-01 19:11:10 +08:00
EillesWan
36f8db722b Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2023-06-11 13:10:42 +08:00
EillesWan
bedb3924c6 新增一个感谢人 2023-06-11 13:10:39 +08:00
EillesWan
81ae7ae146 修复一些小问题 2023-06-11 13:09:11 +08:00
bgArray
c39c6c4488 Remove feudal superstition 2023-06-04 21:33:20 +08:00
EillesWan
556ce74cfb 修复打击乐器播放时的错误,即将步入红石音乐生成阶段 2023-06-04 17:01:59 +08:00
EillesWan
3da472052e 代码架构升级,功能分割,减少强制性依赖,API全新换代,文档全面更新,新一代音·创,音·创1.0.0 2023-05-27 18:41:02 +08:00
EillesWan
ffe5837c9f 不要更新!不要更新!命运的齿轮尚未开始转动,前方的道路将会铺满沙尘。 2023-05-07 19:25:51 +08:00
EillesWan
42e2fcdd98 要更新,要更新 2023-05-01 19:34:22 +08:00
EillesWan
755de846c7 小改example 2023-05-01 13:58:11 +08:00
EillesWan
cb95c51a47 修复重大问题:延迟播放器中音符的延迟过长,感谢Mono 2023-05-01 12:40:40 +08:00
bgArray
6fe8e41dfa update 2023/4/30 pypi update 2023-04-30 22:45:03 +08:00
EillesWan
b8bae7b913 你觉得我为什么还在用3.8 2023-04-30 22:05:43 +08:00
EillesWan
7663bf8838 Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2023-04-30 21:57:40 +08:00
EillesWan
8aa503710d 紧急更新,修复安卓无法使用的小bug 2023-04-30 21:57:14 +08:00
bgArray
d5d7230537 update 2023/4/29 pypi update 2023-04-29 22:04:41 +08:00
EillesWan
5d48fcd96a Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2023-04-29 21:32:16 +08:00
EillesWan
aa94e3758b 更新一下版本 2023-04-29 21:32:09 +08:00
mingfengpigeon
8b06078a90 Format. 2023-04-29 21:28:36 +08:00
EillesWan
799b9b664d Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2023-04-29 17:00:36 +08:00
EillesWan
4874ace92d 史诗级更新!支持导出mcstructure库,更改部分API和文档,修改了部分代码格式,新增红乐测试 2023-04-29 17:00:32 +08:00
bgArray
54cd6b196e update 2023/4/19 pypi update2 2023-04-19 21:45:43 +08:00
bgArray
882fa4175e Merge remote-tracking branch 'origin/master' 2023-04-19 21:18:04 +08:00
bgArray
b15c89b6c8 update 2023/4/19 pypi update & v0.4.0 update 2023-04-19 21:17:51 +08:00
EillesWan
b758a2f967 更改依赖版本 2023-04-16 19:14:13 +08:00
EillesWan
a9ec7582a9 更新版本 2023-04-16 19:12:10 +08:00
EillesWan
48f5a975db 革命尚未成功?也许是库的问题,但是没时间检查了,先走了((( 2023-04-16 19:11:55 +08:00
EillesWan
acb347b491 新增一个依赖,减少一个文档,把文档移动到了MCS库里 2023-03-26 19:18:00 +08:00
EillesWan
6ca8820fa8 改个东西 2023-03-12 17:57:40 +08:00
EillesWan
fb4cd07dc6 新增MCSTRUCTURE结构文档 2023-03-12 17:43:52 +08:00
EillesWan
3db35b24ab Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2023-03-12 14:35:26 +08:00
EillesWan
a07f723d7c 这改了不啦,一代一代更新有个鬼用,什么都没变嘛;我带的是什么队啊,我带的是睿穆啊,先把这个理念搞懂嘛 2023-03-12 14:35:23 +08:00
EillesWan
7cc70d6948 修改部分文档 2023-03-11 18:23:40 +08:00
EillesWan
b1fb82712d Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2023-03-05 18:58:08 +08:00
EillesWan
bd329082c2 这不是最终版本,请不要提交更新 2023-03-05 18:58:05 +08:00
Eilles Wan
16be83ea29 update README.md.
Signed-off-by: Eilles Wan <w-yi_doctoryi@outlook.com>
2023-02-19 02:25:00 +00:00
EillesWan
5ffb654ec9 修复method3的时长bug 2023-02-12 17:34:03 +08:00
bgArray
7f5efea038 update 2023/2/12 bug fixed 2023-02-12 16:25:52 +08:00
EillesWan
5efabd5463 更新版本号 2023-02-12 16:11:21 +08:00
EillesWan
24fd0121a3 更新文档与依赖描述 2023-02-12 16:08:54 +08:00
EillesWan
a65be910a5 Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2023-02-12 15:55:24 +08:00
EillesWan
15cd0ad777 little update 2023-02-12 15:55:09 +08:00
bgArray
4904f612e1 update 2023/2/5 小改 2023-02-05 17:56:06 +08:00
bgArray
27c6556693 update 2023/2/5 小改 2023-02-05 17:52:20 +08:00
EillesWan
26fbd346d8 新增第三种算法,解决通道与音轨的乐器问题;修复to_DICT中的音符时间问题 2023-02-05 16:16:12 +08:00
EillesWan
c419adc274 Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2023-02-01 22:40:36 +08:00
EillesWan
f8926d2f4f 删除一些没用的文件 2023-02-01 22:40:24 +08:00
bgArray
4268139313 Merge remote-tracking branch 'origin/master' 2023-02-01 22:35:21 +08:00
bgArray
8b8334202c 2023/2/1 update pypi update 2023-02-01 22:35:09 +08:00
EillesWan
1cfbfe1ba1 新增安装教程 2023-02-01 22:31:21 +08:00
EillesWan
3669b5f804 Merge branch 'pkgver' of https://gitee.com/TriM-Organization/Musicreater 2023-02-01 22:10:35 +08:00
EillesWan
7146d17051 删除不必要的文件 2023-02-01 21:51:31 +08:00
EillesWan
8a21b6c3e7 同步仓库描述,准备合并至主版本 2023-02-01 21:48:12 +08:00
EillesWan
5cce59135a 修bug,准备合并 2023-02-01 19:19:40 +08:00
EillesWan
53fda9c986 修复部分bug,准备合并 2023-02-01 18:48:30 +08:00
EillesWan
75f94dab4c 准备与pkgver合并 2023-02-01 18:24:44 +08:00
bgArray
6aa557dcd2 2023/2/1 update fug fixed 2.1 2023-02-01 11:59:14 +08:00
bgArray
215bcafb64 2023/2/1 update fug fixed 2.0 2023-02-01 10:55:07 +08:00
bgArray
f568803b0d 2023/2/1 update fug fixed 2023-02-01 10:09:16 +08:00
EillesWan
a8c0c9e1d5 bug fixed 2023-01-31 21:06:22 +08:00
EillesWan
4d7a1b614a 修复部分bug again 2023-01-31 20:49:53 +08:00
EillesWan
975dbd71e3 新增一点教程 2023-01-31 20:47:09 +08:00
EillesWan
8722d4fd83 修复部分bug 2023-01-31 20:39:50 +08:00
EillesWan
8b9d9a78a3 bug fixed 2023-01-29 13:02:43 +08:00
bgArray
032bbb81c4 update 2023/1/28 formatting and blacking codes 2023-01-28 11:56:09 +08:00
bgArray
49c4cfcbe2 update 2023/1/27 formatting and fixing bugs 2023-01-27 23:25:28 +08:00
EillesWan
352807cba0 小bug能奈我何? 2023-01-27 22:16:15 +08:00
EillesWan
8e938b4f84 Merge branch 'pkgver' of https://gitee.com/TriM-Organization/Musicreater into pkgver 2023-01-27 02:19:56 +08:00
EillesWan
bf0ef8ebb2 新增 延迟播放器算法2、修复进度条无法显示播放百分比、播放时最大积分错误的bug 2023-01-27 02:19:53 +08:00
bgArray
c4664f0e0c add .gitattributes.
Signed-off-by: bgArray <474037765@qq.com>
2023-01-26 02:05:22 +00:00
EillesWan
65a21dcfa7 Bug Fixed 2023-01-24 17:28:05 +08:00
EillesWan
032e2f0f0a 更新部分文档,以及英文语言支持 2023-01-24 16:24:02 +08:00
EillesWan
87e80406a3 移除部分内容 2023-01-23 01:35:56 +08:00
EillesWan
41a6a84342 新增言论,修改其余部分的协议声明 2023-01-23 01:30:44 +08:00
EillesWan
4b9550db21 bug不过年 2023-01-22 00:04:09 +08:00
EillesWan
3d66c06e6d 修复一个小问题 2023-01-21 19:39:14 +08:00
EillesWan
9872a69a66 同时修复bug 2023-01-21 04:12:31 +08:00
EillesWan
0ce72fc4e3 避免GPL传染,我fo了 2023-01-21 04:11:53 +08:00
EillesWan
e0557da3cf 今年就这样罢,我摆了,紧急更新,修复一点点小bug 2023-01-20 22:26:04 +08:00
EillesWan
7d6faebc5b 增加调试功能,降低容错率(不是,我真的没有反向更新(((
但是修改了算法1,算法1的响度错误解决了。但是我仍然推荐算法2
2023-01-20 21:34:15 +08:00
EillesWan
502f4eb54d 紧急修复bug 2023-01-20 01:33:57 +08:00
EillesWan
290db45c23 更新文档 2023-01-20 01:03:59 +08:00
EillesWan
782395b6c9 除旧迎新,0.2.0大更新! 2023-01-20 00:33:09 +08:00
EillesWan
392e74d8d9 0.2.0前的预备更新 2023-01-20 00:31:45 +08:00
EillesWan
ebfaeafcda 更新新手指南文档 2023-01-07 01:19:13 +08:00
EillesWan
6955bb8eaf 完善文档 2023-01-06 12:32:18 +08:00
EillesWan
582e66cd58 Merge branch 'pkgver' of https://gitee.com/EillesWan/Musicreater into pkgver 2023-01-05 17:30:39 +08:00
EillesWan
d64540bfd0 新增文档,修复小bug,更新版本 2023-01-05 17:30:36 +08:00
bgArray
63499732eb 2023/1/2 update-新手答疑指南.md 2023-01-02 16:40:17 +08:00
EillesWan
be40f8f920 改错版本号了qwq 2023-01-02 14:32:25 +08:00
EillesWan
190ef70fc4 忘了改版本号 2023-01-02 14:31:23 +08:00
EillesWan
a74fd4b4de 更加优秀的打击乐器判断! 2023-01-02 14:28:25 +08:00
EillesWan
13f7c0cda0 修改协议声明并新增名言警句 2023-01-02 00:35:08 +08:00
EillesWan
2ebdf6be24 修改版权声明以及部分教程文档 2023-01-01 03:23:48 +08:00
EillesWan
9ca0788eb2 准备修改协议 2022-12-31 13:21:30 +08:00
EillesWan
fba83ef968 Merge branch 'pkgver' of https://gitee.com/EillesWan/Musicreater into pkgver 2022-12-31 13:20:32 +08:00
EillesWan
68e019412d 没写完,但是新算法 2022-12-31 13:20:30 +08:00
bgArray
bde2b4e378 update msctPkgver/main.py.
Signed-off-by: bgArray <474037765@qq.com>
2022-12-29 11:52:10 +00:00
bgArray
b04c01772f 更新指令格式 2022-12-29 11:09:30 +08:00
Eilles Wan
3cd85897fc update README.md.
Signed-off-by: Eilles Wan <w-yi_doctoryi@outlook.com>
2022-12-10 11:09:21 +00:00
Eilles Wan
00c2dcdeab update README.md.
Signed-off-by: Eilles Wan <w-yi_doctoryi@outlook.com>
2022-12-10 11:08:49 +00:00
EillesWan
88d36f94f0 Merge branch 'pkgver' of https://gitee.com/EillesWan/Musicreater into pkgver 2022-11-27 18:13:05 +08:00
EillesWan
8eda5c4736 别急,没写完 2022-11-27 18:13:01 +08:00
Eilles Wan
37ccbe3dae 没翻译完,差不多就行
没翻译完

Signed-off-by: Eilles Wan <w-yi_doctoryi@outlook.com>
2022-11-25 04:40:51 +00:00
EillesWan
be43e4a92a 修复mcpack制作过程中的问题 2022-11-20 15:28:09 +08:00
EillesWan
5929ae417d 改太快了,出问题了,修复了 2022-11-20 12:26:28 +08:00
EillesWan
517c7db112 好玩~ 2022-11-20 12:16:59 +08:00
Eilles Wan
91c31d4e90 update resources/myWords.txt.
Signed-off-by: Eilles Wan <w-yi_doctoryi@outlook.com>
2022-11-20 04:09:02 +00:00
EillesWan
3ae456c4d1 Merge branch 'pkgver' of https://gitee.com/EillesWan/Musicreater into pkgver 2022-11-20 12:02:45 +08:00
EillesWan
b0af5f4950 紧急修复文件路径问题和进度条问题(没修进度条,但是胜似修了) 2022-11-20 12:02:40 +08:00
Eilles Wan
1ccaa8e9aa update docs/功能使用说明.md.
Signed-off-by: Eilles Wan <w-yi_doctoryi@outlook.com>
2022-11-20 02:59:00 +00:00
EillesWan
8d1e6b2ecf 习语进人 2022-11-06 18:24:12 +08:00
EillesWan
75fabf9c37 行了,重要的不急 2022-10-30 01:58:29 +08:00
EillesWan
95e34f5c7b 不好意思,我急了 2022-10-30 01:40:53 +08:00
EillesWan
5bb0227a7a 没写完,不急 2022-10-30 01:39:20 +08:00
EillesWan
397825b483 更新 言·论 2022-10-29 22:26:52 +08:00
EillesWan
9c9ebe0a08 Bug++ 2022-10-23 02:12:11 +08:00
EillesWan
54d434404c 更新言·论 2022-10-16 23:32:13 +08:00
EillesWan
0b3a19faad 更新安卓端的使用方法说明 2022-10-16 23:16:46 +08:00
EillesWan
6cde168bf3 学校的更改 2022-10-16 21:19:14 +08:00
Eilles Wan
451d1d8e80 update msctPkgver/main.py.
插值OK

Signed-off-by: Eilles Wan <w-yi_doctoryi@outlook.com>
2022-10-07 12:23:02 +00:00
EillesWan
085ab3f4c0 没写完 2022-10-07 19:25:28 +08:00
EillesWan
a16109f29a 搞错了再来一发 2022-10-07 16:52:17 +08:00
EillesWan
813186bef9 快好了,不急不急 2022-10-07 16:49:47 +08:00
EillesWan
3921190832 okk准备睡觉 2022-10-06 00:06:41 +08:00
EillesWan
c32b94592a 更新文档及其翻译……,另外对无法批量转换带进度条的bug做了更正……现在批量可以用进度条了 2022-10-05 23:59:06 +08:00
EillesWan
77b7344277 把教程改了个名 2022-10-05 22:29:07 +08:00
EillesWan
0a17f8b8f8 Merge branch 'pkgver' of https://gitee.com/EillesWan/Musicreater into pkgver 2022-10-05 22:27:55 +08:00
EillesWan
f8d707ac11 由于Merge错误,我得提交未完成的修改 2022-10-05 22:21:13 +08:00
EillesWan
ad7e047abc Merge branch 'pkgver' of https://gitee.com/EillesWan/Musicreater into pkgver 2022-10-05 22:18:15 +08:00
EillesWan
5ea47c54cb 这是学校里做的更新 2022-10-05 22:18:03 +08:00
EillesWan
5956e27d83 Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2022-10-05 01:12:42 +08:00
EillesWan
b73a041883 更新文案 2022-10-05 01:12:37 +08:00
bgArray
36f1831cd1 update README.md.
Signed-off-by: bgArray <474037765@qq.com>
2022-09-25 14:58:11 +00:00
bgArray
b20f350da4 update README.md.
Signed-off-by: bgArray <474037765@qq.com>
2022-09-10 12:58:14 +00:00
Eilles
5aaae83020 呼呼,教程写完啦 2022-09-10 00:02:55 +08:00
Eilles
85e6340570 教程写完了,欧耶 2022-09-10 00:00:58 +08:00
Eilles
662c6506d8 Merge branch 'pkgver' of https://gitee.com/EillesWan/Musicreater into pkgver 2022-09-09 22:58:59 +08:00
Eilles
ad2fcd6f14 更新demo的输入操作,一并增加教程 2022-09-09 22:58:05 +08:00
bgArray
5dde31d081 帮金羿提交,顺便改点小语法 2022-09-09 14:14:51 +08:00
bgArray
2e11a9949b 格式化 2022-09-08 22:36:01 +08:00
EillesWan
0a9c70f97c 更新依赖 2022-08-09 18:45:17 +08:00
EillesWan
814ab2ab0e 更新自动启动工具 2022-08-09 16:07:59 +08:00
EillesWan
3b53846ade ok 2022-08-09 15:13:25 +08:00
EillesWan
96af5183ac 库版 0.0.1 正式推出 2022-08-09 15:10:52 +08:00
EillesWan
4aebf9db16 Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2022-08-09 12:54:25 +08:00
EillesWan
fff689446a 我真的不鸽 2022-08-09 12:54:18 +08:00
bgArray
59fd2645e4 update README.md. 2022-08-08 16:07:19 +00:00
bgArray
33ce7ed8c8 update README.md. 2022-08-08 15:57:07 +00:00
bgArray
14f7d6fd7e update README.md. 2022-08-08 15:52:01 +00:00
bgArray
fb2fd13b21 readme 更新 2022-08-08 23:49:22 +08:00
EillesWan
5025cba356 魔法计划启动! 2022-08-08 22:08:08 +08:00
EillesWan
abb53a7499 没改 2022-07-22 17:06:09 +08:00
EillesWan
73854be7be 格式化一下文档 2022-07-12 11:48:32 +08:00
EillesWan
6f6a77d0b4 Merge branch 'pkgver' of https://gitee.com/EillesWan/Musicreater into pkgver 2022-07-12 11:46:49 +08:00
EillesWan
e0a3399fed 新增更新工具 2022-07-12 11:46:37 +08:00
CaryXiong
2afd6beb3f 更新 2022-06-30 12:14:22 +08:00
bgArray
9517c49ec5 修正库导入BUG 2022-06-28 07:43:15 +08:00
bgArray
887452184b update msctPkgver/load_InstrumentLabel.py. 2022-06-26 12:06:52 +00:00
Eilles Wan
d36134e525 诸葛八卦没改完
我来给他改一下,改完来,这家伙没料到我后面留了一手吧,qwq
2022-06-25 16:17:42 +00:00
bgArray
453ca745af 修改算法,修正bug 2022-06-25 23:20:56 +08:00
EillesWan
7d814bcda2 !!update 2022-06-25 22:21:12 +08:00
EillesWan
50dff06ac2 Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2022-06-25 22:06:24 +08:00
EillesWan
d88e50819d 更了?没更 2022-06-25 22:06:19 +08:00
EillesWan
325bb37210 更了?没更…… 2022-06-25 22:04:01 +08:00
CaryXiong
55b933ac85 更新算法,使之适配多乐器 2022-06-24 13:46:46 +08:00
EillesWan
56fb029e75 新增批量转换新样例 2022-06-19 02:04:17 +08:00
EillesWan
c5a7dfb608 小改动 2022-06-19 01:39:50 +08:00
EillesWan
0a640267ff 修复进度条显示错误 2022-06-19 01:36:52 +08:00
EillesWan
a6d7022f87 修复错误 2022-06-19 01:26:53 +08:00
EillesWan
dcc62ca66d 完成延时播放器 2022-06-19 01:07:07 +08:00
EillesWan
4b53938adf 装逼 2022-06-12 18:38:31 +08:00
EillesWan
8502a02880 小改动 2022-06-12 18:22:07 +08:00
bgArray
80fab8ced0 update resources/msctDevAuthors.txt. 2022-06-12 00:19:36 +00:00
EillesWan
1980aeb8de 浅浅更新一下 2022-06-11 13:53:59 +08:00
CaryXiong
1fde8ca253 小改动 2022-06-11 12:19:15 +08:00
CaryXiong
0ce24bff92 小更新 2022-06-09 16:21:08 +08:00
EillesWan
992572cbf1 更新了一下文件结构 2022-06-08 01:50:14 +08:00
EillesWan
ce1099a246 我再更新一些小东西 2022-06-07 23:49:04 +08:00
EillesWan
2340741bb9 更新便于使用的预览程序 2022-06-07 23:43:14 +08:00
EillesWan
1f94f558c4 Merge 2022-06-07 23:12:42 +08:00
EillesWan
0c50ce1134 Merge 2022-06-07 23:12:06 +08:00
EillesWan
2e0dd06db6 更新延期!详见群内 2022-06-07 23:07:55 +08:00
Eilles Wan
9fd32d499f Update sth. 2022-05-26 18:40:33 +08:00
EillesWan
5c501a3466 更新个屁,Gitee脸都不要了 2022-05-21 00:47:54 +08:00
EillesWan
e872c19676 Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2022-05-21 00:45:17 +08:00
EillesWan
51d248cdc2 更新个屁,Gitee脸都不要了 2022-05-21 00:45:10 +08:00
bgArray
a1dfe65042 add readme2.md. 2022-05-16 13:29:04 +00:00
bgArray
70fc686057 删除文件 readme2.md 2022-05-16 13:27:08 +00:00
bgArray
ba691d177a 删除文件 readme3.md 2022-05-16 13:27:02 +00:00
bgArray
b81860dadd update readme3.md. 2022-05-16 13:26:18 +00:00
bgArray
343a62be47 add readme3.md. 2022-05-16 13:24:32 +00:00
bgArray
ab26c82bf9 add readme2.md. 2022-05-16 13:23:47 +00:00
bgArray
a2ae755e87 删除文件 readme2.md 2022-05-16 13:23:21 +00:00
bgArray
e94af73c4f add readme2.md. 2022-05-16 13:22:00 +00:00
Eilles Wan
bc247e1507 Nothing, at all 2022-05-16 13:10:54 +00:00
Eilles Wan
874cd1aea9 Nothing 2022-05-16 13:09:34 +00:00
bgArray
5a6d874204 删除文件 readme2.md 2022-05-16 13:06:29 +00:00
bgArray
08ffa1a6e2 add readme2.md. 2022-05-16 13:05:51 +00:00
bgArray
16d0ecf009 删除文件 .idea 2022-05-16 13:00:01 +00:00
bgArray
f57f4257fb readme-- 2022-05-16 20:59:36 +08:00
EillesWan
3f0f714a7c Merge branch 'pkgver' of https://gitee.com/EillesWan/Musicreater into pkgver 2022-05-16 16:02:48 +08:00
EillesWan
65c23ca427 没问题 2022-05-16 16:02:43 +08:00
EillesWan
bc99f8f4be 更新requirement.txt 2022-05-11 23:41:08 +08:00
bgArray
3b6cb3865f 教程 2022-05-11 23:38:26 +08:00
EillesWan
64292a21b3 更新一点点 2022-05-11 21:34:16 +08:00
EillesWan
3ba8fdad8c Merge 2022-05-11 15:23:44 +08:00
EillesWan
7357a09e5f 无主要的修改,即将更换窗口库 2022-05-11 15:19:40 +08:00
EillesWan
cae0b7f23d 有bug 2022-05-08 18:33:28 +08:00
EillesWan
e1d4cd9933 更新自动重置积分、进度条的功能 2022-05-08 01:30:30 +08:00
bgArray
3bc19ac396 update README.md. 2022-05-06 11:59:04 +00:00
EillesWan
3499c05eb4 修复x轴不增加的bug 2022-04-29 23:17:14 +08:00
EillesWan
259fb04980 完成了更改,现在支持bdx导出 2022-04-29 18:45:06 +08:00
EillesWan
8ce7a9fa83 update format 2022-04-29 11:34:41 +08:00
EillesWan
affb5b5404 Merge branch 'pkgver' of https://gitee.com/EillesWan/Musicreater into pkgver 2022-04-29 11:33:52 +08:00
EillesWan
1805ab53c0 新增bdx转换功能 2022-04-29 11:29:49 +08:00
EillesWan
f3c5044800 撤销了disp类,但是bug依旧没有变化。 2022-04-24 17:56:57 +08:00
EillesWan
ca2e8c9155 更新。 2022-04-21 23:52:16 +08:00
bgArray
eda3a957cf 修复已知问题 2022-04-12 18:11:57 +08:00
bgArray
4788d8949b 格式化 2022-04-12 14:15:33 +08:00
EillesWan
1715735800 Merge 2022-04-08 01:29:39 +08:00
EillesWan
d22b5b0c42 我不知道这样有没有用,但是这确实是更新了bdx操作,我不知道bug在哪里 2022-04-08 01:26:35 +08:00
Eilles Wan
549dd3dbf9 删除文件 msctplugin 2022-04-07 16:19:48 +00:00
Eilles Wan
a65bfeba20 删除文件 Musicreater.New.py 2022-04-07 16:19:33 +00:00
EillesWan
7edf979aee 修复bdx生成不了的问题 2022-04-07 18:07:32 +08:00
EillesWan
8f6cc04780 重设样例代码 2022-04-07 08:27:43 +08:00
EillesWan
6cb5c9ba89 提交包模式的更新 2022-04-05 23:43:03 +08:00
EillesWan
ae83f9c21e 更新了内部数据格式文档,修复部分bug 2022-04-05 00:55:43 +08:00
EillesWan
dd34ac9998 紧急更新,修复了无法将整首歌转换bdx的bug,其余bug正在发现。 2022-04-03 21:38:17 +08:00
EillesWan
2da06136c5 Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2022-04-03 00:51:39 +08:00
EillesWan
e9f4230a2d 金羿生日快乐~ 2022-04-03 00:51:34 +08:00
bgArray
1b7ab73c02 update README_EN.md. 2022-04-01 07:54:34 +00:00
EillesWan
2624c081bb 生活不易,改README出气 2022-04-01 15:09:03 +08:00
EillesWan
87cc189da8 修正单词拼写错误 2022-04-01 15:07:22 +08:00
Eilles Wan
668bf480b2 重命名 README_en.md 为 README_EN.md 2022-04-01 06:42:30 +00:00
EillesWan
4cc3f2678f 没事?没事就改README! 2022-04-01 14:38:50 +08:00
EillesWan
701e9d5129 Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2022-04-01 14:23:08 +08:00
EillesWan
8dd2694e82 左改改右改改,就是不改代码/qwq 2022-04-01 14:23:03 +08:00
bgArray
ac962f0ab7 update README.md. 2022-04-01 05:50:48 +00:00
EillesWan
b800384547 Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2022-04-01 12:48:41 +08:00
EillesWan
5da1041664 喵喵喵? 2022-04-01 12:48:35 +08:00
Eilles Wan
966ce515c3 update README.md. 2022-04-01 04:22:57 +00:00
EillesWan
349c255f5f LOGO 2022-04-01 11:53:43 +08:00
bgArray
cbb77086d0 update README.md. 2022-04-01 03:17:14 +00:00
bgArray
280e50f4cb update README.md. 2022-04-01 02:55:58 +00:00
bgArray
a2a37be8ef update README.md. 2022-04-01 02:33:10 +00:00
Eilles Wan
7acd6b7e38 update resources/myWords.txt. 移除上一次更改 2022-03-31 01:23:11 +00:00
bgArray
4c95f76cf9 update 道法重大更新 2022-03-30 03:01:08 +00:00
Eilles Wan
8a1a159eb8 update README.md. 2022-03-29 08:51:53 +00:00
Eilles Wan
cb43caef26 update README.md. 2022-03-29 08:51:29 +00:00
Eilles Wan
200167ef84 update README.md. 2022-03-29 08:51:23 +00:00
EillesWan
7e0a127406 Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2022-03-29 16:46:40 +08:00
EillesWan
5e70dd73a1 重大更新,我新增了很多名言名句!!! 2022-03-29 16:46:31 +08:00
bgArray
7229b12e99 删除文件 languages/log 2022-03-28 15:13:37 +00:00
bgArray
074124bc3d 删除文件 .idea 2022-03-28 15:13:09 +00:00
bgArray
91446bfd81 翻译保存器 2022-03-28 23:12:24 +08:00
EillesWan
5011efcdee Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2022-03-28 20:42:12 +08:00
EillesWan
3b4af19379 暂存更改以便pull 2022-03-28 20:40:17 +08:00
EillesWan
d3d89d2567 增强对于Linux的支持 2022-03-28 20:29:36 +08:00
EillesWan
441d1e9982 我把旧的MMFM的日志一起加入进来,作为历史的参考。 2022-03-28 14:04:30 +08:00
EillesWan
77e738cb7e 修改读我档 2022-03-28 12:33:35 +08:00
EillesWan
46b6884e64 Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2022-03-26 21:01:29 +08:00
EillesWan
d0a63d0f8e 为了pull,暂存更改 2022-03-26 21:01:23 +08:00
bgArray
887394570b update README.md. 2022-03-26 12:17:08 +00:00
EillesWan
c62806d470 更新安装指示,完成关于界面的编写,UI进一步制作中 2022-03-26 12:24:00 +08:00
EillesWan
d21c26d632 浅浅更新一下~ 2022-03-26 00:30:12 +08:00
EillesWan
26ec42a71d 慢慢更新。 2022-03-24 01:01:45 +08:00
EillesWan
245e2fa1ec 新增自动安装器 2022-03-21 19:05:27 +08:00
bgArray
e70fc806be update README_en.md. 2022-03-20 03:28:59 +00:00
bgArray
72a3715722 update README.md. 2022-03-20 03:27:06 +00:00
bgArray
fd3d27d596 update README.md. 2022-03-20 03:26:47 +00:00
bgArray
3d283bfded 编辑器 2022-03-20 11:20:59 +08:00
EillesWan
7d9b63b9fe 加flag 2022-03-19 19:59:16 +08:00
EillesWan
82850a3d74 终于……我知道Text组件怎么用了 2022-03-19 11:58:28 +08:00
EillesWan
629cfa402b 语言支持增加 2022-03-17 16:21:17 +08:00
EillesWan
0818957f51 新增语言支持 2022-03-16 16:21:10 +08:00
EillesWan
954a30f722 简单修改国际化支持 2022-03-13 22:29:57 +08:00
EillesWan
96e6bc2f7a update LISENCE and some words 2022-03-13 15:16:23 +08:00
bgArray
ca0e56e771 update LICENSE. 2022-03-12 10:24:56 +00:00
EillesWan
8fe71dbe0d 重新设计UI,准备进行国际化支持 2022-03-11 23:27:48 +08:00
EillesWan
97f334789e 改改改 2022-03-11 00:13:01 +08:00
EillesWan
d2a6ce2529 完全想好了窗口改怎么做了,正在逐步更新,顺便取代前尘旧物。 2022-03-10 21:42:11 +08:00
EillesWan
066e0b0cac Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2022-03-09 22:11:01 +08:00
EillesWan
052142ac08 正在修改,没改完 2022-03-09 22:10:54 +08:00
EillesWan
59c481f6da Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2022-03-06 23:49:23 +08:00
EillesWan
53c17f0328 没有做任何更改,上传一下做备份。 2022-03-06 23:46:35 +08:00
EillesWan
ed28fc4866 没有做任何更改,上传一下做备份。 2022-03-06 23:40:31 +08:00
EillesWan
320114533d 思想确认,具体请看群内,正在进一步修改UI,以及插件效果。 2022-02-07 18:51:51 +08:00
EillesWan
aa210ac678 代码重构第一步 2022-02-06 18:59:45 +08:00
EillesWan
c4dd7b1ce8 代码正在被重构,请不要更新功能,如果要更新,请推送至OldUI分支 2022-02-02 22:51:26 +08:00
EillesWan
99509be48c 慢慢改bug 2022-02-02 15:09:11 +08:00
EillesWan
073ae827ab Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2022-02-01 21:41:48 +08:00
EillesWan
58b312554d 改动 2022-02-01 21:36:52 +08:00
bgArray
55eeddb108 改bug 2022-02-01 21:35:46 +08:00
EillesWan
9d4a75cd41 先画个饼 2022-02-01 19:12:00 +08:00
EillesWan
882ce96b90 修复了部分midi文件无法读取的错误 2022-02-01 18:20:14 +08:00
bgArray
9093a66639 重传 2022-02-01 14:34:56 +08:00
bgArray
b48a66115a 删除文件 bgArrayLib/resourcesPath.rpposi 2022-02-01 06:32:40 +00:00
bgArray
db20a87ae9 删除文件 logs 2022-02-01 06:32:27 +00:00
bgArray
69eebd25a9 删除文件 log 2022-02-01 06:32:19 +00:00
bgArray
89ef3bcb55 修了一些BUG,但是有些BUG还没有解决 2022-02-01 14:31:44 +08:00
EillesWan
f08a2ff9be 修复了生成指令出错的问题 2022-02-01 01:46:38 +08:00
bgArray
9f8694ef14 新年第一次更新 2022-02-01 00:07:22 +08:00
EillesWan
81272a1166 紧急修改一下刚刚更新造成的bug 2022-01-31 23:07:22 +08:00
EillesWan
a83052bd5d 新增整个音乐生成单个BDX,同时需要合并一下诸葛亮的分支 2022-01-31 23:01:11 +08:00
EillesWan
2fcfdca0a0 修复部分bug,增加部分bug 2022-01-31 13:53:38 +08:00
bgArray
bcaeb129a5 删除文件 .idea 2022-01-30 08:11:02 +00:00
bgArray
36b639f029 ReadMe(en)更新 2022-01-30 16:06:51 +08:00
bgArray
4b241dfc0b 合并 2022-01-29 22:02:42 +08:00
bgArray
255cc84ef3 删除文件 .idea 2022-01-29 08:42:15 +00:00
bgArray
c04b622297 文件函数变更,新功能回增 2022-01-29 16:41:33 +08:00
bgArray
b6a63a794e 删除文件 log 2022-01-28 10:30:35 +00:00
bgArray
7c0dd218b2 删除文件 .idea 2022-01-28 10:30:29 +00:00
bgArray
19bbb92cff 小改动、回档 2022-01-28 18:29:51 +08:00
bgArray
26ee16f071 删除文件 log 2022-01-28 09:40:50 +00:00
bgArray
05eb0c05ec 删除文件 .idea 2022-01-28 09:32:54 +00:00
bgArray
5d2e0582c3 回档奥 2022-01-28 17:28:52 +08:00
bgArray
d3e0f78d2c 基础版本-δ-0.1.0,本地0.5.1 2022-01-28 17:07:29 +08:00
EillesWan
79a2dfb5b1 删掉一些没用的东西,此外用python38编译一遍……然后把 言·论 部分增加了一些……对了!言·论 指的是中间那行很富有哲理的话哦! 2022-01-28 00:48:28 +08:00
EillesWan
bf3367c7e4 Merge branch 'master' of https://gitee.com/EillesWan/Musicreater 2022-01-28 00:43:56 +08:00
EillesWan
c91f5cf2f9 更新 言·论 板块内容。 2022-01-28 00:43:10 +08:00
bgArray
d8ea747ef1 还不能用的新版本 2022-01-27 21:21:25 +08:00
EillesWan
39148a6c76 更新一下我的言论部分 2022-01-25 15:08:38 +08:00
EillesWan
0e3eb394ab 没有任何更改 2022-01-23 22:33:41 +08:00
EillesWan
f0dedabe1a Merge https://gitee.com/EillesWan/Musicreater 2022-01-23 22:02:36 +08:00
EillesWan
dda78ab35f 我也不知道改了什么,理论上什么都没改 2022-01-23 21:56:48 +08:00
Eilles Wan
5b6ff5ee68 none 2022-01-19 22:25:16 +08:00
bgArray
fcb1a25227 修bug 2022-01-19 18:03:52 +08:00
bgArray
81eb8e5376 日志和bug修改 2022-01-19 17:48:47 +08:00
bgArray
7e13b22d46 除个别外语法错误整体修正 2022-01-19 13:32:53 +08:00
EillesWan
07e153f215 感谢,bgArray “诸葛亮与八卦阵”的更改,此外,修了一些东东吧。 2022-01-19 12:31:37 +08:00
bgArray
908f622433 initial commit 2022-01-19 10:46:20 +08:00
EillesWan
5f7ca0859f 更新读我文档 2022-01-15 00:49:15 +08:00
EillesWan
32ae868c34 增强多语言支持 2022-01-15 00:40:17 +08:00
EillesWan
ce441e4905 更改一些东西 2022-01-14 22:49:21 +08:00
Eilles Wan
3dc099af7e 新增英语支持 2022-01-05 22:24:38 +08:00
Eilles Wan
cf7e51595e 新增多语言支持。 2022-01-03 20:45:25 +08:00
EillesWan
737b2d5f07 增加一个文件,等会再处理,先去上课了,作业一个字都没写 2022-01-03 14:55:35 +08:00
EillesWan
143de0bb37 把所有需要翻译的字符串用双引号字符串表示,其余全是单引号字符串 2022-01-03 14:50:14 +08:00
EillesWan
17f9fa7ffc 1.重新采用Tk作为窗口库
2.优化界面布局
3.修改部分按钮无响应的bug
4.新增启动时的Saying
5.删除命令行模式
6.更新README文件,并增添英文README
2022-01-02 21:43:34 +08:00
EillesWan
1bc1c6485b 新增滑块 2022-01-01 20:19:41 +08:00
99 changed files with 6644 additions and 4821 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
*.yaml linguist-language=Python
*.xml linguist-language=Python
*.md linguist-language=Python

189
.gitignore vendored
View File

@@ -1,38 +1,31 @@
# Byte-compiled / optimized / DLL files
# sth. can't open
/msctPkgver/secrets/*.py
/msctPkgver/secrets/*.c
# mystuff
/.vscode
/*.mid
/*.midi
/*.mcpack
/*.bdx
/*.json
/*.mcstructure
.mscbackup
/logs
/languages
/llc_cli.py
/utils
test.py
RES.txt
# Byte-compiled / optimized
__pycache__/
*.py[cod]
*.pyc
*$py.class
# OSX useful to ignore
*.DS_Store
.AppleDouble
.LSOverride
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
@@ -44,23 +37,129 @@ lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# IntelliJ Idea family of suites
.idea
*.iml
## File-based project format:
*.ipr
*.iws
## mpeltonen/sbt-idea plugin
.idea_modules/
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Briefcase build directories
iOS/
macOS/
windows/
android/
linux/
django/
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# Pycharm
/.idea
# log
/.log
# package
.7z

View File

@@ -1,3 +1,30 @@
**注意以下条款或版权声明应当且必须是高于此项目中任何其他声明的**
1. ·创的全部开发者享有其完整版权其开发者可以在任一时刻终止以后音·创源代码开放若经由其开发者授予特殊权利则授权对象可以将源代码进行特定的被特殊授权的操作
2. ·创或其代码允许在 Apache2.0 协议的条款与说明下进行非商业使用
3. 除部分代码特殊声明外·创允许对其或其代码进行商业化使用但是需要经过音·创主要开发者诸葛亮与八卦阵金羿的一致授权同时授权对象在商业化授权的使用过程中必须依照 Apache2.0 协议的条款与说明
4. 若存在对于音·创包含的部分代码的特殊开源声明则此部分代码依照其特定的开源方式授权但若此部分代码经由此部分代码的主要开发者一致特殊授权后商用则授权对象在商用时依照此部分的开发者所准许的方式或条款进行商用
5. Apache2.0 协议的英文原文副本可见下文
> The English Translation of the TERMS AND CONDITIONS above is listed below
>
> This translated version is for reference only and has no legal effect.
>
> The version with legal effect is the Chinese version above.
**Note, The TERMS AND CONDITIONS below should and must be above all others in this project**
1. *Musicreater* is fully copyrighted by all its developers, the developers have the right to make *Musicreater* close sourced at any time. Operations are permitted under specific terms instructed by its developer(s).
2. Non-commercial use of *Musicreater* and(or) its source code is permitted under Apache License 2.0.
3. Commercial use of *Musicreater* is permitted under Apache License 2.0 with the unanimous permission of the steering developers of *Musicreater* (*bgArray*诸葛亮与八卦阵 and *Eilles*金羿).
4. *Musicreater* is open sourced under priority given:
1. License granted by the core developer(s) of a section after negotiation.
2. Explicitly stated license.
3. Apache 2.0 License.
5. A copy of the original Apache Lisence 2.0 can be found below.
```text
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@@ -175,18 +202,8 @@
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2023 TriM-Organization 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & all the developers of Musicreater
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -199,3 +216,4 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```

37
Musicreater/__init__.py Normal file
View File

@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
"""一个简单的我的世界音频转换库
音·创 (Musicreater)
是一款免费开源的针对《我的世界》的midi音乐转换库
Musicreater(音·创)
A free open source library used for convert midi file into formats that is suitable for **Minecraft**.
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
__version__ = "1.6.2"
__vername__ = "新的转换算法测试,更好的声音适配"
__author__ = (
("金羿", "Eilles Wan"),
("诸葛亮与八卦阵", "bgArray"),
("偷吃不是Touch", "Touch"),
("鸣凤鸽子", "MingFengPigeon"),
)
__all__ = [
# 主要类
"MidiConvert",
# 附加类
"SingleNote",
"SingleCommand",
# "TimeStamp", 未来功能
]
from .main import *

427
Musicreater/constants.py Normal file
View File

@@ -0,0 +1,427 @@
# -*- coding: utf-8 -*-
"""
存放常量与数值性内容
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from typing import Dict, List, Tuple
x = "x"
"""
x
"""
y = "y"
"""
y
"""
z = "z"
"""
z
"""
DEFAULT_PROGRESSBAR_STYLE = (
r"%%N [ %%s/%^s %%% __________ %%t|%^t ]",
("§e=§r", "§7=§r"),
)
"""
默认的进度条样式组
"""
MIDI_PITCH_NAME_TABLE: Dict[int, str] = {
0: "C",
1: "C#",
2: "D",
3: "D#",
4: "E",
5: "F",
6: "F#",
7: "G",
8: "G#",
9: "A",
10: "A#",
11: "B",
12: "C",
13: "C#",
14: "D",
15: "D#",
16: "E",
17: "F",
18: "F#",
19: "G",
20: "G#",
21: "A",
22: "A#",
23: "B",
24: "C",
25: "C#",
26: "D",
27: "D#",
28: "E",
29: "F",
30: "F#",
31: "G",
32: "G#",
33: "A",
34: "A#",
35: "B",
36: "C",
37: "C#",
38: "D",
39: "D#",
40: "E",
41: "F",
42: "F#",
43: "G",
44: "G#",
45: "A",
46: "A#",
47: "B",
48: "C",
49: "C#",
50: "D",
51: "D#",
52: "E",
53: "F",
54: "F#",
55: "G",
56: "G#",
57: "A",
58: "A#",
59: "B",
60: "C",
61: "C#",
62: "D",
63: "D#",
64: "E",
65: "F",
66: "F#",
67: "G",
68: "G#",
69: "A",
70: "A#",
71: "B",
72: "C",
73: "C#",
74: "D",
75: "D#",
76: "E",
77: "F",
78: "F#",
79: "G",
80: "G#",
81: "A",
82: "A#",
83: "B",
84: "C",
85: "C#",
86: "D",
87: "D#",
88: "E",
89: "F",
90: "F#",
91: "G",
92: "G#",
93: "A",
94: "A#",
95: "B",
96: "C",
97: "C#",
98: "D",
99: "D#",
100: "E",
101: "F",
102: "F#",
103: "G",
104: "G#",
105: "A",
106: "A#",
107: "B",
108: "C",
109: "C#",
110: "D",
111: "D#",
112: "E",
113: "F",
114: "F#",
115: "G",
116: "G#",
117: "A",
118: "A#",
119: "B",
120: "C",
121: "C#",
122: "D",
123: "D#",
124: "E",
125: "F",
126: "F#",
127: "G",
}
# 以下是由 Touch “偷吃” 带来的高准确率音效对照表
# 包括乐音乐器对照和打击乐器对照
PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
0: ("note.harp", 6),
1: ("note.harp", 6),
2: ("note.pling", 6),
3: ("note.harp", 6),
4: ("note.pling", 6),
5: ("note.pling", 6),
6: ("note.guitar", 7),
7: ("note.guitar", 7),
8: ("note.iron_xylophone", 8),
9: ("note.bell", 4), # 打击乐器无音域
10: ("note.iron_xylophone", 6),
11: ("note.iron_xylophone", 6),
12: ("note.iron_xylophone", 6),
13: ("note.xylophone", 4),
14: ("note.chime", 4),
15: ("note.banjo", 6),
16: ("note.xylophone", 6),
17: ("note.iron_xylophone", 6),
18: ("note.flute", 5),
19: ("note.flute", 5),
20: ("note.flute", 5),
21: ("note.flute", 5),
22: ("note.flute", 5),
23: ("note.flute", 5),
24: ("note.guitar", 7),
25: ("note.guitar", 7),
26: ("note.guitar", 7),
27: ("note.guitar", 7),
28: ("note.guitar", 7),
29: ("note.guitar", 7),
30: ("note.guitar", 7),
31: ("note.bass", 8),
32: ("note.bass", 8),
33: ("note.bass", 8),
34: ("note.bass", 8),
35: ("note.bass", 8),
36: ("note.bass", 8),
37: ("note.bass", 8),
38: ("note.bass", 8),
39: ("note.bass", 8),
40: ("note.flute", 5),
41: ("note.flute", 5),
42: ("note.flute", 5),
43: ("note.bass", 8),
44: ("note.flute", 5),
45: ("note.iron_xylophone", 6),
46: ("note.harp", 6),
47: ("note.snare", 7),
48: ("note.flute", 5),
49: ("note.flute", 5),
50: ("note.flute", 5),
51: ("note.flute", 5),
52: ("note.didgeridoo", 5),
53: ("note.flute", 5), # 合唱“啊”音
54: ("note.flute", 5), # 人声“嘟”音
55: ("mob.zombie.wood", 7), # 合成人声
56: ("note.flute", 5),
57: ("note.flute", 5),
58: ("note.flute", 5),
59: ("note.flute", 5),
60: ("note.flute", 5),
61: ("note.flute", 5),
62: ("note.flute", 5),
63: ("note.flute", 5),
64: ("note.bit", 6),
65: ("note.bit", 6),
66: ("note.bit", 6),
67: ("note.bit", 6),
68: ("note.flute", 5),
69: ("note.bit", 6),
70: ("note.banjo", 6),
71: ("note.flute", 5),
72: ("note.flute", 5),
73: ("note.flute", 5),
74: ("note.flute", 5),
75: ("note.flute", 5),
76: ("note.iron_xylophone", 6),
77: ("note.iron_xylophone", 6),
78: ("note.flute", 5),
79: ("note.flute", 5),
80: ("note.bit", 6),
81: ("note.bit", 6),
82: ("note.flute", 5),
83: ("note.flute", 5),
84: ("note.guitar", 7),
85: ("note.flute", 5),
86: ("note.bass", 8),
87: ("note.bass", 8),
88: ("note.bit", 6),
89: ("note.flute", 5),
90: ("note.bit", 6),
91: ("note.flute", 5),
92: ("note.bell", 4),
93: ("note.guitar", 7),
94: ("note.flute", 5),
95: ("note.bit", 6),
96: ("note.bit", 6), # 雨声
97: ("note.flute", 5),
98: ("note.bell", 4),
99: ("note.bit", 6), # 大气
100: ("note.bit", 6), # 明亮
101: ("note.bit", 6), # 鬼怪
102: ("note.bit", 6), # 回声
103: ("note.bit", 6), # 科幻
104: ("note.iron_xylophone", 6),
105: ("note.banjo", 6),
106: ("note.harp", 6),
107: ("note.harp", 6),
108: ("note.bell", 4),
109: ("note.flute", 5),
110: ("note.flute", 5),
111: ("note.flute", 5),
112: ("note.bell", 4),
113: ("note.xylophone", 4),
114: ("note.flute", 5),
115: ("note.hat", 7), # 打击乐器无音域
116: ("note.snare", 7), # 打击乐器无音域
117: ("note.snare", 7), # 打击乐器无音域
118: ("note.bd", 7), # 打击乐器无音域
119: ("firework.blast", 7), # 打击乐器无音域
120: ("note.guitar", 7), # 吉他还把杂音
121: ("note.harp", 6), # 呼吸声
122: ("note.harp", 6), # 海浪声
123: ("note.harp", 6), # 鸟鸣
124: ("note.bit", 6),
125: ("note.hat", 7), # 直升机
126: ("firework.twinkle", 7), # 打击乐器无音域
127: ("mob.zombie.wood", 7), # 打击乐器无音域
}
PERCUSSION_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
34: ("note.hat", 7),
35: ("note.bd", 7),
36: ("note.bd", 7),
37: ("note.snare", 7),
38: ("note.snare", 7),
39: ("fire.ignite", 7),
40: ("note.snare", 7),
41: ("note.hat", 7),
42: ("note.hat", 7),
43: ("firework.blast", 7),
44: ("note.hat", 7),
45: ("note.snare", 4),
46: ("note.snare", 7),
47: ("note.snare", 7),
48: ("note.bell", 4),
49: ("note.hat", 7),
50: ("note.bell", 4),
51: ("note.bell", 4),
52: ("note.bell", 4),
53: ("note.bell", 4),
54: ("note.bell", 4),
55: ("note.bell", 4),
56: ("note.snare", 7),
57: ("note.hat", 7),
58: ("note.chime", 4),
59: ("note.iron_xylophone", 6),
60: ("note.bd", 7),
61: ("note.bd", 7),
62: ("note.xylophone", 4),
63: ("note.xylophone", 4),
64: ("note.xylophone", 4),
65: ("note.hat", 7),
66: ("note.bell", 4),
67: ("note.bell", 4),
68: ("note.hat", 7),
69: ("note.hat", 7),
70: ("note.flute", 5),
71: ("note.flute", 5),
72: ("note.hat", 7),
73: ("note.hat", 7),
74: ("note.xylophone", 4),
75: ("note.hat", 7),
76: ("note.hat", 7),
77: ("note.xylophone", 4),
78: ("note.xylophone", 4),
79: ("note.bell", 4),
80: ("note.bell", 4),
}
PERCUSSION_INSTRUMENT_LIST: List[str] = [
"note.snare",
"note.bd",
"note.hat",
"note.basedrum",
"firework.blast",
"firework.twinkle",
"fire.ignite",
"mob.zombie.wood",
]
INSTRUMENT_BLOCKS_TABLE: Dict[str, Tuple[str, ...]] = {
"note.bass": ("planks",),
"note.snare": ("sand",),
"note.hat": ("glass",),
"note.bd": ("stone",),
"note.basedrum": ("stone",),
"note.bell": ("gold_block",),
"note.flute": ("clay",),
"note.chime": ("packed_ice",),
"note.guitar": ("wool",),
"note.xylobone": ("bone_block",),
"note.iron_xylophone": ("iron_block",),
"note.cow_bell": ("soul_sand",),
"note.didgeridoo": ("pumpkin",),
"note.bit": ("emerald_block",),
"note.banjo": ("hay_block",),
"note.pling": ("glowstone",),
"note.bassattack": ("command_block",), # 无法找到此音效
"note.harp": ("dirt",),
# 呃……
"firework.blast": ("sandstone",),
"firework.twinkle": ("red_sandstone",),
"fire.ignite": ("concrete_powder",),
"mob.zombie.wood": ("sand",),
}
# 即将启用
height2note = {
0.5: 0,
0.53: 1,
0.56: 2,
0.6: 3,
0.63: 4,
0.67: 5,
0.7: 6,
0.75: 7,
0.8: 8,
0.84: 9,
0.9: 10,
0.94: 11,
1.0: 12,
1.05: 13,
1.12: 14,
1.2: 15,
1.25: 16,
1.33: 17,
1.4: 18,
1.5: 19,
1.6: 20,
1.7: 21,
1.8: 22,
1.9: 23,
2.0: 24,
}
"""音高对照表\n
MC音高:音符盒音调"""

117
Musicreater/exceptions.py Normal file
View File

@@ -0,0 +1,117 @@
# -*- coding: utf-8 -*-
"""
存放一堆报错类型
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
class MSCTBaseException(Exception):
"""音·创库版本的所有错误均继承于此"""
def __init__(self, *args):
"""音·创库版本的所有错误均继承于此"""
super().__init__(*args)
def meow(
self,
):
for i in self.args:
print(i + "喵!")
def crash_it(self):
raise self
class MidiFormatException(MSCTBaseException):
"""音·创库版本的所有MIDI格式错误均继承于此"""
def __init__(self, *args):
"""音·创库版本的所有MIDI格式错误均继承于此"""
super().__init__("MIDI格式错误", *args)
class MidiDestroyedError(MSCTBaseException):
"""Midi文件损坏"""
def __init__(self, *args):
"""Midi文件损坏"""
super().__init__("MIDI文件损坏无法读取MIDI文件", *args)
class MidiUnboundError(MSCTBaseException):
"""未定义Midi对象"""
def __init__(self, *args):
"""未绑定Midi对象"""
super().__init__("未定义MidiFile对象你甚至没有对象就想要生孩子", *args)
class CommandFormatError(RuntimeError):
"""指令格式与目标格式不匹配而引起的错误"""
def __init__(self, *args):
"""指令格式与目标格式不匹配而引起的错误"""
super().__init__("指令格式不匹配", *args)
# class CrossNoteError(MidiFormatException):
# """同通道下同音符交叉出现所产生的错误"""
# def __init__(self, *args):
# """同通道下同音符交叉出现所产生的错误"""
# super().__init__("同通道下同音符交叉", *args)
# 这TM是什么错误
# 我什么时候写的这玩意?
# 我哪知道这说的是啥?
class NotDefineTempoError(MidiFormatException):
"""没有Tempo设定导致时间无法计算的错误"""
def __init__(self, *args):
"""没有Tempo设定导致时间无法计算的错误"""
super().__init__("在曲目开始时没有声明Tempo未指定拍长", *args)
class ChannelOverFlowError(MidiFormatException):
"""一个midi中含有过多的通道"""
def __init__(self, max_channel=16, *args):
"""一个midi中含有过多的通道"""
super().__init__("含有过多的通道(数量应≤{}".format(max_channel), *args)
class NotDefineProgramError(MidiFormatException):
"""没有Program设定导致没有乐器可以选择的错误"""
def __init__(self, *args):
"""没有Program设定导致没有乐器可以选择的错误"""
super().__init__("未指定演奏乐器", *args)
class NoteOnOffMismatchError(MidiFormatException):
"""音符开音和停止不匹配的错误"""
def __init__(self, *args):
"""音符开音和停止不匹配的错误"""
super().__init__("音符不匹配", *args)
class ZeroSpeedError(ZeroDivisionError):
"""以0作为播放速度的错误"""
def __init__(self, *args):
"""以0作为播放速度的错误"""
super().__init__("播放速度为0", *args)

333
Musicreater/experiment.py Normal file
View File

@@ -0,0 +1,333 @@
# -*- coding: utf-8 -*-
"""
新版本功能以及即将启用的函数
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import random
from .constants import INSTRUMENT_BLOCKS_TABLE
from .exceptions import *
from .main import MidiConvert
from .subclass import *
from .utils import *
from .types import Tuple, List, Dict
class FutureMidiConvertRSNB(MidiConvert):
"""
加入红石音乐适配
"""
music_command_list: Dict[int, SingleNoteBox]
"""音乐指令列表"""
@staticmethod
def soundID_to_block(sound_id: str, random_select: bool = False) -> str:
"""
将我的世界乐器名改作音符盒所需的对应方块名称
Parameters
----------
sound_id: str
将我的世界乐器名
random_select: bool
是否随机选取对应方块
Returns
-------
str方块名称
"""
try:
if random_select:
return random.choice(INSTRUMENT_BLOCKS_TABLE[sound_id])
else:
return INSTRUMENT_BLOCKS_TABLE[sound_id][0]
except KeyError:
return "air"
class FutureMidiConvertM4(MidiConvert):
"""
加入插值算法优化音感
: 经测试,生成效果已经达到,感觉良好
"""
# 临时用的插值计算函数
@staticmethod
def _linear_note(
_note: SingleNote,
_apply_time_division: float = 100,
) -> List[SingleNote]:
"""传入音符数据,返回以半秒为分割的插值列表
:param _note: SingleNote 音符
:param _apply_time_division: int 间隔毫秒数
:return list[tuple(int开始时间毫秒, int乐器, int音符, int力度内置, float音量播放),]"""
if _note.percussive:
return [
_note,
]
totalCount = int(_note.duration / _apply_time_division)
if totalCount == 0:
return [
_note,
]
# print(totalCount)
result: List[SingleNote] = []
for _i in range(totalCount):
result.append(
SingleNote(
instrument=_note.inst,
pitch=_note.pitch,
velocity=_note.velocity,
startime=int(_note.start_time + _i * (_note.duration / totalCount)),
lastime=int(_note.duration / totalCount),
track_number=_note.track_no,
is_percussion=_note.percussive,
)
# (
# _note.start_time + _i * _apply_time_division,
# _note.instrument,
# _note.pitch,
# _note.velocity,
# ((totalCount - _i) / totalCount),
# )
)
return result
def to_command_list_in_delay(
self,
max_volume: float = 1.0,
speed: float = 1.0,
player_selector: str = "@a",
) -> Tuple[List[SingleCommand], int, int]:
"""
将midi转换为我的世界命令列表并输出每个音符之后的延迟
Parameters
----------
max_volume: float
最大播放音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
speed: float
速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
player_selector: str
玩家选择器,默认为`@a`
Returns
-------
tuple( list[SingleCommand,...], int音乐时长游戏刻, int最大同时播放的指令数量 )
"""
if speed == 0:
raise ZeroSpeedError("播放速度仅可为(0,1]范围内的正实数")
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
notes_list: List[SingleNote] = []
# 此处 我们把通道视为音轨
for channel in self.to_music_note_channels().values():
for note in channel:
if not note.percussive:
notes_list.extend(self._linear_note(note, note.get_mc_pitch * 500))
else:
notes_list.append(note)
notes_list.sort(key=lambda a: a.start_time)
self.music_command_list = []
multi = max_multi = 0
delaytime_previous = 0
for note in notes_list:
delaytime_now = round(note.start_time / speed / 50)
if (tickdelay := (delaytime_now - delaytime_previous)) == 0:
multi += 1
else:
max_multi = max(max_multi, multi)
multi = 0
self.music_command_list.append(
SingleCommand(
self.execute_cmd_head.format(player_selector)
+ note.to_command(max_volume),
tick_delay=tickdelay,
annotation="{}播放{}%{}".format(
mctick2timestr(delaytime_now),
max_volume * 100,
"{}:{}".format(note.mc_sound_ID, note.mc_pitch),
),
)
)
delaytime_previous = delaytime_now
self.music_tick_num = round(notes_list[-1].start_time / speed / 50)
return self.music_command_list, self.music_tick_num, max_multi + 1
class FutureMidiConvertM5(MidiConvert):
"""
加入同刻偏移算法优化音感
"""
# 神奇的偏移音
def to_command_list_in_delay(
self,
max_volume: float = 1.0,
speed: float = 1.0,
player_selector: str = "@a",
) -> Tuple[List[SingleCommand], int]:
"""
使用金羿的转换思路使用同刻偏移算法优化音感后将midi转换为我的世界命令列表并输出每个音符之后的延迟
Parameters
----------
max_volume: float
最大播放音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
speed: float
速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
player_selector: str
玩家选择器,默认为`@a`
Returns
-------
tuple( list[SingleCommand,...], int音乐时长游戏刻 )
"""
if speed == 0:
raise ZeroSpeedError("播放速度仅可为正实数")
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
self.to_music_channels()
tracks = {}
InstID = -1
# 此处 我们把通道视为音轨
for i in self.channels.keys():
# 如果当前通道为空 则跳过
if not self.channels[i]:
continue
# 第十通道是打击乐通道
SpecialBits = True if i == 9 else False
# nowChannel = []
for track_no, track in self.channels[i].items():
for msg in track:
if msg[0] == "PgmC":
InstID = msg[1]
elif msg[0] == "NoteS":
soundID, _X = (
self.perc_inst_to_soundID_withX(msg[1])
if SpecialBits
else self.inst_to_souldID_withX(InstID)
)
score_now = round(msg[-1] / float(speed) / 50)
# print(score_now)
try:
tracks[score_now].append(
self.execute_cmd_head.format(player_selector)
+ f"playsound {soundID} @s ^ ^ ^{128 / max_volume / msg[2] - 1} {msg[2] / 128} "
+ (
""
if SpecialBits
else f"{2 ** ((msg[1] - 60 - _X) / 12)}"
)
)
except KeyError:
tracks[score_now] = [
self.execute_cmd_head.format(player_selector)
+ f"playsound {soundID} @s ^ ^ ^{128 / max_volume / msg[2] - 1} {msg[2] / 128} "
+ (
""
if SpecialBits
else f"{2 ** ((msg[1] - 60 - _X) / 12)}"
)
]
all_ticks = list(tracks.keys())
all_ticks.sort()
results = []
for i in range(len(all_ticks)):
for j in range(len(tracks[all_ticks[i]])):
results.append(
SingleCommand(
tracks[all_ticks[i]][j],
tick_delay=(
(
0
if (
(all_ticks[i + 1] - all_ticks[i])
/ len(tracks[all_ticks[i]])
< 1
)
else 1
)
if j != 0
else (
(
all_ticks[i]
- all_ticks[i - 1]
- (
0
if (
(all_ticks[i] - all_ticks[i - 1])
/ len(tracks[all_ticks[i - 1]])
< 1
)
else (len(tracks[all_ticks[i - 1]]) - 1)
)
)
if i != 0
else all_ticks[i]
)
),
annotation="{}播放{}%{}".format(
mctick2timestr(
i + 0
if (
(all_ticks[i + 1] - all_ticks[i])
/ len(tracks[all_ticks[i]])
< 1
)
else j
),
max_volume * 100,
"",
),
)
)
self.music_command_list = results
self.music_tick_num = max(all_ticks)
return results, self.music_tick_num
class FutureMidiConvertM6(MidiConvert):
"""
加入插值算法优化音感,但仅用于第一音轨
"""
# TODO 没写完的!!!!

225
Musicreater/magicmain.py Normal file
View File

@@ -0,0 +1,225 @@
# -*- coding: utf-8 -*-
"""
功能测试 若非已知 请勿更改
此文件仅供功能测试,并非实际调用的文件
请注意,此处的文件均为测试使用
不要更改 不要更改 不要更改
请注意这里的一切均需要其原作者更改
这里用于放置一些新奇的点子
用于测试
不要更改 不要更改 不要更改!
"""
# 音·创 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
"""
音·创 (Musicreater)
是一款免费开源的针对《我的世界》的midi音乐转换库
Musicreater (音·创)
A free open source library used for convert midi file into formats that is suitable for **Minecraft**.
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 ../License.md
Terms & Conditions: ../License.md
"""
# ============================
import mido
class NoteMessage:
def __init__(self, channel, pitch, velocity, startT, lastT, midi, now_bpm, change_bpm=None):
self.channel = channel
self.note = pitch
self.velocity = velocity
self.startTime = startT
self.lastTime = lastT
self.tempo = now_bpm # 这里要程序实现获取bpm可以参考我的程序
def mt2gt(mt, tpb_a, bpm_a):
return mt / tpb_a / bpm_a * 60
self.startTrueTime = mt2gt(self.startTime, midi.ticks_per_beat, self.tempo) # / 20
# delete_extra_zero(round_up())
if change_bpm is not None:
self.lastTrueTime = mt2gt(self.lastTime, midi.ticks_per_beat, change_bpm) # / 20
else:
self.lastTrueTime = mt2gt(self.lastTime, midi.ticks_per_beat, self.tempo) # / 20
# delete_extra_zero(round_up())
print((self.startTime * self.tempo) / (midi.ticks_per_beat * 50000))
def __str__(self):
return "noteMessage channel=" + str(self.channel) + " note=" + str(self.note) + " velocity=" + \
str(self.velocity) + " startTime=" + str(self.startTime) + " lastTime=" + str(self.lastTime) + \
" startTrueTime=" + str(self.startTrueTime) + " lastTrueTime=" + str(self.lastTrueTime)
def load(mid: mido.MidiFile):
type_ = [False, False, False] # note_off / note_on+0 / mixed
is_tempo = False
# 预检
for i, track in enumerate(mid.tracks):
for msg in track:
# print(msg)
if msg.is_meta is not True:
if msg.type == 'note_on' and msg.velocity == 0:
type_[1] = True
elif msg.type == "note_off":
type_[0] = True
if msg.is_meta is True and msg.type == "set_tempo":
is_tempo = True
if is_tempo is not True:
raise Exception("这个mid没有可供计算时间的tempo事件")
if type_[0] is True and type_[1] is True:
type_[2] = True
type_[1] = False
type_[0] = False
print(type_)
bpm = 0
recent_change_bpm = 0
is_change_bpm = False
# 实检
for i, track in enumerate(mid.tracks):
noteOn = []
trackS = []
ticks = 0
for msg in track:
print(msg)
ticks += msg.time
print(ticks)
if msg.is_meta is True and msg.type == "set_tempo":
recent_change_bpm = bpm
bpm = 60000000 / msg.tempo
is_change_bpm = True
if msg.type == 'note_on' and msg.velocity != 0:
noteOn.append([msg, msg.note, ticks])
if type_[1] is True:
if msg.type == 'note_on' and msg.velocity == 0:
for u in noteOn:
index = 0
if u[1] == msg.note:
lastMessage = u[0]
lastTick = u[2]
break
index += 1
print(lastTick)
if is_change_bpm and recent_change_bpm != 0:
trackS.append(NoteMessage(msg.channel, msg.note, lastMessage.velocity, lastTick, ticks - lastTick,
mid, recent_change_bpm, bpm))
is_change_bpm = False
else:
trackS.append(
NoteMessage(msg.channel, msg.note, lastMessage.velocity, lastTick, ticks - lastTick,
mid, bpm))
# print(noteOn)
# print(index)
try:
noteOn.pop(index)
except IndexError:
noteOn.pop(index - 1)
print(trackS)
for j in trackS:
print(j)
if __name__ == '__main__':
load(mido.MidiFile("test.mid"))
# ============================
from typing import Literal
from ..constants import x,y,z
# 不要用 没写完
def delay_to_note_blocks(
baseblock: str = "stone",
position_forward: Literal['x','y','z'] = z,
):
"""传入音符,生成以音符盒存储的红石音乐
:param:
baseblock: 中继器的下垫方块
position_forward: 结构延长方向
:return 是否生成成功
"""
from TrimMCStruct import Structure, Block
struct = Structure(
(_sideLength, max_height, _sideLength), # 声明结构大小
)
log = print
startpos = [0,0,0]
# 1拍 x 2.5 rt
for i in notes:
error = True
try:
struct.set_block(
[startpos[0], startpos[1] + 1, startpos[2]],
form_note_block_in_NBT_struct(height2note[i[0]], instrument),
)
struct.set_block(startpos, Block("universal_minecraft", instuments[i[0]][1]),)
error = False
except ValueError:
log("无法放置音符:" + str(i) + "" + str(startpos))
struct.set_block(Block("universal_minecraft", baseblock), startpos)
struct.set_block(
Block("universal_minecraft", baseblock),
[startpos[0], startpos[1] + 1, startpos[2]],
)
finally:
if error is True:
log("无法放置音符:" + str(i) + "" + str(startpos))
struct.set_block(Block("universal_minecraft", baseblock), startpos)
struct.set_block(
Block("universal_minecraft", baseblock),
[startpos[0], startpos[1] + 1, startpos[2]],
)
delay = int(i[1] * speed + 0.5)
if delay <= 4:
startpos[0] += 1
struct.set_block(
form_repeater_in_NBT_struct(delay, "west"),
[startpos[0], startpos[1] + 1, startpos[2]],
)
struct.set_block(Block("universal_minecraft", baseblock), startpos)
else:
for j in range(int(delay / 4)):
startpos[0] += 1
struct.set_block(
form_repeater_in_NBT_struct(4, "west"),
[startpos[0], startpos[1] + 1, startpos[2]],
)
struct.set_block(Block("universal_minecraft", baseblock), startpos)
if delay % 4 != 0:
startpos[0] += 1
struct.set_block(
form_repeater_in_NBT_struct(delay % 4, "west"),
[startpos[0], startpos[1] + 1, startpos[2]],
)
struct.set_block(Block("universal_minecraft", baseblock), startpos)
startpos[0] += posadder[0]
startpos[1] += posadder[1]
startpos[2] += posadder[2]

728
Musicreater/main.py Normal file
View File

@@ -0,0 +1,728 @@
# -*- coding: utf-8 -*-
"""
音·创 (Musicreater)
是一款免费开源的针对《我的世界》的midi音乐转换库
Musicreater (音·创)
A free open source library used for convert midi file into formats that is suitable for **Minecraft**.
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 音·创 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库根目录下的 License.md
# BUG退散BUG退散 BUG退散BUG退散
# 异常、错误作乱之时 異常、誤りが、困った時は
# 二六字组!万国码合!二六字组!万国码合! グループ!コード#!グループ!コード#!
# 赶快呼叫 程序员Let's Go 直ぐに呼びましょプログラマ レッツゴー!
import math
import os
from .constants import *
from .exceptions import *
from .subclass import *
from .types import *
from .utils import *
"""
学习笔记:
tempo: microseconds per quarter note 毫秒每四分音符,换句话说就是一拍占多少毫秒
tick: midi帧
ticks_per_beat: 帧每拍,即一拍多少帧
那么:
tick / ticks_per_beat => amount_of_beats 拍数(四分音符数)
tempo * amount_of_beats => 毫秒数
所以:
tempo * tick / ticks_per_beat => 毫秒数
###########
seconds per tick:
(tempo / 1000000.0) / ticks_per_beat
seconds:
tick * tempo / 1000000.0 / ticks_per_beat
microseconds:
tick * tempo / 1000.0 / ticks_per_beat
gameticks:
tick * tempo / 1000000.0 / ticks_per_beat * 一秒多少游戏刻
"""
@dataclass(init=False)
class MidiConvert:
"""
将Midi文件转换为我的世界内容
"""
midi: VoidMido
"""MidiFile对象"""
midi_music_name: str
"""Midi乐曲名"""
enable_old_exe_format: bool
"""是否启用旧版execute指令格式"""
execute_cmd_head: str
"""execute指令头部"""
channels: Union[ChannelType, NoteChannelType]
"""频道信息字典"""
music_command_list: List[SingleCommand]
"""音乐指令列表"""
music_tick_num: int
"""音乐总延迟"""
progress_bar_command: List[SingleCommand]
"""进度条指令列表"""
def __init__(
self,
midi_obj: VoidMido,
midi_name: str,
enable_old_exe_format: bool = False,
):
"""
简单的midi转换类将midi对象转换为我的世界结构或者包
Parameters
----------
midi_obj: mido.MidiFile 对象
需要处理的midi对象
midi_name: MIDI乐曲名称
此音乐之名
enable_old_exe_format: bool
是否启用旧版(≤1.19)指令格式,默认为否
"""
self.midi: VoidMido = midi_obj
self.midi_music_name: str = midi_name
self.enable_old_exe_format: bool = enable_old_exe_format
self.execute_cmd_head = (
"execute {} ~ ~ ~ "
if enable_old_exe_format
else "execute as {} at @s positioned ~ ~ ~ run "
)
self.progress_bar_command = self.music_command_list = []
self.channels = {}
self.music_tick_num = 0
@classmethod
def from_midi_file(
cls,
midi_file_path: str,
old_exe_format: bool = False,
):
"""
直接输入文件地址将midi文件读入
Parameters
----------
midi_file: str
midi文件地址
enable_old_exe_format: bool
是否启用旧版(≤1.19)指令格式,默认为否
"""
midi_music_name = os.path.splitext(os.path.basename(midi_file_path))[0].replace(
" ", "_"
)
"""文件名,不含路径且不含后缀"""
try:
return cls(
mido.MidiFile(midi_file_path, clip=True),
midi_music_name,
old_exe_format,
)
except (ValueError, TypeError) as E:
raise MidiDestroyedError(f"文件{midi_file_path}损坏:{E}")
except FileNotFoundError as E:
raise FileNotFoundError(f"文件{midi_file_path}不存在:{E}")
# ……真的那么重要吗
# 我又几曾何时,知道祂真的会抛下我
def form_progress_bar(
self,
max_score: int,
scoreboard_name: str,
progressbar_style: tuple = DEFAULT_PROGRESSBAR_STYLE,
) -> List[SingleCommand]:
"""
生成进度条
Parameters
----------
maxscore: int
midi的乐器ID
scoreboard_name: str
所使用的计分板名称
progressbar_style: tuple
此参数详见 ../docs/库的生成与功能文档.md#进度条自定义
Returns
-------
list[SingleCommand,]
"""
pgs_style = progressbar_style[0]
"""用于被替换的进度条原始样式"""
"""
| 标识符 | 指定的可变量 |
|---------|----------------|
| `%%N` | 乐曲名(即传入的文件名)|
| `%%s` | 当前计分板值 |
| `%^s` | 计分板最大值 |
| `%%t` | 当前播放时间 |
| `%^t` | 曲目总时长 |
| `%%%` | 当前进度比率 |
| `_` | 用以表示进度条占位|
"""
perEach = max_score / pgs_style.count("_")
"""每个进度条代表的分值"""
result: List[SingleCommand] = []
if r"%^s" in pgs_style:
pgs_style = pgs_style.replace(r"%^s", str(max_score))
if r"%^t" in pgs_style:
pgs_style = pgs_style.replace(r"%^t", mctick2timestr(max_score))
sbn_pc = scoreboard_name[:2]
if r"%%%" in pgs_style:
result.append(
SingleCommand(
'scoreboard objectives add {}PercT dummy "百分比计算"'.format(sbn_pc),
annotation="新增临时计算用计分板(百分比)",
)
)
result.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores={" + scoreboard_name + "=1..}]"
)
+ "scoreboard players set MaxScore {} {}".format(
scoreboard_name, max_score
),
annotation="设定此音乐最大计分",
)
)
result.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores={" + scoreboard_name + "=1..}]"
)
+ "scoreboard players set n100 {} 100".format(scoreboard_name),
annotation="设置常量100",
)
)
result.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores={" + scoreboard_name + "=1..}]"
)
+ "scoreboard players operation @s {} = @s {}".format(
sbn_pc + "PercT", scoreboard_name
),
annotation="为临时变量赋值",
)
)
result.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores={" + scoreboard_name + "=1..}]"
)
+ "scoreboard players operation @s {} *= n100 {}".format(
sbn_pc + "PercT", scoreboard_name
),
annotation="改变临时变量的单位为百分比(扩大精度)",
)
)
result.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores={" + scoreboard_name + "=1..}]"
)
+ "scoreboard players operation @s {} /= MaxScore {}".format(
sbn_pc + "PercT", scoreboard_name
),
annotation="使用临时变量计算百分比",
)
)
# 那是假的
# 一切都并未留下痕迹啊
# 那梦又是多么的真实……
if r"%%t" in pgs_style:
result.append(
SingleCommand(
'scoreboard objectives add {}TMinT dummy "时间计算:分"'.format(sbn_pc),
annotation="新增临时计算计分板(分)",
)
)
result.append(
SingleCommand(
'scoreboard objectives add {}TSecT dummy "时间计算:秒"'.format(sbn_pc),
annotation="新增临时计算计分板(秒)",
)
)
result.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores={" + scoreboard_name + "=1..}]"
)
+ "scoreboard players set n20 {} 20".format(scoreboard_name),
annotation="设置常量20",
)
)
result.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores={" + scoreboard_name + "=1..}]"
)
+ "scoreboard players set n60 {} 60".format(scoreboard_name),
annotation="设置常量60",
)
)
result.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores={" + scoreboard_name + "=1..}]"
)
+ "scoreboard players operation @s {} = @s {}".format(
sbn_pc + "TMinT", scoreboard_name
),
annotation="为临时变量(分)赋值",
)
)
result.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores={" + scoreboard_name + "=1..}]"
)
+ "scoreboard players operation @s {} /= n20 {}".format(
sbn_pc + "TMinT", scoreboard_name
),
annotation="将临时变量转换单位为秒(缩减精度)",
)
)
result.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores={" + scoreboard_name + "=1..}]"
)
+ "scoreboard players operation @s {} = @s {}".format(
sbn_pc + "TSecT", sbn_pc + "TMinT"
),
annotation="为临时变量(秒)赋值",
)
)
result.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores={" + scoreboard_name + "=1..}]"
)
+ "scoreboard players operation @s {} /= n60 {}".format(
sbn_pc + "TMinT", scoreboard_name
),
annotation="将临时变量(分)转换单位为分(缩减精度)",
)
)
result.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores={" + scoreboard_name + "=1..}]"
)
+ "scoreboard players operation @s {} %= n60 {}".format(
sbn_pc + "TSecT", scoreboard_name
),
annotation="将临时变量(秒)确定下来(框定精度区间)",
)
)
for i in range(pgs_style.count("_")):
npg_stl = (
pgs_style.replace("_", progressbar_style[1][0], i + 1)
.replace("_", progressbar_style[1][1])
.replace(r"%%N", self.midi_music_name)
if r"%%N" in pgs_style
else pgs_style.replace("_", progressbar_style[1][0], i + 1).replace(
"_", progressbar_style[1][1]
)
)
if r"%%s" in npg_stl:
npg_stl = npg_stl.replace(
r"%%s",
'"},{"score":{"name":"*","objective":"'
+ scoreboard_name
+ '"}},{"text":"',
)
if r"%%%" in npg_stl:
npg_stl = npg_stl.replace(
r"%%%",
r'"},{"score":{"name":"*","objective":"'
+ sbn_pc
+ r'PercT"}},{"text":"%',
)
if r"%%t" in npg_stl:
npg_stl = npg_stl.replace(
r"%%t",
r'"},{"score":{"name":"*","objective":"{-}TMinT"}},{"text":":"},'
r'{"score":{"name":"*","objective":"{-}TSecT"}},{"text":"'.replace(
r"{-}", sbn_pc
),
)
result.append(
SingleCommand(
self.execute_cmd_head.format(
r"@a[scores={"
+ scoreboard_name
+ f"={int(i * perEach)}..{math.ceil((i + 1) * perEach)}"
+ r"}]"
)
+ r'titleraw @s actionbar {"rawtext":[{"text":"'
+ npg_stl
+ r'"}]}',
annotation="进度条显示",
)
)
if r"%%%" in pgs_style:
result.append(
SingleCommand(
"scoreboard objectives remove {}PercT".format(sbn_pc),
annotation="移除临时计算计分板(百分比)",
)
)
if r"%%t" in pgs_style:
result.append(
SingleCommand(
"scoreboard objectives remove {}TMinT".format(sbn_pc),
annotation="移除临时计算计分板(分)",
)
)
result.append(
SingleCommand(
"scoreboard objectives remove {}TSecT".format(sbn_pc),
annotation="移除临时计算计分板(秒)",
)
)
self.progress_bar_command = result
return result
def to_music_note_channels(
self,
ignore_mismatch_error: bool = True,
) -> NoteChannelType:
"""
将midi解析并转换为频道音符字典
Returns
-------
以频道作为分割的Midi音符列表字典:
Dict[int,List[SingleNote,]]
"""
if self.midi is None:
raise MidiUnboundError(
"你是否正在使用的是一个由 copy_important 生成的MidiConvert对象这是不可复用的。"
)
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
midi_channels: NoteChannelType = empty_midi_channels(staff=[])
tempo = mido.midifiles.midifiles.DEFAULT_TEMPO
# 我们来用通道统计音乐信息
# 但是是用分轨的思路的
for track_no, track in enumerate(self.midi.tracks):
microseconds = 0
if not track:
continue
note_queue_A: Dict[
int,
List[
Tuple[
int,
int,
]
],
] = empty_midi_channels(staff=[])
note_queue_B: Dict[
int,
List[
Tuple[
int,
int,
]
],
] = empty_midi_channels(staff=[])
channel_program: Dict[int, int] = empty_midi_channels(staff=-1)
for msg in track:
if msg.time != 0:
microseconds += msg.time * tempo / self.midi.ticks_per_beat / 1000
if msg.is_meta:
if msg.type == "set_tempo":
tempo = msg.tempo
else:
if msg.type == "program_change":
channel_program[msg.channel] = msg.program
elif msg.type == "note_on" and msg.velocity != 0:
note_queue_A[msg.channel].append(
(msg.note, channel_program[msg.channel])
)
note_queue_B[msg.channel].append((msg.velocity, microseconds))
elif (msg.type == "note_off") or (
msg.type == "note_on" and msg.velocity == 0
):
if (msg.note, channel_program[msg.channel]) in note_queue_A[
msg.channel
]:
_velocity, _ms = note_queue_B[msg.channel][
note_queue_A[msg.channel].index(
(msg.note, channel_program[msg.channel])
)
]
note_queue_A[msg.channel].remove(
(msg.note, channel_program[msg.channel])
)
note_queue_B[msg.channel].remove((_velocity, _ms))
midi_channels[msg.channel].append(
SingleNote(
instrument=msg.note,
pitch=channel_program[msg.channel],
velocity=_velocity,
startime=_ms,
lastime=microseconds - _ms,
track_number=track_no,
is_percussion=True,
)
if msg.channel == 9
else SingleNote(
instrument=channel_program[msg.channel],
pitch=msg.note,
velocity=_velocity,
startime=_ms,
lastime=microseconds - _ms,
track_number=track_no,
is_percussion=False,
)
)
else:
if ignore_mismatch_error:
print(
"[WARRING] MIDI格式错误 音符不匹配 {} 无法在上文中找到与之匹配的音符开音消息".format(
msg
)
)
else:
raise NoteOnOffMismatchError(
"当前的MIDI很可能有损坏之嫌……", msg, "无法在上文中找到与之匹配的音符开音消息。"
)
"""整合后的音乐通道格式
每个通道包括若干消息元素其中逃不过这三种:
1 切换乐器消息
("PgmC", 切换后的乐器ID: int, 距离演奏开始的毫秒)
2 音符开始消息
("NoteS", 开始的音符ID, 力度(响度), 距离演奏开始的毫秒)
3 音符结束消息
("NoteE", 结束的音符ID, 距离演奏开始的毫秒)"""
del tempo, self.channels
self.channels = dict(
[
(channel_no, sorted(channel_notes, key=lambda note: note.start_time))
for channel_no, channel_notes in midi_channels.items()
]
)
return self.channels
def to_command_list_in_score(
self,
scoreboard_name: str = "mscplay",
max_volume: float = 1.0,
speed: float = 1.0,
) -> Tuple[List[List[SingleCommand]], int, int]:
"""
将midi转换为我的世界命令列表
Parameters
----------
scoreboard_name: str
我的世界的计分板名称
max_volume: float
最大播放音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放
speed: float
速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
Returns
-------
tuple( list[list[SingleCommand指令,... ],... ], int指令数量, int音乐时长游戏刻 )
"""
if speed == 0:
raise ZeroSpeedError("播放速度仅可为(0,1]范围内的正实数")
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
command_channels = []
command_amount = 0
max_score = 0
# 此处 我们把通道视为音轨
for channel in self.to_music_note_channels().values():
# 如果当前通道为空 则跳过
if not channel:
continue
this_channel = []
for note in channel:
score_now = round(note.start_time / float(speed) / 50)
max_score = max(max_score, score_now)
this_channel.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores=({}={})]".format(scoreboard_name, score_now)
.replace("(", r"{")
.replace(")", r"}")
)
+ note.to_command(
(max_volume) if note.track_no == 0 else (max_volume * 0.9)
),
annotation="{}播放{}%{}".format(
mctick2timestr(score_now),
max_volume * 100,
"{}:{}".format(note.mc_sound_ID, note.mc_pitch),
),
),
)
command_amount += 1
if this_channel:
self.music_command_list.extend(this_channel)
command_channels.append(this_channel)
self.music_tick_num = max_score
return (command_channels, command_amount, max_score)
def to_command_list_in_delay(
self,
max_volume: float = 1.0,
speed: float = 1.0,
player_selector: str = "@a",
) -> Tuple[List[SingleCommand], int, int]:
"""
将midi转换为我的世界命令列表并输出每个音符之后的延迟
Parameters
----------
max_volume: float
最大播放音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
speed: float
速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
player_selector: str
玩家选择器,默认为`@a`
Returns
-------
tuple( list[SingleCommand,...], int音乐时长游戏刻, int最大同时播放的指令数量 )
"""
if speed == 0:
raise ZeroSpeedError("播放速度仅可为(0,1]范围内的正实数")
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
notes_list: List[SingleNote] = []
# 此处 我们把通道视为音轨
for channel in self.to_music_note_channels().values():
notes_list.extend(channel)
notes_list.sort(key=lambda a: a.start_time)
self.music_command_list = []
multi = max_multi = 0
delaytime_previous = 0
for note in notes_list:
delaytime_now = round(note.start_time / speed / 50)
if (tickdelay := (delaytime_now - delaytime_previous)) == 0:
multi += 1
else:
max_multi = max(max_multi, multi)
multi = 0
self.music_command_list.append(
SingleCommand(
self.execute_cmd_head.format(player_selector)
+ note.to_command(
(max_volume) if note.track_no == 0 else (max_volume * 0.9)
),
tick_delay=tickdelay,
annotation="{}播放{}%{}".format(
mctick2timestr(delaytime_now),
max_volume * 100,
"{}:{}".format(note.mc_sound_ID, note.mc_pitch),
),
)
)
delaytime_previous = delaytime_now
self.music_tick_num = round(notes_list[-1].start_time / speed / 50)
return self.music_command_list, self.music_tick_num, max_multi + 1
def copy_important(self):
dst = MidiConvert(
midi_obj=None,
midi_name=self.midi_music_name,
enable_old_exe_format=self.enable_old_exe_format,
)
dst.music_command_list = [i.copy() for i in self.music_command_list]
dst.progress_bar_command = [i.copy() for i in self.progress_bar_command]
dst.music_tick_num = self.music_tick_num
return dst

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
"""
存放非音·创本体的附加内容(插件?)
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
__all__ = [
"ConvertConfig",
]
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"))
from .main import *

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
"""
用以生成附加包的附加功能
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
__all__ = [
"to_addon_pack_in_delay",
"to_addon_pack_in_score",
"to_addon_pack_in_repeater",
]
__author__ = (("金羿", "Eilles Wan"),)
from .main import (
to_addon_pack_in_delay,
to_addon_pack_in_repeater,
to_addon_pack_in_score,
)

View File

@@ -0,0 +1,541 @@
# -*- coding: utf-8 -*-
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import json
import os
import shutil
from typing import Tuple
from TrimMCStruct import Structure
from ...main import MidiConvert
from ..archive import behavior_mcpack_manifest, compress_zipfile
from ..main import ConvertConfig
from ..mcstructure import (
COMPABILITY_VERSION_117,
COMPABILITY_VERSION_119,
commands_to_redstone_delay_structure,
commands_to_structure,
form_command_block_in_NBT_struct,
)
def to_addon_pack_in_score(
midi_cvt: MidiConvert,
data_cfg: ConvertConfig,
scoreboard_name: str = "mscplay",
auto_reset: bool = False,
) -> Tuple[int, int]:
"""
将midi以计分播放器形式转换为我的世界函数附加包
Parameters
----------
midi_cvt: MidiConvert 对象
用于转换的MidiConvert对象
data_cfg: ConvertConfig 对象
部分转换通用参数
scoreboard_name: str
我的世界的计分板名称
auto_reset: bool
是否自动重置计分板
Returns
-------
tuple[int指令数量, int音乐总延迟]
"""
cmdlist, maxlen, maxscore = midi_cvt.to_command_list_in_score(
scoreboard_name, data_cfg.volume_ratio, data_cfg.speed_multiplier
)
# 当文件f夹{self.outputPath}/temp/functions存在时清空其下所有项目然后创建
if os.path.exists(f"{data_cfg.dist_path}/temp/functions/"):
shutil.rmtree(f"{data_cfg.dist_path}/temp/functions/")
os.makedirs(f"{data_cfg.dist_path}/temp/functions/mscplay")
# 写入manifest.json
with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f:
json.dump(
behavior_mcpack_manifest(
pack_description=f"{midi_cvt.midi_music_name} 音乐播放包MCFUNCTION(MCPACK) 计分播放器 - 由 音·创 生成",
pack_name=midi_cvt.midi_music_name + "播放",
modules_description=f"无 - 由 音·创 生成",
),
fp=f,
indent=4,
)
# 写入stop.mcfunction
with open(
f"{data_cfg.dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8"
) as f:
f.write("scoreboard players reset @a {}".format(scoreboard_name))
# 将命令列表写入文件
index_file = open(
f"{data_cfg.dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8"
)
for i in range(len(cmdlist)):
index_file.write(f"function mscplay/track{i + 1}\n")
with open(
f"{data_cfg.dist_path}/temp/functions/mscplay/track{i + 1}.mcfunction",
"w",
encoding="utf-8",
) as f:
f.write("\n".join([single_cmd.cmd for single_cmd in cmdlist[i]]))
index_file.writelines(
(
"scoreboard players add @a[scores={"
+ scoreboard_name
+ "=1..}] "
+ scoreboard_name
+ " 1\n",
(
"scoreboard players reset @a[scores={"
+ scoreboard_name
+ "="
+ str(maxscore + 20)
+ "..}]"
+ f" {scoreboard_name}\n"
)
if auto_reset
else "",
f"function mscplay/progressShow\n" if data_cfg.progressbar_style else "",
)
)
if data_cfg.progressbar_style:
with open(
f"{data_cfg.dist_path}/temp/functions/mscplay/progressShow.mcfunction",
"w",
encoding="utf-8",
) as f:
f.writelines(
"\n".join(
[
single_cmd.cmd
for single_cmd in midi_cvt.form_progress_bar(
maxscore, scoreboard_name, data_cfg.progressbar_style
)
]
)
)
index_file.close()
if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack"):
os.remove(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack")
compress_zipfile(
f"{data_cfg.dist_path}/temp/",
f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack",
)
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
return maxlen, maxscore
def to_addon_pack_in_delay(
midi_cvt: MidiConvert,
data_cfg: ConvertConfig,
player: str = "@a",
max_height: int = 64,
) -> Tuple[int, int]:
"""
将midi以延迟播放器形式转换为mcstructure结构文件后打包成附加包并在附加包中生成相应地导入函数
Parameters
----------
midi_cvt: MidiConvert 对象
用于转换的MidiConvert对象
data_cfg: ConvertConfig 对象
部分转换通用参数
player: str
玩家选择器,默认为`@a`
max_height: int
生成结构最大高度
Returns
-------
tuple[int指令数量, int音乐总延迟]
"""
compability_ver = (
COMPABILITY_VERSION_117
if midi_cvt.enable_old_exe_format
else COMPABILITY_VERSION_119
)
command_list, max_delay = midi_cvt.to_command_list_in_delay(
data_cfg.volume_ratio,
data_cfg.speed_multiplier,
player,
)[:2]
if not os.path.exists(data_cfg.dist_path):
os.makedirs(data_cfg.dist_path)
# 当文件f夹{self.outputPath}/temp/存在时清空其下所有项目,然后创建
if os.path.exists(f"{data_cfg.dist_path}/temp/"):
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
os.makedirs(f"{data_cfg.dist_path}/temp/functions/")
os.makedirs(f"{data_cfg.dist_path}/temp/structures/")
# 写入manifest.json
with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f:
json.dump(
behavior_mcpack_manifest(
pack_description=f"{midi_cvt.midi_music_name} 音乐播放包MCSTRUCTURE(MCPACK) 延迟播放器 - 由 音·创 生成",
pack_name=midi_cvt.midi_music_name + "播放",
modules_description=f"无 - 由 音·创 生成",
),
fp=f,
indent=4,
)
# 写入stop.mcfunction
with open(
f"{data_cfg.dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8"
) as f:
f.write(
"gamerule commandblocksenabled false\ngamerule commandblocksenabled true"
)
# 将命令列表写入文件
index_file = open(
f"{data_cfg.dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8"
)
struct, size, end_pos = commands_to_structure(
command_list,
max_height - 1,
compability_version_=compability_ver,
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_main.mcstructure",
)
),
"wb+",
) as f:
struct.dump(f)
del struct
if data_cfg.progressbar_style:
scb_name = midi_cvt.midi_music_name[:3] + "Pgb"
index_file.write("scoreboard objectives add {0} dummy {0}\n".format(scb_name))
struct_a = Structure((1, 1, 1), compability_version=compability_ver)
struct_a.set_block(
(0, 0, 0),
form_command_block_in_NBT_struct(
r"scoreboard players add {} {} 1".format(player, scb_name),
(0, 0, 0),
1,
1,
alwaysRun=False,
customName="显示进度条并加分",
compability_version_number=compability_ver,
),
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_start.mcstructure",
)
),
"wb+",
) as f:
struct_a.dump(f)
index_file.write(f"structure load {midi_cvt.midi_music_name}_start ~ ~ ~1\n")
pgb_struct, pgbSize, pgbNowPos = commands_to_structure(
midi_cvt.form_progress_bar(max_delay, scb_name, data_cfg.progressbar_style),
max_height - 1,
compability_version_=compability_ver,
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_pgb.mcstructure",
)
),
"wb+",
) as f:
pgb_struct.dump(f)
index_file.write(f"structure load {midi_cvt.midi_music_name}_pgb ~ ~1 ~1\n")
struct_a = Structure(
(1, 1, 1),
)
struct_a.set_block(
(0, 0, 0),
form_command_block_in_NBT_struct(
r"scoreboard players reset {} {}".format(player, scb_name),
(0, 0, 0),
1,
0,
alwaysRun=False,
customName="重置进度条计分板",
compability_version_number=compability_ver,
),
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_reset.mcstructure",
)
),
"wb+",
) as f:
struct_a.dump(f)
del struct_a, pgb_struct
index_file.write(
f"structure load {midi_cvt.midi_music_name}_reset ~{pgbSize[0] + 2} ~ ~1\n"
)
index_file.write(
f"structure load {midi_cvt.midi_music_name}_main ~{pgbSize[0] + 2} ~1 ~1\n"
)
else:
index_file.write(f"structure load {midi_cvt.midi_music_name}_main ~ ~ ~1\n")
index_file.close()
if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack"):
os.remove(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack")
compress_zipfile(
f"{data_cfg.dist_path}/temp/",
f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack",
)
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
return len(command_list), max_delay
def to_addon_pack_in_repeater(
midi_cvt: MidiConvert,
data_cfg: ConvertConfig,
player: str = "@a",
max_height: int = 65,
) -> Tuple[int, int]:
"""
将midi以中继器播放器形式转换为mcstructure结构文件后打包成附加包并在附加包中生成相应地导入函数
Parameters
----------
midi_cvt: MidiConvert 对象
用于转换的MidiConvert对象
data_cfg: ConvertConfig 对象
部分转换通用参数
player: str
玩家选择器,默认为`@a`
max_height: int
生成结构最大高度
Returns
-------
tuple[int指令数量, int音乐总延迟]
"""
compability_ver = (
COMPABILITY_VERSION_117
if midi_cvt.enable_old_exe_format
else COMPABILITY_VERSION_119
)
command_list, max_delay, max_together = midi_cvt.to_command_list_in_delay(
data_cfg.volume_ratio,
data_cfg.speed_multiplier,
player,
)
if not os.path.exists(data_cfg.dist_path):
os.makedirs(data_cfg.dist_path)
# 当文件f夹{self.outputPath}/temp/存在时清空其下所有项目,然后创建
if os.path.exists(f"{data_cfg.dist_path}/temp/"):
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
os.makedirs(f"{data_cfg.dist_path}/temp/functions/")
os.makedirs(f"{data_cfg.dist_path}/temp/structures/")
# 写入manifest.json
with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f:
json.dump(
behavior_mcpack_manifest(
pack_description=f"{midi_cvt.midi_music_name} 音乐播放包MCSTRUCTURE(MCPACK) 中继器播放器 - 由 音·创 生成",
pack_name=midi_cvt.midi_music_name + "播放",
modules_description=f"无 - 由 音·创 生成",
),
fp=f,
indent=4,
)
# 写入stop.mcfunction
with open(
f"{data_cfg.dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8"
) as f:
f.write(
"gamerule commandblocksenabled false\ngamerule commandblocksenabled true"
)
# 将命令列表写入文件
index_file = open(
f"{data_cfg.dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8"
)
struct, size, end_pos = commands_to_redstone_delay_structure(
command_list,
max_delay,
max_together,
compability_version_=compability_ver,
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_main.mcstructure",
)
),
"wb+",
) as f:
struct.dump(f)
del struct
if data_cfg.progressbar_style:
scb_name = midi_cvt.midi_music_name[:3] + "Pgb"
index_file.write("scoreboard objectives add {0} dummy {0}\n".format(scb_name))
struct_a = Structure((1, 1, 1), compability_version=compability_ver)
struct_a.set_block(
(0, 0, 0),
form_command_block_in_NBT_struct(
r"scoreboard players add {} {} 1".format(player, scb_name),
(0, 0, 0),
1,
1,
alwaysRun=False,
customName="显示进度条并加分",
compability_version_number=compability_ver,
),
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_start.mcstructure",
)
),
"wb+",
) as f:
struct_a.dump(f)
index_file.write(f"structure load {midi_cvt.midi_music_name}_start ~ ~ ~1\n")
pgb_struct, pgbSize, pgbNowPos = commands_to_structure(
midi_cvt.form_progress_bar(max_delay, scb_name, data_cfg.progressbar_style),
max_height - 1,
compability_version_=compability_ver,
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_pgb.mcstructure",
)
),
"wb+",
) as f:
pgb_struct.dump(f)
index_file.write(f"structure load {midi_cvt.midi_music_name}_pgb ~ ~1 ~1\n")
struct_a = Structure(
(1, 1, 1),
)
struct_a.set_block(
(0, 0, 0),
form_command_block_in_NBT_struct(
r"scoreboard players reset {} {}".format(player, scb_name),
(0, 0, 0),
1,
0,
alwaysRun=False,
customName="重置进度条计分板",
compability_version_number=compability_ver,
),
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_reset.mcstructure",
)
),
"wb+",
) as f:
struct_a.dump(f)
del struct_a, pgb_struct
index_file.write(
f"structure load {midi_cvt.midi_music_name}_reset ~{pgbSize[0] + 2} ~ ~1\n"
)
index_file.write(
f"structure load {midi_cvt.midi_music_name}_main ~{pgbSize[0] + 2} ~1 ~1\n"
)
else:
index_file.write(f"structure load {midi_cvt.midi_music_name}_main ~ ~ ~1\n")
index_file.close()
if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack"):
os.remove(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack")
compress_zipfile(
f"{data_cfg.dist_path}/temp/",
f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack",
)
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
return len(command_list), max_delay

View File

@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
"""
存放关于文件打包的内容
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import datetime
import os
import uuid
import zipfile
from typing import List, Literal, Union
def compress_zipfile(sourceDir, outFilename, compression=8, exceptFile=None):
"""使用compression指定的算法打包目录为zip文件\n
默认算法为DEFLATED(8),可用算法如下:\n
STORED = 0\n
DEFLATED = 8\n
BZIP2 = 12\n
LZMA = 14\n
"""
zipf = zipfile.ZipFile(outFilename, "w", compression)
pre_len = len(os.path.dirname(sourceDir))
for parent, dirnames, filenames in os.walk(sourceDir):
for filename in filenames:
if filename == exceptFile:
continue
pathfile = os.path.join(parent, filename)
arc_name = pathfile[pre_len:].strip(os.path.sep) # 相对路径
zipf.write(pathfile, arc_name)
zipf.close()
def behavior_mcpack_manifest(
pack_description: str = "",
pack_version: Union[List[int], Literal[None]] = None,
pack_name: str = "",
pack_uuid: Union[str, Literal[None]] = None,
modules_description: str = "",
modules_version: List[int] = [0, 0, 1],
modules_uuid: Union[str, Literal[None]] = None,
):
"""
生成一个我的世界行为包组件的定义清单文件
"""
if not pack_version:
now_date = datetime.datetime.now()
pack_version = [
now_date.year,
now_date.month * 100 + now_date.day,
now_date.hour * 100 + now_date.minute,
]
return {
"format_version": 1,
"header": {
"description": pack_description,
"version": pack_version,
"name": pack_name,
"uuid": str(uuid.uuid4()) if not pack_uuid else pack_uuid,
},
"modules": [
{
"description": modules_description,
"type": "data",
"version": modules_version,
"uuid": str(uuid.uuid4()) if not modules_uuid else modules_uuid,
}
],
}

215
Musicreater/plugin/bdx.py Normal file
View File

@@ -0,0 +1,215 @@
# -*- coding: utf-8 -*-
"""
存放有关BDX结构操作的内容
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from typing import List
from ..constants import x, y, z
from ..subclass import SingleCommand
from .common import bottem_side_length_of_smallest_square_bottom_box
bdx_key = {
"x": [b"\x0f", b"\x0e", b"\x1c", b"\x14", b"\x15"],
"y": [b"\x11", b"\x10", b"\x1d", b"\x16", b"\x17"],
"z": [b"\x13", b"\x12", b"\x1e", b"\x18", b"\x19"],
}
"""key存储了方块移动指令的数据其中可以用key[x|y|z][0|1]来表示xyz的减或增
而key[][2+]是用来增加指定数目的"""
def bdx_move(axis: str, value: int):
if value == 0:
return b""
if abs(value) == 1:
return bdx_key[axis][0 if value == -1 else 1]
pointer = sum(
[
1 if i else 0
for i in (
value != -1,
value < -1 or value > 1,
value < -128 or value > 127,
value < -32768 or value > 32767,
)
]
)
return bdx_key[axis][pointer] + value.to_bytes(
2 ** (pointer - 2), "big", signed=True
)
def form_command_block_in_BDX_bytes(
command: str,
particularValue: int,
impluse: int = 0,
condition: bool = False,
needRedstone: bool = True,
tickDelay: int = 0,
customName: str = "",
executeOnFirstTick: bool = False,
trackOutput: bool = True,
):
"""
使用指定项目返回指定的指令方块放置指令项
:param command: `str`
指令
:param particularValue:
方块特殊值,即朝向
:0 下 无条件
:1 上 无条件
:2 z轴负方向 无条件
:3 z轴正方向 无条件
:4 x轴负方向 无条件
:5 x轴正方向 无条件
:6 下 无条件
:7 下 无条件
:8 下 有条件
:9 上 有条件
:10 z轴负方向 有条件
:11 z轴正方向 有条件
:12 x轴负方向 有条件
:13 x轴正方向 有条件
:14 下 有条件
:14 下 有条件
注意此处特殊值中的条件会被下面condition参数覆写
:param impluse: `int 0|1|2`
方块类型
0脉冲 1循环 2连锁
:param condition: `bool`
是否有条件
:param needRedstone: `bool`
是否需要红石
:param tickDelay: `int`
执行延时
:param customName: `str`
悬浮字
lastOutput: `str`
上次输出字符串,注意此处需要留空
:param executeOnFirstTick: `bool`
首刻执行(循环指令方块是否激活后立即执行若为False则从激活时起延迟后第一次执行)
:param trackOutput: `bool`
是否输出
:return:str
"""
block = b"\x24" + particularValue.to_bytes(2, byteorder="big", signed=False)
for i in [
impluse.to_bytes(4, byteorder="big", signed=False),
bytes(command, encoding="utf-8") + b"\x00",
bytes(customName, encoding="utf-8") + b"\x00",
bytes("", encoding="utf-8") + b"\x00",
tickDelay.to_bytes(4, byteorder="big", signed=True),
executeOnFirstTick.to_bytes(1, byteorder="big"),
trackOutput.to_bytes(1, byteorder="big"),
condition.to_bytes(1, byteorder="big"),
needRedstone.to_bytes(1, byteorder="big"),
]:
block += i
return block
def commands_to_BDX_bytes(
commands_list: List[SingleCommand],
max_height: int = 64,
):
"""
:param commands: 指令列表(指令, 延迟)
:param max_height: 生成结构最大高度
:return 成功与否,成功返回(True,未经过压缩的源,结构占用大小),失败返回(False,str失败原因)
"""
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
len(commands_list), max_height
)
_bytes = b""
y_forward = True
z_forward = True
now_y = 0
now_z = 0
now_x = 0
for command in commands_list:
_bytes += form_command_block_in_BDX_bytes(
command.command_text,
(1 if y_forward else 0)
if (
((now_y != 0) and (not y_forward))
or (y_forward and (now_y != (max_height - 1)))
)
else (
(3 if z_forward else 2)
if (
((now_z != 0) and (not z_forward))
or (z_forward and (now_z != _sideLength - 1))
)
else 5
),
impluse=2,
condition=command.conditional,
needRedstone=False,
tickDelay=command.delay,
customName=command.annotation_text,
executeOnFirstTick=False,
trackOutput=True,
)
# (1 if y_forward else 0) if ( # 如果y+则向上,反之向下
# ((now_y != 0) and (not y_forward)) # 如果不是y轴上首个方块
# or (y_forward and (now_y != (max_height - 1))) # 如果不是y轴上末端方块
# ) else ( # 否则即是y轴末端或首个方块
# (3 if z_forward else 2) if ( # 如果z+则向z轴正方向反之负方向
# ((now_z != 0) and (not z_forward)) # 如果不是z轴上的首个方块
# or (z_forward and (now_z != _sideLength - 1)) # 如果不是z轴上的末端方块
# ) else 5 # 否则则要面向x轴正方向
# )
now_y += 1 if y_forward else -1
if ((now_y >= max_height) and y_forward) or ((now_y < 0) and (not y_forward)):
now_y -= 1 if y_forward else -1
y_forward = not y_forward
now_z += 1 if z_forward else -1
if ((now_z >= _sideLength) and z_forward) or (
(now_z < 0) and (not z_forward)
):
now_z -= 1 if z_forward else -1
z_forward = not z_forward
_bytes += bdx_key[x][1]
now_x += 1
else:
_bytes += bdx_key[z][int(z_forward)]
else:
_bytes += bdx_key[y][int(y_forward)]
return (
_bytes,
[
now_x + 1,
max_height if now_x or now_z else now_y,
_sideLength if now_x else now_z,
],
[now_x, now_y, now_z],
)

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
"""
用以生成BDX结构文件的附加功能
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
__all__ = ["to_BDX_file_in_score", "to_BDX_file_in_delay"]
__author__ = (("金羿", "Eilles Wan"),)
from .main import to_BDX_file_in_delay, to_BDX_file_in_score

View File

@@ -0,0 +1,226 @@
# -*- coding: utf-8 -*-
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import os
import brotli
from ...main import MidiConvert
from ...subclass import SingleCommand
from ..bdx import (
bdx_move,
commands_to_BDX_bytes,
form_command_block_in_BDX_bytes,
x,
y,
z,
)
from ..main import ConvertConfig
def to_BDX_file_in_score(
midi_cvt: MidiConvert,
data_cfg: ConvertConfig,
scoreboard_name: str = "mscplay",
auto_reset: bool = False,
author: str = "Eilles",
max_height: int = 64,
):
"""
将midi以计分播放器形式转换为BDX结构文件
Parameters
----------
midi_cvt: MidiConvert 对象
用于转换的MidiConvert对象
data_cfg: ConvertConfig 对象
部分转换通用参数
scoreboard_name: str
我的世界的计分板名称
auto_reset: bool
是否自动重置计分板
author: str
作者名称
max_height: int
生成结构最大高度
Returns
-------
tuple[int指令数量, int音乐总延迟, tuple[int,]结构大小, tuple[int,]终点坐标]
"""
cmdlist, command_count, max_score = midi_cvt.to_command_list_in_score(
scoreboard_name, data_cfg.volume_ratio, data_cfg.speed_multiplier
)
if not os.path.exists(data_cfg.dist_path):
os.makedirs(data_cfg.dist_path)
with open(
os.path.abspath(
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx")
),
"w+",
) as f:
f.write("BD@")
_bytes = (
b"BDX\x00" + author.encode("utf-8") + b" & Musicreater\x00\x01command_block\x00"
)
cmdBytes, size, finalPos = commands_to_BDX_bytes(
midi_cvt.music_command_list
+ (
[
SingleCommand(
command="scoreboard players reset @a[scores={"
+ scoreboard_name
+ "="
+ str(max_score + 20)
+ "}] "
+ scoreboard_name,
annotation="自动重置计分板",
)
]
if auto_reset
else []
),
max_height - 1,
)
if data_cfg.progressbar_style:
pgbBytes, pgbSize, pgbNowPos = commands_to_BDX_bytes(
midi_cvt.form_progress_bar(
max_score, scoreboard_name, data_cfg.progressbar_style
),
max_height - 1,
)
_bytes += pgbBytes
_bytes += bdx_move(y, -pgbNowPos[1])
_bytes += bdx_move(z, -pgbNowPos[2])
_bytes += bdx_move(x, 2)
size[0] += 2 + pgbSize[0]
size[1] = max(size[1], pgbSize[1])
size[2] = max(size[2], pgbSize[2])
_bytes += cmdBytes
with open(
os.path.abspath(
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx")
),
"ab+",
) as f:
f.write(brotli.compress(_bytes + b"XE"))
return command_count, max_score, size, finalPos
def to_BDX_file_in_delay(
midi_cvt: MidiConvert,
data_cfg: ConvertConfig,
player: str = "@a",
author: str = "Eilles",
max_height: int = 64,
):
"""
使用method指定的转换算法将midi转换为BDX结构文件
Parameters
----------
midi_cvt: MidiConvert 对象
用于转换的MidiConvert对象
data_cfg: ConvertConfig 对象
部分转换通用参数
player: str
玩家选择器,默认为`@a`
author: str
作者名称
max_height: int
生成结构最大高度
Returns
-------
tuple[int指令数量, int音乐总延迟, tuple[int,]结构大小, tuple[int,]终点坐标]
"""
cmdlist, max_delay = midi_cvt.to_command_list_in_delay(
data_cfg.volume_ratio,
data_cfg.speed_multiplier,
player,
)[:2]
if not os.path.exists(data_cfg.dist_path):
os.makedirs(data_cfg.dist_path)
with open(
os.path.abspath(
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx")
),
"w+",
) as f:
f.write("BD@")
_bytes = (
b"BDX\x00" + author.encode("utf-8") + b" & Musicreater\x00\x01command_block\x00"
)
cmdBytes, size, finalPos = commands_to_BDX_bytes(cmdlist, max_height - 1)
if data_cfg.progressbar_style:
scb_name = midi_cvt.midi_music_name[:3] + "Pgb"
_bytes += form_command_block_in_BDX_bytes(
r"scoreboard objectives add {} dummy {}".replace(r"{}", scb_name),
1,
customName="初始化进度条",
)
_bytes += bdx_move(z, 2)
_bytes += form_command_block_in_BDX_bytes(
r"scoreboard players add {} {} 1".format(player, scb_name),
1,
1,
customName="显示进度条并加分",
)
_bytes += bdx_move(y, 1)
pgbBytes, pgbSize, pgbNowPos = commands_to_BDX_bytes(
midi_cvt.form_progress_bar(max_delay, scb_name, data_cfg.progressbar_style),
max_height - 1,
)
_bytes += pgbBytes
_bytes += bdx_move(y, -1 - pgbNowPos[1])
_bytes += bdx_move(z, -2 - pgbNowPos[2])
_bytes += bdx_move(x, 2)
_bytes += form_command_block_in_BDX_bytes(
r"scoreboard players reset {} {}".format(player, scb_name),
1,
customName="置零进度条",
)
_bytes += bdx_move(y, 1)
size[0] += 2 + pgbSize[0]
size[1] = max(size[1], pgbSize[1])
size[2] = max(size[2], pgbSize[2])
size[1] += 1
_bytes += cmdBytes
with open(
os.path.abspath(
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx")
),
"ab+",
) as f:
f.write(brotli.compress(_bytes + b"XE"))
return len(cmdlist), max_delay, size, finalPos

View File

@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
"""
存放通用的普遍性的插件内容
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import math
def bottem_side_length_of_smallest_square_bottom_box(total: int, maxHeight: int):
"""给定总方块数量和最大高度,返回所构成的图形外切正方形的边长
:param total: 总方块数量
:param maxHeight: 最大高度
:return: 外切正方形的边长 int"""
return math.ceil(math.sqrt(math.ceil(total / maxHeight)))

View File

@@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
"""
存放附加内容功能
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from dataclasses import dataclass
from typing import Literal, Tuple, Union
from ..constants import DEFAULT_PROGRESSBAR_STYLE
@dataclass(init=False)
class ConvertConfig:
"""
转换通用设置存储类
"""
volume_ratio: float
"""音量比例"""
speed_multiplier: float
"""速度倍率"""
progressbar_style: Union[Tuple[str, Tuple[str, str]], Literal[None]]
"""进度条样式组"""
dist_path: str
"""输出目录"""
def __init__(
self,
output_path: str,
volume: float = 1.0,
speed: float = 1.0,
progressbar: Union[bool, Tuple[str, Tuple[str, str]]] = True,
):
"""
将已经转换好的数据内容指令载入MC可读格式
Parameters
----------
output_path: str
生成内容的输出目录
volume: float
音量比率,范围为(0,1],其原理为在距离玩家 (1 / volume -1) 的地方播放音频
speed: float
速度倍率,注意:这里的速度指的是播放速度倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
progressbar: bool|tuple[str, Tuple[str,]]
进度条,当此参数为 `True` 时使用默认进度条,为其他的**值为真**的参数时识别为进度条自定义参数,为其他**值为假**的时候不生成进度条
"""
self.dist_path = output_path
"""输出目录"""
self.volume_ratio = volume
"""音量比例"""
self.speed_multiplier = speed
"""速度倍率"""
if progressbar:
# 此处是对于仅有 True 的参数和自定义参数的判断
# 改这一段没🐎
if progressbar is True:
self.progressbar_style = DEFAULT_PROGRESSBAR_STYLE
"""进度条样式组"""
else:
self.progressbar_style = progressbar
"""进度条样式组"""
else:
self.progressbar_style = None
"""进度条样式组"""

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
"""
用以生成单个mcstructure文件的附加功能
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
__all__ = [
"to_mcstructure_file_in_delay",
"to_mcstructure_file_in_repeater",
]
__author__ = (("金羿", "Eilles Wan"),)
from .main import to_mcstructure_file_in_delay, to_mcstructure_file_in_repeater

View File

@@ -0,0 +1,143 @@
# -*- coding: utf-8 -*-
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import os
from typing import Literal
from ...exceptions import CommandFormatError
from ...main import MidiConvert
from ..main import ConvertConfig
from ..mcstructure import (
COMPABILITY_VERSION_117,
COMPABILITY_VERSION_119,
commands_to_redstone_delay_structure,
commands_to_structure,
)
def to_mcstructure_file_in_delay(
midi_cvt: MidiConvert,
data_cfg: ConvertConfig,
player: str = "@a",
max_height: int = 64,
):
"""
将midi以延迟播放器形式转换为mcstructure结构文件
Parameters
----------
midi_cvt: MidiConvert 对象
用于转换的MidiConvert对象
data_cfg: ConvertConfig 对象
部分转换通用参数
player: str
玩家选择器,默认为`@a`
max_height: int
生成结构最大高度
Returns
-------
tuple[tuple[int,]结构大小, int音乐总延迟]
"""
compability_ver = (
COMPABILITY_VERSION_117
if midi_cvt.enable_old_exe_format
else COMPABILITY_VERSION_119
)
cmd_list, max_delay = midi_cvt.to_command_list_in_delay(
data_cfg.volume_ratio,
data_cfg.speed_multiplier,
player,
)[:2]
if not os.path.exists(data_cfg.dist_path):
os.makedirs(data_cfg.dist_path)
struct, size, end_pos = commands_to_structure(
cmd_list, max_height - 1, compability_version_=compability_ver
)
with open(
os.path.abspath(
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.mcstructure")
),
"wb+",
) as f:
struct.dump(f)
return size, max_delay
def to_mcstructure_file_in_repeater(
midi_cvt: MidiConvert,
data_cfg: ConvertConfig,
player: str = "@a",
axis_side: Literal["z+", "z-", "Z+", "Z-", "x+", "x-", "X+", "X-"] = "z+",
basement_block: str = "concrete",
):
"""
将midi以延迟播放器形式转换为mcstructure结构文件
Parameters
----------
midi_cvt: MidiConvert 对象
用于转换的MidiConvert对象
data_cfg: ConvertConfig 对象
部分转换通用参数
player: str
玩家选择器,默认为`@a`
axis_side: Literal["z+","z-","Z+","Z-","x+","x-","X+","X-"]
生成结构的延展方向
basement_block: str
结构的基底方块
Returns
-------
tuple[tuple[int,]结构大小, int音乐总延迟]
"""
compability_ver = (
COMPABILITY_VERSION_117
if midi_cvt.enable_old_exe_format
else COMPABILITY_VERSION_119
)
cmd_list, max_delay, max_multiple_cmd = midi_cvt.to_command_list_in_delay(
data_cfg.volume_ratio,
data_cfg.speed_multiplier,
player,
)
if not os.path.exists(data_cfg.dist_path):
os.makedirs(data_cfg.dist_path)
struct, size, end_pos = commands_to_redstone_delay_structure(
cmd_list,
max_delay,
max_multiple_cmd,
basement_block,
axis_side,
compability_version_=compability_ver,
)
with open(
os.path.abspath(
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.mcstructure")
),
"wb+",
) as f:
struct.dump(f)
return size, max_delay

View File

@@ -0,0 +1,487 @@
# -*- coding: utf-8 -*-
"""
存放有关MCSTRUCTURE结构操作的内容
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from typing import List, Literal, Tuple
from TrimMCStruct import Block, Structure, TAG_Byte, TAG_Long
from ..constants import x, y, z
from ..subclass import SingleCommand
from .common import bottem_side_length_of_smallest_square_bottom_box
def antiaxis(axis: Literal["x", "z", "X", "Z"]):
return z if axis == x else x
def forward_IER(forward: bool):
return 1 if forward else -1
AXIS_PARTICULAR_VALUE = {
x: {
True: 5,
False: 4,
},
y: {
True: 1,
False: 0,
},
z: {
True: 3,
False: 2,
},
}
# 1.19的结构兼容版本号
COMPABILITY_VERSION_119: int = 17959425
"""
Minecraft 1.19 兼容版本号
"""
# 1.17的结构兼容版本号
COMPABILITY_VERSION_117: int = 17879555
"""
Minecraft 1.17 兼容版本号
"""
def command_statevalue(axis_: Literal["x", "y", "z", "X", "Y", "Z"], forward_: bool):
return AXIS_PARTICULAR_VALUE[axis_.lower()][forward_]
def form_note_block_in_NBT_struct(
note: int,
coordinate: Tuple[int, int, int],
instrument: str = "note.harp",
powered: bool = False,
compability_version_number: int = COMPABILITY_VERSION_119,
):
"""生成音符盒方块
:param note: `int`(0~24)
音符的音高
:param coordinate: `tuple[int,int,int]`
此方块所在之相对坐标
:param instrument: `str`
音符盒的乐器
:param powered: `bool`
是否已被激活
:return Block
"""
return Block(
"minecraft",
"noteblock",
{
"instrument": instrument.replace("note.", ""),
"note": note,
"powered": powered,
},
{
"block_entity_data": {
"note": TAG_Byte(note),
"id": "noteblock",
"x": coordinate[0],
"y": coordinate[1],
"z": coordinate[2],
} # type: ignore
},
compability_version=compability_version_number,
)
def form_repeater_in_NBT_struct(
delay: int,
facing: int,
compability_version_number: int = COMPABILITY_VERSION_119,
):
"""生成中继器方块
:param facing: 朝向:
Z- 北 0
X- 东 1
Z+ 南 2
X+ 西 3
:param delay: 0~3
:return Block()"""
return Block(
"minecraft",
"unpowered_repeater",
{
"repeater_delay": delay,
"direction": facing,
},
compability_version=compability_version_number,
)
def form_command_block_in_NBT_struct(
command: str,
coordinate: tuple,
particularValue: int,
impluse: int = 0,
condition: bool = False,
alwaysRun: bool = True,
tickDelay: int = 0,
customName: str = "",
executeOnFirstTick: bool = False,
trackOutput: bool = True,
compability_version_number: int = COMPABILITY_VERSION_119,
):
"""
使用指定项目返回指定的指令方块结构
:param command: `str`
指令
:param coordinate: `tuple[int,int,int]`
此方块所在之相对坐标
:param particularValue:
方块特殊值,即朝向
:0 下 无条件
:1 上 无条件
:2 z轴负方向 无条件
:3 z轴正方向 无条件
:4 x轴负方向 无条件
:5 x轴正方向 无条件
:6 下 无条件
:7 下 无条件
:8 下 有条件
:9 上 有条件
:10 z轴负方向 有条件
:11 z轴正方向 有条件
:12 x轴负方向 有条件
:13 x轴正方向 有条件
:14 下 有条件
:14 下 有条件
注意此处特殊值中的条件会被下面condition参数覆写
:param impluse: `int 0|1|2`
方块类型
0脉冲 1循环 2连锁
:param condition: `bool`
是否有条件
:param alwaysRun: `bool`
是否始终执行
:param tickDelay: `int`
执行延时
:param customName: `str`
悬浮字
:param executeOnFirstTick: `bool`
首刻执行(循环指令方块是否激活后立即执行若为False则从激活时起延迟后第一次执行)
:param trackOutput: `bool`
是否输出
:return:str
"""
return Block(
"minecraft",
"command_block"
if impluse == 0
else ("repeating_command_block" if impluse == 1 else "chain_command_block"),
states={"conditional_bit": condition, "facing_direction": particularValue},
extra_data={
"block_entity_data": {
"Command": command,
"CustomName": customName,
"ExecuteOnFirstTick": executeOnFirstTick,
"LPCommandMode": 0,
"LPCondionalMode": False,
"LPRedstoneMode": False,
"LastExecution": TAG_Long(0),
"LastOutput": "",
"LastOutputParams": [],
"SuccessCount": 0,
"TickDelay": tickDelay,
"TrackOutput": trackOutput,
"Version": 25,
"auto": alwaysRun,
"conditionMet": False, # 是否已经满足条件
"conditionalMode": condition,
"id": "CommandBlock",
"isMovable": True,
"powered": False, # 是否已激活
"x": coordinate[0],
"y": coordinate[1],
"z": coordinate[2],
} # type: ignore
},
compability_version=compability_version_number,
)
def commands_to_structure(
commands: List[SingleCommand],
max_height: int = 64,
compability_version_: int = COMPABILITY_VERSION_119,
):
"""
:param commands: 指令列表
:param max_height: 生成结构最大高度
:return 结构类,结构占用大小,终点坐标
"""
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
len(commands), max_height
)
struct = Structure(
size=(_sideLength, max_height, _sideLength), # 声明结构大小
compability_version=compability_version_,
)
y_forward = True
z_forward = True
now_y = 0
now_z = 0
now_x = 0
for command in commands:
coordinate = (now_x, now_y, now_z)
struct.set_block(
coordinate,
form_command_block_in_NBT_struct(
command=command.command_text,
coordinate=coordinate,
particularValue=(1 if y_forward else 0)
if (
((now_y != 0) and (not y_forward))
or (y_forward and (now_y != (max_height - 1)))
)
else (
(3 if z_forward else 2)
if (
((now_z != 0) and (not z_forward))
or (z_forward and (now_z != _sideLength - 1))
)
else 5
),
impluse=2,
condition=False,
alwaysRun=True,
tickDelay=command.delay,
customName=command.annotation_text,
executeOnFirstTick=False,
trackOutput=True,
compability_version_number=compability_version_,
),
)
now_y += 1 if y_forward else -1
if ((now_y >= max_height) and y_forward) or ((now_y < 0) and (not y_forward)):
now_y -= 1 if y_forward else -1
y_forward = not y_forward
now_z += 1 if z_forward else -1
if ((now_z >= _sideLength) and z_forward) or (
(now_z < 0) and (not z_forward)
):
now_z -= 1 if z_forward else -1
z_forward = not z_forward
now_x += 1
return (
struct,
(
now_x + 1,
max_height if now_x or now_z else now_y,
_sideLength if now_x else now_z,
),
(now_x, now_y, now_z),
)
def commands_to_redstone_delay_structure(
commands: List[SingleCommand],
delay_length: int,
max_multicmd_length: int,
base_block: str = "concrete",
axis_: Literal["z+", "z-", "Z+", "Z-", "x+", "x-", "X+", "X-"] = "z+",
compability_version_: int = COMPABILITY_VERSION_119,
) -> Tuple[Structure, Tuple[int, int, int], Tuple[int, int, int]]:
"""
:param commands: 指令列表
:param delay_length: 延时总长
:param max_multicmd_length: 最大同时播放的音符数量
:param base_block: 生成结构的基底方块
:param axis_: 生成结构的延展方向
:return 结构类,结构占用大小,终点坐标
"""
if axis_ in ["z+", "Z+"]:
extensioon_direction = z
aside_direction = x
repeater_facing = 2
forward = True
elif axis_ in ["z-", "Z-"]:
extensioon_direction = z
aside_direction = x
repeater_facing = 0
forward = False
elif axis_ in ["x+", "X+"]:
extensioon_direction = x
aside_direction = z
repeater_facing = 3
forward = True
elif axis_ in ["x-", "X-"]:
extensioon_direction = x
aside_direction = z
repeater_facing = 1
forward = False
else:
raise ValueError(f"axis_({axis_}) 参数错误。")
goahead = forward_IER(forward)
command_actually_length = sum([int(bool(cmd.delay)) for cmd in commands])
a = 1
a_max = 0
total_cmd = 0
for cmd in commands:
# print("\r 正在进行处理:",end="")
if cmd.delay > 2:
a_max = max(a,a_max)
total_cmd += (a := 1)
else:
a += 1
struct = Structure(
size=(
round(delay_length / 2 + total_cmd)
if extensioon_direction == x
else a_max,
3,
round(delay_length / 2 + total_cmd)
if extensioon_direction == z
else a_max,
),
fill=Block("minecraft", "air", compability_version=compability_version_),
compability_version=compability_version_,
)
pos_now = {
x: ((1 if extensioon_direction == x else 0) if forward else struct.size[0]),
y: 0,
z: ((1 if extensioon_direction == z else 0) if forward else struct.size[2]),
}
chain_list = 0
# print("结构元信息设定完毕")
for cmd in commands:
# print("\r 正在进行处理:",end="")
if cmd.delay > 1:
# print("\rdelay > 0",end='')
single_repeater_value = int(cmd.delay / 2) % 4 - 1
additional_repeater = int(cmd.delay / 2 // 4)
for i in range(additional_repeater):
struct.set_block(
tuple(pos_now.values()), # type: ignore
Block(
"minecraft",
base_block,
compability_version=compability_version_,
),
)
struct.set_block(
(pos_now[x], 1, pos_now[z]),
form_repeater_in_NBT_struct(
delay=3,
facing=repeater_facing,
compability_version_number=compability_version_,
),
)
pos_now[extensioon_direction] += goahead
if single_repeater_value >= 0:
struct.set_block(
tuple(pos_now.values()), # type: ignore
Block(
"minecraft",
base_block,
compability_version=compability_version_,
),
)
struct.set_block(
(pos_now[x], 1, pos_now[z]),
form_repeater_in_NBT_struct(
delay=single_repeater_value,
facing=repeater_facing,
compability_version_number=compability_version_,
),
)
pos_now[extensioon_direction] += goahead
struct.set_block(
(pos_now[x], 1, pos_now[z]),
form_command_block_in_NBT_struct(
command=cmd.command_text,
coordinate=(pos_now[x], 1, pos_now[z]),
particularValue=command_statevalue(extensioon_direction, forward),
# impluse= (0 if first_impluse else 2),
impluse=0,
condition=False,
alwaysRun=False,
tickDelay=cmd.delay % 2,
customName=cmd.annotation_text,
compability_version_number=compability_version_,
),
)
struct.set_block(
(pos_now[x], 2, pos_now[z]),
Block(
"minecraft",
"redstone_wire",
compability_version=compability_version_,
),
)
pos_now[extensioon_direction] += goahead
chain_list = 1
else:
# print(pos_now)
now_pos_copy = pos_now.copy()
now_pos_copy[extensioon_direction] -= goahead
now_pos_copy[aside_direction] += chain_list
# print(pos_now,"\n=========")
struct.set_block(
(now_pos_copy[x], 1, now_pos_copy[z]),
form_command_block_in_NBT_struct(
command=cmd.command_text,
coordinate=(now_pos_copy[x], 1, now_pos_copy[z]),
particularValue=command_statevalue(extensioon_direction, forward),
# impluse= (0 if first_impluse else 2),
impluse=0,
condition=False,
alwaysRun=False,
tickDelay=cmd.delay % 2,
customName=cmd.annotation_text,
compability_version_number=compability_version_,
),
)
struct.set_block(
(now_pos_copy[x], 2, now_pos_copy[z]),
Block(
"minecraft",
"redstone_wire",
compability_version=compability_version_,
),
)
chain_list += 1
return struct, struct.size, tuple(pos_now.values()) # type: ignore

View File

@@ -0,0 +1,117 @@
# -*- coding: utf-8 -*-
"""
存放有关红石音乐生成操作的内容
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from ..exceptions import NotDefineProgramError, ZeroSpeedError
from ..main import MidiConvert
from ..subclass import SingleCommand
from ..utils import inst_to_souldID_withX, perc_inst_to_soundID_withX
# 你以为写完了吗?其实并没有
def to_note_list(
midi_cvt: MidiConvert,
speed: float = 1.0,
) -> list:
"""
使用金羿的转换思路将midi转换为我的世界音符盒所用的音高列表并输出每个音符之后的延迟
Parameters
----------
speed: float
速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
Returns
-------
tuple( list[tuple(str指令, int距离上一个指令的延迟 ),...], int音乐时长游戏刻 )
"""
if speed == 0:
raise ZeroSpeedError("播放速度仅可为正实数")
midi_channels = (
midi_cvt.to_music_channels() if not midi_cvt.channels else midi_cvt.channels
)
tracks = {}
# 此处 我们把通道视为音轨
for i in midi_channels.keys():
# 如果当前通道为空 则跳过
if not midi_channels[i]:
continue
# 第十通道是打击乐通道
SpecialBits = True if i == 9 else False
# nowChannel = []
for track_no, track in midi_channels[i].items():
for msg in track:
if msg[0] == "PgmC":
InstID = msg[1]
elif msg[0] == "NoteS":
try:
soundID, _X = (
perc_inst_to_soundID_withX(InstID)
if SpecialBits
else inst_to_souldID_withX(InstID)
)
except UnboundLocalError as E:
soundID, _X = (
perc_inst_to_soundID_withX(-1)
if SpecialBits
else inst_to_souldID_withX(-1)
)
score_now = round(msg[-1] / float(speed) / 50)
# print(score_now)
try:
tracks[score_now].append(
self.execute_cmd_head.format(player)
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
)
except KeyError:
tracks[score_now] = [
self.execute_cmd_head.format(player)
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
]
all_ticks = list(tracks.keys())
all_ticks.sort()
results = []
for i in range(len(all_ticks)):
for j in range(len(tracks[all_ticks[i]])):
results.append(
(
tracks[all_ticks[i]][j],
(
0
if j != 0
else (
all_ticks[i] - all_ticks[i - 1] if i != 0 else all_ticks[i]
)
),
)
)
return [results, max(all_ticks)]

View File

@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
"""
存放有关Schematic结构生成的内容
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import nbtschematic

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
"""
用以生成Schematic结构的附加功能
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
__all__ = [
]
__author__ = (("金羿", "Eilles Wan"),)
from .main import *

View File

@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from ..schematic import *

View File

@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
"""
存放有关WebSocket服务器操作的内容
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import fcwslib
# 这个库有问题,正在检修
class Plugin(fcwslib.Plugin):
async def on_connect(self) -> None:
print("对象已被连接")
await self.send_command("list", callback=self.list)
await self.subscribe("PlayerMessage", callback=self.player_message)
async def on_disconnect(self) -> None:
print("对象停止连接")
async def on_receive(self, response) -> None:
print("已接收非常规回复 {}".format(response))
async def list(self, response) -> None:
print("已收取指令执行回复 {}".format(response))
async def player_message(self, response) -> None:
print("已收取玩家事件回复 {}".format(response))

View File

@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
"""
用以启动WebSocket服务器播放的附加功能
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
__all__ = []
__author__ = (("金羿", "Eilles Wan"),)
from .main import *

View File

@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import fcwslib
from ...main import MidiConvert
from ..main import ConvertConfig
from ...subclass import SingleCommand
def open_websocket_server(
midi_cvt: MidiConvert,
data_cfg: ConvertConfig,
player: str = "@a",
server_dist: str = "localhost",
server_port: int = 8000,
):
wssever = fcwslib.Server(server=server_dist,port=server_port,debug_mode=False)

455
Musicreater/previous.py Normal file
View File

@@ -0,0 +1,455 @@
# -*- coding: utf-8 -*-
"""
旧版本转换功能以及已经弃用的函数
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from .exceptions import *
from .main import MidiConvert, mido
from .subclass import *
from .types import ChannelType
from .utils import *
class ObsoleteMidiConvert(MidiConvert):
"""
我说一句话:
这些破烂老代码能跑得起来就是谢天谢地,你们还指望我怎么样?这玩意真的不会再维护了,我发誓!
"""
def to_music_channels(
self,
) -> ChannelType:
"""
使用金羿的转换思路将midi解析并转换为频道信息字典
Returns
-------
以频道作为分割的Midi信息字典:
Dict[int,Dict[int,List[Union[Tuple[Literal["PgmC"], int, int],Tuple[Literal["NoteS"], int, int, int],Tuple[Literal["NoteE"], int, int],]],],]
"""
if self.midi is None:
raise MidiUnboundError(
"你是否正在使用的是一个由 copy_important 生成的MidiConvert对象这是不可复用的。"
)
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
midi_channels: ChannelType = empty_midi_channels()
tempo = mido.midifiles.midifiles.DEFAULT_TEMPO
# 我们来用通道统计音乐信息
# 但是是用分轨的思路的
for track_no, track in enumerate(self.midi.tracks):
microseconds = 0
if not track:
continue
note_queue = empty_midi_channels(staff=[])
for msg in track:
if msg.time != 0:
microseconds += msg.time * tempo / self.midi.ticks_per_beat / 1000
if msg.is_meta:
if msg.type == "set_tempo":
tempo = msg.tempo
else:
try:
if not track_no in midi_channels[msg.channel].keys():
midi_channels[msg.channel][track_no] = []
except AttributeError as E:
print(msg, E)
if msg.type == "program_change":
midi_channels[msg.channel][track_no].append(
("PgmC", msg.program, microseconds)
)
elif msg.type == "note_on" and msg.velocity != 0:
midi_channels[msg.channel][track_no].append(
("NoteS", msg.note, msg.velocity, microseconds)
)
elif (msg.type == "note_on" and msg.velocity == 0) or (
msg.type == "note_off"
):
midi_channels[msg.channel][track_no].append(
("NoteE", msg.note, microseconds)
)
"""整合后的音乐通道格式
每个通道包括若干消息元素其中逃不过这三种:
1 切换乐器消息
("PgmC", 切换后的乐器ID: int, 距离演奏开始的毫秒)
2 音符开始消息
("NoteS", 开始的音符ID, 力度(响度), 距离演奏开始的毫秒)
3 音符结束消息
("NoteE", 结束的音符ID, 距离演奏开始的毫秒)"""
del tempo, self.channels
self.channels = midi_channels
# [print([print(no,tno,sum([True if i[0] == 'NoteS' else False for i in track])) for tno,track in cna.items()]) if cna else False for no,cna in midi_channels.items()]
return midi_channels
def to_command_list_method1(
self,
scoreboard_name: str = "mscplay",
MaxVolume: float = 1.0,
speed: float = 1.0,
) -> list:
"""
使用Dislink Sforza的转换思路将midi转换为我的世界命令列表
:param scoreboard_name: 我的世界的计分板名称
:param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
:return: tuple(命令列表, 命令个数, 计分板最大值)
"""
# :param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
tracks = []
if speed == 0:
raise ZeroSpeedError("播放速度仅可为正实数")
if not self.midi:
raise MidiUnboundError(
"你是否正在使用的是一个由 copy_important 生成的MidiConvert对象这是不可复用的。"
)
MaxVolume = 1 if MaxVolume > 1 else (0.001 if MaxVolume <= 0 else MaxVolume)
commands = 0
maxscore = 0
tempo = mido.midifiles.midifiles.DEFAULT_TEMPO
# 分轨的思路其实并不好,但这个算法就是这样
# 所以我建议用第二个方法 _toCmdList_m2
for i, track in enumerate(self.midi.tracks):
ticks = 0
instrumentID = 0
singleTrack = []
for msg in track:
ticks += msg.time
if msg.is_meta:
if msg.type == "set_tempo":
tempo = msg.tempo
else:
if msg.type == "program_change":
instrumentID = msg.program
if msg.type == "note_on" and msg.velocity != 0:
nowscore = round(
(ticks * tempo)
/ ((self.midi.ticks_per_beat * float(speed)) * 50000)
)
maxscore = max(maxscore, nowscore)
if msg.channel == 9:
soundID, _X = perc_inst_to_soundID_withX(instrumentID)
else:
soundID, _X = inst_to_souldID_withX(instrumentID)
singleTrack.append(
"execute @a[scores={"
+ str(scoreboard_name)
+ "="
+ str(nowscore)
+ "}"
+ f"] ~ ~ ~ playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg.velocity / 128} "
f"{2 ** ((msg.note - 60 - _X) / 12)}"
)
commands += 1
if len(singleTrack) != 0:
tracks.append(singleTrack)
return [tracks, commands, maxscore]
def _toCmdList_m1(
self, scoreboardname: str = "mscplay", volume: float = 1.0, speed: float = 1.0
) -> list:
"""
使用Dislink Sforza的转换思路将midi转换为我的世界命令列表
:param scoreboardname: 我的世界的计分板名称
:param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
:param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
:return: tuple(命令列表, 命令个数, 计分板最大值)
"""
tracks = []
if volume > 1:
volume = 1
if volume <= 0:
volume = 0.001
commands = 0
maxscore = 0
for i, track in enumerate(self.midi.tracks): # type:ignore
ticks = 0
instrumentID = 0
singleTrack = []
for msg in track:
ticks += msg.time
# print(msg)
if msg.is_meta:
if msg.type == "set_tempo":
tempo = msg.tempo
else:
if msg.type == "program_change":
# print("TT")
instrumentID = msg.program
if msg.type == "note_on" and msg.velocity != 0:
nowscore = round(
(ticks * tempo) / ((self.midi.ticks_per_beat * float(speed)) * 50000) # type: ignore
)
maxscore = max(maxscore, nowscore)
soundID, _X = inst_to_souldID_withX(instrumentID)
singleTrack.append(
"execute @a[scores={"
+ str(scoreboardname)
+ "="
+ str(nowscore)
+ "}"
+ f"] ~ ~ ~ playsound {soundID} @s ~ ~{1 / volume - 1} ~ {msg.velocity * (0.7 if msg.channel == 0 else 0.9)} {2 ** ((msg.note - 60 - _X) / 12)}"
)
commands += 1
if len(singleTrack) != 0:
tracks.append(singleTrack)
return [tracks, commands, maxscore]
# 原本这个算法的转换效果应该和上面的算法相似的
def _toCmdList_m2(
self,
scoreboard_name: str = "mscplay",
MaxVolume: float = 1.0,
speed: float = 1.0,
) -> tuple:
"""
使用神羽和金羿的转换思路将midi转换为我的世界命令列表
:param scoreboard_name: 我的世界的计分板名称
:param MaxVolume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
:param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
:return: tuple(命令列表, 命令个数, 计分板最大值)
"""
if speed == 0:
raise ZeroSpeedError("播放速度仅可为正实数")
MaxVolume = 1 if MaxVolume > 1 else (0.001 if MaxVolume <= 0 else MaxVolume)
tracks = []
cmdAmount = 0
maxScore = 0
InstID = -1
self.to_music_channels()
# 此处 我们把通道视为音轨
for i in self.channels.keys():
# 如果当前通道为空 则跳过
if not self.channels[i]:
continue
if i == 9:
SpecialBits = True
else:
SpecialBits = False
nowTrack = []
for track_no, track in self.channels[i].items(): # type: ignore
for msg in track:
if msg[0] == "PgmC":
InstID = msg[1]
elif msg[0] == "NoteS":
soundID, _X = (
perc_inst_to_soundID_withX(msg[1])
if SpecialBits
else inst_to_souldID_withX(InstID)
)
score_now = round(msg[-1] / float(speed) / 50)
maxScore = max(maxScore, score_now)
nowTrack.append(
self.execute_cmd_head.format(
"@a[scores=({}={})]".format(scoreboard_name, score_now)
.replace("(", r"{")
.replace(")", r"}")
)
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
)
cmdAmount += 1
if nowTrack:
tracks.append(nowTrack)
return tracks, cmdAmount, maxScore
def _toCmdList_withDelay_m1(
self,
MaxVolume: float = 1.0,
speed: float = 1.0,
player: str = "@a",
) -> list:
"""
使用Dislink Sforza的转换思路将midi转换为我的世界命令列表并输出每个音符之后的延迟
:param MaxVolume: 最大播放音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
:param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
:param player: 玩家选择器,默认为`@a`
:return: 全部指令列表[ ( str指令, int距离上一个指令的延迟 ),...]
"""
tracks = {}
if speed == 0:
raise ZeroSpeedError("播放速度仅可为正实数")
if not self.midi:
raise MidiUnboundError(
"你是否正在使用的是一个由 copy_important 生成的MidiConvert对象这是不可复用的。"
)
MaxVolume = 1 if MaxVolume > 1 else (0.001 if MaxVolume <= 0 else MaxVolume)
tempo = mido.midifiles.midifiles.DEFAULT_TEMPO
for i, track in enumerate(self.midi.tracks):
instrumentID = 0
ticks = 0
for msg in track:
ticks += msg.time
if msg.is_meta:
if msg.type == "set_tempo":
tempo = msg.tempo
else:
if msg.type == "program_change":
instrumentID = msg.program
if msg.type == "note_on" and msg.velocity != 0:
now_tick = round(
(ticks * tempo)
/ ((self.midi.ticks_per_beat * float(speed)) * 50000)
)
soundID, _X = inst_to_souldID_withX(instrumentID)
try:
tracks[now_tick].append(
self.execute_cmd_head.format(player)
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg.velocity / 128} "
f"{2 ** ((msg.note - 60 - _X) / 12)}"
)
except KeyError:
tracks[now_tick] = [
self.execute_cmd_head.format(player)
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg.velocity / 128} "
f"{2 ** ((msg.note - 60 - _X) / 12)}"
]
results = []
all_ticks = list(tracks.keys())
all_ticks.sort()
for i in range(len(all_ticks)):
if i != 0:
for j in range(len(tracks[all_ticks[i]])):
if j != 0:
results.append((tracks[all_ticks[i]][j], 0))
else:
results.append(
(tracks[all_ticks[i]][j], all_ticks[i] - all_ticks[i - 1])
)
else:
for j in range(len(tracks[all_ticks[i]])):
results.append((tracks[all_ticks[i]][j], all_ticks[i]))
return [results, max(all_ticks)]
def _toCmdList_withDelay_m2(
self,
MaxVolume: float = 1.0,
speed: float = 1.0,
player: str = "@a",
) -> list:
"""
使用神羽和金羿的转换思路将midi转换为我的世界命令列表并输出每个音符之后的延迟
:param MaxVolume: 最大播放音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
:param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
:param player: 玩家选择器,默认为`@a`
:return: 全部指令列表[ ( str指令, int距离上一个指令的延迟 ),...]
"""
tracks = {}
if speed == 0:
raise ZeroSpeedError("播放速度仅可为正实数")
MaxVolume = 1 if MaxVolume > 1 else (0.001 if MaxVolume <= 0 else MaxVolume)
InstID = -1
self.to_music_channels()
results = []
for i in self.channels.keys():
# 如果当前通道为空 则跳过
if not self.channels[i]:
continue
if i == 9:
SpecialBits = True
else:
SpecialBits = False
for track_no, track in self.channels[i].items(): # type: ignore
for msg in track:
if msg[0] == "PgmC":
InstID = msg[1]
elif msg[0] == "NoteS":
soundID, _X = (
perc_inst_to_soundID_withX(msg[1])
if SpecialBits
else inst_to_souldID_withX(InstID)
)
score_now = round(msg[-1] / float(speed) / 50)
try:
tracks[score_now].append(
self.execute_cmd_head.format(player)
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
)
except KeyError:
tracks[score_now] = [
self.execute_cmd_head.format(player)
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
]
all_ticks = list(tracks.keys())
all_ticks.sort()
for i in range(len(all_ticks)):
for j in range(len(tracks[all_ticks[i]])):
results.append(
(
tracks[all_ticks[i]][j],
(
0
if j != 0
else (
all_ticks[i] - all_ticks[i - 1]
if i != 0
else all_ticks[i]
)
),
)
)
return [results, max(all_ticks)]

361
Musicreater/subclass.py Normal file
View File

@@ -0,0 +1,361 @@
# -*- coding: utf-8 -*-
"""
存储许多非主要的相关类
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from dataclasses import dataclass
from typing import Optional
from .constants import PERCUSSION_INSTRUMENT_LIST
from .utils import inst_to_souldID_withX, perc_inst_to_soundID_withX, volume2distance
@dataclass(init=False)
class SingleNote:
"""存储单个音符的类"""
instrument: int
"""乐器编号"""
note: int
"""音符编号"""
velocity: int
"""力度/响度"""
start_time: int
"""开始之时 ms"""
duration: int
"""音符持续时间 ms"""
track_no: int
"""音符所处的音轨"""
percussive: bool
"""是否为打击乐器"""
def __init__(
self,
instrument: int,
pitch: int,
velocity: int,
startime: int,
lastime: int,
track_number: int = 0,
is_percussion: Optional[bool] = None,
):
"""用于存储单个音符的类
:param instrument 乐器编号
:param pitch 音符编号
:param velocity 力度/响度
:param startTime 开始之时(ms)
注:此处的时间是用从乐曲开始到当前的毫秒数
:param lastTime 音符延续时间(ms)"""
self.instrument: int = instrument
"""乐器编号"""
self.note: int = pitch
"""音符编号"""
self.velocity: int = velocity
"""力度/响度"""
self.start_time: int = startime
"""开始之时 ms"""
self.duration: int = lastime
"""音符持续时间 ms"""
self.track_no: int = track_number
"""音符所处的音轨"""
self.percussive = (
(is_percussion in PERCUSSION_INSTRUMENT_LIST)
if (is_percussion is None)
else is_percussion
)
"""是否为打击乐器"""
@property
def inst(self) -> int:
"""乐器编号"""
return self.instrument
@inst.setter
def inst(self, inst_: int):
self.instrument = inst_
@property
def pitch(self) -> int:
"""音符编号"""
return self.note
@property
def get_mc_pitch(self) -> float:
self.mc_sound_ID, _X = (
perc_inst_to_soundID_withX(self.inst)
if self.percussive
else inst_to_souldID_withX(self.inst)
)
return -1 if self.percussive else 2 ** ((self.note - 60 - _X) / 12)
def __str__(self, is_track: bool = False):
return "{}Note(Instrument = {}, {}Velocity = {}, StartTime = {}, Duration = {}{})".format(
"Percussive" if self.percussive else "",
self.inst,
"" if self.percussive else "Pitch = {}, ".format(self.pitch),
self.start_time,
self.duration,
", Track = {}".format(self.track_no) if is_track else "",
)
def __tuple__(self):
return (
(
self.percussive,
self.inst,
self.velocity,
self.start_time,
self.duration,
self.track_no,
)
if self.percussive
else (
self.percussive,
self.inst,
self.note,
self.velocity,
self.start_time,
self.duration,
self.track_no,
)
)
def __dict__(self):
return (
{
"Percussive": self.percussive,
"Instrument": self.inst,
"Velocity": self.velocity,
"StartTime": self.start_time,
"Duration": self.duration,
"Track": self.track_no,
}
if self.percussive
else {
"Percussive": self.percussive,
"Instrument": self.inst,
"Pitch": self.note,
"Velocity": self.velocity,
"StartTime": self.start_time,
"Duration": self.duration,
"Track": self.track_no,
}
)
def __eq__(self, other) -> bool:
if not isinstance(other, self.__class__):
return False
return self.__str__() == other.__str__()
def to_command(self, volume_percentage: float = 1) -> str:
"""
将音符转为播放的指令
:param volume_percentage:int 音量占比(0,1]
:return str指令
"""
self.mc_sound_ID, _X = (
perc_inst_to_soundID_withX(self.inst)
if self.percussive
else inst_to_souldID_withX(self.inst)
)
# delaytime_now = round(self.start_time / float(speed) / 50)
self.mc_pitch = "" if self.percussive else 2 ** ((self.note - 60 - _X) / 12)
self.mc_distance_volume = volume2distance(self.velocity * volume_percentage)
return "playsound {} @s ^ ^ ^{} {} {}".format(
self.mc_sound_ID,
self.mc_distance_volume,
volume_percentage,
self.mc_pitch,
)
@dataclass(init=False)
class SingleCommand:
"""存储单个指令的类"""
command_text: str
"""指令文本"""
conditional: bool
"""执行是否有条件"""
delay: int
"""执行的延迟"""
annotation_text: str
"""指令注释"""
def __init__(
self,
command: str,
condition: bool = False,
tick_delay: int = 0,
annotation: str = "",
):
"""
存储单个指令的类
Parameters
----------
command: str
指令
condition: bool
是否有条件
tick_delay: int
执行延时
annotation: str
注释
"""
self.command_text = command
self.conditional = condition
self.delay = tick_delay
self.annotation_text = annotation
def copy(self):
return SingleCommand(
command=self.command_text,
condition=self.conditional,
tick_delay=self.delay,
annotation=self.annotation_text,
)
@property
def cmd(self) -> str:
"""
我的世界函数字符串(包含注释)
"""
return self.__str__()
def __str__(self) -> str:
"""
转为我的世界函数文件格式(包含注释)
"""
return "#[{cdt}]<{delay}> {ant}\n{cmd}".format(
cdt="CDT" if self.conditional else "",
delay=self.delay,
ant=self.annotation_text,
cmd=self.command_text,
)
def __eq__(self, other) -> bool:
if not isinstance(other, self.__class__):
return False
return self.__str__() == other.__str__()
@dataclass(init=False)
class SingleNoteBox:
"""存储单个音符盒"""
instrument_block: str
"""乐器方块"""
note_value: int
"""音符盒音高"""
annotation_text: str
"""音符注释"""
is_percussion: bool
"""是否为打击乐器"""
def __init__(
self,
instrument_block_: str,
note_value_: int,
percussion: Optional[bool] = None,
annotation: str = "",
):
"""用于存储单个音符盒的类
:param instrument_block_ 音符盒演奏所使用的乐器方块
:param note_value_ 音符盒的演奏音高
:param percussion 此音符盒乐器是否作为打击乐处理
注:若为空,则自动识别是否为打击乐器
:param annotation 音符注释"""
self.instrument_block = instrument_block_
"""乐器方块"""
self.note_value = note_value_
"""音符盒音高"""
self.annotation_text = annotation
"""音符注释"""
if percussion is None:
self.is_percussion = percussion in PERCUSSION_INSTRUMENT_LIST
else:
self.is_percussion = percussion
@property
def inst(self) -> str:
"""获取音符盒下的乐器方块"""
return self.instrument_block
@inst.setter
def inst(self, inst_):
self.instrument_block = inst_
@property
def note(self) -> int:
"""获取音符盒音调特殊值"""
return self.note_value
@note.setter
def note(self, note_):
self.note_value = note_
@property
def annotation(self) -> str:
"""获取音符盒的备注"""
return self.annotation_text
@annotation.setter
def annotation(self, annotation_):
self.annotation_text = annotation_
def copy(self):
return SingleNoteBox(
instrument_block_=self.instrument_block,
note_value_=self.note_value,
annotation=self.annotation_text,
)
def __str__(self) -> str:
return f"Note(inst = {self.inst}, note = {self.note}, )"
def __tuple__(self) -> tuple:
return self.inst, self.note, self.annotation
def __dict__(self) -> dict:
return {
"inst": self.inst,
"note": self.note,
"annotation": self.annotation,
}
def __eq__(self, other) -> bool:
if not isinstance(other, self.__class__):
return False
return self.__str__() == other.__str__()

64
Musicreater/types.py Normal file
View File

@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
"""
存放数据类型的定义
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
import mido
from .subclass import SingleNote
ProgressStyle = Tuple[str, Tuple[str, str]]
"""
进度条样式类型
"""
VoidMido = Union[mido.MidiFile, None] # void mido
"""
空Midi类类型
"""
NoteChannelType = Dict[
int,
List[SingleNote,],
]
"""
频道信息类型
Dict[int,Dict[int,List[SingleNote,],],]
"""
ChannelType = Dict[
int,
Dict[
int,
List[
Union[
Tuple[Literal["PgmC"], int, int],
Tuple[Literal["NoteS"], int, int, int],
Tuple[Literal["NoteE"], int, int],
]
],
],
]
"""
以字典所标记的频道信息类型(已弃用)
Dict[int,Dict[int,List[Union[Tuple[Literal["PgmC"], int, int],Tuple[Literal["NoteS"], int, int, int],Tuple[Literal["NoteE"], int, int],]],],]
"""

119
Musicreater/utils.py Normal file
View File

@@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
"""
存放主程序所必须的功能性内容
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import math
from .constants import PERCUSSION_INSTRUMENT_TABLE, PITCHED_INSTRUMENT_TABLE
from typing import Any, Dict, Tuple
def mctick2timestr(mc_tick: int) -> str:
"""
将《我的世界》的游戏刻计转为表示时间的字符串
"""
return str(int(int(mc_tick / 20) / 60)) + ":" + str(int(int(mc_tick / 20) % 60))
def empty_midi_channels(channel_count: int = 17, staff: Any = {}) -> Dict[int, Any]:
"""
空MIDI通道字典
"""
return dict(
(
i,
(staff.copy() if isinstance(staff, (dict, list)) else staff),
) # 这告诉我们你不能忽略任何一个复制的序列因为它真的我哭死折磨我一整天全在这个bug上了
for i in range(channel_count)
)
def inst_to_souldID_withX(
instrumentID: int,
) -> Tuple[str, int]:
"""
返回midi的乐器ID对应的我的世界乐器名对于音域转换算法如下
2**( ( msg.note - 60 - X ) / 12 ) 即为MC的音高其中
X的取值随乐器不同而变化
竖琴harp、电钢琴pling、班卓琴banjo、方波bit、颤音琴iron_xylophone 的时候为6
吉他的时候为7
贝斯bass、迪吉里杜管didgeridoo的时候为8
长笛flute、牛铃cou_bell的时候为5
钟琴bell、管钟chime、木琴xylophone的时候为4
而存在一些打击乐器bd(basedrum)、hat、snare没有音域则没有X那么我们返回7即可
Parameters
----------
instrumentID: int
midi的乐器ID
Returns
-------
tuple(str我的世界乐器名, int转换算法中的X)
"""
try:
return PITCHED_INSTRUMENT_TABLE[instrumentID]
except KeyError:
return "note.flute", 5
def perc_inst_to_soundID_withX(instrumentID: int) -> Tuple[str, int]:
"""
对于Midi第10通道所对应的打击乐器返回我的世界乐器名
Parameters
----------
instrumentID: int
midi的乐器ID
Returns
-------
tuple(str我的世界乐器名, int转换算法中的X)
"""
try:
return PERCUSSION_INSTRUMENT_TABLE[instrumentID]
except KeyError:
return "note.bd", 7
# 明明已经走了
# 凭什么还要在我心里留下缠绵缱绻
def volume2distance(vol: float) -> float:
"""
midi力度值拟合成的距离函数
Parameters
----------
vol: int
midi音符力度值
Returns
-------
float播放中心到玩家的距离
"""
return (
-8.081720684086314
* math.log(
vol + 14.579508825070013,
)
+ 37.65806375944386
if vol < 60.64
else 0.2721359356095803 * ((vol + 2592.272889454798) ** 1.358571233418649)
+ -6.313841334963396 * (vol + 2592.272889454798)
+ 4558.496367823575
)

136
README.md
View File

@@ -1,49 +1,127 @@
# 音·创 Musicreater
<h1 align="center">
· Musicreater
</h1>
### 介绍
·(Musicreater)是由金羿(W-YI)开发的一款我的世界基岩版音乐生成辅助软件
<p align="center">
<img width="128" height="128" src="https://gitee.com/TriM-Organization/Musicreater/raw/master/resources/msctIcon.ico">
</img>
</p>
欢迎加群861684859
<h3 align="center">一款免费开源的我的世界数字音频转换库</h3>
### 软件架构
<p align="center">
<img src="https://img.shields.io/badge/BUILD%20WITH%20LOVE-FF3432?style=for-the-badge">
<a href='https://gitee.com/TriM-Organization/Musicreater'>
<img align="right" src='https://gitee.com/TriM-Organization/Musicreater/widgets/widget_1.svg' alt='Fork me on Gitee'>
</img>
</a>
<p>
软件采用Python作为第一语言目前还没有使用其他语言辅助使用BeeWare作为图形库兼容安卓
[![][Bilibili: 金羿ELS]](https://space.bilibili.com/397369002/)
[![][Bilibili: 诸葛亮与八卦阵]](https://space.bilibili.com/604072474)
[![CodeStyle: black]](https://github.com/psf/black)
[![][python]](https://www.python.org/)
[![][license]](LICENSE)
[![][release]](../../releases)
尽量全平台支持
[![GiteeStar](https://gitee.com/TriM-Organization/Musicreater/badge/star.svg?theme=gray)](https://gitee.com/TriM-Organization/Musicreater/stargazers)
[![GiteeFork](https://gitee.com/TriM-Organization/Musicreater/badge/fork.svg?theme=gray)](https://gitee.com/TriM-Organization/Musicreater/members)
[![GitHub Repo stars](https://img.shields.io/github/stars/TriM-Organization/Musicreater?color=white&logo=GitHub&style=plastic)](https://github.com/TriM-Organization/Musicreater/stargazers)
[![GitHub Repo Forks](https://img.shields.io/github/forks/TriM-Organization/Musicreater?color=white&logo=GitHub&style=plastic)](https://github.com/TriM-Organization/Musicreater/forks)
简体中文 🇨🇳 | [English🇬🇧](README_EN.md)
### 安装教程
## 介绍 🚀
#### Windows
· 是一个免费开源的针对 **我的世界** MIDI 音乐转换库
即将到来
欢迎加群[861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)
#### Linux
## 安装 🔳
即将到来
- 使用 pypi
#### Android
```bash
pip install --upgrade Musicreater
```
即将到来
- 如果无法更新最新可以尝试
```bash
pip install --upgrade -i https://pypi.python.org/simple Musicreater
```
### 使用说明
- 克隆仓库并安装**不推荐**
```bash
git clone https://gitee.com/TriM-Organization/Musicreater.git
cd Musicreater
python setup.py install
```
1. 直接运行就好
2. 有不懂的问题来群里问
3. 请理解英文表述
以上命令中 `python``pip` 请依照各个环境不同灵活更换可能为`python3``pip3`之类
### 致谢
## 文档 📄
1. 感谢由 [Fuckcraft](https://github.com/fuckcraft) 鸣凤鸽子 带来的我的世界websocket服务器功能
2. 感谢 昀梦QQ1515399885 找出指令生成错误bug并指正
3. 感谢由 Charlie_Ping 查理平 带来的bdx转换功能
4. 感谢由 CMA_2401PT 提供的 BDXWorkShop作为.bdx结构的操作指导
5. 感谢广大群友为此程序提供的测试等支持
6. 若您为我找出了错误但您的名字没有显示在此列表中请联系我
[生成文件的使用](./docs/%E7%94%9F%E6%88%90%E6%96%87%E4%BB%B6%E7%9A%84%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.md)
[仓库 API 文档](./docs/%E5%BA%93%E7%9A%84%E7%94%9F%E6%88%90%E4%B8%8E%E5%8A%9F%E8%83%BD%E6%96%87%E6%A1%A3.md)
### 作者<金羿>联系方式
## 作者
1. QQ 2647547478
2. 电邮 EillesWan2006@163.com W-YI_DoctorYI@outlook.com
3. 微信 WYI_DoctorYI
**金羿 Eilles**我的世界基岩版指令师个人开发者B 站不知名 UP 江西在校高中生
**诸葛亮与八卦阵 bgArray**我的世界基岩版玩家喜欢编程和音乐深圳初二学生
**偷吃不是Touch Touch**我的世界基岩版指令师提供 BDX 导入测试支持
## 致谢 🙏
本致谢列表排名无顺序
- 感谢 **昀梦**\<QQ1515399885\> 找出指令生成错误 bug 并指正
- 感谢由 **Charlie_Ping 查理平** 带来的 BDX 文件转换参考以及 MIDI-我的世界对应乐器 参考表格
- 感谢由 **[CMA_2401PT](https://github.com/CMA2401PT)** 为我们的软件开发的一些方面进行指导同时我们参考了他的 BDXworkshop 作为 BDX 结构编辑的参考
- 感谢由 **[Dislink Sforza](https://github.com/Dislink) 断联·斯福尔扎**\<QQ1600515314\> 带来的 midi 音色解析以及转换指令的算法我们将其改编并应用同时感谢他的[网页版转换器](https://dislink.github.io/midi2bdx/)给我们的开发与更新带来巨大的压力和动力让我们在原本一骑绝尘的摸鱼道路上转向开发
- 感谢 **Mono**\<QQ738893087\> 反馈安装时的问题辅助我们找到了视窗操作系统下的兼容性问题感谢其反馈延迟播放器出现的重大问题让我们得以修改全部延迟播放错误尤其感谢他对于我们的软件的大力宣传
- 感谢 **Ammelia 艾米利亚**\<QQ2838334637\> 敦促我们进行新的功能开发并为新功能提出了非常优秀的大量建议以及提供的 BDX 导入测试支持为我们的新结构生成算法提供了大量的实际理论支持
- 感谢 **[神羽](https://gitee.com/snowykami) [SnowyKami](https://github.com/snowyfirefly)** 对我们项目的支持与宣传非常感谢他为我们提供的服务器
- 感谢 **指令师\_苦力怕 playjuice123**\<QQ240667197\> 为我们的程序找出错误并提醒我们修复一个一直存在的大 bug
- 感谢 **雷霆**\<QQ3555268519\> 用他那令所有开发者都大为光火的操作方法为我们的程序找出错误并提醒修复 bug
- 感谢 **小埋**\<QQ2039310975\> 反馈附加包生成时缺少描述和标题的问题
- <table><tr><td>感谢 **油炸**\<QQ2836146704\> 激励我们不断开发新的内容</td><td><img height="50" src="https://foruda.gitee.com/images/1695478907647543027/08ea9909_9911226.jpeg"></td></tr></table>
> 感谢广大群友为此库提供的测试和建议等
>
> 若您对我们有所贡献但您的名字没有出现在此列表中请联系我们
## 联系 📞
若遇到库中的问题欢迎在[](https://gitee.com/TriM-Organization/Musicreater/issues/new)提出你的 issue
如果需要与开发组进行交流欢迎加入我们的[开发闲聊 Q ](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)
亦可以联系我们[睿乐组织官方邮箱](mailto:TriM-Organization@hotmail.com)取得进一步联系
---
此项目并非一个官方 我的世界_Minecraft_项目
此项目不隶属或关联于 Mojang Studios 微软
此项目亦不隶属或关联于 网易
Minecraft Mojang Synergies AB 的商标此项目中所有对于我的世界Minecraft等相关称呼均为引用性使用
- 上文提及的 网易 公司指代的是在中国大陆运营我的世界中国版的上海网之易网络科技发展有限公司
NOT AN OFFICIAL MINECRAFT PRODUCT.
NOT APPROVED BY OR ASSOCIATED WITH MOJANG OR MICROSOFT.
NOT APPROVED BY OR ASSOCIATED WITH NETEASE.
[Bilibili: 金羿ELS]: https://img.shields.io/badge/Bilibili-%E5%87%8C%E4%BA%91%E9%87%91%E7%BE%BF-00A1E7?style=for-the-badge
[Bilibili: 诸葛亮与八卦阵]: https://img.shields.io/badge/Bilibili-%E8%AF%B8%E8%91%9B%E4%BA%AE%E4%B8%8E%E5%85%AB%E5%8D%A6%E9%98%B5-00A1E7?style=for-the-badge
[CodeStyle: black]: https://img.shields.io/badge/code%20style-black-121110.svg?style=for-the-badge
[python]: https://img.shields.io/badge/python-3.8-AB70FF?style=for-the-badge
[release]: https://img.shields.io/github/v/release/EillesWan/Musicreater?style=for-the-badge
[license]: https://img.shields.io/badge/Licence-Apache-228B22?style=for-the-badge

View File

@@ -1,12 +0,0 @@
Musicreater
===========
**This cross-platform app was generated by** `Briefcase`_ **- part of**
`The BeeWare Project`_. **If you want to see more tools like Briefcase, please
consider** `becoming a financial member of BeeWare`_.
音·创(Musicreater)是由金羿(W-YI)开发的一款《我的世界》基岩版音乐生成辅助软件
.. _`Briefcase`: https://github.com/beeware/briefcase
.. _`The BeeWare Project`: https://beeware.org/
.. _`becoming a financial member of BeeWare`: https://beeware.org/contributing/membership

130
README_EN.md Normal file
View File

@@ -0,0 +1,130 @@
<h1 align="center">
· Musicreater
</h1>
<p align="center">
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
</img>
</p>
<h3 align="center">A free open-source library of converting digital music files into <i>Minecraft</i> formats.</h3>
<p align="center">
<img src="https://img.shields.io/badge/BUILD%20WITH%20LOVE-FF3432?style=for-the-badge">
</img>
<p>
[![][Bilibili: Eilles]](https://space.bilibili.com/397369002/)
[![][Bilibili: bgArray]](https://space.bilibili.com/604072474)
[![CodeStyle: black]](https://github.com/psf/black)
[![][python]](https://www.python.org/)
[![][license]](LICENSE)
[![][release]](../../releases)
[![GiteeStar](https://gitee.com/TriM-Organization/Musicreater/badge/star.svg?theme=gray)](https://gitee.com/TriM-Organization/Musicreater/stargazers)
[![GiteeFork](https://gitee.com/TriM-Organization/Musicreater/badge/fork.svg?theme=gray)](https://gitee.com/TriM-Organization/Musicreater/members)
[![GitHub Repo stars](https://img.shields.io/github/stars/TriM-Organization/Musicreater?color=white&logo=GitHub&style=plastic)](https://github.com/TriM-Organization/Musicreater/stargazers)
[![GitHub Repo Forks](https://img.shields.io/github/forks/TriM-Organization/Musicreater?color=white&logo=GitHub&style=plastic)](https://github.com/TriM-Organization/Musicreater/forks)
[简体中文 🇨🇳](README.md) | English🇬🇧
**Notice that the localizations of documents may NOT be up-to-date.**
## Introduction🚀
Musicreater is a free open-source library used for converting digital music files into formats that could be read in _Minecraft_.
Welcome to join our QQ group: [861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)
## Installation 🔳
- Via pypi
```bash
pip install Musicreater --upgrade
```
- If above command cannot fetch latest version, try:
```bash
pip install -i https://pypi.python.org/simple Musicreater --upgrade
```
- Clone repo and Install (**NOT RECOMMANDED**):
```bash
git clone https://github.com/TriM-Organization/Musicreater.git
cd Musicreater
python setup.py install
```
Commands such as `python``pip` could be changed to some like `python3` or `pip3` according to the difference of platforms.
## Documentation 📄
(Not in English yet)
[生成文件的使用](./docs/%E7%94%9F%E6%88%90%E6%96%87%E4%BB%B6%E7%9A%84%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.md)
[仓库 API 文档](./docs/%E5%BA%93%E7%9A%84%E7%94%9F%E6%88%90%E4%B8%8E%E5%8A%9F%E8%83%BD%E6%96%87%E6%A1%A3.md)
### Authors ✒
**Eilles (金羿)**A senior high school student, individual developer, unfamous Bilibili UPer, which knows a little about commands in _Minecraft: Bedrock Edition_
**bgArray (诸葛亮与八卦阵)**: A junior high school student, player of _Minecraft: Bedrock Edition_, which is a fan of music and programming.
**Touch (偷吃不是Touch)**: A man who is used to use command(s) in _Minecraft: Bedrock Edition_, who supported us of debugging and testing program and algorithm
## Thanks 🙏
This list is not in any order.
- Thank _昀梦_\<QQ1515399885\> for finding and correcting the bugs in the commands that _Musicreater_ generated.
- Thank _Charlie_Ping 查理平_ for the bdx convert function for reference, and the reference chart that's used to convert the mid's instruments into Minecraft's instruments.
- Thank _[CMA_2401PT](https://github.com/CMA2401PT)_ for BDXWorkShop for reference of the .bdx structure's operation, and his guidance in some aspects of our development.
- Thank _[Dislink Sforza](https://github.com/Dislink) 断联·斯福尔扎_ \<QQ1600515314\> for his midi analysis algorithm brought to us, we had adapted it and made it applied in one of our working method; Also, thank him for the [WebConvertor](https://dislink.github.io/midi2bdx/) which brought us so much pressure and power to develop as well as update our projects better, instead of loaf on our project.
- Thank _Mono_\<QQ738893087\> for reporting problems while installing
- Thank _Ammelia 艾米利亚_\<QQ2838334637\> for urging us to develop new functions, and put forward a lot of excellent suggestions for new functions, as well as the BDX file's importing test support provided, which has given a lot of practical theoretical support for our new Structure Generating Algorithm
- Thank _[神羽](https://gitee.com/snowykami) [SnowyKami](https://github.com/snowyfirefly)_ for supporting and promoting our project, and also thanks him for his server which given us to use for free.
- Thank **指令师\_苦力怕 playjuice123**\<QQ240667197\> for finding bugs within our code, and noticed us to repair a big problem.
- Thank **雷霆**\<QQ3555268519\> for his annoying and provoking operations which may awake some problems within the program by chance and reminding us to repair.
- Thank **小埋**\<QQ2039310975\> for reporting the empty add-on packs title and description problem.
- <table><tr><td>Thank **油炸**\<QQ2836146704\> for inspiring us to constantly develop something new.</td><td><img width="260" src="https://foruda.gitee.com/images/1695478907647543027/08ea9909_9911226.jpeg" alt="The groupmate on the picture was saying that our convert-QQ-bot had once brought him great convinience but now it closed down by some reason so he was feeling regretful." title="&quot;It was once, a convert-QQ-bot is just in front my eyes&quot;&#10;&quot;Until lose, I finally know cannot chase back what I needs&quot;"></td><td><small>&quot;It was once, a convert-QQ-bot is just in front my eyes&quot;<br>&quot;Until lose, I finally know cannot chase back what I needs&quot;</small></td></tr></table>
> Thanks for the support and help of a lot of groupmates
>
> If you have given contributions but have not been in the list, please contact us!
## Contact Us 📞
Meet problems? Welcome to give out your issue [here](https://github.com/EillesWan/Musicreater/issues/new)!
Want to get in contact of developers? Welcome to join our [Chat QQ group](https://jq.qq.com/?_wv=1027&k=hpeRxrYr).
Or contact us via [TriM-Org Official Email](mailto:TriM-Organization@hotmail.com)!
---
NOT AN OFFICIAL MINECRAFT PRODUCT.
NOT APPROVED BY OR ASSOCIATED WITH MOJANG OR MICROSOFT.
NOT APPROVED BY OR ASSOCIATED WITH NETEASE.
此项目并非一个官方 我的世界_Minecraft_项目
此项目不隶属或关联于 Mojang Studios 微软
此项目亦不隶属或关联于 网易 相关
Minecraft Mojang Synergies AB 的商标此项目中所有对于我的世界Minecraft等相关称呼均为引用性使用
- 上文提及的 网易 公司指代的是在中国大陆运营我的世界中国版的上海网之易网络科技发展有限公司
[Bilibili: Eilles]: https://img.shields.io/badge/Bilibili-%E5%87%8C%E4%BA%91%E9%87%91%E7%BE%BF-00A1E7?style=for-the-badge
[Bilibili: bgArray]: https://img.shields.io/badge/Bilibili-%E8%AF%B8%E8%91%9B%E4%BA%AE%E4%B8%8E%E5%85%AB%E5%8D%A6%E9%98%B5-00A1E7?style=for-the-badge
[CodeStyle: black]: https://img.shields.io/badge/code%20style-black-121110.svg?style=for-the-badge
[python]: https://img.shields.io/badge/python-3.8-AB70FF?style=for-the-badge
[release]: https://img.shields.io/github/v/release/EillesWan/Musicreater?style=for-the-badge
[license]: https://img.shields.io/badge/Licence-Apache-228B22?style=for-the-badge

View File

@@ -1,52 +0,0 @@
# Musicreater
### Introduction
Musicreater(·) is an Eilles(*W-YI*)'s app that is used for creating musics in **Minecraft: Bedrock Edition**.
Welcome to join our QQ group: 861684859
### Framework
Use *Python* to develop, use *BeeWare* as a Windows Library.
We are trying to support every platform.
### Tutorials
#### Windows
Please wait for a while...
Comming soon
#### Linux
Please wait for a while...
Comming soon
#### Android
Please wait for a while...
Comming soon
### Instructions
1. Just make u understand the Chinese
2. If u dont understand, u can come to the QQ group or email me to ask questions
3. The English Edition is comming soon.
### Thanks
1. Thank [Fuckcraft](https://github.com/fuckcraft) 鸣凤鸽子and so on for the function of Creating the Websocket Server for Minecraft: Bedrock Edition.
2. Thank 昀梦QQ1515399885 for finding and correcting the bugs in the commands that *Musicreater* Created.
3. Thank Charlie_Ping 查理平 for bdx convert funtion.
4. Thank CMA_2401PT for BDXWorkShop as the .bdx structure's operation guide.
5. Thanks for a lot of groupmates who support me and help me to test the program.
6. If u have give me some help but u haven't been in the list, please contact me.
### Contact *Eilles(W-YI)*(金羿)
1. QQ 2647547478
2. E-mail EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com
3. WeChat WYI_DoctorYI

8
RSMC_test.py Normal file
View File

@@ -0,0 +1,8 @@
import Musicreater.experiment
print(
Musicreater.experiment.FutureMidiConvertRSNB.from_midi_file(
input("midi路径:"), old_exe_format=False
).to_note_list_in_delay()
)

View File

@@ -1 +0,0 @@
briefcase dev

View File

@@ -1,60 +0,0 @@
# -*- coding: utf-8 -*-
import os,shutil
from sys import platform
print("更新执行位置...")
if platform == 'win32':
try:
os.chdir(__file__[:len(__file__)-__file__[len(__file__)::-1].index('\\')]+'src\\')
print("更新执行位置,当前文件位置"+__file__)
except:
pass
else:
try:
os.chdir(__file__[:len(__file__)-__file__[len(__file__)::-1].index('/')]+'src/')
except:
pass
print("其他平台:"+platform+"更新执行位置,当前文件位置"+__file__)
print('完成!')
try:
import toga,amulet
except:
print("You'd better install the libraries of this app\nNow, we're helping you with this.")
from src.musicreater.msctspt.bugReporter import version
version.installLibraries(version)
if platform == 'win32':
os.system("python ./Musicreater.py")
elif platform == 'linux':
os.system("python3 ./Musicreater.py")
try:
if os.path.exists("./log/"):
shutil.rmtree("./log/")
if os.path.exists("./logs/"):
shutil.rmtree("./logs/")
if os.path.exists("./cache/"):
shutil.rmtree("./cache/")
except:
print("无法清除日志及临时文件")

22
clean_update.py Normal file
View File

@@ -0,0 +1,22 @@
import shutil
import os
from rich.console import Console
from rich.progress import track
console = Console()
def main():
with console.status("Find the full path of .egg-info folder"):
egg_info: list = []
for file in os.listdir():
if file.endswith(".egg-info"):
egg_info.append(file)
console.print(file)
for file in track(["build", "dist", "logs", *egg_info], description="Deleting files"):
if os.path.isdir(file) and os.access(file, os.W_OK):
shutil.rmtree(file)
if __name__ == "__main__":
main()

13
docs/API.md Normal file
View File

@@ -0,0 +1,13 @@
<h1 align="center">· Musicreater</h1>
<p align="center">
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
</p>
**此为开发相关文档内容包括库的简单调用所生成文件结构的详细说明特殊参数的详细解释**
# [main.py](../Musicreater/main.py)
## [类] MidiConvert
### [类函数] from_midi_file

View File

@@ -0,0 +1,307 @@
<h1 align="center">· Musicreater</h1>
<p align="center">
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
</p>
**此为开发相关文档内容包括库的简单调用所生成文件结构的详细说明特殊参数的详细解释**
# 库的简单调用
参见[example.py的相关部分](../example.py)使用此库进行MIDI转换非常简单
- 在导入转换库后使用 MidiConvert 类建立转换对象读取Midi文件
·创库支持新旧两种execute语法需要在对象实例化时指定
```python
# 导入音·创库
import Musicreater
# 指定是否使用旧的execute指令语法即1.18及以前的《我的世界:基岩版》语法)
old_execute_format = False
# 可以通过文件地址自动读取
cvt_mid = Musicreater.MidiConvert.from_midi_file(
"Midi文件地址",
old_exe_format=old_execute_format
)
# 也可以导入Mido对象
cvt_mid = Musicreater.MidiConvert(
mido.MidiFile("Midi文件地址"),
"音乐名称",
old_exe_format=old_execute_format
)
```
- 获取 Midi 音乐经转换后的播放指令
```python
# 通过函数 to_command_list_in_score, to_command_list_in_delay
# 分别可以得到
# 以计分板作为播放器的指令对象列表、以延迟作为播放器的指令对象列表
# 数据不仅存储在对象本身内,也会以返回值的形式返回,详见代码内文档
# 使用 to_command_list_in_score 函数进行转换之后,返回值有三个
# 值得注意的是第一个返回值返回的是依照midi频道存储的指令对象列表
# 也就是列表套列表
# 但是,在对象内部所存储的数据却不会如此嵌套
command_channel_list, command_count, max_score = cvt_mid.to_command_list_in_score(
"计分板名称",
1.0, # 音量比率
1.0, # 速度倍率
)
# 使用 to_command_list_in_delay 转换后的返回值只有两个
# 但是第一个返回值没有列表套列表
command_list, max_delay = cvt_mid.to_command_list_in_delay(
1.0, # 音量比率
1.0, # 速度倍率
"@a", # 玩家选择器
)
# 运行之后,指令和总延迟会存储至对象内
print(
"音乐长度:{}/游戏刻".format(
cvt_mid.music_tick_num
)
)
print(
"指令如下:\n{}".format(
cvt_mid.music_command_list
)
)
```
- 除了获取播放指令外还可以获取进度条指令
```python
# 通过函数 form_progress_bar 可以获得
# 以计分板为载体所生成的进度条的指令对象列表
# 数据不仅存储在对象本身内,也会以返回值的形式返回,详见代码内文档
# 使用 form_progress_bar 函数进行转换之后,返回值有三个
# 值得注意的是第一个返回值返回的是依照midi频道存储的指令对象列表
# 也就是列表套列表
cvt_mid.form_progress_bar(
max_score, # 音乐时长游戏刻
scoreboard_name, # 进度条使用的计分板名称
progressbar_style, # 进度条样式组(详见下方)
)
# 同上面生成播放指令的理,进度条指令也会存储至对象内
print(
"进度条指令如下:\n{}".format(
cvt_mid.progress_bar_command
)
)
```
在上面的代码中进度条样式是可以自定义的详见[下方说明](%E5%BA%93%E7%9A%84%E7%94%9F%E6%88%90%E4%B8%8E%E5%8A%9F%E8%83%BD%E6%96%87%E6%A1%A3.md#进度条自定义)。
- 转换成指令是一个方面接下来是再转换为可以导入MC的格式我们提供了 **·** 内置的附加组件可以借助 `MidiConvert` 对象转换为相应格式
```python
# 导入 Musicreater
import Musicreater
# 导入附加组件功能
import Musicreater.plugin
# 导入相应的文件格式转换功能
# 转换为函数附加包
import Musicreater.plugin.funpack
# 转换为 BDX 结构文件
import Musicreater.plugin.bdxfile
# 转换为 mcstructure 结构文件
import Musicreater.plugin.mcstructfile
# 转换为结构附加包
import Musicreater.plugin.mcstructpack
# 直接通过 websocket 功能播放(正在开发)
import Musicreater.plugin.websocket
# 定义转换参数
cvt_cfg = Musicreater.plugin.ConvertConfig(
output_path,
volumn, # 音量大小参数
speed, # 速度倍率
progressbar, # 进度条样式组(详见下方)
)
# 使用附加组件转换,其调用的函数应为:
# Musicreater.plugin.输出格式.播放器格式
# 值得注意的是,并非所有输出格式都支持所有播放器格式
# 调用的时候还请注意甄别
# 例如,以下函数是将 MidiConvert 对象 cvt_mid
# 以 cvt_cfg 指定的参数
# 以延迟播放器转换为 mcstructure 文件
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay(
cvt_mid,
cvt_cfg,
)
```
# 生成文件结构
## 名词解释
|名词|解释|备注|
|--------|-----------|----------|
|指令区|一个用于放置指令系统的区域通常是常加载区|常见于服务器指令系统好友联机房间中|
|指令链|与链式指令方块不同一个指令链通常指代的是一串由某种非链式指令方块作为开头后面连着一串链式指令方块的结构|通常的链都应用于需要单次激活而多指令的简单功能|
|起始块|链最初的那个非链式指令方块|此方块为脉冲方块或重复方块皆可|
|指令系统系统|指令系统通常指的是由一个或多个指令链以及相关红石机构相互配合一同组成的为达到某种特定的功能而构建的整体结构|通常的系统都应用于需要综合调配指令的复杂功能可由多个实现不同功能的模块构成不同系统之间可以相互调用各自的模块|
|游戏刻|游戏的一刻是指我的世界的游戏进程循环运行一次所占用的时间[详见我的世界中文维基](https://minecraft.fandom.com/zh/wiki/%E5%88%BB#%E6%B8%B8%E6%88%8F%E5%88%BB))。指令方块的延迟功能(即指令方块的“延迟刻数”设置项,此项的名称被误译为“已选中项的延迟”)的单位即为`1`游戏刻。|正常情况下,游戏固定以每秒钟 $20$ 刻的速率运行。但是,由于游戏内的绝大多数操作都是基于游戏进程循环而非现实中的时间来计时并进行的,一次游戏循环内也许会发生大量的操作,更多情况下,一秒对应的游戏刻会更少。|
|红石刻|一个红石刻代表了两个游戏刻[详见我的世界中文维基](https://minecraft.fandom.com/zh/wiki/%E5%88%BB#%E7%BA%A2%E7%9F%B3%E5%88%BB))。红石中继器会带来 $1$~$4$ 个红石刻的延迟,其默认的延迟时间为 $1$ 红石刻。|正常情况下,红石信号在一个红石电路中传输回存在 $\frac{1}{10}$ 秒左右的延迟。但是,同理于游戏刻,一秒对应的红石刻是不定的。|
## 播放器
**·**生成的文件可以采用多种方式播放一类播放方式我们称其为**播放器**例如**延迟播放器****计分板播放器**等等以后推出的新的播放器届时也会在此处更新
为什么要设计这么多播放器是为了适应不同的播放环境需要通常情况下一个音乐中含有多个音符音符与音符之间存在间隔这里就产生了不一样的实现音符间时间间隔的方式而不同的应用环境下又会产生不一样的要求接下来将对不同的播放器进行详细介绍
### 参数释义
|参数|说明|备注|
|--------|-----------|----------|
|`ScBd`|指定的计分板名称||
|`Tg`|播放对象|选择器或玩家名|
|`x`|音发出时对应的分数值||
|`InstID`|声音效果ID|不同的声音ID可以对应不同的乐器详见转换[乐器对照表](./%E8%BD%AC%E6%8D%A2%E4%B9%90%E5%99%A8%E5%AF%B9%E7%85%A7%E8%A1%A8.md)|
|`Ht`|播放点对玩家的距离|通过距离来表达声音的响度 $S$ 表示此参数`Ht`以Vol表示音量百分比则计算公式为 $S = \frac{1}{Vol}-1$ |
|`Vlct`|原生我的世界中规定的播放力度|这个参数是一个谜一样的存在似乎它的值毫不重要因为无论这个值是多少我们听起来都差不多当此音符所在MIDI通道为第一通道则这个值为 $0.7$ 倍MIDI指定力度其他则为 $0.9$ |
|`Ptc`|音符的音高|这是决定音调的参数 $P$ 表示此参数 $n$ 表示其在MIDI中的编号 $x$ 表示一定的音调偏移则计算公式为 $P = 2^\frac{n-60-x}{12}$之所以存在音调偏移是因为在我的世界不同的[乐器存在不同的音域](https://minecraft.fandom.com/zh/wiki/%E9%9F%B3%E7%AC%A6%E7%9B%92#%E4%B9%90%E5%99%A8),我们通过音调偏移来进行调整。|
### 播放器内容
1. 计分板播放器
计分板播放器是一种传统的我的世界音乐播放方式通过对于计分板加分来实现播放不同的音符一个很简单的原理就是**用不同的计分板分值对应不同的音符**再通过加分来达到那个分值即播放出来
**·**用来达到这种效果的指令是这样的
```mcfunction
execute @a[scores={ScBd=x}] ~ ~ ~ playsound InstID @s ^ ^ ^Ht Vlct Ptc
```
后四个参数决定了这个音的性质而前两个参数仅仅是为了决定音播放的时间
2. 延迟播放器
延迟播放器是通过我的世界游戏中指令方块的设置项延迟刻数来达到定位音符的效果**将所有的音符依照其播放时距离乐曲开始时的时间毫秒放在一个序列内再计算音符两两之间对应的时间差值转换为我的世界内对应的游戏刻数之后填入指令方块的设置中**
**·**由于此方式播放的音乐不需要用计分板所以播放指令是这样的
```mcfunction
execute Tg ~ ~ ~ playsound InstID @s ^ ^ ^Ht Vlct Ptc
```
其中后四个参数决定了这个音的性质
由于这样的延迟数据是依赖于指令方块的设置项所以使用这种播放器所转换出的结果仅可以存储在包含方块NBT信息及方块实体NBT信息的结构文件中或者直接输出至世界
3. 中继器播放器
中继器播放器是一种传统的我的世界红石音乐播放方式利用游戏内红石组件红石中继器以达到定位音符之用**但是中继器的延迟为1红石刻**
## 文件格式
1. 附加包格式`.mcpack`
使用附加包格式导出音乐若采用计分板 播放器则音乐会以指令函数文件`.mcfunction`存储于附加包内而若为延迟或中继器播放器则音乐回以结构文件`.mcstructure`存储在所生成的附加包中函数文件的存储结构应为
- `functions\`
- `index.mcfunction`
- `stop.mcfunction`
- `mscply\`
- `progressShow.mcfunction`
- `track1.mcfunction`
- `track2.mcfunction`
- ...
- `trackN.mcfunction`
- `structures\`
- `XXX_main.mcstructure`
- `XXX_start.mcstructure`
- `XXX_reset.mcstructure`
- `XXX_pgb.mcstructure`
如图其中`index.mcfunction`文件`stop.mcfunction`文件和`mscply`文件夹存在于函数目录的根下`mscply`目录中包含音乐导出的众多音轨播放文件`trackX.mcfunction`同时若使用计分板播放器生成此包时启用生成进度条则会包含`progressShow.mcfunction`文件若选择延迟或中继器播放器则会生成`structures`目录以及相关`.mcstructure`文件其中`mian`表示音乐播放用的主要结构`start`是用于初始化播放的部分仅包含一个指令方块即起始块`reset``pgb`仅在启用生成进度条时出现前者用于重置临时计分板后者用于显示进度条
`index.mcfunction`用于开始播放
1. 若为计分板播放器则其中包含打开各个音轨对应函数的指令以及加分指令这里的加分是将**播放计分板的值大于等于 $1$ 的所有玩家**的播放计分板分数增加 $1$同时若生成此包时选择了自动重置计分板的选项则会包含一条重置计分板的指令
2. 若为延迟或中继器播放器则其中的指令仅包含用以正确加载结构的`structure`指令
`stop.mcfunction`用于终止播放
1. 若为计分板播放器则其中包含将**全体玩家的播放计分板**重置的指令
2. 若为延迟或中继器播放器则其中包含**停用命令方块****启用命令方块**的指令~~然鹅实际上对于播放而言是一点用也没有~~
> 你知道吗·创的最早期版本我的世界函数音乐生成器正是用函数来播放不过这个版本采取的读入数据的形式大有不同
2. 生成结构的方式
无论是音·创生成的是何种结构`MCSTRUCTURE`还是`BDX`都会依照此处的格式来生成此处我们想说明的结构的格式不是结构文件存储的格式而是结构导出之后方块摆放的方式结构文件存储的格式这一点在各个我的世界开发的相关网站上都可能会有说明
考虑到进行我的世界游戏开发时为了节约常加载区域很多游戏会将指令区设立为一种层叠式的结构这种结构会限制每一层的指令系统的高度但是虽然长宽也是有限的却仍然比其纵轴延伸得更加自由
所以结构的生成形状依照给定的高度和内含指令的数量决定 $Z$ 轴延伸长度为指令方块数量对于给定高度之商的向下取整结果的平方根的向下取整用数学公式的方式表达则是
$$ MaxZ = \left\lfloor\sqrt{\left\lfloor{\frac{NoC}{MaxH}}\right\rfloor}\right\rfloor $$
其中$MaxZ$ 即生成结构的$Z$轴最大延伸长度$NoC$ 表示链结构中所含指令方块的个数$MaxH$ 表示给定的生成结构的最大高度
我们的结构生成器在生成指令链时将首先以相对坐标系 $(0, 0, 0)$ 即相对原点开始自下向上堆叠高度轴 $Y$ 的长当高度轴达到了限制的高度时便将 $Z$ 轴向正方向堆叠 $1$ 个方块并开始自上向下重新堆叠直至高度轴坐标达到相对为 $0$若当所生成结构的 $Z$ 轴长达到了其最大延伸长度则此结构生成器将反转 $Z$ 轴的堆叠方向直至 $Z$ 轴坐标相对为 $0$如此往复直至指令链堆叠完成
# 进度条自定义
因为我们提供了可以自动转换进度条的功能因此在这里给出进度条自定义参数的详细解释
一个进度条明显地**固定部分****可变部分**来构成而可变部分又包括了文字和图形两种当然我的世界里头的进度条可变的图形也就是那个这一点你需要了解因为后文中包含了很多这方面的概念需要你了解
进度条的自定义功能使用一个字符串来定义自己的样式其中包含众多**标识符**来表示可变部分
标识符如下注意大小写
| 标识符 | 指定的可变量 |
|---------|----------------|
| `%%N` | 乐曲名(即传入的文件名)|
| `%%s` | 当前计分板值 |
| `%^s` | 计分板最大值 |
| `%%t` | 当前播放时间 |
| `%^t` | 曲目总时长 |
| `%%%` | 当前进度比率 |
| `_` | 用以表示进度条占位|
表示进度条占位的 `_` 是用来标识你的进度条的也就是可变部分的唯一的图形部分
**样式定义字符串**的样例如下这也是默认的进度条的样式
` %%N [ %%s/%^s %%% __________ %%t|%^t]`
这是单独一行的进度条当然你也可以制作多行的如果是一行的输出时所使用的指令便是 `title`而如果是多行的话输出就会用 `titleraw` 作为进度条字幕
哦对了上面的只不过是样式定义同时还需要定义的是可变图形的部分也就是进度条上那个真正的
对于这个我们就采用了固定参数的方法对于一个进度条无非就是已经播放过的没播放过的两种形态所以使用一个元组来传入这两个参数就是最简单的了元组的格式也很简单`(str: 播放过的部分长啥样, str: 没播放过的部分长啥样)` 例如我们默认的进度的定义是这样的
`('§e=§r', '§7=§r')`
综合起来把这些参数传给函数需要一个参数整合你猜用的啥啊对对对我用的还是元组
我们的默认定义参数如下
`(r'%%N [ %%s/%^s %%% __________ %%t|%^t]',('§e=§r', '§7=§r'))`
*为了避免生成错误请尽量避免使用标识符作为定义样式字符串的其他部分*

View File

@@ -0,0 +1,56 @@
<h1 align="center">· Musicreater</h1>
<p align="center">
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
</p>
# 生成文件的使用
*这是本库所生成文件的使用声明不是使用本库的教程若要查看**本库的文档**可点击[此处](./%E5%BA%93%E7%9A%84%E7%94%9F%E6%88%90%E4%B8%8E%E5%8A%9F%E8%83%BD%E6%96%87%E6%A1%A3.md)若要查看有关文件结构的内容可以点击[此处](./%E7%94%9F%E6%88%90%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84%E8%AF%B4%E6%98%8E.md)*
## 附加包格式
支持的文件后缀`.MCPACK`
- 计分板播放器
1. 导入附加包
2. 在一个循环方块中输入指令 `function index`
3. 将需要聆听音乐的实体的播放所用计分板设置为 `1`
4. 激活循环方块
5. 若想要暂停播放可以停止循环指令方块的激活状态
6. 若想要重置某实体的播放可以将其播放用的计分板重置
7. 若要终止全部玩家的播放在聊天框输入指令 `function stop`
> 其中 步骤三 步骤四 的顺序可以调换
- 延迟播放器
1. 导入附加包
2. 在聊天框输入指令 `function index`
3. 同时激活所生成的循环和脉冲指令方块
4. 若要终止播放在聊天框输入指令 `function stop` 试试看不确保有用
> 需要注意的是循环指令方块需要一直激活直到音乐结束
## 结构格式
支持的文件后缀`.MCSTRUCTURE``.BDX`
1. 将结构导入世界
- 延迟播放器
2. 将结构生成的第一个指令方块之模式更改为**脉冲**
3. 激活脉冲方块
4. 若欲重置播放可以停止对此链的激活例如停止区块加载
5. 此播放器不支持暂停
- 计分板播放器
2. 在所生成的第一个指令方块前放置一个循环指令方块其朝向应当对着所生成的第一个方块
3. 在循环指令方块中输入使播放对象的播放用计分板加分的指令延迟为 `0`每次循环增加 `1`
4. 激活循环方块
5. 若想要暂停播放可以停止循环指令方块的激活状态
6. 若想要重置某实体的播放可以将其播放用的计分板重置

View File

@@ -0,0 +1,217 @@
<h1 align="center">· Musicreater</h1>
<p align="center">
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
</p>
# 转换乐器对照表
**_注意本文档中的对照表版权归属于音·创作者并按照本仓库根目录下 LICENSE.md 中规定开源_**
原表格请见[constant.py](../Musicreater/constants.py#176)
**_使用时请遵循协议规定_**
- 版权所有 © 2023 · 开发者
- Copyright © 2023 all the developers of Musicreater
* 开源相关声明请见 仓库根目录下的 License.md
* Terms & Conditions: License.md in the root directory
· 开发交流群 861684859\
Email TriM-Organization@hotmail.com\
若需转载或借鉴 许可声明请查看仓库根目录下的 License.md
### 名词解释
| 名词 | 说明 |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| 音符名称 | 我的世界游戏内用于播放音乐的 `playsound` 指令所规定的 `Sound ID` |
| 音调偏移参数 | 我的世界不同乐器的音域不同对应的 `pitch` 值也不尽相同该参数的解释请参考[文档说明](库的生成与功能文档.md#参数释义) |
# 乐音乐器
对照表版本2023 0527
| Midi 乐器值 | 音符名称 | 音调偏移参数 |
| ----------- | ------------------- | ------------ |
| 0 | note.harp | 6 |
| 1 | note.harp | 6 |
| 2 | note.pling | 6 |
| 3 | note.harp | 6 |
| 4 | note.pling | 6 |
| 5 | note.pling | 6 |
| 6 | note.harp | 6 |
| 7 | note.harp | 6 |
| 8 | note.share | 7 |
| 9 | note.harp | 6 |
| 10 | note.didgeridoo | 8 |
| 11 | note.harp | 6 |
| 12 | note.xylophone | 4 |
| 13 | note.chime | 4 |
| 14 | note.harp | 6 |
| 15 | note.harp | 6 |
| 16 | note.bass | 8 |
| 17 | note.harp | 6 |
| 18 | note.harp | 6 |
| 19 | note.harp | 6 |
| 20 | note.harp | 6 |
| 21 | note.harp | 6 |
| 22 | note.harp | 6 |
| 23 | note.guitar | 7 |
| 24 | note.guitar | 7 |
| 25 | note.guitar | 7 |
| 26 | note.guitar | 7 |
| 27 | note.guitar | 7 |
| 28 | note.guitar | 7 |
| 29 | note.guitar | 7 |
| 30 | note.guitar | 7 |
| 31 | note.bass | 8 |
| 32 | note.bass | 8 |
| 33 | note.bass | 8 |
| 34 | note.bass | 8 |
| 35 | note.bass | 8 |
| 36 | note.bass | 8 |
| 37 | note.bass | 8 |
| 38 | note.bass | 8 |
| 39 | note.bass | 8 |
| 40 | note.harp | 6 |
| 41 | note.harp | 6 |
| 42 | note.harp | 6 |
| 43 | note.harp | 6 |
| 44 | note.iron_xylophone | 6 |
| 45 | note.guitar | 7 |
| 46 | note.harp | 6 |
| 47 | note.harp | 6 |
| 48 | note.guitar | 7 |
| 49 | note.guitar | 7 |
| 50 | note.bit | 6 |
| 51 | note.bit | 6 |
| 52 | note.harp | 6 |
| 53 | note.harp | 6 |
| 54 | note.bit | 6 |
| 55 | note.flute | 5 |
| 56 | note.flute | 5 |
| 57 | note.flute | 5 |
| 58 | note.flute | 5 |
| 59 | note.flute | 5 |
| 60 | note.flute | 5 |
| 61 | note.flute | 5 |
| 62 | note.flute | 5 |
| 63 | note.flute | 5 |
| 64 | note.bit | 6 |
| 65 | note.bit | 6 |
| 66 | note.bit | 6 |
| 67 | note.bit | 6 |
| 68 | note.flute | 5 |
| 69 | note.harp | 6 |
| 70 | note.harp | 6 |
| 71 | note.flute | 5 |
| 72 | note.flute | 5 |
| 73 | note.flute | 5 |
| 74 | note.harp | 6 |
| 75 | note.flute | 5 |
| 76 | note.harp | 6 |
| 77 | note.harp | 6 |
| 78 | note.harp | 6 |
| 79 | note.harp | 6 |
| 80 | note.bit | 6 |
| 81 | note.bit | 6 |
| 82 | note.bit | 6 |
| 83 | note.bit | 6 |
| 84 | note.bit | 6 |
| 85 | note.bit | 6 |
| 86 | note.bit | 6 |
| 87 | note.bit | 6 |
| 88 | note.bit | 6 |
| 89 | note.bit | 6 |
| 90 | note.bit | 6 |
| 91 | note.bit | 6 |
| 92 | note.bit | 6 |
| 93 | note.bit | 6 |
| 94 | note.bit | 6 |
| 95 | note.bit | 6 |
| 96 | note.bit | 6 |
| 97 | note.bit | 6 |
| 98 | note.bit | 6 |
| 99 | note.bit | 6 |
| 100 | note.bit | 6 |
| 101 | note.bit | 6 |
| 102 | note.bit | 6 |
| 103 | note.bit | 6 |
| 104 | note.harp | 6 |
| 105 | note.banjo | 6 |
| 106 | note.harp | 6 |
| 107 | note.harp | 6 |
| 108 | note.harp | 6 |
| 109 | note.harp | 6 |
| 110 | note.harp | 6 |
| 111 | note.guitar | 7 |
| 112 | note.harp | 6 |
| 113 | note.bell | 4 |
| 114 | note.harp | 6 |
| 115 | note.cow_bell | 5 |
| 116 | note.bd | 7 |
| 117 | note.bass | 8 |
| 118 | note.bit | 6 |
| 119 | note.bd | 7 |
| 120 | note.guitar | 7 |
| 121 | note.harp | 6 |
| 122 | note.harp | 6 |
| 123 | note.harp | 6 |
| 124 | note.harp | 6 |
| 125 | note.hat | 7 |
| 126 | note.bd | 7 |
| 127 | note.snare | 7 |
# 打击乐器
| Midi 打击乐器值 | 音符名称 | 音调偏移参数 |
| --------------- | ------------------- | ------------ |
| 34 | note.bd | 7 |
| 35 | note.bd | 7 |
| 36 | note.hat | 7 |
| 37 | note.snare | 7 |
| 38 | note.snare | 7 |
| 39 | note.snare | 7 |
| 40 | note.hat | 7 |
| 41 | note.snare | 7 |
| 42 | note.hat | 7 |
| 43 | note.snare | 7 |
| 44 | note.snare | 7 |
| 45 | note.bell | 4 |
| 46 | note.snare | 7 |
| 47 | note.snare | 7 |
| 48 | note.bell | 4 |
| 49 | note.hat | 7 |
| 50 | note.bell | 4 |
| 51 | note.bell | 4 |
| 52 | note.bell | 4 |
| 53 | note.bell | 4 |
| 54 | note.bell | 4 |
| 55 | note.bell | 4 |
| 56 | note.snare | 7 |
| 57 | note.hat | 7 |
| 58 | note.chime | 4 |
| 59 | note.iron_xylophone | 6 |
| 60 | note.bd | 7 |
| 61 | note.bd | 7 |
| 62 | note.xylophone | 4 |
| 63 | note.xylophone | 4 |
| 64 | note.xylophone | 4 |
| 65 | note.hat | 7 |
| 66 | note.bell | 4 |
| 67 | note.bell | 4 |
| 68 | note.hat | 7 |
| 69 | note.hat | 7 |
| 70 | note.flute | 5 |
| 71 | note.flute | 5 |
| 72 | note.hat | 7 |
| 73 | note.hat | 7 |
| 74 | note.xylophone | 4 |
| 75 | note.hat | 7 |
| 76 | note.hat | 7 |
| 77 | note.xylophone | 4 |
| 78 | note.xylophone | 4 |
| 79 | note.bell | 4 |
| 80 | note.bell | 4 |

142
example.py Normal file
View File

@@ -0,0 +1,142 @@
# -*- coding: utf-8 -*-
# 伶伦 开发交流群 861684859
"""
音·创 (Musicreater) 演示程序
是一款免费开源的针对《我的世界》的midi音乐转换库
Musicreater (音·创)
A free open source library used for convert midi file into formats that is suitable for **Minecraft**.
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
开源相关声明请见 ./License.md
Terms & Conditions: ./License.md
"""
import os
import Musicreater
from Musicreater.plugin import ConvertConfig
from Musicreater.plugin.addonpack import (
to_addon_pack_in_delay,
to_addon_pack_in_repeater,
to_addon_pack_in_score,
)
from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score
# 获取midi列表
midi_path = input(f"请输入MIDI路径")
# 获取输出地址
out_path = input(f"请输入输出路径:")
# 选择输出格式
fileFormat = int(input(f"请输入输出格式[BDX(1) 或 MCPACK(0)]").lower())
playerFormat = int(input(f"请选择播放方式[红石(2) 或 计分板(1) 或 延迟(0)]").lower())
# 真假字符串判断
def bool_str(sth: str):
try:
return bool(float(sth))
except:
if str(sth).lower() in ("true", "", "", "y", "t"):
return True
elif str(sth).lower() in ("false", "", "", "f", "n"):
return False
else:
raise ValueError("布尔字符串啊?")
if os.path.exists("./demo_config.json"):
import json
prompts = json.load(open("./demo_config.json", "r", encoding="utf-8"))
else:
prompts = []
# 提示语 检测函数 错误提示语
for args in [
(
f"输入音量:",
float,
),
(
f"输入播放速度:",
float,
),
(
f"是否启用进度条:",
bool_str,
),
(
f"计分板名称:",
str,
)
if playerFormat == 1
else (
f"玩家选择器:",
str,
),
(
f"是否自动重置计分板:",
bool_str,
)
if playerFormat == 1
else (),
(
f"作者名称:",
str,
)
if fileFormat == 1
else (),
()
if playerFormat == 1
else (
f"最大结构高度:",
int,
),
]:
if args:
prompts.append(args[1](input(args[0])))
print(f"正在处理 {midi_path} ")
cvt_mid = Musicreater.MidiConvert.from_midi_file(midi_path, old_exe_format=False)
cvt_cfg = ConvertConfig(out_path, *prompts[:3])
if playerFormat == 1:
cvt_method = to_addon_pack_in_score
elif playerFormat == 0:
cvt_method = to_addon_pack_in_delay
elif playerFormat == 2:
cvt_method = to_addon_pack_in_repeater
print(
" 指令总长:{},最高延迟:{}".format(
*(cvt_method(cvt_mid, cvt_cfg, *prompts[3:])) # type: ignore
)
if fileFormat == 0
else " 指令总长:{},最高延迟:{},结构大小{},终点坐标{}".format(
*(
to_BDX_file_in_score(cvt_mid, cvt_cfg, *prompts[3:])
if playerFormat == 1
else to_BDX_file_in_delay(cvt_mid, cvt_cfg, *prompts[3:])
)
)
)
exitSth = input("回车退出").lower()
if exitSth == "record":
import json
with open("./demo_config.json", "w", encoding="utf-8") as f:
json.dump(prompts, f)
elif exitSth == "delrec":
os.remove("./demo_config.json")

13
example_futureFunction.py Normal file
View File

@@ -0,0 +1,13 @@
import Musicreater.experiment
import Musicreater.plugin
import Musicreater.plugin.mcstructfile
print(
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay(
Musicreater.experiment.FutureMidiConvertM4.from_midi_file(
input("midi路径:"), old_exe_format=False
),
Musicreater.plugin.ConvertConfig(input("输出路径:"), volume=1),
max_height=32,
)
)

11
example_mcstructure.py Normal file
View File

@@ -0,0 +1,11 @@
import Musicreater
import Musicreater.plugin
import Musicreater.plugin.mcstructfile
print(
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay(
Musicreater.MidiConvert.from_midi_file(input("midi路径:"), old_exe_format=False),
Musicreater.plugin.ConvertConfig(input("输出路径:"), volume=1),
max_height=32,
)
)

View File

@@ -0,0 +1,10 @@
import Musicreater
import Musicreater.plugin
import Musicreater.plugin.mcstructfile
print(
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_repeater(
Musicreater.MidiConvert.from_midi_file(input("midi路径:"), old_exe_format=False),
Musicreater.plugin.ConvertConfig(input("输出路径:"), volume=1),
)
)

View File

@@ -0,0 +1,12 @@
import Musicreater.experiment
import Musicreater.plugin
import Musicreater.plugin.mcstructfile
print(
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_repeater(
Musicreater.experiment.FutureMidiConvertM4.from_midi_file(
input("midi路径:"), old_exe_format=False
),
Musicreater.plugin.ConvertConfig(input("输出路径:"), volume=1),
)
)

View File

@@ -1,49 +0,0 @@
[tool.briefcase]
project_name = "Musicreater"
bundle = "com.ryoun.musicreater"
version = "0.0.1"
url = "https://musicreater.ryoun.com/musicreater"
license = "Apache Software License"
author = 'Eilles Wan'
author_email = "W-YI_DoctorYI@outlook.com"
[tool.briefcase.app.musicreater]
formal_name = "Musicreater"
description = "Musicreater is an Eilles's app that is used for creating musics in Minecraft: Bedrock Edition"
icon = "src/musicreater/resources/musicreater"
sources = ['src/musicreater']
requires = []
[tool.briefcase.app.musicreater.macOS]
requires = [
'toga-cocoa>=0.3.0.dev20',
]
[tool.briefcase.app.musicreater.linux]
requires = [
'toga-gtk>=0.3.0.dev20',
]
system_requires = [
'libgirepository1.0-dev',
'libcairo2-dev',
'libpango1.0-dev',
'libwebkitgtk-3.0-0',
'gir1.2-webkit-3.0',
]
[tool.briefcase.app.musicreater.windows]
requires = [
'toga-winforms>=0.3.0.dev20',
]
# Mobile deployments
[tool.briefcase.app.musicreater.iOS]
requires = [
'toga-iOS>=0.3.0.dev20',
]
[tool.briefcase.app.musicreater.android]
requires = [
'toga-android>=0.3.0.dev20',
]

1
requirements.txt Normal file
View File

@@ -0,0 +1 @@
mido>=1.3

View File

@@ -0,0 +1,21 @@
# 注意,这里是作者署名文件,文件格式开头为单子启
# 紧跟其后,不加空格留下常用名,常用名即常用网名
# 而在其后是各个语言下的名字。用 井字符 开头表示
# 注释,请注意,注释符号必须在一行之首否则无作用
# 每进行一次分段表示一个新的开发者,换行表示一个
# 新的语言。请全体开发者就此署名,谢谢!
启金羿
zh-CN 金羿
zh-TW 金羿
zh-ME 金羿羿喵
zh-HK 金 羿
en-GB Eilles Wan
en-US EillesWan
启诸葛亮与八卦阵
zh-CN 诸葛亮与八卦阵
zh-TW 諸葛亮與八卦陣
zh-ME 诸葛八卦喵
zh-HK 諸葛亮與八卦陣
en-GB Bagua Array
en-US bgArray

BIN
resources/msctIcon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
resources/msctIcon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 MiB

47
resources/poem.md Normal file
View File

@@ -0,0 +1,47 @@
> 是谁把科技的领域布满政治的火药
>
> 是谁把纯净的蓝天染上暗淡的沉灰
>
> 中国人民无不热爱自己伟大的祖国
>
> 我们不会忘记屈辱历史留下的惨痛
>
> 我们希望世界和平
>
> 我们希望获得世界的尊重
>
> 愿世上再也没有战争
>
> 无论是热还是冷
>
> 无论是经济还是政治
>
> 让美妙的和平的优雅的音乐响彻世界
>
> 金羿
> 2022 5 7
> Who has dropped political gunpowder into the technology
>
> Who has dyed clear blue sky into the dark grey
>
> All Chinese people love our great homeland
>
> We *WILL* remember the remain pain of the humiliating history
>
> We love the whole world but in peace
>
> We love everyone but under respect
>
> It is to be hoped that the war ends forever
>
> Whatever it is cold or hot
>
> Whatever it is economical or political
>
> Just let the wonderful music of peace surround the world
>
> ---- Eilles Wan
> 7/5 2022

View File

@@ -0,0 +1,42 @@
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
def q_function1(x, a, a2, c1,):
return a * np.log( x + a2,)+ c1
def q_function2(x, b, b2, b3, b4, c2):
return b * ((x + b2) ** b3) + b4 * (x+b2) + c2
x_data = np.array([0, 16, 32, 48, 64, 80, 96, 112, 128])
y_data = np.array([16, 10, 6.75, 4, 2.5, 1.6, 0.8, 0.3, 0])
p_est1, err_est1 = curve_fit(q_function1, x_data[:5], y_data[:5], maxfev=1000000)
p_est2, err_est2 = curve_fit(q_function2, x_data[4:], y_data[4:], maxfev=1000000)
print(q_function1(x_data[:5], *p_est1))
print(q_function2(x_data[4:], *p_est2))
print("参数一:",*p_est1)
print("参数二:",*p_est2)
# 绘制图像
plt.plot(
np.arange(0, 64.1, 0.1), q_function1(np.arange(0, 64.1, 0.1), *p_est1), label=r"FIT1"
)
plt.plot(
np.arange(64, 128.1, 0.1), q_function2(np.arange(64, 128.1, 0.1), *p_est2), label=r"FIT2"
)
plt.scatter(x_data, y_data, color="red") # 标记给定的点
# plt.xlabel('x')
# plt.ylabel('y')
plt.title("Function Fit")
plt.legend()
# plt.grid(True)
plt.show()

View File

@@ -0,0 +1,36 @@
import matplotlib.pyplot as plt
import numpy as np
# 定义对数函数
def q_function1(vol):
# return -23.65060754864053*((x+508.2130392724084)**0.8433764630986903) + 7.257078620637543 * (x+407.86870598508153) + 1585.6201108739122
# return -58.863374003875954 *((x+12.41481943150274 )**0.9973316187745871 ) +57.92341268595151 * (x+ 13.391132186222036) + -32.92986286030519
return -8.081720684086314 * np.log( vol + 14.579508825070013,)+ 37.65806375944386
def q_function2(vol):
return 0.2721359356095803 * ((vol + 2592.272889454798) ** 1.358571233418649) + -6.313841334963396 * (vol + 2592.272889454798) + 4558.496367823575
# 生成 x 值
x_values = np.linspace(0, 128, 1000)
x_data = np.array([0,16,32,48,64,80,96,112,128])
y_data = np.array([16, 10, 6.75, 4, 2.5, 1.6, 0.8, 0.3, 0])
print(q_function1(x_data))
print(q_function2(x_data))
# 绘制图像
plt.plot(x_values, q_function1(x_values,),label = "fit1")
plt.plot(x_values, q_function2(x_values,),label = "fit2")
plt.scatter(x_data, y_data, color='red') # 标记给定的点
# plt.scatter(x_data, y_data2, color='green') # 标记给定的点
# plt.scatter(x_data, y_data3, color='blue') # 标记给定的点
plt.xlabel('x')
plt.ylabel('y')
plt.title('Function')
plt.legend()
plt.grid(True)
plt.show()

BIN
resources/测试片段.mid Normal file

Binary file not shown.

BIN
resources/测试片段.mscz Normal file

Binary file not shown.

View File

@@ -0,0 +1,14 @@
音乐谱面文件
测试片段.mid、测试片段.mscz
皆系 音·创 库所用之连带软件
禁止在 音·创 库以外传播
不允许独立发行、独立副本
不允许未经允许的二次创作
不允许脱离于 音·创 独立存在
不得随任何其他非 音·创 软件连带发布
不得用于商业用途
若 音·创 库被用于商业用途,应当将其剔除
版权所有 © 2023 诸葛亮与八卦阵

38
setup.py Normal file
View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
import setuptools
import Musicreater
with open("requirements.txt", "r", encoding="utf-8") as fh:
dependences = fh.read().strip().split("\n")
with open("README_EN.md", "r", encoding="utf-8") as fh:
long_description = fh.read().replace(
"./docs/", "https://github.com/TriM-Organization/Musicreater/blob/master/docs/"
)
setuptools.setup(
name="Musicreater",
version=Musicreater.__version__,
author="Eilles Wan, bgArray",
author_email="TriM-Organization@hotmail.com",
description="一款免费开源的 《我的世界》 mid音乐转换库。\n"
"A free open-source python library used to convert midi into Minecraft.",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/TriM-Organization/Musicreater",
packages=setuptools.find_packages(),
classifiers=[
"Intended Audience :: Developers",
"Natural Language :: Chinese (Simplified)",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Topic :: Software Development :: Libraries",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
],
# 需要安装的依赖
install_requires=dependences,
)

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
briefcase

View File

@@ -1,10 +0,0 @@
Metadata-Version: 2.1
Briefcase-Version: 0.3.5
Name: musicreater
Formal-Name: Musicreater
App-ID: com.ryoun.musicreater.musicreater
Version: 0.0.1
Home-page: https://musicreater.ryoun.com/musicreater
Author: Eilles Wan
Author-email: W-YI_DoctorYI@outlook.com
Summary: Musicreater is an Eilles's app that is used for creating musics in Minecraft: Bedrock Edition

View File

@@ -1,364 +0,0 @@
# -*- coding: utf-8 -*-
# W-YI 金羿
# QQ 2647547478
# 音·创 开发交流群 861684859
# Email EillesWan2006@163.com W-YI_DoctorYI@outlook.com
# 版权所有 Team-Ryoun 金羿
# 若需转载或借鉴 请附作者
# 代码写的并非十分的漂亮还请大佬多多包涵本软件源代码依照Apache软件协议公开
import json
import os
import shutil
import threading
import sys
from musicreater.msctspt.threadOpera import NewThread
from musicreater.msctspt.bugReporter import version
from musicreater.nmcsup.log import log
__version__ = version.version[1]+version.version[0]
__author__ = 'W-YI (金羿)'
log("系统工作————————加载变量及函数")
print("更新执行位置...")
if sys.platform == 'win32':
os.chdir(__file__[:len(__file__)-__file__[len(__file__)::-1].index('\\')])
log("更新执行位置,当前文件位置"+__file__)
else:
try:
os.chdir(__file__[:len(__file__) -
__file__[len(__file__)::-1].index('/')])
except:
pass
log("其他平台:"+sys.platform+"更新执行位置,当前文件位置"+__file__)
print('完成!')
print('建立变量,存入内存,载入字典常量函数')
# 主体部分
# 支持多文件同时操作
# dataset[{ 'mainset':{ 'x':'y' }, 'musics': [ { 'set' :{ 'A':'B' } , 'note' : [ [ 'a' , b ], ] }, ] }, ]
# 编辑:
# 修改主设置: dataset[第几个项目]['mainset']['什么设置'] = '设置啥'
# 修改音乐: dataset[第几个项目]['musics'][第几个音轨]['notes'][第几个音符][音符还是时间01] = 改成啥
# 修改音轨设置: dataset[第几个项目]['musics'][第几个音轨]['set']['什么设置'] = '设置啥'
#
# 新增音轨: dataset[第几个项目]['musics'].append(datasetmodelpart)
#
'''
dataset=[
{
'mainset':{
'PackName':"Ryoun",
'MusicTitle':'Noname',
'IsRepeat':False,
'PlayerSelect':''
},
'musics':[
{
'set':{
'EntityName':'music_support',
'ScoreboardName':'music_support',
'Instrument':'harp',
'FileName':"Music"
},
'notes':[
[0.0,1.0],
]
},
],
},
]
'''
global dataset
dataset = [
{
'mainset': {
'PackName': "Ryoun",
'MusicTitle': 'Noname',
'IsRepeat': False,
'PlayerSelect': ''
},
'musics': [
{
'set': {
'EntityName': 'MusicSupport',
'ScoreboardName': 'MusicSupport',
'Instrument': 'note.harp',
'FileName': "Music"
},
'notes': [
[0.0, 1.0],
]
},
],
},
]
global is_new_file
global is_save
global ProjectName
global NowMusic
is_new_file = True
is_save = True
ProjectName = ''
NowMusic = 0
def DMM(): # 反回字典用于编辑
datasetmodelpart = {
'set': {
'EntityName': 'MusicSupport',
'ScoreboardName': 'MusicSupport',
'Instrument': 'note.harp',
'FileName': "Music"
},
'notes': []
}
return datasetmodelpart
print("完成")
# 菜单命令
print('加载菜单命令...')
def exitapp(cmd):
log("程序正常退出", False)
global is_save
if is_save == False:
if '/s' in cmd:
saveProject()
else:
print("您尚未保存,请使用 /s 开关保存并退出")
return False
try:
global dataset
del dataset
except:
pass
if '/c' in cmd:
print("清除log此句不载入日志")
try:
if os.path.exists("./log/"):
shutil.rmtree("./log/")
if os.path.exists("./logs/"):
shutil.rmtree("./logs/")
if os.path.exists("./cache/"):
shutil.rmtree("./cache/")
except:
print("无法清除日志及临时文件")
exit()
print('退出函数加载完成!')
print("载入文件读取函数")
def ReadFile(fn: str):
from nmcsup.nmcreader import ReadFile as fileRead
k = fileRead(fn)
if k == False:
log("找不到"+fn)
return False
else:
return k
def ReadMidi(midfile: str):
from nmcsup.nmcreader import ReadMidi as midiRead
k = midiRead(midfile)
if k == False:
log("找不到"+midfile)
return False
else:
return k
print('完成!')
print("载入命令函数")
def saveProject(cmd: list):
global is_new_file
if '/a' in cmd:
log("另存项目")
ProjectName = cmd[cmd.index('/a')+1]
else:
if is_new_file:
print("初次存储请使用 /a 开关规定存储文件名")
log("文件未保存")
return False
log("存储文件:"+ProjectName)
with open(ProjectName, 'w', encoding='utf-8') as f:
json.dump(dataset[0], f)
global is_save
is_save = True
print('保存项目函数加载完成!')
def loadMusic(cmd: list):
if '/mid' in cmd:
th = NewThread(ReadMidi, (cmd[cmd.index('/mid')+1],))
th.start()
def midiSPT(th):
for i in th.getResult():
datas = DMM()
datas['notes'] = i
dataset[0]['musics'].append(datas)
del th
global is_save
is_save = False
threading.Thread(target=midiSPT, args=(th,)).start()
del th
elif '/txt' in cmd:
th = NewThread(ReadFile, (cmd[cmd.index('/txt')+1],))
th.start()
def midiSPT(th):
for i in th.getResult():
datas = DMM()
datas['notes'] = i
dataset[0]['musics'].append(datas)
del th
global is_save
is_save = False
threading.Thread(target=midiSPT, args=(th,)).start()
elif '/input' in cmd:
datas = []
for i in cmd[cmd.index('/input')+1:]:
datas.append([str(i), 1.0])
from nmcsup.trans import note2list
datat = DMM()
datat['notes'] = note2list(datas)
dataset[0]['musics'].append(datat)
del datas, datat
global is_save
is_save = False
else:
log("无参数,无法读入。")
print("请查看帮助文件查看指令格式。")
return False
print('音轨载入函数加载完成!')
def funBuild(cmd: list):
if '/file' in cmd:
from msctspt.funcOpera import makeFuncFiles
makepath = cmd[cmd.index('/file')+1]
if makepath[-1] != '/':
makepath += '/'
makeFuncFiles(dataset[0], makepath)
elif '/directory' in cmd:
from msctspt.funcOpera import makeFunDir
makepath = cmd[cmd.index('/directory')+1]
if makepath[-1] != '/':
makepath += '/'
makeFunDir(dataset[0], makepath)
elif '/mcpack' in cmd:
import zipfile
from msctspt.funcOpera import makeFunDir
makepath = cmd[cmd.index('/mcpack')+1]
if makepath[-1] != '/':
makepath += '/'
if not os.path.exists('./temp/'):
os.makedirs('./temp/')
makeFunDir(dataset[0], './temp/')
shutil.move('./temp/'+dataset[0]['mainset']['PackName'] +
"Pack/behavior_packs/"+dataset[0]['mainset']['PackName']+"/functions", './')
shutil.move('./temp/'+dataset[0]['mainset']['PackName'] + "Pack/behavior_packs/" +
dataset[0]['mainset']['PackName']+"/manifest.json", './')
with zipfile.ZipFile(makepath+dataset[0]['mainset']['PackName']+'.mcpack', "w") as zipobj:
for i in os.listdir('./functions/'):
zipobj.write('./functions/'+i)
zipobj.write('./manifest.json')
shutil.move('./functions', './temp/')
shutil.move('./manifest.json', './temp/')
shutil.rmtree("./temp/")
else:
log("无参数,无法读入。")
print("请查看帮助文件查看指令格式。")
return False
print("函数建立函数加载完成")
def __main__():
if sys.platform == 'win32':
os.system("cls")
else:
os.system("clear")
if sys.platform in ('win32', 'linux'):
print("您当前的运行环境为标准桌面,您可以打开 Musicreater.py 运行窗口模式的 音·创")
print("您也可以输入 win 指令在不退出命令行模式的同时打开窗口模式\n")
print(__author__+" 音·创 —— 当前核心版本 "+__version__+'\n')
nowWorkPath = os.path.split(os.path.realpath(__file__))[0]
while True:
strcmd = input("MSCT "+nowWorkPath+">")
cmd = strcmd.lower().split(' ')
if cmd[0] == 'exit':
exitapp(cmd[1:])
elif cmd[0] == 'save':
saveProject(cmd[1:])
elif cmd[0] == 'load':
loadMusic(cmd[1:])
elif cmd[0] == 'win':
def run(cmd):
os.system(cmd)
if sys.platform == 'win32':
NewThread(run, ("python "+os.path.split(os.path.realpath(__file__))
[0]+"/Musicreater.py",)).start()
else:
NewThread(run, ("python3 "+os.path.split(os.path.realpath(__file__))
[0]+"/Musicreater.py",)).start()
elif cmd[0] == 'chdir':
nowWorkPath = os.path.realpath(cmd[1])
os.chdir(nowWorkPath)
elif cmd[0] == 'build':
funBuild(cmd[1:])
else:
os.system(strcmd)
if __name__ == '__main__':
__main__

View File

@@ -1,4 +0,0 @@
from musicreater.app import main
if __name__ == '__main__':
main().main_loop()

View File

@@ -1,133 +0,0 @@
"""
音·创(Musicreater)是由金羿(W-YI)开发的一款《我的世界》基岩版音乐生成辅助软件
"""
# W-YI 金羿
# QQ 2647547478
# 音·创 开发交流群 861684859
# Email EillesWan2006@163.com W-YI_DoctorYI@outlook.com
# 版权所有 Team-Ryoun 金羿
# 若需转载或借鉴 请附作者
# 代码写的并非十分的漂亮还请大佬多多包涵本软件源代码依照Apache软件协议公开
import sys
import toga
from toga.style import Pack
from toga.style.pack import COLUMN, ROW
from musicreater.Cmd_Msct import *
from musicreater.msctspt.bugReporter import version
from musicreater.resources.ChineseLang import LANGUAGE
__version__ = version.version[1]+version.version[0]
__author__ = 'W-YI (金羿)'
if sys.platform == 'win32':
os.chdir(__file__[:len(__file__)-__file__[len(__file__)::-1].index('\\')])
log("更新执行位置,当前文件位置"+__file__)
else:
try:
os.chdir(__file__[:len(__file__) -
__file__[len(__file__)::-1].index('/')])
except:
pass
log("其他平台:"+sys.platform+"更新执行位置,当前文件位置"+__file__)
class Musicreater(toga.App):
'''音·创 本体\n
W-YI 金羿\n
QQ 2647547478\n
音·创 开发交流群 861684859\n
Email EillesWan2006@163.com W-YI_DoctorYI@outlook.com\n
版权所有 Team-Ryoun 金羿\n
若需转载或借鉴 请附作者\n
'''
def startup(self):
# Start to draw the window
main_box = toga.Box(style=Pack(direction=COLUMN))
self.noticeLabel = toga.Label('MSCT >>>',style=Pack(padding=(0, 5)))
self.inputBox = toga.TextInput(style=Pack(flex=1))
#dispImage = toga.ImageView("./resources/oddevenmatrix.png")
cmd_box = toga.Box(style=Pack(direction=ROW, padding=5))
cmd_box.add(self.noticeLabel)
cmd_box.add(self.inputBox)
# cmd_box.add(dispImage)
button = toga.Button(
LANGUAGE['main']['run'],
on_press=self.showMessage,
style=Pack(padding=5)
)
main_box.add(cmd_box)
main_box.add(button)
self.main_window = toga.MainWindow(title=self.formal_name)
self.main_window.content = main_box
self.main_window.show()
self.main_window.info_dialog('',"{} {} —— {} {}".format(__author__,LANGUAGE['main']['name'],LANGUAGE['main']['version'],__version__))
self.nowWorkPath = os.path.split(os.path.realpath(__file__))[0]
def showMessage(self, widget):
strcmd = self.inputBox.value
cmd = strcmd.lower().split(' ')
if cmd[0] == 'exit':
if exitapp(cmd[1:]) == False:
self.main_window.info_dialog('',LANGUAGE['command']['FormatError'])
elif cmd[0] == 'save':
if saveProject(cmd[1:]) == False:
self.main_window.info_dialog('',LANGUAGE['command']['FormatError'])
elif cmd[0] == 'load':
if loadMusic(cmd[1:]) == False:
self.main_window.info_dialog('',LANGUAGE['command']['FormatError'])
elif cmd[0] == 'chdir':
self.main_window.info_dialog('',LANGUAGE['command']['NotAvailable'])
return
nowWorkPath = os.path.realpath(cmd[1])
os.chdir(nowWorkPath)
elif cmd[0] == 'build':
if funBuild(cmd[1:]) == False:
self.main_window.info_dialog('',LANGUAGE['command']['FormatError'])
else:
return
os.system(strcmd)
def main():
return Musicreater()

View File

@@ -1,141 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
__version__ = '0.0.1'
__all__ = ['run_server', 'subscribe', 'unsubscribe', 'send_command', 'tellraw']
__author__ = 'Fuckcraft <https://gitee.com/fuckcraft>'
import os
import json
import uuid
import logging
import asyncio
import time
import websockets
# 写这段代码的时候,只有我和上帝知道这段代码是干什么的。
# 现在只有上帝知道。
# 此函数用于向 Minecraft 订阅请求
async def subscribe(websocket, event_name):
'''
参数:
: websocket : websocket 对象 :
: event_name : 需要订阅的请求 :
返回:
None
'''
response = {
'body': {
'eventName': str(event_name) # 示例PlayerMessage
},
'header': {
'requestId': str(uuid.uuid4()),
'messagePurpose': 'subscribe',
'version': 1,
'messageType': 'commandRequest'
}
}
# 增加 json 的可读性
# response = json.dumps(response, sort_keys=True, indent=4, separators=(', ', ': '), ensure_ascii=False)
response = json.dumps(response)
await websocket.send(response)
# 此函数用于向 Minecraft 消除订阅请求
async def unsubscribe(webscket):
'''
参数:
: websocket : websocket 对象 :
: event_name : 需要消除订阅的请求 :
返回:
None
'''
response = {
"body": {
"eventName": str(event_name) # PlayerMessage
},
"header": {
"requestId": str(uuid.uuid4()),
"messagePurpose": "unsubscribe",
"version": 1,
"messageType": "commandRequest"
}
}
# 增加 json 的可读性
# response = json.dumps(response, sort_keys=True, indent=4, separators=(', ', ': '), ensure_ascii=False)
response = json.dumps(response)
await websocket.send(response)
# 此函数用于向 Minecraft 执行命令
async def send_command(websocket, command):
'''
参数:
: websocket : websocket 对象 :
: command : 执行的命令 :
返回:
None
'''
response = {
'body': {
'origin': {
'type': 'player'
},
'commandLine': str(command),
'version': 1
},
'header': {
'requestId': str(uuid.uuid4()),
'messagePurpose': 'commandRequest',
'version': 1,
'messageType': 'commandRequest'
}
}
# 增加 json 的可读性
# response = json.dumps(response, sort_keys=True, indent=4, separators=(', ', ': '), ensure_ascii=False)
response = json.dumps(response)
await websocket.send(response)
# 此函数用于向 Minecraft 发送消息
async def tellraw(websocket, message):
'''
参数:
: websocket : websocket 对象 :
: message : 发送的消息 :
返回:
None
'''
command = {
'rawtext':[
{
'text':'[{}] {}'.format(time.asctime(), message)
}
]
}
# 增加 json 可读性
# command = json.dumps(command, sort_keys=True, indent=4, separators=(', ', ': '), ensure_ascii=False)
command = json.dumps(command)
command = 'tellraw @a {}'.format(command)
await send_command(websocket, command)
def run_server(function):
# 修改 ip 地址和端口
start_server = websockets.serve(function, 'localhost', 8080)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

View File

@@ -1,2 +0,0 @@
00:13:19 更新执行位置当前文件位置F:\W-YI\Programming\音·创\程序\src\musicreater\Cmd_Msct.py
00:13:19 更新执行位置当前文件位置F:\W-YI\Programming\音·创\程序\src\musicreater\app.py

View File

@@ -1,173 +0,0 @@
音·创(Musicreater)是由金羿(W-YI)开发的一款《我的世界》基岩版音乐生成辅助软件
本软件源代码依照Apache软件协议公开。
Copyright © W-YI 2021
本软件是金羿前作函数音创和世界音创的集合版本,同时增加了大量功能更新。
To-Do
1.可以导出自定义的结构文件用于存储要导入地图中的结构
2.进度条
3.可以将音乐写入音符盒(红乐)
4.更换tk库为briefcase库支持安卓系统
5.支持自动给音符盒绑定更多的音色
6.可以由.schematic文件导入地图亦可反向处理
7.支持自定义指令方块区域的长宽高等
8.支持自定义创建websockeet服务器播放音乐感谢由 Fuckcraft <https://github.com/fuckcraft> “鸣凤鸽子”等 带来的我的世界websocket服务器功能
9.支持使用红石播放指令音乐
10.支持采用延时的播放器
11.支持使用bdx导出结构
12.支持采用tp的方法播放
13.支持识别曲谱图片解析音乐
14.支持使用瀑布流的方式播放音乐
15.帮助菜单
16.多语言
17.支持自动搜寻地图目录位置(网易&微软)
新更新日志
Beta 0.0.4.3
2021 11 3~2021 12 26
1.不断改进包以及代码可读性
2.修正部分源码错误
3.修正部分格式错误
4.加强对Linux系统的支持
5.新增命令行模式
6.代码中新增大量注释
Beta 0.0.4 ~ Beta 0.0.4.2
2021 11 20 ~ 2021 11 21
1.完全支持Linux系统
2.支持以.RyStruct导出结构
3.修复大量bug
4.支持拖拽打开参数1为.msct文件
Beta 0.0.3.1~0.0.3.5
2021 11 1~2021 11 2
1.更新部分提示信息使之更加科学
2.强制性限制不得使用非Win32平台打开此程序
3.支持在Windwos7上使用此程序发现错误并解决DLL缺失MSVCP140.dll
4.开始对结构导出进行部分支持
5.发现红乐写入的错误,正在排查修复
Beta 0.0.3
2021 10 29 ~ 2021 10 31
1.修改部分窗口排版
2.修复指令载入地图的结构的错误
3.修复指令生成出现的指令错误(感谢 昀梦<QQ1515399885> 找出bug并指正
4.支持生成红石音乐(以音符盒存储的音乐),并写入地图
5.修复了生成指令音乐导致的错误
6.修复bdx文件y轴过长导致无法生成完毕的错误现在bdx的y轴为200格
Beta 0.0.2
2021 10 25
1.修复了邮件发送错误报告无法生成压缩包的问题
2.修复了导入音轨时无法获得进程返回值的问题
3.修复了.bdx文件生成时无法选择文件的问题
4.修复了生成指令音乐(计分板)没有起始方块的问题
5.新增了创建Websocket的功能可以在localhost:8080创建websocket服务器播放音乐感谢由 Fuckcraft <https://gitee.com/fuckcraft> “鸣凤鸽子”等 带来的我的世界websocket服务器功能(fcwslib)
6.解决了打包成可执行文件时无法正常退出的问题
Beta 0.0.1
2021 10月
1.支持生成.bdx文件感谢由 Charlie_Ping “查理平” 带来的bdx转换功能
2.逐步增强对安卓系统的支持
3.逐步放弃对Windows的强行要求
4.逐步提升性能,增加多线程
Alpha部分更新日志
Alpha 0.0.0
2021 8 20
1.集合了 函数音创0.1.4.1 与 世界音创Beta0.0.1 的功能于本应用
2.新增了可以生成 .mcpack 包的方法
Alpha 0.0.1
2021 8 25
1.新增两个彩蛋(就是函数音创命令行模式的彩蛋
Alpha 0.0.1.1
2021 8 25
1.修复大量已知问题
2.修复了部分彩蛋bug但是程序仍然不会正常退出
3.菜单界面优化
Alpha 0.0.1.2
2021.8.29
1.修复大量已知问题
2.现在可以操作指令文件了
3.窗口界面优化
Alpha 0.0.2
2021 9 5
1.修复部分已知问题
2.指令链导入之时仅生成链式方块且允许折转
3.[Dev]正在逐步支持结构导出
Alpha 0.0.3
2021 9 7
1.修复指令链转入世界的摆放错误
2.指令存储的音乐(包括函数)支持不同玩家不同的播放
3.支持播放进度条
4.删除彩蛋任务栏图标
4.[Dev]已确定导出结构格式
Alpha 0.0.3.1
2021 9 11
1.取消输入玩家选择器时不会出现bug了
2.删除日志文件修改为删除临时文件
3.可以删除用于确认档案存在的文件了
Alpha 0.0.4
2021 10 4-5
1.可以将大函数导入世界(以一条链执行多个函数的方式)
2.关闭了试听音乐的功能但是保留其函数于funOpera.py中
3.修改部分代码减少更多bug
4.发现指令链转入世界的摆放错误,但是没改正
1.0.3
2021 10 5-6
1.解决一些已知问题
2.解决了文件读取造成的字符编码问题
3.使用PyPinyin库将汉字转化为拼音首字母
Alpha 0.0.4.1
2021 10 9
1.将清除日志功能设置为结束后统一清除,避免了清除过程中文件占用导致的问题
Alpha 0.0.5
2021 10 10
1.支持使用邮件方式发送错误报告(日志)
Alpha 0.0.5.1
1.修复了邮件发送错误报告无法发送的问题
2.修复了打包成.exe文件之后无法正常退出的问题

View File

@@ -1,215 +0,0 @@
import os
import brotli
'''感谢由 Charlie_Ping “查理平” 带来的bdx转换代码'''
class BdxConverter:
__header = "BD@"
__bin_header = b"BDX"
__generator_author = b"&Charlie_Ping"
keys = {
# x--, x++, addSmallX(-128~127), addX(-32768~32767), addBigX(-2147483648~2147483647)
"x": [b"\x0f", b"\x0e", b"\x1c", b"\x14", b"\x15"],
"y": [b"\x11", b"\x10", b"\x1d", b"\x16", b"\x17"],
"z": [b"\x13", b"\x12", b"\x1e", b"\x18", b"\x19"],
"end": b"\x58",
"isSigned": b"\x5a",
"placeCommandBlockWithData": b"\x1b",
"placeBlock": b"\x07"
}
def __init__(self, file_path: str, author: str, blocks):
self.author = author
self.blocks = blocks
self.file_path = file_path
self.direction = [0, 0, 0]
self.block_type = self.get_block_type
self.__file = self.create_and_upload_file
@property
def get_block_type(self):
"""
blocks
[
{
"direction": [x: int, y: int, z: int],
block_name: str,
particular_value: int,
}
]
:return: list 给出的所有方块种类名称
"""
block_type = set()
for block in self.blocks:
block_type.add(block["block_name"])
block_type = list(block_type)
return block_type
@property
def create_and_upload_file(self):
"""
瞎用property 害怕
创建一个bdx文件
要close
:return: 一个文件对象
"""
_dir = os.path.dirname(self.file_path)
if not os.path.isdir(_dir):
os.makedirs(_dir)
_bytes = self.__bin_header
_bytes += b"\x00"
_bytes += self.author.encode("utf-8") + self.__generator_author
for i in self.block_type:
_bytes += b"\x00\x01"
_bytes += bytes(i, encoding="utf-8")
_bytes += b"\x00"
_bytes += self.upload_blocks()
_bytes += b"X"
with open(self.file_path, "w+") as f:
f.write("BD@")
f.close()
with open(self.file_path, "ab+") as f:
f.write(brotli.compress(_bytes))
f.close()
return
def upload_blocks(self):
"""
计算差值
写入移动过程
写入方块
更新差值
:return:
"""
_types = b""
for block in self.blocks:
# print(f"当前方块:{block['block_name']}, 位置: {block['direction']}]")
diff = self.move_pointer(self.direction, block["direction"])
_types += diff
if block["block_name"] in ["command_block",
"chain_command_block",
"repeating_command_block"]:
_types += self.obtain_command_block(block)
else:
_types += self.obtain_universal_block(block)
self.direction = block["direction"]
return _types
def move_pointer(self, direction: list, new_direction):
"""
给出 两个[x, y, z]坐标返回pointer的移动过程
:param direction: 坐标 1
:param new_direction: 坐标 2
:return: bytes
"""
_bytes = b""
for i, sign in enumerate(["x", "y", "z"]):
# print(f"<{sign}> 新-旧={new_direction[i]-direction[i]}")
distance = new_direction[i] - direction[i]
if distance == 0:
# print("距离是0跳过了")
continue
_bytes += self.obtain_pointer_type(distance, sign)
# print(f"向 {sign} 运动了 {distance} 格子")
return _bytes
@classmethod
def obtain_pointer_type(cls, num: int, coordinate: str):
"""
用于确定辅助玩家以某一数据类型走指定长度
-1 -> 0
1 -> 1
[128, 127] -> 2
[-32768, 32767] -> 3
[-2147483648, 2147483647] -> 4
:param num:
:param coordinate: 坐标轴种类x y 或 z
:return:
"""
if num == 0:
return
pointer = 0
condition = (num != -1, # byte=0, pointer=1
num < -1 or num > 1, # byte=1, pointer=2
num < -128 or num > 127, # byte=2, pointer=3
num < -32768 or num > 32767, # byte=4, pointer=4
)
for i in condition:
if i:
pointer += 1
pointer_type = cls.keys[coordinate][pointer]
byte_len = 2 ** (pointer - 2)
if byte_len >= 1:
num_byte = num.to_bytes(byte_len, byteorder="big", signed=True)
return pointer_type + num_byte
return pointer_type
def obtain_universal_block(self, block):
"""
给定一个方块, 返回此方块在这个bdx中的id和方块data
:param block: {block_name: str,particular_value: int}
:return: bytes
"""
block_id = b"\x07" + self.block_type.index(block["block_name"]).to_bytes(2, byteorder="big", signed=False)
particular_value = block["particular_value"].to_bytes(2, byteorder="big", signed=False)
block_header = block_id + particular_value
return block_header
def obtain_command_block(self, block):
"""
给定一个命令方块,返回命令方块各种数据
:param block: {
"direction": [x: int, y: int, z: int]
"block_name": str,
"particular_value": int,
"impluse": int, # unsigned_int32
"command": str,
"customName": str,
"lastOutput": str, # 没特殊要求写个\x00就得了
"tickdelay": int, # int32
"executeOnFirstTick": int, # 1 bytes
"trackOutput": int, # 1 bytes
"conditional": int, # 1 bytes
"needRedstone": int # 1 bytes
}
:return: bytes of command_block
"""
block_id = b"\x1b" + self.block_type.index(block["block_name"]).to_bytes(2, byteorder="big", signed=False)
particular_value = block["particular_value"].to_bytes(2, byteorder="big", signed=False)
block_header = block_id + particular_value
for i in [
block["impluse"].to_bytes(4, byteorder="big", signed=False),
bytes(block["command"], encoding="utf-8") + b"\x00",
bytes(block["customName"], encoding="utf-8") + b"\x00",
bytes(block["lastOutput"], encoding="utf-8") + b"\x00",
block["tickdelay"].to_bytes(4, byteorder="big", signed=True),
block["executeOnFirstTick"].to_bytes(1, byteorder="big"),
block["trackOutput"].to_bytes(1, byteorder="big"),
block["conditional"].to_bytes(1, byteorder="big"),
block["needRedstone"].to_bytes(1, byteorder="big")
]:
block_header += i
return block_header
if __name__ == '__main__':
block = [{"direction": [-1, -1, -1], "block_name": "concrete", "particular_value": 5},
{"direction": [1, 5, 1], "block_name": "stained_glass", "particular_value": 7},
{"direction": [2, 4, 1], "block_name": "command_block", "particular_value": 3,
"impluse": 0,
"command": "say A generator test",
"customName": "test",
"lastOutput": "",
"tickdelay": 24,
"executeOnFirstTick": 0,
"trackOutput": 0,
"conditional": 0,
"needRedstone": 1
},
{"direction": [3, 4, 1], "block_name": "concrete", "particular_value": 6},
{"direction": [-123412133, 4, 1], "block_name": "concrete", "particular_value": 7}]
bdx = BdxConverter("./test02.bdx", "Charlie_Ping",block)

View File

@@ -1,134 +0,0 @@
# -*- coding: UTF-8 -*-
'''提供错误报告的基本操作及方法 顺便提供版本更新、安装库等功能'''
def makeZip(sourceDir, outFilename,compression = 8,exceptFile = None):
'''使用compression指定的算法打包目录为zip文件\n
默认算法为DEFLATED(8),可用算法如下:\n
STORED = 0\n
DEFLATED = 8\n
BZIP2 = 12\n
LZMA = 14\n
'''
import os, zipfile
zipf = zipfile.ZipFile(outFilename, 'w',compression)
pre_len = len(os.path.dirname(sourceDir))
for parent, dirnames, filenames in os.walk(sourceDir):
for filename in filenames:
if filename == exceptFile:
continue;
print(filename)
pathfile = os.path.join(parent, filename)
arcname = pathfile[pre_len:].strip(os.path.sep) #相对路径
zipf.write(pathfile, arcname)
zipf.close()
del zipf,pre_len
#以上函数节选并修改自 正在攀登的小蜗牛 的博客https://blog.csdn.net/qq_21127151/article/details/107503942
class report():
'''发送报告以及相应的任务处理'''
def __init__(self,senderName:str = 'Unknown',senderContact:str = 'None',describetion:str = ''):
''':param senderName 发送者名称
:param senderContact 发送者联系方式
:param describetion 问题描述'''
self.senderName = senderName;
self.senderContact = senderContact;
self.describetion = describetion;
if not self.senderName :
self.senderName = 'Unknown';
if not self.senderContact :
self.senderContact = 'None';
def emailReport(self):
'''使用E-mail方法发送当前的日志和临时文件等'''
import smtplib
from email.mime.text import MIMEText;
from email.mime.multipart import MIMEMultipart;
from email.header import Header;
from musicreater.nmcsup.log import log
log("发送错误报告")
import os;
log("添加标题与正文")
msg = MIMEMultipart();
#发送者与接收者显示名称
msg["From"] = Header(self.senderName,'utf-8');
msg["To"] = Header("W-YI (QQ2647547478)",'utf-8');
#标题
msg["Subject"] = '音·创 - 来自 '+self.senderName+' 的错误报告';
#正文
msg.attach(MIMEText("来自"+self.senderName+"( "+self.senderContact+" )的错误描述:\n"+self.describetion,'plain','utf-8'));
log("添加完毕,正在生成压缩包...")
makeZip("./","Temps&Logs.zip",exceptFile="Temps&Logs.zip");
attafile=MIMEText(open("Temps&Logs.zip",'rb').read(),"base64",'gb2312');
attafile["Content-Type"] = 'application/octet-stream';
attafile["Content-Disposition"] = 'attachment;filename="BugReport_from_'+self.senderName+'.zip"';
msg.attach(attafile);
log("完毕,准备发送")
try:
smtp = smtplib.SMTP()
smtp.connect("smtp.163.com");
#SIQQKQQYCZRVIDFJ是授权密码
smtp.login("RyounDevTeam@163.com","SIQQKQQYCZRVIDFJ");
smtp.sendmail("RyounDevTeam@163.com",["RyounDevTeam@163.com",],msg.as_string())
log("错误汇报邮件已发送")
except smtplib.SMTPException as e:
log("错误汇报邮件发送失败:\n"+str(e));
log("清空内存和临时文件")
del msg,attafile
os.remove("./Temps&Logs.zip")
class version:
libraries = ('mido','amulet','amulet-core','amulet-nbt','piano_transcription_inference','pypinyin','briefcase','toga','pyinstaller','py7zr','websockets','torch')
'''当前开发所需库'''
version = ('0.0.0','Gamma',)
'''当前版本'''
def __init__(self) -> None:
self.libraries = version.libraries
'''当前开发所需库'''
self.version = version.version
'''当前版本'''
def installLibraries(self):
'''安装全部开发用库'''
from sys import platform
import os
if platform == 'win32':
import shutil
try:
shutil.rmtree(os.getenv('APPDATA')+'\\Musicreater\\')
except:
pass;
for i in self.libraries:
print("安装库:"+i)
os.system("python -m pip install "+i+" -i https://pypi.tuna.tsinghua.edu.cn/simple")
elif platform == 'linux':
os.system("sudo apt-get install python3-pip")
os.system("sudo apt-get install python3-tkinter")
for i in self.libraries:
print("安装库:"+i)
os.system("sudo python3 -m pip install "+i+" -i https://pypi.tuna.tsinghua.edu.cn/simple")

View File

@@ -1,215 +0,0 @@
# -*- coding: utf-8 -*-
"""音·创 的函数操作和一些其他功能"""
def delPart(Data,starter,ender,includeStart :bool= True,includend :bool= True):
'''删除序列从starter物件到ender物件之间的部分\n
includeStart与inclodend分别控制此函数是否包括starter和ender物件所在部分默认为真\n
starter与ender若为None则默认从首或尾开始'''
try:
if starter == None:
includeStart = True;
starter = Data[0];
if ender == None:
includend = True;
ender = Data[len(Data)-1];
if includend:
if includeStart:
return Data[Data.index(starter):len(Data)-Data[len(Data)::-1].index(ender)];
else:
return Data[Data.index(starter)+1:len(Data)-Data[len(Data)::-1].index(ender)];
else:
if includeStart:
return Data[Data.index(starter):len(Data)-Data[len(Data)::-1].index(ender)-1];
else:
return Data[Data.index(starter)+1:len(Data)-Data[len(Data)::-1].index(ender)-1];
except:
return 0
def keepart(Data,starter,ender,includeStart :bool= True,includend :bool= True):
'''保留序列从starter物件到ender物件之间的部分\n
includeStart与inclodend分别控制此函数是否包括starter和ender物件所在部分默认为真\n
starter与ender若为None则默认从首或尾开始'''
try:
if starter == None:
includeStart = True;
starter = Data[0];
if ender == None:
includend = True;
ender = Data[len(Data)-1];
if includend:
if includeStart:
return Data[Data.index(starter):Data.index(ender)+1];
else:
return Data[Data.index(starter)+1:Data.index(ender)+1];
else:
if includeStart:
return Data[Data.index(starter):Data.index(ender)];
else:
return Data[Data.index(starter)+1:Data.index(ender)];
except:
return 0
def lenFunction(fun) -> int:
'''取得函数指令部分长度,即忽略#开头的注释'''
try:
l = 0;
for i in fun:
if i.replace(" ",'')[0] == '#':
l += 1;
return len(fun)-l;
except:
return -1;
def funSplit(bigFile,maxCmdLen : int = 10000 ):
'''分割bigFile大的函数文件bigFile需要读入文件流\n
返回的部分,每行指令皆带有行尾换行符\\n\n
返回-1为大小低于maxCmdLen最长函数指令长度'''
bigFile = bigFile.readlines()
if lenFunction(bigFile) < maxCmdLen:
return -1;
part = [];
parts = [];
l = 0;
for i in bigFile:
if i.replace(" ",'')[0] == '#':
part.append(i+'\n');
else:
part.append(i+'\n');
l += 1;
if l >= 10000:
parts.append(part)
part = [];
l = 0;
return parts;
def makeFuncFiles(musicset, path='./'):
from musicreater.nmcsup.log import log
'''在指定目录下生成函数文件'''
from musicreater.nmcsup.trans import Note2Cmd
commands = []
starts = []
log("=========================正在在此处生成文件:"+path)
maxlen = -1
for i in range(len(musicset['musics'])):
log('写入第'+str(i)+'个数据')
commands.append("scoreboard players add @e[name=\""+musicset['musics'][i]['set']['EntityName']+"\"] "+musicset['musics'][i]['set']['ScoreboardName']+" 1\n")
commands.append("execute @e[name=\""+musicset['musics'][i]['set']['EntityName'] +"\",scores={"+musicset['musics'][i]['set']['ScoreboardName']+"=1..10}] ~~~ title @a"+musicset['mainset']['PlayerSelect']+" title "+musicset['mainset']['MusicTitle']+"\n")
commands.append("execute @e[name=\""+musicset['musics'][i]['set']['EntityName'] +"\",scores={"+musicset['musics'][i]['set']['ScoreboardName']+"=1..10}] ~~~ title @a"+musicset['mainset']['PlayerSelect']+" subtitle 本函数乐曲由§b§l凌云§r§3函数音乐创建§r生成\n")
if len(musicset['musics'][i]['notes']) > maxlen:
maxlen = len(musicset['musics'][i]['notes'])
starts.append("scoreboard objectives add " +musicset['musics'][i]['set']['ScoreboardName']+" dummy\n")
starts.append("summon armor_stand " +musicset['musics'][i]['set']['EntityName']+'\n')
with open(path+musicset['mainset']['MusicTitle']+'_Part'+str(i)+'.mcfunction', 'w', encoding='UTF-8') as f:
f.writelines(Note2Cmd(musicset['musics'][i]['notes'],musicset['musics'][i]['set']['ScoreboardName'],musicset['musics'][i]['set']['Instrument'],musicset['mainset']['PlayerSelect'],True))
if musicset['mainset']['IsRepeat']:
log("增加重复语句")
for i in range(len(musicset['musics'])):
commands.append("execute @e[name=\""+musicset['musics'][i]['set']['EntityName']+"\",scores={"+musicset['musics'][i]['set']['ScoreboardName']+"="+str((maxlen+2)*10)+"}] ~~~ scoreboard players set @e[name=\""+musicset['musics'][i]['set']['EntityName']+"\"] "+musicset['musics'][i]['set']['ScoreboardName']+" -1\n")
log("增加版权语句")
commands.append("\n\n# 凌云我的世界开发团队 x 凌云软件开发团队 : W-YI金羿\n")
starts.append("\n\n# 凌云我的世界开发团队 x 凌云软件开发团队 : W-YI金羿\n")
log("写入支持文件")
with open(path+musicset['mainset']['MusicTitle']+'_Support.mcfunction', 'w', encoding='UTF-8') as f:
f.writelines(commands)
log("写入开始文件")
with open(path+'Start_'+musicset['mainset']['MusicTitle']+'.mcfunction', 'w', encoding='UTF-8') as f:
f.writelines(starts)
del commands, starts, maxlen
log("完成============================")
def makeFunDir(musicset, path='./'):
from musicreater.nmcsup.log import log
'''在指定目录下生成函数包文件夹'''
import os
import uuid
log("=============================生成函数包文件夹")
# note,packname="Ryoun",FileName="Music",EntityName_='music_support',ScoreboardName_='music_support',MusicTitle_='Noname',PlayerSelect_='',Repeat_=False,Instrument_='harp'
try:
os.makedirs(path+musicset['mainset']['PackName'] +"Pack/behavior_packs/"+musicset['mainset']['PackName']+"/functions")
log("已创建目录"+path+musicset['mainset']['PackName'] +"Pack/behavior_packs/"+musicset['mainset']['PackName']+"/functions")
except:
log("目录已有无需创建")
pass
# 判断文件皆存在
if not(os.path.exists(path+musicset['mainset']['PackName']+"Pack/world_behavior_packs.json") and os.path.exists(path+musicset['mainset']['PackName']+"Pack/behavior_packs/"+musicset['mainset']['PackName']+"/manifest.json")):
log("创建manifest.json以及world_behavior_packs.json")
behaviorUuid = uuid.uuid4()
with open(path+musicset['mainset']['PackName']+"Pack/world_behavior_packs.json", "w") as f:
f.write("[\n {\"pack_id\": \"" + str(behaviorUuid) +"\",\n \"version\": [ 0, 0, 1 ]}\n]")
with open(path+musicset['mainset']['PackName']+"Pack/behavior_packs/"+musicset['mainset']['PackName']+"/manifest.json", "w") as f:
f.write("{\n \"format_version\": 1,\n \"header\": {\n \"description\": \""+musicset['mainset']['PackName']+" Pack : behavior pack\",\n \"version\": [ 0, 0, 1 ],\n \"name\": \""+musicset['mainset']['PackName']+"Pack\",\n \"uuid\": \"" + str(behaviorUuid) + "\"\n },\n \"modules\": [\n {\n \"description\": \""+musicset['mainset']['PackName']+" Pack : behavior pack\",\n \"type\": \"data\",\n \"version\": [ 0, 0, 1 ],\n \"uuid\": \"" + str(uuid.uuid4()) + "\"\n }\n ]\n}")
makeFuncFiles(musicset, path+musicset['mainset']['PackName'] +"Pack/behavior_packs/"+musicset['mainset']['PackName']+"/functions/")
log("完成============================")
'''
这里是往事,用于记载一些用不到的功能
#存在于 Musicreater.py 播放(试听)音乐
def PlayNote(Notes, t=480): # Notes是音符列表t是一拍占有的毫秒数
tkinter.messagebox.showinfo(title='提示!', message="播放发音不一定标准\n说不定还会坏音响/(ㄒoㄒ)/~~qwq\n请注意。")
import winsound
import time
from nmcsup.trans import mcnote2freq
Notes = mcnote2freq(Notes)
for frequency, duration in Notes:
log("播放:"+str([int(frequency), int(duration*t)]))
if int(frequency) != 0:
winsound.Beep(int(frequency), int(duration*t))
elif int(frequency) == 0:
time.sleep(duration*t/1000)
#同上,执行播放命令
def PlayOne():
log("试听")
tkinter.messagebox.showwarning(title="警告⚠", message="试听音质可能引起您的不适,更可能引起您的扬声器的不适,请酌情播放。")
global NowMusic
PlayNote(dataset[0]['musics'][NowMusic]['notes'])
#同上,是早期 MinecraftMusicFunctionMaker.py (函数音创)的代码转移至音·创时的注解
n2c(dataset[0]['musics'][i]['notes'],EntityName=dataset[0]['musics'][i]['set']['EntityName'],ScoreboardName=dataset[0]['musics'][i]['set']['ScoreboardName'],PlayerSelect=dataset[0]['mainset']['PlayerSelect'],Instrument=dataset[0]['musics'][i]['set']["Instrument"])
'''

View File

@@ -1,27 +0,0 @@
import threading
class NewThread(threading.Thread):
'''新建一个进程来运行函数,函数运行完毕后可以使用.getResult方法获取其返回值'''
def __init__(self, func, args=()):
super(NewThread, self).__init__()
self.func = func
self.args = args
def run(self):
self.result = self.func(*self.args)
def getResult(self):
threading.Thread.join(self) # 等待线程执行完毕
try:
return self.result
except Exception:
return None
#
# ————————————————
# 版权声明上面的类NewThread修改自CSDN博主「星火燎愿」的原创文章中的内容遵循CC 4.0 BY-SA版权协议转载请附上原文出处链接及本声明。
# 原文链接https://blog.csdn.net/xpt211314/article/details/109543014
# ————————————————
#

View File

@@ -1,308 +0,0 @@
"""音·创 的转换工具库"""
def hans2pinyin(hans,style=3):
"""将汉字字符串转化为拼音字符串"""
from pypinyin import lazy_pinyin
result = lazy_pinyin(hans=hans,style=style)
final = ''
for i in result:
final += i;
return final
def formCmdBlock(direction:list,command:str,particularValue:int,impluse:int,condition:bool=False,needRedstone:bool=True,tickDelay:int=0,customName:str='',lastOutput:str='',executeOnFirstTick:bool=False,trackOutput:bool=True):
"""
使用指定项目返回指定的指令方块格式字典
:param block: {
"direction": [x: int, y: int, z: int] #方块位置
"block_name": str, #方块名称无需指定默认为command_block
"particular_value": int, #方块特殊值
"impluse": int, #方块类型0脉冲 1循环 2连锁 unsigned_int32
"command": str, #指令
"customName": str, #悬浮字
"lastOutput": str, #上次输出
"tickdelay": int, #方块延时 int32
"executeOnFirstTick": int, #执行第一个选项 1 bytes
"trackOutput": int, #是否输出 1 bytes
"conditional": int, #是否有条件 1 bytes
"needRedstone": int #是否需要红石 1 bytes
}
:return: 指令方块字典结构
"""
return {"direction": direction,
"block_name": "command_block",
"particular_value": particularValue,
"impluse": impluse,
"command": command,
"customName": customName,
"lastOutput": lastOutput,
"tickdelay": tickDelay,
"executeOnFirstTick": executeOnFirstTick,
"trackOutput": trackOutput,
"conditional": condition,
"needRedstone": needRedstone
}
def note2bdx(filePath:str,dire:list,Notes : list,ScoreboardName:str,Instrument:str, PlayerSelect:str='',isProsess:bool=False,height:int = 200) :
'''使用方法同Note2Cmd
:param 参数说明:
filePath: 生成.bdx文件的位置
dire: 指令方块在地图中生成的起始位置(相对位置)
Notes: 以 list[ list[ float我的世界playsound指令音调 , float延续时常单位s ] ] 格式存储的音符列表 例如Musicreater.py的(dataset[0]['musics'][NowMusic]['notes'])
ScoreboardName: 用于执行的计分板名称
Instrument: 播放的乐器
PlayerSelect: 执行的玩家选择器
isProsess: 是否显示进度条(会很卡)
height: 生成结构的最高高度
:return 返回一个BdxConverter类实际上没研究过同时在指定位置生成.bdx文件'''
from musicreater.msctspt.transfer import formCmdBlock
from musicreater.nmcsup.trans import Note2Cmd
from musicreater.msctspt.bdxOpera_CP import BdxConverter
cmd = Note2Cmd(Notes,ScoreboardName,Instrument, PlayerSelect,isProsess)
cdl = []
for i in cmd:
try:
if (i[:i.index('#')].replace(' ','') != '\n') and(i[:i.index('#')].replace(' ','') != ''):
cdl.append(i[:i.index('#')])
except:
cdl.append(i)
i = 0
down = False
blocks = [formCmdBlock(dire,cdl.pop(0),1,1)]
dire[1]+=1;
for j in cdl:
if dire[1]+i > height:
dire[0]+=1
i=0
down = not down
if dire[1]+i == height :
blocks.append(formCmdBlock([dire[0],dire[1]+i,dire[2]],j,5,2,False,False))
else:
if down:
blocks.append(formCmdBlock([dire[0],dire[1]+i,dire[2]],j,0,2,False,False))
else:
blocks.append(formCmdBlock([dire[0],dire[1]+i,dire[2]],j,1,2,False,False))
i+=1
del i, cdl, down, cmd
return BdxConverter(filePath,'Build by RyounMusicreater',blocks)
def note2webs(Notes : list,Instrument:str, speed:float = 5.0, PlayerSelect:str='',isProsess:bool=False) :
'''传入音符在oaclhost:8080上建立websocket服务器以供我的世界connect/wssever指令连接
:param 参数说明:
Notes: 以 list[ list[ float我的世界playsound指令音调 , float延续时常单位s ] ] 格式存储的音符列表 例如Musicreater.py的(dataset[0]['musics'][NowMusic]['notes'])
Instrument: 播放的乐器
speed: 用于控制播放速度,数值越大,播放速度越快,相当于把一秒变为几拍
PlayerSelect: 执行的玩家选择器
isProsess: 是否显示进度条
:return None'''
import time
import fcwslib
import asyncio
from musicreater.nmcsup.log import log
from musicreater.nmcsup.vers import VER
async def run_server(websocket, path):
log('服务器连接创建')
await fcwslib.tellraw(websocket, '已连接服务器——音·创'+VER[1]+VER[0]+' 作者:金羿(W-YI)')
if isProsess:
length = len(Notes)
j = 1;
for i in range(len(Notes)):
await fcwslib.send_command(websocket,'execute @a'+PlayerSelect+' ~ ~ ~ playsound '+Instrument+' @s ~ ~ ~ 1000 '+str(Notes[i][0])+' 1000')
if isProsess:
fcwslib.send_command(websocket,'execute @a'+PlayerSelect+' ~ ~ ~ title @s actionbar §e▶ 播放中: §a'+str(j)+'/'+str(length)+' || '+str(int(j/length*1000)/10))
j+=1;
time.sleep(Notes[i][1]/speed)
fcwslib.run_server(run_server)
import amulet
from amulet.api.block import Block
from amulet.utils.world_utils import block_coords_to_chunk_coords as bc2cc
from amulet_nbt import TAG_String as ts
from nmcsup.log import log
def note2RSworld(world:str,startpos:list,notes:list,instrument:str,speed:float = 2.5,posadder:list = [1,0,0],baseblock:str = 'stone') -> bool:
'''传入音符,生成以音符盒存储的红石音乐
:param 参数说明:
world: 地图文件的路径
startpos: list[int,int,int] 开始生成的坐标
notes: list[list[float,float]] 以 list[ list[ float我的世界playsound指令音调 , float延续时常单位s ] ] 格式存储的音符列表 例如Musicreater.py的dataset[0]['musics'][NowMusic]['notes']
instrument: 播放的乐器
speed: 一拍占多少个中继器延迟(红石刻/rt)
posadder: list[int,int,int] 坐标增加规律,即红石的延长时按照此增加规律增加坐标
baseblock: 在中继器下垫着啥方块呢~
:return 是否生成成功
'''
from musicreater.msctspt.values import height2note,instuments
def formNoteBlock(note:int,instrument:str='note.harp',powered:bool = False):
'''生成音符盒方块
:param note: 0~24
:return Block()'''
if powered:
powered = 'true';
else:
powered = 'false';
return Block('universal_minecraft','noteblock',{"instrument":ts(instrument.replace("note.",'')),'note':ts(str(note)),'powered':ts(powered)})
def formRepeater(delay:int,facing:str,locked:bool=False,powered:bool=False):
'''生成中继器方块
:param delay: 1~4
:return Block()'''
if powered:powered = 'true';
else:powered = 'false';
if locked:locked = 'true';
else:locked = 'false';
return Block('universal_minecraft','repeater',{"delay":ts(str(delay)),'facing':ts(facing),'locked':ts(locked),'powered':ts(powered)})
level = amulet.load_level(world)
def setblock(block:Block,pos:list):
'''pos : list[int,int,int]'''
cx, cz = bc2cc(pos[0], pos[2])
chunk = level.get_chunk(cx, cz, "minecraft:overworld")
offset_x, offset_z = pos[0] - 16 * cx, pos[2] - 16 * cz
chunk.blocks[offset_x, pos[1], offset_z] = level.block_palette.get_add_block(block)
chunk.changed = True
# 1拍 x 2.5 rt
def placeNoteBlock():
for i in notes:
try :
setblock(formNoteBlock(height2note[i[0]],instrument),[startpos[0],startpos[1]+1,startpos[2]])
setblock(Block("universal_minecraft",instuments[i[0]][1]),startpos)
except :
log("无法放置音符:"+str(i)+''+str(startpos))
setblock(Block("universal_minecraft",baseblock),startpos)
setblock(Block("universal_minecraft",baseblock),[startpos[0],startpos[1]+1,startpos[2]])
delay = int(i[1]*speed+0.5)
if delay <= 4:
startpos[0]+=1
setblock(formRepeater(delay,'west'),[startpos[0],startpos[1]+1,startpos[2]])
setblock(Block("universal_minecraft",baseblock),startpos)
else:
for i in range(int(delay/4)):
startpos[0]+=1
setblock(formRepeater(4,'west'),[startpos[0],startpos[1]+1,startpos[2]])
setblock(Block("universal_minecraft",baseblock),startpos)
if delay % 4 != 0:
startpos[0]+=1
setblock(formRepeater(delay%4,'west'),[startpos[0],startpos[1]+1,startpos[2]])
setblock(Block("universal_minecraft",baseblock),startpos)
startpos[0]+=posadder[0]
startpos[1]+=posadder[1]
startpos[2]+=posadder[2]
try:
placeNoteBlock()
except:
log("无法放置方块了,可能是因为区块未加载叭")
level.save()
level.close()
class ryStruct:
def __init__(self,world:str) -> None:
self.RyStruct = dict()
self._world = world
self._level = amulet.load_level(world)
def reloadLevel(self):
try:
self._level = amulet.load_level(self.world)
except:
log("无法重载地图")
def closeLevel(self):
try:
self._level.close()
except:
log("无法关闭地图")
def world2Rys(self,startp:list,endp:list,includeAir:bool=False):
'''将世界转换为RyStruct字典注意此函数运行成功后将关闭地图若要打开需要运行 reloadLevel
:param startp: [x,y,z] 转化的起始坐标
:param endp : [x,y,z] 转换的终止坐标,注意,终止坐标需要大于起始坐标,且最终结果包含终止坐标
:param includeAir : bool = False 是否包含空气,即空气是否在生成之时覆盖地图内容
:return dict RyStruct '''
level = self._level
for x in range(startp[0],endp[0]+1):
for y in range(startp[1],endp[1]+1):
for z in range(startp[2],endp[2]+1):
RyStructBlock = dict()
cx, cz = bc2cc(x, z)
chunk = level.get_chunk(cx, cz, "minecraft:overworld")
universal_block = chunk.block_palette[chunk.blocks[x - 16 * cx, y, z - 16 * cz]]
if universal_block == Block("universal_minecraft","air") and includeAir:
continue
universal_block_entity = chunk.block_entities.get((x, y, z), None)
RyStructBlock["block"] = str(universal_block)
RyStructBlock["blockEntity"] = str(universal_block_entity)
log("载入方块数据"+str(RyStructBlock))
self.RyStruct[(x,y,z)] = RyStructBlock
level.close()
return self.RyStruct
'''
RyStruct = {
(0,0,0) = {
"block": str 完整的方块结构
"blockEntity": str | 'None'
}
}
'''

View File

@@ -1,59 +0,0 @@
instuments = {
'note.banjo' : ['班卓琴','hay_block'],
'note.bass' : ['贝斯','planks'],
'note.bassattack' : ['低音鼓/贝斯','log'],
'note.bd' : ['底鼓','stone'], #即basedrum
'note.bell' : ['铃铛/钟琴','gold_block'],
'note.bit' : ['比特/“芯片”(方波)','emerald_block'],
'note.chime' : ['管钟','packed_ice'],
'note.cow_bell' : ['牛铃','soul_sand'],
'note.didgeridoo' : ['迪吉里杜管','pumpkin'],
'note.flute' : ['长笛','clay'],
'note.guitar' : ['吉他','wool'],
'note.harp' : ['竖琴/钢琴','concrete'], #任意其他类型的方块皆可
'note.hat' : ['击鼓沿/架子鼓','glass'],
'note.iron_xylophone' : ['“铁木琴”(颤音琴)','iron_block'],
'note.pling' : ['“扣弦”(电钢琴)','glowstone'],
'note.snare' : ['小军鼓','sand'],
'note.xylophone' : ['木琴','bone_block']
}
'''乐器对照表\n
乐器英文:[中文, 对应音符盒下方块名称]
注:方块仅取一个'''
height2note = {
0.5: 0,
0.53: 1,
0.56: 2,
0.6: 3,
0.63: 4,
0.67: 5,
0.7: 6,
0.75: 7,
0.8: 8,
0.84: 9,
0.9: 10,
0.94: 11,
1.0: 12,
1.05: 13,
1.12: 14,
1.2: 15,
1.25: 16,
1.33: 17,
1.4: 18,
1.5: 19,
1.6: 20,
1.7: 21,
1.8: 22,
1.9: 23,
2.0: 24,
}
'''音高对照表\n
MC音高:音符盒音调'''

View File

@@ -1,72 +0,0 @@
从此日志开始,我的世界函数音乐构建更名为 函数音创 NoteFunCreater谐音NotFun[狗头]版本号更为0.1.0开始
注意,运行此文件需要第三方库:
1. mido 用于对midi文件的解码
2. py7zr 用于对7z压缩包的压缩与解压等需pycparser, cffi, texttable, pyzstd, pyppmd, pycryptodomex, multivolumefile, brotli, bcj-cffi支持 -(从0.1.3开始不需要)
3. zipfile 用于自动生成函数包的压缩
4. pystray 用于支持窗口任务栏
5. pillow 相当于Python2的PIL用于绘图
0.1.0
2021 7 10 - 2021 7 12
1.程序窗口化
2.仅支持基本的菜单操作
3.程序文件皆储存至其相应目录下
4.程序./bin/目录下文件将会自动防修改
5.删除了彩蛋
0.1.1
2021 7 14
1.新增版本辨别的提示
2.窗口中显示歌曲信息
0.1.2
2021 7 14 - 2021 7 15
1.在没运行过的机器上会自动安装库
2.从midi导入时不会删除其他音轨
3.改进UI样式
4.支持对于单个音轨设置的修改以及音乐主设置的修改
5.当未保存便退出时,会询问存储
6.新增加载进度提示
0.1.3
2021 7 15 - 2021 7 19
1.不再从文件中读取音符及乐器信息(所以包更小了)
2.改进UI
3.修复了修改玩家选择器时变更了音乐标题的bug
4.新增删除当前选定音轨按钮
5.新增重置设置按钮(将音乐总设置设置为开始时的设置)
6.运用多线程加载函数与文件等,程序运行效率更高
7.修复变量作用域混淆问题
0.1.3.1
2021 7 19
1.修复了菜单中无法退出程序的问题
0.1.4
2021 7 22
1.支持显示指令于列表中
TO-DO
1.支持从midi文件的元信息中收取音符信息并自动生成
2.支持生成zip函数包
3.支持使用WebSocket接口自动播放已编辑的音乐
4.可以编辑多个项目
5.能够自动将一个长串的音乐分成多个函数文件
6.支持用户导入自己的乐器
7.支持汇报崩溃记录(通过邮件附件的方式)
8.支持播放字幕
9.支持任务栏角标与通知
10.将控制台版本的彩蛋移植到此版本,开启了任务栏
11.可编辑音符

View File

@@ -1,36 +0,0 @@
世界音创(NoteMapCreater)是金羿开发的一款用于生成我的世界中各类有关音乐的物件的软件
软件禁止商用,源代码始终公开,如使用未经授权的音乐经过此软件生成的任何物件侵犯了他人权利与本软件及其作者无关
Copyright © W-YI 2021
开头,特别感谢:
KCINE提供Cinemusicedit函数包(虽然函数包没怎么用过)
Charlie_Ping提供MusiCreaterBot(音乐地图生成QQ机器人)源码核心以及时不时的催更(虽然源码没有抄)
金羿(作者本人)提供NoteFunCreater(函数音创)的制作经验以及时不时的摸鱼(虽然不是很支持函数音创)
广大群友:高效的催更作业让我以蜗牛的速度前进
Alpha 0.0.0
2021 8 1 - 2021 8 10
1.确定了大概的功能
2.不支持无参数传入
3.可以查看帮助,但是帮助大多功能没实现
4.可以从格式文本、midi文件、钢琴声音MP3导入音轨
5.可以生成一些方块到世界里,但是没有播放器(半支持bw开关)
5.提供了修改文件地址的方法,但是不能修改
Alpha 0.0.1
2021 8 10
1.可以从函数音创的工程文件读取音轨
2.可以新建一个空白世界来生成
3.支持修改输出文件地址
4.支持修改输出方块起始位置
5.支持指定播放乐器,执行实体,执行积分板,播放玩家选择器
6.可以生成指令音乐地图(完全支持-w开关)
Beta 0.0.0
2021 8 X?
1.除了-nw 和 -f 开关不支持以外都支持了
Beta 0.0.1
2021 8 19
1.修复了大量bug

View File

@@ -1,320 +0,0 @@
"""音创系列的音符对照表 以及一系列常数"""
notes = {
'....A' : [0.074, 27.5, 'wood', 8],
'....A#' : [0.0787, 29.135, 'wood', 9],
'....B' : [0.083, 30.868, 'wood', 10],
'...C' : [0.088, 32.703, 'wood', 11],
'...C#' : [0.094, 34.648, 'wood', 12],
'...D' : [0.1, 36.708, 'wood', 13],
'...D#' : [0.105, 38.891, 'log', 0],
'...E' : [0.11, 41.203, 'log', 1],
'...F' : [0.12, 43.654, 'log', 2],
'...F#' : [0.125, 46.249, 'wood', 0],
'...G' : [0.13, 48.999, 'wood', 1],
'...G#' : [0.14, 51.913, 'wood', 2],
'...A' : [0.15, 55.0, 'wood', 3],
'...A#' : [0.16, 58.27, 'wood', 4],
'...B' : [0.17, 61.735, 'wood', 5],
'..C' : [0.18, 65.406, 'wool', 0],
'..C#' : [0.19, 69.296, 'wool', 1],
'..D' : [0.2, 73.416, 'wool', 2],
'..D#' : [0.21, 77.782, 'wool', 3],
'..E' : [0.22, 82.407, 'wool', 4],
'..F' : [0.235, 87.307, 'wool', 5],
'..F#' : [0.25, 92.499, 'concretepowder', 0],
'..G' : [0.26, 97.999, 'concretepowder', 1],
'..G#' : [0.28, 103.826, 'concretepowder', 2],
'..A' : [0.3, 110.0, 'concretepowder', 3],
'..A#' : [0.31, 116.541, 'concretepowder', 4],
'..B' : [0.33, 123.471, 'concretepowder', 5],
'.C' : [0.35, 130.813, 'concretepowder', 6],
'.C#' : [0.37, 138.591, 'concretepowder', 7],
'.D' : [0.4, 146.832, 'concretepowder', 8],
'.D#' : [0.42, 155.563, 'concretepowder', 9],
'.E' : [0.44, 164.814, 'concretepowder', 10],
'.F' : [0.47, 174.614, 'concretepowder', 11],
'.F#' : [0.5, 184.997, 'concretepowder', 12],
'.G' : [0.53, 195.998, 'concretepowder', 13],
'.G#' : [0.56, 207.652, 'concretepowder', 14],
'.A' : [0.6, 220.0, 'concretepowder', 15],
'.A#' : [0.63, 233.082, 'concrete', 0],
'.B' : [0.67, 246.942, 'concrete', 1],
'C' : [0.7, 261.626, 'concrete', 2],
'C#' : [0.75, 277.183, 'concrete', 3],
'D' : [0.8, 293.665, 'concrete', 4],
'D#' : [0.84, 311.127, 'concrete', 5],
'E' : [0.9, 329.628, 'concrete', 6],
'F' : [0.94, 349.228, 'concrete', 7],
'F#' : [1.0, 369.994, 'concrete', 8],
'G' : [1.05, 391.995, 'concrete', 9],
'G#' : [1.12, 415.305, 'concrete', 10],
'A' : [1.2, 440.0, 'concrete', 11],
'A#' : [1.25, 466.164, 'concrete', 12],
'B' : [1.33, 493.883, 'concrete', 13],
'`C' : [1.4, 523.251, 'concrete', 14],
'`C#' : [1.5, 554.365, 'concrete', 15],
'`D' : [1.6, 587.33, 'stained_hardened_clay', 0],
'`D#' : [1.7, 622.254, 'stained_hardened_clay', 1],
'`E' : [1.8, 659.255, 'stained_hardened_clay', 2],
'`F' : [1.9, 698.456, 'stained_hardened_clay', 3],
'`F#' : [2.0, 739.989, 'stained_hardened_clay', 4],
'`G' : [2.1, 783.991, 'stained_hardened_clay', 5],
'`G#' : [2.24, 830.609, 'stained_hardened_clay', 6],
'`A' : [2.4, 880.0, 'stained_hardened_clay', 7],
'`A#' : [2.5, 932.328, 'stained_hardened_clay', 8],
'`B' : [2.67, 987.767, 'stained_hardened_clay', 9],
'``C' : [2.83, 1046.502, 'stained_hardened_clay', 10],
'``C#' : [3.0, 1108.731, 'stained_hardened_clay', 11],
'``D' : [3.17, 1174.659, 'stained_hardened_clay', 12],
'``D#' : [3.36, 1244.508, 'stained_hardened_clay', 13],
'``E' : [3.56, 1318.51, 'stained_hardened_clay', 14],
'``F' : [3.78, 1396.913, 'stained_hardened_clay', 15],
'``F#' : [4.0, 1479.978, 'white_glazed_terracotta', 0],
'``G' : [4.24, 1567.982, 'orange_glazed_terracotta', 0],
'``G#' : [4.5, 1661.219, 'magenta_glazed_terracotta', 0],
'``A' : [4.76, 1760.0, 'light_blue_glazed_terracotta', 0],
'``A#' : [5.04, 1864.655, 'yellow_glazed_terracotta', 0],
'``B' : [5.34, 1975.533, 'lime_glazed_terracotta', 0],
'```C' : [5.66, 2093.005, 'pink_glazed_terracotta', 0],
'```C#' : [6.0, 2217.461, 'gray_glazed_terracotta', 0],
'```D' : [6.35, 2349.318, 'silver_glazed_terracotta', 0],
'```D#' : [6.73, 2489.016, 'cyan_glazed_terracotta', 0],
'```E' : [7.13, 2637.02, 'purple_glazed_terracotta', 0],
'```F' : [7.55, 2793.826, 'blue_glazed_terracotta', 0],
'```F#' : [8.0, 2959.955, 'brown_glazed_terracotta', 0],
'```G' : [8.47, 3135.963, 'green_glazed_terracotta', 0],
'```G#' : [8.98, 3322.438, 'red_glazed_terracotta', 0],
'```A' : [9.51, 3520.0, 'black_glazed_terracotta', 0],
'```A#' : [10.08, 3729.31, 'stained_glass', 0],
'```B' : [10.68, 3951.066, 'stained_glass', 1],
'````C' : [11.31, 4186.009, 'stained_glass', 2],
'0' : [0.0, 0.0, 'glass', 0]
}
'''音符对照表\n
音符:[MC音调, 声音频率, 方块名称, 数据值]'''
#方块
'''
blocks = {
0.074 : ['stained_glass', 3],
0.0787 : ['stained_glass', 4],
0.083 : ['stained_glass', 5],
0.088 : ['stained_glass', 6],
0.094 : ['stained_glass', 7],
0.1 : ['stained_glass', 8],
0.105 : ['stained_glass', 9],
0.11 : ['stained_glass', 10],
0.12 : ['stained_glass', 11],
0.125 : ['stained_glass', 12],
0.13 : ['stained_glass', 13],
0.14 : ['stained_glass', 14],
0.15 : ['stained_glass', 15],
0.16 : ['wool', 0],
0.17 : ['wool', 1],
0.18 : ['wool', 2],
0.19 : ['wool', 3],
0.2 : ['wool', 4],
0.21 : ['wool', 5],
0.22 : ['wool', 6],
0.235 : ['wool', 7],
0.25 : ['concretepowder', 0],
0.26 : ['concretepowder', 1],
0.28 : ['concretepowder', 2],
0.3 : ['concretepowder', 3],
0.31 : ['concretepowder', 4],
0.33 : ['concretepowder', 5],
0.35 : ['concretepowder', 6],
0.37 : ['concretepowder', 7],
0.4 : ['concretepowder', 8],
0.42 : ['concretepowder', 9],
0.44 : ['concretepowder', 10],
0.47 : ['concretepowder', 11],
0.5 : ['concretepowder', 12],
0.53 : ['concretepowder', 13],
0.56 : ['concretepowder', 14],
0.6 : ['concretepowder', 15],
0.63 : ['concrete', 0],
0.67 : ['concrete', 1],
0.7 : ['concrete', 2],
0.75 : ['concrete', 3],
0.8 : ['concrete', 4],
0.84 : ['concrete', 5],
0.9 : ['concrete', 6],
0.94 : ['concrete', 7],
1.0 : ['concrete', 8],
1.05 : ['concrete', 9],
1.12 : ['concrete', 10],
1.2 : ['concrete', 11],
1.25 : ['concrete', 12],
1.33 : ['concrete', 13],
1.4 : ['concrete', 14],
1.5 : ['concrete', 15],
1.6 : ['stained_hardened_clay', 0],
1.7 : ['stained_hardened_clay', 1],
1.8 : ['stained_hardened_clay', 2],
1.9 : ['stained_hardened_clay', 3],
2.0 : ['stained_hardened_clay', 4],
2.1 : ['stained_hardened_clay', 5],
2.24 : ['stained_hardened_clay', 6],
2.4 : ['stained_hardened_clay', 7],
2.5 : ['stained_hardened_clay', 8],
2.67 : ['stained_hardened_clay', 9],
2.83 : ['stained_hardened_clay', 10],
3.0 : ['stained_hardened_clay', 11],
3.17 : ['stained_hardened_clay', 12],
3.36 : ['stained_hardened_clay', 13],
3.56 : ['stained_hardened_clay', 14],
3.78 : ['stained_hardened_clay', 15],
4.0 : ['stained_glass_pane', 0],
4.24 : ['stained_glass_pane', 1],
4.5 : ['stained_glass_pane', 2],
4.76 : ['stained_glass_pane', 3],
5.04 : ['stained_glass_pane', 4],
5.34 : ['stained_glass_pane', 5],
5.66 : ['stained_glass_pane', 6],
6.0 : ['stained_glass_pane', 7],
6.35 : ['stained_glass_pane', 8],
6.73 : ['stained_glass_pane', 9],
7.13 : ['stained_glass_pane', 10],
7.55 : ['stained_glass_pane', 11],
8.0 : ['stained_glass_pane', 12],
8.47 : ['stained_glass_pane', 13],
8.98 : ['stained_glass_pane', 14],
9.51 : ['stained_glass_pane', 15],
10.08 : ['stained_glass', 0],
10.68 : ['stained_glass', 1],
11.31 : ['stained_glass', 2],
0.0 : ['glass', 0]
}
#向查理平致敬!!!!!
'''
Blocks = {
0.074: 'barrel',
0.0787: 'beacon',
0.083: 'bedrock',
0.088: 'black_glazed_terracotta',
0.094: 'blast_furnace',
0.1: 'blue_glazed_terracotta',
0.105: 'blue_ice',
0.11: 'bone_block',
0.12: 'bookshelf',
0.125: 'brick_block',
0.13: 'brown_glazed_terracotta',
0.14: 'cartography_table',
0.15: 'carved_pumpkin',
0.16: 'clay',
0.17: 'coal_block',
0.18: 'coal_ore',
0.19: 'cobblestone',
0.2: 'concrete',
0.21: 'crafting_table',
0.22: 'cyan_glazed_terracotta',
0.235: 'diamond_block',
0.25: 'diamond_ore',
0.26: 'white_glazed_terracotta',
0.28: 'dispenser',
0.3: 'dried_kelp_block',
0.31: 'dropper',
0.33: 'emerald_block',
0.35: 'emerald_ore',
0.37: 'end_bricks',
0.4: 'end_stone',
0.42: 'fletching_table',
0.44: 'furnace',
0.47: 'glass',
0.5: 'glowingobsidian',
0.53: 'glowstone',
0.56: 'gold_block',
0.6: 'gold_ore',
0.63: 'grass',
0.67: 'gray_glazed_terracotta',
0.7: 'green_glazed_terracotta',
0.75: 'hardened_clay',
0.8: 'hay_block',
0.84: 'iron_block',
0.9: 'iron_ore',
0.94: 'jukebox',
1.0: 'lapis_block',
1.05: 'lapis_ore',
1.12: 'light_blue_glazed_terracotta',
1.2: 'lime_glazed_terracotta',
1.25: 'lit_pumpkin',
1.33: 'log',
1.4: 'loom',
1.5: 'magenta_glazed_terracotta',
1.6: 'magma',
1.7: 'melon_block',
1.8: 'web',
1.9: 'mossy_cobblestone',
2.0: 'nether_brick',
2.1: 'nether_wart_block',
2.24: 'netherrack',
2.4: 'noteblock',
2.5: 'observer',
2.67: 'obsidian',
2.83: 'orange_glazed_terracotta',
3.0: 'pink_glazed_terracotta',
3.17: 'piston',
3.36: 'planks',
3.56: 'prismarine',
3.78: 'pumpkin',
4.0: 'purple_glazed_terracotta',
4.24: 'purpur_block',
4.5: 'quartz_block',
4.76: 'quartz_ore',
5.04: 'red_glazed_terracotta',
5.34: 'red_nether_brick',
5.66: 'red_sandstone',
6.0: 'redstone_block',
6.35: 'yellow_glazed_terracotta',
6.73: 'sandstone',
7.13: 'stonebrick',
7.55: 'silver_glazed_terracotta',
8.0: 'slime',
8.47: 'smithing_table',
8.98: 'smoker',
9.51: 'smooth_stone',
10.08: 'snow',
10.68: 'soul_sand',
11.31: 'sponge',
0.0: 'stone'
}
'''频率对照表\n
MC音调:方块名称'''
# 乐器
Instuments = {
'note.banjo' : '班卓',
'note.bass' : '低音',
'note.bassattack' : '贝斯',
'note.bd' : '鼓声',
'note.bell' : '铃声',
'note.bit' : '比特',
'note.cow_bell' : '牛铃',
'note.didgeridoo' : '迪吉',
'note.flute' : '长笛',
'note.guitar' : '吉他',
'note.harp' : '竖琴',
'note.hat' : '架鼓',
'note.chime' : '钟声',
'note.iron_xylophone' : '铁琴',
'note.pling' : '叮叮',
'note.snare' : '响弦',
'note.xylophone' : '木琴'
}
'''乐器对照表\n
乐器英文:中文
翻译雪莹工坊Fun-Fer'''

View File

@@ -1,17 +0,0 @@
"""提供对于音创系列的日志"""
import datetime,os
#载入日志功能
StrStartTime = str(datetime.datetime.now()).replace(':', '_')[:-7]
'''字符串型的程序开始时间'''
def log(info:str = '',isPrinted:bool = True):
'''将信息连同当前时间载入日志'''
if not os.path.exists('./log/'):
os.makedirs('./log/')
with open('./log/'+StrStartTime+'.msct.log', 'a',encoding='UTF-8') as f:
f.write(str(datetime.datetime.now())[11:19]+' '+info+'\n')
if isPrinted:
print(str(datetime.datetime.now())[11:19]+' '+info)

View File

@@ -1,87 +0,0 @@
"""音创系列的文件读取功能"""
from musicreater.nmcsup.log import log
from musicreater.nmcsup.const import notes
#从格式文本文件读入一个音轨并存入一个列表
def ReadFile(fn : str) -> list:
from musicreater.nmcsup.trans import note2list
log('打开'+fn+"并读取音符")
try:
nat = open(fn, 'r', encoding='UTF-8').read().split(" ")
del fn
except:
log("找不到读取目标文件")
return False
Notes = []
log(str(nat)+"已读取")
for i in range(int(len(nat)/2)):
Notes.append([nat[i*2], float(nat[i*2+1])])
Notes = note2list(Notes)
log('音符数据更新'+str(Notes))
return [Notes,]
#从midi读入多个音轨返回多个音轨列表
def ReadMidi(midfile : str ) -> list:
import mido
from musicreater.msctspt.threadOpera import NewThread
Notes = []
try:
mid = mido.MidiFile(midfile)
except:
log("找不到文件或无法读取文件"+midfile)
return False
# 解析
ks = list(notes.values())
def loadMidi(track):
datas = []
for i in track:
if i.is_meta:
log('元信息'+str(i))
pass # 不处理元信息
elif 'note_on' in str(i):
msg = str(i).replace("note=", '').replace("time=", '').split(" ")
log('音符on消息处理后'+str(msg))
if msg[4] == '0':
datas.append([ks[int(msg[2])-20][0], 1.0])
log('延续时间0tick--:添加音符'+str([ks[int(msg[2])-20][0], 1.0]))
else:
datas.append([ks[int(msg[2])-20][0], float(msg[4])/480])
log('延续时间'+msg[4]+'tick--:添加音符' +str([ks[int(msg[2])-20][0], float(msg[4])/480]))
del msg
log('音符增加'+str(datas))
return datas
for j, track in enumerate(mid.tracks):
th = NewThread(loadMidi,(track,))
th.start()
Notes.append(th.getResult())
del ks
return Notes
def ReadOldProject(fn:str) -> list:
import json
from musicreater.nmcsup.trans import note2list
log("读取文件:"+fn)
try:
with open(fn, 'r', encoding='UTF-8') as c:
dataset = json.load(c)
except:
print('找不到文件:'+fn+",请查看您是否输入正确")
log("丢失"+fn)
return False
for i in range(len(dataset['musics'])):
dataset['musics'][i]['notes'] = note2list(dataset['musics'][i]['notes'])
#返回 音轨列表 选择器
return dataset

View File

@@ -1,254 +0,0 @@
"""音创系列的转换功能"""
from nmcsup.log import log
# 输入一个列表 [ [str, float ], [], ... ] 音符str 值为持续时间float
def note2list(Notes : list) -> list:
from musicreater.nmcsup.const import notes
def change(base):
enwo = {
'a': 'A',
'b': 'B',
'c': 'C',
'd': "D",
"e": "E",
'f': 'F',
'g': "G"
}
nuwo = {
'6': 'A',
'7': 'B',
'1': 'C',
'2': "D",
"3": "E",
'4': 'F',
'5': "G"
}
for k, v in enwo.items():
if k in base:
base = base.replace(k, v)
for k, v in nuwo.items():
if k in base:
base = base.replace(k, v)
return base
res = []
log(" === 音符列表=>音调列表")
for i in Notes:
s2 = change(i[0])
log(' === 正在操作音符'+i[0]+'->'+s2)
if s2 in notes.keys():
log(" === 找到此音符,加入:"+str(notes[s2][0]))
res.append([notes[s2][0], float(i[1])])
else:
log(' === '+s2+'不在音符表内,此处自动替换为 休止符0 ')
res.append(['0', float(i[1])])
log(' === 最终反回'+str(res))
return res
def mcnote2freq(Notes):
from musicreater.nmcsup.const import notes
mcnback = {}
for i,j in notes.items():
mcnback[j[0]] = i
res = []
log(" === 我的世界音调表=>频率列表")
for i in Notes:
log(' === 正在操作音符'+i[0]+'->'+mcnback[i[0]])
res.append([notes[mcnback[i[0]]][1], float(i[1])])
log(' === 最终反回'+str(res))
return res
#MP3文件转midi文件
def Mp32Mid(mp3File, midFile):
from piano_transcription_inference import PianoTranscription, sample_rate, load_audio
# 加载
(audio, _) = load_audio(mp3File, sr=sample_rate, mono=True)
# 实例化并转换
PianoTranscription(device="cpu").transcribe(audio, midFile)
#传入一个音符列表转为指令列表
def Note2Cmd(Notes : list,ScoreboardName:str,Instrument:str, PlayerSelect:str='',isProsess:bool=False) -> list:
commands = []
a = 0.0
if isProsess:
length = len(Notes)
j = 1
for i in range(len(Notes)):
commands.append("execute @a"+PlayerSelect+" ~ ~ ~ execute @s[scores={"+ScoreboardName+"="+str(int((a+2)*5+int(Notes[i][1]*5)))+"}] ~ ~ ~ playsound "+Instrument+" @s ~ ~ ~ 1000 "+str(Notes[i][0])+" 1000\n")
a += Notes[i][1]
if isProsess:
commands.append("execute @a"+PlayerSelect+" ~ ~ ~ execute @s[scores={"+ScoreboardName+"="+str(int((a+2)*5+int(Notes[i][1]*5)))+"}] ~ ~ ~ title @s actionbar §e▶ 播放中: §a"+str(j)+"/"+str(length)+" || "+str(int(j/length*1000)/10)+"\n")
j+=1
commands.append("\n\n# 凌云我的世界开发团队 x 凌云软件开发团队 : W-YI金羿\n")
return commands
import amulet
import amulet_nbt
from amulet.api.block import Block
from amulet.api.block_entity import BlockEntity
from amulet.utils.world_utils import block_coords_to_chunk_coords
from amulet_nbt import TAG_String,TAG_Compound,TAG_Byte
#简单载入方块
#level.set_version_block(posx,posy,posz,"minecraft:overworld",("bedrock", (1, 16, 20)),Block(namespace, name))
#转入指令列表与位置信息转至世界
def Cmd2World(cmd:list,world:str,dire:list):
'''将指令以命令链的形式载入世界\n
cmd指令列表位为一个序列中包含指令字符串\n
world为地图所在位置需要指向文件夹dire为指令方块生成之位置'''
level = amulet.load_level(world)
cdl = []
for i in cmd:
try:
if (i[:i.index('#')].replace(' ','') != '\n') and(i[:i.index('#')].replace(' ','') != ''):
cdl.append(i[:i.index('#')])
except:
cdl.append(i)
i = 0
#第一个是特殊
universal_block = Block('universal_minecraft','command_block',{'conditional':TAG_String("false"),'facing':TAG_String('up'),'mode':TAG_String("repeating")})
cx, cz = block_coords_to_chunk_coords(dire[0], dire[2])
chunk = level.get_chunk(cx, cz, "minecraft:overworld")
offset_x, offset_z = dire[0] - 16 * cx, dire[2] - 16 * cz
universal_block_entity = BlockEntity( 'universal_minecraft','command_block',dire[0],dire[1],dire[2],amulet_nbt.NBTFile(TAG_Compound({'utags': TAG_Compound({'auto': TAG_Byte(0),'Command': TAG_String(cdl.pop(0))}) })))
chunk.blocks[offset_x, dire[1], offset_z] = level.block_palette.get_add_block(universal_block)
chunk.block_entities[(dire[0], dire[1], dire[2])] = universal_block_entity
chunk.changed = True
#集体上移
dire[1]+=1;
#真正开始
down = False
for j in cdl:
if dire[1]+i >= 255:
dire[0]+=1
i=0
down = not down
#定义此方块
if dire[1]+i == 254 :
universal_block = Block('universal_minecraft','command_block',{'conditional':TAG_String("false"),'facing':TAG_String('east'),'mode':TAG_String("chain")})
else:
if down:
universal_block = Block('universal_minecraft','command_block',{'conditional':TAG_String("false"),'facing':TAG_String('down'),'mode':TAG_String("chain")})
else:
universal_block = Block('universal_minecraft','command_block',{'conditional':TAG_String("false"),'facing':TAG_String('up'),'mode':TAG_String("chain")})
cx, cz = block_coords_to_chunk_coords(dire[0], dire[2])
#获取区块
chunk = level.get_chunk(cx, cz, "minecraft:overworld")
offset_x, offset_z = dire[0] - 16 * cx, dire[2] - 16 * cz
if down:
#定义方块实体
universal_block_entity = BlockEntity( 'universal_minecraft','command_block',dire[0],254-i,dire[2],amulet_nbt.NBTFile(TAG_Compound({'utags': TAG_Compound({'auto': TAG_Byte(1),'Command': TAG_String(j)}) })))
#将方块加入世界
chunk.blocks[offset_x, 254-i, offset_z] = level.block_palette.get_add_block(universal_block)
chunk.block_entities[(dire[0], 254-i, dire[2])] = universal_block_entity
else:
#定义方块实体
universal_block_entity = BlockEntity( 'universal_minecraft','command_block',dire[0],dire[1]+i,dire[2],amulet_nbt.NBTFile(TAG_Compound({'utags': TAG_Compound({'auto': TAG_Byte(1),'Command': TAG_String(j)}) })))
#将方块加入世界
chunk.blocks[offset_x, dire[1]+i, offset_z] = level.block_palette.get_add_block(universal_block)
chunk.block_entities[(dire[0], dire[1]+i, dire[2])] = universal_block_entity
#设置为已更新区块
chunk.changed = True
i+=1
del i, cdl
#保存世界并退出
level.save()
level.close()
#音符转成方块再加载到世界里头
def Blocks2World(world:str,dire:list,Datas:list):
from musicreater.nmcsup.const import Blocks
level = amulet.load_level(world)
i = 0
def setblock(block:str,pos:list):
'''pos : list[int,int,int]'''
cx, cz = block_coords_to_chunk_coords(pos[0], pos[2])
chunk = level.get_chunk(cx, cz, "minecraft:overworld")
offset_x, offset_z = pos[0] - 16 * cx, pos[2] - 16 * cz
chunk.blocks[offset_x, pos[1], offset_z] = level.block_palette.get_add_block(Block("minecraft",block))
chunk.changed = True
for j in Datas:
if dire[1]+1 >= 255:
i = 0
dire[0]+=1
setblock(Blocks[j[0]],[dire[0],dire[1]+i,dire[2]])
i = int(i+j[1]+0.5) #四舍五入
level.save()
level.close()
#传入音符列表制作播放器指令
def Notes2Player(Note,dire:list,CmdData:dict):
'''传入音符列表、坐标、指令数据,生成播放器指令'''
Notes = {}
for i in Note:
Notes[i[0]] = ''
Notes = list(Notes.keys())
from musicreater.nmcsup.const import Blocks
Cmds = []
for j in Notes:
Cmds.append('execute @e[x='+str(dire[0])+',y='+str(dire[1])+',z='+str(dire[2])+',dy='+str(255-dire[1])+',name='+CmdData['Ent']+'] ~ ~ ~ detect ~ ~ ~ '+Blocks[j]+' 0 execute @a '+CmdData['Pls']+' ~ ~ ~ playsound '+CmdData['Ins']+' @s ~ ~ ~ 1000 '+str(j)+' 1000\n')
Cmds+=['#本函数由 金羿 音·创 生成\n','execute @e[y='+str(dire[1])+',dy='+str(255-dire[1])+',name='+CmdData['Ent']+'] ~ ~ ~ tp ~ ~1 ~\n','execute @e[y=255,dy=100,name='+CmdData['Ent']+'] ~ ~ ~ tp ~1 '+str(dire[1])+' ~\n','#音·创 开发交流群 861684859']
return Cmds
#传入音符列表生成方块至世界
def Datas2BlkWorld(NoteData,world:str,dire:list):
for i in range(len(NoteData)):
Blocks2World(world,[dire[0],dire[1],dire[2]+i],NoteData[i])

View File

@@ -1,89 +0,0 @@
"""音创系列版本号和版本操作函数"""
from musicreater.msctspt.bugReporter import version
#以下下两个值请在 msctspt/bugReporter 的version类中修改
VER = version.version
'''当前版本'''
LIBS = version.libraries
'''当前所需库'''
#判断版本、临时文件与补全库
def compver(ver1, ver2):
"""
传入不带英文的版本号,特殊情况:"10.12.2.6.5">"10.12.2.6"
:param ver1: 版本号1
:param ver2: 版本号2
:return: ver1< = >ver2返回-1/0/1
"""
list1 = str(ver1).split(".")
list2 = str(ver2).split(".")
# 循环次数为短的列表的len
for i in range(len(list1)) if len(list1) < len(list2) else range(len(list2)):
if int(list1[i]) == int(list2[i]):
pass
elif int(list1[i]) < int(list2[i]):
return -1
else:
return 1
# 循环结束,哪个列表长哪个版本号高
if len(list1) == len(list2):
return 0
elif len(list1) < len(list2):
return -1
else:
return 1
#
# ————————————————
# 版权声明上面的函数compver为CSDN博主「基友死得早」的原创文章中的函数遵循CC 4.0 BY-SA版权协议转载请附上原文出处链接及本声明。
# 原文链接https://blog.csdn.net/tinyjm/article/details/93514261
# ————————————————
#
import os
def InstallLibs(now,LIBS):
'''比对库信息并安装库'''
from os import system as run
for i in LIBS:
if not i in now:
print("安装库:"+i)
run("python -m pip install "+i+" -i https://pypi.tuna.tsinghua.edu.cn/simple")
def chkver(ver = VER,libs = LIBS):
'''通过文件比对版本信息并安装库'''
if not os.path.exists(os.getenv('APPDATA')+'\\Musicreater\\msct.ActiveDatas.msct'):
print("新安装库")
os.makedirs(os.getenv('APPDATA')+'\\Musicreater\\')
with open(os.getenv('APPDATA')+'\\Musicreater\\msct.ActiveDatas.msct', 'w') as f:
f.write(ver[0]+'\n')
for i in libs:
f.write(i+'\n')
InstallLibs([],libs)
else:
with open(os.getenv('APPDATA')+'\\Musicreater\\msct.ActiveDatas.msct', 'r') as f:
v = f.readlines()
cp = compver(ver[0], v[0])
if cp != 0:
InstallLibs(v[1:],libs)
with open(os.getenv('APPDATA')+'\\Musicreater\\msct.ActiveDatas.msct', 'w') as f:
f.write(ver[0]+'\n')
for i in libs:
f.write(i+'\n')
del cp
def resetver():
'''重置版本信息'''
import shutil
shutil.rmtree(os.getenv('APPDATA')+'\\Musicreater\\')

View File

@@ -1,11 +0,0 @@
LANGUAGE = {
'main':{
"name":"音·创",
"version":"当前版本",
"run":"执行指令",
},
'command':{
"NotAvailable":"此指令不可用。",
"FormatError":"指令格式错误,请查看 命令行操作.md 以查阅指令。"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 441 KiB

View File

@@ -1,11 +0,0 @@
生命灵动 当用激情跃起奋发之力
奇偶数阵
学海无涯 应用爱意徜徉
在生命的起源寻找灵魂的慰藉
纪念那一段辉煌灿烂的青春年华
以梦想为驱使 创造属于自己的未来
集青春之力 绽放爱意之花
那个曾与我相伴的人 依稀在我的心头留恋
我爱你 我 爱 你
你是我灵魂中绽放出最艳丽的花朵
心之所向 意之所属

3
upload.bat Normal file
View File

@@ -0,0 +1,3 @@
python setup.py sdist bdist_wheel
python -m twine upload dist/*
python clean_update.py

View File

@@ -1,34 +0,0 @@
# Musicreater 音·创 命令行模式
为了对后期安卓的兼容先支持命令行
## 命令列表
### exit \[/s\] \[/c\]
- ```/s``` 保存并退出
- ```/c``` 清除日志
### save \[/a \<fileName\>\]
- ```/a <文件名>``` 指定文件名保存
### load \[/\<loadMethod: input|txt|mid\> \<text|fileName\>\]
- ```/input <文本>``` 输入音符
- ```/txt <文件名>``` 从文本载入音符
- ```/mid <文件名>``` 解析midi文件载入音符
### win
- 开启窗口模式的 ·仅支持的系统下有效
### chdir \<direction\>
- 切换当前工作目录
### build \[/\<buildMethod: file|directory|mcpack\> \<direction\>\]
- ```/file <目录位置>``` 在指定目录生成函数文件散装
- ```/directory <目录位置>``` 在指定目录生成一个附加包目录
- ```/mcpack <目录位置>``` 在指定目录生成一个.mcpack附加包

View File

@@ -1,29 +0,0 @@
# -*- conding: utf8 -*-
import os
from src.musicreater.msctspt.funcOpera import keepart
l = 0
for path,dir_list,file_list in os.walk(r"./") :
for file_name in file_list:
if keepart(file_name,'.',None) == '.py':
file = os.path.join(path, file_name)
print("得到文件名:"+str(file))
for i in open(file,'r',encoding="utf-8"):
code = i.replace(' ','').replace('\n','')
try:
code -= code[code.index('#'):]
except:
pass;
if code:
print("\t"+code)
l+=1
else:
pass;
input("\n最终代码行数为:"+str(l))

View File

@@ -1,224 +0,0 @@
{
"mainset": {
"PackName": "RyounTeamSong",
"MusicTitle": "RyounUp",
"IsRepeat": false,
"PlayerSelect": ""
},
"musics": [
{
"set": {
"EntityName": "MusicSupport",
"ScoreboardName": "MusicSupport",
"Instrument": "harp",
"FileName": "RyounUp_RyounTeamSong"
},
"notes": [
[
".5",
1.0
],
[
"1",
1.0
],
[
"1",
1.0
],
[
"3",
1.5
],
[
"3",
0.5
],
[
"2",
1.5
],
[
"1",
0.5
],
[
"2",
1.0
],
[
"5",
1.0
],
[
"3",
1.5
],
[
"2",
0.5
],
[
".6",
1.0
],
[
".5",
1.0
],
[
"3",
1.5
],
[
"2",
0.5
],
[
"1",
1.0
],
[
"5",
1.0
],
[
"5",
1.0
],
[
"6",
0.5
],
[
"5",
0.5
],
[
"3",
1.0
],
[
"5",
0.5
],
[
"3",
0.5
],
[
"2",
1.0
],
[
"2",
1.0
],
[
"3",
0.5
],
[
"2",
0.5
],
[
"1",
1.0
],
[
"1",
1.0
],
[
"3",
1.0
],
[
"3",
1.0
],
[
"5",
1.0
],
[
"0",
1.0
],
[
".5",
0.5
],
[
".6",
0.5
],
[
"1",
1.0
],
[
"1",
1.0
],
[
"6",
1.0
],
[
"1",
1.0
],
[
"2",
1.0
],
[
"3",
1.0
],
[
"3",
1.0
],
[
"3",
1.0
],
[
"3",
1.0
],
[
"2",
1.0
],
[
"5",
1.5
],
[
".5",
0.5
],
[
"1",
1.0
],
[
"1",
1.0
],
[
"0",
1.0
],
[
"0",
1.0
]
]
}
]
}

Binary file not shown.

File diff suppressed because one or more lines are too long