From 45e9adbbd1f8b60ab0710c5887b0c4f9e4bb195f Mon Sep 17 00:00:00 2001 From: EillesWan Date: Sun, 25 May 2025 04:20:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=A5=9E=E7=BE=BD=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE.md | 18 +- Musicreater/__init__.py | 6 +- Musicreater/experiment.py | 336 +++++++++++++++++++++- Musicreater/types.py | 11 +- Musicreater/utils.py | 63 ++++ Packer/MSCT_Packer.py | 10 +- README.md | 71 +++-- README_EN.md | 9 +- pyproject.toml | 2 + setup.py | 42 --- example_fsq_opera.py => test_fsq_opera.py | 4 +- test_future_kamires.py | 33 +++ example_msq_opera.py => test_msq_opera.py | 4 +- uv.lock | 13 +- 14 files changed, 506 insertions(+), 116 deletions(-) delete mode 100644 setup.py rename example_fsq_opera.py => test_fsq_opera.py (100%) create mode 100644 test_future_kamires.py rename example_msq_opera.py => test_msq_opera.py (100%) diff --git a/LICENSE.md b/LICENSE.md index 989c51d..94c54c8 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ -# 汉钰律许可协议 +# 汉钰律许可协议,第一版 -**总第一版 · 二〇二四年七月七日编** +**总第一版 第二次修订 · 二〇二四年七月七日编 二〇二五年四月二十六日修订** ## 一、重要须知 @@ -12,13 +12,13 @@ 4. 由于互联网服务、互联网内容的特殊性,若本协议以电子协议形式分发并签订,其依然有效。您一旦开始对本协议所授权之作品进行本协议所授权的行为,即视为您已经阅读、理解并同意并已经接受本协议的全部条款。 -5. 本协议的订立、履行、解释及争议的解决均**适用中华人民共和国法律并排除其他一切冲突法的适用**。_本协议订立于许可证最初的颁发者的地址。若其为自然人,则订立于该自然人户籍所在地;若为法人或非法人组织,则订立于其注册地_。本协议的订立各方应友好协商解决于协议所规定之行为的履行相关的争议;如协商不成,任何一方均可向合同签订地有管辖权的人民法院提起诉讼。 +5. 本协议的订立、履行、解释及争议的解决均**适用中华人民共和国法律并排除其他一切冲突法的适用**。_本协议订立于许可证最初的颁发者的地址。若颁发者为自然人,则订立于该自然人户籍所在地;若为法人或非法人组织,则订立于其注册地_。本协议的订立各方应友好协商解决于协议所规定之行为的履行相关的争议;如协商不成,任何一方均可向合同签订地有管辖权的人民法院提起诉讼。 6. 本协议的原本仅为现代汉语,书写于简体中文。若存在其他语言的翻译或其他同等语言但非简体中文文本的版本,应当无法律效力。 ## 二、术语定义 -1. “**许可证**”、“**协议**”(后文称“本协议”)是指根据本文档中所列举的全部术语、定义、条款、限制等文本,是本合同的简称称谓。本合同全称是“汉钰律许可协议”。 +1. “**许可证**”、“**协议**”(后文称“本协议”)是指根据本文档中所列举的全部术语、定义、条款、限制等文本,是本合同的简称称谓。本合同全称是 **“汉钰律许可协议,第一版”**。 2. “**协议颁发者**”(后文称“颁发者”)是将条款或协议应用于其拥有著作财产权的作品的民事主体,或由其指定从而拥有颁发者身份的民事主体。 @@ -28,13 +28,13 @@ 5. “**采用本协议的作品**”(后文称“此作品”)是指经颁发者授权而使用本协议进行授权的任何作品,该作品应在自然人可见处明确附加一个自然人可读的版权通知(可以参考文末附录中提供的示例);若在一个可分割的作品中,部分地采用本协议进行授权,则该部分应当视为一个独立的采用本协议的作品,该作品应当在自然人可见处明确附加一个自然人可读的范围限定和版权通知(同样可以参考文末附录中提供的示例)。 -6. “**贡献**”是指对作品进行的,意在提交给此作品颁发者以让著作权人包含在其作品中的任何修订或补充,该修订或补充同样属于一种作品。依据此定义,“提交”一词表示经由此作品颁发者所指定的形式,将其所进行的修改发送给此作品颁发者。该形式应当包括在此作品颁发者指定的平台内发送易于编辑的修改信息、在此作品颁发者指定的电子邮箱中发送易于编辑的修改信息、在此作品颁发者指定的源码控制系统或发布跟踪系统上提交的易于编辑的修改信息,但由著作权人以明显标注或指定为“非贡献”的活动除外。颁发者自己对作品进行的修改同样视作对作品的贡献。 +6. “**贡献**”是指对作品进行的,意在提交给此作品颁发者以让著作权人包含在其作品中的任何修订或补充,该修订或补充同样属于一种作品。依据此定义,**“提交”**一词表示经由此作品颁发者所指定的形式,将其所进行的修改发送给此作品颁发者。该形式应当包括在此作品颁发者指定的平台内发送易于编辑的修改信息、在此作品颁发者指定的电子邮箱中发送易于编辑的修改信息、在此作品颁发者指定的源码控制系统或发布跟踪系统上提交的易于编辑的修改信息,但由著作权人以明显标注或指定为“非贡献”的活动除外。颁发者自己对作品进行的修改同样视作对作品的贡献。 7. “**贡献者**”是指此作品颁发者接受的贡献的提交者,或包含在作品的贡献清单中的民事主体。贡献者在提交贡献并经此作品颁发者通过且该贡献已经被应用于此作品中后,该贡献者应当视为此作品的著作权人之一,但不应视为此作品非其贡献的部分的著作权人。一个作品的颁发者同样属于其贡献者。**请注意**,针对贡献者提交的贡献,该贡献者应被视为该贡献的协议颁发者,但不应视作本作品的颁发者。 8. “**用户**”、“**使用者**”是指行使本协议所授权之行为的民事主体。据此,贡献者亦属于用户。 -9. “**商业性使用**”、“**商用**”是指任何以谋取利益为目的的使用,包括但不限于以贩卖、出租的形式对作品进行使用;但若将该牟利活动明确指示为“捐赠”,且在牟利者进行本协议所授权的活动时不以捐赠数额为标准,则此种的获取利益的“捐赠”行为不属于商业性使用。 +9. “**商业性使用**”、“**商用**”是指任何以谋取利益为目的的使用,包括但不限于以贩卖、出租的形式对作品进行使用;但若将该获取利益之活动明确指示为“捐赠”,且在获利者在进行本协议所授权的活动时不以捐赠数额为标准而区别之,则此种的获取利益的“捐赠”行为不属于商业性使用。 ## 三、权利授予 @@ -82,7 +82,7 @@ 该通知的内容仅供信息提供,不应对许可证进行任何文字上的修改。用户可在其分发的作品中,在不构成修改本协议的前提下,在作品自身的声明通知或属性描述后或作为附录添加。 -6. 依据本款第3条,若用户二次分发此作品时,选择向作品的接收者提供收费的担保服务,则必须明确告知该接收者本协议全部内容与此作品原出处,并确保其知悉上述内容;但若用户在二次分发此作品是,不选择提供任何服务,则该用户不允许向作品的接收者收取任何费用,除非该用户获得了此作品颁发者的特殊许可、或该用户即为此作品颁发者本人。 +6. 依据本款第3条,若用户二次分发此作品时,选择向作品的接收者提供收费的担保服务,则必须明确告知该接收者本协议全部内容与此作品原出处,并确保其知悉上述内容;但若用户在二次分发此作品时,不选择提供任何服务,则该用户不允许向作品的接收者收取任何费用,除非该用户获得了此作品颁发者的特殊许可、或该用户即为此作品颁发者本人。 ## 五、提交贡献 @@ -94,7 +94,7 @@ ## 七、免责声明 -1. 若非因法律要求或经过了特殊准许,此作品在根据本协议“原样”提供的基础上,**不予提供任何形式的担保、任何明示、任何暗示或类似承诺**,此类包括但不限于担保此作品毫无缺陷、担保此作品适于贩卖、担保此作品适于特定目的、担保使用此作品绝不侵权。用户将自行承担因此作品的质量或性能问题而产生的全部风险。若此作品在任何方面欠妥,将由用户(而非任何贡献者、而非任何颁发者)承担所有必要的服务、维修或除错的任何成本。本免责声明本许可的重要组成部分。当且仅当遵守本免责声明时,本协议的其他条款中对本作品的使用授权方可生效。 +1. 若非因法律要求或经过了特殊准许,此作品在根据本协议“原样”提供的基础上,**不予提供任何形式的担保、任何明示、任何暗示或类似承诺**,此类包括但不限于担保此作品毫无缺陷、担保此作品适于贩卖、担保此作品适于特定目的、担保使用此作品绝不侵权。用户将自行承担因此作品的质量或性能问题而产生的全部风险。若此作品在任何方面欠妥,将由用户(而非任何贡献者、而非任何颁发者)承担所有必要的服务、维修或除错的任何成本。本免责声明是本许可的重要组成部分。当且仅当遵守本免责声明时,本协议的其他条款中对本作品的使用授权方可生效。 2. 无论是因何种原因,如果不是在法律规定的特殊情况(如,确为贡献者的故意或重大过失)下或者经过了特殊准许,即使贡献者事先已知发生损害的可能,在使用本作品时,用户产生的任何直接、间接、特殊、偶然或必然造成的损失(包括但不限于商誉损失、工作延误、计算机系统故障等),**均不由任一贡献者承担**。 @@ -116,7 +116,7 @@ 版权所有 © 〔年份〕 〔著作权人〕 〔或者:版权所有 (C) 〔年份〕 〔著作权人〕〕 -〔该作品〕根据 第一版 汉钰律许可协议(“本协议”)授权。 +〔该作品〕根据 汉钰律许可协议,第一版(“本协议”)授权。 任何人皆可从以下地址获得本协议副本:〔本协议副本所在地址〕。 若非因法律要求或经过了特殊准许,此作品在根据本协议“原样”提供的基础上,不予提供任何形式的担保、任何明示、任何暗示或类似承诺。也就是说,用户将自行承担因此作品的质量或性能问题而产生的全部风险。 详细的准许和限制条款请见原协议文本。 diff --git a/Musicreater/__init__.py b/Musicreater/__init__.py index 452e464..df9b37a 100644 --- a/Musicreater/__init__.py +++ b/Musicreater/__init__.py @@ -22,8 +22,8 @@ The Licensor of Musicreater("this project") is Eilles, bgArray. # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md -__version__ = "2.3.1" -__vername__ = "新增部分新可指定的默认值" +__version__ = "2.3.2" +__vername__ = "支持神羽资源包" __author__ = ( ("金羿", "Eilles"), ("诸葛亮与八卦阵", "bgArray"), @@ -49,6 +49,8 @@ __all__ = [ "MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE", "MM_TOUCH_PITCHED_INSTRUMENT_TABLE", "MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE", + "MM_DISLINK_PITCHED_INSTRUMENT_TABLE", + "MM_DISLINK_PERCUSSION_INSTRUMENT_TABLE", "MM_NBS_PITCHED_INSTRUMENT_TABLE", "MM_NBS_PERCUSSION_INSTRUMENT_TABLE", # 操作性函数 diff --git a/Musicreater/experiment.py b/Musicreater/experiment.py index dda37b1..9f01202 100644 --- a/Musicreater/experiment.py +++ b/Musicreater/experiment.py @@ -16,19 +16,353 @@ Terms & Conditions: License.md in the root directory # Email TriM-Organization@hotmail.com # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md +from typing import Dict, List, Tuple from .exceptions import * from .main import ( MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE, + MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE, + MM_TOUCH_PITCHED_INSTRUMENT_TABLE, MidiConvert, mido, ) from .subclass import * -from .types import ChannelType, Dict, List, Tuple +from .types import ChannelType, FittingFunctionType from .utils import * +class FutureMidiConvertKamiRES(MidiConvert): + """ + 神羽资源包之测试支持 + """ + @staticmethod + def to_music_note_channels( + midi: mido.MidiFile, + ignore_mismatch_error: bool = True, + speed: float = 1.0, + default_program_value: int = -1, + 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, + vol_processing_function: FittingFunctionType = natural_curve, + note_rtable_replacement: Dict[str, str] = {}, + ) -> Tuple[MineNoteChannelType, int, Dict[str, int]]: + """ + 将midi解析并转换为频道音符字典 + + Parameters + ---------- + midi: mido.MidiFile 对象 + 需要处理的midi对象 + speed: float + 音乐播放速度倍数 + default_program_value: int + 默认的 MIDI 乐器值 + default_tempo_value: int + 默认的 MIDI TEMPO 值 + pitched_note_rtable: Dict[int, Tuple[str, int]] + 乐音乐器Midi-MC对照表 + percussion_note_rtable: Dict[int, Tuple[str, int]] + 打击乐器Midi-MC对照表 + vol_processing_function: Callable[[float], float] + 声像偏移拟合函数 + note_rtable_replacement: Dict[str, str] + 音符名称替换表,此表用于对 Minecraft 乐器名称进行替换,而非 Midi Program 的替换 + + Returns + ------- + 以频道作为分割的Midi音符列表字典, 音符总数, 乐器使用统计: + Tuple[MineNoteChannelType, int, Dict[str, int]] + """ + + if speed == 0: + raise ZeroSpeedError("播放速度为 0 ,其需要(0,1]范围内的实数。") + + # 一个midi中仅有16个通道 我们通过通道来识别而不是音轨 + midi_channels: MineNoteChannelType = empty_midi_channels(default_staff=[]) + channel_program: Dict[int, int] = empty_midi_channels( + default_staff=default_program_value + ) + tempo = default_tempo_value + note_count = 0 + note_count_per_instrument: Dict[str, int] = {} + microseconds = 0 + + note_queue_A: Dict[ + int, + List[ + Tuple[ + int, + int, + ] + ], + ] = empty_midi_channels(default_staff=[]) + note_queue_B: Dict[ + int, + List[ + Tuple[ + int, + int, + ] + ], + ] = empty_midi_channels(default_staff=[]) + + # 直接使用mido.midifiles.tracks.merge_tracks转为单轨 + # 采用的时遍历信息思路 + for msg in midi.merged_track: + if msg.time != 0: + # 微秒 + microseconds += msg.time * tempo / midi.ticks_per_beat + + # 简化 + if msg.type == "set_tempo": + tempo = msg.tempo + else: + if msg.type == "program_change": + channel_program[msg.channel] = msg.program + + elif msg.type == "note_on" and msg.velocity != 0: + note_queue_A[msg.channel].append( + (msg.note, channel_program[msg.channel]) + ) + note_queue_B[msg.channel].append((msg.velocity, microseconds)) + + elif (msg.type == "note_off") or ( + msg.type == "note_on" and msg.velocity == 0 + ): + if (msg.note, channel_program[msg.channel]) in note_queue_A[ + msg.channel + ]: + _velocity, _ms = note_queue_B[msg.channel][ + note_queue_A[msg.channel].index( + (msg.note, channel_program[msg.channel]) + ) + ] + note_queue_A[msg.channel].remove( + (msg.note, channel_program[msg.channel]) + ) + note_queue_B[msg.channel].remove((_velocity, _ms)) + + midi_channels[msg.channel].append( + that_note := midi_msgs_to_minenote_using_kami_respack( + inst_=( + msg.note + if msg.channel == 9 + else channel_program[msg.channel] + ), + note_=( + channel_program[msg.channel] + if msg.channel == 9 + else msg.note + ), + percussive_=(msg.channel == 9), + velocity_=_velocity, + start_time_=_ms, # 微秒 + duration_=microseconds - _ms, # 微秒 + play_speed=speed, + midi_reference_table=( + percussion_note_rtable + if msg.channel == 9 + else pitched_note_rtable + ), + volume_processing_method_=vol_processing_function, + note_table_replacement=note_rtable_replacement, + ) + ) + note_count += 1 + if that_note.sound_name in note_count_per_instrument.keys(): + note_count_per_instrument[that_note.sound_name] += 1 + else: + note_count_per_instrument[that_note.sound_name] = 1 + else: + if ignore_mismatch_error: + print( + "[WARRING] MIDI格式错误 音符不匹配 {} 无法在上文中找到与之匹配的音符开音消息".format( + msg + ) + ) + else: + raise NoteOnOffMismatchError( + "当前的MIDI很可能有损坏之嫌……", + msg, + "无法在上文中找到与之匹配的音符开音消息。", + ) + + """整合后的音乐通道格式 + 每个通道包括若干消息元素其中逃不过这三种: + + 1 切换乐器消息 + ("PgmC", 切换后的乐器ID: int, 距离演奏开始的毫秒) + + 2 音符开始消息 + ("NoteS", 开始的音符ID, 力度(响度), 距离演奏开始的毫秒) + + 3 音符结束消息 + ("NoteE", 结束的音符ID, 距离演奏开始的毫秒)""" + del tempo + channels = dict( + [ + (channel_no, sorted(channel_notes, key=lambda note: note.start_tick)) + for channel_no, channel_notes in midi_channels.items() + ] + ) + + return ( + channels, + note_count, + note_count_per_instrument, + ) + + + def to_command_list_in_score( + self, + scoreboard_name: str = "mscplay", + ) -> Tuple[List[List[MineCommand]], int, int]: + """ + 将midi转换为我的世界命令列表 + + Parameters + ---------- + scoreboard_name: str + 我的世界的计分板名称 + + Returns + ------- + tuple( list[list[MineCommand指令,... ],... ], int指令数量, int音乐时长游戏刻 ) + """ + + command_channels = [] + command_amount = 0 + max_score = 0 + + # 此处 我们把通道视为音轨 + for channel in self.channels.values(): + # 如果当前通道为空 则跳过 + if not channel: + continue + + this_channel = [] + + for note in channel: + max_score = max(max_score, note.start_tick) + + ( + mc_sound_ID, + relative_coordinates, + volume_percentage, + mc_pitch, + ) = minenote_to_command_paramaters( + note, + pitch_deviation=self.music_deviation, + ) + + this_channel.append( + MineCommand( + ( + self.execute_cmd_head.format( + "@a[scores=({}={})]".format( + scoreboard_name, note.start_tick + ) + .replace("(", r"{") + .replace(")", r"}") + ) + + r"playsound {} @s ^{} ^{} ^{} {} {} {}".format( + mc_sound_ID, + *relative_coordinates, + volume_percentage, + 1.0, + self.minimum_volume, + ) + ), + annotation=( + "在{}播放{}".format( + mctick2timestr(note.start_tick), + mc_sound_ID, + ) + ), + ), + ) + + command_amount += 1 + + if this_channel: + self.music_command_list.extend(this_channel) + command_channels.append(this_channel) + + return command_channels, command_amount, max_score + + def to_command_list_in_delay( + self, + player_selector: str = "@a", + ) -> Tuple[List[MineCommand], int, int]: + """ + 将midi转换为我的世界命令列表,并输出每个音符之后的延迟 + + Parameters + ---------- + player_selector: str + 玩家选择器,默认为`@a` + + Returns + ------- + tuple( 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, + ) + + # 此处 我们把通道视为音轨 + self.music_command_list = [] + multi = max_multi = 0 + delaytime_previous = 0 + + for note in notes_list: + 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, + ) = minenote_to_command_paramaters( + note, + pitch_deviation=self.music_deviation, + ) + + self.music_command_list.append( + MineCommand( + command=( + self.execute_cmd_head.format(player_selector) + + r"playsound {} @s ^{} ^{} ^{} {} {} {}".format( + mc_sound_ID, + *relative_coordinates, + volume_percentage, + 1.0, + self.minimum_volume, + ) + ), + annotation=( + "在{}播放音{}".format( + mctick2timestr(note.start_tick), + mc_sound_ID, + ) + ), + tick_delay=tickdelay, + ), + ) + delaytime_previous = note.start_tick + + return self.music_command_list, notes_list[-1].start_tick, max_multi + 1 + + + class FutureMidiConvertJavaE(MidiConvert): def form_java_progress_bar( diff --git a/Musicreater/types.py b/Musicreater/types.py index a585cf2..c2a8a07 100644 --- a/Musicreater/types.py +++ b/Musicreater/types.py @@ -16,19 +16,10 @@ Terms & Conditions: License.md in the root directory # Email TriM-Organization@hotmail.com # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md -from typing import ( - Dict, - List, - Literal, - Tuple, - Union, - Mapping, - Callable, -) +from typing import Callable, Dict, List, Literal, Mapping, Tuple, Union from .subclass import MineNote - MidiNoteNameTableType = Mapping[int, Tuple[str, ...]] """ Midi音符名称对照表类型 diff --git a/Musicreater/utils.py b/Musicreater/utils.py index dd80d61..38edcef 100644 --- a/Musicreater/utils.py +++ b/Musicreater/utils.py @@ -295,6 +295,69 @@ def midi_msgs_to_minenote( ) +def midi_msgs_to_minenote_using_kami_respack( + inst_: int, # 乐器编号 + note_: int, + percussive_: bool, # 是否作为打击乐器启用 + velocity_: int, + start_time_: int, + duration_: int, + play_speed: float, + midi_reference_table: MidiInstrumentTableType, + volume_processing_method_: Callable[[float], float], + note_table_replacement: Dict[str, str] = {}, +) -> MineNote: + """ + 将Midi信息转为我的世界音符对象 + :param inst_: int 乐器编号 + :param note_: int 音高编号(音符编号) + :param percussive_: bool 是否作为打击乐器启用 + :param velocity_: int 力度(响度) + :param start_time_: int 音符起始时间(微秒) + :param duration_: int 音符持续时间(微秒) + :param play_speed: float 曲目播放速度 + :param midi_reference_table: Dict[int, str] 转换对照表 + :param volume_proccessing_method_: Callable[[float], float] 音量处理函数 + :param note_table_replacement: Dict[str, str] 音符替换表,定义 Minecraft 音符字串的替换 + + :return MineNote我的世界音符对象 + """ + + using_original = False + if not percussive_ and (0 <= inst_ <= 119): + mc_sound_ID = "{}{}.{}".format( + # inst_, "d" if duration_ < 500_000 else "c", note_ + inst_, "d", note_ + ) + elif percussive_ and (27 <= inst_ <= 87): + mc_sound_ID = "-1d.{}".format(inst_) + else: + using_original = True + mc_sound_ID = midi_inst_to_mc_sound( + inst_, + midi_reference_table, + "note.bd" if percussive_ else "note.flute", + ) + + mc_distance_volume = volume_processing_method_(velocity_) + + return MineNote( + mc_sound_name=note_table_replacement.get(mc_sound_ID, mc_sound_ID), + midi_pitch=note_ if using_original else 1, + midi_velocity=velocity_, + start_time=(tk := int(start_time_ / float(play_speed) / 50000)), + last_time=round(duration_ / float(play_speed) / 50000), + mass_precision_time=round((start_time_ / float(play_speed) - tk * 50000) / 800), + is_percussion=percussive_, + displacement=(0, mc_distance_volume, 0), + extra_information={ + "USING_ORIGINAL_SOUND": using_original, # 判断 extra_information 中是否有 USING_ORIGINAL_SOUND 键是判断是否使用神羽资源包解析的一个显著方法 + "INST_VALUE": note_ if percussive_ else inst_, + "NOTE_VALUE": inst_ if percussive_ else note_, + }, + ) + + # def single_note_to_minenote( # note_: SingleNote, # reference_table: MidiInstrumentTableType, diff --git a/Packer/MSCT_Packer.py b/Packer/MSCT_Packer.py index 9f0cb13..4fea1c9 100644 --- a/Packer/MSCT_Packer.py +++ b/Packer/MSCT_Packer.py @@ -1,26 +1,24 @@ import Musicreater import Musicreater.experiment -import Musicreater.previous - import Musicreater.plugin +# import Musicreater.previous from Musicreater.plugin.addonpack import ( to_addon_pack_in_delay, to_addon_pack_in_repeater, to_addon_pack_in_score, ) +from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score from Musicreater.plugin.mcstructfile import ( to_mcstructure_file_in_delay, to_mcstructure_file_in_repeater, to_mcstructure_file_in_score, ) -from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score - MSCT_MAIN = ( Musicreater, Musicreater.experiment, - Musicreater.previous, + # Musicreater.previous, ) MSCT_PLUGIN = (Musicreater.plugin,) @@ -38,8 +36,8 @@ MSCT_PLUGIN_FUNCTION = ( import hashlib -import dill import brotli +import dill def enpack_msct_pack(sth, to_dist: str): diff --git a/README.md b/README.md index e7bbe40..d5f4600 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,7 @@ [CodeStyle: black]: https://img.shields.io/badge/code%20style-black-121110.svg?style=for-the-badge [python]: https://img.shields.io/badge/python-3.8-AB70FF?style=for-the-badge [release]: https://img.shields.io/github/v/release/EillesWan/Musicreater?style=for-the-badge -[license]: https://img.shields.io/badge/Licence-Apache-228B22?style=for-the-badge - +[license]: https://img.shields.io/badge/Licence-%E6%B1%89%E9%92%B0%E5%BE%8B%E8%AE%B8%E5%8F%AF%E5%8D%8F%E8%AE%AE-228B22?style=for-the-badge

