This commit is contained in:
CaryXiong 2022-06-30 12:14:22 +08:00
parent 9517c49ec5
commit 2afd6beb3f
2 changed files with 339 additions and 273 deletions

View File

@ -1,22 +0,0 @@
idList = {"0": "harp", "1": "harp", "2": "pling", "3": "harp", "4": "pling", "5": "pling", "6": "harp", "7": "harp",
"8": "share", "9": "harp", "10": "didgeridoo", "11": "harp", "12": "xylophone", "13": "chime", "14": "harp",
"15": "harp", "16": "bass", "17": "harp", "18": "harp", "19": "harp", "20": "harp", "21": "harp",
"22": "harp",
"23": "guitar", "24": "guitar", "25": "guitar", "26": "guitar", "27": "guitar", "28": "guitar",
"29": "guitar",
"30": "guitar", "31": "bass", "32": "bass", "33": "bass", "34": "bass", "35": "bass", "36": "bass",
"37": "bass",
"38": "bass", "39": "bass", "40": "harp", "41": "harp", "42": "harp", "43": "harp", "44": "iron_xylophone",
"45": "guitar", "46": "harp", "47": "harp", "48": "guitar", "49": "guitar", "50": "bit", "51": "bit",
"52": "harp", "53": "harp", "54": "bit", "55": "flute", "56": "flute", "57": "flute", "58": "flute",
"59": "flute", "60": "flute", "61": "flute", "62": "flute", "63": "flute", "64": "bit", "65": "bit",
"66": "bit", "67": "bit", "68": "flute", "69": "harp", "70": "harp", "71": "flute", "72": "flute",
"73": "flute", "74": "harp", "75": "flute", "76": "harp", "77": "harp", "78": "harp", "79": "harp",
"80": "bit", "81": "bit", "82": "bit", "83": "bit", "84": "bit", "85": "bit", "86": "bit", "87": "bit",
"88": "bit", "89": "bit", "90": "bit", "91": "bit", "92": "bit", "93": "bit", "94": "bit", "95": "bit",
"96": "bit", "97": "bit", "98": "bit", "99": "bit", "100": "bit", "101": "bit", "102": "bit", "103": "bit",
"104": "harp", "105": "banjo", "106": "harp", "107": "harp", "108": "harp", "109": "harp", "110": "harp",
"111": "guitar", "112": "harp", "113": "bell", "114": "harp", "115": "cow_bell", "116": "basedrum",
"117": "bass", "118": "bit", "119": "basedrum", "120": "guitar", "121": "harp", "122": "harp", "123": "harp",
"124": "harp", "125": "hat", "126": "basedrum", "127": "snare"}

View File

