mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2025-02-28 13:26:34 +08:00
update 2023/1/28 formatting and blacking codes
This commit is contained in:
parent
49c4cfcbe2
commit
032bbb81c4
121
magicDemo.py
121
magicDemo.py
@ -4,7 +4,7 @@
|
|||||||
# 音·创 开发交流群 861684859
|
# 音·创 开发交流群 861684859
|
||||||
# Email EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com
|
# Email EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com
|
||||||
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 Lisence.md
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -13,14 +13,15 @@ Musicreater Package Version : Demo for Midi Conversion
|
|||||||
|
|
||||||
Copyright 2023 all the developers of Musicreater
|
Copyright 2023 all the developers of Musicreater
|
||||||
|
|
||||||
开源相关声明请见 ./Lisence.md
|
开源相关声明请见 ./License.md
|
||||||
Terms & Conditions: ./Lisence.md
|
Terms & Conditions: ./Lisense.md
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import random
|
import random
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
languages = {
|
languages = {
|
||||||
"ZH_CN": {
|
"ZH_CN": {
|
||||||
"MSCT": "音·创",
|
"MSCT": "音·创",
|
||||||
@ -64,7 +65,6 @@ languages = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if sys.argv.__len__() > 0:
|
if sys.argv.__len__() > 0:
|
||||||
currentLang = sys.argv[0]
|
currentLang = sys.argv[0]
|
||||||
if currentLang not in languages.keys():
|
if currentLang not in languages.keys():
|
||||||
@ -74,16 +74,16 @@ else:
|
|||||||
|
|
||||||
|
|
||||||
def _(__):
|
def _(__):
|
||||||
'''
|
"""
|
||||||
`languages`
|
`languages`
|
||||||
'''
|
"""
|
||||||
return languages[currentLang][__]
|
return languages[currentLang][__]
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import msctPkgver
|
import msctPkgver
|
||||||
except ModuleNotFoundError as E:
|
except ModuleNotFoundError as E:
|
||||||
if input("您需要安装 mido、Brotli 模块才能使用这个样例\n请问是否安装?(y/n):").lower() in ('y', '1'):
|
if input("您需要安装 mido、Brotli 模块才能使用这个样例\n请问是否安装?(y/n):").lower() in ("y", "1"):
|
||||||
os.system("pip install -r requirements.txt")
|
os.system("pip install -r requirements.txt")
|
||||||
import msctPkgver
|
import msctPkgver
|
||||||
else:
|
else:
|
||||||
@ -92,12 +92,13 @@ except ModuleNotFoundError as E:
|
|||||||
try:
|
try:
|
||||||
from msctPkgver.magicBeing import *
|
from msctPkgver.magicBeing import *
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
# import zhdate
|
# import zhdate
|
||||||
except ModuleNotFoundError as E:
|
except ModuleNotFoundError as E:
|
||||||
if input(
|
if input(
|
||||||
"您需要安装以下模块才能使用这个样例\nrequests==2.28.1\nrich==12.6.0\nzhdate==0.1\n请问是否安装?(y/n):"
|
"您需要安装以下模块才能使用这个样例\nrequests==2.28.1\nrich==12.6.0\nzhdate==0.1\n请问是否安装?(y/n):"
|
||||||
).lower() in ('y', '1'):
|
).lower() in ("y", "1"):
|
||||||
open("Demo_Requirements.txt", 'w').write(
|
open("Demo_Requirements.txt", "w").write(
|
||||||
"requests==2.28.1\nrich==12.6.0\nzhdate==0.1"
|
"requests==2.28.1\nrich==12.6.0\nzhdate==0.1"
|
||||||
)
|
)
|
||||||
os.system("pip install -r Demo_Requirements.txt")
|
os.system("pip install -r Demo_Requirements.txt")
|
||||||
@ -115,34 +116,37 @@ MainConsole.print(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# 显示大标题
|
# 显示大标题
|
||||||
|
MainConsole.rule(title="[bold #AB70FF]欢迎使用音·创独立转换器", characters="=", style="#26E2FF")
|
||||||
MainConsole.rule(
|
MainConsole.rule(
|
||||||
title="[bold #AB70FF]欢迎使用音·创独立转换器",
|
title="[bold #AB70FF]Welcome to Independent Musicreater Converter", characters="-"
|
||||||
characters="=",
|
)
|
||||||
style="#26E2FF")
|
|
||||||
MainConsole.rule(
|
|
||||||
title="[bold #AB70FF]Welcome to Independent Musicreater Convernter",
|
|
||||||
characters="-")
|
|
||||||
|
|
||||||
nowYang = datetime.datetime.now()
|
nowYang = datetime.datetime.now()
|
||||||
|
|
||||||
if nowYang.month == 8 and nowYang.day == 6:
|
if nowYang.month == 8 and nowYang.day == 6:
|
||||||
# 诸葛八卦生日
|
# 诸葛八卦生日
|
||||||
MainConsole.print("[#7DB5F0 on #121110]今天可不是催更的日子!\n诸葛亮与八卦阵{}岁生日快乐!".format(
|
MainConsole.print(
|
||||||
nowYang.year - 2009), style="#7DB5F0 on #121110", justify="center", )
|
"[#7DB5F0 on #121110]今天可不是催更的日子!\n诸葛亮与八卦阵{}岁生日快乐!".format(nowYang.year - 2009),
|
||||||
|
style="#7DB5F0 on #121110",
|
||||||
|
justify="center",
|
||||||
|
)
|
||||||
elif nowYang.month == 4 and nowYang.day == 3:
|
elif nowYang.month == 4 and nowYang.day == 3:
|
||||||
# 金羿生日快乐
|
# 金羿生日快乐
|
||||||
MainConsole.print("[#0089F2 on #F0F2F4]今天就不要催更啦!\n金羿{}岁生日快乐!".format(
|
MainConsole.print(
|
||||||
nowYang.year - 2006), style="#0089F2 on #F0F2F4", justify="center", )
|
"[#0089F2 on #F0F2F4]今天就不要催更啦!\n金羿{}岁生日快乐!".format(nowYang.year - 2006),
|
||||||
|
style="#0089F2 on #F0F2F4",
|
||||||
|
justify="center",
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# 显示箴言部分
|
# 显示箴言部分
|
||||||
MainConsole.print(
|
MainConsole.print(
|
||||||
"[#121110 on #F0F2F4]{}".format(
|
"[#121110 on #F0F2F4]{}".format(
|
||||||
random.choice(
|
random.choice(
|
||||||
requests.get(
|
requests.get(
|
||||||
'https://gitee.com/EillesWan/Musicreater/raw/master/resources/myWords.txt'
|
"https://gitee.com/EillesWan/Musicreater/raw/master/resources/myWords.txt"
|
||||||
)
|
)
|
||||||
.text.strip('\r\n')
|
.text.strip("\r\n")
|
||||||
.split('\r\n')
|
.split("\r\n")
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
style="#121110 on #F0F2F4",
|
style="#121110 on #F0F2F4",
|
||||||
@ -152,40 +156,40 @@ else:
|
|||||||
prt(f"{_('LangChd')}{_(':')}{_(currentLang)}")
|
prt(f"{_('LangChd')}{_(':')}{_(currentLang)}")
|
||||||
|
|
||||||
|
|
||||||
def formatipt(
|
def format_ipt(
|
||||||
notice: str,
|
notice: str,
|
||||||
fun,
|
fun,
|
||||||
errnote: str = f"{_('ErrEnter')}{_(',')}{_('Re-Enter')}{_('.')}",
|
err_note: str = f"{_('ErrEnter')}{_(',')}{_('Re-Enter')}{_('.')}",
|
||||||
*extraArg,
|
*extraArg,
|
||||||
):
|
):
|
||||||
'''循环输入,以某种格式
|
"""循环输入,以某种格式
|
||||||
notice: 输入时的提示
|
notice: 输入时的提示
|
||||||
fun: 格式函数
|
fun: 格式函数
|
||||||
errnote: 输入不符格式时的提示
|
err_note: 输入不符格式时的提示
|
||||||
*extraArg: 对于函数的其他参数'''
|
*extraArg: 对于函数的其他参数"""
|
||||||
while True:
|
while True:
|
||||||
result = ipt(notice)
|
result = ipt(notice)
|
||||||
try:
|
try:
|
||||||
funresult = fun(result, *extraArg)
|
fun_result = fun(result, *extraArg)
|
||||||
break
|
break
|
||||||
except BaseException:
|
except BaseError:
|
||||||
prt(errnote)
|
prt(err_note)
|
||||||
continue
|
continue
|
||||||
return result, funresult
|
return result, fun_result
|
||||||
|
|
||||||
|
|
||||||
# 获取midi列表
|
# 获取midi列表
|
||||||
while True:
|
while True:
|
||||||
midipath = ipt(f"{_('ChoosePath')}{_(':')}").lower()
|
midi_path = ipt(f"{_('ChoosePath')}{_(':')}").lower()
|
||||||
if os.path.exists(midipath):
|
if os.path.exists(midi_path):
|
||||||
if os.path.isfile(midipath):
|
if os.path.isfile(midi_path):
|
||||||
midis = (midipath,)
|
midis = (midi_path,)
|
||||||
elif os.path.isdir(midipath):
|
elif os.path.isdir(midi_path):
|
||||||
midis = tuple(
|
midis = tuple(
|
||||||
(
|
(
|
||||||
os.path.join(midipath, i)
|
os.path.join(midi_path, i)
|
||||||
for i in os.listdir(midipath)
|
for i in os.listdir(midi_path)
|
||||||
if i.lower().endswith('.mid') or i.lower().endswith('.midi')
|
if i.lower().endswith(".mid") or i.lower().endswith(".midi")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@ -197,7 +201,7 @@ while True:
|
|||||||
break
|
break
|
||||||
|
|
||||||
# 获取输出地址
|
# 获取输出地址
|
||||||
outpath = formatipt(
|
out_path = format_ipt(
|
||||||
f"{_('ChooseOutPath')}{_(':')}",
|
f"{_('ChooseOutPath')}{_(':')}",
|
||||||
os.path.exists,
|
os.path.exists,
|
||||||
f"{_('FileNotFound')}{_(',')}{_('Re-Enter')}{_('.')}",
|
f"{_('FileNotFound')}{_(',')}{_('Re-Enter')}{_('.')}",
|
||||||
@ -206,18 +210,18 @@ outpath = formatipt(
|
|||||||
# 选择输出格式
|
# 选择输出格式
|
||||||
while True:
|
while True:
|
||||||
fileFormat = ipt(f"{_('ChooseFileFormat')}{_(':')}").lower()
|
fileFormat = ipt(f"{_('ChooseFileFormat')}{_(':')}").lower()
|
||||||
if fileFormat in ('0', 'mcpack'):
|
if fileFormat in ("0", "mcpack"):
|
||||||
fileFormat = 0
|
fileFormat = 0
|
||||||
playerFormat = 1
|
playerFormat = 1
|
||||||
break
|
break
|
||||||
|
|
||||||
elif fileFormat in ('1', 'bdx'):
|
elif fileFormat in ("1", "bdx"):
|
||||||
fileFormat = 1
|
fileFormat = 1
|
||||||
while True:
|
while True:
|
||||||
playerFormat = ipt(f"{_('ChoosePlayer')}{_(':')}").lower()
|
playerFormat = ipt(f"{_('ChoosePlayer')}{_(':')}").lower()
|
||||||
if playerFormat in ('0', '延迟', 'delay'):
|
if playerFormat in ("0", "延迟", "delay"):
|
||||||
playerFormat = 0
|
playerFormat = 0
|
||||||
elif playerFormat in ('1', '计分板', 'scoreboard'):
|
elif playerFormat in ("1", "计分板", "scoreboard"):
|
||||||
playerFormat = 1
|
playerFormat = 1
|
||||||
else:
|
else:
|
||||||
prt(f"{_('ErrEnter')}{_(',')}{_('Re-Enter')}{_('.')}")
|
prt(f"{_('ErrEnter')}{_(',')}{_('Re-Enter')}{_('.')}")
|
||||||
@ -232,13 +236,13 @@ debug = False
|
|||||||
|
|
||||||
|
|
||||||
# 真假字符串判断
|
# 真假字符串判断
|
||||||
def boolstr(sth: str) -> bool:
|
def bool_str(sth: str) -> bool:
|
||||||
try:
|
try:
|
||||||
return bool(int(sth))
|
return bool(int(sth))
|
||||||
except BaseException:
|
except BaseError:
|
||||||
if str(sth).lower() == 'true':
|
if str(sth).lower() == "true":
|
||||||
return True
|
return True
|
||||||
elif str(sth).lower() == 'false':
|
elif str(sth).lower() == "false":
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
raise "布尔字符串啊?"
|
raise "布尔字符串啊?"
|
||||||
@ -247,7 +251,7 @@ def boolstr(sth: str) -> bool:
|
|||||||
if os.path.exists("./demo_config.json"):
|
if os.path.exists("./demo_config.json"):
|
||||||
import json
|
import json
|
||||||
|
|
||||||
prompts = json.load(open("./demo_config.json", 'r', encoding="utf-8"))
|
prompts = json.load(open("./demo_config.json", "r", encoding="utf-8"))
|
||||||
if prompts[-1] == "debug":
|
if prompts[-1] == "debug":
|
||||||
debug = True
|
debug = True
|
||||||
prompts = prompts[:-1]
|
prompts = prompts[:-1]
|
||||||
@ -265,7 +269,7 @@ else:
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
f'{_("WhetherPgb")}{_(":")}',
|
f'{_("WhetherPgb")}{_(":")}',
|
||||||
boolstr,
|
bool_str,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
f'{_("EnterSbName")}{_(":")}',
|
f'{_("EnterSbName")}{_(":")}',
|
||||||
@ -278,7 +282,7 @@ else:
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
f'{_("WhetherSbReset")}{_(":")}',
|
f'{_("WhetherSbReset")}{_(":")}',
|
||||||
boolstr,
|
bool_str,
|
||||||
)
|
)
|
||||||
if playerFormat == 1
|
if playerFormat == 1
|
||||||
else (),
|
else (),
|
||||||
@ -296,14 +300,14 @@ else:
|
|||||||
else (),
|
else (),
|
||||||
]:
|
]:
|
||||||
if args:
|
if args:
|
||||||
prompts.append(formatipt(*args)[1])
|
prompts.append(format_ipt(*args)[1])
|
||||||
|
|
||||||
conversion = msctPkgver.midiConvert(debug)
|
conversion = msctPkgver.midiConvert(debug)
|
||||||
for singleMidi in midis:
|
for singleMidi in midis:
|
||||||
prt("\n" f"{_('Dealing')} {singleMidi} {_(':')}")
|
prt("\n" f"{_('Dealing')} {singleMidi} {_(':')}")
|
||||||
conversion.convert(singleMidi, outpath)
|
conversion.convert(singleMidi, out_path)
|
||||||
if debug:
|
if debug:
|
||||||
with open("./records.json", 'a', encoding="utf-8") as f:
|
with open("./records.json", "a", encoding="utf-8") as f:
|
||||||
json.dump(conversion.toDICT(), f)
|
json.dump(conversion.toDICT(), f)
|
||||||
f.write(5 * "\n")
|
f.write(5 * "\n")
|
||||||
conversion_result = (
|
conversion_result = (
|
||||||
@ -317,8 +321,13 @@ for singleMidi in midis:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if conversion_result[0]:
|
if conversion_result[0]:
|
||||||
|
c3 = conversion_result[3]
|
||||||
|
c4 = conversion_result[4]
|
||||||
|
fF = fileFormat
|
||||||
prt(
|
prt(
|
||||||
f" {_('CmdLength')}{_(':')}{conversion_result[1]}{_(',')}{_('MaxDelay')}{_(':')}{conversion_result[2]}{f'''{_(',')}{_('PlaceSize')}{_(':')}{conversion_result[3]}{_(',')}{_('LastPos')}{_(':')}{conversion_result[4]}''' if fileFormat == 1 else ''}"
|
f" {_('CmdLength')}{_(':')}{conversion_result[1]}{_(',')}{_('MaxDelay')}{_(':')}"
|
||||||
|
f"{conversion_result[2]}"
|
||||||
|
f"{f'''{_(',')}{_('PlaceSize')}{_(':')}{c3}{_(',')}{_('LastPos')}{_(':')}{c4}''' if fF == 1 else ''}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
prt(f"{_('Failed')}")
|
prt(f"{_('Failed')}")
|
||||||
@ -327,7 +336,7 @@ exitSth = ipt(_("PressEnterExit")).lower()
|
|||||||
if exitSth == "record":
|
if exitSth == "record":
|
||||||
import json
|
import json
|
||||||
|
|
||||||
with open("./demo_config.json", 'w', encoding="utf-8") as f:
|
with open("./demo_config.json", "w", encoding="utf-8") as f:
|
||||||
json.dump(prompts, f)
|
json.dump(prompts, f)
|
||||||
elif exitSth == "delrec":
|
elif exitSth == "delrec":
|
||||||
os.remove("./demo_config.json")
|
os.remove("./demo_config.json")
|
||||||
|
@ -17,12 +17,13 @@ Terms & Conditions: ../License.md
|
|||||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
from .main import *
|
from .main import *
|
||||||
__version__ = '0.2.2.1'
|
|
||||||
|
__version__ = "0.2.2.1"
|
||||||
__all__ = []
|
__all__ = []
|
||||||
__author__ = (('金羿', 'Eilles Wan'), ('诸葛亮与八卦阵', 'bgArray'), ('鸣凤鸽子', 'MingFengPigeon'))
|
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"), ("鸣凤鸽子", "MingFengPigeon"))
|
||||||
|
|
||||||
|
|
||||||
print('此Midi转换功能由音·创开发者开发,版权归参与开发的人员共同所有。')
|
print("此Midi转换功能由音·创开发者开发,版权归参与开发的人员共同所有。")
|
||||||
print('Copyright © 2022 all the developers of Musicreater')
|
print("Copyright © 2022 all the developers of Musicreater")
|
||||||
print("小贴:不妨试试Mid-BDX转换网页:在线的多功能Midi转换器")
|
print("小贴:不妨试试Mid-BDX转换网页:在线的多功能Midi转换器")
|
||||||
print("https://dislink.github.io/midi2bdx/")
|
print("https://dislink.github.io/midi2bdx/")
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
import brotli
|
import brotli
|
||||||
|
|
||||||
input(brotli.decompress(open(input("BDX文件:"), 'rb').read()[3:]))
|
input(brotli.decompress(open(input("BDX文件:"), "rb").read()[3:]))
|
||||||
|
@ -27,7 +27,9 @@ class MSCTBaseException(Exception):
|
|||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
|
|
||||||
def miao(self, ):
|
def miao(
|
||||||
|
self,
|
||||||
|
):
|
||||||
for i in self.args:
|
for i in self.args:
|
||||||
print(i + "喵!")
|
print(i + "喵!")
|
||||||
|
|
||||||
@ -37,29 +39,35 @@ class MSCTBaseException(Exception):
|
|||||||
|
|
||||||
class CrossNoteError(MSCTBaseException):
|
class CrossNoteError(MSCTBaseException):
|
||||||
"""同通道下同音符交叉出现所产生的错误"""
|
"""同通道下同音符交叉出现所产生的错误"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NotDefineTempoError(MSCTBaseException):
|
class NotDefineTempoError(MSCTBaseException):
|
||||||
"""没有Tempo设定导致时间无法计算的错误"""
|
"""没有Tempo设定导致时间无法计算的错误"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MidiDestroyedError(MSCTBaseException):
|
class MidiDestroyedError(MSCTBaseException):
|
||||||
"""Midi文件损坏"""
|
"""Midi文件损坏"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ChannelOverFlowError(MSCTBaseException):
|
class ChannelOverFlowError(MSCTBaseException):
|
||||||
"""一个midi中含有过多的通道(数量应≤16)"""
|
"""一个midi中含有过多的通道(数量应≤16)"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NotDefineProgramError(MSCTBaseException):
|
class NotDefineProgramError(MSCTBaseException):
|
||||||
"""没有Program设定导致没有乐器可以选择的错误"""
|
"""没有Program设定导致没有乐器可以选择的错误"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BaseError(BaseException):
|
class BaseError(BaseException):
|
||||||
"""专门骗过PEP8的错误"""
|
"""专门骗过PEP8的错误"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
@ -30,18 +30,14 @@ from .utils import *
|
|||||||
from .exceptions import *
|
from .exceptions import *
|
||||||
from typing import TypeVar, Union
|
from typing import TypeVar, Union
|
||||||
|
|
||||||
T = TypeVar('T') # Declare type variable
|
T = TypeVar("T") # Declare type variable
|
||||||
VM = TypeVar("VM", mido.MidiFile, None) # void mido
|
VM = TypeVar("VM", mido.MidiFile, None) # void mido
|
||||||
|
|
||||||
|
|
||||||
class SingleNote:
|
class SingleNote:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self, instrument: int, pitch: int, velocity, startTime: int, lastTime: int
|
||||||
instrument: int,
|
):
|
||||||
pitch: int,
|
|
||||||
velocity,
|
|
||||||
startTime: int,
|
|
||||||
lastTime: int):
|
|
||||||
"""用于存储单个音符的类
|
"""用于存储单个音符的类
|
||||||
:param instrument 乐器编号
|
:param instrument 乐器编号
|
||||||
:param pitch 音符编号
|
:param pitch 音符编号
|
||||||
@ -50,15 +46,15 @@ class SingleNote:
|
|||||||
注:此处的时间是用从乐曲开始到当前的毫秒数
|
注:此处的时间是用从乐曲开始到当前的毫秒数
|
||||||
:param lastTime 音符延续时间(ms)"""
|
:param lastTime 音符延续时间(ms)"""
|
||||||
self.instrument = instrument
|
self.instrument = instrument
|
||||||
'''乐器编号'''
|
"""乐器编号"""
|
||||||
self.note = pitch
|
self.note = pitch
|
||||||
'''音符编号'''
|
"""音符编号"""
|
||||||
self.velocity = velocity
|
self.velocity = velocity
|
||||||
'''力度/响度'''
|
"""力度/响度"""
|
||||||
self.startTime = startTime
|
self.startTime = startTime
|
||||||
'''开始之时 ms'''
|
"""开始之时 ms"""
|
||||||
self.lastTime = lastTime
|
self.lastTime = lastTime
|
||||||
'''音符持续时间 ms'''
|
"""音符持续时间 ms"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def inst(self):
|
def inst(self):
|
||||||
@ -71,16 +67,13 @@ class SingleNote:
|
|||||||
return self.note
|
return self.note
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Note(inst = {self.inst}, pitch = {self.note}, velocity = {self.velocity}, " \
|
return (
|
||||||
|
f"Note(inst = {self.inst}, pitch = {self.note}, velocity = {self.velocity}, "
|
||||||
f"startTime = {self.startTime}, lastTime = {self.lastTime}, )"
|
f"startTime = {self.startTime}, lastTime = {self.lastTime}, )"
|
||||||
|
)
|
||||||
|
|
||||||
def __tuple__(self):
|
def __tuple__(self):
|
||||||
return (
|
return self.inst, self.note, self.velocity, self.startTime, self.lastTime
|
||||||
self.inst,
|
|
||||||
self.note,
|
|
||||||
self.velocity,
|
|
||||||
self.startTime,
|
|
||||||
self.lastTime)
|
|
||||||
|
|
||||||
def __dict__(self):
|
def __dict__(self):
|
||||||
return {
|
return {
|
||||||
@ -146,11 +139,7 @@ class midiConvert:
|
|||||||
self.midFileName: str = ""
|
self.midFileName: str = ""
|
||||||
self.exeHead = ""
|
self.exeHead = ""
|
||||||
self.methods = MethodList(
|
self.methods = MethodList(
|
||||||
[
|
[self._toCmdList_m1(), self._toCmdList_m2(), self._toCmdList_m3()]
|
||||||
self._toCmdList_m1(),
|
|
||||||
self._toCmdList_m2(),
|
|
||||||
self._toCmdList_m3()
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.methods_byDelay = MethodList(
|
self.methods_byDelay = MethodList(
|
||||||
@ -166,11 +155,7 @@ class midiConvert:
|
|||||||
self.prt = prt
|
self.prt = prt
|
||||||
self.ipt = ipt
|
self.ipt = ipt
|
||||||
|
|
||||||
def convert(
|
def convert(self, midiFile: str, outputPath: str, oldExeFormat: bool = True):
|
||||||
self,
|
|
||||||
midiFile: str,
|
|
||||||
outputPath: str,
|
|
||||||
oldExeFormat: bool = True):
|
|
||||||
"""转换前需要先运行此函数来获取基本信息"""
|
"""转换前需要先运行此函数来获取基本信息"""
|
||||||
|
|
||||||
self.midiFile = midiFile
|
self.midiFile = midiFile
|
||||||
@ -349,53 +334,53 @@ class midiConvert:
|
|||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
return {
|
return {
|
||||||
34: ('note.bd', 7),
|
34: ("note.bd", 7),
|
||||||
35: ('note.bd', 7),
|
35: ("note.bd", 7),
|
||||||
36: ('note.hat', 7),
|
36: ("note.hat", 7),
|
||||||
37: ('note.snare', 7),
|
37: ("note.snare", 7),
|
||||||
38: ('note.snare', 7),
|
38: ("note.snare", 7),
|
||||||
39: ('note.snare', 7),
|
39: ("note.snare", 7),
|
||||||
40: ('note.hat', 7),
|
40: ("note.hat", 7),
|
||||||
41: ('note.snare', 7),
|
41: ("note.snare", 7),
|
||||||
42: ('note.hat', 7),
|
42: ("note.hat", 7),
|
||||||
43: ('note.snare', 7),
|
43: ("note.snare", 7),
|
||||||
44: ('note.snare', 7),
|
44: ("note.snare", 7),
|
||||||
45: ('note.bell', 4),
|
45: ("note.bell", 4),
|
||||||
46: ('note.snare', 7),
|
46: ("note.snare", 7),
|
||||||
47: ('note.snare', 7),
|
47: ("note.snare", 7),
|
||||||
48: ('note.bell', 4),
|
48: ("note.bell", 4),
|
||||||
49: ('note.hat', 7),
|
49: ("note.hat", 7),
|
||||||
50: ('note.bell', 4),
|
50: ("note.bell", 4),
|
||||||
51: ('note.bell', 4),
|
51: ("note.bell", 4),
|
||||||
52: ('note.bell', 4),
|
52: ("note.bell", 4),
|
||||||
53: ('note.bell', 4),
|
53: ("note.bell", 4),
|
||||||
54: ('note.bell', 4),
|
54: ("note.bell", 4),
|
||||||
55: ('note.bell', 4),
|
55: ("note.bell", 4),
|
||||||
56: ('note.snare', 7),
|
56: ("note.snare", 7),
|
||||||
57: ('note.hat', 7),
|
57: ("note.hat", 7),
|
||||||
58: ('note.chime', 4),
|
58: ("note.chime", 4),
|
||||||
59: ('note.iron_xylophone', 6),
|
59: ("note.iron_xylophone", 6),
|
||||||
60: ('note.bd', 7),
|
60: ("note.bd", 7),
|
||||||
61: ('note.bd', 7),
|
61: ("note.bd", 7),
|
||||||
62: ('note.xylophone', 4),
|
62: ("note.xylophone", 4),
|
||||||
63: ('note.xylophone', 4),
|
63: ("note.xylophone", 4),
|
||||||
64: ('note.xylophone', 4),
|
64: ("note.xylophone", 4),
|
||||||
65: ('note.hat', 7),
|
65: ("note.hat", 7),
|
||||||
66: ('note.bell', 4),
|
66: ("note.bell", 4),
|
||||||
67: ('note.bell', 4),
|
67: ("note.bell", 4),
|
||||||
68: ('note.hat', 7),
|
68: ("note.hat", 7),
|
||||||
69: ('note.hat', 7),
|
69: ("note.hat", 7),
|
||||||
70: ('note.flute', 5),
|
70: ("note.flute", 5),
|
||||||
71: ('note.flute', 5),
|
71: ("note.flute", 5),
|
||||||
72: ('note.hat', 7),
|
72: ("note.hat", 7),
|
||||||
73: ('note.hat', 7),
|
73: ("note.hat", 7),
|
||||||
74: ('note.xylophone', 4),
|
74: ("note.xylophone", 4),
|
||||||
75: ('note.hat', 7),
|
75: ("note.hat", 7),
|
||||||
76: ('note.hat', 7),
|
76: ("note.hat", 7),
|
||||||
77: ('note.xylophone', 4),
|
77: ("note.xylophone", 4),
|
||||||
78: ('note.xylophone', 4),
|
78: ("note.xylophone", 4),
|
||||||
79: ('note.bell', 4),
|
79: ("note.bell", 4),
|
||||||
80: ('note.bell', 4),
|
80: ("note.bell", 4),
|
||||||
}[instrumentID]
|
}[instrumentID]
|
||||||
except BaseError:
|
except BaseError:
|
||||||
return "note.bd", 7
|
return "note.bd", 7
|
||||||
@ -412,8 +397,7 @@ class midiConvert:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __score2time(score: int):
|
def __score2time(score: int):
|
||||||
return str(int(int(score / 20) / 60)) + ":" + \
|
return str(int(int(score / 20) / 60)) + ":" + str(int(int(score / 20) % 60))
|
||||||
str(int(int(score / 20) % 60))
|
|
||||||
|
|
||||||
def __formProgressBar(
|
def __formProgressBar(
|
||||||
self,
|
self,
|
||||||
@ -439,7 +423,7 @@ class midiConvert:
|
|||||||
| `%%%` | 当前进度比率 |
|
| `%%%` | 当前进度比率 |
|
||||||
| `_` | 用以表示进度条占位|
|
| `_` | 用以表示进度条占位|
|
||||||
"""
|
"""
|
||||||
perEach = maxscore / pgs_style.count('_')
|
perEach = maxscore / pgs_style.count("_")
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
@ -451,20 +435,17 @@ class midiConvert:
|
|||||||
|
|
||||||
def replaceBar(_i):
|
def replaceBar(_i):
|
||||||
try:
|
try:
|
||||||
return pgs_style.replace(
|
return pgs_style.replace("_", progressbar[1][0], _i + 1).replace(
|
||||||
'_',
|
"_", progressbar[1][1]
|
||||||
progressbar[1][0],
|
)
|
||||||
_i +
|
|
||||||
1).replace(
|
|
||||||
'_',
|
|
||||||
progressbar[1][1])
|
|
||||||
except BaseError:
|
except BaseError:
|
||||||
return pgs_style.replace('_', progressbar[1][0], _i + 1)
|
return pgs_style.replace("_", progressbar[1][0], _i + 1)
|
||||||
|
|
||||||
sbn_pc = scoreboard_name[:2]
|
sbn_pc = scoreboard_name[:2]
|
||||||
if r"%%%" in pgs_style:
|
if r"%%%" in pgs_style:
|
||||||
result.append(
|
result.append(
|
||||||
"scoreboard objectives add {}PercT dummy \"百分比计算\"".format(sbn_pc))
|
'scoreboard objectives add {}PercT dummy "百分比计算"'.format(sbn_pc)
|
||||||
|
)
|
||||||
result.append(
|
result.append(
|
||||||
self.exeHead.format("@a[scores={" + scoreboard_name + "=1..}]")
|
self.exeHead.format("@a[scores={" + scoreboard_name + "=1..}]")
|
||||||
+ "scoreboard players set MaxScore {} {}".format(
|
+ "scoreboard players set MaxScore {} {}".format(
|
||||||
@ -496,9 +477,11 @@ class midiConvert:
|
|||||||
|
|
||||||
if r"%%t" in pgs_style:
|
if r"%%t" in pgs_style:
|
||||||
result.append(
|
result.append(
|
||||||
"scoreboard objectives add {}TMinT dummy \"时间计算:分\"".format(sbn_pc))
|
'scoreboard objectives add {}TMinT dummy "时间计算:分"'.format(sbn_pc)
|
||||||
|
)
|
||||||
result.append(
|
result.append(
|
||||||
"scoreboard objectives add {}TSecT dummy \"时间计算:秒\"".format(sbn_pc))
|
'scoreboard objectives add {}TSecT dummy "时间计算:秒"'.format(sbn_pc)
|
||||||
|
)
|
||||||
result.append(
|
result.append(
|
||||||
self.exeHead.format("@a[scores={" + scoreboard_name + "=1..}]")
|
self.exeHead.format("@a[scores={" + scoreboard_name + "=1..}]")
|
||||||
+ "scoreboard players set n20 {} 20".format(scoreboard_name)
|
+ "scoreboard players set n20 {} 20".format(scoreboard_name)
|
||||||
@ -546,7 +529,7 @@ class midiConvert:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
for i in range(pgs_style.count('_')):
|
for i in range(pgs_style.count("_")):
|
||||||
npg_stl = (
|
npg_stl = (
|
||||||
replaceBar(i).replace(r"%%N", self.midFileName)
|
replaceBar(i).replace(r"%%N", self.midFileName)
|
||||||
if r"%%N" in pgs_style
|
if r"%%N" in pgs_style
|
||||||
@ -568,9 +551,12 @@ class midiConvert:
|
|||||||
)
|
)
|
||||||
if r"%%t" in npg_stl:
|
if r"%%t" in npg_stl:
|
||||||
npg_stl = npg_stl.replace(
|
npg_stl = npg_stl.replace(
|
||||||
r"%%t", r'"},{"score":{"name":"*","objective":"{-}TMinT"}},{"text":":"},'
|
r"%%t",
|
||||||
|
r'"},{"score":{"name":"*","objective":"{-}TMinT"}},{"text":":"},'
|
||||||
r'{"score":{"name":"*","objective":"{-}TSecT"}},{"text":"'.replace(
|
r'{"score":{"name":"*","objective":"{-}TSecT"}},{"text":"'.replace(
|
||||||
r"{-}", sbn_pc), )
|
r"{-}", sbn_pc
|
||||||
|
),
|
||||||
|
)
|
||||||
result.append(
|
result.append(
|
||||||
self.exeHead.format(
|
self.exeHead.format(
|
||||||
"@a[scores={"
|
"@a[scores={"
|
||||||
@ -595,7 +581,8 @@ class midiConvert:
|
|||||||
self,
|
self,
|
||||||
scoreboard_name: str = "mscplay",
|
scoreboard_name: str = "mscplay",
|
||||||
MaxVolume: float = 1.0,
|
MaxVolume: float = 1.0,
|
||||||
speed: float = 1.0) -> list:
|
speed: float = 1.0,
|
||||||
|
) -> list:
|
||||||
"""
|
"""
|
||||||
使用Dislink Sforza的转换思路,将midi转换为我的世界命令列表
|
使用Dislink Sforza的转换思路,将midi转换为我的世界命令列表
|
||||||
:param scoreboard_name: 我的世界的计分板名称
|
:param scoreboard_name: 我的世界的计分板名称
|
||||||
@ -604,8 +591,7 @@ class midiConvert:
|
|||||||
"""
|
"""
|
||||||
# :param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
# :param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||||
tracks = []
|
tracks = []
|
||||||
MaxVolume = 1 if MaxVolume > 1 else (
|
MaxVolume = 1 if MaxVolume > 1 else (0.001 if MaxVolume <= 0 else MaxVolume)
|
||||||
0.001 if MaxVolume <= 0 else MaxVolume)
|
|
||||||
|
|
||||||
commands = 0
|
commands = 0
|
||||||
maxscore = 0
|
maxscore = 0
|
||||||
@ -639,17 +625,17 @@ class midiConvert:
|
|||||||
if msg.channel == 9:
|
if msg.channel == 9:
|
||||||
soundID, _X = self.__bitInst2ID_withX(instrumentID)
|
soundID, _X = self.__bitInst2ID_withX(instrumentID)
|
||||||
else:
|
else:
|
||||||
soundID, _X = self.__Inst2soundID_withX(
|
soundID, _X = self.__Inst2soundID_withX(instrumentID)
|
||||||
instrumentID)
|
|
||||||
|
|
||||||
singleTrack.append(
|
singleTrack.append(
|
||||||
"execute @a[scores={" +
|
"execute @a[scores={"
|
||||||
str(scoreboard_name) +
|
+ str(scoreboard_name)
|
||||||
"=" +
|
+ "="
|
||||||
str(nowscore) +
|
+ str(nowscore)
|
||||||
"}" +
|
+ "}"
|
||||||
f"] ~ ~ ~ playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg.velocity / 128} "
|
+ f"] ~ ~ ~ playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg.velocity / 128} "
|
||||||
f"{2 ** ((msg.note - 60 - _X) / 12)}")
|
f"{2 ** ((msg.note - 60 - _X) / 12)}"
|
||||||
|
)
|
||||||
commands += 1
|
commands += 1
|
||||||
if len(singleTrack) != 0:
|
if len(singleTrack) != 0:
|
||||||
tracks.append(singleTrack)
|
tracks.append(singleTrack)
|
||||||
@ -671,8 +657,7 @@ class midiConvert:
|
|||||||
:return: tuple(命令列表, 命令个数, 计分板最大值)
|
:return: tuple(命令列表, 命令个数, 计分板最大值)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
MaxVolume = 1 if MaxVolume > 1 else (
|
MaxVolume = 1 if MaxVolume > 1 else (0.001 if MaxVolume <= 0 else MaxVolume)
|
||||||
0.001 if MaxVolume <= 0 else MaxVolume)
|
|
||||||
|
|
||||||
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
||||||
channels = {
|
channels = {
|
||||||
@ -724,14 +709,12 @@ class midiConvert:
|
|||||||
if self.debugMode:
|
if self.debugMode:
|
||||||
try:
|
try:
|
||||||
if msg.channel > 15:
|
if msg.channel > 15:
|
||||||
raise ChannelOverFlowError(
|
raise ChannelOverFlowError(f"当前消息 {msg} 的通道超限(≤15)")
|
||||||
f"当前消息 {msg} 的通道超限(≤15)")
|
|
||||||
except BaseError:
|
except BaseError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if msg.type == "program_change":
|
if msg.type == "program_change":
|
||||||
channels[msg.channel].append(
|
channels[msg.channel].append(("PgmC", msg.program, microseconds))
|
||||||
("PgmC", msg.program, microseconds))
|
|
||||||
|
|
||||||
elif msg.type == "note_on" and msg.velocity != 0:
|
elif msg.type == "note_on" and msg.velocity != 0:
|
||||||
channels[msg.channel].append(
|
channels[msg.channel].append(
|
||||||
@ -741,8 +724,7 @@ class midiConvert:
|
|||||||
elif (msg.type == "note_on" and msg.velocity == 0) or (
|
elif (msg.type == "note_on" and msg.velocity == 0) or (
|
||||||
msg.type == "note_off"
|
msg.type == "note_off"
|
||||||
):
|
):
|
||||||
channels[msg.channel].append(
|
channels[msg.channel].append(("NoteE", msg.note, microseconds))
|
||||||
("NoteE", msg.note, microseconds))
|
|
||||||
|
|
||||||
"""整合后的音乐通道格式
|
"""整合后的音乐通道格式
|
||||||
每个通道包括若干消息元素其中逃不过这三种:
|
每个通道包括若干消息元素其中逃不过这三种:
|
||||||
@ -802,15 +784,13 @@ class midiConvert:
|
|||||||
|
|
||||||
nowTrack.append(
|
nowTrack.append(
|
||||||
self.exeHead.format(
|
self.exeHead.format(
|
||||||
"@a[scores=({}={})]".format(
|
"@a[scores=({}={})]".format(scoreboard_name, score_now)
|
||||||
scoreboard_name,
|
.replace("(", r"{")
|
||||||
score_now).replace(
|
.replace(")", r"}")
|
||||||
'(',
|
)
|
||||||
r"{").replace(
|
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
||||||
")",
|
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
||||||
r"}")) +
|
)
|
||||||
f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
|
||||||
f"{2 ** ((msg[1] - 60 - _X) / 12)}")
|
|
||||||
|
|
||||||
cmdAmount += 1
|
cmdAmount += 1
|
||||||
|
|
||||||
@ -839,8 +819,7 @@ class midiConvert:
|
|||||||
MaxVolume = 0.001
|
MaxVolume = 0.001
|
||||||
|
|
||||||
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
||||||
channels = [[], [], [], [], [], [], [],
|
channels = [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]
|
||||||
[], [], [], [], [], [], [], [], []]
|
|
||||||
|
|
||||||
# 我们来用通道统计音乐信息
|
# 我们来用通道统计音乐信息
|
||||||
for i, track in enumerate(self.midi.tracks):
|
for i, track in enumerate(self.midi.tracks):
|
||||||
@ -867,8 +846,7 @@ class midiConvert:
|
|||||||
channelMsg = False
|
channelMsg = False
|
||||||
if channelMsg:
|
if channelMsg:
|
||||||
if msg.channel > 15:
|
if msg.channel > 15:
|
||||||
raise ChannelOverFlowError(
|
raise ChannelOverFlowError(f"当前消息 {msg} 的通道超限(≤15)")
|
||||||
f"当前消息 {msg} 的通道超限(≤15)")
|
|
||||||
|
|
||||||
if msg.type == "program_change":
|
if msg.type == "program_change":
|
||||||
channels[msg.channel].append(
|
channels[msg.channel].append(
|
||||||
@ -883,8 +861,7 @@ class midiConvert:
|
|||||||
elif (msg.type == "note_on" and msg.velocity == 0) or (
|
elif (msg.type == "note_on" and msg.velocity == 0) or (
|
||||||
msg.type == "note_off"
|
msg.type == "note_off"
|
||||||
):
|
):
|
||||||
channels[msg.channel].append(
|
channels[msg.channel].append(("NoteE", msg.note, microseconds))
|
||||||
("NoteE", msg.note, microseconds))
|
|
||||||
|
|
||||||
"""整合后的音乐通道格式
|
"""整合后的音乐通道格式
|
||||||
每个通道包括若干消息元素其中逃不过这三种:
|
每个通道包括若干消息元素其中逃不过这三种:
|
||||||
@ -901,8 +878,7 @@ class midiConvert:
|
|||||||
|
|
||||||
("NoteS", 结束的音符ID, 距离演奏开始的毫秒)"""
|
("NoteS", 结束的音符ID, 距离演奏开始的毫秒)"""
|
||||||
|
|
||||||
note_channels = [[], [], [], [], [], [],
|
note_channels = [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]
|
||||||
[], [], [], [], [], [], [], [], [], []]
|
|
||||||
|
|
||||||
# 此处 我们把通道视为音轨
|
# 此处 我们把通道视为音轨
|
||||||
for i in range(len(channels)):
|
for i in range(len(channels)):
|
||||||
@ -995,13 +971,14 @@ class midiConvert:
|
|||||||
maxScore = max(maxScore, score_now)
|
maxScore = max(maxScore, score_now)
|
||||||
|
|
||||||
nowTrack.append(
|
nowTrack.append(
|
||||||
"execute @a[scores={" +
|
"execute @a[scores={"
|
||||||
str(scoreboard_name) +
|
+ str(scoreboard_name)
|
||||||
"=" +
|
+ "="
|
||||||
str(score_now) +
|
+ str(score_now)
|
||||||
"}" +
|
+ "}"
|
||||||
f"] ~ ~ ~ playsound {soundID} @s ~ ~{1 / every_note[4] - 1} ~ "
|
+ f"] ~ ~ ~ playsound {soundID} @s ~ ~{1 / every_note[4] - 1} ~ "
|
||||||
f"{note.velocity * (0.7 if CheckFirstChannel else 0.9)} {2 ** ((note.pitch - 60 - _X) / 12)}")
|
f"{note.velocity * (0.7 if CheckFirstChannel else 0.9)} {2 ** ((note.pitch - 60 - _X) / 12)}"
|
||||||
|
)
|
||||||
|
|
||||||
cmdAmount += 1
|
cmdAmount += 1
|
||||||
tracks.append(nowTrack)
|
tracks.append(nowTrack)
|
||||||
@ -1023,8 +1000,7 @@ class midiConvert:
|
|||||||
# :param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
# :param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||||
tracks = {}
|
tracks = {}
|
||||||
|
|
||||||
MaxVolume = 1 if MaxVolume > 1 else (
|
MaxVolume = 1 if MaxVolume > 1 else (0.001 if MaxVolume <= 0 else MaxVolume)
|
||||||
0.001 if MaxVolume <= 0 else MaxVolume)
|
|
||||||
|
|
||||||
for i, track in enumerate(self.midi.tracks):
|
for i, track in enumerate(self.midi.tracks):
|
||||||
|
|
||||||
@ -1047,14 +1023,16 @@ class midiConvert:
|
|||||||
soundID, _X = self.__Inst2soundID_withX(instrumentID)
|
soundID, _X = self.__Inst2soundID_withX(instrumentID)
|
||||||
try:
|
try:
|
||||||
tracks[now_tick].append(
|
tracks[now_tick].append(
|
||||||
self.exeHead.format(player) +
|
self.exeHead.format(player)
|
||||||
f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg.velocity / 128} "
|
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg.velocity / 128} "
|
||||||
f"{2 ** ((msg.note - 60 - _X) / 12)}")
|
f"{2 ** ((msg.note - 60 - _X) / 12)}"
|
||||||
|
)
|
||||||
except BaseError:
|
except BaseError:
|
||||||
tracks[now_tick] = [
|
tracks[now_tick] = [
|
||||||
self.exeHead.format(player) +
|
self.exeHead.format(player)
|
||||||
f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg.velocity / 128} "
|
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg.velocity / 128} "
|
||||||
f"{2 ** ((msg.note - 60 - _X) / 12)}"]
|
f"{2 ** ((msg.note - 60 - _X) / 12)}"
|
||||||
|
]
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
@ -1090,8 +1068,7 @@ class midiConvert:
|
|||||||
# :param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
# :param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||||
tracks = {}
|
tracks = {}
|
||||||
|
|
||||||
MaxVolume = 1 if MaxVolume > 1 else (
|
MaxVolume = 1 if MaxVolume > 1 else (0.001 if MaxVolume <= 0 else MaxVolume)
|
||||||
0.001 if MaxVolume <= 0 else MaxVolume)
|
|
||||||
|
|
||||||
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
||||||
channels = {
|
channels = {
|
||||||
@ -1143,14 +1120,12 @@ class midiConvert:
|
|||||||
if self.debugMode:
|
if self.debugMode:
|
||||||
try:
|
try:
|
||||||
if msg.channel > 15:
|
if msg.channel > 15:
|
||||||
raise ChannelOverFlowError(
|
raise ChannelOverFlowError(f"当前消息 {msg} 的通道超限(≤15)")
|
||||||
f"当前消息 {msg} 的通道超限(≤15)")
|
|
||||||
except BaseError:
|
except BaseError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if msg.type == "program_change":
|
if msg.type == "program_change":
|
||||||
channels[msg.channel].append(
|
channels[msg.channel].append(("PgmC", msg.program, microseconds))
|
||||||
("PgmC", msg.program, microseconds))
|
|
||||||
|
|
||||||
elif msg.type == "note_on" and msg.velocity != 0:
|
elif msg.type == "note_on" and msg.velocity != 0:
|
||||||
channels[msg.channel].append(
|
channels[msg.channel].append(
|
||||||
@ -1160,8 +1135,7 @@ class midiConvert:
|
|||||||
elif (msg.type == "note_on" and msg.velocity == 0) or (
|
elif (msg.type == "note_on" and msg.velocity == 0) or (
|
||||||
msg.type == "note_off"
|
msg.type == "note_off"
|
||||||
):
|
):
|
||||||
channels[msg.channel].append(
|
channels[msg.channel].append(("NoteE", msg.note, microseconds))
|
||||||
("NoteE", msg.note, microseconds))
|
|
||||||
|
|
||||||
"""整合后的音乐通道格式
|
"""整合后的音乐通道格式
|
||||||
每个通道包括若干消息元素其中逃不过这三种:
|
每个通道包括若干消息元素其中逃不过这三种:
|
||||||
@ -1212,14 +1186,16 @@ class midiConvert:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
tracks[score_now].append(
|
tracks[score_now].append(
|
||||||
self.exeHead.format(player) +
|
self.exeHead.format(player)
|
||||||
f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
||||||
f"{2 ** ((msg[1] - 60 - _X) / 12)}")
|
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
||||||
|
)
|
||||||
except BaseError:
|
except BaseError:
|
||||||
tracks[score_now] = [
|
tracks[score_now] = [
|
||||||
self.exeHead.format(player) +
|
self.exeHead.format(player)
|
||||||
f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
||||||
f"{2 ** ((msg[1] - 60 - _X) / 12)}"]
|
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
||||||
|
]
|
||||||
|
|
||||||
all_ticks = list(tracks.keys())
|
all_ticks = list(tracks.keys())
|
||||||
if self.debugMode:
|
if self.debugMode:
|
||||||
@ -1227,8 +1203,20 @@ class midiConvert:
|
|||||||
|
|
||||||
for i in range(len(all_ticks)):
|
for i in range(len(all_ticks)):
|
||||||
for j in range(len(tracks[all_ticks[i]])):
|
for j in range(len(tracks[all_ticks[i]])):
|
||||||
results.append((tracks[all_ticks[i]][j], (0 if j != 0 else (
|
results.append(
|
||||||
all_ticks[i] - all_ticks[i - 1] if i != 0 else all_ticks[i]))))
|
(
|
||||||
|
tracks[all_ticks[i]][j],
|
||||||
|
(
|
||||||
|
0
|
||||||
|
if j != 0
|
||||||
|
else (
|
||||||
|
all_ticks[i] - all_ticks[i - 1]
|
||||||
|
if i != 0
|
||||||
|
else all_ticks[i]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return [results, max(all_ticks)]
|
return [results, max(all_ticks)]
|
||||||
|
|
||||||
@ -1253,8 +1241,9 @@ class midiConvert:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# try:
|
# try:
|
||||||
cmdlist, maxlen, maxscore = self.methods[method -
|
cmdlist, maxlen, maxscore = self.methods[method - 1](
|
||||||
1](scoreboard_name, volume, speed)
|
scoreboard_name, volume, speed
|
||||||
|
)
|
||||||
# except:
|
# except:
|
||||||
# return (False, f"无法找到算法ID{method}对应的转换算法")
|
# return (False, f"无法找到算法ID{method}对应的转换算法")
|
||||||
|
|
||||||
@ -1268,17 +1257,19 @@ class midiConvert:
|
|||||||
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('{\n "format_version": 1,\n "header": {\n "description": "' +
|
f.write(
|
||||||
self.midFileName +
|
'{\n "format_version": 1,\n "header": {\n "description": "'
|
||||||
' Pack : behavior pack",\n "version": [ 0, 0, 1 ],\n "name": "' +
|
+ self.midFileName
|
||||||
self.midFileName +
|
+ ' Pack : behavior pack",\n "version": [ 0, 0, 1 ],\n "name": "'
|
||||||
'Pack",\n "uuid": "' +
|
+ self.midFileName
|
||||||
str(uuid.uuid4()) +
|
+ 'Pack",\n "uuid": "'
|
||||||
'"\n },\n "modules": [\n {\n "description": "' +
|
+ str(uuid.uuid4())
|
||||||
f"the Player of the Music {self.midFileName}" +
|
+ '"\n },\n "modules": [\n {\n "description": "'
|
||||||
'",\n "type": "data",\n "version": [ 0, 0, 1 ],\n "uuid": "' +
|
+ f"the Player of the Music {self.midFileName}"
|
||||||
str(uuid.uuid4()) +
|
+ '",\n "type": "data",\n "version": [ 0, 0, 1 ],\n "uuid": "'
|
||||||
'"\n }\n ]\n}')
|
+ str(uuid.uuid4())
|
||||||
|
+ '"\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"
|
||||||
@ -1292,14 +1283,14 @@ class midiConvert:
|
|||||||
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",
|
open(f"{self.outputPath}/temp/manifest.json", "w", encoding="utf-8").write(
|
||||||
encoding="utf-8").write(json.dumps(data))
|
json.dumps(data)
|
||||||
|
)
|
||||||
|
|
||||||
# 将命令列表写入文件
|
# 将命令列表写入文件
|
||||||
index_file = open(
|
index_file = open(
|
||||||
f"{self.outputPath}/temp/functions/index.mcfunction",
|
f"{self.outputPath}/temp/functions/index.mcfunction", "w", encoding="utf-8"
|
||||||
"w",
|
)
|
||||||
encoding="utf-8")
|
|
||||||
for track in cmdlist:
|
for track in cmdlist:
|
||||||
index_file.write(
|
index_file.write(
|
||||||
"function mscplay/track" + str(cmdlist.index(track) + 1) + "\n"
|
"function mscplay/track" + str(cmdlist.index(track) + 1) + "\n"
|
||||||
@ -1339,10 +1330,8 @@ class midiConvert:
|
|||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
) as f:
|
) as f:
|
||||||
f.writelines(
|
f.writelines(
|
||||||
"\n".join(
|
"\n".join(self.__formProgressBar(maxscore, scoreboard_name))
|
||||||
self.__formProgressBar(
|
)
|
||||||
maxscore,
|
|
||||||
scoreboard_name)))
|
|
||||||
else:
|
else:
|
||||||
with open(
|
with open(
|
||||||
f"{self.outputPath}/temp/functions/mscplay/progressShow.mcfunction",
|
f"{self.outputPath}/temp/functions/mscplay/progressShow.mcfunction",
|
||||||
@ -1361,8 +1350,9 @@ class midiConvert:
|
|||||||
|
|
||||||
if os.path.exists(f"{self.outputPath}/{self.midFileName}.mcpack"):
|
if os.path.exists(f"{self.outputPath}/{self.midFileName}.mcpack"):
|
||||||
os.remove(f"{self.outputPath}/{self.midFileName}.mcpack")
|
os.remove(f"{self.outputPath}/{self.midFileName}.mcpack")
|
||||||
makeZip(f"{self.outputPath}/temp/",
|
makeZip(
|
||||||
f"{self.outputPath}/{self.midFileName}.mcpack")
|
f"{self.outputPath}/temp/", f"{self.outputPath}/{self.midFileName}.mcpack"
|
||||||
|
)
|
||||||
|
|
||||||
shutil.rmtree(f"{self.outputPath}/temp/")
|
shutil.rmtree(f"{self.outputPath}/temp/")
|
||||||
|
|
||||||
@ -1429,7 +1419,8 @@ class midiConvert:
|
|||||||
)
|
)
|
||||||
|
|
||||||
cmdBytes, size, finalPos = toBDX_bytes(
|
cmdBytes, size, finalPos = toBDX_bytes(
|
||||||
[(i, 0) for i in commands], max_height - 1)
|
[(i, 0) for i in commands], max_height - 1
|
||||||
|
)
|
||||||
# 此处是对于仅有 True 的参数和自定义参数的判断
|
# 此处是对于仅有 True 的参数和自定义参数的判断
|
||||||
if progressbar:
|
if progressbar:
|
||||||
pgbBytes, pgbSize, pgbNowPos = toBDX_bytes(
|
pgbBytes, pgbSize, pgbNowPos = toBDX_bytes(
|
||||||
@ -1522,8 +1513,10 @@ class midiConvert:
|
|||||||
if progressbar:
|
if progressbar:
|
||||||
scb_name = self.midFileName[:5] + "Pgb"
|
scb_name = self.midFileName[:5] + "Pgb"
|
||||||
_bytes += formCMD_blk(
|
_bytes += formCMD_blk(
|
||||||
r"scoreboard objectives add {} dummy {}播放用".replace(
|
r"scoreboard objectives add {} dummy {}播放用".replace(r"{}", scb_name),
|
||||||
r"{}", scb_name), 1, customName="初始化进度条", )
|
1,
|
||||||
|
customName="初始化进度条",
|
||||||
|
)
|
||||||
_bytes += move(z, 2)
|
_bytes += move(z, 2)
|
||||||
_bytes += formCMD_blk(
|
_bytes += formCMD_blk(
|
||||||
r"scoreboard players add {} {} 1".format(player, scb_name),
|
r"scoreboard players add {} {} 1".format(player, scb_name),
|
||||||
@ -1596,8 +1589,7 @@ class midiConvert:
|
|||||||
else:
|
else:
|
||||||
|
|
||||||
if msg.type == "program_change":
|
if msg.type == "program_change":
|
||||||
channels[msg.channel].append(
|
channels[msg.channel].append(("PgmC", msg.program, microseconds))
|
||||||
("PgmC", msg.program, microseconds))
|
|
||||||
|
|
||||||
elif msg.type == "note_on" and msg.velocity != 0:
|
elif msg.type == "note_on" and msg.velocity != 0:
|
||||||
channels[msg.channel].append(
|
channels[msg.channel].append(
|
||||||
@ -1607,8 +1599,7 @@ class midiConvert:
|
|||||||
elif (msg.type == "note_on" and msg.velocity == 0) or (
|
elif (msg.type == "note_on" and msg.velocity == 0) or (
|
||||||
msg.type == "note_off"
|
msg.type == "note_off"
|
||||||
):
|
):
|
||||||
channels[msg.channel].append(
|
channels[msg.channel].append(("NoteE", msg.note, microseconds))
|
||||||
("NoteE", msg.note, microseconds))
|
|
||||||
|
|
||||||
"""整合后的音乐通道格式
|
"""整合后的音乐通道格式
|
||||||
每个通道包括若干消息元素其中逃不过这三种:
|
每个通道包括若干消息元素其中逃不过这三种:
|
||||||
|
@ -16,7 +16,7 @@ z = "z"
|
|||||||
|
|
||||||
def move(axis: str, value: int):
|
def move(axis: str, value: int):
|
||||||
if value == 0:
|
if value == 0:
|
||||||
return b''
|
return b""
|
||||||
if abs(value) == 1:
|
if abs(value) == 1:
|
||||||
return key[axis][0 if value == -1 else 1]
|
return key[axis][0 if value == -1 else 1]
|
||||||
|
|
||||||
@ -32,8 +32,7 @@ def move(axis: str, value: int):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
return key[axis][pointer] + \
|
return key[axis][pointer] + value.to_bytes(2 ** (pointer - 2), "big", signed=True)
|
||||||
value.to_bytes(2 ** (pointer - 2), 'big', signed=True)
|
|
||||||
|
|
||||||
|
|
||||||
def makeZip(sourceDir, outFilename, compression=8, exceptFile=None):
|
def makeZip(sourceDir, outFilename, compression=8, exceptFile=None):
|
||||||
@ -113,8 +112,7 @@ def formCMD_blk(
|
|||||||
|
|
||||||
:return:str
|
:return:str
|
||||||
"""
|
"""
|
||||||
block = b"\x24" + \
|
block = b"\x24" + particularValue.to_bytes(2, byteorder="big", signed=False)
|
||||||
particularValue.to_bytes(2, byteorder="big", signed=False)
|
|
||||||
|
|
||||||
for i in [
|
for i in [
|
||||||
impluse.to_bytes(4, byteorder="big", signed=False),
|
impluse.to_bytes(4, byteorder="big", signed=False),
|
||||||
@ -150,7 +148,7 @@ def toBDX_bytes(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
_sideLength = __fillSquareSideLength(len(commands), max_height)
|
_sideLength = __fillSquareSideLength(len(commands), max_height)
|
||||||
_bytes = b''
|
_bytes = b""
|
||||||
|
|
||||||
y_forward = True
|
y_forward = True
|
||||||
z_forward = True
|
z_forward = True
|
||||||
@ -191,8 +189,7 @@ def toBDX_bytes(
|
|||||||
|
|
||||||
now_y += 1 if y_forward else -1
|
now_y += 1 if y_forward else -1
|
||||||
|
|
||||||
if ((now_y >= max_height) and y_forward) or (
|
if ((now_y >= max_height) and y_forward) or ((now_y < 0) and (not y_forward)):
|
||||||
(now_y < 0) and (not y_forward)):
|
|
||||||
now_y -= 1 if y_forward else -1
|
now_y -= 1 if y_forward else -1
|
||||||
|
|
||||||
y_forward = not y_forward
|
y_forward = not y_forward
|
||||||
@ -200,7 +197,8 @@ def toBDX_bytes(
|
|||||||
now_z += 1 if z_forward else -1
|
now_z += 1 if z_forward else -1
|
||||||
|
|
||||||
if ((now_z > _sideLength) and z_forward) or (
|
if ((now_z > _sideLength) and z_forward) or (
|
||||||
(now_z < 0) and (not z_forward)):
|
(now_z < 0) and (not z_forward)
|
||||||
|
):
|
||||||
now_z -= 1 if z_forward else -1
|
now_z -= 1 if z_forward else -1
|
||||||
z_forward = not z_forward
|
z_forward = not z_forward
|
||||||
_bytes += key[x][1]
|
_bytes += key[x][1]
|
||||||
@ -215,6 +213,10 @@ def toBDX_bytes(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
_bytes,
|
_bytes,
|
||||||
[now_x + 1, max_height if now_x or now_z else now_y, _sideLength if now_x else now_z],
|
[
|
||||||
|
now_x + 1,
|
||||||
|
max_height if now_x or now_z else now_y,
|
||||||
|
_sideLength if now_x else now_z,
|
||||||
|
],
|
||||||
[now_x, now_y, now_z],
|
[now_x, now_y, now_z],
|
||||||
)
|
)
|
||||||
|
@ -18,7 +18,7 @@ z = "z"
|
|||||||
|
|
||||||
def move(axis: str, value: int):
|
def move(axis: str, value: int):
|
||||||
if value == 0:
|
if value == 0:
|
||||||
return b''
|
return b""
|
||||||
if abs(value) == 1:
|
if abs(value) == 1:
|
||||||
return key[axis][0 if value == -1 else 1]
|
return key[axis][0 if value == -1 else 1]
|
||||||
|
|
||||||
@ -34,8 +34,7 @@ def move(axis: str, value: int):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
return key[axis][pointer] + \
|
return key[axis][pointer] + value.to_bytes(2 ** (pointer - 2), "big", signed=True)
|
||||||
value.to_bytes(2 ** (pointer - 2), 'big', signed=True)
|
|
||||||
|
|
||||||
|
|
||||||
def makeZip(sourceDir, outFilename, compression=8, exceptFile=None):
|
def makeZip(sourceDir, outFilename, compression=8, exceptFile=None):
|
||||||
@ -115,8 +114,7 @@ def formCMD_blk(
|
|||||||
|
|
||||||
:return:str
|
:return:str
|
||||||
"""
|
"""
|
||||||
block = b"\x24" + \
|
block = b"\x24" + particularValue.to_bytes(2, byteorder="big", signed=False)
|
||||||
particularValue.to_bytes(2, byteorder="big", signed=False)
|
|
||||||
|
|
||||||
for i in [
|
for i in [
|
||||||
impluse.to_bytes(4, byteorder="big", signed=False),
|
impluse.to_bytes(4, byteorder="big", signed=False),
|
||||||
@ -162,7 +160,7 @@ def toLineBDX_bytes(
|
|||||||
axis: str,
|
axis: str,
|
||||||
forward: bool,
|
forward: bool,
|
||||||
):
|
):
|
||||||
_bytes = b''
|
_bytes = b""
|
||||||
impluse = 2
|
impluse = 2
|
||||||
needRedstone = False
|
needRedstone = False
|
||||||
customName = ""
|
customName = ""
|
||||||
@ -193,7 +191,7 @@ def toBDX_bytes(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
_sideLength = __fillSquareSideLength(len(commands), max_height)
|
_sideLength = __fillSquareSideLength(len(commands), max_height)
|
||||||
_bytes = b''
|
_bytes = b""
|
||||||
|
|
||||||
y_forward = True
|
y_forward = True
|
||||||
z_forward = True
|
z_forward = True
|
||||||
@ -234,8 +232,7 @@ def toBDX_bytes(
|
|||||||
|
|
||||||
now_y += 1 if y_forward else -1
|
now_y += 1 if y_forward else -1
|
||||||
|
|
||||||
if ((now_y >= max_height) and y_forward) or (
|
if ((now_y >= max_height) and y_forward) or ((now_y < 0) and (not y_forward)):
|
||||||
(now_y < 0) and (not y_forward)):
|
|
||||||
now_y -= 1 if y_forward else -1
|
now_y -= 1 if y_forward else -1
|
||||||
|
|
||||||
y_forward = not y_forward
|
y_forward = not y_forward
|
||||||
@ -243,7 +240,8 @@ def toBDX_bytes(
|
|||||||
now_z += 1 if z_forward else -1
|
now_z += 1 if z_forward else -1
|
||||||
|
|
||||||
if ((now_z > _sideLength) and z_forward) or (
|
if ((now_z > _sideLength) and z_forward) or (
|
||||||
(now_z < 0) and (not z_forward)):
|
(now_z < 0) and (not z_forward)
|
||||||
|
):
|
||||||
now_z -= 1 if z_forward else -1
|
now_z -= 1 if z_forward else -1
|
||||||
z_forward = not z_forward
|
z_forward = not z_forward
|
||||||
_bytes += key[x][1]
|
_bytes += key[x][1]
|
||||||
@ -258,7 +256,11 @@ def toBDX_bytes(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
_bytes,
|
_bytes,
|
||||||
[now_x + 1, max_height if now_x or now_z else now_y, _sideLength if now_x else now_z],
|
[
|
||||||
|
now_x + 1,
|
||||||
|
max_height if now_x or now_z else now_y,
|
||||||
|
_sideLength if now_x else now_z,
|
||||||
|
],
|
||||||
[now_x, now_y, now_z],
|
[now_x, now_y, now_z],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
Brotli==1.0.9
|
Brotli==1.0.9
|
||||||
mido==1.2.10
|
mido==1.2.10
|
||||||
rich==13.2.0
|
rich==13.2.0
|
||||||
|
zhdate==0.1
|
||||||
|
requests==2.27.1
|
Loading…
x
Reference in New Issue
Block a user