19 Commits

Author SHA1 Message Date
EillesWan
041c64ff65 优化example结构,支持mcstructure的计分板播放 2024-02-03 16:54:43 +08:00
EillesWan
b0bdb7b445 修复了乐音乐器的execute前缀缺失的问题 2024-02-02 20:50:35 +08:00
EillesWan
99a7564648 修复了打击乐器转换时,注释部分format代码报错的问题 2024-02-02 18:12:29 +08:00
EillesWan
dbb3f4c83f 修复了一个小兼容性bug 2024-01-23 18:36:25 +08:00
EillesWan
edd40c078f 加个copy方法 2024-01-23 18:17:15 +08:00
EillesWan
d3b637a0c8 修复类型定义的重复问题-2 2024-01-23 18:14:41 +08:00
EillesWan
d7e3c62deb 修复类型定义的重复问题 2024-01-23 17:01:42 +08:00
EillesWan
7b319268fe 改个版本号 2024-01-14 22:08:27 +08:00
EillesWan
41883f7798 可以用了(?) 2024-01-14 22:05:55 +08:00
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
52 changed files with 3471 additions and 1864 deletions

16
.gitignore vendored
View File

@@ -5,18 +5,22 @@
# mystuff
/.vscode
*.mid
*.midi
*.mcpack
*.bdx
*.json
*.mcstructure
/*.mid
/*.midi
/*.mcpack
/*.bdx
/*.json
/*.mcstructure
.mscbackup
/logs
/languages
/llc_cli.py
/utils
test.py
RES.txt
/MSCT_Packer.py
/Packer/*.MPK
/Packer/checksum.txt
# Byte-compiled / optimized
__pycache__/

View File

@@ -203,7 +203,7 @@
END OF TERMS AND CONDITIONS
Copyright 2022 TriM-Organization 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & all the developers of Musicreater
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.

View File

@@ -5,28 +5,41 @@
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
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
__version__ = "1.4.0"
__vername__ = "红石指令音乐的生成"
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"), ("鸣凤鸽子", "MingFengPigeon"))
__version__ = "1.7.3"
__vername__ = "功能结构化"
__author__ = (
("金羿", "Eilles Wan"),
("诸葛亮与八卦阵", "bgArray"),
("偷吃不是Touch", "Touch"),
("鸣凤鸽子", "MingFengPigeon"),
)
__all__ = [
# 主要类
"MidiConvert",
# 附加类
# "SingleNote",
"SingleNote",
"SingleCommand",
"SingleNoteBox",
# "TimeStamp", 未来功能
# 默认值
"DEFAULT_PROGRESSBAR_STYLE",
"MM_INSTRUMENT_DEVIATION_TABLE",
"MM_CLASSIC_PITCHED_INSTRUMENT_TABLE",
"MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE",
"MM_TOUCH_PITCHED_INSTRUMENT_TABLE",
"MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE",
]
from .main import *

View File

@@ -1,23 +1,22 @@
# -*- coding: utf-8 -*-
from typing import Dict, List, Tuple
"""
存放常量与数值性内容
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from .types import Dict, List, Tuple, MidiInstrumentTableType, MidiNoteNameTableType
x = "x"
"""
@@ -34,42 +33,438 @@ z = "z"
z
"""
DEFAULT_PROGRESSBAR_STYLE = (
r"%%N [ %%s/%^s %%% __________ %%t|%^t ]",
("§e=§r", "§7=§r"),
)
"""
默认的进度条样式组
"""
# Midi用对照表
# 以下是由 Touch “偷吃” 带来的高准确率音效对照表
# 包括乐音乐器对照和打击乐器对照
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",
}
"""Midi音高名称对照表"""
PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
MIDI_PITCHED_NOTE_NAME_GROUP: Dict[int, Tuple[str, str]] = {
1: ("钢琴", "Piano"),
9: ("半音阶打击乐器", "Chromatic Percussion"),
17: ("风琴", "Organ"),
25: ("吉他", "Guitar"),
33: ("贝斯", "Bass"),
41: ("弦乐器", "Strings"),
49: ("合奏乐器", "Ensemble"),
57: ("铜管乐器", "Brass"),
65: ("簧乐器", "Reed"),
73: ("吹管乐器", "Pipe"),
81: ("合成主旋律", "Synth Lead"),
89: ("合成和弦", "Synth Pad"),
97: ("合成声效", "Synth Effects"),
105: ("民族乐器", "Ethnic"),
113: ("打击乐器", "Percussive"),
121: ("特殊音效", "Sound Effects"),
}
"""Midi乐音乐器分组名称对照表"""
MIDI_PITCHED_NOTE_NAME_TABLE: Dict[int, Tuple[str, str]] = {
1: ("原声平台钢琴", "Acoustic Grand Piano"),
2: ("亮音原声钢琴", "Bright Acoustic Piano"),
3: ("数码电钢琴", "Electric Grand Piano"),
4: ("酒吧钢琴", "Honky-tonk Piano"),
5: ("电气电钢琴", "Electric Piano 1(Rhodes Piano)"),
6: ("合唱效果电钢琴", "Electric Piano 2(Chorused Piano)"),
7: ("拨弦古钢琴(羽管键琴)", "Harpsichord"),
8: ("古钢琴", "Clavi"),
9: ("钢片琴", "Celesta"),
10: ("钟琴", "Glockenspiel"),
11: ("八音盒", "Music box"),
12: ("颤音琴", "Vibraphone"),
13: ("马林巴琴", "Marimba"),
14: ("木琴", "Xylophone"),
15: ("管钟", "Tubular Bells"),
16: ("扬琴", "Dulcimer"),
17: ("音栓风琴(击杆风琴)", "Drawbar Organ (Hammond Organ)"),
18: ("打击风琴", "Percussive Organ"),
19: ("摇滚管风琴", "Rock Organ"),
20: ("教堂管风琴", "Church Organ"),
21: ("簧风琴", "Reed Organ"),
22: ("手风琴", "Accordion"),
23: ("口琴", "Harmonica"),
24: ("探戈手风琴", "Tango Accordion"),
25: ("尼龙弦吉他", "Acoustic Guitar (nylon)"),
26: ("钢弦吉他", "Acoustic Guitar (steel)"),
27: ("爵士电吉他", "Electric Guitar (jazz)"),
28: ("清音电吉他", "Electric Guitar (clean)"),
29: ("弱音电吉他", "Electric Guitar (muted)"),
30: ("过驱电吉他", "Overdriven Guitar"),
31: ("失真电吉他", "Distortion Guitar"),
32: ("吉他泛音", "Guitar harmonics"),
33: ("原声贝斯", "Acoustic Bass"),
34: ("指奏电贝斯", "Electric Bass (finger)"),
35: ("拨奏电贝斯", "Electric Bass (pick)"),
36: ("无品贝斯", "Fretless Bass"),
37: ("击弦贝斯 1", "Slap Bass 1"),
38: ("击弦贝斯 2", "Slap Bass 2"),
39: ("合成贝斯 1", "Synth Bass 1"),
40: ("合成贝斯 2", "Synth Bass 2"),
41: ("小提琴", "Violin"),
42: ("中提琴", "Viola"),
43: ("大提琴", "Cello"),
44: ("低音提琴", "Contrabass"),
45: ("颤弓弦乐(弦乐震音)", "Tremolo Strings"),
46: ("弹拨弦乐(弦乐拨奏)", "Pizzicato Strings"),
47: ("竖琴", "Orchestral Harp"),
48: ("定音鼓", "Timpani"),
49: ("弦乐合奏 1", "String Ensemble 1"),
50: ("弦乐合奏 2", "String Ensemble 2"),
51: ("合成弦乐 1", "Synth Strings 1"),
52: ("合成弦乐 2", "Synth Strings 2"),
53: ("合唱“啊”音", "Choir Aahs"),
54: ("人声“嘟”音", "Voice Oohs"),
55: ("合成人声", "Synth Voice"),
56: ("交响打击乐", "Orchestra Hit"),
57: ("小号", "Trumpet"),
58: ("长号", "Trombone"),
59: ("大号", "Tuba"),
60: ("弱音小号", "Muted Trumpet"),
61: ("圆号(法国号)", "French Horn"),
62: ("铜管乐组", "Brass Section"),
63: ("合成铜管 1", "Synth Brass 1"),
64: ("合成铜管 2", "Synth Brass 2"),
65: ("高音萨克斯风", "Soprano Sax"),
66: ("中音萨克斯风", "Alto Sax"),
67: ("次中音萨克斯风", "Tenor Sax"),
68: ("上低音萨克斯风", "Baritone Sax"),
69: ("双簧管", "Oboe"),
70: ("英国管", "English Horn"),
71: ("大管(巴松管)", "Bassoon"),
72: ("单簧管(黑管)", "Clarinet"),
73: ("短笛", "Piccolo"),
74: ("长笛", "Flute"),
75: ("竖笛", "Recorder"),
76: ("排笛", "Pan Flute"),
77: ("瓶笛", "Blown Bottle"),
78: ("尺八", "Shakuhachi"),
79: ("哨子", "Whistle"),
80: ("陶笛", "Ocarina"),
81: ("方波", "Lead 1 (square)"),
82: ("锯齿波", "Lead 2 (sawtooth)"),
83: ("汽笛风琴", "Lead 3 (calliope)"),
84: ("合成吹管", "Lead 4 (chiff)"),
85: ("合成电吉他", "Lead 5 (charang)"),
86: ("人声键盘", "Lead 6 (voice)"),
87: ("五度音", "Lead 7 (fifths)"),
88: ("低音+主音", "Lead 8 (bass+lead)"),
89: ("新纪元", "Pad 1 (new age)"),
90: ("暖温", "Pad 2 (warm)"),
91: ("复合成音", "Pad 3 (polysynth)"),
92: ("人声合唱", "Pad 4 (choir)"),
93: ("弓弦", "Pad 5 (bowed)"),
94: ("银铃", "Pad 6 (metallic)"),
95: ("荣光", "Pad 7 (halo)"),
96: ("轻扫", "Pad 8 (sweep)"),
97: ("夏雨", "FX 1 (rain)"),
98: ("音轨", "FX 2 (soundtrack)"),
99: ("水晶", "FX 3 (crystal)"),
100: ("大气", "FX 4 (atmosphere)"),
101: ("轻曼", "FX 5 (light)"),
102: ("魅影", "FX 6 (goblins)"),
103: ("回响", "FX 7 (echoes)"),
104: ("科幻", "FX 8 (sci-fi)"),
105: ("西塔琴", "Sitar"),
106: ("五弦琴(班卓琴)", "Banjo"),
107: ("三味线", "Shamisen"),
108: ("日本筝", "Koto"),
109: ("卡林巴铁片琴", "Kalimba"),
110: ("苏格兰风笛", "Bagpipe"),
111: ("古提琴", "Fiddle"),
112: ("唢呐", "Shanai"),
113: ("铃铛", "Tinkle Bell"),
114: ("拉丁打铃", "Agogo"),
115: ("钢鼓", "Steel Drums"),
116: ("木块", "Woodblock"),
117: ("太鼓", "Taiko Drum"),
118: ("古式高音鼓", "Melodic Tom"),
119: ("合成鼓", "Synth Drum"),
120: ("铜钹", "Reverse Cymbal"),
121: ("吉他品格杂音", "Guitar Fret Noise"),
122: ("呼吸杂音", "Breath Noise"),
123: ("浪潮", "Seashore"),
124: ("鸟鸣", "Bird Tweet"),
125: ("电话", "Telephone"),
126: ("直升机", "Helicopter"),
127: ("鼓掌", "Applause"),
128: ("射击", "Gunshot"),
}
"""Midi乐音乐器名称对照表"""
MIDI_PERCUSSION_NOTE_NAME_TABLE: Dict[int, Tuple[str, str]] = {
35: ("原声大鼓", "Acoustic Bass Drum"),
36: ("大鼓", "Bass Drum 1"),
37: ("小鼓鼓边", "Side Stick"),
38: ("原声小军鼓", "Acoustic Snare"),
39: ("拍手", "Hand Clap"),
40: ("电子小军鼓", "Electric Snare"),
41: ("低音落地桶鼓", "Low Floor Tom"),
42: ("闭镲", "Closed Hi-Hat"),
43: ("高音落地桶鼓", "High Floor Tom"),
44: ("脚踏踩镲", "Pedal Hi-Hat"),
45: ("低桶鼓", "Low Tom"),
46: ("开镲", "Open Hi-Hat"),
47: ("低音中桶鼓", "Low-Mid Tom"),
48: ("高音中桶鼓", "Hi Mid Tom 2"),
49: ("强音钹 1", "Crash Cymbal 1"),
50: ("高桶鼓", "High Tom"),
51: ("打点钹 1", "Ride Cymbal 1"),
52: ("", "Chinese Cymbal"),
53: ("圆铃", "Ride Bell"),
54: ("铃鼓", "Tambourine"),
55: ("小钹铜钹", "Splash Cymbal"),
56: ("牛铃", "Cowbell"),
57: ("强音钹 2", "Crash Cymbal 2"),
58: ("颤音器", "Vibra-Slap"),
59: ("打点钹 2", "Ride Cymbal 2"),
60: ("高音邦加鼓", "Hi Bongo"),
61: ("低音邦加鼓", "Low Bongo"),
62: ("弱音高音康加鼓", "Mute Hi Conga"),
63: ("强音高音康加鼓", "Open Hi Conga"),
64: ("低音康加鼓", "Low Conga"),
65: ("高音天巴鼓", "High Timbale"),
66: ("低音天巴鼓", "Low Timbale"),
67: ("高音阿哥哥", "High Agogo"),
68: ("低音阿哥哥", "Low Agogo"),
69: ("串珠", "Cabasa"),
70: ("沙铃", "Maracas"),
71: ("短口哨", "Short Whistle"),
72: ("长口哨", "Long Whistle"),
73: ("短刮壶", "Short Guiro"),
74: ("长刮壶", "Long Guiro"),
75: ("梆子", "Claves"),
76: ("高音木块", "Hi Wood Block"),
77: ("低音木块", "Low Wood Block"),
78: ("弱音锯加鼓", "Mute Cuica"),
79: ("开音锯加鼓", "Open Cuica"),
80: ("弱音三角铁", "Mute Triangle"),
81: ("强音三角铁", "Open Triangle"),
}
"""Midi打击乐器名称对照表"""
# Minecraft用对照表
MC_PERCUSSION_INSTRUMENT_LIST: List[str] = [
"note.snare",
"note.bd",
"note.hat",
"note.basedrum",
"firework.blast",
"firework.twinkle",
"fire.ignite",
"mob.zombie.wood",
]
"""打击乐器列表"""
MC_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": ("stone",), # 无法找到此音效
"note.harp": ("dirt",),
# 呃……
"firework.blast": ("sandstone",),
"firework.twinkle": ("red_sandstone",),
"fire.ignite": ("concrete_powder",),
"mob.zombie.wood": ("sand",),
}
"""MC乐器对音符盒下垫方块对照表"""
# Midi对MC通用对照表
MM_INSTRUMENT_DEVIATION_TABLE: Dict[str, int] = {
"note.harp": 6,
"note.pling": 6,
"note.guitar": 7,
"note.iron_xylophone": 6,
"note.bell": 4,
"note.xylophone": 4,
"note.chime": 4,
"note.banjo": 6,
"note.flute": 5,
"note.bass": 8,
"note.snare": -1,
"note.didgeridoo": 8,
"mob.zombie.wood": -1,
"note.bit": 6,
"note.hat": -1,
"note.bd": -1,
"firework.blast": -1,
"firework.twinkle": -1,
"fire.ignite": -1,
"note.share": -1,
"note.cow_bell": 5,
}
"""不同乐器的音调偏离对照表"""
# Midi乐器对MC乐器对照表
MM_CLASSIC_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),
6: ("note.harp", 6),
7: ("note.harp", 6),
8: ("note.bell", 4), # 打击乐器无音域
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),
8: ("note.share", -1), # 打击乐器无音域
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.flute", 5),
18: ("note.harp", 6),
19: ("note.harp", 6),
20: ("note.harp", 6),
21: ("note.flute", 5),
22: ("note.flute", 5),
21: ("note.harp", 6),
22: ("note.harp", 6),
23: ("note.guitar", 7),
24: ("note.guitar", 7),
25: ("note.guitar", 7),
@@ -87,21 +482,21 @@ PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
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.flute", 5),
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.bd", 7),
47: ("note.harp", 6),
48: ("note.guitar", 7),
49: ("note.guitar", 7),
50: ("note.bit", 6),
51: ("note.bit", 6),
52: ("note.flute", 5),
53: ("note.flute", 5),
54: ("note.flute", 5),
52: ("note.harp", 6),
53: ("note.harp", 6),
54: ("note.bit", 6),
55: ("note.flute", 5),
56: ("note.flute", 5),
57: ("note.flute", 5),
@@ -116,16 +511,16 @@ PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
66: ("note.bit", 6),
67: ("note.bit", 6),
68: ("note.flute", 5),
69: ("note.bit", 6),
70: ("note.banjo", 6),
69: ("note.harp", 6),
70: ("note.harp", 6),
71: ("note.flute", 5),
72: ("note.flute", 5),
73: ("note.flute", 5),
74: ("note.flute", 5),
74: ("note.harp", 6),
75: ("note.flute", 5),
76: ("note.harp", 6),
77: ("note.harp", 6),
78: ("note.flute", 5),
78: ("note.harp", 6),
79: ("note.harp", 6),
80: ("note.bit", 6),
81: ("note.bit", 6),
@@ -155,141 +550,293 @@ PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
105: ("note.banjo", 6),
106: ("note.harp", 6),
107: ("note.harp", 6),
108: ("note.bell", 4),
109: ("note.flute", 5),
110: ("note.flute", 5),
108: ("note.harp", 6),
109: ("note.harp", 6),
110: ("note.harp", 6),
111: ("note.guitar", 7),
112: ("note.bell", 4),
112: ("note.harp", 6),
113: ("note.bell", 4),
114: ("note.flute", 5),
114: ("note.harp", 6),
115: ("note.cow_bell", 5),
116: ("note.bd", 7), # 打击乐器无音域
116: ("note.bd", -1), # 打击乐器无音域
117: ("note.bass", 8),
118: ("note.bit", 6),
119: ("firework.blast", 7), # 打击乐器无音域
119: ("note.bd", -1), # 打击乐器无音域
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: ("firework.twinkle", 7), # 打击乐器无音域
127: ("note.snare", 7), # 打击乐器无音域
125: ("note.hat", -1), # 打击乐器无音域
126: ("note.bd", -1), # 打击乐器无音域
127: ("note.snare", -1), # 打击乐器无音域
}
"""“经典”乐音乐器对照表"""
PERCUSSION_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
34: ("note.bd", 7),
35: ("note.bd", 7),
36: ("note.hat", 7),
37: ("note.snare", 7),
38: ("note.snare", 7),
39: ("fire.ignite", 7),
40: ("note.hat", 7),
41: ("note.snare", 7),
42: ("note.hat", 7),
43: ("note.snare", 7),
44: ("note.snare", 7),
MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
34: ("note.bd", -1),
35: ("note.bd", -1),
36: ("note.hat", -1),
37: ("note.snare", -1),
38: ("note.snare", -1),
39: ("note.snare", -1),
40: ("note.hat", -1),
41: ("note.snare", -1),
42: ("note.hat", -1),
43: ("note.snare", -1),
44: ("note.snare", -1),
45: ("note.bell", 4),
46: ("note.snare", 7),
47: ("note.snare", 7),
46: ("note.snare", -1),
47: ("note.snare", -1),
48: ("note.bell", 4),
49: ("note.hat", 7),
49: ("note.hat", -1),
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),
56: ("note.snare", -1),
57: ("note.hat", -1),
58: ("note.chime", 4),
59: ("note.iron_xylophone", 6),
60: ("note.bd", 7),
61: ("note.bd", 7),
60: ("note.bd", -1),
61: ("note.bd", -1),
62: ("note.xylophone", 4),
63: ("note.xylophone", 4),
64: ("note.xylophone", 4),
65: ("note.hat", 7),
65: ("note.hat", -1),
66: ("note.bell", 4),
67: ("note.bell", 4),
68: ("note.hat", 7),
69: ("note.hat", 7),
68: ("note.hat", -1),
69: ("note.hat", -1),
70: ("note.flute", 5),
71: ("note.flute", 5),
72: ("note.hat", 7),
73: ("note.hat", 7),
72: ("note.hat", -1),
73: ("note.hat", -1),
74: ("note.xylophone", 4),
75: ("note.hat", 7),
76: ("note.hat", 7),
75: ("note.hat", -1),
76: ("note.hat", -1),
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",
]
# 以下是由 Touch “偷吃” 带来的高准确率音效对照表
# 包括乐音乐器对照和打击乐器对照
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",),
MM_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", -1),
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", -1), # 合成人声
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", -1), # 打击乐器无音域
116: ("note.snare", -1), # 打击乐器无音域
117: ("note.snare", -1), # 打击乐器无音域
118: ("note.bd", -1), # 打击乐器无音域
119: ("firework.blast", -1), # 打击乐器无音域
120: ("note.guitar", 7), # 吉他还把杂音
121: ("note.harp", 6), # 呼吸声
122: ("note.harp", 6), # 海浪声
123: ("note.harp", 6), # 鸟鸣
124: ("note.bit", 6),
125: ("note.hat", -1), # 直升机
126: ("firework.twinkle", -1), # 打击乐器无音域
127: ("mob.zombie.wood", -1), # 打击乐器无音域
}
"""“偷吃”乐音乐器对照表"""
MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
34: ("note.hat", -1),
35: ("note.bd", -1),
36: ("note.bd", -1),
37: ("note.snare", -1),
38: ("note.snare", -1),
39: ("fire.ignite", 7),
40: ("note.snare", -1),
41: ("note.hat", -1),
42: ("note.hat", -1),
43: ("firework.blast", -1),
44: ("note.hat", -1),
45: ("note.snare", -1),
46: ("note.snare", -1),
47: ("note.snare", -1),
48: ("note.bell", 4),
49: ("note.hat", -1),
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", -1),
57: ("note.hat", -1),
58: ("note.chime", 4),
59: ("note.iron_xylophone", 6),
60: ("note.bd", -1),
61: ("note.bd", -1),
62: ("note.xylophone", 4),
63: ("note.xylophone", 4),
64: ("note.xylophone", 4),
65: ("note.hat", -1),
66: ("note.bell", 4),
67: ("note.bell", 4),
68: ("note.hat", -1),
69: ("note.hat", -1),
70: ("note.flute", 5),
71: ("note.flute", 5),
72: ("note.hat", -1),
73: ("note.hat", -1),
74: ("note.xylophone", 4),
75: ("note.hat", -1),
76: ("note.hat", -1),
77: ("note.xylophone", 4),
78: ("note.xylophone", 4),
79: ("note.bell", 4),
80: ("note.bell", 4),
}
"""“偷吃”打击乐器对照表"""
# 即将启用
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音高:音符盒音调"""
# 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

@@ -5,14 +5,14 @@
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
@@ -24,7 +24,7 @@ class MSCTBaseException(Exception):
"""音·创库版本的所有错误均继承于此"""
super().__init__(*args)
def miao(
def meow(
self,
):
for i in self.args:
@@ -66,12 +66,15 @@ class CommandFormatError(RuntimeError):
super().__init__("指令格式不匹配", *args)
class CrossNoteError(MidiFormatException):
"""同通道下同音符交叉出现所产生的错误"""
# class CrossNoteError(MidiFormatException):
# """同通道下同音符交叉出现所产生的错误"""
def __init__(self, *args):
"""同通道下同音符交叉出现所产生的错误"""
super().__init__("同通道下同音符交叉", *args)
# def __init__(self, *args):
# """同通道下同音符交叉出现所产生的错误"""
# super().__init__("同通道下同音符交叉", *args)
# 这TM是什么错误
# 我什么时候写的这玩意?
# 我哪知道这说的是啥?
class NotDefineTempoError(MidiFormatException):
@@ -98,7 +101,15 @@ class NotDefineProgramError(MidiFormatException):
super().__init__("未指定演奏乐器", *args)
class ZeroSpeedError(MidiFormatException):
class NoteOnOffMismatchError(MidiFormatException):
"""音符开音和停止不匹配的错误"""
def __init__(self, *args):
"""音符开音和停止不匹配的错误"""
super().__init__("音符不匹配", *args)
class ZeroSpeedError(ZeroDivisionError):
"""以0作为播放速度的错误"""
def __init__(self, *args):

View File

@@ -5,25 +5,27 @@
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import random
from typing import Dict, List, Tuple, Union
from .constants import INSTRUMENT_BLOCKS_TABLE
from .exceptions import *
from .main import MidiConvert
from .subclass import *
from .utils import *
from .main import (
MidiConvert,
MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE,
MM_CLASSIC_PITCHED_INSTRUMENT_TABLE,
)
from .types import Tuple, List, Dict, ChannelType
class FutureMidiConvertRSNB(MidiConvert):
@@ -34,125 +36,6 @@ 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"
def to_note_list_in_delay(
self,
) -> Tuple[Dict[int, SingleNoteBox], int]:
"""
使用金羿的转换思路将midi转换为我的世界音符盒音高、乐器及延迟列表并输出每个音符之后的延迟
Returns
-------
tuple( Dict[int, SingleNoteBox], int音乐时长游戏刻 )
"""
self.to_music_channels()
tracks = {}
note_range = {}
InstID = -1
# cmd_amount = 0
# 此处 我们把通道视为音轨
for i in self.channels.keys():
# 如果当前通道为空 则跳过
if not self.channels[i]:
continue
# 第十通道是打击乐通道
SpecialBits = True if i == 9 else False
for track_no, track in self.channels[i].items():
for msg in track:
if msg[0] == "PgmC":
InstID = msg[1]
elif msg[0] == "NoteS":
# block_id = self.soundID_to_block((
# self.perc_inst_to_soundID_withX(msg[1])
# if SpecialBits
# else self.inst_to_souldID_withX(InstID)
# )[0])
# delaytime_now = round(msg[-1] / 50)
note_ = 2 ** ((msg[1] - 60) / 12)
try:
tracks[msg[-1]].append(
(
InstID,
note_,
)
)
except KeyError:
tracks[msg[-1]] = [(InstID, note_)]
try:
note_range[InstID]["max"] = max(
note_range[InstID]["max"], note_
)
note_range[InstID]["min"] = min(
note_range[InstID]["min"], note_
)
except KeyError:
note_range[InstID] = {"max": note_, "min": note_}
del InstID
all_ticks = list(tracks.keys())
all_ticks.sort()
results = []
print(note_range)
exit()
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 j != 0
else (
all_ticks[i] - all_ticks[i - 1]
if i != 0
else all_ticks[i]
)
),
annotation="{}播放{}%{}".format(
mctick2timestr(i), max_volume * 100, ""
),
)
)
self.music_command_list = results
self.music_tick_num = max(all_ticks)
return results, self.music_tick_num
class FutureMidiConvertM4(MidiConvert):
"""
@@ -164,179 +47,59 @@ class FutureMidiConvertM4(MidiConvert):
@staticmethod
def _linear_note(
_note: SingleNote,
_apply_time_division: int = 100,
) -> List[Tuple[int, int, int, int, float],]:
_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.start_time, _note.inst, _note.pitch, _note.velocity, 1),
_note,
]
# print(totalCount)
result: List[Tuple[int, int, int, int, float],] = []
result: List[SingleNote] = []
for _i in range(totalCount):
result.append(
(
_note.start_time + _i * _apply_time_division,
_note.instrument,
_note.pitch,
_note.velocity,
((totalCount - _i) / totalCount),
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,
extra_information=_note.extra_info,
)
# (
# _note.start_time + _i * _apply_time_division,
# _note.instrument,
# _note.pitch,
# _note.velocity,
# ((totalCount - _i) / totalCount),
# )
)
return result
# 简单的单音填充
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("播放速度仅可为正实数")
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
self.to_music_channels()
note_channels: Dict[int, List[SingleNote]] = empty_midi_channels(staff=[])
InstID = -1
# 此处 我们把通道视为音轨
for i in self.channels.keys():
# 如果当前通道为空 则跳过
if not self.channels[i]:
continue
# nowChannel = []
for track_no, track in self.channels[i].items():
noteMsgs = []
MsgIndex = []
for msg in track:
if msg[0] == "PgmC":
InstID = msg[1]
elif msg[0] == "NoteS":
noteMsgs.append(msg[1:])
MsgIndex.append(msg[1])
elif msg[0] == "NoteE":
if msg[1] in MsgIndex:
note_channels[i].append(
SingleNote(
InstID,
msg[1],
noteMsgs[MsgIndex.index(msg[1])][1],
noteMsgs[MsgIndex.index(msg[1])][2],
msg[-1] - noteMsgs[MsgIndex.index(msg[1])][2],
track_number=track_no,
)
)
noteMsgs.pop(MsgIndex.index(msg[1]))
MsgIndex.pop(MsgIndex.index(msg[1]))
del InstID
tracks = []
cmd_amount = 0
max_score = 0
# 此处 我们把通道视为音轨
for no, track in note_channels.items():
# 如果当前通道为空 则跳过
if not track:
continue
SpecialBits = True if no == 9 else False
track_now = []
for note in track:
for every_note in self._linear_note(
note, 50 if note.track_no == 0 else 500
):
soundID, _X = (
self.perc_inst_to_soundID_withX(note.pitch)
if SpecialBits
else self.inst_to_souldID_withX(note.inst)
)
score_now = round(every_note[0] / speed / 50)
max_score = max(max_score, score_now)
mc_pitch = 2 ** ((note.pitch - 60 - _X) / 12)
blockmeter = (
1
/ (1 if note.track_no == 0 else 0.9)
/ max_volume
/ every_note[4]
- 1
)
track_now.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores=({}={})]".format(scoreboard_name, score_now)
.replace("(", r"{")
.replace(")", r"}")
)
+ "playsound {} @s ^ ^ ^{} {} {}".format(
soundID,
blockmeter,
note.velocity / 128,
"" if SpecialBits else mc_pitch,
),
annotation="{}播放{}%({}BM)的{}".format(
mctick2timestr(score_now),
max_volume * 100,
blockmeter,
"{}:{}".format(soundID, note.pitch),
),
),
)
cmd_amount += 1
if track_now:
self.music_command_list.extend(track_now)
tracks.append(track_now)
self.music_tick_num = max_score
return (tracks, cmd_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]:
) -> Tuple[List[SingleCommand], int, int]:
"""
使用金羿的转换思路,使用完全填充算法优化音感后,将midi转换为我的世界命令列表并输出每个音符之后的延迟
将midi转换为我的世界命令列表并输出每个音符之后的延迟
Parameters
----------
@@ -349,126 +112,65 @@ class FutureMidiConvertM4(MidiConvert):
Returns
-------
tuple( list[SingleCommand,...], int音乐时长游戏刻 )
tuple( list[SingleCommand,...], int音乐时长游戏刻, int最大同时播放的指令数量 )
"""
if speed == 0:
raise ZeroSpeedError("播放速度仅可为正实数")
raise ZeroSpeedError("播放速度仅可为(0,1]范围内的正实数")
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
self.to_music_channels()
note_channels: Dict[int, List[SingleNote]] = empty_midi_channels(staff=[])
InstID = -1
notes_list: List[SingleNote] = []
# 此处 我们把通道视为音轨
for i in self.channels.keys():
# 如果当前通道为空 则跳过
if not self.channels[i]:
continue
# nowChannel = []
for track_no, track in self.channels[i].items():
noteMsgs = []
MsgIndex = []
for msg in track:
if msg[0] == "PgmC":
InstID = msg[1]
elif msg[0] == "NoteS":
noteMsgs.append(msg[1:])
MsgIndex.append(msg[1])
elif msg[0] == "NoteE":
if msg[1] in MsgIndex:
note_channels[i].append(
SingleNote(
InstID,
msg[1],
noteMsgs[MsgIndex.index(msg[1])][1],
noteMsgs[MsgIndex.index(msg[1])][2],
msg[-1] - noteMsgs[MsgIndex.index(msg[1])][2],
track_number=track_no,
)
)
noteMsgs.pop(MsgIndex.index(msg[1]))
MsgIndex.pop(MsgIndex.index(msg[1]))
del InstID
tracks = {}
InstID = -1
# open("RES.TXT", "w", encoding="utf-8").write(str(note_channels))
# 此处 我们把通道视为音轨
for no, track in note_channels.items():
# 如果当前通道为空 则跳过
if not track:
continue
SpecialBits = True if no == 9 else False
for note in track:
for every_note in self._linear_note(
note, 50 if note.track_no == 0 else 500
):
soundID, _X = (
self.perc_inst_to_soundID_withX(note.pitch)
if SpecialBits
else self.inst_to_souldID_withX(note.inst)
)
score_now = round(every_note[0] / speed / 50)
try:
tracks[score_now].append(
self.execute_cmd_head.format(player_selector)
+ f"playsound {soundID} @s ^ ^ ^{1 / (1 if note.track_no == 0 else 0.9) / max_volume / every_note[4] - 1} {note.velocity / 128} "
+ (
""
if SpecialBits
else f"{2 ** ((note.pitch - 60 - _X) / 12)}"
)
)
except KeyError:
tracks[score_now] = [
self.execute_cmd_head.format(player_selector)
+ f"playsound {soundID} @s ^ ^ ^{1 / (1 if note.track_no == 0 else 0.9) / max_volume / every_note[4] - 1} {note.velocity / 128} "
+ (
""
if SpecialBits
else f"{2 ** ((note.pitch - 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 j != 0
else (
all_ticks[i] - all_ticks[i - 1]
if i != 0
else all_ticks[i]
)
),
annotation="{}播放{}%{}".format(
mctick2timestr(i), max_volume * 100, ""
for channel in self.to_music_note_channels().values():
for note in channel:
note.set_info(
note_to_command_parameters(
note,
(
self.percussion_note_referrence_table
if note.percussive
else self.pitched_note_reference_table
),
(max_volume) if note.track_no == 0 else (max_volume * 0.9),
self.volume_processing_function,
)
)
self.music_command_list = results
self.music_tick_num = max(all_ticks)
return results, self.music_tick_num
if not note.percussive:
notes_list.extend(self._linear_note(note, note.extra_info[3] * 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)
+ r"playsound {} @s ^ ^ ^{} {} {}".format(*note.extra_info),
tick_delay=tickdelay,
annotation="{}播放{}%{}".format(
mctick2timestr(delaytime_now),
max_volume * 100,
"{}:{}".format(note.extra_info[0], note.extra_info[3]),
),
)
)
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):
@@ -476,6 +178,82 @@ class FutureMidiConvertM5(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 = 500000
# 我们来用通道统计音乐信息
# 但是是用分轨的思路的
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: ChannelType = 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_in_delay(
self,
@@ -527,9 +305,13 @@ class FutureMidiConvertM5(MidiConvert):
elif msg[0] == "NoteS":
soundID, _X = (
self.perc_inst_to_soundID_withX(msg[1])
inst_to_sould_with_deviation(
msg[1], MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE
)
if SpecialBits
else self.inst_to_souldID_withX(InstID)
else inst_to_sould_with_deviation(
InstID, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE
)
)
score_now = round(msg[-1] / float(speed) / 50)

View File

@@ -22,24 +22,24 @@
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
版权所有 © 2024 音·创 开发者
Copyright © 2024 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):
def __init__(
self, channel, pitch, velocity, startT, lastT, midi, now_bpm, change_bpm=None
):
self.channel = channel
self.note = pitch
self.velocity = velocity
@@ -49,19 +49,39 @@ class NoteMessage:
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
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
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
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)
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):
@@ -75,7 +95,7 @@ def load(mid: mido.MidiFile):
for msg in track:
# print(msg)
if msg.is_meta is not True:
if msg.type == 'note_on' and msg.velocity == 0:
if msg.type == "note_on" and msg.velocity == 0:
type_[1] = True
elif msg.type == "note_off":
type_[0] = True
@@ -105,13 +125,13 @@ def load(mid: mido.MidiFile):
print(ticks)
if msg.is_meta is True and msg.type == "set_tempo":
recent_change_bpm = bpm
bpm = 60000000 / msg.tempo
bpm = 60000000 / msg.tempo
is_change_bpm = True
if msg.type == 'note_on' and msg.velocity != 0:
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:
if msg.type == "note_on" and msg.velocity == 0:
for u in noteOn:
index = 0
if u[1] == msg.note:
@@ -121,13 +141,31 @@ def load(mid: mido.MidiFile):
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))
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))
NoteMessage(
msg.channel,
msg.note,
lastMessage.velocity,
lastTick,
ticks - lastTick,
mid,
bpm,
)
)
# print(noteOn)
# print(index)
try:
@@ -139,20 +177,19 @@ def load(mid: mido.MidiFile):
print(j)
if __name__ == '__main__':
if __name__ == "__main__":
load(mido.MidiFile("test.mid"))
# ============================
from typing import Literal
from ..constants import x,y,z
from ..constants import x, y, z
# 不要用 没写完
def delay_to_note_blocks(
baseblock: str = "stone",
position_forward: Literal['x','y','z'] = z,
baseblock: str = "stone",
position_forward: Literal["x", "y", "z"] = z,
):
"""传入音符,生成以音符盒存储的红石音乐
:param:
@@ -169,8 +206,7 @@ def delay_to_note_blocks(
log = print
startpos = [0,0,0]
startpos = [0, 0, 0]
# 1拍 x 2.5 rt
for i in notes:
@@ -180,7 +216,10 @@ def delay_to_note_blocks(
[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]),)
struct.set_block(
startpos,
Block("universal_minecraft", instuments[i[0]][1]),
)
error = False
except ValueError:
log("无法放置音符:" + str(i) + "" + str(startpos))
@@ -222,4 +261,4 @@ def delay_to_note_blocks(
struct.set_block(Block("universal_minecraft", baseblock), startpos)
startpos[0] += posadder[0]
startpos[1] += posadder[1]
startpos[2] += posadder[2]
startpos[2] += posadder[2]

View File

@@ -7,8 +7,8 @@
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
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
@@ -27,39 +27,15 @@ Terms & Conditions: License.md in the root directory
import math
import os
from typing import List, Literal, Tuple, Union
import mido
from .constants import *
from .exceptions import *
from .subclass import *
from .types import *
from .utils import *
VoidMido = Union[mido.MidiFile, None] # void mido
"""
空Midi类类型
"""
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],]],],]
"""
"""
学习笔记:
tempo: microseconds per quarter note 毫秒每四分音符,换句话说就是一拍占多少毫秒
@@ -94,6 +70,12 @@ tick * tempo / 1000000.0 / ticks_per_beat * 一秒多少游戏刻
"""
VoidMido = Union[mido.MidiFile, None] # void mido
"""
空Midi类类型
"""
@dataclass(init=False)
class MidiConvert:
"""
@@ -103,6 +85,15 @@ class MidiConvert:
midi: VoidMido
"""MidiFile对象"""
pitched_note_reference_table: MidiInstrumentTableType
"""乐音乐器Midi-MC对照表"""
percussion_note_referrence_table: MidiInstrumentTableType
"""打击乐器Midi-MC对照表"""
volume_processing_function: FittingFunctionType
"""音量处理函数"""
midi_music_name: str
"""Midi乐曲名"""
@@ -112,7 +103,7 @@ class MidiConvert:
execute_cmd_head: str
"""execute指令头部"""
channels: ChannelType
channels: Union[ChannelType, NoteChannelType]
"""频道信息字典"""
music_command_list: List[SingleCommand]
@@ -129,6 +120,9 @@ class MidiConvert:
midi_obj: VoidMido,
midi_name: str,
enable_old_exe_format: bool = False,
pitched_note_rtable: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
percussion_note_rtable: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
vol_processing_function: FittingFunctionType = natural_curve,
):
"""
简单的midi转换类将midi对象转换为我的世界结构或者包
@@ -141,6 +135,10 @@ class MidiConvert:
此音乐之名
enable_old_exe_format: bool
是否启用旧版(≤1.19)指令格式,默认为否
pitched_note_rtable: Dict[int, Tuple[str, int]]
乐音乐器Midi-MC对照表
percussion_note_rtable: Dict[int, Tuple[str, int]]
打击乐器Midi-MC对照表
"""
self.midi: VoidMido = midi_obj
@@ -155,6 +153,10 @@ class MidiConvert:
else "execute as {} at @s positioned ~ ~ ~ run "
)
self.pitched_note_reference_table = pitched_note_rtable
self.percussion_note_referrence_table = percussion_note_rtable
self.volume_processing_function = vol_processing_function
self.progress_bar_command = self.music_command_list = []
self.channels = {}
self.music_tick_num = 0
@@ -164,6 +166,9 @@ class MidiConvert:
cls,
midi_file_path: str,
old_exe_format: bool = False,
pitched_note_table: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
percussion_note_table: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
vol_processing_func: FittingFunctionType = natural_curve,
):
"""
直接输入文件地址将midi文件读入
@@ -174,6 +179,10 @@ class MidiConvert:
midi文件地址
enable_old_exe_format: bool
是否启用旧版(≤1.19)指令格式,默认为否
pitched_note_table: Dict[int, Tuple[str, int]]
乐音乐器Midi-MC对照表
percussion_note_table: Dict[int, Tuple[str, int]]
打击乐器Midi-MC对照表
"""
midi_music_name = os.path.splitext(os.path.basename(midi_file_path))[0].replace(
@@ -186,6 +195,9 @@ class MidiConvert:
mido.MidiFile(midi_file_path, clip=True),
midi_music_name,
old_exe_format,
pitched_note_table,
percussion_note_table,
vol_processing_func,
)
except (ValueError, TypeError) as E:
raise MidiDestroyedError(f"文件{midi_file_path}损坏:{E}")
@@ -195,62 +207,11 @@ class MidiConvert:
# ……真的那么重要吗
# 我又几曾何时,知道祂真的会抛下我
@staticmethod
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
@staticmethod
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 form_progress_bar(
self,
max_score: int,
scoreboard_name: str,
progressbar_style: tuple = DEFAULT_PROGRESSBAR_STYLE,
progressbar_style: ProgressBarStyle = DEFAULT_PROGRESSBAR_STYLE,
) -> List[SingleCommand]:
"""
生成进度条
@@ -270,7 +231,7 @@ class MidiConvert:
-------
list[SingleCommand,]
"""
pgs_style = progressbar_style[0]
pgs_style = progressbar_style.base_style
"""用于被替换的进度条原始样式"""
"""
@@ -299,8 +260,10 @@ class MidiConvert:
if r"%%%" in pgs_style:
result.append(
SingleCommand(
'scoreboard objectives add {}PercT dummy "百分比计算"'.format(sbn_pc),
annotation="新增临时计算用计分板(百分比)",
'scoreboard objectives add {}PercT dummy "百分比计算"'.format(
sbn_pc
),
annotation="新增临时百分比变量",
)
)
result.append(
@@ -311,7 +274,7 @@ class MidiConvert:
+ "scoreboard players set MaxScore {} {}".format(
scoreboard_name, max_score
),
annotation="设定音乐最大计分",
annotation="设定音乐最大延迟分数",
)
)
result.append(
@@ -331,7 +294,7 @@ class MidiConvert:
+ "scoreboard players operation @s {} = @s {}".format(
sbn_pc + "PercT", scoreboard_name
),
annotation="为临时变量赋值",
annotation="赋值临时百分比",
)
)
result.append(
@@ -342,7 +305,7 @@ class MidiConvert:
+ "scoreboard players operation @s {} *= n100 {}".format(
sbn_pc + "PercT", scoreboard_name
),
annotation="改变临时变量的单位为百分比(扩大精度)",
annotation="转换临时百分比之单位至%(扩大精度)",
)
)
result.append(
@@ -353,7 +316,7 @@ class MidiConvert:
+ "scoreboard players operation @s {} /= MaxScore {}".format(
sbn_pc + "PercT", scoreboard_name
),
annotation="使用临时变量计算百分比",
annotation="计算百分比",
)
)
@@ -364,14 +327,18 @@ class MidiConvert:
if r"%%t" in pgs_style:
result.append(
SingleCommand(
'scoreboard objectives add {}TMinT dummy "时间计算:分"'.format(sbn_pc),
annotation="新增临时计算计分板(分)",
'scoreboard objectives add {}TMinT dummy "时间计算:分"'.format(
sbn_pc
),
annotation="新增临时分变量",
)
)
result.append(
SingleCommand(
'scoreboard objectives add {}TSecT dummy "时间计算:秒"'.format(sbn_pc),
annotation="新增临时计算计分板(秒)",
'scoreboard objectives add {}TSecT dummy "时间计算:秒"'.format(
sbn_pc
),
annotation="新增临时秒变量",
)
)
result.append(
@@ -401,7 +368,7 @@ class MidiConvert:
+ "scoreboard players operation @s {} = @s {}".format(
sbn_pc + "TMinT", scoreboard_name
),
annotation="为临时变量(分)赋值",
annotation="赋值临时分",
)
)
result.append(
@@ -412,7 +379,7 @@ class MidiConvert:
+ "scoreboard players operation @s {} /= n20 {}".format(
sbn_pc + "TMinT", scoreboard_name
),
annotation="将临时变量转换单位为秒(缩减精度)",
annotation="转换临时分之单位为秒(缩减精度)",
)
)
result.append(
@@ -423,7 +390,7 @@ class MidiConvert:
+ "scoreboard players operation @s {} = @s {}".format(
sbn_pc + "TSecT", sbn_pc + "TMinT"
),
annotation="为临时变量(秒)赋值",
annotation="赋值临时秒",
)
)
@@ -435,7 +402,7 @@ class MidiConvert:
+ "scoreboard players operation @s {} /= n60 {}".format(
sbn_pc + "TMinT", scoreboard_name
),
annotation="将临时变量(分)转换单位为分(缩减精度)",
annotation="转换临时分之单位为分(缩减精度)",
)
)
@@ -447,42 +414,35 @@ class MidiConvert:
+ "scoreboard players operation @s {} %= n60 {}".format(
sbn_pc + "TSecT", scoreboard_name
),
annotation="将临时变量(秒)确定下来(框定精度区间)",
annotation="确定临时秒(框定精度区间)",
)
)
for i in range(pgs_style.count("_")):
npg_stl = (
pgs_style.replace("_", progressbar_style[1][0], i + 1)
.replace("_", progressbar_style[1][1])
pgs_style.replace("_", progressbar_style.played_style, i + 1)
.replace("_", progressbar_style.to_play_style)
.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(
.replace(
r"%%s",
'"},{"score":{"name":"*","objective":"'
+ scoreboard_name
+ '"}},{"text":"',
)
if r"%%%" in npg_stl:
npg_stl = npg_stl.replace(
.replace(
r"%%%",
r'"},{"score":{"name":"*","objective":"'
+ sbn_pc
+ r'PercT"}},{"text":"%',
)
if r"%%t" in npg_stl:
npg_stl = npg_stl.replace(
.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(
@@ -502,45 +462,48 @@ class MidiConvert:
result.append(
SingleCommand(
"scoreboard objectives remove {}PercT".format(sbn_pc),
annotation="移除临时计算计分板(百分比",
annotation="移除临时百分比变量",
)
)
if r"%%t" in pgs_style:
result.append(
SingleCommand(
"scoreboard objectives remove {}TMinT".format(sbn_pc),
annotation="移除临时计算计分板(分)",
annotation="移除临时分变量",
)
)
result.append(
SingleCommand(
"scoreboard objectives remove {}TSecT".format(sbn_pc),
annotation="移除临时计算计分板(秒)",
annotation="移除临时秒变量",
)
)
self.progress_bar_command = result
return result
def to_music_channels(
def to_music_note_channels(
self,
) -> ChannelType:
default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO,
ignore_mismatch_error: bool = True,
) -> NoteChannelType:
"""
使用金羿的转换思路,将midi解析并转换为频道信息字典
将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],]],],]
以频道作为分割的Midi音符列表字典:
Dict[int,List[SingleNote,]]
"""
if self.midi is None:
raise MidiUnboundError(
"你是否正在使用的是一个由 copy_important 生成的MidiConvert对象这是不可复用的。"
)
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
midi_channels: ChannelType = empty_midi_channels()
tempo = mido.midifiles.midifiles.DEFAULT_TEMPO
midi_channels: NoteChannelType = empty_midi_channels(staff=[])
tempo = default_tempo_value
# 我们来用通道统计音乐信息
# 但是是用分轨的思路的
@@ -549,6 +512,27 @@ class MidiConvert:
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
@@ -557,28 +541,64 @@ class MidiConvert:
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)
)
channel_program[msg.channel] = msg.program
elif msg.type == "note_on" and msg.velocity != 0:
midi_channels[msg.channel][track_no].append(
("NoteS", msg.note, msg.velocity, microseconds)
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_on" and msg.velocity == 0) or (
msg.type == "note_off"
elif (msg.type == "note_off") or (
msg.type == "note_on" and msg.velocity == 0
):
midi_channels[msg.channel][track_no].append(
("NoteE", msg.note, microseconds)
)
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,
"无法在上文中找到与之匹配的音符开音消息。",
)
"""整合后的音乐通道格式
每个通道包括若干消息元素其中逃不过这三种:
@@ -592,9 +612,14 @@ class MidiConvert:
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
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,
@@ -603,7 +628,7 @@ class MidiConvert:
speed: float = 1.0,
) -> Tuple[List[List[SingleCommand]], int, int]:
"""
使用金羿的转换思路,将midi转换为我的世界命令列表
将midi转换为我的世界命令列表
Parameters
----------
@@ -620,72 +645,86 @@ class MidiConvert:
"""
if speed == 0:
raise ZeroSpeedError("播放速度仅可为正实数")
raise ZeroSpeedError("播放速度仅可为(0,1]范围内的正实数")
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
tracks = []
cmdAmount = 0
maxScore = 0
InstID = -1
self.to_music_channels()
command_channels = []
command_amount = 0
max_score = 0
# 此处 我们把通道视为音轨
for i in self.channels.keys():
for channel in self.to_music_note_channels().values():
# 如果当前通道为空 则跳过
if not self.channels[i]:
if not channel:
continue
# 第十通道是打击乐通道
SpecialBits = True if i == 9 else False
this_channel = []
for track_no, track in self.channels[i].items():
nowTrack = []
for note in channel:
score_now = round(note.start_time / float(speed) / 50)
max_score = max(max_score, score_now)
for msg in track:
if msg[0] == "PgmC":
InstID = msg[1]
(
mc_sound_ID,
mc_distance_volume,
volume_percentage,
mc_pitch,
) = note_to_command_parameters(
note,
(
self.percussion_note_referrence_table
if note.percussive
else self.pitched_note_reference_table
),
(max_volume) if note.track_no == 0 else (max_volume * 0.9),
self.volume_processing_function,
)
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)
maxScore = max(maxScore, score_now)
mc_pitch = "" if SpecialBits else 2 ** ((msg[1] - 60 - _X) / 12)
nowTrack.append(
SingleCommand(
self.execute_cmd_head.format(
"@a[scores=({}={})]".format(
scoreboard_name, score_now
)
.replace("(", r"{")
.replace(")", r"}")
this_channel.append(
SingleCommand(
(
self.execute_cmd_head.format(
"@a[scores=({}={})]".format(scoreboard_name, score_now)
.replace("(", r"{")
.replace(")", r"}")
)
+ (
r"playsound {} @s ^ ^ ^{} {}".format(
mc_sound_ID, mc_distance_volume, volume_percentage
)
+ "playsound {} @s ^ ^ ^{} {} {}".format(
soundID, 1 / max_volume - 1, msg[2] / 128, mc_pitch
),
annotation="{}播放{}%{}".format(
mctick2timestr(score_now),
max_volume * 100,
"{}:{}".format(soundID, mc_pitch),
),
),
)
if note.percussive
else r"playsound {} @s ^ ^ ^{} {} {}".format(
mc_sound_ID,
mc_distance_volume,
volume_percentage,
mc_pitch,
)
)
),
annotation=(
"{}播放{}%{}噪音".format(
mctick2timestr(score_now),
max_volume * 100,
mc_sound_ID,
)
if note.percussive
else "{}播放{}%{}乐音".format(
mctick2timestr(score_now),
max_volume * 100,
"{}:{:.2f}".format(mc_sound_ID, mc_pitch),
)
),
),
)
cmdAmount += 1
command_amount += 1
if nowTrack:
self.music_command_list.extend(nowTrack)
tracks.append(nowTrack)
if this_channel:
self.music_command_list.extend(this_channel)
command_channels.append(this_channel)
# print(cmdAmount)
del InstID
self.music_tick_num = maxScore
return (tracks, cmdAmount, maxScore)
self.music_tick_num = max_score
return command_channels, command_amount, max_score
def to_command_list_in_delay(
self,
@@ -694,7 +733,7 @@ class MidiConvert:
player_selector: str = "@a",
) -> Tuple[List[SingleCommand], int, int]:
"""
使用金羿的转换思路,将midi转换为我的世界命令列表并输出每个音符之后的延迟
将midi转换为我的世界命令列表并输出每个音符之后的延迟
Parameters
----------
@@ -711,95 +750,82 @@ class MidiConvert:
"""
if speed == 0:
raise ZeroSpeedError("播放速度仅可为正实数")
raise ZeroSpeedError("播放速度仅可为(0,1]范围内的正实数")
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
self.to_music_channels()
tracks = {}
InstID = -1
# cmd_amount = 0
notes_list: List[SingleNote] = []
# 此处 我们把通道视为音轨
for i in self.channels.keys():
# 如果当前通道为空 则跳过
if not self.channels[i]:
continue
for channel in self.to_music_note_channels().values():
notes_list.extend(channel)
# 第十通道是打击乐通道
SpecialBits = True if i == 9 else False
notes_list.sort(key=lambda a: a.start_time)
# nowChannel = []
self.music_command_list = []
multi = max_multi = 0
delaytime_previous = 0
for track_no, track in self.channels[i].items():
for msg in track:
if msg[0] == "PgmC":
InstID = msg[1]
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
elif msg[0] == "NoteS":
soundID, _X = (
self.perc_inst_to_soundID_withX(msg[1])
if SpecialBits
else self.inst_to_souldID_withX(InstID)
(
mc_sound_ID,
mc_distance_volume,
volume_percentage,
mc_pitch,
) = note_to_command_parameters(
note,
(
self.percussion_note_referrence_table
if note.percussive
else self.pitched_note_reference_table
),
(max_volume) if note.track_no == 0 else (max_volume * 0.9),
self.volume_processing_function,
)
self.music_command_list.append(
SingleCommand(
command=(
self.execute_cmd_head.format(player_selector)
+ (
r"playsound {} @s ^ ^ ^{} {}".format(
mc_sound_ID, mc_distance_volume, volume_percentage
)
if note.percussive
else r"playsound {} @s ^ ^ ^{} {} {}".format(
mc_sound_ID,
mc_distance_volume,
volume_percentage,
mc_pitch,
)
)
),
annotation=(
"{}播放{}%{}噪音".format(
mctick2timestr(delaytime_now),
max_volume * 100,
mc_sound_ID,
)
if note.percussive
else "{}播放{}%{}乐音".format(
mctick2timestr(delaytime_now),
max_volume * 100,
"{}:{:.2f}".format(mc_sound_ID, mc_pitch),
)
),
tick_delay=tickdelay,
),
)
delaytime_previous = delaytime_now
delaytime_now = round(msg[-1] / float(speed) / 50)
try:
tracks[delaytime_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[delaytime_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)}"
)
]
# cmd_amount += 1
# print(cmd_amount)
del InstID
all_ticks = list(tracks.keys())
all_ticks.sort()
results = []
max_multi = 0
for i in range(len(all_ticks)):
max_multi = max(max_multi, len(tracks[all_ticks[i]]))
for j in range(len(tracks[all_ticks[i]])):
results.append(
SingleCommand(
tracks[all_ticks[i]][j],
tick_delay=(
0
if j != 0
else (
all_ticks[i] - all_ticks[i - 1]
if i != 0
else all_ticks[i]
)
),
annotation="{}播放{}%{}".format(
mctick2timestr(i), max_volume * 100, ""
),
)
)
self.music_command_list = results
self.music_tick_num = max(all_ticks)
return results, self.music_tick_num, max_multi
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(

View File

@@ -2,21 +2,70 @@
"""
存放非音·创本体的附加内容(插件?)
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
__all__ = [
# 通用
"ConvertConfig",
"bottem_side_length_of_smallest_square_bottom_box",
# 打包
"compress_zipfile",
"behavior_mcpack_manifest",
# MCSTRUCTURE 函数
"antiaxis",
"forward_IER",
"command_statevalue",
"form_note_block_in_NBT_struct",
"form_repeater_in_NBT_struct",
"form_command_block_in_NBT_struct",
"commands_to_structure",
"commands_to_redstone_delay_structure",
# MCSTRUCTURE 常量
"AXIS_PARTICULAR_VALUE",
"COMPABILITY_VERSION_117",
"COMPABILITY_VERSION_119",
# BDX 函数
"bdx_move",
"form_command_block_in_BDX_bytes",
"commands_to_BDX_bytes",
# BDX 常量
"BDX_MOVE_KEY",
]
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"))
from .main import *
from .main import ConvertConfig
from .archive import compress_zipfile, behavior_mcpack_manifest
from .bdx import (
BDX_MOVE_KEY,
bdx_move,
form_command_block_in_BDX_bytes,
commands_to_BDX_bytes,
)
from .common import bottem_side_length_of_smallest_square_bottom_box
from .mcstructure import (
antiaxis,
forward_IER,
AXIS_PARTICULAR_VALUE,
COMPABILITY_VERSION_119,
COMPABILITY_VERSION_117,
command_statevalue,
form_note_block_in_NBT_struct,
form_repeater_in_NBT_struct,
form_command_block_in_NBT_struct,
commands_to_structure,
commands_to_redstone_delay_structure,
)

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
"""
用以生成附加包的附加功能
版权所有 © 2024 音·创 开发者
Copyright © 2024 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,543 @@
# -*- coding: utf-8 -*-
"""
版权所有 © 2024 音·创 开发者
Copyright © 2024 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

@@ -5,23 +5,23 @@
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import datetime
import os
import uuid
import zipfile
import datetime
from typing import List, Union, Literal
from typing import List, Literal, Union
def compress_zipfile(sourceDir, outFilename, compression=8, exceptFile=None):

View File

@@ -4,14 +4,14 @@
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
@@ -21,7 +21,7 @@ from ..constants import x, y, z
from ..subclass import SingleCommand
from .common import bottem_side_length_of_smallest_square_bottom_box
bdx_key = {
BDX_MOVE_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"],
@@ -34,21 +34,18 @@ 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]
return BDX_MOVE_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,
)
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(
return BDX_MOVE_KEY[axis][pointer] + value.to_bytes(
2 ** (pointer - 2), "big", signed=True
)
@@ -150,18 +147,20 @@ def commands_to_BDX_bytes(
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)
(
(1 if y_forward else 0)
if (
((now_z != 0) and (not z_forward))
or (z_forward and (now_z != _sideLength - 1))
((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
)
else 5
),
impluse=2,
condition=command.conditional,
@@ -196,13 +195,13 @@ def commands_to_BDX_bytes(
):
now_z -= 1 if z_forward else -1
z_forward = not z_forward
_bytes += bdx_key[x][1]
_bytes += BDX_MOVE_KEY[x][1]
now_x += 1
else:
_bytes += bdx_key[z][int(z_forward)]
_bytes += BDX_MOVE_KEY[z][int(z_forward)]
else:
_bytes += bdx_key[y][int(y_forward)]
_bytes += BDX_MOVE_KEY[y][int(y_forward)]
return (
_bytes,

View File

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

View File

@@ -1,13 +1,13 @@
# -*- coding: utf-8 -*-
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
@@ -18,8 +18,14 @@ 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 ..bdx import (
bdx_move,
commands_to_BDX_bytes,
form_command_block_in_BDX_bytes,
x,
y,
z,
)
from ..main import ConvertConfig

View File

@@ -4,14 +4,14 @@
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
@@ -25,5 +25,3 @@ def bottem_side_length_of_smallest_square_bottom_box(total: int, maxHeight: int)
:param maxHeight: 最大高度
:return: 外切正方形的边长 int"""
return math.ceil(math.sqrt(math.ceil(total / maxHeight)))

View File

@@ -1,23 +0,0 @@
# -*- 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_function_addon_in_score"
]
__author__ = (("金羿", "Eilles Wan"),)
from .main import *

View File

@@ -1,132 +0,0 @@
# -*- 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 ...main import MidiConvert
from ..archive import behavior_mcpack_manifest, compress_zipfile
from ..main import ConvertConfig
def to_function_addon_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,
)
# 将命令列表写入文件
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

View File

@@ -4,22 +4,22 @@
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from dataclasses import dataclass
from typing import Tuple, Union, Literal
from typing import Literal, Tuple, Union
from ..constants import DEFAULT_PROGRESSBAR_STYLE
from ..subclass import DEFAULT_PROGRESSBAR_STYLE, ProgressBarStyle
@dataclass(init=False)
@@ -34,8 +34,8 @@ class ConvertConfig:
speed_multiplier: float
"""速度倍率"""
progressbar_style: Union[Tuple[str, Tuple[str, str]], Literal[None]]
"""进度条样式"""
progressbar_style: Union[ProgressBarStyle, None]
"""进度条样式"""
dist_path: str
"""输出目录"""
@@ -45,7 +45,8 @@ class ConvertConfig:
output_path: str,
volume: float = 1.0,
speed: float = 1.0,
progressbar: Union[bool, Tuple[str, Tuple[str, str]]] = True,
progressbar: Union[bool, Tuple[str, Tuple[str, str]], ProgressBarStyle] = True,
ignore_progressbar_param_error: bool = False,
):
"""
将已经转换好的数据内容指令载入MC可读格式
@@ -77,10 +78,29 @@ class ConvertConfig:
# 改这一段没🐎
if progressbar is True:
self.progressbar_style = DEFAULT_PROGRESSBAR_STYLE
"""进度条样式"""
else:
"""进度条样式"""
return
elif isinstance(progressbar, ProgressBarStyle):
self.progressbar_style = progressbar
"""进度条样式"""
else:
self.progressbar_style = None
"""进度条样式组"""
"""进度条样式"""
return
elif isinstance(progressbar, tuple):
if isinstance(progressbar[0], str) and isinstance(
progressbar[1], tuple
):
if isinstance(progressbar[1][0], str) and isinstance(
progressbar[1][1], str
):
self.progressbar_style = ProgressBarStyle(
progressbar[0], progressbar[1][0], progressbar[1][1]
)
return
if not ignore_progressbar_param_error:
raise TypeError(
"参数 {} 的类型 {} 与所需类型 Union[bool, Tuple[str, Tuple[str, str]], ProgressBarStyle] 不符。".format(
progressbar, type(progressbar)
)
)
self.progressbar_style = None
"""进度条样式组"""

View File

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

View File

@@ -1,24 +1,29 @@
# -*- coding: utf-8 -*-
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import os
from typing import Literal
from ...exceptions import CommandFormatError
# from ...exceptions import CommandFormatError
from ...main import MidiConvert
from ..main import ConvertConfig
from ..mcstructure import commands_to_structure,commands_to_redstone_delay_structure
from ...subclass import SingleCommand
from ..mcstructure import (
COMPABILITY_VERSION_117,
COMPABILITY_VERSION_119,
commands_to_redstone_delay_structure,
commands_to_structure,
)
def to_mcstructure_file_in_delay(
@@ -43,11 +48,14 @@ def to_mcstructure_file_in_delay(
Returns
-------
tuple[tuple[int,]结构大小, int音乐总延迟]
tuple[tuple[int,int,int]结构大小, int音乐总延迟]
"""
if midi_cvt.enable_old_exe_format:
raise CommandFormatError("使用mcstructure结构文件导出时不支持旧版本的指令格式。")
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,
@@ -58,7 +66,9 @@ def to_mcstructure_file_in_delay(
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)
struct, size, end_pos = commands_to_structure(
cmd_list, max_height - 1, compability_version_=compability_ver
)
with open(
os.path.abspath(
@@ -71,12 +81,83 @@ def to_mcstructure_file_in_delay(
return size, max_delay
def to_mcstructure_file_in_score(
midi_cvt: MidiConvert,
data_cfg: ConvertConfig,
scoreboard_name: str = "mscplay",
auto_reset: bool = False,
max_height: int = 64,
):
"""
将midi以延迟播放器形式转换为mcstructure结构文件
def to_mcstructure_file_in_redstone_CD(
Parameters
----------
midi_cvt: MidiConvert 对象
用于转换的MidiConvert对象
data_cfg: ConvertConfig 对象
部分转换通用参数
scoreboard_name: str
我的世界的计分板名称
auto_reset: bool
是否自动重置计分板
max_height: int
生成结构最大高度
Returns
-------
tuple[tuple[int,int,int]结构大小, int音乐总延迟, int指令数量
"""
compability_ver = (
COMPABILITY_VERSION_117
if midi_cvt.enable_old_exe_format
else COMPABILITY_VERSION_119
)
cmd_list, cmd_count, max_delay = 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)
struct, size, end_pos = commands_to_structure(
midi_cvt.music_command_list+(
[
SingleCommand(
command="scoreboard players reset @a[scores={"
+ scoreboard_name
+ "="
+ str(max_delay + 20)
+ "}] "
+ scoreboard_name,
annotation="自动重置计分板",
)
]
if auto_reset
else []
), 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, cmd_count
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+",
axis_side: Literal["z+", "z-", "Z+", "Z-", "x+", "x-", "X+", "X-"] = "z+",
basement_block: str = "concrete",
):
"""
@@ -97,11 +178,14 @@ def to_mcstructure_file_in_redstone_CD(
Returns
-------
tuple[tuple[int,]结构大小, int音乐总延迟]
tuple[tuple[int,int,int]结构大小, int音乐总延迟]
"""
if midi_cvt.enable_old_exe_format:
raise CommandFormatError("使用mcstructure结构文件导出时不支持旧版本的指令格式。")
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,
@@ -112,7 +196,14 @@ def to_mcstructure_file_in_redstone_CD(
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)
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(
@@ -123,4 +214,3 @@ def to_mcstructure_file_in_redstone_CD(
struct.dump(f)
return size, max_delay

View File

@@ -1,23 +0,0 @@
# -*- 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_mcstructure_addon_in_delay"
]
__author__ = (("金羿", "Eilles Wan"),)
from .main import *

View File

@@ -1,205 +0,0 @@
# -*- 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 TrimMCStruct import Structure
from ...exceptions import CommandFormatError
from ...main import MidiConvert
from ..archive import behavior_mcpack_manifest, compress_zipfile
from ..main import ConvertConfig
from ..mcstructure import commands_to_structure, form_command_block_in_NBT_struct
def to_mcstructure_addon_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[int指令数量, int音乐总延迟]
"""
if midi_cvt.enable_old_exe_format:
raise CommandFormatError("使用mcstructure结构文件导出时不支持旧版本的指令格式。")
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,
)
# 将命令列表写入文件
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)
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),
)
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="显示进度条并加分",
),
)
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,
)
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="重置进度条计分板",
),
)
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

@@ -4,14 +4,14 @@
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
@@ -23,16 +23,16 @@ 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
import struct
def antiaxis(axis: Literal['x','z','X','Z']):
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,
@@ -48,7 +48,19 @@ AXIS_PARTICULAR_VALUE = {
},
}
def command_statevalue(axis_: Literal['x','y','z','X','Y','Z'],forward_:bool):
# 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_]
@@ -57,6 +69,7 @@ def form_note_block_in_NBT_struct(
coordinate: Tuple[int, int, int],
instrument: str = "note.harp",
powered: bool = False,
compability_version_number: int = COMPABILITY_VERSION_119,
):
"""生成音符盒方块
:param note: `int`(0~24)
@@ -87,10 +100,15 @@ def form_note_block_in_NBT_struct(
"z": coordinate[2],
} # type: ignore
},
compability_version=compability_version_number,
)
def form_repeater_in_NBT_struct(delay: int, facing: int):
def form_repeater_in_NBT_struct(
delay: int,
facing: int,
compability_version_number: int = COMPABILITY_VERSION_119,
):
"""生成中继器方块
:param facing: 朝向:
Z- 北 0
@@ -107,6 +125,7 @@ def form_repeater_in_NBT_struct(delay: int, facing: int):
"repeater_delay": delay,
"direction": facing,
},
compability_version=compability_version_number,
)
@@ -121,6 +140,7 @@ def form_command_block_in_NBT_struct(
customName: str = "",
executeOnFirstTick: bool = False,
trackOutput: bool = True,
compability_version_number: int = COMPABILITY_VERSION_119,
):
"""
使用指定项目返回指定的指令方块结构
@@ -169,9 +189,11 @@ def form_command_block_in_NBT_struct(
return Block(
"minecraft",
"command_block"
if impluse == 0
else ("repeating_command_block" if impluse == 1 else "chain_command_block"),
(
"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": {
@@ -199,13 +221,14 @@ def form_command_block_in_NBT_struct(
"z": coordinate[2],
} # type: ignore
},
compability_version=17959425,
compability_version=compability_version_number,
)
def commands_to_structure(
commands: List[SingleCommand],
max_height: int = 64,
compability_version_: int = COMPABILITY_VERSION_119,
):
"""
:param commands: 指令列表
@@ -218,7 +241,8 @@ def commands_to_structure(
)
struct = Structure(
(_sideLength, max_height, _sideLength), # 声明结构大小
size=(_sideLength, max_height, _sideLength), # 声明结构大小
compability_version=compability_version_,
)
y_forward = True
@@ -235,18 +259,20 @@ def commands_to_structure(
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)
particularValue=(
(1 if y_forward else 0)
if (
((now_z != 0) and (not z_forward))
or (z_forward and (now_z != _sideLength - 1))
((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
)
else 5
),
impluse=2,
condition=False,
@@ -255,6 +281,7 @@ def commands_to_structure(
customName=command.annotation_text,
executeOnFirstTick=False,
trackOutput=True,
compability_version_number=compability_version_,
),
)
@@ -284,12 +311,14 @@ def commands_to_structure(
(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+",
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: 指令列表
@@ -322,70 +351,137 @@ def commands_to_redstone_delay_structure(
else:
raise ValueError(f"axis_({axis_}) 参数错误。")
goahead = forward_IER(forward)
struct = Structure(
(round(delay_length/2+0.5+len(commands)) if extensioon_direction == x else max_multicmd_length, 3, round(delay_length/2+0.5+len(commands)) if extensioon_direction == z else max_multicmd_length)
)
pos_now = {x:(0 if forward else struct.size[0]),y:0,z:(0 if forward else struct.size[2])}
command_actually_length = sum([int(bool(cmd.delay)) for cmd in commands])
first_impluse = True
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:
single_repeater_value = round(cmd.delay / 2) % 4 - 1
additional_repeater = round(cmd.delay / 2)// 4
for i in range(additional_repeater):
# 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(
tuple(pos_now.values()),
Block("minecraft", base_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],1,pos_now[z]),
form_repeater_in_NBT_struct(
delay=3,
facing=repeater_facing,
(pos_now[x], 2, pos_now[z]),
Block(
"minecraft",
"redstone_wire",
compability_version=compability_version_,
),
)
pos_now[extensioon_direction] += goahead
first_impluse = True
if single_repeater_value >= 0:
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(
tuple(pos_now.values()),
Block("minecraft", base_block,),
)
struct.set_block(
(pos_now[x],1,pos_now[z]),
form_repeater_in_NBT_struct(
delay=single_repeater_value,
facing=repeater_facing,
(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_,
),
)
pos_now[extensioon_direction] += goahead
first_impluse = True
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=0,
customName=cmd.annotation_text,
),
)
struct.set_block(
(pos_now[x],2,pos_now[z]),
Block("minecraft", "redstone_wire"),
)
pos_now[extensioon_direction] += goahead
first_impluse = False
return struct, struct.size, tuple(pos_now.values())
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

@@ -4,14 +4,14 @@
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
@@ -19,6 +19,7 @@ Terms & Conditions: License.md in the root directory
from ..exceptions import NotDefineProgramError, ZeroSpeedError
from ..main import MidiConvert
from ..subclass import SingleCommand
from ..utils import inst_to_sould_with_deviation, perc_inst_to_soundID_withX
# 你以为写完了吗?其实并没有
@@ -68,15 +69,15 @@ def to_note_list(
elif msg[0] == "NoteS":
try:
soundID, _X = (
midi_cvt.perc_inst_to_soundID_withX(InstID)
perc_inst_to_soundID_withX(InstID)
if SpecialBits
else midi_cvt.inst_to_souldID_withX(InstID)
else inst_to_sould_with_deviation(InstID)
)
except UnboundLocalError as E:
soundID, _X = (
midi_cvt.perc_inst_to_soundID_withX(-1)
perc_inst_to_soundID_withX(-1)
if SpecialBits
else midi_cvt.inst_to_souldID_withX(-1)
else inst_to_sould_with_deviation(-1)
)
score_now = round(msg[-1] / float(speed) / 50)
# print(score_now)

View File

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

View File

@@ -2,14 +2,14 @@
"""
用以生成Schematic结构的附加功能
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md

View File

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

View File

@@ -4,38 +4,37 @@
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 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)
print("对象已被连接")
await self.send_command("list", callback=self.list)
await self.subscribe("PlayerMessage", callback=self.player_message)
async def on_disconnect(self) -> None:
print('对象停止连接')
print("对象停止连接")
async def on_receive(self, response) -> None:
print('已接收非常规回复 {}'.format(response))
print("已接收非常规回复 {}".format(response))
async def list(self, response) -> None:
print('已收取指令执行回复 {}'.format(response))
print("已收取指令执行回复 {}".format(response))
async def player_message(self, response) -> None:
print('已收取玩家事件回复 {}'.format(response))
print("已收取玩家事件回复 {}".format(response))

View File

@@ -2,14 +2,14 @@
"""
用以启动WebSocket服务器播放的附加功能
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md

View File

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

View File

@@ -4,23 +4,23 @@
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from typing import Dict, List, Tuple, Union
from .exceptions import *
from .main import MidiConvert, mido
from .subclass import *
from .types import ChannelType
from .utils import *
from .constants import *
class ObsoleteMidiConvert(MidiConvert):
@@ -29,6 +29,82 @@ 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",
@@ -78,9 +154,13 @@ class ObsoleteMidiConvert(MidiConvert):
)
maxscore = max(maxscore, nowscore)
if msg.channel == 9:
soundID, _X = self.perc_inst_to_soundID_withX(instrumentID)
soundID, _X = inst_to_sould_with_deviation(
instrumentID, MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE
)
else:
soundID, _X = self.inst_to_souldID_withX(instrumentID)
soundID, _X = inst_to_sould_with_deviation(
instrumentID, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE
)
singleTrack.append(
"execute @a[scores={"
@@ -99,10 +179,8 @@ class ObsoleteMidiConvert(MidiConvert):
return [tracks, commands, maxscore]
def _toCmdList_m1(
self,
scoreboardname: str = "mscplay",
volume: float = 1.0,
speed: float = 1.0) -> list:
self, scoreboardname: str = "mscplay", volume: float = 1.0, speed: float = 1.0
) -> list:
"""
使用Dislink Sforza的转换思路将midi转换为我的世界命令列表
:param scoreboardname: 我的世界的计分板名称
@@ -119,8 +197,7 @@ class ObsoleteMidiConvert(MidiConvert):
commands = 0
maxscore = 0
for i, track in enumerate(self.midi.tracks):
for i, track in enumerate(self.midi.tracks): # type:ignore
ticks = 0
instrumentID = 0
singleTrack = []
@@ -137,18 +214,25 @@ class ObsoleteMidiConvert(MidiConvert):
instrumentID = msg.program
if msg.type == "note_on" and msg.velocity != 0:
nowscore = round(
(ticks * tempo)
/ ((self.midi.ticks_per_beat * float(speed)) * 50000)
(ticks * tempo) / ((self.midi.ticks_per_beat * float(speed)) * 50000) # type: ignore
)
maxscore = max(maxscore, nowscore)
soundID, _X = self.__Inst2soundID_withX(instrumentID)
if msg.channel == 9:
soundID, _X = inst_to_sould_with_deviation(
instrumentID, MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE
)
else:
soundID, _X = inst_to_sould_with_deviation(
instrumentID, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE
)
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)}")
"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)
@@ -157,7 +241,7 @@ class ObsoleteMidiConvert(MidiConvert):
# 原本这个算法的转换效果应该和上面的算法相似的
def _toCmdList_m2(
self: MidiConvert,
self,
scoreboard_name: str = "mscplay",
MaxVolume: float = 1.0,
speed: float = 1.0,
@@ -194,16 +278,20 @@ class ObsoleteMidiConvert(MidiConvert):
nowTrack = []
for track_no, track in self.channels[i].items():
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 = (
self.perc_inst_to_soundID_withX(msg[1])
inst_to_sould_with_deviation(
msg[1], MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE
)
if SpecialBits
else self.inst_to_souldID_withX(InstID)
else inst_to_sould_with_deviation(
InstID, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE
)
)
score_now = round(msg[-1] / float(speed) / 50)
maxScore = max(maxScore, score_now)
@@ -226,7 +314,7 @@ class ObsoleteMidiConvert(MidiConvert):
return tracks, cmdAmount, maxScore
def _toCmdList_withDelay_m1(
self: MidiConvert,
self,
MaxVolume: float = 1.0,
speed: float = 1.0,
player: str = "@a",
@@ -267,7 +355,15 @@ class ObsoleteMidiConvert(MidiConvert):
(ticks * tempo)
/ ((self.midi.ticks_per_beat * float(speed)) * 50000)
)
soundID, _X = self.inst_to_souldID_withX(instrumentID)
if msg.channel == 9:
soundID, _X = inst_to_sould_with_deviation(
instrumentID, MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE
)
else:
soundID, _X = inst_to_sould_with_deviation(
instrumentID, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE
)
try:
tracks[now_tick].append(
self.execute_cmd_head.format(player)
@@ -302,7 +398,7 @@ class ObsoleteMidiConvert(MidiConvert):
return [results, max(all_ticks)]
def _toCmdList_withDelay_m2(
self: MidiConvert,
self,
MaxVolume: float = 1.0,
speed: float = 1.0,
player: str = "@a",
@@ -334,16 +430,20 @@ class ObsoleteMidiConvert(MidiConvert):
else:
SpecialBits = False
for track_no, track in self.channels[i].items():
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 = (
self.perc_inst_to_soundID_withX(msg[1])
inst_to_sould_with_deviation(
msg[1], MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE
)
if SpecialBits
else self.inst_to_souldID_withX(InstID)
else inst_to_sould_with_deviation(
InstID, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE
)
)
score_now = round(msg[-1] / float(speed) / 50)

View File

@@ -5,22 +5,23 @@
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from dataclasses import dataclass
from typing import Optional
from typing import Any
from .types import Optional, Any, List, Mapping
from .constants import PERCUSSION_INSTRUMENT_LIST
from .constants import MC_PERCUSSION_INSTRUMENT_LIST
@dataclass(init=False)
@@ -45,14 +46,22 @@ class SingleNote:
track_no: int
"""音符所处的音轨"""
percussive: bool
"""是否为打击乐器"""
extra_info: Any
"""你觉得放什么好?"""
def __init__(
self,
instrument: int,
pitch: int,
velocity: int,
startTime: int,
lastTime: int,
startime: int,
lastime: int,
track_number: int = 0,
is_percussion: Optional[bool] = None,
extra_information: Any = None,
):
"""用于存储单个音符的类
:param instrument 乐器编号
@@ -67,44 +76,98 @@ class SingleNote:
"""音符编号"""
self.velocity: int = velocity
"""力度/响度"""
self.start_time: int = startTime
self.start_time: int = startime
"""开始之时 ms"""
self.duration: int = lastTime
self.duration: int = lastime
"""音符持续时间 ms"""
self.track_no: int = track_number
"""音符所处的音轨"""
self.percussive = (
(is_percussion in MC_PERCUSSION_INSTRUMENT_LIST)
if (is_percussion is None)
else is_percussion
)
"""是否为打击乐器"""
self.extra_info = extra_information
@property
def inst(self):
def inst(self) -> int:
"""乐器编号"""
return self.instrument
@inst.setter
def inst(self, inst_):
def inst(self, inst_: int):
self.instrument = inst_
@property
def pitch(self):
def pitch(self) -> int:
"""音符编号"""
return self.note
def __str__(self):
return (
f"Note(inst = {self.inst}, pitch = {self.note}, velocity = {self.velocity}, "
f"startTime = {self.start_time}, lastTime = {self.duration}, )"
# @property
# def get_mc_pitch(self,table: Dict[int, Tuple[str, int]]) -> float:
# self.mc_sound_ID, _X = inst_to_sould_with_deviation(self.inst,table,"note.bd" if self.percussive else "note.flute",)
# return -1 if self.percussive else 2 ** ((self.note - 60 - _X) / 12)
def set_info(self, sth: Any):
"""设置附加信息"""
self.extra_info = sth
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.inst, self.note, self.velocity, self.start_time, self.duration
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 {
"inst": self.inst,
"pitch": self.note,
"velocity": self.velocity,
"startTime": self.start_time,
"lastTime": self.duration,
}
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__):
@@ -222,7 +285,7 @@ class SingleNoteBox:
self.annotation_text = annotation
"""音符注释"""
if percussion is None:
self.is_percussion = percussion in PERCUSSION_INSTRUMENT_LIST
self.is_percussion = percussion in MC_PERCUSSION_INSTRUMENT_LIST
else:
self.is_percussion = percussion
@@ -277,3 +340,62 @@ class SingleNoteBox:
if not isinstance(other, self.__class__):
return False
return self.__str__() == other.__str__()
@dataclass(init=False)
class ProgressBarStyle:
"""进度条样式类"""
base_style: str
"""基础样式"""
to_play_style: str
"""未播放之样式"""
played_style: str
"""已播放之样式"""
def __init__(self, base_s: str, to_play_s: str, played_s: str):
"""用于存储进度条样式的类
:param base_s 基础样式,用以定义进度条整体
:param to_play_s 进度条样式:尚未播放的样子
:param played_s 已经播放的样子"""
self.base_style = base_s
self.to_play_style = to_play_s
self.played_style = played_s
def set_base_style(self, value: str):
"""设置基础样式"""
self.base_style = value
def set_to_play_style(self, value: str):
"""设置未播放之样式"""
self.to_play_style = value
def set_played_style(self, value: str):
"""设置已播放之样式"""
self.played_style = value
def copy(self):
dst = ProgressBarStyle(self.base_style, self.to_play_style, self.played_style)
return dst
DEFAULT_PROGRESSBAR_STYLE = ProgressBarStyle(
r"%%N [ %%s/%^s %%% __________ %%t|%^t ]",
r"§e=§r",
r"§7=§r",
)
"""
默认的进度条样式
"""
NoteChannelType = Mapping[
int,
List[SingleNote,],
]
"""
频道信息类型
Dict[int,Dict[int,List[SingleNote,],],]
"""

64
Musicreater/types.py Normal file
View File

@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
"""
存放数据类型的定义
"""
"""
版权所有 © 2024 音·创 开发者
Copyright © 2024 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,
Iterable,
Sequence,
Mapping,
Callable,
)
MidiNoteNameTableType = Mapping[int, Tuple[str, ...]]
"""
Midi音符名称对照表类型
"""
MidiInstrumentTableType = Mapping[int, Tuple[str, int]]
"""
Midi乐器对照表类型
"""
FittingFunctionType = Callable[[float], float]
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],]],],]
"""

View File

@@ -4,18 +4,33 @@
"""
"""
版权所有 © 2023 音·创 开发者
Copyright © 2023 all the developers of Musicreater
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿组织 开发交流群 861684859
# 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from typing import Any, Dict
import math
import random
from .constants import MM_INSTRUMENT_DEVIATION_TABLE, MC_INSTRUMENT_BLOCKS_TABLE
from .subclass import SingleNote
from .types import (
Any,
Dict,
Tuple,
Optional,
Callable,
Literal,
Union,
MidiInstrumentTableType,
)
def mctick2timestr(mc_tick: int) -> str:
@@ -37,3 +52,174 @@ def empty_midi_channels(channel_count: int = 17, staff: Any = {}) -> Dict[int, A
) # 这告诉我们你不能忽略任何一个复制的序列因为它真的我哭死折磨我一整天全在这个bug上了
for i in range(channel_count)
)
def inst_to_sould_with_deviation(
instrumentID: int,
reference_table: MidiInstrumentTableType,
default_instrument: str = "note.flute",
default_deviation: Optional[int] = 5,
) -> 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
reference_table: Dict[int, Tuple[str, int]]
转换乐器参照表
Returns
-------
tuple(str我的世界乐器名, int转换算法中的X)
"""
return reference_table.get(
instrumentID,
(
default_instrument,
(
default_deviation
if default_deviation
else MM_INSTRUMENT_DEVIATION_TABLE.get(default_instrument, -1)
),
),
)
# 明明已经走了
# 凭什么还要在我心里留下缠绵缱绻
def natural_curve(
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
)
def straight_line(vol: float) -> float:
"""
midi力度值拟合成的距离函数
Parameters
----------
vol: int
midi音符力度值
Returns
-------
float播放中心到玩家的距离
"""
return vol / -8 + 16
def note_to_command_parameters(
note_: SingleNote,
reference_table: MidiInstrumentTableType,
volume_percentage: float = 1,
volume_processing_method: Callable[[float], float] = natural_curve,
) -> Tuple[
str,
float,
float,
Union[float, Literal[None]],
]:
"""
将音符转为播放的指令
:param note_:int 音符对象
:param reference_table:Dict[int, Tuple[str, int]] 转换对照表
:param volume_percentage:int 音量占比(0,1]
:param volume_proccessing_method:Callable[[float], float]: 音量处理函数
:return str[我的世界音符ID], float[播放距离], float[指令音量参数], float[指令音调参数]
"""
mc_sound_ID, deviation = inst_to_sould_with_deviation(
note_.inst,
reference_table,
"note.bd" if note_.percussive else "note.flute",
)
# delaytime_now = round(self.start_time / float(speed) / 50)
mc_pitch = None if note_.percussive else 2 ** ((note_.note - 60 - deviation) / 12)
mc_distance_volume = volume_processing_method(note_.velocity * volume_percentage)
return mc_sound_ID, mc_distance_volume, volume_percentage, mc_pitch
def from_single_note(
note_: SingleNote, random_select: bool = False, default_block: str = "air"
):
"""
将我的世界乐器名改作音符盒所需的对应方块名称
Parameters
----------
note_: SingleNote
音符类
random_select: bool
是否随机选取对应方块
default_block: str
查表查不到怎么办?默认一个!
Returns
-------
str方块名称
"""
pass
# return SingleNoteBox() # TO-DO
@staticmethod
def soundID_to_blockID(
sound_id: str, random_select: bool = False, default_block: str = "air"
) -> str:
"""
将我的世界乐器名改作音符盒所需的对应方块名称
Parameters
----------
sound_id: str
将我的世界乐器名
random_select: bool
是否随机选取对应方块
default_block: str
查表查不到怎么办?默认一个!
Returns
-------
str方块名称
"""
if random_select:
return random.choice(MC_INSTRUMENT_BLOCKS_TABLE.get(sound_id, (default_block,)))
else:
return MC_INSTRUMENT_BLOCKS_TABLE.get(sound_id, (default_block,))[0]

70
Packer/MSCT_Packer.py Normal file
View File

@@ -0,0 +1,70 @@
import Musicreater
import Musicreater.experiment
import Musicreater.previous
import Musicreater.plugin
from Musicreater.plugin.addonpack import (
to_addon_pack_in_delay,
to_addon_pack_in_repeater,
to_addon_pack_in_score,
)
from Musicreater.plugin.mcstructfile import (
to_mcstructure_file_in_delay,
to_mcstructure_file_in_repeater,
to_mcstructure_file_in_score,
)
from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score
MSCT_MAIN = (
Musicreater,
Musicreater.experiment,
Musicreater.previous,
)
MSCT_PLUGIN = (Musicreater.plugin,)
MSCT_PLUGIN_FUNCTION = (
to_addon_pack_in_delay,
to_addon_pack_in_repeater,
to_addon_pack_in_score,
to_mcstructure_file_in_delay,
to_mcstructure_file_in_repeater,
to_mcstructure_file_in_score,
to_BDX_file_in_delay,
to_BDX_file_in_score,
)
import hashlib
import dill
import brotli
def enpack_llc_pack(sth, to_dist: str):
packing_bytes = brotli.compress(
dill.dumps(
sth,
)
)
with open(
to_dist,
"wb",
) as f:
f.write(packing_bytes)
return hashlib.sha256(packing_bytes)
with open("./Packer/checksum.txt", "w", encoding="utf-8") as f:
f.write("MSCT_MAIN:\n")
f.write(enpack_llc_pack(MSCT_MAIN, "./Packer/MSCT_MAIN.MPK").hexdigest())
f.write("\nMSCT_PLUGIN:\n")
f.write(enpack_llc_pack(MSCT_PLUGIN, "./Packer/MSCT_PLUGIN.MPK").hexdigest())
f.write("\nMSCT_PLUGIN_FUNCTION:\n")
f.write(
enpack_llc_pack(
MSCT_PLUGIN_FUNCTION, "./Packer/MSCT_PLUGIN_FUNCTION.MPK"
).hexdigest()
)

View File

@@ -10,7 +10,7 @@
<h3 align="center">一款免费开源的我的世界数字音频转换库</h3>
<p align="center">
<img src="https://forthebadge.com/images/badges/built-with-love.svg">
<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>
@@ -42,21 +42,15 @@
- 使用 pypi
```bash
pip install Musicreater
pip install --upgrade Musicreater
```
- 如果出现错误可以尝试
- 如果无法更新最新可以尝试
```bash
pip install -i https://pypi.python.org/simple Musicreater
pip install --upgrade -i https://pypi.python.org/simple Musicreater
```
- 升级
```bash
pip install -i https://pypi.python.org/simple Musicreater --upgrade
```
- 克隆仓库并安装
- 克隆仓库并安装最新版本但**不推荐**
```bash
git clone https://gitee.com/TriM-Organization/Musicreater.git
cd Musicreater
@@ -73,9 +67,11 @@
## 作者 ✒
金羿 Eilles我的世界基岩版指令师个人开发者B 站不知名 UP 江西在校高中生
**金羿 Eilles**我的世界基岩版指令师个人开发者B 站不知名 UP 江西在校高中生
诸葛亮与八卦阵 bgArray我的世界基岩版玩家喜欢编程和音乐深圳初二学生
**诸葛亮与八卦阵 bgArray**我的世界基岩版玩家喜欢编程和音乐深圳初二学生
**偷吃不是Touch Touch**我的世界基岩版指令师提供 BDX 导入测试支持
## 致谢 🙏
@@ -84,18 +80,18 @@
- 感谢 **昀梦**\<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/)给我们的开发与更新带来巨大的压力和动力让我们在原本一骑绝尘的摸鱼道路上转向开发希望他能考上一个理想的大学
- 感谢 **Touch 偷吃**\<QQ1793537164\> 提供的 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)** 对我们项目的支持与宣传希望他能考得一所优秀的大学
- 感谢 **[神羽](https://gitee.com/snowykami) [SnowyKami](https://github.com/snowyfirefly)** 对我们项目的支持与宣传非常感谢他为我们提供的服务器
- 感谢 **指令师\_苦力怕 playjuice123**\<QQ240667197\> 为我们的程序找出错误并提醒我们修复一个一直存在的大 bug
- 感谢 **雷霆**\<QQ3555268519\> 用他那令所有开发者都大为光火的操作方法为我们的程序找出错误并提醒修复 bug
- 感谢 **小埋**\<2039310975\> 反馈附加包生成时缺少描述和标题的问题
- 感谢 **小埋**\<QQ2039310975\> 反馈附加包生成时缺少描述和标题的问题
- <table><tr><td>感谢 **油炸**\<QQ2836146704\> 激励我们不断开发新的内容</td><td><img height="50" src="https://foruda.gitee.com/images/1695478907647543027/08ea9909_9911226.jpeg"></td></tr></table>
> 感谢广大群友为此程序提供的测试等支持
> 感谢广大群友为此提供的测试和建议
>
> 若您对我们有所贡献但您的名字没有显示在此列表中请联系我们
> 若您对我们有所贡献但您的名字没有出现在此列表中请联系我们
## 联系 📞
@@ -103,6 +99,8 @@
如果需要与开发组进行交流欢迎加入我们的[开发闲聊 Q ](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)
亦可以联系我们[睿乐组织官方邮箱](mailto:TriM-Organization@hotmail.com)取得进一步联系
---
此项目并非一个官方 我的世界_Minecraft_项目

View File

@@ -10,7 +10,7 @@
<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://forthebadge.com/images/badges/built-with-love.svg">
<img src="https://img.shields.io/badge/BUILD%20WITH%20LOVE-FF3432?style=for-the-badge">
</img>
<p>
@@ -28,7 +28,7 @@
[简体中文 🇨🇳](README.md) | English🇬🇧
**Notice that the language translation of _Musicreater_ may be a little SLOW.**
**Notice that the localizations of documents may NOT be up-to-date.**
## Introduction🚀
@@ -41,21 +41,15 @@ Welcome to join our QQ group: [861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr
- Via pypi
```bash
pip install Musicreater
pip install Musicreater --upgrade
```
- If not work, also try:
```bash
pip install -i https://pypi.python.org/simple Musicreater
```
- Update:
- If above command cannot fetch latest version, try:
```bash
pip install -i https://pypi.python.org/simple Musicreater --upgrade
```
- Clone repo and Install:
- Clone repo and Install (Latest but **NOT RECOMMANDED**):
```bash
git clone https://github.com/TriM-Organization/Musicreater.git
cd Musicreater
@@ -65,7 +59,7 @@ Welcome to join our QQ group: [861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr
Commands such as `python``pip` could be changed to some like `python3` or `pip3` according to the difference of platforms.
## Documentation📄
## Documentation 📄
(Not in English yet)
@@ -73,37 +67,42 @@ Commands such as `python`、`pip` could be changed to some like `python3` or `pi
[仓库 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✒
### Authors
Eilles (金羿)A senior high school student, individual developer, unfamous Bilibili UPer, which knows a little about commands in _Minecraft: Bedrock Edition_
**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.
**bgArray (诸葛亮与八卦阵)**: A junior high school student, player of _Minecraft: Bedrock Edition_, which is a fan of music and programming.
## Thanks🙏
**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
## Acknowledgements 🙏
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. We hope he can get into a good university as he wantted to!
- Thank _Touch 偷吃_\<QQ1793537164\> for support of debugging and testing program and algorithm, as well his/her suggestions to the improvement of our project
- 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. Hope he could get into a good university!
- 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 a lot of groupmates' support and help
> Thanks for the support and help of a lot of groupmates
>
> If you have given contribution but haven't been in the list, please contact us!
> If you have given contributions but have not been in the list, please contact us!
## 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.

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

@@ -1,12 +1,10 @@
<h1 align="center">· Musicreater</h1>
<h2 align="center">库版 Package Version</h2>
<p align="center">
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
</p>
**此为开发相关文档内容包括所生成文件结构的详细说明特殊参数的详细解释**
**此为开发相关文档内容包括库的简单调用所生成文件结构的详细说明特殊参数的详细解释**
# 库的简单调用
@@ -91,7 +89,7 @@
progressbar_style, # 进度条样式组(详见下方)
)
# 同上面生成播放指令的理,进度条指令也存储至对象内
# 同上面生成播放指令的理,进度条指令也存储至对象内
print(
"进度条指令如下:\n{}".format(
cvt_mid.progress_bar_command
@@ -101,13 +99,15 @@
在上面的代码中进度条样式是可以自定义的详见[下方说明](%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` 对象转换为相应格式
- 转换成指令是一个方面接下来是再转换为可以导入MC的格式我们提供了 **·** 内置的附加组件可以借助 `MidiConvert` 对象转换为相应格式
```python
# 导入 Musicreater
import Musicreater
# 导入附加组件功能
import Musicreater.plugin
# 导入相应的文件格式转换功能
# 转换为函数附加包
@@ -122,7 +122,6 @@
import Musicreater.plugin.websocket
# 定义转换参数
cvt_cfg = Musicreater.plugin.ConvertConfig(
output_path,
@@ -131,9 +130,17 @@
progressbar, # 进度条样式组(详见下方)
)
# 懒得写了,等哪天有空写吧,后面的没用,是老的。
# 使用附加组件转换,其调用的函数应为:
# Musicreater.plugin.输出格式.播放器格式
# 值得注意的是,并非所有输出格式都支持所有播放器格式
# 调用的时候还请注意甄别
# 例如,以下函数是将 MidiConvert 对象 cvt_mid
# 以 cvt_cfg 指定的参数
# 以延迟播放器转换为 mcstructure 文件
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay(
cvt_mid,
cvt_cfg,
)
```
@@ -147,49 +154,29 @@
|指令链|与链式指令方块不同一个指令链通常指代的是一串由某种非链式指令方块作为开头后面连着一串链式指令方块的结构|通常的链都应用于需要单次激活而多指令的简单功能|
|起始块|链最初的那个非链式指令方块|此方块为脉冲方块或重复方块皆可|
|指令系统系统|指令系统通常指的是由一个或多个指令链以及相关红石机构相互配合一同组成的为达到某种特定的功能而构建的整体结构|通常的系统都应用于需要综合调配指令的复杂功能可由多个实现不同功能的模块构成不同系统之间可以相互调用各自的模块|
|游戏刻|游戏的一刻是指我的世界的游戏循环运行一次所占用的时间[详见我的世界中文维基](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#%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}$ 秒左右的延迟。但是,同理于游戏刻,一秒对应的红石刻是不定的。|
## 文件格式
1. 附加包格式`.mcpack`
使用附加包格式导出音乐则音乐会以指令函数文件`.mcfunction`存储于附加包内在附加包中函数文件的存储结构应为
- `functions\`
- `index.mcfunction`
- `mscply\`
- `progressShow.mcfunction`
- `track1.mcfunction`
- `track2.mcfunction`
- ...
- `trackN.mcfunction`
如图其中`index.mcfunction`文件和`mscply`文件夹存在于函数目录的根下`mscply`目录中包含音乐导出的众多音轨播放文件`trackX.mcfunction`同时若生成此包时选择了带有进度条的选项则会包含`progressShow.mcfunction`文件
`index.mcfunction`用于开始播放其中包含打开各个音轨对应函数的指令以及加分指令这里的加分是将**播放计分板的值大于等于`1`**的所有**玩家**的播放计分板分数增加`1`同时若生成此包时选择了自动重置计分板的选项则会包含一条重置计分板的指令
> 你知道吗·创的最早期版本我的世界函数音乐生成器正是用函数来播放不过这个版本采取的读入数据的形式大有不同
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`如此往复直至指令链堆叠完成
## 播放器
以结构生成的文件可以采用多种方式播放一类播放方式我们称其为**播放器**例如**延迟播放器****计分板播放器**等等以后推出的新的播放器届时也会在此处更新
**·**生成的文件可以采用多种方式播放一类播放方式我们称其为**播放器**例如**延迟播放器****计分板播放器**等等以后推出的新的播放器届时也会在此处更新
为什么要设计这么多播放器是为了适应不同的播放环境需要通常情况下一个音乐中含有多个音符音符与音符之间存在间隔这里就产生了不一样的实现音符间时间间隔的方式而不同的应用环境下又会产生不一样的要求接下来将对不同的播放器进行详细介绍
### 参数释义
|参数|说明|备注|
|--------|-----------|----------|
|`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://zh.minecraft.wiki/wiki/%E9%9F%B3%E7%AC%A6%E7%9B%92#%E4%B9%90%E5%99%A8),我们通过音调偏移来进行调整。|
### 播放器内容
1. 计分板播放器
计分板播放器是一种传统的我的世界音乐播放方式通过对于计分板加分来实现播放不同的音符一个很简单的原理就是**用不同的计分板分值对应不同的音符**再通过加分来达到那个分值即播放出来
@@ -200,14 +187,7 @@
execute @a[scores={ScBd=x}] ~ ~ ~ playsound InstID @s ^ ^ ^Ht Vlct Ptc
```
|参数|说明|备注|
|--------|-----------|----------|
|`ScBd`|指定的计分板名称||
|`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}$ |
后四个参数决定了这个音的性质而前两个参数仅仅是为了决定音播放的时间
@@ -215,32 +195,78 @@
延迟播放器是通过我的世界游戏中指令方块的设置项延迟刻数来达到定位音符的效果**将所有的音符依照其播放时距离乐曲开始时的时间毫秒放在一个序列内再计算音符两两之间对应的时间差值转换为我的世界内对应的游戏刻数之后填入指令方块的设置中**
·由于此方式播放的音乐不需要用计分板所以播放指令是这样的
**·**由于此方式播放的音乐不需要用计分板所以播放指令是这样的
```mcfunction
execute Tg ~ ~ ~ playsound InstID @s ^ ^ ^Ht Vlct Ptc
```
|参数|说明|备注|
|--------|-----------|----------|
|`Tg`|播放对象|选择器或玩家名|
|`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}$ |
其中后四个参数决定了这个音的性质
由于这样的延迟数据是依赖于指令方块的设置项所以使用这种播放器所转换出的结果仅可以存储在包含方块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$如此往复直至指令链堆叠完成
# 进度条自定义
因为我们提供了可以自动转换进度条的功能因此在这里给出进度条自定义参数的详细解释
请注意并非所有的演示样例程序都支持自定义进度条
一个进度条明显地**固定部分****可变部分**来构成而可变部分又包括了文字和图形两种当然我的世界里头的进度条可变的图形也就是那个这一点你需要了解因为后文中包含了很多这方面的概念需要你了解
进度条的自定义功能使用一个字符串来定义自己的样式其中包含众多**标识符**来表示可变部分
@@ -259,23 +285,31 @@
表示进度条占位的 `_` 是用来标识你的进度条的也就是可变部分的唯一的图形部分
**样式定义字符串**的样例如下这也是默认进度条的样式
**样式定义字符串基础样式**的样例如下这也是默认进度条的基础样式
` %%N [ %%s/%^s %%% __________ %%t|%^t]`
``` %%N [ %%s/%^s %%% __________ %%t|%^t]```
这是单独一行的进度条当然你也可以制作多行的如果是一行的输出时所使用的指令便是 `title`而如果是多行的话输出就会用 `titleraw` 作为进度条字幕
哦对了上面的只不过是样式定义同时还需要定义的是可变图形的部分也就是进度条上那个真正的
对于这个我们就采用了固定参数的方法对于一个进度条无非就是已经播放过的没播放过的两种形态所以使用一个元组来传入这两个参数就是最简单的了元组的格式也很简单`(str: 播放过的部分长啥样, str: 没播放过的部分长啥样)` 例如我们默认的进度的定义是这样的
对于这个我们就采用了固定参数的方法对于一个进度条无非就是已经播放过的没播放过的两种形态例如我们默认的进度**可变样式**的定义是这样的
`('§e=§r', '§7=§r')`
**可变样式甲已播放样式**`'§e=§r'`
综合起来把这些参数传给函数需要一个参数整合你猜用的啥啊对对对我用的还是元组
**可变样式乙未播放样式**`'§7=§r'`
综合起来把这些参数传给函数需要一个参数整合使用位于 `Musicreater/subclass.py` 下的 `ProgressBarStyle` 类进行定义
我们的默认定义参数如下
`(r'%%N [ %%s/%^s %%% __________ %%t|%^t]',('§e=§r', '§7=§r'))`
```python
DEFAULT_PROGRESSBAR_STYLE = ProgressBarStyle(
r"%%N [ %%s/%^s %%% __________ %%t|%^t ]",
r"§e=§r",
r"§7=§r",
)
```
*为了避免生成错误请尽量避免使用标识符作为定义样式字符串的其他部分*

View File

@@ -1,16 +1,12 @@
<h1 align="center">· Musicreater</h1>
<h2 align="center">库版 Package Version</h2>
<p align="center">
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
</p>
# 生成文件的使用
*由于先前的 **读我文件**(README.md) 过于冗杂现另辟蹊径来给大家全方位的教程*
*这是本库所生成文件的使用声明不是使用本库的教程若要查看**本库的大致文档**可点击[此处](./%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)*
*这是本库所生成文件的使用声明不是使用本库的教程若要查看**本库的文档**可点击[此处](./%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)*
## 附加包格式
@@ -24,6 +20,7 @@
4. 激活循环方块
5. 若想要暂停播放可以停止循环指令方块的激活状态
6. 若想要重置某实体的播放可以将其播放用的计分板重置
7. 若要终止全部玩家的播放在聊天框输入指令 `function stop`
> 其中 步骤三 步骤四 的顺序可以调换
@@ -32,6 +29,7 @@
1. 导入附加包
2. 在聊天框输入指令 `function index`
3. 同时激活所生成的循环和脉冲指令方块
4. 若要终止播放在聊天框输入指令 `function stop` 试试看不确保有用
> 需要注意的是循环指令方块需要一直激活直到音乐结束

View File

@@ -1,9 +1,19 @@
<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
**_使用时请遵循协议规定_**
- 版权所有 © 2024 · 开发者
- Copyright © 2024 all the developers of Musicreater
* 开源相关声明请见 仓库根目录下的 License.md
* Terms & Conditions: License.md in the root directory
@@ -12,187 +22,196 @@
Email TriM-Organization@hotmail.com\
若需转载或借鉴 许可声明请查看仓库根目录下的 License.md
### 名词解释
| 名词 | 说明 |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| 音符名称 | 我的世界游戏内用于播放音乐的 `playsound` 指令所规定的 `Sound ID` |
| 音调偏移参数 | 我的世界不同乐器的音域不同对应的 `pitch` 值也不尽相同该参数的解释请参考[文档说明](库的生成与功能文档.md#参数释义) |
# 乐音乐器
| 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 |
对照表版本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 |
| 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 |

View File

@@ -9,8 +9,8 @@
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
版权所有 © 2024 音·创 开发者
Copyright © 2024 all the developers of Musicreater
开源相关声明请见 ./License.md
Terms & Conditions: ./License.md
@@ -20,9 +20,18 @@ 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.mcstructfile import (
to_mcstructure_file_in_delay,
to_mcstructure_file_in_repeater,
to_mcstructure_file_in_score,
)
from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score
from Musicreater.plugin.funcpack import to_function_addon_in_score
from Musicreater.plugin.mcstructpack import to_mcstructure_addon_in_delay
# 获取midi列表
midi_path = input(f"请输入MIDI路径")
@@ -33,21 +42,33 @@ out_path = input(f"请输入输出路径:")
# 选择输出格式
fileFormat = int(input(f"请输入输出格式[BDX(1) 或 MCPACK(0)]").lower())
playerFormat = int(input(f"请选择播放方式[计分板(1) 或 延迟(0)]").lower())
fileFormat = int(
input(f"请输入输出格式[MCSTRUCTURE(2) 或 BDX(1) 或 MCPACK(0)]").lower()
)
playerFormat = int(input(f"请选择播放方式[红石(2) 或 计分板(1) 或 延迟(0)]").lower())
# 真假字符串判断
def bool_str(sth: str) -> bool:
def bool_str(sth: str):
try:
return bool(float(sth))
except ValueError:
if str(sth).lower() == "true":
except:
if str(sth).lower() in ("true", "", "", "y", "t"):
return True
elif str(sth).lower() == "false":
elif str(sth).lower() in ("false", "", "", "f", "n"):
return False
else:
raise ValueError("布尔字符串啊?")
raise ValueError("非法逻辑字串")
def isin(sth: str, range_list: dict):
sth = sth.lower()
for bool_value, res_list in range_list.items():
if sth in res_list:
return bool_value
raise ValueError(
"不在可选范围内:{}".format([j for i in range_list.values() for j in i])
)
if os.path.exists("./demo_config.json"):
@@ -71,31 +92,63 @@ else:
bool_str,
),
(
f"计分板名称:",
str,
)
if playerFormat == 1
else (
f"玩家选择器:",
str,
(
f"计分板名称:",
str,
)
if playerFormat == 1
else (
f"玩家选择器:",
str,
)
),
(
f"是否自动重置计分板:",
bool_str,
)
if playerFormat == 1
else (),
(
f"是否自动重置计分板:",
bool_str,
)
if playerFormat == 1
else ()
),
(
f"作者名称:",
str,
)
if fileFormat == 1
else (),
()
if playerFormat == 1
else (
f"最大结构高度",
int,
(
f"BDX作者署名",
str,
)
if fileFormat == 1
else (
(
(
"结构延展方向",
lambda a: isin(
a,
{
"z+": ["z+", "Z+"],
"x+": ["X+", "x+"],
"z-": ["Z-", "z-"],
"x-": ["x-", "X-"],
},
),
)
if (playerFormat == 2 and fileFormat == 2)
else ()
),
)
),
(
()
if playerFormat == 1
else (
(
"基础空白方块:",
str,
)
if (playerFormat == 2 and fileFormat == 2)
else (
f"最大结构高度:",
int,
)
)
),
]:
if args:
@@ -106,20 +159,42 @@ 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 fileFormat == 0:
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
elif fileFormat == 2:
if playerFormat == 1:
cvt_method = to_mcstructure_file_in_score
elif playerFormat == 0:
cvt_method = to_mcstructure_file_in_delay
elif playerFormat == 2:
cvt_method = to_mcstructure_file_in_repeater
print(
" 指令总长:{},最高延迟:{}".format(
*(
to_function_addon_in_score(cvt_mid, cvt_cfg, *prompts[3:])
if playerFormat == 1
else to_mcstructure_addon_in_delay(cvt_mid, cvt_cfg, *prompts[3:])
)
*(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:])
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:])
)
)
if fileFormat == 1
else (" 结构大小:{},延迟总数:{},指令数量:{}".format(
*(cvt_method(cvt_mid, cvt_cfg, *prompts[3:])) # type: ignore
) if playerFormat == 2 else
" 结构大小:{},延迟总数:{}".format(
*(cvt_method(cvt_mid, cvt_cfg, *prompts[3:])) # type: ignore
)
)
)
)

View File

@@ -1,13 +1,13 @@
import Musicreater.experiment
import Musicreater.plugin
import Musicreater.plugin.mcstructpack
import Musicreater.plugin.mcstructfile
print(
Musicreater.plugin.mcstructpack.to_mcstructure_addon_in_delay(
Musicreater.experiment.FutureMidiConvertM4.from_midi_file(input("midi路径:"), old_exe_format=False),
Musicreater.plugin.ConvertConfig(
input("输出路径:"),
volume=1
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,
)
)

View File

@@ -1,13 +0,0 @@
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
),
)
)

View File

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

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
皆系 音·创 库所用之连带软件
禁止在 音·创 库以外传播
不允许独立发行、独立副本
不允许未经允许的二次创作
不允许脱离于 音·创 独立存在
不得随任何其他非 音·创 软件连带发布
不得用于商业用途
若 音·创 库被用于商业用途,应当将其剔除
版权所有 © 2024 诸葛亮与八卦阵