音·创 Musicreater

@@ -45,25 +44,25 @@ ## 安装 🔳 -- 使用 pypi - - ```bash - pip install --upgrade Musicreater - ``` +- 使用 pypi -- 如果无法更新最新,可以尝试: - - ```bash - pip install --upgrade -i https://pypi.python.org/simple Musicreater - ``` + ```bash + pip install --upgrade Musicreater + ``` -- 克隆仓库并安装(最新版本但**不推荐**) - - ```bash - git clone https://gitee.com/TriM-Organization/Musicreater.git - cd Musicreater - python setup.py install - ``` +- 如果无法更新最新,可以尝试: + + ```bash + pip install --upgrade -i https://pypi.python.org/simple Musicreater + ``` + +- 克隆仓库并安装(最新版本但**不推荐**) + + ```bash + git clone https://gitee.com/TriM-Organization/Musicreater.git + cd Musicreater + python setup.py install + ``` 以上命令中 `python`、`pip` 请依照各个环境不同灵活更换,可能为`python3`或`pip3`之类。 @@ -85,23 +84,23 @@ 本致谢列表排名无顺序。 -- 感谢 **昀梦**\ 找出指令生成错误 bug 并指正 -- 感谢由 **Charlie_Ping “查理平”** 带来的 BDX 文件转换参考,以及 MIDI-我的世界对应乐器 参考表格 -- 感谢由 **[CMA_2401PT](https://github.com/CMA2401PT)** 为我们的软件开发的一些方面进行指导,同时我们参考了他的 BDXworkshop 作为 BDX 结构编辑的参考 -- 感谢由 **[Dislink Sforza](https://github.com/Dislink) “断联·斯福尔扎”**\ 带来的 midi 音色解析以及转换指令的算法,我们将其改编并应用;同时,感谢他的[网页版转换器](https://dislink.github.io/midi2bdx/)给我们的开发与更新带来巨大的压力和动力,让我们在原本一骑绝尘的摸鱼道路上转向开发。 -- 感谢 **Mono**\ 反馈安装时的问题,辅助我们找到了视窗操作系统下的兼容性问题;感谢其反馈延迟播放器出现的重大问题,让我们得以修改全部延迟播放错误;尤其感谢他对于我们的软件的大力宣传 -- 感谢 **Ammelia “艾米利亚”**\ 敦促我们进行新的功能开发,并为新功能提出了非常优秀的大量建议,以及提供的 BDX 导入测试支持,为我们的新结构生成算法提供了大量的实际理论支持 -- 感谢 **[神羽](https://gitee.com/snowykami) “[SnowyKami](https://github.com/snowyfirefly)”** 对我们项目的支持与宣传,非常感谢他为我们提供的服务器! -- 感谢 **指令师\_苦力怕 playjuice123**\ 为我们的程序找出错误,并提醒我们修复一个一直存在的大 bug。 -- 感谢 **雷霆**\ 用他那令所有开发者都大为光火的操作方法为我们的程序找出错误,并提醒修复 bug。 -- 感谢 **小埋**\ 反馈附加包生成时缺少描述和标题的问题。 --
感谢 **油炸**<QQ2836146704> 激励我们不断开发新的内容。
-- 感谢 **雨**\ 反馈在新版本的指令格式下,计分板播放器的附加包无法播放的问题。 -- 感谢 **梦幻duang**\ 为我们提供 Java 1.12.2 版本命令格式参考。 -- 感谢 [_Open Note Block Studio_](https://github.com/OpenNBS/NoteBlockStudio) 项目的开发为我们提供持续的追赶动力。 +- 感谢 **昀梦**\ 找出指令生成错误 bug 并指正 +- 感谢由 **Charlie_Ping “查理平”** 带来的 BDX 文件转换参考,以及 MIDI-我的世界对应乐器 参考表格 +- 感谢由 **[CMA_2401PT](https://github.com/CMA2401PT)** 为我们的软件开发的一些方面进行指导,同时我们参考了他的 BDXworkshop 作为 BDX 结构编辑的参考 +- 感谢由 **[Dislink Sforza](https://github.com/Dislink) “断联·斯福尔扎”**\ 带来的 midi 音色解析以及转换指令的算法,我们将其改编并应用;同时,感谢他的[网页版转换器](https://dislink.github.io/midi2bdx/)给我们的开发与更新带来巨大的压力和动力,让我们在原本一骑绝尘的摸鱼道路上转向开发。 +- 感谢 **Mono**\ 反馈安装时的问题,辅助我们找到了视窗操作系统下的兼容性问题;感谢其反馈延迟播放器出现的重大问题,让我们得以修改全部延迟播放错误;尤其感谢他对于我们的软件的大力宣传 +- 感谢 **Ammelia “艾米利亚”**\ 敦促我们进行新的功能开发,并为新功能提出了非常优秀的大量建议,以及提供的 BDX 导入测试支持,为我们的新结构生成算法提供了大量的实际理论支持 +- 感谢 **[神羽](https://gitee.com/snowykami) “[SnowyKami](https://github.com/snowyfirefly)”** 对我们项目的支持与宣传,非常感谢他为我们提供的服务器! +- 感谢 **指令师\_苦力怕 playjuice123**\ 为我们的程序找出错误,并提醒我们修复一个一直存在的大 bug。 +- 感谢 **雷霆**\ 用他那令所有开发者都大为光火的操作方法为我们的程序找出错误,并提醒修复 bug。 +- 感谢 **小埋**\ 反馈附加包生成时缺少描述和标题的问题。 +-
感谢 **油炸**<QQ2836146704> 激励我们不断开发新的内容。
+- 感谢 **雨**\ 反馈在新版本的指令格式下,计分板播放器的附加包无法播放的问题。 +- 感谢 **梦幻duang**\ 为我们提供 Java 1.12.2 版本命令格式参考。 +- 感谢 [_Open Note Block Studio_](https://github.com/OpenNBS/NoteBlockStudio) 项目的开发为我们提供持续的追赶动力。 -> 感谢广大群友为此库提供的测试和建议等 -> 若您对我们有所贡献但您的名字没有出现在此列表中,请联系我们! +> 感谢广大群友为此库提供的测试和建议等 +> 若您对我们有所贡献但您的名字没有出现在此列表中,请联系我们! ## 联系 📞 @@ -121,7 +120,7 @@ “Minecraft”是 Mojang Synergies AB 的商标,此项目中所有对于“我的世界”、“Minecraft”等相关称呼均为必要的介绍性使用 -- 上文提及的 网易 公司,指代的是在中国大陆运营《我的世界:中国版》的上海网之易璀璨网络科技有限公司 +- 上文提及的 网易 公司,指代的是在中国大陆运营《我的世界:中国版》的上海网之易璀璨网络科技有限公司 NOT AN OFFICIAL MINECRAFT PRODUCT. diff --git a/README_EN.md b/README_EN.md index 2e1aa2c..94a2e51 100644 --- a/README_EN.md +++ b/README_EN.md @@ -3,7 +3,7 @@ [CodeStyle: black]: https://img.shields.io/badge/code%20style-black-121110.svg?style=for-the-badge [python]: https://img.shields.io/badge/python-3.8-AB70FF?style=for-the-badge [release]: https://img.shields.io/github/v/release/EillesWan/Musicreater?style=for-the-badge -[license]: https://img.shields.io/badge/Licence-Apache-228B22?style=for-the-badge +[license]: https://img.shields.io/badge/Licence-%E6%B1%89%E9%92%B0%E5%BE%8B%E8%AE%B8%E5%8F%AF%E5%8D%8F%E8%AE%AE-228B22?style=for-the-badge

音·创 Musicreater @@ -98,11 +98,11 @@ This list is not in any order. - Thank _小埋_\ for reporting the empty add-on packs title and description problem. -
Thank 油炸 <QQ2836146704> for inspiring us to constantly develop something new.The groupmate on the picture was saying that our convert-QQ-bot had once brought him great convinience but now it closed down by some reason so he was feeling regretful."It was once, a convert-QQ-bot is just in front my eyes"
"Until lose, I finally know cannot chase back what I needs"
- Thank _雨_\ for give us report that under the new `execute` command format that the scoreboard player's add-on packs cannot play correctly. -- Thank _梦幻duang_\ for providing us with his knowlodeg of the command format in Minecraft: Java Edition Version 1.12.2. -- Thank [_Open Note Block Studio_](https://github.com/OpenNBS/NoteBlockStudio)'s Project for giving us the power and energy of continual developing. +- Thank _梦幻duang_\ for providing us with his knowlodeg of the command format in Minecraft: Java Edition Version 1.12.2. +- Thank [_Open Note Block Studio_](https://github.com/OpenNBS/NoteBlockStudio)'s Project for giving us the power and energy of continual developing. > Thanks for the support and help of a lot of groupmates -> If you have given contributions but have not been in the list, please contact us! +> If you have given contributions but have not been in the list, please contact us! ## Contact Us 📞 @@ -129,4 +129,3 @@ NOT APPROVED BY OR ASSOCIATED WITH NETEASE. “Minecraft”是 Mojang Synergies AB 的商标,此项目中所有对于“我的世界”、“Minecraft”等相关称呼均为必要的介绍性使用 - 上文提及的 网易 公司,指代的是在中国大陆运营《我的世界:中国版》的上海网之易璀璨网络科技有限公司 - diff --git a/pyproject.toml b/pyproject.toml index 4fee649..217a217 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ dev = [ "TrimMCStruct <= 0.0.5.9", "brotli >= 1.0.0", + "dill", "rich", "pyinstaller", "twine", @@ -91,3 +92,4 @@ [tool.pdm.version] source = "file" path = "Musicreater/__init__.py" + diff --git a/setup.py b/setup.py deleted file mode 100644 index 6391f1b..0000000 --- a/setup.py +++ /dev/null @@ -1,42 +0,0 @@ -# -*- coding: utf-8 -*- -import setuptools -import os - -# from Musicreater import __version__ - -os.chdir(os.path.dirname(os.path.abspath(__file__))) - -with open("./requirements.txt", "r", encoding="utf-8") as fh: - dependences = fh.read().strip().split("\n") - -with open("./README_EN.md", "r", encoding="utf-8") as fh: - long_description = fh.read().replace( - "./docs/", "https://github.com/TriM-Organization/Musicreater/blob/master/docs/" - ) - -setuptools.setup( - name="Musicreater", - version="2.2.4", - author="金羿Eilles, bgArray, 鱼旧梦ElapsingDreams", - author_email="TriM-Organization@hotmail.com", - description="A free open source library used for dealing with **Minecraft** digital musics.\n一款开源《我的世界》数字音频支持库。", - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/TriM-Organization/Musicreater", - packages=setuptools.find_packages(), - classifiers=[ - "Intended Audience :: Developers", - "Natural Language :: Chinese (Simplified)", - # "License :: OSI Approved :: Apache Software License", - "Operating System :: OS Independent", - "Topic :: Software Development :: Libraries", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Topic :: Multimedia", - "Topic :: Multimedia :: Sound/Audio :: MIDI", - ], - # 需要安装的依赖 - install_requires=dependences, - python_requires=">=3.8", - license=open("./LICENSE.md", "r", encoding="utf-8").read(), -) diff --git a/example_fsq_opera.py b/test_fsq_opera.py similarity index 100% rename from example_fsq_opera.py rename to test_fsq_opera.py index b56bcbc..cacee93 100644 --- a/example_fsq_opera.py +++ b/test_fsq_opera.py @@ -1,11 +1,11 @@ +from rich.pretty import pprint + import Musicreater from Musicreater.utils import ( load_decode_fsq_flush_release, load_decode_musicsequence_metainfo, ) -from rich.pretty import pprint - msc_seq = Musicreater.MusicSequence.from_mido( Musicreater.mido.MidiFile( "./resources/测试片段.mid", diff --git a/test_future_kamires.py b/test_future_kamires.py new file mode 100644 index 0000000..4857b38 --- /dev/null +++ b/test_future_kamires.py @@ -0,0 +1,33 @@ +import Musicreater.experiment +import Musicreater.plugin +import Musicreater.plugin.mcstructfile + +msct = Musicreater.experiment.FutureMidiConvertKamiRES.from_midi_file( + input("midi路径:"), old_exe_format=False +) + +opt = input("输出路径:") + +print( + "乐器使用情况", +) + +for name in set( + sorted( + [ + n.split(".")[0].replace("c", "").replace("d", "") + for n in msct.note_count_per_instrument.keys() + ] + ) +): + print("\t", name, flush=True) + +print( + "\n输出:", + Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay( + msct, + opt, + # Musicreater.plugin.ConvertConfig(input("输出路径:"),), + max_height=32, + ), +) diff --git a/example_msq_opera.py b/test_msq_opera.py similarity index 100% rename from example_msq_opera.py rename to test_msq_opera.py index 7a410b8..a4580ec 100644 --- a/example_msq_opera.py +++ b/test_msq_opera.py @@ -1,11 +1,11 @@ +from rich.pretty import pprint + import Musicreater from Musicreater.utils import ( load_decode_msq_flush_release, load_decode_musicsequence_metainfo, ) -from rich.pretty import pprint - msc_seq = Musicreater.MusicSequence.from_mido( Musicreater.mido.MidiFile( "./resources/测试片段.mid", diff --git a/uv.lock b/uv.lock index fb9341c..525af6d 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 1 +revision = 2 requires-python = ">=3.8, <4.0" resolution-markers = [ "python_full_version >= '3.10'", @@ -314,6 +314,15 @@ wheels = [ { url = "https://mirror.nju.edu.cn/pypi/web/packages/57/ff/f3b4b2d007c2a646b0f69440ab06224f9cf37a977a72cdb7b50632174e8a/cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:04abd71114848aa25edb28e225ab5f268096f44cf0127f3d36975bdf1bdf3390" }, ] +[[package]] +name = "dill" +version = "0.4.0" +source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" } +sdist = { url = "https://mirror.nju.edu.cn/pypi/web/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0" } +wheels = [ + { url = "https://mirror.nju.edu.cn/pypi/web/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049" }, +] + [[package]] name = "docutils" version = "0.20.1" @@ -575,6 +584,7 @@ dependencies = [ [package.optional-dependencies] dev = [ { name = "brotli" }, + { name = "dill" }, { name = "pyinstaller" }, { name = "rich" }, { name = "trimmcstruct" }, @@ -589,6 +599,7 @@ full = [ requires-dist = [ { name = "brotli", marker = "extra == 'dev'", specifier = ">=1.0.0" }, { name = "brotli", marker = "extra == 'full'", specifier = ">=1.0.0" }, + { name = "dill", marker = "extra == 'dev'" }, { name = "mido", specifier = ">=1.3" }, { name = "pyinstaller", marker = "extra == 'dev'" }, { name = "rich", marker = "extra == 'dev'" },