@ -33,6 +33,9 @@ Note! Except for this source file, all the files in this repository and this pro
import os import os
import mido import mido
import brotli import brotli
import json
import uuid
import shutil
def makeZip(sourceDir, outFilename, compression=8, exceptFile=None): def makeZip(sourceDir, outFilename, compression=8, exceptFile=None):
@ -45,7 +48,7 @@ def makeZip(sourceDir, outFilename, compression=8, exceptFile=None):
""" """
import zipfile import zipfile
zipf = zipfile.ZipFile(outFilename, 'w', compression) zipf = zipfile.ZipFile(outFilename, "w", compression)
pre_len = len(os.path.dirname(sourceDir)) pre_len = len(os.path.dirname(sourceDir))
for parent, dirnames, filenames in os.walk(sourceDir): for parent, dirnames, filenames in os.walk(sourceDir):
for filename in filenames: for filename in filenames:
@ -59,21 +62,21 @@ def makeZip(sourceDir, outFilename, compression=8, exceptFile=None):
class midiConvert: class midiConvert:
def __init__(self): def __init__(self):
'''简单的midi转换类将midi文件转换为我的世界结构或者包''' """简单的midi转换类将midi文件转换为我的世界结构或者包"""
pass pass
def convert(self, midiFile: str, outputPath: str): def convert(self, midiFile: str, outputPath: str):
'''转换前需要先运行此函数来获取基本信息''' """转换前需要先运行此函数来获取基本信息"""
self.midiFile = midiFile self.midiFile = midiFile
'''midi文件路径''' """midi文件路径"""
self.midi = mido.MidiFile(self.midiFile) self.midi = mido.MidiFile(self.midiFile)
'''MidiFile对象''' """MidiFile对象"""
self.outputPath = outputPath self.outputPath = outputPath
'''输出路径''' """输出路径"""
# 将self.midiFile的文件名不含路径且不含后缀存入self.midiFileName # 将self.midiFile的文件名不含路径且不含后缀存入self.midiFileName
self.midFileName = os.path.splitext(os.path.basename(self.midiFile))[0] self.midFileName = os.path.splitext(os.path.basename(self.midiFile))[0]
'''文件名,不含路径且不含后缀''' """文件名,不含路径且不含后缀"""
def __Inst2soundIDwithX(self, instrumentID): def __Inst2soundIDwithX(self, instrumentID):
"""返回midi的乐器ID对应的我的世界乐器名对于音域转换算法如下 """返回midi的乐器ID对应的我的世界乐器名对于音域转换算法如下
@ -84,76 +87,159 @@ class midiConvert:
贝斯bass迪吉里杜管didgeridoo的时候为8 贝斯bass迪吉里杜管didgeridoo的时候为8
长笛flute牛铃cou_bell的时候为5 长笛flute牛铃cou_bell的时候为5
钟琴bell管钟chime木琴xylophone的时候为4 钟琴bell管钟chime木琴xylophone的时候为4
而存在一些打击乐器basedrumhatsnare没有音域则没有X那么我们返回7即可
:param instrumentID: midi的乐器ID :param instrumentID: midi的乐器ID
:param default: 如果instrumentID不在范围内返回的默认我的世界乐器名称 :param default: 如果instrumentID不在范围内返回的默认我的世界乐器名称
:return: (str我的世界乐器名, int转换算法中的X)""" :return: (str我的世界乐器名, int转换算法中的X)"""
import msctPkgver.load_InstrumentLabel return {
X_Range = {"harp": 6, "pling": 6, "banjo": 6, "iron_xylophone": 6, "guitar": 7, "bass": 8, 0: ("note.harp", 6),
"didgeridoo": 8, "flute": 5, "xylophone": 4, "cou_bell": 5, "chime": 4, "bell": 4, 1: ("note.harp", 6),
"bit": 6, "basedrum": 7, "hat": 7, "snare": 7} 2: ("note.pling", 6),
3: ("note.harp", 6),
# 以下为旧版运算算法 4: ("note.pling", 6),
5: ("note.pling", 6),
# if instrumentID == 105: 6: ("note.harp", 6),
# return 'note.banjo', 6 7: ("note.harp", 6),
# if instrumentID in range(32, 40): 8: ("note.share", 7), # 打击乐器无音域
# return 'note.bass', 8 9: ("note.harp", 6),
# if instrumentID in range(115, 119): 10: ("note.didgeridoo", 8),
# return 'note.basedrum', 7 # 注意,这里是底鼓,打击乐器无音域 11: ("note.harp", 6),
# if instrumentID == 9 or instrumentID == 14: 12: ("note.xylophone", 4),
# return 'note.bell', 4 13: ("note.chime", 4),
# if instrumentID == 80 or instrumentID == 81: 14: ("note.harp", 6),
# return 'note.bit', 6 15: ("note.harp", 6),
# if instrumentID == 112: 16: ("note.bass", 8),
# return 'note.cow_bell', 5 17: ("note.harp", 6),
# if instrumentID == -1: 18: ("note.harp", 6),
# return 'note.didgeridoo', 8 19: ("note.harp", 6),
# if instrumentID in range(72, 80): 20: ("note.harp", 6),
# return 'note.flute', 5 21: ("note.harp", 6),
# if instrumentID in range(24, 32): 22: ("note.harp", 6),
# return 'note.guitar', 7 23: ("note.guitar", 7),
# if instrumentID == -2: 24: ("note.guitar", 7),
# return 'note.hat', 7 # 注意,这里是击鼓沿,打击乐器无音域 25: ("note.guitar", 7),
# if instrumentID == 14: 26: ("note.guitar", 7),
# return 'note.chime', 4 27: ("note.guitar", 7),
# if instrumentID == 8 or instrumentID == 11: 28: ("note.guitar", 7),
# return 'iron_xylophone', 6 29: ("note.guitar", 7),
# if instrumentID == 2: 30: ("note.guitar", 7),
# return 'note.pling', 6 31: ("note.bass", 8),
# if instrumentID == 114: 32: ("note.bass", 8),
# return 'note.snare', 7 # 注意,这里是小军鼓,打击乐器无音域 33: ("note.bass", 8),
# if instrumentID == 13: 34: ("note.bass", 8),
# return 'note.xylophone', 4 35: ("note.bass", 8),
# return 'note.harp', 6 36: ("note.bass", 8),
37: ("note.bass", 8),
# 以下为新版运算算法 38: ("note.bass", 8),
39: ("note.bass", 8),
# print(instrumentID) 40: ("note.harp", 6),
# print("note." + msctPkgver.load_InstrumentLabel.idList.get(str(instrumentID))) 41: ("note.harp", 6),
# print(X_Range.get(msctPkgver.load_InstrumentLabel.idList.get(str(instrumentID)))) 42: ("note.harp", 6),
43: ("note.harp", 6),
return "note." + msctPkgver.load_InstrumentLabel.idList.get(str(instrumentID)), \ 44: ("note.iron_xylophone", 6),
X_Range.get(msctPkgver.load_InstrumentLabel.idList.get(str(instrumentID))) 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.basedrum", 7), # 打击乐器无音域
117: ("note.bass", 8),
118: ("note.bit", 6),
119: ("note.basedrum", 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.basedrum", 7), # 打击乐器无音域
127: ("note.snare", 7), # 打击乐器无音域
}[instrumentID]
def __score2time(self, score: int): def __score2time(self, score: int):
return str(int(int(score / 20) / 60)) + ':' + str(int(int(score / 20) % 60)) return str(int(int(score / 20) / 60)) + ":" + str(int(int(score / 20) % 60))
def __formProgressBar( def __formProgressBar(
self, self,
maxscore: int, maxscore: int,
scoreboardname: str, scoreboardname: str,
progressbar: tuple = ( progressbar: tuple = (
r'%%N [ %%s/%^s %%% __________ %%t|%^t ]', r"%%N [ %%s/%^s %%% __________ %%t|%^t ]",
('§e=§r', '§7=§r'), ("§e=§r", "§7=§r"),
), ),
) -> list: ) -> list:
pgsstyle = progressbar[0] pgsstyle = progressbar[0]
'''用于被替换的进度条原始样式''' """用于被替换的进度条原始样式"""
''' """
| 标识符 | 指定的可变量 | | 标识符 | 指定的可变量 |
|---------|----------------| |---------|----------------|
| `%%N` | 乐曲名(即传入的文件名)| | `%%N` | 乐曲名(即传入的文件名)|
@ -163,16 +249,16 @@ class midiConvert:
| `%^t` | 曲目总时长 | | `%^t` | 曲目总时长 |
| `%%%` | 当前进度比率 | | `%%%` | 当前进度比率 |
| `_` | 用以表示进度条占位| | `_` | 用以表示进度条占位|
''' """
def __replace( def __replace(
s: str, tobeReplaced: str, replaceWith: str, times: int, other: str s: str, tobeReplaced: str, replaceWith: str, times: int, other: str
): ):
if times == 0: if times == 0:
return s.replace(tobeReplaced, other) return s.replace(tobeReplaced, other)
if times == s.count(tobeReplaced): if times == s.count(tobeReplaced):
return s.replace(tobeReplaced, replaceWith) return s.replace(tobeReplaced, replaceWith)
result = '' result = ""
t = 0 t = 0
for i in s: for i in s:
if i == tobeReplaced: if i == tobeReplaced:
@ -187,12 +273,12 @@ class midiConvert:
return result return result
idlist = { idlist = {
r'%%N': self.midFileName, r"%%N": self.midFileName,
r'%%s': r'%%s', r"%%s": r"%%s",
r'%^s': str(maxscore), r"%^s": str(maxscore),
r'%%t': r'%%t', r"%%t": r"%%t",
r'%^t': self.__score2time(maxscore), r"%^t": self.__score2time(maxscore),
r'%%%': r'%%%', r"%%%": r"%%%",
} }
ids = {} ids = {}
@ -209,48 +295,48 @@ class midiConvert:
del idlist del idlist
pgblength = pgsstyle.count('_') pgblength = pgsstyle.count("_")
'''进度条的“条”长度''' """进度条的“条”长度"""
finalprgsbar = [] finalprgsbar = []
for i in range(maxscore): for i in range(maxscore):
nowstr = pgsstyle nowstr = pgsstyle
if ids[r'%%s'] == True: if ids[r"%%s"] == True:
nowstr = nowstr.replace(r'%%s', str(i + 1)) nowstr = nowstr.replace(r"%%s", str(i + 1))
if ids[r'%%t'] == True: if ids[r"%%t"] == True:
nowstr = nowstr.replace(r'%%t', self.__score2time(i + 1)) nowstr = nowstr.replace(r"%%t", self.__score2time(i + 1))
if ids[r'%%%'] == True: if ids[r"%%%"] == True:
nowstr = nowstr.replace( nowstr = nowstr.replace(
r'%%%', str(int((i + 1) / maxscore * 10000) / 100) + '%' r"%%%", str(int((i + 1) / maxscore * 10000) / 100) + "%"
) )
countof_s = int((i + 1) / maxscore * pgblength) countof_s = int((i + 1) / maxscore * pgblength)
finalprgsbar.append( finalprgsbar.append(
'title @a[scores={' "title @a[scores={"
+ scoreboardname + scoreboardname
+ '=' + "="
+ str(i + 1) + str(i + 1)
+ '}] actionbar ' + "}] actionbar "
+ __replace( + __replace(
nowstr, '_', progressbar[1][0], countof_s, progressbar[1][1] nowstr, "_", progressbar[1][0], countof_s, progressbar[1][1]
) )
) )
return finalprgsbar return finalprgsbar
def __formCMDblk( def __formCMDblk(
self, self,
command: str, command: str,
particularValue: int, particularValue: int,
impluse: int = 0, impluse: int = 0,
condition: bool = False, condition: bool = False,
needRedstone: bool = True, needRedstone: bool = True,
tickDelay: int = 0, tickDelay: int = 0,
customName: str = '', customName: str = "",
executeOnFirstTick: bool = False, executeOnFirstTick: bool = False,
trackOutput: bool = True, trackOutput: bool = True,
): ):
""" """
使用指定项目返回指定的指令方块放置指令项 使用指定项目返回指定的指令方块放置指令项
@ -302,7 +388,7 @@ class midiConvert:
impluse.to_bytes(4, byteorder="big", signed=False), impluse.to_bytes(4, byteorder="big", signed=False),
bytes(command, encoding="utf-8") + b"\x00", bytes(command, encoding="utf-8") + b"\x00",
bytes(customName, encoding="utf-8") + b"\x00", bytes(customName, encoding="utf-8") + b"\x00",
bytes('', encoding="utf-8") + b"\x00", bytes("", encoding="utf-8") + b"\x00",
tickDelay.to_bytes(4, byteorder="big", signed=True), tickDelay.to_bytes(4, byteorder="big", signed=True),
executeOnFirstTick.to_bytes(1, byteorder="big"), executeOnFirstTick.to_bytes(1, byteorder="big"),
trackOutput.to_bytes(1, byteorder="big"), trackOutput.to_bytes(1, byteorder="big"),
@ -313,7 +399,7 @@ class midiConvert:
return block return block
def _toCmdList_m1( def _toCmdList_m1(
self, scoreboardname: str = 'mscplay', volume: float = 1.0, speed: float = 1.0 self, scoreboardname: str = "mscplay", volume: float = 1.0, speed: float = 1.0
) -> list: ) -> list:
""" """
使用Dislink Sforza的转换思路将midi转换为我的世界命令列表 使用Dislink Sforza的转换思路将midi转换为我的世界命令列表
@ -341,13 +427,13 @@ class midiConvert:
ticks += msg.time ticks += msg.time
# print(msg) # print(msg)
if msg.is_meta: if msg.is_meta:
if msg.type == 'set_tempo': if msg.type == "set_tempo":
tempo = msg.tempo tempo = msg.tempo
else: else:
if msg.type == 'program_change': if msg.type == "program_change":
# print("TT") # print("TT")
instrumentID = msg.program instrumentID = msg.program
if msg.type == 'note_on' and msg.velocity != 0: if msg.type == "note_on" and msg.velocity != 0:
nowscore = round( nowscore = round(
(ticks * tempo) (ticks * tempo)
/ ((self.midi.ticks_per_beat * float(speed)) * 50000) / ((self.midi.ticks_per_beat * float(speed)) * 50000)
@ -355,12 +441,12 @@ class midiConvert:
maxscore = max(maxscore, nowscore) maxscore = max(maxscore, nowscore)
soundID, _X = self.__Inst2soundIDwithX(instrumentID) soundID, _X = self.__Inst2soundIDwithX(instrumentID)
singleTrack.append( singleTrack.append(
'execute @a[scores={' "execute @a[scores={"
+ str(scoreboardname) + str(scoreboardname)
+ '=' + "="
+ str(nowscore) + 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)}' + 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 commands += 1
if len(singleTrack) != 0: if len(singleTrack) != 0:
@ -369,11 +455,11 @@ class midiConvert:
return tracks, commands, maxscore return tracks, commands, maxscore
def _toCmdList_withDelay_m1( def _toCmdList_withDelay_m1(
self, self,
volume: float = 1.0, volume: float = 1.0,
speed: float = 1.0, speed: float = 1.0,
player: str = '@a', player: str = "@a",
isMixedWithPrograssBar=False, isMixedWithPrograssBar=False,
) -> list: ) -> list:
""" """
使用Dislink Sforza的转换思路将midi转换为我的世界命令列表并输出每个音符之后的延迟 使用Dislink Sforza的转换思路将midi转换为我的世界命令列表并输出每个音符之后的延迟
@ -392,8 +478,8 @@ class midiConvert:
if isMixedWithPrograssBar == True: if isMixedWithPrograssBar == True:
isMixedWithPrograssBar = ( isMixedWithPrograssBar = (
r'%%N [ %%s/%^s %%% __________ %%t|%^t ]', r"%%N [ %%s/%^s %%% __________ %%t|%^t ]",
('§e=§r', '§7=§r'), ("§e=§r", "§7=§r"),
) )
for i, track in enumerate(self.midi.tracks): for i, track in enumerate(self.midi.tracks):
@ -404,12 +490,12 @@ class midiConvert:
for msg in track: for msg in track:
ticks += msg.time ticks += msg.time
if msg.is_meta: if msg.is_meta:
if msg.type == 'set_tempo': if msg.type == "set_tempo":
tempo = msg.tempo tempo = msg.tempo
else: else:
if msg.type == 'program_change': if msg.type == "program_change":
instrumentID = msg.program instrumentID = msg.program
if msg.type == 'note_on' and msg.velocity != 0: if msg.type == "note_on" and msg.velocity != 0:
nowtick = round( nowtick = round(
(ticks * tempo) (ticks * tempo)
/ ((self.midi.ticks_per_beat * float(speed)) * 50000) / ((self.midi.ticks_per_beat * float(speed)) * 50000)
@ -417,11 +503,11 @@ class midiConvert:
soundID, _X = self.__Inst2soundIDwithX(instrumentID) soundID, _X = self.__Inst2soundIDwithX(instrumentID)
try: try:
tracks[nowtick].append( tracks[nowtick].append(
f'execute {player} ~ ~ ~ playsound {soundID} @s ~ ~{1 / volume - 1} ~ {msg.velocity * (0.7 if msg.channel == 0 else 0.9)} {2 ** ((msg.note - 60 - _X) / 12)}' f"execute {player} ~ ~ ~ playsound {soundID} @s ~ ~{1 / volume - 1} ~ {msg.velocity * (0.7 if msg.channel == 0 else 0.9)} {2 ** ((msg.note - 60 - _X) / 12)}"
) )
except: except:
tracks[nowtick] = [ tracks[nowtick] = [
f'execute {player} ~ ~ ~ playsound {soundID} @s ~ ~{1 / volume - 1} ~ {msg.velocity * (0.7 if msg.channel == 0 else 0.9)} {2 ** ((msg.note - 60 - _X) / 12)}', f"execute {player} ~ ~ ~ playsound {soundID} @s ~ ~{1 / volume - 1} ~ {msg.velocity * (0.7 if msg.channel == 0 else 0.9)} {2 ** ((msg.note - 60 - _X) / 12)}",
] ]
allticks = list(tracks.keys()) allticks = list(tracks.keys())
@ -429,9 +515,9 @@ class midiConvert:
if isMixedWithPrograssBar: if isMixedWithPrograssBar:
pgsstyle = isMixedWithPrograssBar[0] pgsstyle = isMixedWithPrograssBar[0]
'''用于被替换的进度条原始样式''' """用于被替换的进度条原始样式"""
''' """
| 标识符 | 指定的可变量 | | 标识符 | 指定的可变量 |
|---------|----------------| |---------|----------------|
| `%%N` | 乐曲名(即传入的文件名)| | `%%N` | 乐曲名(即传入的文件名)|
@ -441,16 +527,16 @@ class midiConvert:
| `%^t` | 曲目总时长 | | `%^t` | 曲目总时长 |
| `%%%` | 当前进度比率 | | `%%%` | 当前进度比率 |
| `_` | 用以表示进度条占位| | `_` | 用以表示进度条占位|
''' """
def __replace( def __replace(
s: str, tobeReplaced: str, replaceWith: str, times: int, other: str s: str, tobeReplaced: str, replaceWith: str, times: int, other: str
): ):
if times == 0: if times == 0:
return s.replace(tobeReplaced, other) return s.replace(tobeReplaced, other)
if times == s.count(tobeReplaced): if times == s.count(tobeReplaced):
return s.replace(tobeReplaced, replaceWith) return s.replace(tobeReplaced, replaceWith)
result = '' result = ""
t = 0 t = 0
for i in s: for i in s:
if i == tobeReplaced: if i == tobeReplaced:
@ -465,12 +551,12 @@ class midiConvert:
return result return result
idlist = { idlist = {
r'%%N': self.midFileName, r"%%N": self.midFileName,
r'%%s': r'%%s', r"%%s": r"%%s",
r'%^s': str(allticks[-1]), r"%^s": str(allticks[-1]),
r'%%t': r'%%t', r"%%t": r"%%t",
r'%^t': self.__score2time(allticks[-1]), r"%^t": self.__score2time(allticks[-1]),
r'%%%': r'%%%', r"%%%": r"%%%",
} }
ids = {} ids = {}
@ -487,8 +573,8 @@ class midiConvert:
del idlist del idlist
pgblength = pgsstyle.count('_') pgblength = pgsstyle.count("_")
'''进度条的“条”长度''' """进度条的“条”长度"""
results = [] results = []
@ -508,46 +594,52 @@ class midiConvert:
if isMixedWithPrograssBar: if isMixedWithPrograssBar:
nowstr = pgsstyle nowstr = pgsstyle
if ids[r'%%s'] == True: if ids[r"%%s"] == True:
nowstr = nowstr.replace(r'%%s', str(allticks[i] + 1)) nowstr = nowstr.replace(r"%%s", str(allticks[i] + 1))
if ids[r'%%t'] == True: if ids[r"%%t"] == True:
nowstr = nowstr.replace(r'%%t', self.__score2time(allticks[i] + 1)) nowstr = nowstr.replace(r"%%t", self.__score2time(allticks[i] + 1))
if ids[r'%%%'] == True: if ids[r"%%%"] == True:
nowstr = nowstr.replace( nowstr = nowstr.replace(
r'%%%', str(int((allticks[i] + 1) / allticks[-1] * 10000) / 100) + '%' r"%%%",
str(int((allticks[i] + 1) / allticks[-1] * 10000) / 100) + "%",
) )
countof_s = int((allticks[i] + 1) / allticks[-1] * pgblength) countof_s = int((allticks[i] + 1) / allticks[-1] * pgblength)
titlenow = __replace( titlenow = __replace(
nowstr, nowstr,
'_', "_",
isMixedWithPrograssBar[1][0], isMixedWithPrograssBar[1][0],
countof_s, countof_s,
isMixedWithPrograssBar[1][1], isMixedWithPrograssBar[1][1],
) )
results.append((f'title {player} actionbar {titlenow}', 0,)) results.append(
(
f"title {player} actionbar {titlenow}",
0,
)
)
return results return results
def __fillSquareSideLength(self, total: int, maxHeight: int): def __fillSquareSideLength(self, total: int, maxHeight: int):
'''给定总方块数量和最大高度,返回所构成的图形外切正方形的边长 """给定总方块数量和最大高度,返回所构成的图形外切正方形的边长
:param total: 总方块数量 :param total: 总方块数量
:param maxHeight: 最大高度 :param maxHeight: 最大高度
:return: 外切正方形的边长 int''' :return: 外切正方形的边长 int"""
import math import math
return math.ceil(math.sqrt(math.ceil(total / maxHeight))) return math.ceil(math.sqrt(math.ceil(total / maxHeight)))
def tomcpack( def tomcpack(
self, self,
method: int = 1, method: int = 1,
isAutoReset: bool = False, isAutoReset: bool = False,
progressbar=None, progressbar=None,
scoreboardname: str = 'mscplay', scoreboardname: str = "mscplay",
volume: float = 1.0, volume: float = 1.0,
speed: float = 1.0, speed: float = 1.0,
) -> bool: ) -> bool:
""" """
使用method指定的转换算法将midi转换为我的世界mcpack格式的包 使用method指定的转换算法将midi转换为我的世界mcpack格式的包
@ -562,106 +654,102 @@ class midiConvert:
if method == 1: if method == 1:
cmdlist, _a, maxscore = self._toCmdList_m1(scoreboardname, volume, speed) cmdlist, _a, maxscore = self._toCmdList_m1(scoreboardname, volume, speed)
else: else:
return (False, f'无法找到算法ID{method}对应的转换算法') return (False, f"无法找到算法ID{method}对应的转换算法")
del _a del _a
import json
import uuid
import shutil
# 当文件f夹{self.outputPath}/temp/functions存在时清空其下所有项目若其不存在则创建 # 当文件f夹{self.outputPath}/temp/functions存在时清空其下所有项目若其不存在则创建
if os.path.exists(f'{self.outputPath}/temp/functions/'): if os.path.exists(f"{self.outputPath}/temp/functions/"):
shutil.rmtree(f'{self.outputPath}/temp/functions/') shutil.rmtree(f"{self.outputPath}/temp/functions/")
os.makedirs(f'{self.outputPath}/temp/functions/mscplay') os.makedirs(f"{self.outputPath}/temp/functions/mscplay")
# 写入manifest.json # 写入manifest.json
if not os.path.exists(f'{self.outputPath}/temp/manifest.json'): if not os.path.exists(f"{self.outputPath}/temp/manifest.json"):
with open( with open(
f"{self.outputPath}/temp/manifest.json", "w", encoding='utf-8' f"{self.outputPath}/temp/manifest.json", "w", encoding="utf-8"
) as f: ) as f:
f.write( f.write(
"{\n \"format_version\": 1,\n \"header\": {\n \"description\": \"" '{\n "format_version": 1,\n "header": {\n "description": "'
+ self.midFileName + self.midFileName
+ " Pack : behavior pack\",\n \"version\": [ 0, 0, 1 ],\n \"name\": \"" + ' Pack : behavior pack",\n "version": [ 0, 0, 1 ],\n "name": "'
+ self.midFileName + self.midFileName
+ "Pack\",\n \"uuid\": \"" + 'Pack",\n "uuid": "'
+ str(uuid.uuid4()) + str(uuid.uuid4())
+ "\"\n },\n \"modules\": [\n {\n \"description\": \"" + '"\n },\n "modules": [\n {\n "description": "'
+ f"the Player of the Music {self.midFileName}" + f"the Player of the Music {self.midFileName}"
+ "\",\n \"type\": \"data\",\n \"version\": [ 0, 0, 1 ],\n \"uuid\": \"" + '",\n "type": "data",\n "version": [ 0, 0, 1 ],\n "uuid": "'
+ str(uuid.uuid4()) + str(uuid.uuid4())
+ "\"\n }\n ]\n}" + '"\n }\n ]\n}'
) )
else: else:
with open( with open(
f'{self.outputPath}/temp/manifest.json', 'r', encoding='utf-8' f"{self.outputPath}/temp/manifest.json", "r", encoding="utf-8"
) as manifest: ) as manifest:
data = json.loads(manifest.read()) data = json.loads(manifest.read())
data['header'][ data["header"][
'description' "description"
] = f"the Player of the Music {self.midFileName}" ] = f"the Player of the Music {self.midFileName}"
data['header']['name'] = self.midFileName data["header"]["name"] = self.midFileName
data['header']['uuid'] = str(uuid.uuid4()) data["header"]["uuid"] = str(uuid.uuid4())
data['modules'][0]['description'] = 'None' data["modules"][0]["description"] = "None"
data['modules'][0]['uuid'] = str(uuid.uuid4()) data["modules"][0]["uuid"] = str(uuid.uuid4())
manifest.close() manifest.close()
open(f'{self.outputPath}/temp/manifest.json', 'w', encoding='utf-8').write( open(f"{self.outputPath}/temp/manifest.json", "w", encoding="utf-8").write(
json.dumps(data) json.dumps(data)
) )
# 将命令列表写入文件 # 将命令列表写入文件
indexfile = open( indexfile = open(
f'{self.outputPath}/temp/functions/index.mcfunction', 'w', encoding='utf-8' f"{self.outputPath}/temp/functions/index.mcfunction", "w", encoding="utf-8"
) )
for track in cmdlist: for track in cmdlist:
indexfile.write( indexfile.write(
'function mscplay/track' + str(cmdlist.index(track) + 1) + '\n' "function mscplay/track" + str(cmdlist.index(track) + 1) + "\n"
) )
with open( with open(
f'{self.outputPath}/temp/functions/mscplay/track{cmdlist.index(track) + 1}.mcfunction', f"{self.outputPath}/temp/functions/mscplay/track{cmdlist.index(track) + 1}.mcfunction",
'w', "w",
encoding='utf-8', encoding="utf-8",
) as f: ) as f:
f.write('\n'.join(track)) f.write("\n".join(track))
indexfile.writelines( indexfile.writelines(
( (
'scoreboard players add @a[scores={' "scoreboard players add @a[scores={"
+ scoreboardname + scoreboardname
+ '=1..}] ' + "=1..}] "
+ scoreboardname + scoreboardname
+ ' 1\n', + " 1\n",
( (
'scoreboard players reset @a[scores={' "scoreboard players reset @a[scores={"
+ scoreboardname + scoreboardname
+ '=' + "="
+ str(maxscore + 20) + str(maxscore + 20)
+ '..}]' + "..}]"
+ f' {scoreboardname}\n' + f" {scoreboardname}\n"
) )
if isAutoReset if isAutoReset
else '', else "",
f'function mscplay/progressShow\n' if progressbar else '', f"function mscplay/progressShow\n" if progressbar else "",
) )
) )
if progressbar: if progressbar:
if progressbar == True: if progressbar == True:
with open( with open(
f'{self.outputPath}/temp/functions/mscplay/progressShow.mcfunction', f"{self.outputPath}/temp/functions/mscplay/progressShow.mcfunction",
'w', "w",
encoding='utf-8', encoding="utf-8",
) as f: ) as f:
f.writelines( f.writelines(
'\n'.join(self.__formProgressBar(maxscore, scoreboardname)) "\n".join(self.__formProgressBar(maxscore, scoreboardname))
) )
else: else:
with open( with open(
f'{self.outputPath}/temp/functions/mscplay/progressShow.mcfunction', f"{self.outputPath}/temp/functions/mscplay/progressShow.mcfunction",
'w', "w",
encoding='utf-8', encoding="utf-8",
) as f: ) as f:
f.writelines( f.writelines(
'\n'.join( "\n".join(
self.__formProgressBar( self.__formProgressBar(
maxscore, scoreboardname, progressbar maxscore, scoreboardname, progressbar
) )
@ -671,21 +759,21 @@ class midiConvert:
indexfile.close() indexfile.close()
makeZip( makeZip(
f'{self.outputPath}/temp/', self.outputPath + f'/{self.midFileName}.mcpack' f"{self.outputPath}/temp/", self.outputPath + f"/{self.midFileName}.mcpack"
) )
shutil.rmtree(f'{self.outputPath}/temp/') shutil.rmtree(f"{self.outputPath}/temp/")
def toBDXfile( def toBDXfile(
self, self,
method: int = 1, method: int = 1,
author: str = 'Eilles', author: str = "Eilles",
progressbar=False, progressbar=False,
maxheight: int = 64, maxheight: int = 64,
scoreboardname: str = 'mscplay', scoreboardname: str = "mscplay",
volume: float = 1.0, volume: float = 1.0,
speed: float = 1.0, speed: float = 1.0,
isAutoReset: bool = False, isAutoReset: bool = False,
): ):
""" """
使用method指定的转换算法将midi转换为BDX结构文件 使用method指定的转换算法将midi转换为BDX结构文件
@ -705,7 +793,7 @@ class midiConvert:
scoreboardname, volume, speed scoreboardname, volume, speed
) )
else: else:
return (False, f'无法找到算法ID {method} 对应的转换算法') return (False, f"无法找到算法ID {method} 对应的转换算法")
if not os.path.exists(self.outputPath): if not os.path.exists(self.outputPath):
os.makedirs(self.outputPath) os.makedirs(self.outputPath)
@ -714,9 +802,9 @@ class midiConvert:
f.write("BD@") f.write("BD@")
_bytes = ( _bytes = (
b"BDX\x00" b"BDX\x00"
+ author.encode("utf-8") + author.encode("utf-8")
+ b" & Musicreater\x00\x01command_block\x00" + b" & Musicreater\x00\x01command_block\x00"
) )
key = { key = {
@ -724,10 +812,10 @@ class midiConvert:
"y": (b"\x11", b"\x10"), "y": (b"\x11", b"\x10"),
"z": (b"\x13", b"\x12"), "z": (b"\x13", b"\x12"),
} }
'''key存储了方块移动指令的数据其中可以用key[x|y|z][0|1]来表示xyz的减或增''' """key存储了方块移动指令的数据其中可以用key[x|y|z][0|1]来表示xyz的减或增"""
x = 'x' x = "x"
y = 'y' y = "y"
z = 'z' z = "z"
_sideLength = self.__fillSquareSideLength(totalcount, maxheight) _sideLength = self.__fillSquareSideLength(totalcount, maxheight)
@ -745,12 +833,12 @@ class midiConvert:
if isAutoReset: if isAutoReset:
commands += ( commands += (
'scoreboard players reset @a[scores={' "scoreboard players reset @a[scores={"
+ scoreboardname + scoreboardname
+ '=' + "="
+ str(maxScore + 20) + str(maxScore + 20)
+ '}] ' + "}] "
+ scoreboardname + scoreboardname
) )
if progressbar: if progressbar:
@ -766,20 +854,20 @@ class midiConvert:
cmd, cmd,
(1 if yforward else 0) (1 if yforward else 0)
if ( if (
((nowy != 0) and (not yforward)) ((nowy != 0) and (not yforward))
or ((yforward) and (nowy != maxheight)) or ((yforward) and (nowy != maxheight))
) )
else (3 if zforward else 2) else (3 if zforward else 2)
if ( if (
((nowz != 0) and (not zforward)) ((nowz != 0) and (not zforward))
or ((zforward) and (nowz != _sideLength)) or ((zforward) and (nowz != _sideLength))
) )
else 5, else 5,
impluse=2, impluse=2,
condition=False, condition=False,
needRedstone=False, needRedstone=False,
tickDelay=0, tickDelay=0,
customName='', customName="",
executeOnFirstTick=False, executeOnFirstTick=False,
trackOutput=True, trackOutput=True,
) )
@ -794,7 +882,7 @@ class midiConvert:
nowz += 1 if zforward else -1 nowz += 1 if zforward else -1
if ((nowz > _sideLength) and (zforward)) or ( if ((nowz > _sideLength) and (zforward)) or (
(nowz < 0) and (not zforward) (nowz < 0) and (not zforward)
): ):
nowz -= 1 if zforward else -1 nowz -= 1 if zforward else -1
zforward = not zforward zforward = not zforward
@ -809,19 +897,19 @@ class midiConvert:
_bytes += key[y][int(yforward)] _bytes += key[y][int(yforward)]
with open(f"{self.outputPath}/{self.midFileName}.bdx", "ab+") as f: with open(f"{self.outputPath}/{self.midFileName}.bdx", "ab+") as f:
f.write(brotli.compress(_bytes + b'XE')) f.write(brotli.compress(_bytes + b"XE"))
return (True, _bytes, (nowx, maxheight, _sideLength)) return (True, _bytes, (nowx, maxheight, _sideLength))
def toBDXfile_withDelay( def toBDXfile_withDelay(
self, self,
method: int = 1, method: int = 1,
author: str = 'Eilles', author: str = "Eilles",
progressbar=False, progressbar=False,
maxheight: int = 64, maxheight: int = 64,
volume: float = 1.0, volume: float = 1.0,
speed: float = 1.0, speed: float = 1.0,
player: str = '@a', player: str = "@a",
): ):
""" """
使用method指定的转换算法将midi转换为BDX结构文件 使用method指定的转换算法将midi转换为BDX结构文件
@ -838,7 +926,7 @@ class midiConvert:
if method == 1: if method == 1:
cmdlist = self._toCmdList_withDelay_m1(volume, speed, player, progressbar) cmdlist = self._toCmdList_withDelay_m1(volume, speed, player, progressbar)
else: else:
return (False, f'无法找到算法ID {method} 对应的转换算法') return (False, f"无法找到算法ID {method} 对应的转换算法")
if not os.path.exists(self.outputPath): if not os.path.exists(self.outputPath):
os.makedirs(self.outputPath) os.makedirs(self.outputPath)
@ -847,9 +935,9 @@ class midiConvert:
f.write("BD@") f.write("BD@")
_bytes = ( _bytes = (
b"BDX\x00" b"BDX\x00"
+ author.encode("utf-8") + author.encode("utf-8")
+ b" & Musicreater\x00\x01command_block\x00" + b" & Musicreater\x00\x01command_block\x00"
) )
key = { key = {
@ -857,10 +945,10 @@ class midiConvert:
"y": (b"\x11", b"\x10"), "y": (b"\x11", b"\x10"),
"z": (b"\x13", b"\x12"), "z": (b"\x13", b"\x12"),
} }
'''key存储了方块移动指令的数据其中可以用key[x|y|z][0|1]来表示xyz的减或增''' """key存储了方块移动指令的数据其中可以用key[x|y|z][0|1]来表示xyz的减或增"""
x = 'x' x = "x"
y = 'y' y = "y"
z = 'z' z = "z"
_sideLength = self.__fillSquareSideLength(len(cmdlist), maxheight) _sideLength = self.__fillSquareSideLength(len(cmdlist), maxheight)
@ -876,20 +964,20 @@ class midiConvert:
cmd, cmd,
(1 if yforward else 0) (1 if yforward else 0)
if ( if (
((nowy != 0) and (not yforward)) ((nowy != 0) and (not yforward))
or ((yforward) and (nowy != maxheight)) or ((yforward) and (nowy != maxheight))
) )
else (3 if zforward else 2) else (3 if zforward else 2)
if ( if (
((nowz != 0) and (not zforward)) ((nowz != 0) and (not zforward))
or ((zforward) and (nowz != _sideLength)) or ((zforward) and (nowz != _sideLength))
) )
else 5, else 5,
impluse=2, impluse=2,
condition=False, condition=False,
needRedstone=False, needRedstone=False,
tickDelay=delay, tickDelay=delay,
customName='', customName="",
executeOnFirstTick=False, executeOnFirstTick=False,
trackOutput=True, trackOutput=True,
) )
@ -904,7 +992,7 @@ class midiConvert:
nowz += 1 if zforward else -1 nowz += 1 if zforward else -1
if ((nowz > _sideLength) and (zforward)) or ( if ((nowz > _sideLength) and (zforward)) or (
(nowz < 0) and (not zforward) (nowz < 0) and (not zforward)
): ):
nowz -= 1 if zforward else -1 nowz -= 1 if zforward else -1
zforward = not zforward zforward = not zforward
@ -919,6 +1007,6 @@ class midiConvert:
_bytes += key[y][int(yforward)] _bytes += key[y][int(yforward)]
with open(f"{self.outputPath}/{self.midFileName}.bdx", "ab+") as f: with open(f"{self.outputPath}/{self.midFileName}.bdx", "ab+") as f:
f.write(brotli.compress(_bytes + b'XE')) f.write(brotli.compress(_bytes + b"XE"))
return (True, _bytes, (nowx, maxheight, _sideLength)) return (True, _bytes, (nowx, maxheight, _sideLength))