From d4925e4d753351511a9057f7409b4b699162aa55 Mon Sep 17 00:00:00 2001 From: EillesWan Date: Sun, 10 Mar 2024 02:22:12 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9B=E4=B8=80=E6=AD=A5=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=BB=93=E6=9E=84=EF=BC=8C=E6=8F=90=E9=AB=98=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Musicreater/__init__.py | 3 +- Musicreater/constants.py | 43 +++- Musicreater/experiment.py | 91 ++++--- Musicreater/main.py | 236 +++++++++++------- Musicreater/plugin/__init__.py | 6 +- Musicreater/plugin/addonpack/__init__.py | 2 + Musicreater/plugin/addonpack/main.py | 260 +++++++++++++++----- Musicreater/plugin/bdxfile/main.py | 48 ++-- Musicreater/plugin/main.py | 126 +++++----- Musicreater/plugin/mcstructfile/__init__.py | 2 + Musicreater/plugin/mcstructfile/main.py | 113 +++++++-- Musicreater/subclass.py | 20 ++ Musicreater/utils.py | 45 ++-- example.py | 49 +++- example_futureFunction.py | 3 +- example_singleConvert.py | 14 ++ 16 files changed, 732 insertions(+), 329 deletions(-) create mode 100644 example_singleConvert.py diff --git a/Musicreater/__init__.py b/Musicreater/__init__.py index b3efed4..f5dd249 100644 --- a/Musicreater/__init__.py +++ b/Musicreater/__init__.py @@ -17,7 +17,7 @@ Terms & Conditions: License.md in the root directory # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md -__version__ = "2.0.0-alpha" +__version__ = "2.0.0-beta" __vername__ = "全新组织架构" __author__ = ( ("金羿", "Eilles Wan"), @@ -34,6 +34,7 @@ __all__ = [ "MineNote", "MineCommand", "SingleNoteBox", + "ProgressBarStyle", # "TimeStamp", 未来功能 # 默认值 "DEFAULT_PROGRESSBAR_STYLE", diff --git a/Musicreater/constants.py b/Musicreater/constants.py index 9d3772c..f9683a1 100644 --- a/Musicreater/constants.py +++ b/Musicreater/constants.py @@ -385,6 +385,23 @@ MC_PERCUSSION_INSTRUMENT_LIST: List[str] = [ ] """打击乐器列表""" +MC_PITCHED_INSTRUMENT_LIST: List[str] = [ + "note.harp", + "note.pling", + "note.guitar", + "note.iron_xylophone", + "note.bell", + "note.xylophone", + "note.chime", + "note.banjo", + "note.flute", + "note.bass", + "note.didgeridoo", + "note.bit", + "note.cow_bell", +] +"""乐音乐器列表""" + MC_INSTRUMENT_BLOCKS_TABLE: Dict[str, Tuple[str, ...]] = { "note.bass": ("planks",), "note.snare": ("sand",), @@ -425,7 +442,7 @@ MM_INSTRUMENT_RANGE_TABLE: Dict[str, Tuple[Tuple[int, int], int]] = { "note.banjo": ((42, 66), 54), "note.flute": ((54, 78), 66), "note.bass": ((18, 42), 30), - "note.snare": ((-1, 128), 0), # 实际上是 0~127 + "note.snare": ((-1, 128), 0), # 实际上是 0~127 "note.didgeridoo": ((18, 42), 30), "mob.zombie.wood": ((-1, 128), 0), "note.bit": ((42, 66), 54), @@ -439,6 +456,30 @@ MM_INSTRUMENT_RANGE_TABLE: Dict[str, Tuple[Tuple[int, int], int]] = { } """不同乐器的音域偏离对照表""" +MM_INSTRUMENT_DEVIATION_TABLE: Dict[str, int] = { + "note.harp": 6, + "note.pling": 6, + "note.guitar": 7, + "note.iron_xylophone": 6, + "note.bell": 4, + "note.xylophone": 4, + "note.chime": 4, + "note.banjo": 6, + "note.flute": 5, + "note.bass": 8, + "note.snare": -1, + "note.didgeridoo": 8, + "mob.zombie.wood": -1, + "note.bit": 6, + "note.hat": -1, + "note.bd": -1, + "firework.blast": -1, + "firework.twinkle": -1, + "fire.ignite": -1, + "note.cow_bell": 5, +} +"""不同乐器的音调偏离对照表""" + # Midi乐器对MC乐器对照表 MM_CLASSIC_PITCHED_INSTRUMENT_TABLE: Dict[int, str] = { diff --git a/Musicreater/experiment.py b/Musicreater/experiment.py index 6d267a6..20a587b 100644 --- a/Musicreater/experiment.py +++ b/Musicreater/experiment.py @@ -47,12 +47,12 @@ class FutureMidiConvertM4(MidiConvert): # 临时用的插值计算函数 @staticmethod def _linear_note( - _note: SingleNote, - _apply_time_division: float = 100, - ) -> List[SingleNote]: - """传入音符数据,返回以半秒为分割的插值列表 + _note: MineNote, + _apply_time_division: float = 10, + ) -> List[MineNote]: + """传入音符数据,返回分割后的插值列表 :param _note: SingleNote 音符 - :param _apply_time_division: int 间隔毫秒数 + :param _apply_time_division: int 间隔帧数 :return list[tuple(int开始时间(毫秒), int乐器, int音符, int力度(内置), float音量(播放)),]""" if _note.percussive: @@ -63,21 +63,24 @@ class FutureMidiConvertM4(MidiConvert): totalCount = int(_note.duration / _apply_time_division) if totalCount == 0: + print(_note.extra_info) return [ _note, ] # print(totalCount) - result: List[SingleNote] = [] + result: List[MineNote] = [] for _i in range(totalCount): result.append( - SingleNote( - instrument=_note.inst, - pitch=_note.pitch, - velocity=_note.velocity, - startime=int(_note.start_time + _i * (_note.duration / totalCount)), - lastime=int(_note.duration / totalCount), + MineNote( + mc_sound_name=_note.sound_name, + midi_pitch=_note.note_pitch, + midi_velocity=_note.velocity, + start_time=int( + _note.start_tick + _i * (_note.duration / totalCount) + ), + last_time=int(_note.duration / totalCount), track_number=_note.track_no, is_percussion=_note.percussive, extra_information=_note.extra_info, @@ -116,54 +119,68 @@ class FutureMidiConvertM4(MidiConvert): for channel in self.channels.values(): for note in channel: note.set_info( - single_note_to_note_parameters( + minenote_to_command_paramaters( note, - ( - self.percussion_note_referrence_table - if note.percussive - else self.pitched_note_reference_table - ), - deviation=0, - volume_percentage=( - (max_volume) if note.track_no == 0 else (max_volume * 0.9) - ), - volume_processing_method=self.volume_processing_function, + pitch_deviation=self.music_deviation, ) ) if not note.percussive: - notes_list.extend(self._linear_note(note, note.extra_info[3] * 500)) + notes_list.extend(self._linear_note(note,1 * note.extra_info[3])) else: notes_list.append(note) - notes_list.sort(key=lambda a: a.start_time) + notes_list.sort(key=lambda a: a.start_tick) self.music_command_list = [] multi = max_multi = 0 delaytime_previous = 0 for note in notes_list: - delaytime_now = round(note.start_time / speed / 50) - if (tickdelay := (delaytime_now - delaytime_previous)) == 0: + if (tickdelay := (note.start_tick - delaytime_previous)) == 0: multi += 1 else: max_multi = max(max_multi, multi) multi = 0 + ( + mc_sound_ID, + relative_coordinates, + volume_percentage, + mc_pitch, + ) = note.extra_info self.music_command_list.append( MineCommand( - self.execute_cmd_head.format(player_selector) - + r"playsound {} @s ^ ^ ^{} {} {}".format(*note.extra_info), - tick_delay=tickdelay, - annotation="在{}播放{}%的{}音".format( - mctick2timestr(delaytime_now), - max_volume * 100, - "{}:{}".format(note.extra_info[0], note.extra_info[3]), + command=( + self.execute_cmd_head.format(player_selector) + + r"playsound {} @s ^{} ^{} ^{} {} {} {}".format( + mc_sound_ID, + *relative_coordinates, + volume_percentage, + 1.0 if note.percussive else mc_pitch, + self.minium_volume, + ) ), - ) + annotation=( + "在{}播放噪音{}".format( + mctick2timestr(note.start_tick), + mc_sound_ID, + ) + if note.percussive + else "在{}播放乐音{}".format( + mctick2timestr(note.start_tick), + "{}:{:.2f}".format(mc_sound_ID, mc_pitch), + ) + ), + tick_delay=tickdelay, + ), ) - delaytime_previous = delaytime_now + delaytime_previous = note.start_tick - return self.music_command_list, round(notes_list[-1].start_time / speed / 50), max_multi + 1 + return ( + self.music_command_list, + notes_list[-1].start_tick + notes_list[-1].duration, + max_multi + 1, + ) class FutureMidiConvertM5(MidiConvert): diff --git a/Musicreater/main.py b/Musicreater/main.py index 8bf0720..c886e66 100644 --- a/Musicreater/main.py +++ b/Musicreater/main.py @@ -70,13 +70,6 @@ tick * tempo / 1000000.0 / ticks_per_beat * 一秒多少游戏刻 """ -# VoidMido = Union[mido.MidiFile, None] # void mido -# """ -# 空Midi类类型 -# """ -# 已经成为历史了 - - @dataclass(init=False) class MusicSequence: """ @@ -92,23 +85,23 @@ class MusicSequence: total_note_count: int """音符总数""" - used_instrument: List[str] + note_count_per_instrument: Dict[str, int] """所使用的乐器""" minium_volume: float """乐曲最小音量""" music_deviation: float - """乐曲音调偏移""" + """全曲音调偏移""" def __init__( self, name_of_music: str, channels_of_notes: MineNoteChannelType, music_note_count: Optional[int] = None, - used_instrument_of_music: Optional[List[str]] = None, + note_used_per_instrument: Optional[Dict[str, int]] = None, minium_volume_of_music: float = 0.1, - deviation: Optional[float] = None, + deviation_value: Optional[float] = None, ) -> None: """ 《我的世界》音符序列类 @@ -135,26 +128,21 @@ class MusicSequence: self.channels = channels_of_notes self.minium_volume = minium_volume_of_music - if used_instrument_of_music is None or music_note_count is None: + if (note_used_per_instrument is None) or (music_note_count is None): kp = [i.sound_name for j in self.channels.values() for i in j] self.total_note_count = ( len(kp) if music_note_count is None else music_note_count ) - self.used_instrument = ( - list(set(kp)) - if used_instrument_of_music is None - else used_instrument_of_music + self.note_count_per_instrument = ( + dict([(it, kp.count(it)) for it in set(kp)]) + if note_used_per_instrument is None + else note_used_per_instrument ) + else: + self.total_note_count = music_note_count + self.note_count_per_instrument = note_used_per_instrument - self.music_deviation = ( - self.guess_deviation( - self.total_note_count, - len(self.used_instrument), - music_channels=self.channels, - ) - if deviation is None - else deviation - ) + self.music_deviation = 0 if deviation_value is None else deviation_value @classmethod def from_mido( @@ -168,35 +156,28 @@ class MusicSequence: minium_vol: float = 0.1, volume_processing_function: FittingFunctionType = natural_curve, default_tempo: int = mido.midifiles.midifiles.DEFAULT_TEMPO, - devation_guess_enabled: bool = True, + deviation: float = 0, ): - note_channels, note_count_total, inst_note_count, qualified_inst_note_count = ( - cls.to_music_note_channels( - midi=mido_file, - speed=speed_multiplier, - pitched_note_rtable=pitched_note_referance_table, - percussion_note_rtable=percussion_note_referance_table, - default_tempo_value=default_tempo, - vol_processing_function=volume_processing_function, - ignore_mismatch_error=mismatch_error_ignorance, - ) + ( + note_channels, + note_count_total, + inst_note_count, # qualified_inst_note_count, + ) = cls.to_music_note_channels( + midi=mido_file, + speed=speed_multiplier, + pitched_note_rtable=pitched_note_referance_table, + percussion_note_rtable=percussion_note_referance_table, + default_tempo_value=default_tempo, + vol_processing_function=volume_processing_function, + ignore_mismatch_error=mismatch_error_ignorance, ) return cls( name_of_music=midi_music_name, channels_of_notes=note_channels, music_note_count=note_count_total, - used_instrument_of_music=list(inst_note_count.keys()), + note_used_per_instrument=inst_note_count, minium_volume_of_music=minium_vol, - deviation=( - cls.guess_deviation( - note_count_total, - len(inst_note_count), - inst_note_count, - qualified_inst_note_count, - ) - if devation_guess_enabled - else 0 - ), + deviation_value=deviation, ) def set_min_volume(self, volume_value: int): @@ -215,30 +196,31 @@ class MusicSequence: self.channels[channel_no].sort(key=lambda note: note.start_tick) @staticmethod - def guess_deviation( + def guess_deviation_wasted( total_note_count: int, total_instrument_count: int, - note_count_per_instruments: Optional[Dict[str, int]] = None, - qualified_note_count_per_instruments: Optional[Dict[str, int]] = None, + note_count_per_instrument: Optional[Dict[str, int]] = None, + qualified_note_count_per_instrument: Optional[Dict[str, int]] = None, music_channels: Optional[MineNoteChannelType] = None, ) -> float: + """已废弃""" if ( - note_count_per_instruments is None - or qualified_note_count_per_instruments is None + note_count_per_instrument is None + or qualified_note_count_per_instrument is None ): if music_channels is None: raise ValueError("参数不足,算逑!") - note_count_per_instruments = {} - qualified_note_count_per_instruments = {} + note_count_per_instrument = {} + qualified_note_count_per_instrument = {} for this_note in [k for j in music_channels.values() for k in j]: - if this_note.sound_name in note_count_per_instruments.keys(): - note_count_per_instruments[this_note.sound_name] += 1 - qualified_note_count_per_instruments[ + if this_note.sound_name in note_count_per_instrument.keys(): + note_count_per_instrument[this_note.sound_name] += 1 + qualified_note_count_per_instrument[ this_note.sound_name ] += is_note_in_diapason(this_note) else: - note_count_per_instruments[this_note.sound_name] = 1 - qualified_note_count_per_instruments[this_note.sound_name] = int( + note_count_per_instrument[this_note.sound_name] = 1 + qualified_note_count_per_instrument[this_note.sound_name] = int( is_note_in_diapason(this_note) ) return ( @@ -251,9 +233,9 @@ class MusicSequence: / total_note_count - MM_INSTRUMENT_RANGE_TABLE[inst][-1] ) - * (note_count - qualified_note_count_per_instruments[inst]) + * (note_count - qualified_note_count_per_instrument[inst]) ) - for inst, note_count in note_count_per_instruments.items() + for inst, note_count in note_count_per_instrument.items() ] ) / total_instrument_count @@ -269,7 +251,7 @@ class MusicSequence: percussion_note_rtable: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE, default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO, vol_processing_function: FittingFunctionType = natural_curve, - ) -> Tuple[MineNoteChannelType, int, Dict[str, int], Dict[str, int]]: + ) -> Tuple[MineNoteChannelType, int, Dict[str, int]]: # , Dict[str, int]]: """ 将midi解析并转换为频道音符字典 @@ -291,8 +273,8 @@ class MusicSequence: midi_channels: MineNoteChannelType = empty_midi_channels(staff=[]) tempo = default_tempo_value note_count = 0 - note_count_per_instruments: Dict[str, int] = {} - qualified_note_count_per_instruments: Dict[str, int] = {} + note_count_per_instrument: Dict[str, int] = {} + # qualified_note_count_per_instruments: Dict[str, int] = {} # 我们来用通道统计音乐信息 # 但是是用分轨的思路的 @@ -382,19 +364,16 @@ class MusicSequence: ) ) note_count += 1 - if ( - that_note.sound_name - in note_count_per_instruments.keys() - ): - note_count_per_instruments[that_note.sound_name] += 1 - qualified_note_count_per_instruments[ - that_note.sound_name - ] += is_note_in_diapason(that_note) + if that_note.sound_name in note_count_per_instrument.keys(): + note_count_per_instrument[that_note.sound_name] += 1 + # qualified_note_count_per_instruments[ + # that_note.sound_name + # ] += is_note_in_diapason(that_note) else: - note_count_per_instruments[that_note.sound_name] = 1 - qualified_note_count_per_instruments[ - that_note.sound_name - ] = int(is_note_in_diapason(that_note)) + note_count_per_instrument[that_note.sound_name] = 1 + # qualified_note_count_per_instruments[ + # that_note.sound_name + # ] = int(is_note_in_diapason(that_note)) else: if ignore_mismatch_error: print( @@ -431,8 +410,8 @@ class MusicSequence: return ( channels, note_count, - note_count_per_instruments, - qualified_note_count_per_instruments, + note_count_per_instrument, + # qualified_note_count_per_instruments, ) @@ -463,7 +442,7 @@ class MidiConvert(MusicSequence): default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO, pitched_note_rtable: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE, percussion_note_rtable: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE, - enable_devation_guess: bool = True, + # enable_devation_guess: bool = True, enable_old_exe_format: bool = False, minium_volume: float = 0.1, vol_processing_function: FittingFunctionType = natural_curve, @@ -505,7 +484,7 @@ class MidiConvert(MusicSequence): minium_vol=minium_volume, volume_processing_function=vol_processing_function, default_tempo=default_tempo_value, - devation_guess_enabled=enable_devation_guess, + # devation_guess_enabled=enable_devation_guess, mismatch_error_ignorance=ignore_mismatch_error, ) @@ -518,7 +497,7 @@ class MidiConvert(MusicSequence): default_tempo: int = mido.midifiles.midifiles.DEFAULT_TEMPO, pitched_note_table: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE, percussion_note_table: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE, - devation_guess_enabled: bool = True, + # devation_guess_enabled: bool = True, old_exe_format: bool = False, min_volume: float = 0.1, vol_processing_func: FittingFunctionType = natural_curve, @@ -560,13 +539,13 @@ class MidiConvert(MusicSequence): default_tempo_value=default_tempo, pitched_note_rtable=pitched_note_table, percussion_note_rtable=percussion_note_table, - enable_devation_guess=devation_guess_enabled, + # enable_devation_guess=devation_guess_enabled, enable_old_exe_format=old_exe_format, minium_volume=min_volume, vol_processing_function=vol_processing_func, ) except (ValueError, TypeError) as E: - raise MidiDestroyedError(f"文件{midi_file_path}损坏:{E}") + raise MidiDestroyedError(f"文件{midi_file_path}可能损坏:{E}") except FileNotFoundError as E: raise FileNotFoundError(f"文件{midi_file_path}不存在:{E}") @@ -584,13 +563,13 @@ class MidiConvert(MusicSequence): Parameters ---------- - maxscore: int + max_score: int midi的乐器ID scoreboard_name: str 所使用的计分板名称 - progressbar_style: tuple + progressbar_style: ProgressBarStyle 此参数详见 ../docs/库的生成与功能文档.md#进度条自定义 Returns @@ -1004,6 +983,93 @@ class MidiConvert(MusicSequence): return self.music_command_list, notes_list[-1].start_tick, max_multi + 1 + def to_command_list_in_delay_devided_by_instrument( + self, + player_selector: str = "@a", + ) -> Tuple[Dict[str, List[MineCommand]], int, Dict[str, int]]: + """ + 将midi转换为我的世界命令列表,并输出每个音符之后的延迟 + + Parameters + ---------- + player_selector: str + 玩家选择器,默认为`@a` + + Returns + ------- + Tuple[Dict[str, List[MineCommand]], int音乐时长游戏刻, int最大同时播放的指令数量 ) + """ + + notes_list: List[MineNote] = sorted( + [i for j in self.channels.values() for i in j], + key=lambda note: note.start_tick, + ) + + command_dict: Dict[str, List[MineCommand]] = dict( + [(inst, []) for inst in self.note_count_per_instrument.keys()] + ) + multi: Dict[str, int] = dict( + [(inst, 0) for inst in self.note_count_per_instrument.keys()] + ) + max_multi: Dict[str, int] = dict( + [(inst, 0) for inst in self.note_count_per_instrument.keys()] + ) + delaytime_previous: Dict[str, int] = dict( + [(inst, 0) for inst in self.note_count_per_instrument.keys()] + ) + + for note in notes_list: + if ( + tickdelay := (note.start_tick - delaytime_previous[note.sound_name]) + ) == 0: + multi[note.sound_name] += 1 + else: + max_multi[note.sound_name] = max( + max_multi[note.sound_name], multi[note.sound_name] + ) + multi[note.sound_name] = 0 + + ( + mc_sound_ID, + relative_coordinates, + volume_percentage, + mc_pitch, + ) = minenote_to_command_paramaters( + note, + pitch_deviation=self.music_deviation, + ) + + command_dict[note.sound_name].append( + MineCommand( + command=( + self.execute_cmd_head.format(player_selector) + + r"playsound {} @s ^{} ^{} ^{} {} {} {}".format( + mc_sound_ID, + *relative_coordinates, + volume_percentage, + 1.0 if note.percussive else mc_pitch, + self.minium_volume, + ) + ), + annotation=( + "在{}播放噪音{}".format( + mctick2timestr(note.start_tick), + mc_sound_ID, + ) + if note.percussive + else "在{}播放乐音{}".format( + mctick2timestr(note.start_tick), + "{}:{:.2f}".format(mc_sound_ID, mc_pitch), + ) + ), + tick_delay=tickdelay, + ), + ) + delaytime_previous[note.sound_name] = note.start_tick + + self.music_command_list = [j for i in command_dict.values() for j in i] + return command_dict, notes_list[-1].start_tick, max_multi + def copy_important(self): dst = MidiConvert.from_mido_obj( midi_obj=mido.MidiFile(), diff --git a/Musicreater/plugin/__init__.py b/Musicreater/plugin/__init__.py index c9a7506..ed2af53 100644 --- a/Musicreater/plugin/__init__.py +++ b/Musicreater/plugin/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -存放非音·创本体的附加内容(插件?) +存放非音·创本体的附加功能件 版权所有 © 2024 音·创 开发者 Copyright © 2024 all the developers of Musicreater @@ -16,7 +16,7 @@ Terms & Conditions: License.md in the root directory __all__ = [ # 通用 - "ConvertConfig", + # "ConvertConfig", "bottem_side_length_of_smallest_square_bottom_box", # 打包 "compress_zipfile", @@ -43,7 +43,7 @@ __all__ = [ ] __author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray")) -from .main import ConvertConfig +# from .main import ConvertConfig from .archive import compress_zipfile, behavior_mcpack_manifest diff --git a/Musicreater/plugin/addonpack/__init__.py b/Musicreater/plugin/addonpack/__init__.py index 8fe5539..56af4c0 100644 --- a/Musicreater/plugin/addonpack/__init__.py +++ b/Musicreater/plugin/addonpack/__init__.py @@ -18,6 +18,7 @@ __all__ = [ "to_addon_pack_in_delay", "to_addon_pack_in_score", "to_addon_pack_in_repeater", + "to_addon_pack_in_repeater_divided_by_instrument", ] __author__ = (("金羿", "Eilles Wan"),) @@ -25,4 +26,5 @@ from .main import ( to_addon_pack_in_delay, to_addon_pack_in_repeater, to_addon_pack_in_score, + to_addon_pack_in_repeater_divided_by_instrument, ) diff --git a/Musicreater/plugin/addonpack/main.py b/Musicreater/plugin/addonpack/main.py index 5837e5c..71105c3 100644 --- a/Musicreater/plugin/addonpack/main.py +++ b/Musicreater/plugin/addonpack/main.py @@ -16,12 +16,12 @@ import os import shutil from typing import Tuple -from TrimMCStruct import Structure - from ...main import MidiConvert from ..archive import behavior_mcpack_manifest, compress_zipfile -from ..main import ConvertConfig +from ...subclass import ProgressBarStyle +from ...types import Optional, Literal from ..mcstructure import ( + Structure, COMPABILITY_VERSION_117, COMPABILITY_VERSION_119, commands_to_redstone_delay_structure, @@ -32,7 +32,8 @@ from ..mcstructure import ( def to_addon_pack_in_score( midi_cvt: MidiConvert, - data_cfg: ConvertConfig, + dist_path: str, + progressbar_style: Optional[ProgressBarStyle], scoreboard_name: str = "mscplay", auto_reset: bool = False, ) -> Tuple[int, int]: @@ -43,8 +44,10 @@ def to_addon_pack_in_score( ---------- midi_cvt: MidiConvert 对象 用于转换的MidiConvert对象 - data_cfg: ConvertConfig 对象 - 部分转换通用参数 + dist_path: str + 转换结果输出的目标路径 + progressbar_style: ProgressBarStyle 对象 + 进度条对象 scoreboard_name: str 我的世界的计分板名称 auto_reset: bool @@ -60,12 +63,12 @@ def to_addon_pack_in_score( ) # 当文件f夹{self.outputPath}/temp/functions存在时清空其下所有项目,然后创建 - if os.path.exists(f"{data_cfg.dist_path}/temp/functions/"): - shutil.rmtree(f"{data_cfg.dist_path}/temp/functions/") - os.makedirs(f"{data_cfg.dist_path}/temp/functions/mscplay") + if os.path.exists(f"{dist_path}/temp/functions/"): + shutil.rmtree(f"{dist_path}/temp/functions/") + os.makedirs(f"{dist_path}/temp/functions/mscplay") # 写入manifest.json - with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f: + with open(f"{dist_path}/temp/manifest.json", "w", encoding="utf-8") as f: json.dump( behavior_mcpack_manifest( pack_description=f"{midi_cvt.music_name} 音乐播放包,MCFUNCTION(MCPACK) 计分播放器 - 由 音·创 生成", @@ -78,18 +81,18 @@ def to_addon_pack_in_score( # 写入stop.mcfunction with open( - f"{data_cfg.dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8" + f"{dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8" ) as f: f.write("scoreboard players reset @a {}".format(scoreboard_name)) # 将命令列表写入文件 index_file = open( - f"{data_cfg.dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8" + f"{dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8" ) for i in range(len(cmdlist)): index_file.write(f"function mscplay/track{i + 1}\n") with open( - f"{data_cfg.dist_path}/temp/functions/mscplay/track{i + 1}.mcfunction", + f"{dist_path}/temp/functions/mscplay/track{i + 1}.mcfunction", "w", encoding="utf-8", ) as f: @@ -113,13 +116,13 @@ def to_addon_pack_in_score( if auto_reset else "" ), - f"function mscplay/progressShow\n" if data_cfg.progressbar_style else "", + f"function mscplay/progressShow\n" if progressbar_style else "", ) ) - if data_cfg.progressbar_style: + if progressbar_style: with open( - f"{data_cfg.dist_path}/temp/functions/mscplay/progressShow.mcfunction", + f"{dist_path}/temp/functions/mscplay/progressShow.mcfunction", "w", encoding="utf-8", ) as f: @@ -128,7 +131,7 @@ def to_addon_pack_in_score( [ single_cmd.cmd for single_cmd in midi_cvt.form_progress_bar( - maxscore, scoreboard_name, data_cfg.progressbar_style + maxscore, scoreboard_name, progressbar_style ) ] ) @@ -136,21 +139,22 @@ def to_addon_pack_in_score( index_file.close() - if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack"): - os.remove(f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack") + if os.path.exists(f"{dist_path}/{midi_cvt.music_name}.mcpack"): + os.remove(f"{dist_path}/{midi_cvt.music_name}.mcpack") compress_zipfile( - f"{data_cfg.dist_path}/temp/", - f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack", + f"{dist_path}/temp/", + f"{dist_path}/{midi_cvt.music_name}.mcpack", ) - shutil.rmtree(f"{data_cfg.dist_path}/temp/") + shutil.rmtree(f"{dist_path}/temp/") return maxlen, maxscore def to_addon_pack_in_delay( midi_cvt: MidiConvert, - data_cfg: ConvertConfig, + dist_path: str, + progressbar_style: Optional[ProgressBarStyle], player: str = "@a", max_height: int = 64, ) -> Tuple[int, int]: @@ -161,8 +165,10 @@ def to_addon_pack_in_delay( ---------- midi_cvt: MidiConvert 对象 用于转换的MidiConvert对象 - data_cfg: ConvertConfig 对象 - 部分转换通用参数 + dist_path: str + 转换结果输出的目标路径 + progressbar_style: ProgressBarStyle 对象 + 进度条对象 player: str 玩家选择器,默认为`@a` max_height: int @@ -183,17 +189,17 @@ def to_addon_pack_in_delay( player_selector=player, )[:2] - if not os.path.exists(data_cfg.dist_path): - os.makedirs(data_cfg.dist_path) + if not os.path.exists(dist_path): + os.makedirs(dist_path) # 当文件f夹{self.outputPath}/temp/存在时清空其下所有项目,然后创建 - if os.path.exists(f"{data_cfg.dist_path}/temp/"): - shutil.rmtree(f"{data_cfg.dist_path}/temp/") - os.makedirs(f"{data_cfg.dist_path}/temp/functions/") - os.makedirs(f"{data_cfg.dist_path}/temp/structures/") + if os.path.exists(f"{dist_path}/temp/"): + shutil.rmtree(f"{dist_path}/temp/") + os.makedirs(f"{dist_path}/temp/functions/") + os.makedirs(f"{dist_path}/temp/structures/") # 写入manifest.json - with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f: + with open(f"{dist_path}/temp/manifest.json", "w", encoding="utf-8") as f: json.dump( behavior_mcpack_manifest( pack_description=f"{midi_cvt.music_name} 音乐播放包,MCSTRUCTURE(MCPACK) 延迟播放器 - 由 音·创 生成", @@ -206,7 +212,7 @@ def to_addon_pack_in_delay( # 写入stop.mcfunction with open( - f"{data_cfg.dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8" + f"{dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8" ) as f: f.write( "gamerule commandblocksenabled false\ngamerule commandblocksenabled true" @@ -214,7 +220,7 @@ def to_addon_pack_in_delay( # 将命令列表写入文件 index_file = open( - f"{data_cfg.dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8" + f"{dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8" ) struct, size, end_pos = commands_to_structure( @@ -225,7 +231,7 @@ def to_addon_pack_in_delay( with open( os.path.abspath( os.path.join( - data_cfg.dist_path, + dist_path, "temp/structures/", f"{midi_cvt.music_name}_main.mcstructure", ) @@ -236,7 +242,7 @@ def to_addon_pack_in_delay( del struct - if data_cfg.progressbar_style: + if progressbar_style: scb_name = midi_cvt.music_name[:3] + "Pgb" index_file.write("scoreboard objectives add {0} dummy {0}计\n".format(scb_name)) @@ -257,7 +263,7 @@ def to_addon_pack_in_delay( with open( os.path.abspath( os.path.join( - data_cfg.dist_path, + dist_path, "temp/structures/", f"{midi_cvt.music_name}_start.mcstructure", ) @@ -269,7 +275,7 @@ def to_addon_pack_in_delay( index_file.write(f"structure load {midi_cvt.music_name}_start ~ ~ ~1\n") pgb_struct, pgbSize, pgbNowPos = commands_to_structure( - midi_cvt.form_progress_bar(max_delay, scb_name, data_cfg.progressbar_style), + midi_cvt.form_progress_bar(max_delay, scb_name, progressbar_style), max_height - 1, compability_version_=compability_ver, ) @@ -277,7 +283,7 @@ def to_addon_pack_in_delay( with open( os.path.abspath( os.path.join( - data_cfg.dist_path, + dist_path, "temp/structures/", f"{midi_cvt.music_name}_pgb.mcstructure", ) @@ -307,7 +313,7 @@ def to_addon_pack_in_delay( with open( os.path.abspath( os.path.join( - data_cfg.dist_path, + dist_path, "temp/structures/", f"{midi_cvt.music_name}_reset.mcstructure", ) @@ -331,21 +337,22 @@ def to_addon_pack_in_delay( index_file.close() - if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack"): - os.remove(f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack") + if os.path.exists(f"{dist_path}/{midi_cvt.music_name}.mcpack"): + os.remove(f"{dist_path}/{midi_cvt.music_name}.mcpack") compress_zipfile( - f"{data_cfg.dist_path}/temp/", - f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack", + f"{dist_path}/temp/", + f"{dist_path}/{midi_cvt.music_name}.mcpack", ) - shutil.rmtree(f"{data_cfg.dist_path}/temp/") + shutil.rmtree(f"{dist_path}/temp/") return len(command_list), max_delay def to_addon_pack_in_repeater( midi_cvt: MidiConvert, - data_cfg: ConvertConfig, + dist_path: str, + progressbar_style: Optional[ProgressBarStyle], player: str = "@a", max_height: int = 65, ) -> Tuple[int, int]: @@ -356,8 +363,10 @@ def to_addon_pack_in_repeater( ---------- midi_cvt: MidiConvert 对象 用于转换的MidiConvert对象 - data_cfg: ConvertConfig 对象 - 部分转换通用参数 + dist_path: str + 转换结果输出的目标路径 + progressbar_style: ProgressBarStyle 对象 + 进度条对象 player: str 玩家选择器,默认为`@a` max_height: int @@ -378,17 +387,17 @@ def to_addon_pack_in_repeater( player_selector=player, ) - if not os.path.exists(data_cfg.dist_path): - os.makedirs(data_cfg.dist_path) + if not os.path.exists(dist_path): + os.makedirs(dist_path) # 当文件f夹{self.outputPath}/temp/存在时清空其下所有项目,然后创建 - if os.path.exists(f"{data_cfg.dist_path}/temp/"): - shutil.rmtree(f"{data_cfg.dist_path}/temp/") - os.makedirs(f"{data_cfg.dist_path}/temp/functions/") - os.makedirs(f"{data_cfg.dist_path}/temp/structures/") + if os.path.exists(f"{dist_path}/temp/"): + shutil.rmtree(f"{dist_path}/temp/") + os.makedirs(f"{dist_path}/temp/functions/") + os.makedirs(f"{dist_path}/temp/structures/") # 写入manifest.json - with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f: + with open(f"{dist_path}/temp/manifest.json", "w", encoding="utf-8") as f: json.dump( behavior_mcpack_manifest( pack_description=f"{midi_cvt.music_name} 音乐播放包,MCSTRUCTURE(MCPACK) 中继器播放器 - 由 音·创 生成", @@ -401,7 +410,7 @@ def to_addon_pack_in_repeater( # 写入stop.mcfunction with open( - f"{data_cfg.dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8" + f"{dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8" ) as f: f.write( "gamerule commandblocksenabled false\ngamerule commandblocksenabled true" @@ -409,7 +418,7 @@ def to_addon_pack_in_repeater( # 将命令列表写入文件 index_file = open( - f"{data_cfg.dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8" + f"{dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8" ) struct, size, end_pos = commands_to_redstone_delay_structure( @@ -421,7 +430,7 @@ def to_addon_pack_in_repeater( with open( os.path.abspath( os.path.join( - data_cfg.dist_path, + dist_path, "temp/structures/", f"{midi_cvt.music_name}_main.mcstructure", ) @@ -432,7 +441,7 @@ def to_addon_pack_in_repeater( del struct - if data_cfg.progressbar_style: + if progressbar_style: scb_name = midi_cvt.music_name[:3] + "Pgb" index_file.write("scoreboard objectives add {0} dummy {0}计\n".format(scb_name)) @@ -453,7 +462,7 @@ def to_addon_pack_in_repeater( with open( os.path.abspath( os.path.join( - data_cfg.dist_path, + dist_path, "temp/structures/", f"{midi_cvt.music_name}_start.mcstructure", ) @@ -465,7 +474,7 @@ def to_addon_pack_in_repeater( index_file.write(f"structure load {midi_cvt.music_name}_start ~ ~ ~1\n") pgb_struct, pgbSize, pgbNowPos = commands_to_structure( - midi_cvt.form_progress_bar(max_delay, scb_name, data_cfg.progressbar_style), + midi_cvt.form_progress_bar(max_delay, scb_name, progressbar_style), max_height - 1, compability_version_=compability_ver, ) @@ -473,7 +482,7 @@ def to_addon_pack_in_repeater( with open( os.path.abspath( os.path.join( - data_cfg.dist_path, + dist_path, "temp/structures/", f"{midi_cvt.music_name}_pgb.mcstructure", ) @@ -503,7 +512,7 @@ def to_addon_pack_in_repeater( with open( os.path.abspath( os.path.join( - data_cfg.dist_path, + dist_path, "temp/structures/", f"{midi_cvt.music_name}_reset.mcstructure", ) @@ -527,13 +536,128 @@ def to_addon_pack_in_repeater( index_file.close() - if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack"): - os.remove(f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack") + if os.path.exists(f"{dist_path}/{midi_cvt.music_name}.mcpack"): + os.remove(f"{dist_path}/{midi_cvt.music_name}.mcpack") compress_zipfile( - f"{data_cfg.dist_path}/temp/", - f"{data_cfg.dist_path}/{midi_cvt.music_name}.mcpack", + f"{dist_path}/temp/", + f"{dist_path}/{midi_cvt.music_name}.mcpack", ) - shutil.rmtree(f"{data_cfg.dist_path}/temp/") + shutil.rmtree(f"{dist_path}/temp/") return len(command_list), max_delay + + +def to_addon_pack_in_repeater_divided_by_instrument( + midi_cvt: MidiConvert, + dist_path: str, + player: str = "@a", + max_height: int = 65, + base_block: str = "concrete", +) -> Tuple[int, int]: + """ + 将midi以中继器播放器形式转换为mcstructure结构文件后打包成附加包,并在附加包中生成相应地导入函数 + + Parameters + ---------- + midi_cvt: MidiConvert 对象 + 用于转换的MidiConvert对象 + dist_path: str + 转换结果输出的目标路径 + player: str + 玩家选择器,默认为`@a` + max_height: int + 生成结构最大高度 + + Returns + ------- + tuple[int指令数量, int音乐总延迟] + """ + + compability_ver = ( + COMPABILITY_VERSION_117 + if midi_cvt.enable_old_exe_format + else COMPABILITY_VERSION_119 + ) + + if not os.path.exists(dist_path): + os.makedirs(dist_path) + + # 当文件f夹{self.outputPath}/temp/存在时清空其下所有项目,然后创建 + if os.path.exists(f"{dist_path}/temp/"): + shutil.rmtree(f"{dist_path}/temp/") + os.makedirs(f"{dist_path}/temp/functions/") + os.makedirs(f"{dist_path}/temp/structures/") + + # 写入manifest.json + with open(f"{dist_path}/temp/manifest.json", "w", encoding="utf-8") as f: + json.dump( + behavior_mcpack_manifest( + pack_description=f"{midi_cvt.music_name} 音乐播放包,MCSTRUCTURE(MCPACK) 中继器播放器 - 由 音·创 生成", + pack_name=midi_cvt.music_name + "播放", + modules_description=f"无 - 由 音·创 生成", + ), + fp=f, + indent=4, + ) + + # 写入stop.mcfunction + with open( + f"{dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8" + ) as f: + f.write( + "gamerule commandblocksenabled false\ngamerule commandblocksenabled true" + ) + + # 将命令列表写入文件 + index_file = open( + f"{dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8" + ) + + cmd_dict, max_delay, max_multiple_cmd_count = ( + midi_cvt.to_command_list_in_delay_devided_by_instrument( + player_selector=player, + ) + ) + + base_height = 0 + + for inst, cmd_list in cmd_dict.items(): + struct, size, end_pos = commands_to_redstone_delay_structure( + cmd_list, + max_delay, + max_multiple_cmd_count[inst], + base_block, + "z+", + compability_version_=compability_ver, + ) + + bkn = "{}_{}".format(midi_cvt.music_name, inst.replace(".", "-")) + + with open( + os.path.abspath( + os.path.join( + dist_path, + "temp/structures/", + "{}_main.mcstructure".format(bkn), + ) + ), + "wb+", + ) as f: + struct.dump(f) + + index_file.write("structure load {}_main ~ ~{} ~3\n".format(bkn, base_height)) + base_height += 2 + size[1] + + index_file.close() + + if os.path.exists(f"{dist_path}/{midi_cvt.music_name}.mcpack"): + os.remove(f"{dist_path}/{midi_cvt.music_name}.mcpack") + compress_zipfile( + f"{dist_path}/temp/", + f"{dist_path}/{midi_cvt.music_name}.mcpack", + ) + + shutil.rmtree(f"{dist_path}/temp/") + + return midi_cvt.total_note_count, max_delay diff --git a/Musicreater/plugin/bdxfile/main.py b/Musicreater/plugin/bdxfile/main.py index 07f6bec..7626c5e 100644 --- a/Musicreater/plugin/bdxfile/main.py +++ b/Musicreater/plugin/bdxfile/main.py @@ -17,7 +17,8 @@ import os import brotli from ...main import MidiConvert -from ...subclass import MineCommand +from ...subclass import MineCommand, ProgressBarStyle +from ...types import Optional from ..bdx import ( bdx_move, commands_to_BDX_bytes, @@ -26,12 +27,12 @@ from ..bdx import ( y, z, ) -from ..main import ConvertConfig def to_BDX_file_in_score( midi_cvt: MidiConvert, - data_cfg: ConvertConfig, + dist_path: str, + progressbar_style: Optional[ProgressBarStyle], scoreboard_name: str = "mscplay", auto_reset: bool = False, author: str = "Eilles", @@ -44,8 +45,10 @@ def to_BDX_file_in_score( ---------- midi_cvt: MidiConvert 对象 用于转换的MidiConvert对象 - data_cfg: ConvertConfig 对象 - 部分转换通用参数 + dist_path: str + 转换结果输出的目标路径 + progressbar_style: ProgressBarStyle 对象 + 进度条对象 scoreboard_name: str 我的世界的计分板名称 auto_reset: bool @@ -64,11 +67,11 @@ def to_BDX_file_in_score( scoreboard_name=scoreboard_name, ) - if not os.path.exists(data_cfg.dist_path): - os.makedirs(data_cfg.dist_path) + if not os.path.exists(dist_path): + os.makedirs(dist_path) with open( - os.path.abspath(os.path.join(data_cfg.dist_path, f"{midi_cvt.music_name}.bdx")), + os.path.abspath(os.path.join(dist_path, f"{midi_cvt.music_name}.bdx")), "w+", ) as f: f.write("BD@") @@ -97,11 +100,9 @@ def to_BDX_file_in_score( max_height - 1, ) - if data_cfg.progressbar_style: + if progressbar_style: pgbBytes, pgbSize, pgbNowPos = commands_to_BDX_bytes( - midi_cvt.form_progress_bar( - max_score, scoreboard_name, data_cfg.progressbar_style - ), + midi_cvt.form_progress_bar(max_score, scoreboard_name, progressbar_style), max_height - 1, ) _bytes += pgbBytes @@ -116,7 +117,7 @@ def to_BDX_file_in_score( _bytes += cmdBytes with open( - os.path.abspath(os.path.join(data_cfg.dist_path, f"{midi_cvt.music_name}.bdx")), + os.path.abspath(os.path.join(dist_path, f"{midi_cvt.music_name}.bdx")), "ab+", ) as f: f.write(brotli.compress(_bytes + b"XE")) @@ -126,7 +127,8 @@ def to_BDX_file_in_score( def to_BDX_file_in_delay( midi_cvt: MidiConvert, - data_cfg: ConvertConfig, + dist_path: str, + progressbar_style: Optional[ProgressBarStyle], player: str = "@a", author: str = "Eilles", max_height: int = 64, @@ -138,8 +140,10 @@ def to_BDX_file_in_delay( ---------- midi_cvt: MidiConvert 对象 用于转换的MidiConvert对象 - data_cfg: ConvertConfig 对象 - 部分转换通用参数 + dist_path: str + 转换结果输出的目标路径 + progressbar_style: ProgressBarStyle 对象 + 进度条对象 player: str 玩家选择器,默认为`@a` author: str @@ -156,11 +160,11 @@ def to_BDX_file_in_delay( player_selector=player, )[:2] - if not os.path.exists(data_cfg.dist_path): - os.makedirs(data_cfg.dist_path) + if not os.path.exists(dist_path): + os.makedirs(dist_path) with open( - os.path.abspath(os.path.join(data_cfg.dist_path, f"{midi_cvt.music_name}.bdx")), + os.path.abspath(os.path.join(dist_path, f"{midi_cvt.music_name}.bdx")), "w+", ) as f: f.write("BD@") @@ -171,7 +175,7 @@ def to_BDX_file_in_delay( cmdBytes, size, finalPos = commands_to_BDX_bytes(cmdlist, max_height - 1) - if data_cfg.progressbar_style: + if progressbar_style: scb_name = midi_cvt.music_name[:3] + "Pgb" _bytes += form_command_block_in_BDX_bytes( r"scoreboard objectives add {} dummy {}计".replace(r"{}", scb_name), @@ -187,7 +191,7 @@ def to_BDX_file_in_delay( ) _bytes += bdx_move(y, 1) pgbBytes, pgbSize, pgbNowPos = commands_to_BDX_bytes( - midi_cvt.form_progress_bar(max_delay, scb_name, data_cfg.progressbar_style), + midi_cvt.form_progress_bar(max_delay, scb_name, progressbar_style), max_height - 1, ) _bytes += pgbBytes @@ -208,7 +212,7 @@ def to_BDX_file_in_delay( _bytes += cmdBytes with open( - os.path.abspath(os.path.join(data_cfg.dist_path, f"{midi_cvt.music_name}.bdx")), + os.path.abspath(os.path.join(dist_path, f"{midi_cvt.music_name}.bdx")), "ab+", ) as f: f.write(brotli.compress(_bytes + b"XE")) diff --git a/Musicreater/plugin/main.py b/Musicreater/plugin/main.py index 154af62..b6fa83a 100644 --- a/Musicreater/plugin/main.py +++ b/Musicreater/plugin/main.py @@ -16,77 +16,77 @@ Terms & Conditions: License.md in the root directory # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md -from dataclasses import dataclass -from typing import Literal, Tuple, Union +# from dataclasses import dataclass +# from typing import Literal, Tuple, Union -from ..subclass import DEFAULT_PROGRESSBAR_STYLE, ProgressBarStyle +# from ..subclass import DEFAULT_PROGRESSBAR_STYLE, ProgressBarStyle -@dataclass(init=False) -class ConvertConfig: # 必定要改 - """ - 转换通用设置存储类 - """ +# @dataclass(init=False) +# class ConvertConfig: # 必定要改 +# """ +# 转换通用设置存储类 +# """ - progressbar_style: Union[ProgressBarStyle, None] - """进度条样式""" +# progressbar_style: Union[ProgressBarStyle, None] +# """进度条样式""" - dist_path: str - """输出目录""" +# dist_path: str +# """输出目录""" - def __init__( - self, - output_path: str, - progressbar: Union[bool, Tuple[str, Tuple[str, str]], ProgressBarStyle] = True, - ignore_progressbar_param_error: bool = False, - ): - """ - 将已经转换好的数据内容指令载入MC可读格式 +# def __init__( +# self, +# output_path: str, +# progressbar: Union[bool, Tuple[str, Tuple[str, str]], ProgressBarStyle] = True, +# ignore_progressbar_param_error: bool = False, +# ): +# """ +# 将已经转换好的数据内容指令载入MC可读格式 - Parameters - ---------- - output_path: str - 生成内容的输出目录 - volume: float - 音量比率,范围为(0,1],其原理为在距离玩家 (1 / volume -1) 的地方播放音频 - speed: float - 速度倍率,注意:这里的速度指的是播放速度倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed - progressbar: bool|tuple[str, Tuple[str,]] - 进度条,当此参数为 `True` 时使用默认进度条,为其他的**值为真**的参数时识别为进度条自定义参数,为其他**值为假**的时候不生成进度条 +# Parameters +# ---------- +# output_path: str +# 生成内容的输出目录 +# volume: float +# 音量比率,范围为(0,1],其原理为在距离玩家 (1 / volume -1) 的地方播放音频 +# speed: float +# 速度倍率,注意:这里的速度指的是播放速度倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed +# progressbar: bool|tuple[str, Tuple[str,]] +# 进度条,当此参数为 `True` 时使用默认进度条,为其他的**值为真**的参数时识别为进度条自定义参数,为其他**值为假**的时候不生成进度条 - """ +# """ - self.dist_path = output_path - """输出目录""" +# self.dist_path = output_path +# """输出目录""" - if progressbar: - # 此处是对于仅有 True 的参数和自定义参数的判断 - # 改这一段没🐎 - if progressbar is True: - self.progressbar_style = DEFAULT_PROGRESSBAR_STYLE - """进度条样式""" - return - elif isinstance(progressbar, ProgressBarStyle): - self.progressbar_style = progressbar - """进度条样式""" - return - elif isinstance(progressbar, tuple): - if isinstance(progressbar[0], str) and isinstance( - progressbar[1], tuple - ): - if isinstance(progressbar[1][0], str) and isinstance( - progressbar[1][1], str - ): - self.progressbar_style = ProgressBarStyle( - progressbar[0], progressbar[1][0], progressbar[1][1] - ) - return - if not ignore_progressbar_param_error: - raise TypeError( - "参数 {} 的类型 {} 与所需类型 Union[bool, Tuple[str, Tuple[str, str]], ProgressBarStyle] 不符。".format( - progressbar, type(progressbar) - ) - ) +# if progressbar: +# # 此处是对于仅有 True 的参数和自定义参数的判断 +# # 改这一段没🐎 +# if progressbar is True: +# self.progressbar_style = DEFAULT_PROGRESSBAR_STYLE +# """进度条样式""" +# return +# elif isinstance(progressbar, ProgressBarStyle): +# self.progressbar_style = progressbar +# """进度条样式""" +# return +# elif isinstance(progressbar, tuple): +# if isinstance(progressbar[0], str) and isinstance( +# progressbar[1], tuple +# ): +# if isinstance(progressbar[1][0], str) and isinstance( +# progressbar[1][1], str +# ): +# self.progressbar_style = ProgressBarStyle( +# progressbar[0], progressbar[1][0], progressbar[1][1] +# ) +# return +# if not ignore_progressbar_param_error: +# raise TypeError( +# "参数 {} 的类型 {} 与所需类型 Union[bool, Tuple[str, Tuple[str, str]], ProgressBarStyle] 不符。".format( +# progressbar, type(progressbar) +# ) +# ) - self.progressbar_style = None - """进度条样式组""" +# self.progressbar_style = None +# """进度条样式组""" diff --git a/Musicreater/plugin/mcstructfile/__init__.py b/Musicreater/plugin/mcstructfile/__init__.py index 4181be5..ad779a9 100644 --- a/Musicreater/plugin/mcstructfile/__init__.py +++ b/Musicreater/plugin/mcstructfile/__init__.py @@ -18,6 +18,7 @@ __all__ = [ "to_mcstructure_file_in_delay", "to_mcstructure_file_in_repeater", "to_mcstructure_file_in_score", + "to_mcstructure_files_in_repeater_divided_by_instruments", ] __author__ = (("金羿", "Eilles Wan"),) @@ -25,4 +26,5 @@ from .main import ( to_mcstructure_file_in_delay, to_mcstructure_file_in_repeater, to_mcstructure_file_in_score, + to_mcstructure_files_in_repeater_divided_by_instruments, ) diff --git a/Musicreater/plugin/mcstructfile/main.py b/Musicreater/plugin/mcstructfile/main.py index 37ce215..d378562 100644 --- a/Musicreater/plugin/mcstructfile/main.py +++ b/Musicreater/plugin/mcstructfile/main.py @@ -14,9 +14,7 @@ Terms & Conditions: License.md in the root directory import os from typing import Literal -# from ...exceptions import CommandFormatError from ...main import MidiConvert -from ..main import ConvertConfig from ...subclass import MineCommand from ..mcstructure import ( COMPABILITY_VERSION_117, @@ -28,7 +26,7 @@ from ..mcstructure import ( def to_mcstructure_file_in_delay( midi_cvt: MidiConvert, - data_cfg: ConvertConfig, + dist_path: str, player: str = "@a", max_height: int = 64, ): @@ -39,8 +37,8 @@ def to_mcstructure_file_in_delay( ---------- midi_cvt: MidiConvert 对象 用于转换的MidiConvert对象 - data_cfg: ConvertConfig 对象 - 部分转换通用参数 + dist_path: str + 转换结果输出的目标路径 player: str 玩家选择器,默认为`@a` max_height: int @@ -61,17 +59,15 @@ def to_mcstructure_file_in_delay( player_selector=player, )[:2] - if not os.path.exists(data_cfg.dist_path): - os.makedirs(data_cfg.dist_path) + if not os.path.exists(dist_path): + os.makedirs(dist_path) struct, size, end_pos = commands_to_structure( cmd_list, max_height - 1, compability_version_=compability_ver ) with open( - os.path.abspath( - os.path.join(data_cfg.dist_path, f"{midi_cvt.music_name}.mcstructure") - ), + os.path.abspath(os.path.join(dist_path, f"{midi_cvt.music_name}.mcstructure")), "wb+", ) as f: struct.dump(f) @@ -81,7 +77,7 @@ def to_mcstructure_file_in_delay( def to_mcstructure_file_in_score( midi_cvt: MidiConvert, - data_cfg: ConvertConfig, + dist_path: str, scoreboard_name: str = "mscplay", auto_reset: bool = False, max_height: int = 64, @@ -93,8 +89,8 @@ def to_mcstructure_file_in_score( ---------- midi_cvt: MidiConvert 对象 用于转换的MidiConvert对象 - data_cfg: ConvertConfig 对象 - 部分转换通用参数 + dist_path: str + 转换结果输出的目标路径 scoreboard_name: str 我的世界的计分板名称 auto_reset: bool @@ -117,8 +113,8 @@ def to_mcstructure_file_in_score( scoreboard_name=scoreboard_name, ) - if not os.path.exists(data_cfg.dist_path): - os.makedirs(data_cfg.dist_path) + if not os.path.exists(dist_path): + os.makedirs(dist_path) struct, size, end_pos = commands_to_structure( midi_cvt.music_command_list @@ -142,9 +138,7 @@ def to_mcstructure_file_in_score( ) with open( - os.path.abspath( - os.path.join(data_cfg.dist_path, f"{midi_cvt.music_name}.mcstructure") - ), + os.path.abspath(os.path.join(dist_path, f"{midi_cvt.music_name}.mcstructure")), "wb+", ) as f: struct.dump(f) @@ -154,7 +148,7 @@ def to_mcstructure_file_in_score( def to_mcstructure_file_in_repeater( midi_cvt: MidiConvert, - data_cfg: ConvertConfig, + dist_path: str, player: str = "@a", axis_side: Literal["z+", "z-", "Z+", "Z-", "x+", "x-", "X+", "X-"] = "z+", basement_block: str = "concrete", @@ -166,8 +160,8 @@ def to_mcstructure_file_in_repeater( ---------- midi_cvt: MidiConvert 对象 用于转换的MidiConvert对象 - data_cfg: ConvertConfig 对象 - 部分转换通用参数 + dist_path: str + 转换结果输出的目标路径 player: str 玩家选择器,默认为`@a` axis_side: Literal["z+","z-","Z+","Z-","x+","x-","X+","X-"] @@ -190,8 +184,8 @@ def to_mcstructure_file_in_repeater( player_selector=player, ) - if not os.path.exists(data_cfg.dist_path): - os.makedirs(data_cfg.dist_path) + if not os.path.exists(dist_path): + os.makedirs(dist_path) struct, size, end_pos = commands_to_redstone_delay_structure( cmd_list, @@ -203,11 +197,78 @@ def to_mcstructure_file_in_repeater( ) with open( - os.path.abspath( - os.path.join(data_cfg.dist_path, f"{midi_cvt.music_name}.mcstructure") - ), + os.path.abspath(os.path.join(dist_path, f"{midi_cvt.music_name}.mcstructure")), "wb+", ) as f: struct.dump(f) return size, max_delay + + +def to_mcstructure_files_in_repeater_divided_by_instruments( + midi_cvt: MidiConvert, + dist_path: str, + player: str = "@a", + axis_side: Literal["z+", "z-", "Z+", "Z-", "x+", "x-", "X+", "X-"] = "z+", + basement_block: str = "concrete", +): + """ + 将midi以延迟播放器形式转换为mcstructure结构文件 + + Parameters + ---------- + midi_cvt: MidiConvert 对象 + 用于转换的MidiConvert对象 + dist_path: str + 转换结果输出的目标路径 + player: str + 玩家选择器,默认为`@a` + axis_side: Literal["z+","z-","Z+","Z-","x+","x-","X+","X-"] + 生成结构的延展方向 + basement_block: str + 结构的基底方块 + + Returns + ------- + int音乐总延迟 + """ + + compability_ver = ( + COMPABILITY_VERSION_117 + if midi_cvt.enable_old_exe_format + else COMPABILITY_VERSION_119 + ) + + cmd_dict, max_delay, max_multiple_cmd_count = ( + midi_cvt.to_command_list_in_delay_devided_by_instrument( + player_selector=player, + ) + ) + + if not os.path.exists(dist_path): + os.makedirs(dist_path) + + for inst, cmd_list in cmd_dict.items(): + struct, size, end_pos = commands_to_redstone_delay_structure( + cmd_list, + max_delay, + max_multiple_cmd_count[inst], + basement_block, + axis_side, + compability_version_=compability_ver, + ) + + with open( + os.path.abspath( + os.path.join( + dist_path, + "{}_{}.mcstructure".format( + midi_cvt.music_name, inst.replace(".", "-") + ), + ) + ), + "wb+", + ) as f: + struct.dump(f) + + return max_delay diff --git a/Musicreater/subclass.py b/Musicreater/subclass.py index 2baa81f..d6e5892 100644 --- a/Musicreater/subclass.py +++ b/Musicreater/subclass.py @@ -118,6 +118,7 @@ class MineNote: "Percussive" if self.percussive else "", self.sound_name, "" if self.percussive else "NotePitch = {}, ".format(self.note_pitch), + self.velocity, self.start_tick, self.duration, ", Track = {}".format(self.track_no) if is_track else "", @@ -554,6 +555,25 @@ class ProgressBarStyle: self.to_play_style = to_play_s self.played_style = played_s + @classmethod + def from_tuple(cls, tuplized_style: Tuple[str, Tuple[str, str]]): + """自旧版进度条元组表示法读入数据(已不建议使用)""" + if isinstance(tuplized_style, tuple): + if isinstance(tuplized_style[0], str) and isinstance( + tuplized_style[1], tuple + ): + if isinstance(tuplized_style[1][0], str) and isinstance( + tuplized_style[1][1], str + ): + return cls( + tuplized_style[0], tuplized_style[1][0], tuplized_style[1][1] + ) + raise ValueError( + "元组表示的进度条样式组 {} 格式错误,已不建议使用此功能,请尽快更换。".format( + tuplized_style + ) + ) + def set_base_style(self, value: str): """设置基础样式""" self.base_style = value diff --git a/Musicreater/utils.py b/Musicreater/utils.py index 053697c..ee11a54 100644 --- a/Musicreater/utils.py +++ b/Musicreater/utils.py @@ -18,7 +18,12 @@ Terms & Conditions: License.md in the root directory import math import random -from .constants import MC_INSTRUMENT_BLOCKS_TABLE, MM_INSTRUMENT_RANGE_TABLE +from .constants import ( + MC_INSTRUMENT_BLOCKS_TABLE, + MM_INSTRUMENT_DEVIATION_TABLE, + MC_PITCHED_INSTRUMENT_LIST, + MM_INSTRUMENT_RANGE_TABLE, +) from .subclass import SingleNote, MineNote from .types import ( @@ -61,7 +66,7 @@ def inst_to_sould_with_deviation( ) -> Tuple[str, int]: """ 返回midi的乐器ID对应的我的世界乐器名,对于音域转换算法,如下: - 2**( ( msg.note - 66 ) / 12 ) 即为MC的音高 + 2**( ( msg.note - 60 - X ) / 12 ) 即为MC的音高 Parameters ---------- @@ -74,16 +79,17 @@ def inst_to_sould_with_deviation( ------- tuple(str我的世界乐器名, int转换算法中的X) """ - return ( - reference_table.get( - instrumentID, - default_instrument, - ), - 6, + sound_id = midi_inst_to_mc_sound( + instrumentID=instrumentID, + reference_table=reference_table, + default_instrument=default_instrument, + ) + return sound_id, MM_INSTRUMENT_DEVIATION_TABLE.get( + sound_id, + MM_INSTRUMENT_DEVIATION_TABLE.get( + default_instrument, 6 if sound_id in MC_PITCHED_INSTRUMENT_LIST else -1 + ), ) - - # 明明已经走了 - # 凭什么还要在我心里留下缠绵缱绻 def midi_inst_to_mc_sound( @@ -179,7 +185,18 @@ def minenote_to_command_paramaters( ( None if note_.percussive - else 2 ** ((note_.note_pitch - 66 + pitch_deviation) / 12) + else ( + 2 + ** ( + ( + note_.note_pitch + - 60 + - MM_INSTRUMENT_DEVIATION_TABLE.get(note_.sound_name, 6) + + pitch_deviation + ) + / 12 + ) + ) ), ) @@ -205,7 +222,7 @@ def single_note_to_command_parameters( :return str[我的世界音符ID], Tuple[float,float,float]播放视角坐标, float[指令音量参数], float[指令音调参数] """ - mc_sound_ID = midi_inst_to_mc_sound( + mc_sound_ID, _X = inst_to_sould_with_deviation( note_.inst, reference_table, "note.bd" if note_.percussive else "note.flute", @@ -217,7 +234,7 @@ def single_note_to_command_parameters( mc_sound_ID, (0, mc_distance_volume, 0), note_.velocity / 127, - None if note_.percussive else 2 ** ((note_.pitch - 66 + deviation) / 12), + None if note_.percussive else 2 ** ((note_.pitch - 60 - _X + deviation) / 12), ) diff --git a/example.py b/example.py index fbc9a22..e3a2c20 100644 --- a/example.py +++ b/example.py @@ -19,7 +19,6 @@ Terms & Conditions: ./License.md import os import Musicreater -from Musicreater.plugin import ConvertConfig from Musicreater.plugin.addonpack import ( to_addon_pack_in_delay, to_addon_pack_in_repeater, @@ -160,7 +159,7 @@ print(f"正在处理 {midi_path} :") cvt_mid = Musicreater.MidiConvert.from_midi_file( midi_path, old_exe_format=False, min_volume=prompts[0], play_speed=prompts[1] ) -cvt_cfg = ConvertConfig(out_path, prompts[2]) + if fileFormat == 0: if playerFormat == 1: @@ -177,28 +176,62 @@ elif fileFormat == 2: elif playerFormat == 2: cvt_method = to_mcstructure_file_in_repeater +# 测试 + +# print(cvt_mid) + print( " 指令总长:{},最高延迟:{}".format( - *(cvt_method(cvt_mid, cvt_cfg, *prompts[3:])) # type: ignore + *( + cvt_method( + cvt_mid, + out_path, + Musicreater.DEFAULT_PROGRESSBAR_STYLE if prompts[2] else None, # type: ignore + *prompts[3:], + ) + ) ) if fileFormat == 0 else ( " 指令总长:{},最高延迟:{},结构大小{},终点坐标{}".format( *( - to_BDX_file_in_score(cvt_mid, cvt_cfg, *prompts[3:]) + to_BDX_file_in_score( + cvt_mid, + out_path, + Musicreater.DEFAULT_PROGRESSBAR_STYLE if prompts[2] else None, + *prompts[3:], + ) if playerFormat == 1 - else to_BDX_file_in_delay(cvt_mid, cvt_cfg, *prompts[3:]) + else to_BDX_file_in_delay( + cvt_mid, + out_path, + Musicreater.DEFAULT_PROGRESSBAR_STYLE if prompts[2] else None, + *prompts[3:], + ) ) ) if fileFormat == 1 else ( " 结构大小:{},延迟总数:{},指令数量:{}".format( - *(cvt_method(cvt_mid, cvt_cfg, *prompts[3:])) # type: ignore + *( + cvt_method( + cvt_mid, + out_path, + *prompts[3:], + ) + ) ) - if playerFormat == 2 + if playerFormat == 1 else " 结构大小:{},延迟总数:{}".format( - *(cvt_method(cvt_mid, cvt_cfg, *prompts[3:])) # type: ignore + *( + cvt_method( + cvt_mid, + out_path, + # Musicreater.DEFAULT_PROGRESSBAR_STYLE if prompts[2] else None, # type: ignore + *prompts[3:], + ) + ) ) ) ) diff --git a/example_futureFunction.py b/example_futureFunction.py index 460eff0..dffdbae 100644 --- a/example_futureFunction.py +++ b/example_futureFunction.py @@ -7,7 +7,8 @@ print( Musicreater.experiment.FutureMidiConvertM4.from_midi_file( input("midi路径:"), old_exe_format=False ), - Musicreater.plugin.ConvertConfig(input("输出路径:"),), + input("输出路径:"), + # Musicreater.plugin.ConvertConfig(input("输出路径:"),), max_height=32, ) ) diff --git a/example_singleConvert.py b/example_singleConvert.py new file mode 100644 index 0000000..795ea3c --- /dev/null +++ b/example_singleConvert.py @@ -0,0 +1,14 @@ +import Musicreater +import Musicreater.plugin +import Musicreater.plugin.addonpack + +print( + Musicreater.plugin.addonpack.to_addon_pack_in_repeater_divided_by_instrument( + Musicreater.MidiConvert.from_midi_file( + input("midi路径:"), old_exe_format=False + ), + input("输出路径:"), + # Musicreater.plugin.ConvertConfig(input("输出路径:"),), + # max_height=32, + ) +)