diff --git a/llc_cli.py b/llc_cli.py index 289063c..2dda336 100644 --- a/llc_cli.py +++ b/llc_cli.py @@ -116,7 +116,7 @@ def format_ipt( # 获取midi列表 while True: - midi_path = ipt(f"请键入MIDI地址或所在目录地址:") + midi_path = ipt(f"MIDI地址或所在目录地址:") try: if os.path.exists(midi_path): if os.path.isfile(midi_path): @@ -140,7 +140,7 @@ while True: # 获取输出地址 while True: - out_path = ipt(f"请键入文件生成输出地址:") + out_path = ipt(f"文件生成输出地址:") try: if not os.path.exists(out_path): prt("该地址不存在,或无法访问该地址,请重新输入。") @@ -170,26 +170,26 @@ def is_in_player(sth: str): output_file_format = format_ipt( - "请键入输出文件类型 (mcpack/0|bdx/1)", + "输出文件类型 (mcpack/0|bdx/1)", is_in_bdx_mcpack, "输入内容有误,请重新输入。", )[1] if output_file_format == 0: player_format = format_ipt( - "请选择播放器类型 (延迟/0|计分板/1|中继器/2)", + "播放器类型 (延迟/0|计分板/1|中继器/2)", is_in_player, "输入内容有误,请重新输入。", )[1] else: player_format = format_ipt( - "请选择播放器类型 (延迟/0|计分板/1)", + "播放器类型 (延迟/0|计分板/1)", is_in_player, "输入内容有误,请重新输入。", )[1] -old_exe_enabled = format_ipt( - "启用1.19以前的旧版execute指令格式 (否/0|是/1):", bool_str, "输入内容格式错误,应为 是/1/真/t/y 或 否/0/假/f/n" +old_exe_enabled = not format_ipt( + "启用新版代执行指令 (否/0|是/1):", bool_str, "输入内容格式错误,应为 是/1/真/t/y 或 否/0/假/f/n" )[1] diff --git a/llc_spec_Creeper.py b/llc_spec_Creeper.py new file mode 100644 index 0000000..0806346 --- /dev/null +++ b/llc_spec_Creeper.py @@ -0,0 +1,227 @@ +# -*- coding: utf-8 -*- + +# 伶伦 开发交流群 861684859 + + +""" +伶伦转换器暨模组特用奇巧 +Linglun Converter for Special Use with addons + +版权所有 © 2023 金羿 & 睿穆开发组 +Copyright © 2023 EillesWan & TriM Org. + +开源相关声明请见 ./License.md +Terms & Conditions: ./Lisense.md +""" + +__version__ = "0.0.1" + +import json +import os +import sys + +from rich.table import Table + +import Musicreater +from Musicreater.constants import MIDI_PITCH_NAME_TABLE, PERCUSSION_INSTRUMENT_LIST +from utils.io import * + +osc.project_name = "伶伦暨模组特用奇巧" +osc.version = __version__ + + +if len(sys.argv) > 0: + + def go_for_args(debugMode: str = "False", logfile: str = "True"): + global logger + osc.isRelease = False if debugMode.lower() in ("true", "1") else True + logger.printing = not osc.isRelease + logger.writing = True if logfile.lower() in ("true", "1") else False + + go_for_args(*sys.argv) + + +# 显示大标题 + +MainConsole.print( + "[#121110 on #F0F2F4] ", + style="#121110 on #F0F2F4", + justify="center", +) +MainConsole.rule( + title="[bold #AB70FF]欢迎使用{}".format(osc.project_name), + characters="=", + style="#26E2FF", +) +# MainConsole.rule(title="[bold #AB70FF]Welcome to Linglun Converter", characters="-") +MainConsole.rule( + title="[#AB70FF]版本{} | 音·创内核版本{}".format(__version__, Musicreater.__version__), + characters="-", + style="#26E2FF", +) +MainConsole.print( + "[#121110 on #F0F2F4] ", + style="#121110 on #F0F2F4", + justify="center", +) + + +# 获取midi列表 +while True: + midi_path = ipt(f"MIDI地址或所在目录地址:") + try: + if os.path.exists(midi_path): + if os.path.isfile(midi_path): + midis = (midi_path,) + elif os.path.isdir(midi_path): + midis = ( + os.path.join(midi_path, i) + for i in os.listdir(midi_path) + if i.lower().endswith(".mid") or i.lower().endswith(".midi") + ) + else: + prt("输入内容有误,请重新输入。") + continue + else: + prt("该地址不存在,或无法访问该地址,请重新输入。") + continue + except PermissionError: + prt("无法访问该地址,请检查是否给予程序相关文件的访问权限。") + continue + break + + +# percussion_only = format_ipt( +# "仅处理打击乐器 (否/0|是/1):", bool_str, "输入内容格式错误,应为 是/1/真/t/y 或 否/0/假/f/n" +# )[1] + +speed: float = format_ipt("播放速度", float_str, "错误,需要浮点型数据;请重新输入。")[1] + + + + +final_result: Dict[str, Dict[int, List[Tuple[str, int, str]]]] = {} + +for single_midi in midis: + fn = os.path.splitext(os.path.split(single_midi)[-1])[0] + midi_cvt = Musicreater.MidiConvert.from_midi_file( + single_midi, + ) + midi_cvt.to_music_channels() + + max_delay = 0 + instrument_ID = -1 + total_track = {} + instriments = {} + + # 此处 我们把通道视为音轨 + for i in midi_cvt.channels.keys(): + # 如果当前通道为空 则跳过 + if not midi_cvt.channels[i]: + continue + + # 第十通道是打击乐通道 + SpecialBits = True if i == 9 else False + + for track_no, track in midi_cvt.channels[i].items(): + for msg in track: + if msg[0] == "PgmC": + instrument_ID = msg[1] + + if msg[0] == "NoteS": + soundID, _X = ( + midi_cvt.perc_inst_to_soundID_withX(msg[1]) + if SpecialBits + else midi_cvt.inst_to_souldID_withX(instrument_ID) + ) + score_now = round(msg[-1] / float(speed) / 50) + max_delay = max(max_delay, score_now) + mc_pitch = "" if SpecialBits else 2 ** ((msg[1] - 60 - _X) / 12) + try: + total_track[score_now].append( + (MIDI_PITCH_NAME_TABLE[msg[1]], mc_pitch, soundID), + ) + except (IndexError, KeyError): + total_track[score_now] = [ + (MIDI_PITCH_NAME_TABLE[msg[1]], mc_pitch, soundID), + ] + + try: + instriments[soundID] += 1 + except (IndexError, KeyError): + instriments[soundID] = 1 + + del midi_cvt + + table = Table( + *instriments.keys(), + title="[bold #AB70FF on #121110]{} 乐器统计".format(fn), + title_style="#26E2FF on #121110", + ) + table.add_row(*[str(i) for i in instriments.values()]) + + MainConsole.print( + "[bold #8B50DF on #F0F2F4]-={}=-".format(fn), + style="#AB70FF on #F0F2F4", + justify="center", + ) + + prt(table) + + inst_selected: Set[str] = format_ipt( + """请选择需要保留的乐器;以空格作分割;以 percussion 表示全部打击乐器|以 pitched 表示全部乐音乐器|以 all 表示所有乐器\n:""", + lambda x: set( + [ + i.lower() + for i in x.split(" ") + if isin( + i, + { + True: ( + list(instriments.keys()) + ["all", "pitched", "percussion"] + ) + }, + ) + ] + ), + "输入错误,需要在已有的乐器范围之内。", + strict_mode=True, + )[1] + + if "all" in inst_selected: + inst_selected.remove("all") + for i in instriments.keys(): + inst_selected.add(i) + + if "percussion" in inst_selected: + inst_selected.remove("percussion") + for i in instriments.keys(): + if i in PERCUSSION_INSTRUMENT_LIST: + inst_selected.add(i) + + if "pitched" in inst_selected: + inst_selected.remove("pitched") + for i in instriments.keys(): + if i not in PERCUSSION_INSTRUMENT_LIST: + inst_selected.add(i) + + result_piece = [] + for i, ele in total_track.items(): + this_ele = [k for k in ele if k[-1] in inst_selected] + if this_ele: + result_piece.append((i, this_ele)) + + result_piece.sort(key=lambda x: x[0]) + + final_result[fn] = {} + for ind, ele in result_piece: + final_result[fn][ind - result_piece[0][0]] = ele + + with open("result.json", "w", encoding="utf-8") as f: + json.dump( + final_result, + f, + ensure_ascii=False, + indent=3, + sort_keys=True, + ) diff --git a/llc_win_tk.py b/llc_win_tk.py index ae38a00..69694d7 100644 --- a/llc_win_tk.py +++ b/llc_win_tk.py @@ -21,99 +21,90 @@ import os import random import sys -print("小贴:不妨试试Mid-BDX转换网页:在线的多功能Midi转换器") -print("https://dislink.github.io/midi2bdx/") import Musicreater +from Musicreater.plugin import ConvertConfig +from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score +from Musicreater.plugin.addonpack import ( + to_addon_pack_in_delay, + to_addon_pack_in_repeater, + to_addon_pack_in_score, +) +from Musicreater.constants import DEFAULT_PROGRESSBAR_STYLE + from utils.io import * -from languages.lang import languages - -MainConsole.print( - "[#121110 on #F0F2F4] ", - style="#121110 on #F0F2F4", - justify="center", -) osc.project_name = "伶伦转换器" osc.version = __version__ -def go_for_args( - languageChange: str = "ZH-CN", debugMode: str = "False", logfile: str = "True" -): - global currentLang - global logger - currentLang = ( - languageChange.upper() - if languageChange.upper() in languages.keys() - else "ZH-CN" - ) - osc.isRelease = False if debugMode.lower() in ("true", "1") else True - logger.printing = not osc.isRelease - logger.writing = True if logfile.lower() in ("true", "1") else False - - -if len(sys.argv) > 0: - go_for_args(*sys.argv) - - -def _(__): - """ - `languages` - """ - return languages[currentLang][__] - - -# 显示大标题 -MainConsole.rule(title="[bold #AB70FF]欢迎使用伶伦独立转换器", characters="=", style="#26E2FF") -MainConsole.rule(title="[bold #AB70FF]Welcome to Linglun Converter", characters="-") - -nowYang = datetime.datetime.now() - -if nowYang.month == 8 and nowYang.day == 6: - # 诸葛八卦生日 +def __main__(): MainConsole.print( - "[#7DB5F0 on #121110]今天可不是催更的日子!\n诸葛亮与八卦阵{}岁生日快乐!".format(nowYang.year - 2008), - style="#7DB5F0 on #121110", - justify="center", - ) -elif nowYang.month == 4 and nowYang.day == 3: - # 金羿生日快乐 - MainConsole.print( - "[#0089F2 on #F0F2F4]今天就不要催更啦!\n金羿{}岁生日快乐!".format(nowYang.year - 2006), - style="#0089F2 on #F0F2F4", - justify="center", - ) -else: - # 显示箴言部分 - MainConsole.print( - "[#121110 on #F0F2F4]{}".format(random.choice(myWords)), + "[#121110 on #F0F2F4] ", style="#121110 on #F0F2F4", justify="center", ) -prt(f"{_('LangChd')}{_(':')}{_(currentLang)}") + if len(sys.argv) > 0: + def go_for_args(debugMode: str = "False", logfile: str = "True"): + global logger + osc.isRelease = False if debugMode.lower() in ("true", "1") else True + logger.printing = not osc.isRelease + logger.writing = True if logfile.lower() in ("true", "1") else False + go_for_args(*sys.argv) + # 显示大标题 + MainConsole.rule(title="[bold #AB70FF]欢迎使用伶伦独立转换器", characters="=", style="#26E2FF") + MainConsole.rule(title="[bold #AB70FF]Welcome to Linglun Converter", characters="-") -def __main__(): + nowYang = datetime.datetime.now() - print('伶伦转换器简易版 正在启动……') + if nowYang.month == 8 and nowYang.day == 6: + # 诸葛八卦生日 + MainConsole.print( + "[#7DB5F0 on #121110]今天可不是催更的日子!\n诸葛亮与八卦阵{}岁生日快乐!".format( + nowYang.year - 2008 + ), + style="#7DB5F0 on #121110", + justify="center", + ) + elif nowYang.month == 4 and nowYang.day == 3: + # 金羿生日快乐 + MainConsole.print( + "[#0089F2 on #F0F2F4]今天就不要催更啦!\n金羿{}岁生日快乐!".format(nowYang.year - 2006), + style="#0089F2 on #F0F2F4", + justify="center", + ) + else: + # 显示箴言部分 + MainConsole.print( + "[#121110 on #F0F2F4]{}".format(random.choice(myWords)), + style="#121110 on #F0F2F4", + justify="center", + ) + prt("伶伦转换器简易版 正在启动……") - print('更新执行位置...') - if sys.platform == 'win32': + prt("更新执行位置...") + if sys.platform == "win32": try: - os.chdir(__file__[:len(__file__) - __file__[len(__file__)::-1].index('\\')]) - logger.info('更新执行位置,当前文件位置 {}'.format(__file__)) - except FileNotFoundError: # 程序规范修改:根据新的语法标准:except后面不能没有错误类型,所以既然是pass就随便填一个错误 + os.chdir( + __file__[: len(__file__) - __file__[len(__file__) :: -1].index("\\")] + ) + logger.info("Win32 更新执行位置,当前文件位置 {}".format(__file__)) + except FileNotFoundError: pass else: try: - os.chdir(__file__[:len(__file__) - __file__[len(__file__)::-1].index('/')]) - except Exception: + os.chdir( + __file__[: len(__file__) - __file__[len(__file__) :: -1].index("/")] + ) + except FileNotFoundError: pass - log('其他平台:{} 更新执行位置,当前文件位置 {}'.format(sys.platform, __file__)) - print('完成!') + log("其他平台:{} 更新执行位置,当前文件位置 {}".format(sys.platform, __file__)) + prt("完成!") + + prt("载入功能……") diff --git a/utils/io.py b/utils/io.py index 4cf084c..1ea7180 100644 --- a/utils/io.py +++ b/utils/io.py @@ -1,7 +1,7 @@ -from typing import Any, Literal, Optional, TextIO - -import urllib.request import urllib.error +import urllib.request +from typing import Any, Callable, Literal, Optional, Set, TextIO, Tuple, Dict, List + import TrimLog from TrimLog import Console, object_constants @@ -19,10 +19,10 @@ logger = TrimLog.Logger( try: myWords = ( urllib.request.urlopen( - 'https://gitee.com/TriM-Organization/LinglunStudio/raw/master/resources/myWords.txt' + "https://gitee.com/TriM-Organization/LinglunStudio/raw/master/resources/myWords.txt" ) .read() - .decode('utf-8') + .decode("utf-8") .strip("\n") .split("\n") ) @@ -158,27 +158,32 @@ def ipt( def format_ipt( notice: str, - fun, - err_note: str = "", + fun: Callable, + err_note: str = "{}", + strict_mode: bool = False, *extraArg, -): +) -> Tuple[str, Any]: """循环输入,以某种格式 notice: 输入时的提示 fun: 格式函数 err_note: 输入不符格式时的提示 + strict_mode: 是否将函数值作为结束循环的判断依据之一 *extraArg: 对于函数的其他参数""" while True: result = ipt(notice) - # noinspection PyBroadException try: - fun_result = fun(result, *extraArg) - break - # noinspection PyBroadException - except BaseException: - prt(err_note) + if strict_mode: + if fun_result := fun(result, *extraArg): + break + else: + fun_result = fun(result, *extraArg) + break + except ValueError as E: + prt(err_note.format(E)) continue return result, fun_result + def isin(sth: str, range_list: dict): sth = sth.lower() for bool_value, res_list in range_list.items(): @@ -192,9 +197,9 @@ def bool_str(sth: str): try: return bool(float(sth)) except: - if str(sth).lower() in ("true", "真", "是", 'y', 't'): + if str(sth).lower() in ("true", "真", "是", "y", "t"): return True - elif str(sth).lower() in ("false", "假", "否", 'f', 'n'): + elif str(sth).lower() in ("false", "假", "否", "f", "n"): return False else: raise ValueError @@ -205,12 +210,40 @@ def float_str(sth: str): return float(sth) except ValueError: try: - return float(sth.replace("壹", "1").replace("贰", "2").replace("叁", "3").replace("肆", "4").replace("伍", "5").replace("陆", "6").replace("柒", "7").replace("捌", "8").replace("玖", "9").replace("零", "0").replace("一", "1").replace("二",'2').replace("三", "3").replace("四", "4").replace("五", "5").replace("六", "6").replace("七", "7").replace("八", "8").replace("九", "9").replace("〇", "0").replace("洞", "0").replace("幺", "1").replace("俩", "2").replace("两", "2").replace("拐","7").replace("点",'.')) + return float( + sth.replace("壹", "1") + .replace("贰", "2") + .replace("叁", "3") + .replace("肆", "4") + .replace("伍", "5") + .replace("陆", "6") + .replace("柒", "7") + .replace("捌", "8") + .replace("玖", "9") + .replace("零", "0") + .replace("一", "1") + .replace("二", "2") + .replace("三", "3") + .replace("四", "4") + .replace("五", "5") + .replace("六", "6") + .replace("七", "7") + .replace("八", "8") + .replace("九", "9") + .replace("〇", "0") + .replace("洞", "0") + .replace("幺", "1") + .replace("俩", "2") + .replace("两", "2") + .replace("拐", "7") + .replace("点", ".") + ) except: raise ValueError + def int_str(sth: str): try: return int(float_str(sth)) except ValueError: - raise ValueError \ No newline at end of file + raise ValueError