From 2a5ccb8eebb01596cf84a96e996e8de686cfd637 Mon Sep 17 00:00:00 2001 From: EillesWan Date: Sun, 8 Feb 2026 06:14:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=87=86=E5=A4=87=E7=A7=BB=E6=A4=8D=20v2=20?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=88=B0=E6=8F=92=E4=BB=B6=E6=9D=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Musicreater/__init__.py | 1 - Musicreater/_plugin_abc.py | 7 +- Musicreater/builtin_plugins/midi_read.py | 59 -- .../builtin_plugins/midi_read/__init__.py | 20 + .../builtin_plugins/midi_read/constants.py | 795 ++++++++++++++++++ Musicreater/builtin_plugins/midi_read/main.py | 89 ++ .../builtin_plugins/midi_read/utils.py | 108 +++ Musicreater/constants.py | 769 +---------------- Musicreater/data.py | 5 +- Musicreater/exceptions.py | 1 - Musicreater/main.py | 4 +- Musicreater/plugins.py | 28 +- old-things/Musicreater/old_main.py | 7 +- old-things/Musicreater/utils.py | 87 +- pyproject.toml | 24 +- test_read.py | 21 +- uv.lock | 26 +- 17 files changed, 1095 insertions(+), 956 deletions(-) delete mode 100644 Musicreater/builtin_plugins/midi_read.py create mode 100644 Musicreater/builtin_plugins/midi_read/__init__.py create mode 100644 Musicreater/builtin_plugins/midi_read/constants.py create mode 100644 Musicreater/builtin_plugins/midi_read/main.py create mode 100644 Musicreater/builtin_plugins/midi_read/utils.py diff --git a/Musicreater/__init__.py b/Musicreater/__init__.py index a980ddd..c45d085 100644 --- a/Musicreater/__init__.py +++ b/Musicreater/__init__.py @@ -33,7 +33,6 @@ __author__ = ( ("金羿", "Eilles"), ("玉衡Alioth", "YuhengAlioth"), ("鱼旧梦", "ElapsingDreams"), - ("偷吃不是Touch", "Touch"), ) from .paramcurve import ParamCurve, InterpolationMethod, BoundaryBehaviour diff --git a/Musicreater/_plugin_abc.py b/Musicreater/_plugin_abc.py index 00ae230..f4029db 100644 --- a/Musicreater/_plugin_abc.py +++ b/Musicreater/_plugin_abc.py @@ -41,6 +41,7 @@ from typing import ( Generator, Iterator, Set, + Type, ) if sys.version_info >= (3, 11): @@ -97,6 +98,7 @@ from .data import SingleMusic, SingleTrack # 枚举类 # ======================== + class PluginTypes(str, Enum): """插件类型枚举""" @@ -110,7 +112,6 @@ class PluginTypes(str, Enum): LIBRARY = "library" - # ======================== # 数据类 # ======================== @@ -174,7 +175,7 @@ class PluginConfig(ABC): with file_path.open("wb") as f: tomli_w.dump(self.to_dict(), f, multiline_strings=False, indent=4) except Exception as e: - raise PluginConfigDumpError(e) + raise PluginConfigDumpError("插件配置文件无法保存。") from e @classmethod def load_from_file(cls, file_path: Path) -> "PluginConfig": @@ -199,7 +200,7 @@ class PluginConfig(ABC): with file_path.open("rb") as f: return cls.from_dict(tomllib.load(f)) except Exception as e: - raise PluginConfigLoadError(e) + raise PluginConfigLoadError("插件配置文件无法加载。") from e @dataclass diff --git a/Musicreater/builtin_plugins/midi_read.py b/Musicreater/builtin_plugins/midi_read.py deleted file mode 100644 index 687d35b..0000000 --- a/Musicreater/builtin_plugins/midi_read.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -音·创 v3 内置的 Midi 读取插件 -""" - -""" -版权所有 © 2026 金羿、玉衡Alioth -Copyright © 2026 Eilles, YuhengAlioth - -开源相关声明请见 仓库根目录下的 License.md -Terms & Conditions: License.md in the root directory -""" - -# 睿乐组织 开发交流群 861684859 -# Email TriM-Organization@hotmail.com -# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md - -import mido - -from pathlib import Path - -from typing import BinaryIO, Optional - -from Musicreater import SingleMusic -from Musicreater.plugins import ( - music_input_plugin, - PluginConfig, - PluginMetaInformation, - PluginTypes, - MusicInputPluginBase, -) - - -@music_input_plugin("midi_2_music_plugin") -class MidiImport2MusicPlugin(MusicInputPluginBase): - """Midi 音乐数据导入插件""" - - metainfo = PluginMetaInformation( - name="Midi 导入插件", - author="金羿、玉衡Alioth", - description="从 Midi 文件导入音乐数据", - version=(0, 0, 1), - type=PluginTypes.FUNCTION_MUSIC_IMPORT, - license="Same as Musicreater", - ) - - supported_formats = ("MID", "MIDI") - - def loadbytes( - self, bytes_buffer_in: BinaryIO, config: PluginConfig | None - ) -> SingleMusic: - midi_file = mido.MidiFile(file=bytes_buffer_in) - return SingleMusic() # =========================== TODO: 等待制作 - - def load(self, file_path: Path, config: Optional[PluginConfig]) -> "SingleMusic": - """从 Midi 文件导入音乐数据""" - midi_file = mido.MidiFile(filename=file_path) - return SingleMusic() # =========================== TODO: 等待制作 diff --git a/Musicreater/builtin_plugins/midi_read/__init__.py b/Musicreater/builtin_plugins/midi_read/__init__.py new file mode 100644 index 0000000..931bc77 --- /dev/null +++ b/Musicreater/builtin_plugins/midi_read/__init__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- + +""" +音·创 v3 内置的 Midi 读取插件 +""" + +""" +版权所有 © 2026 金羿、玉衡Alioth、偷吃不是Touch +Copyright © 2026 Eilles, YuhengAlioth, Touch + +开源相关声明请见 仓库根目录下的 License.md +Terms & Conditions: License.md in the root directory +""" + +# 睿乐组织 开发交流群 861684859 +# Email TriM-Organization@hotmail.com +# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md + + +from .main import MidiImportConfig, MidiImport2MusicPlugin \ No newline at end of file diff --git a/Musicreater/builtin_plugins/midi_read/constants.py b/Musicreater/builtin_plugins/midi_read/constants.py new file mode 100644 index 0000000..31f59d1 --- /dev/null +++ b/Musicreater/builtin_plugins/midi_read/constants.py @@ -0,0 +1,795 @@ +# -*- coding: utf-8 -*- + +""" +音·创 v3 内置的 Midi 读取插件的数值常量 +""" + +""" +版权所有 © 2026 金羿、玉衡Alioth、偷吃不是Touch +Copyright © 2026 Eilles, YuhengAlioth, Touch + +开源相关声明请见 仓库根目录下的 License.md +Terms & Conditions: License.md in the root directory +""" + +# 睿乐组织 开发交流群 861684859 +# Email TriM-Organization@hotmail.com +# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md + + +from typing import Dict, List, Tuple + + +# Midi用对照表 + +MIDI_DEFAULT_VOLUME_VALUE: int = ( + 64 # Midi默认音量,当用户未指定时,默认使用折中默认音量 +) + +MIDI_DEFAULT_PROGRAM_VALUE: int = ( + 74 # 当 Midi 本身与用户皆未指定音色时,默认 Flute 长笛 +) + + +# Midi乐器对MC乐器对照表 + +# “经典”对照表,由 Chalie Ping “查理平” 和 金羿ELS 提供 + +MM_CLASSIC_PITCHED_INSTRUMENT_TABLE: Dict[int, str] = { + 0: "note.harp", + 1: "note.harp", + 2: "note.pling", + 3: "note.harp", + 4: "note.pling", + 5: "note.pling", + 6: "note.harp", + 7: "note.harp", + 8: "note.snare", + 9: "note.harp", + 10: "note.didgeridoo", + 11: "note.harp", + 12: "note.xylophone", + 13: "note.chime", + 14: "note.harp", + 15: "note.harp", + 16: "note.bass", + 17: "note.harp", + 18: "note.harp", + 19: "note.harp", + 20: "note.harp", + 21: "note.harp", + 22: "note.harp", + 23: "note.guitar", + 24: "note.guitar", + 25: "note.guitar", + 26: "note.guitar", + 27: "note.guitar", + 28: "note.guitar", + 29: "note.guitar", + 30: "note.guitar", + 31: "note.bass", + 32: "note.bass", + 33: "note.bass", + 34: "note.bass", + 35: "note.bass", + 36: "note.bass", + 37: "note.bass", + 38: "note.bass", + 39: "note.bass", + 40: "note.harp", + 41: "note.harp", + 42: "note.harp", + 43: "note.harp", + 44: "note.iron_xylophone", + 45: "note.guitar", + 46: "note.harp", + 47: "note.harp", + 48: "note.guitar", + 49: "note.guitar", + 50: "note.bit", + 51: "note.bit", + 52: "note.harp", + 53: "note.harp", + 54: "note.bit", + 55: "note.flute", + 56: "note.flute", + 57: "note.flute", + 58: "note.flute", + 59: "note.flute", + 60: "note.flute", + 61: "note.flute", + 62: "note.flute", + 63: "note.flute", + 64: "note.bit", + 65: "note.bit", + 66: "note.bit", + 67: "note.bit", + 68: "note.flute", + 69: "note.harp", + 70: "note.harp", + 71: "note.flute", + 72: "note.flute", + 73: "note.flute", + 74: "note.harp", + 75: "note.flute", + 76: "note.harp", + 77: "note.harp", + 78: "note.harp", + 79: "note.harp", + 80: "note.bit", + 81: "note.bit", + 82: "note.bit", + 83: "note.bit", + 84: "note.bit", + 85: "note.bit", + 86: "note.bit", + 87: "note.bit", + 88: "note.bit", + 89: "note.bit", + 90: "note.bit", + 91: "note.bit", + 92: "note.bit", + 93: "note.bit", + 94: "note.bit", + 95: "note.bit", + 96: "note.bit", + 97: "note.bit", + 98: "note.bit", + 99: "note.bit", + 100: "note.bit", + 101: "note.bit", + 102: "note.bit", + 103: "note.bit", + 104: "note.harp", + 105: "note.banjo", + 106: "note.harp", + 107: "note.harp", + 108: "note.harp", + 109: "note.harp", + 110: "note.harp", + 111: "note.guitar", + 112: "note.harp", + 113: "note.bell", + 114: "note.harp", + 115: "note.cow_bell", + 116: "note.bd", + 117: "note.bass", + 118: "note.bit", + 119: "note.bd", + 120: "note.guitar", + 121: "note.harp", + 122: "note.harp", + 123: "note.harp", + 124: "note.harp", + 125: "note.hat", + 126: "note.bd", + 127: "note.snare", +} +"""“经典”乐音乐器对照表""" + +MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE: Dict[int, str] = { + 34: "note.bd", + 35: "note.bd", + 36: "note.hat", + 37: "note.snare", + 38: "note.snare", + 39: "note.snare", + 40: "note.hat", + 41: "note.snare", + 42: "note.hat", + 43: "note.snare", + 44: "note.snare", + 45: "note.bell", + 46: "note.snare", + 47: "note.snare", + 48: "note.bell", + 49: "note.hat", + 50: "note.bell", + 51: "note.bell", + 52: "note.bell", + 53: "note.bell", + 54: "note.bell", + 55: "note.bell", + 56: "note.snare", + 57: "note.hat", + 58: "note.chime", + 59: "note.iron_xylophone", + 60: "note.bd", + 61: "note.bd", + 62: "note.xylophone", + 63: "note.xylophone", + 64: "note.xylophone", + 65: "note.hat", + 66: "note.bell", + 67: "note.bell", + 68: "note.hat", + 69: "note.hat", + 70: "note.snare", + 71: "note.flute", + 72: "note.hat", + 73: "note.hat", + 74: "note.xylophone", + 75: "note.hat", + 76: "note.hat", + 77: "note.xylophone", + 78: "note.xylophone", + 79: "note.bell", + 80: "note.bell", +} +"""“经典”打击乐器对照表""" + +# Touch “偷吃” 高准确率音色对照表 + +MM_TOUCH_PITCHED_INSTRUMENT_TABLE: Dict[int, str] = { + 0: "note.harp", + 1: "note.harp", + 2: "note.pling", + 3: "note.harp", + 4: "note.pling", + 5: "note.pling", + 6: "note.guitar", + 7: "note.guitar", + 8: "note.iron_xylophone", + 9: "note.bell", + 10: "note.iron_xylophone", + 11: "note.iron_xylophone", + 12: "note.iron_xylophone", + 13: "note.xylophone", + 14: "note.chime", + 15: "note.banjo", + 16: "note.xylophone", + 17: "note.iron_xylophone", + 18: "note.flute", + 19: "note.flute", + 20: "note.flute", + 21: "note.flute", + 22: "note.flute", + 23: "note.flute", + 24: "note.guitar", + 25: "note.guitar", + 26: "note.guitar", + 27: "note.guitar", + 28: "note.guitar", + 29: "note.guitar", + 30: "note.guitar", + 31: "note.bass", + 32: "note.bass", + 33: "note.bass", + 34: "note.bass", + 35: "note.bass", + 36: "note.bass", + 37: "note.bass", + 38: "note.bass", + 39: "note.bass", + 40: "note.flute", + 41: "note.flute", + 42: "note.flute", + 43: "note.bass", + 44: "note.flute", + 45: "note.iron_xylophone", + 46: "note.harp", + 47: "note.snare", + 48: "note.flute", + 49: "note.flute", + 50: "note.flute", + 51: "note.flute", + 52: "note.didgeridoo", + 53: "note.flute", + 54: "note.flute", + 55: "mob.zombie.wood", + 56: "note.flute", + 57: "note.flute", + 58: "note.flute", + 59: "note.flute", + 60: "note.flute", + 61: "note.flute", + 62: "note.flute", + 63: "note.flute", + 64: "note.bit", + 65: "note.bit", + 66: "note.bit", + 67: "note.bit", + 68: "note.flute", + 69: "note.bit", + 70: "note.banjo", + 71: "note.flute", + 72: "note.flute", + 73: "note.flute", + 74: "note.flute", + 75: "note.flute", + 76: "note.iron_xylophone", + 77: "note.iron_xylophone", + 78: "note.flute", + 79: "note.flute", + 80: "note.bit", + 81: "note.bit", + 82: "note.flute", + 83: "note.flute", + 84: "note.guitar", + 85: "note.flute", + 86: "note.bass", + 87: "note.bass", + 88: "note.bit", + 89: "note.flute", + 90: "note.bit", + 91: "note.flute", + 92: "note.bell", + 93: "note.guitar", + 94: "note.flute", + 95: "note.bit", + 96: "note.bit", + 97: "note.flute", + 98: "note.bell", + 99: "note.bit", + 100: "note.bit", + 101: "note.bit", + 102: "note.bit", + 103: "note.bit", + 104: "note.iron_xylophone", + 105: "note.banjo", + 106: "note.harp", + 107: "note.harp", + 108: "note.bell", + 109: "note.flute", + 110: "note.flute", + 111: "note.flute", + 112: "note.bell", + 113: "note.xylophone", + 114: "note.flute", + 115: "note.hat", + 116: "note.snare", + 117: "note.snare", + 118: "note.bd", + 119: "firework.blast", + 120: "note.guitar", + 121: "note.harp", + 122: "note.harp", + 123: "note.harp", + 124: "note.bit", + 125: "note.hat", + 126: "firework.twinkle", + 127: "mob.zombie.wood", +} +"""“偷吃”乐音乐器对照表""" + +MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE: Dict[int, str] = { + 34: "note.hat", + 35: "note.bd", + 36: "note.bd", + 37: "note.snare", + 38: "note.snare", + 39: "fire.ignite", + 40: "note.snare", + 41: "note.hat", + 42: "note.hat", + 43: "firework.blast", + 44: "note.hat", + 45: "note.snare", + 46: "note.snare", + 47: "note.snare", + 48: "note.bell", + 49: "note.hat", + 50: "note.bell", + 51: "note.bell", + 52: "note.bell", + 53: "note.bell", + 54: "note.bell", + 55: "note.bell", + 56: "note.snare", + 57: "note.hat", + 58: "note.chime", + 59: "note.iron_xylophone", + 60: "note.bd", + 61: "note.bd", + 62: "note.xylophone", + 63: "note.xylophone", + 64: "note.xylophone", + 65: "note.hat", + 66: "note.bell", + 67: "note.bell", + 68: "note.hat", + 69: "note.hat", + 70: "note.snare", + 71: "note.flute", + 72: "note.hat", + 73: "note.hat", + 74: "note.xylophone", + 75: "note.hat", + 76: "note.hat", + 77: "note.xylophone", + 78: "note.xylophone", + 79: "note.bell", + 80: "note.bell", +} +"""“偷吃”打击乐器对照表""" + +# Dislink “断联” 音色对照表 +# https://github.com/Dislink/midi2bdx/blob/main/index.html + + +MM_DISLINK_PITCHED_INSTRUMENT_TABLE: Dict[int, str] = { + 0: "note.harp", + 1: "note.harp", + 2: "note.pling", + 3: "note.harp", + 4: "note.harp", + 5: "note.harp", + 6: "note.harp", + 7: "note.harp", + 8: "note.iron_xylophone", + 9: "note.bell", + 10: "note.iron_xylophone", + 11: "note.iron_xylophone", + 12: "note.iron_xylophone", + 13: "note.iron_xylophone", + 14: "note.chime", + 15: "note.iron_xylophone", + 16: "note.harp", + 17: "note.harp", + 18: "note.harp", + 19: "note.harp", + 20: "note.harp", + 21: "note.harp", + 22: "note.harp", + 23: "note.harp", + 24: "note.guitar", + 25: "note.guitar", + 26: "note.guitar", + 27: "note.guitar", + 28: "note.guitar", + 29: "note.guitar", + 30: "note.guitar", + 31: "note.guitar", + 32: "note.bass", + 33: "note.bass", + 34: "note.bass", + 35: "note.bass", + 36: "note.bass", + 37: "note.bass", + 38: "note.bass", + 39: "note.bass", + 40: "note.harp", + 41: "note.flute", + 42: "note.flute", + 43: "note.flute", + 44: "note.flute", + 45: "note.harp", + 46: "note.harp", + 47: "note.harp", + 48: "note.harp", + 49: "note.harp", + 50: "note.harp", + 51: "note.harp", + 52: "note.harp", + 53: "note.harp", + 54: "note.harp", + 55: "note.harp", + 56: "note.harp", + 57: "note.harp", + 58: "note.harp", + 59: "note.harp", + 60: "note.harp", + 61: "note.harp", + 62: "note.harp", + 63: "note.harp", + 64: "note.harp", + 65: "note.harp", + 66: "note.harp", + 67: "note.harp", + 68: "note.harp", + 69: "note.harp", + 70: "note.harp", + 71: "note.harp", + 72: "note.flute", + 73: "note.flute", + 74: "note.flute", + 75: "note.flute", + 76: "note.flute", + 77: "note.flute", + 78: "note.flute", + 79: "note.flute", + 80: "note.bit", + 81: "note.bit", + 82: "note.harp", + 83: "note.harp", + 84: "note.harp", + 85: "note.harp", + 86: "note.harp", + 87: "note.harp", + 88: "note.harp", + 89: "note.harp", + 90: "note.harp", + 91: "note.harp", + 92: "note.harp", + 93: "note.harp", + 94: "note.harp", + 95: "note.harp", + 96: "note.harp", + 97: "note.harp", + 98: "note.harp", + 99: "note.harp", + 100: "note.harp", + 101: "note.harp", + 102: "note.harp", + 103: "note.harp", + 104: "note.harp", + 105: "note.banjo", + 106: "note.harp", + 107: "note.harp", + 108: "note.harp", + 109: "note.harp", + 110: "note.harp", + 111: "note.harp", + 112: "note.cow_bell", + 113: "note.harp", + 114: "note.harp", + 115: "note.bd", + 116: "note.bd", + 117: "note.bd", + 118: "note.bd", + 119: "note.harp", + 120: "note.harp", + 121: "note.harp", + 122: "note.harp", + 123: "note.harp", + 124: "note.harp", + 125: "note.harp", + 126: "note.harp", + 127: "note.harp", +} +"""“断联”乐音乐器对照表""" + +MM_DISLINK_PERCUSSION_INSTRUMENT_TABLE: Dict[int, str] = { + 34: "note.bd", + 35: "note.bd", + 36: "note.snare", + 37: "note.snare", + 38: "note.bd", + 39: "note.snare", + 40: "note.bd", + 41: "note.hat", + 42: "note.bd", + 43: "note.hat", + 44: "note.bd", + 45: "note.hat", + 46: "note.bd", + 47: "note.bd", + 48: "note.bd", + 49: "note.bd", + 50: "note.bd", + 51: "note.bd", + 52: "note.bd", + 53: "note.bd", + 54: "note.bd", + 55: "note.cow_bell", + 56: "note.bd", + 57: "note.bd", + 58: "note.bd", + 59: "note.bd", + 60: "note.bd", + 61: "note.bd", + 62: "note.bd", + 63: "note.bd", + 64: "note.bd", + 65: "note.bd", + 66: "note.bd", + 67: "note.bd", + 68: "note.bd", + 69: "note.bd", + 70: "note.bd", + 71: "note.bd", + 72: "note.bd", + 73: "note.bd", + 74: "note.bd", + 75: "note.bd", + 76: "note.bd", + 77: "note.bd", + 78: "note.bd", + 79: "note.bd", + 80: "note.bd", +} +"""“断联”打击乐器对照表""" + +# NoteBlockStudio “NBS”音色对照表 +# https://github.com/OpenNBS/NoteBlockStudio/blob/main/scripts/midi_instruments/midi_instruments.gml + +MM_NBS_PITCHED_INSTRUMENT_TABLE: Dict[int, str] = { + 0: "note.harp", + 1: "note.pling", + 2: "note.harp", + 3: "note.pling", + 4: "note.harp", + 5: "note.harp", + 6: "note.guitar", + 7: "note.banjo", + 8: "note.bell", + 9: "note.bell", + 10: "note.bell", + 11: "note.iron_xylophone", + 12: "note.iron_xylophone", + 13: "note.xylophone", + 14: "note.bell", + 15: "note.iron_xylophone", + 16: "note.flute", + 17: "note.flute", + 18: "note.flute", + 19: "note.flute", + 20: "note.flute", + 21: "note.flute", + 22: "note.flute", + 23: "note.flute", + 24: "note.guitar", + 25: "note.guitar", + 26: "note.guitar", + 27: "note.bass", + 28: "note.guitar", + 29: "note.guitar", + 30: "note.bass", + 31: "note.bass", + 32: "note.bass", + 33: "note.guitar", + 34: "note.guitar", + 35: "note.bass", + 36: "note.pling", + 37: "note.flute", + 38: "note.flute", + 39: "note.flute", + 40: "note.flute", + 41: "note.flute", + 42: "note.didgeridoo", + 43: "note.flute", + 44: "note.didgeridoo", + 45: "note.flute", + 46: "note.flute", + 47: "note.flute", + 48: "note.flute", + 49: "note.flute", + 50: "note.flute", + 51: "note.flute", + 52: "note.flute", + 53: "note.flute", + 54: "note.flute", + 55: "note.flute", + 56: "note.flute", + 57: "note.flute", + 58: "note.flute", + 59: "note.flute", + 60: "note.bit", + 61: "note.flute", + 62: "note.flute", + 63: "note.flute", + 64: "note.flute", + 65: "note.guitar", + 66: "note.flute", + 67: "note.flute", + 68: "note.flute", + 69: "note.bell", + 70: "note.flute", + 71: "note.flute", + 72: "note.flute", + 73: "note.flute", + 74: "note.chime", + 75: "note.flute", + 76: "note.flute", + 77: "note.guitar", + 78: "note.pling", + 79: "note.flute", + 80: "note.guitar", + 81: "note.banjo", + 82: "note.banjo", + 83: "note.banjo", + 84: "note.guitar", + 85: "note.iron_xylophone", + 86: "note.flute", + 87: "note.flute", + 88: "note.chime", + 89: "note.cow_bell", + 90: "note.iron_xylophone", + 91: "note.xylophone", + 92: "note.basedrum", + 93: "note.snare", + 94: "note.snare", + 95: "note.basedrum", + 96: "note.snare", + 97: "note.hat", + 98: "note.snare", + 99: "note.hat", + 100: "note.basedrum", + 101: "note.hat", + 102: "note.basedrum", + 103: "note.hat", + 104: "note.basedrum", + 105: "note.snare", + 106: "note.snare", + 107: "note.snare", + 108: "note.cow_bell", + 109: "note.snare", + 110: "note.hat", + 111: "note.snare", + 112: "note.hat", + 113: "note.hat", + 114: "note.hat", + 115: "note.hat", + 116: "note.hat", + 117: "note.chime", + 118: "note.hat", + 119: "note.snare", + 120: "note.hat", + 121: "note.hat", + 122: "note.hat", + 123: "note.hat", + 124: "note.hat", + 125: "note.snare", + 126: "note.basedrum", + 127: "note.basedrum", +} +"""“NBS”乐音乐器对照表""" + + +MM_NBS_PERCUSSION_INSTRUMENT_TABLE: Dict[int, str] = { + 24: "note.bit", + 25: "note.snare", + 26: "note.hat", + 27: "note.snare", + 28: "note.snare", + 29: "note.hat", + 30: "note.hat", + 31: "note.hat", + 32: "note.hat", + 33: "note.hat", + 34: "note.chime", + 35: "note.basedrum", + 36: "note.basedrum", + 37: "note.hat", + 38: "note.snare", + 39: "note.hat", + 40: "note.snare", + 41: "note.basedrum", + 42: "note.snare", + 43: "note.basedrum", + 44: "note.snare", + 45: "note.basedrum", + 46: "note.basedrum", + 47: "note.snare", + 48: "note.snare", + 49: "note.snare", + 50: "note.snare", + 51: "note.snare", + 52: "note.snare", + 53: "note.hat", + 54: "note.snare", + 55: "note.snare", + 56: "note.cow_bell", + 57: "note.snare", + 58: "note.hat", + 59: "note.snare", + 60: "note.hat", + 61: "note.hat", + 62: "note.hat", + 63: "note.basedrum", + 64: "note.basedrum", + 65: "note.snare", + 66: "note.snare", + 67: "note.xylophone", + 68: "note.xylophone", + 69: "note.hat", + 70: "note.hat", + 71: "note.flute", + 72: "note.flute", + 73: "note.hat", + 74: "note.hat", + 75: "note.hat", + 76: "note.hat", + 77: "note.hat", + 78: "note.didgeridoo", + 79: "note.didgeridoo", + 80: "note.hat", + 81: "note.chime", + 82: "note.hat", + 83: "note.chime", + 84: "note.chime", + 85: "note.hat", + 86: "note.basedrum", + 87: "note.basedrum", +} +"""“NBS”打击乐器对照表""" diff --git a/Musicreater/builtin_plugins/midi_read/main.py b/Musicreater/builtin_plugins/midi_read/main.py new file mode 100644 index 0000000..1a7ac85 --- /dev/null +++ b/Musicreater/builtin_plugins/midi_read/main.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- + +""" +音·创 v3 内置的 Midi 读取插件 +""" + +""" +版权所有 © 2026 金羿、玉衡Alioth、偷吃不是Touch +Copyright © 2026 Eilles, YuhengAlioth, Touch + +开源相关声明请见 仓库根目录下的 License.md +Terms & Conditions: License.md in the root directory +""" + +# 睿乐组织 开发交流群 861684859 +# Email TriM-Organization@hotmail.com +# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md + +import mido + +from dataclasses import dataclass +from pathlib import Path +from typing import BinaryIO, Optional, Dict, List + +from Musicreater import SingleMusic +from Musicreater.plugins import ( + music_input_plugin, + PluginConfig, + PluginMetaInformation, + PluginTypes, + MusicInputPluginBase, +) +from Musicreater.types import ( + FittingFunctionType, +) + + +from .constants import ( + MIDI_DEFAULT_PROGRAM_VALUE, + MIDI_DEFAULT_VOLUME_VALUE, + MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE, + MM_TOUCH_PITCHED_INSTRUMENT_TABLE, +) +from .utils import velocity_2_distance_natural, panning_2_rotation_trigonometric + + +@dataclass +class MidiImportConfig(PluginConfig): + """Midi 音乐数据导入插件配置""" + + ignore_mismatch_error: bool = True + speed: float = 1.0 + default_program_value: int = MIDI_DEFAULT_PROGRAM_VALUE + default_volume_value: int = MIDI_DEFAULT_VOLUME_VALUE + default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO + pitched_note_rtable: Dict[int, str] = MM_TOUCH_PITCHED_INSTRUMENT_TABLE + percussion_note_rtable: Dict[int, str] = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE + vol_processing_function: FittingFunctionType = velocity_2_distance_natural + pan_processing_function: FittingFunctionType = panning_2_rotation_trigonometric + note_rtable_replacement: Dict[str, str] = {} + + +@music_input_plugin("midi_2_music_by_tracks") +class MidiImport2MusicPlugin(MusicInputPluginBase): + """Midi 音乐数据导入插件""" + + metainfo = PluginMetaInformation( + name="Midi 导入插件", + author="金羿、玉衡Alioth", + description="从 Midi 文件导入音乐数据", + version=(0, 0, 1), + type=PluginTypes.FUNCTION_MUSIC_IMPORT, + license="Same as Musicreater", + ) + + supported_formats = ("MID", "MIDI") + + def loadbytes( + self, bytes_buffer_in: BinaryIO, config: MidiImportConfig = MidiImportConfig() + ) -> SingleMusic: + midi_file = mido.MidiFile(file=bytes_buffer_in) + return SingleMusic() # =========================== TODO: 等待制作 + + def load( + self, file_path: Path, config: MidiImportConfig = MidiImportConfig() + ) -> "SingleMusic": + """从 Midi 文件导入音乐数据""" + midi_file = mido.MidiFile(filename=file_path) + return SingleMusic() # =========================== TODO: 等待制作 diff --git a/Musicreater/builtin_plugins/midi_read/utils.py b/Musicreater/builtin_plugins/midi_read/utils.py new file mode 100644 index 0000000..f2f9557 --- /dev/null +++ b/Musicreater/builtin_plugins/midi_read/utils.py @@ -0,0 +1,108 @@ + +# -*- coding: utf-8 -*- + +""" +音·创 v3 内置的 Midi 读取插件的功能方法 +""" + +""" +版权所有 © 2026 金羿、玉衡Alioth +Copyright © 2026 Eilles, YuhengAlioth + +开源相关声明请见 仓库根目录下的 License.md +Terms & Conditions: License.md in the root directory +""" + +# 睿乐组织 开发交流群 861684859 +# Email TriM-Organization@hotmail.com +# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md + + +import math + + + +def velocity_2_distance_natural( + vol: float, +) -> float: + """ + midi力度值拟合成的距离函数 + + Parameters + ---------- + vol: int + midi 音符力度值 + + Returns + ------- + float播放中心到玩家的距离 + """ + return ( + -8.081720684086314 + * math.log( + vol + 14.579508825070013, + ) + + 37.65806375944386 + if vol < 60.64 + else 0.2721359356095803 * ((vol + 2592.272889454798) ** 1.358571233418649) + + -6.313841334963396 * (vol + 2592.272889454798) + + 4558.496367823575 + ) + + +def velocity_2_distance_straight(vol: float) -> float: + """ + midi力度值拟合成的距离函数 + + Parameters + ---------- + vol: int + midi 音符力度值 + + Returns + ------- + float播放中心到玩家的距离 + """ + return vol / -8 + 16 + + +def panning_2_rotation_linear(pan_: float) -> float: + """ + Midi 左右平衡偏移值线性转为声源旋转角度 + + Parameters + ---------- + pan_: int + Midi 左右平衡偏移值 + 注:此参数为int,范围从 0 到 127,当为 64 时,声源居中 + + Returns + ------- + float + 声源旋转角度 + """ + return (pan_ - 64) * 90 / 63 + + +def panning_2_rotation_trigonometric(pan_: float) -> float: + """ + Midi 左右平衡偏移值,依照圆的声场定位,转为声源旋转角度 + + Parameters + ---------- + pan_: int + Midi 左右平衡偏移值 + 注:此参数为int,范围从 0 到 127,当为 64 时,声源居中 + + Returns + ------- + float + 声源旋转角度 + """ + if pan_ <= 0: + return -90 + elif pan_ >= 127: + return 90 + else: + return math.degrees(math.acos((64 - pan_) / 63)) - 90 + diff --git a/Musicreater/constants.py b/Musicreater/constants.py index 14450cc..63eae0d 100644 --- a/Musicreater/constants.py +++ b/Musicreater/constants.py @@ -46,13 +46,6 @@ MIDI_PAN = "pan" # Midi用对照表 -MIDI_DEFAULT_VOLUME_VALUE: int = ( - 64 # Midi默认音量,当用户未指定时,默认使用折中默认音量 -) - -MIDI_DEFAULT_PROGRAM_VALUE: int = ( - 74 # 当 Midi 本身与用户皆未指定音色时,默认 Flute 长笛 -) MIDI_PITCH_NAME_TABLE: Dict[int, str] = { 0: "C", @@ -528,770 +521,10 @@ MM_INSTRUMENT_DEVIATION_TABLE: Dict[str, int] = { *注意* 该表中的单位是对于 Midi Pitch 音调(整数)的低音偏移。 也就是说,该数值越高,则在 Midi Pitch 中的值域越低 默认的偏移量为 6 ,因为在计算音高时候少减去了 6 个 Pitch 单位 +(在表里的数据是用作被减数的,实际计算时默认有 +6,所以在表中默认的 6 最后就会被抵消) """ -# Midi乐器对MC乐器对照表 -# “经典”对照表,由 Chalie Ping “查理平” 和 金羿ELS 提供 - -MM_CLASSIC_PITCHED_INSTRUMENT_TABLE: Dict[int, str] = { - 0: "note.harp", - 1: "note.harp", - 2: "note.pling", - 3: "note.harp", - 4: "note.pling", - 5: "note.pling", - 6: "note.harp", - 7: "note.harp", - 8: "note.snare", - 9: "note.harp", - 10: "note.didgeridoo", - 11: "note.harp", - 12: "note.xylophone", - 13: "note.chime", - 14: "note.harp", - 15: "note.harp", - 16: "note.bass", - 17: "note.harp", - 18: "note.harp", - 19: "note.harp", - 20: "note.harp", - 21: "note.harp", - 22: "note.harp", - 23: "note.guitar", - 24: "note.guitar", - 25: "note.guitar", - 26: "note.guitar", - 27: "note.guitar", - 28: "note.guitar", - 29: "note.guitar", - 30: "note.guitar", - 31: "note.bass", - 32: "note.bass", - 33: "note.bass", - 34: "note.bass", - 35: "note.bass", - 36: "note.bass", - 37: "note.bass", - 38: "note.bass", - 39: "note.bass", - 40: "note.harp", - 41: "note.harp", - 42: "note.harp", - 43: "note.harp", - 44: "note.iron_xylophone", - 45: "note.guitar", - 46: "note.harp", - 47: "note.harp", - 48: "note.guitar", - 49: "note.guitar", - 50: "note.bit", - 51: "note.bit", - 52: "note.harp", - 53: "note.harp", - 54: "note.bit", - 55: "note.flute", - 56: "note.flute", - 57: "note.flute", - 58: "note.flute", - 59: "note.flute", - 60: "note.flute", - 61: "note.flute", - 62: "note.flute", - 63: "note.flute", - 64: "note.bit", - 65: "note.bit", - 66: "note.bit", - 67: "note.bit", - 68: "note.flute", - 69: "note.harp", - 70: "note.harp", - 71: "note.flute", - 72: "note.flute", - 73: "note.flute", - 74: "note.harp", - 75: "note.flute", - 76: "note.harp", - 77: "note.harp", - 78: "note.harp", - 79: "note.harp", - 80: "note.bit", - 81: "note.bit", - 82: "note.bit", - 83: "note.bit", - 84: "note.bit", - 85: "note.bit", - 86: "note.bit", - 87: "note.bit", - 88: "note.bit", - 89: "note.bit", - 90: "note.bit", - 91: "note.bit", - 92: "note.bit", - 93: "note.bit", - 94: "note.bit", - 95: "note.bit", - 96: "note.bit", - 97: "note.bit", - 98: "note.bit", - 99: "note.bit", - 100: "note.bit", - 101: "note.bit", - 102: "note.bit", - 103: "note.bit", - 104: "note.harp", - 105: "note.banjo", - 106: "note.harp", - 107: "note.harp", - 108: "note.harp", - 109: "note.harp", - 110: "note.harp", - 111: "note.guitar", - 112: "note.harp", - 113: "note.bell", - 114: "note.harp", - 115: "note.cow_bell", - 116: "note.bd", - 117: "note.bass", - 118: "note.bit", - 119: "note.bd", - 120: "note.guitar", - 121: "note.harp", - 122: "note.harp", - 123: "note.harp", - 124: "note.harp", - 125: "note.hat", - 126: "note.bd", - 127: "note.snare", -} -"""“经典”乐音乐器对照表""" - -MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE: Dict[int, str] = { - 34: "note.bd", - 35: "note.bd", - 36: "note.hat", - 37: "note.snare", - 38: "note.snare", - 39: "note.snare", - 40: "note.hat", - 41: "note.snare", - 42: "note.hat", - 43: "note.snare", - 44: "note.snare", - 45: "note.bell", - 46: "note.snare", - 47: "note.snare", - 48: "note.bell", - 49: "note.hat", - 50: "note.bell", - 51: "note.bell", - 52: "note.bell", - 53: "note.bell", - 54: "note.bell", - 55: "note.bell", - 56: "note.snare", - 57: "note.hat", - 58: "note.chime", - 59: "note.iron_xylophone", - 60: "note.bd", - 61: "note.bd", - 62: "note.xylophone", - 63: "note.xylophone", - 64: "note.xylophone", - 65: "note.hat", - 66: "note.bell", - 67: "note.bell", - 68: "note.hat", - 69: "note.hat", - 70: "note.snare", - 71: "note.flute", - 72: "note.hat", - 73: "note.hat", - 74: "note.xylophone", - 75: "note.hat", - 76: "note.hat", - 77: "note.xylophone", - 78: "note.xylophone", - 79: "note.bell", - 80: "note.bell", -} -"""“经典”打击乐器对照表""" - -# Touch “偷吃” 高准确率音色对照表 - -MM_TOUCH_PITCHED_INSTRUMENT_TABLE: Dict[int, str] = { - 0: "note.harp", - 1: "note.harp", - 2: "note.pling", - 3: "note.harp", - 4: "note.pling", - 5: "note.pling", - 6: "note.guitar", - 7: "note.guitar", - 8: "note.iron_xylophone", - 9: "note.bell", - 10: "note.iron_xylophone", - 11: "note.iron_xylophone", - 12: "note.iron_xylophone", - 13: "note.xylophone", - 14: "note.chime", - 15: "note.banjo", - 16: "note.xylophone", - 17: "note.iron_xylophone", - 18: "note.flute", - 19: "note.flute", - 20: "note.flute", - 21: "note.flute", - 22: "note.flute", - 23: "note.flute", - 24: "note.guitar", - 25: "note.guitar", - 26: "note.guitar", - 27: "note.guitar", - 28: "note.guitar", - 29: "note.guitar", - 30: "note.guitar", - 31: "note.bass", - 32: "note.bass", - 33: "note.bass", - 34: "note.bass", - 35: "note.bass", - 36: "note.bass", - 37: "note.bass", - 38: "note.bass", - 39: "note.bass", - 40: "note.flute", - 41: "note.flute", - 42: "note.flute", - 43: "note.bass", - 44: "note.flute", - 45: "note.iron_xylophone", - 46: "note.harp", - 47: "note.snare", - 48: "note.flute", - 49: "note.flute", - 50: "note.flute", - 51: "note.flute", - 52: "note.didgeridoo", - 53: "note.flute", - 54: "note.flute", - 55: "mob.zombie.wood", - 56: "note.flute", - 57: "note.flute", - 58: "note.flute", - 59: "note.flute", - 60: "note.flute", - 61: "note.flute", - 62: "note.flute", - 63: "note.flute", - 64: "note.bit", - 65: "note.bit", - 66: "note.bit", - 67: "note.bit", - 68: "note.flute", - 69: "note.bit", - 70: "note.banjo", - 71: "note.flute", - 72: "note.flute", - 73: "note.flute", - 74: "note.flute", - 75: "note.flute", - 76: "note.iron_xylophone", - 77: "note.iron_xylophone", - 78: "note.flute", - 79: "note.flute", - 80: "note.bit", - 81: "note.bit", - 82: "note.flute", - 83: "note.flute", - 84: "note.guitar", - 85: "note.flute", - 86: "note.bass", - 87: "note.bass", - 88: "note.bit", - 89: "note.flute", - 90: "note.bit", - 91: "note.flute", - 92: "note.bell", - 93: "note.guitar", - 94: "note.flute", - 95: "note.bit", - 96: "note.bit", - 97: "note.flute", - 98: "note.bell", - 99: "note.bit", - 100: "note.bit", - 101: "note.bit", - 102: "note.bit", - 103: "note.bit", - 104: "note.iron_xylophone", - 105: "note.banjo", - 106: "note.harp", - 107: "note.harp", - 108: "note.bell", - 109: "note.flute", - 110: "note.flute", - 111: "note.flute", - 112: "note.bell", - 113: "note.xylophone", - 114: "note.flute", - 115: "note.hat", - 116: "note.snare", - 117: "note.snare", - 118: "note.bd", - 119: "firework.blast", - 120: "note.guitar", - 121: "note.harp", - 122: "note.harp", - 123: "note.harp", - 124: "note.bit", - 125: "note.hat", - 126: "firework.twinkle", - 127: "mob.zombie.wood", -} -"""“偷吃”乐音乐器对照表""" - -MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE: Dict[int, str] = { - 34: "note.hat", - 35: "note.bd", - 36: "note.bd", - 37: "note.snare", - 38: "note.snare", - 39: "fire.ignite", - 40: "note.snare", - 41: "note.hat", - 42: "note.hat", - 43: "firework.blast", - 44: "note.hat", - 45: "note.snare", - 46: "note.snare", - 47: "note.snare", - 48: "note.bell", - 49: "note.hat", - 50: "note.bell", - 51: "note.bell", - 52: "note.bell", - 53: "note.bell", - 54: "note.bell", - 55: "note.bell", - 56: "note.snare", - 57: "note.hat", - 58: "note.chime", - 59: "note.iron_xylophone", - 60: "note.bd", - 61: "note.bd", - 62: "note.xylophone", - 63: "note.xylophone", - 64: "note.xylophone", - 65: "note.hat", - 66: "note.bell", - 67: "note.bell", - 68: "note.hat", - 69: "note.hat", - 70: "note.snare", - 71: "note.flute", - 72: "note.hat", - 73: "note.hat", - 74: "note.xylophone", - 75: "note.hat", - 76: "note.hat", - 77: "note.xylophone", - 78: "note.xylophone", - 79: "note.bell", - 80: "note.bell", -} -"""“偷吃”打击乐器对照表""" - -# Dislink “断联” 音色对照表 -# https://github.com/Dislink/midi2bdx/blob/main/index.html - - -MM_DISLINK_PITCHED_INSTRUMENT_TABLE: Dict[int, str] = { - 0: "note.harp", - 1: "note.harp", - 2: "note.pling", - 3: "note.harp", - 4: "note.harp", - 5: "note.harp", - 6: "note.harp", - 7: "note.harp", - 8: "note.iron_xylophone", - 9: "note.bell", - 10: "note.iron_xylophone", - 11: "note.iron_xylophone", - 12: "note.iron_xylophone", - 13: "note.iron_xylophone", - 14: "note.chime", - 15: "note.iron_xylophone", - 16: "note.harp", - 17: "note.harp", - 18: "note.harp", - 19: "note.harp", - 20: "note.harp", - 21: "note.harp", - 22: "note.harp", - 23: "note.harp", - 24: "note.guitar", - 25: "note.guitar", - 26: "note.guitar", - 27: "note.guitar", - 28: "note.guitar", - 29: "note.guitar", - 30: "note.guitar", - 31: "note.guitar", - 32: "note.bass", - 33: "note.bass", - 34: "note.bass", - 35: "note.bass", - 36: "note.bass", - 37: "note.bass", - 38: "note.bass", - 39: "note.bass", - 40: "note.harp", - 41: "note.flute", - 42: "note.flute", - 43: "note.flute", - 44: "note.flute", - 45: "note.harp", - 46: "note.harp", - 47: "note.harp", - 48: "note.harp", - 49: "note.harp", - 50: "note.harp", - 51: "note.harp", - 52: "note.harp", - 53: "note.harp", - 54: "note.harp", - 55: "note.harp", - 56: "note.harp", - 57: "note.harp", - 58: "note.harp", - 59: "note.harp", - 60: "note.harp", - 61: "note.harp", - 62: "note.harp", - 63: "note.harp", - 64: "note.harp", - 65: "note.harp", - 66: "note.harp", - 67: "note.harp", - 68: "note.harp", - 69: "note.harp", - 70: "note.harp", - 71: "note.harp", - 72: "note.flute", - 73: "note.flute", - 74: "note.flute", - 75: "note.flute", - 76: "note.flute", - 77: "note.flute", - 78: "note.flute", - 79: "note.flute", - 80: "note.bit", - 81: "note.bit", - 82: "note.harp", - 83: "note.harp", - 84: "note.harp", - 85: "note.harp", - 86: "note.harp", - 87: "note.harp", - 88: "note.harp", - 89: "note.harp", - 90: "note.harp", - 91: "note.harp", - 92: "note.harp", - 93: "note.harp", - 94: "note.harp", - 95: "note.harp", - 96: "note.harp", - 97: "note.harp", - 98: "note.harp", - 99: "note.harp", - 100: "note.harp", - 101: "note.harp", - 102: "note.harp", - 103: "note.harp", - 104: "note.harp", - 105: "note.banjo", - 106: "note.harp", - 107: "note.harp", - 108: "note.harp", - 109: "note.harp", - 110: "note.harp", - 111: "note.harp", - 112: "note.cow_bell", - 113: "note.harp", - 114: "note.harp", - 115: "note.bd", - 116: "note.bd", - 117: "note.bd", - 118: "note.bd", - 119: "note.harp", - 120: "note.harp", - 121: "note.harp", - 122: "note.harp", - 123: "note.harp", - 124: "note.harp", - 125: "note.harp", - 126: "note.harp", - 127: "note.harp", -} -"""“断联”乐音乐器对照表""" - -MM_DISLINK_PERCUSSION_INSTRUMENT_TABLE: Dict[int, str] = { - 34: "note.bd", - 35: "note.bd", - 36: "note.snare", - 37: "note.snare", - 38: "note.bd", - 39: "note.snare", - 40: "note.bd", - 41: "note.hat", - 42: "note.bd", - 43: "note.hat", - 44: "note.bd", - 45: "note.hat", - 46: "note.bd", - 47: "note.bd", - 48: "note.bd", - 49: "note.bd", - 50: "note.bd", - 51: "note.bd", - 52: "note.bd", - 53: "note.bd", - 54: "note.bd", - 55: "note.cow_bell", - 56: "note.bd", - 57: "note.bd", - 58: "note.bd", - 59: "note.bd", - 60: "note.bd", - 61: "note.bd", - 62: "note.bd", - 63: "note.bd", - 64: "note.bd", - 65: "note.bd", - 66: "note.bd", - 67: "note.bd", - 68: "note.bd", - 69: "note.bd", - 70: "note.bd", - 71: "note.bd", - 72: "note.bd", - 73: "note.bd", - 74: "note.bd", - 75: "note.bd", - 76: "note.bd", - 77: "note.bd", - 78: "note.bd", - 79: "note.bd", - 80: "note.bd", -} -"""“断联”打击乐器对照表""" - -# NoteBlockStudio “NBS”音色对照表 -# https://github.com/OpenNBS/NoteBlockStudio/blob/main/scripts/midi_instruments/midi_instruments.gml - -MM_NBS_PITCHED_INSTRUMENT_TABLE: Dict[int, str] = { - 0: "note.harp", - 1: "note.pling", - 2: "note.harp", - 3: "note.pling", - 4: "note.harp", - 5: "note.harp", - 6: "note.guitar", - 7: "note.banjo", - 8: "note.bell", - 9: "note.bell", - 10: "note.bell", - 11: "note.iron_xylophone", - 12: "note.iron_xylophone", - 13: "note.xylophone", - 14: "note.bell", - 15: "note.iron_xylophone", - 16: "note.flute", - 17: "note.flute", - 18: "note.flute", - 19: "note.flute", - 20: "note.flute", - 21: "note.flute", - 22: "note.flute", - 23: "note.flute", - 24: "note.guitar", - 25: "note.guitar", - 26: "note.guitar", - 27: "note.bass", - 28: "note.guitar", - 29: "note.guitar", - 30: "note.bass", - 31: "note.bass", - 32: "note.bass", - 33: "note.guitar", - 34: "note.guitar", - 35: "note.bass", - 36: "note.pling", - 37: "note.flute", - 38: "note.flute", - 39: "note.flute", - 40: "note.flute", - 41: "note.flute", - 42: "note.didgeridoo", - 43: "note.flute", - 44: "note.didgeridoo", - 45: "note.flute", - 46: "note.flute", - 47: "note.flute", - 48: "note.flute", - 49: "note.flute", - 50: "note.flute", - 51: "note.flute", - 52: "note.flute", - 53: "note.flute", - 54: "note.flute", - 55: "note.flute", - 56: "note.flute", - 57: "note.flute", - 58: "note.flute", - 59: "note.flute", - 60: "note.bit", - 61: "note.flute", - 62: "note.flute", - 63: "note.flute", - 64: "note.flute", - 65: "note.guitar", - 66: "note.flute", - 67: "note.flute", - 68: "note.flute", - 69: "note.bell", - 70: "note.flute", - 71: "note.flute", - 72: "note.flute", - 73: "note.flute", - 74: "note.chime", - 75: "note.flute", - 76: "note.flute", - 77: "note.guitar", - 78: "note.pling", - 79: "note.flute", - 80: "note.guitar", - 81: "note.banjo", - 82: "note.banjo", - 83: "note.banjo", - 84: "note.guitar", - 85: "note.iron_xylophone", - 86: "note.flute", - 87: "note.flute", - 88: "note.chime", - 89: "note.cow_bell", - 90: "note.iron_xylophone", - 91: "note.xylophone", - 92: "note.basedrum", - 93: "note.snare", - 94: "note.snare", - 95: "note.basedrum", - 96: "note.snare", - 97: "note.hat", - 98: "note.snare", - 99: "note.hat", - 100: "note.basedrum", - 101: "note.hat", - 102: "note.basedrum", - 103: "note.hat", - 104: "note.basedrum", - 105: "note.snare", - 106: "note.snare", - 107: "note.snare", - 108: "note.cow_bell", - 109: "note.snare", - 110: "note.hat", - 111: "note.snare", - 112: "note.hat", - 113: "note.hat", - 114: "note.hat", - 115: "note.hat", - 116: "note.hat", - 117: "note.chime", - 118: "note.hat", - 119: "note.snare", - 120: "note.hat", - 121: "note.hat", - 122: "note.hat", - 123: "note.hat", - 124: "note.hat", - 125: "note.snare", - 126: "note.basedrum", - 127: "note.basedrum", -} -"""“NBS”乐音乐器对照表""" - - -MM_NBS_PERCUSSION_INSTRUMENT_TABLE: Dict[int, str] = { - 24: "note.bit", - 25: "note.snare", - 26: "note.hat", - 27: "note.snare", - 28: "note.snare", - 29: "note.hat", - 30: "note.hat", - 31: "note.hat", - 32: "note.hat", - 33: "note.hat", - 34: "note.chime", - 35: "note.basedrum", - 36: "note.basedrum", - 37: "note.hat", - 38: "note.snare", - 39: "note.hat", - 40: "note.snare", - 41: "note.basedrum", - 42: "note.snare", - 43: "note.basedrum", - 44: "note.snare", - 45: "note.basedrum", - 46: "note.basedrum", - 47: "note.snare", - 48: "note.snare", - 49: "note.snare", - 50: "note.snare", - 51: "note.snare", - 52: "note.snare", - 53: "note.hat", - 54: "note.snare", - 55: "note.snare", - 56: "note.cow_bell", - 57: "note.snare", - 58: "note.hat", - 59: "note.snare", - 60: "note.hat", - 61: "note.hat", - 62: "note.hat", - 63: "note.basedrum", - 64: "note.basedrum", - 65: "note.snare", - 66: "note.snare", - 67: "note.xylophone", - 68: "note.xylophone", - 69: "note.hat", - 70: "note.hat", - 71: "note.flute", - 72: "note.flute", - 73: "note.hat", - 74: "note.hat", - 75: "note.hat", - 76: "note.hat", - 77: "note.hat", - 78: "note.didgeridoo", - 79: "note.didgeridoo", - 80: "note.hat", - 81: "note.chime", - 82: "note.hat", - 83: "note.chime", - 84: "note.chime", - 85: "note.hat", - 86: "note.basedrum", - 87: "note.basedrum", -} -"""“NBS”打击乐器对照表""" # Midi音高对MC方块对照表 diff --git a/Musicreater/data.py b/Musicreater/data.py index d027f71..8b6be8c 100644 --- a/Musicreater/data.py +++ b/Musicreater/data.py @@ -222,11 +222,10 @@ class SingleNote: ) ) raise SingleNoteDecodeError( - e, "技术信息:\nGROUP1\t`{}`\nCODE_BUFFER\t`{}`".format( group_1, code_buffer ), - ) + ) from e def encode(self, is_high_time_precision: bool = True) -> bytes: """ @@ -621,7 +620,7 @@ class SingleMusic(List[SingleTrack]): music_credits: str """曲目的版权信息""" - # 感叹一下什么交冗余设计啊!(叉腰) + # 感叹一下什么叫冗余设计啊!(叉腰) extra_info: Dict[str, Any] """这还得放东西?""" diff --git a/Musicreater/exceptions.py b/Musicreater/exceptions.py index eefb026..bca8663 100644 --- a/Musicreater/exceptions.py +++ b/Musicreater/exceptions.py @@ -241,7 +241,6 @@ class PluginRegisteredError(PluginLoadError): super().__init__("插件重复注册:", *args) - class PluginConfigRelatedError(MusicreaterOuterlyError): """插件配置相关错误""" diff --git a/Musicreater/main.py b/Musicreater/main.py index 3445503..1ec2b6a 100644 --- a/Musicreater/main.py +++ b/Musicreater/main.py @@ -257,13 +257,11 @@ class MusiCreater: self._plugin_cache[plg_id] = self._plugin_cache[plugin_name] return self._plugin_cache[plg_id] - closest = self._get_closest_plugin_id(plg_id) - raise AttributeError( "插件`{}`不存在,请检查插件的惟一识别码是否正确".format(plg_id) + ( ";或者阁下可能想要使用的是`{}`插件?".format(closest) - if closest + if (closest := self._get_closest_plugin_id(plg_id)) else "" ) ) diff --git a/Musicreater/plugins.py b/Musicreater/plugins.py index b6bb34a..71874e2 100644 --- a/Musicreater/plugins.py +++ b/Musicreater/plugins.py @@ -60,6 +60,7 @@ from .exceptions import ( ParameterTypeError, PluginInstanceNotFoundError, PluginRegisteredError, + PluginNotFoundError, ) @@ -119,16 +120,19 @@ def load_plugin_module(package: Union[Path, str]): 插件包路径或名称,当为 Path 类时为路径,为 str 时为包名,切勿混淆。 """ - if isinstance(package, Path): - relative_path = package.resolve().relative_to(Path.cwd().resolve()) - if relative_path.stem == "__init__": - return importlib.import_module(".".join(relative_path.parts[:-1])) + try: + if isinstance(package, Path): + relative_path = package.resolve().relative_to(Path.cwd().resolve()) + if relative_path.stem == "__init__": + return importlib.import_module(".".join(relative_path.parts[:-1])) + else: + return importlib.import_module( + ".".join(relative_path.parts[:-1] + (relative_path.stem,)) + ) else: - return importlib.import_module( - ".".join(relative_path.parts[:-1] + (relative_path.stem,)) - ) - else: - return importlib.import_module(package) + return importlib.import_module(package) + except ModuleNotFoundError as e: + raise PluginNotFoundError("无法找到名为`{}`的插件包".format(package)) class PluginRegistry: @@ -278,10 +282,10 @@ class PluginRegistry: ], key=lambda plugin: plugin.metainfo.version, ) - except ValueError: + except ValueError as e: raise PluginInstanceNotFoundError( "未找到“用于{}、名为`{}`”的插件".format(plugin_usage, plugin_name) - ) + ) from e def get_music_input_plugin(self, plugin_name: str) -> MusicInputPluginBase: """获取指定名称的全曲导入用插件,当名称重叠时,取版本号最大的""" @@ -389,6 +393,7 @@ def music_operate_plugin(plugin_id: str): plugin_id, _global_plugin_registry.register_music_operate_plugin ) + def track_operate_plugin(plugin_id: str): """音轨处理插件装饰器""" return __plugin_regist_decorator( @@ -416,6 +421,7 @@ def service_plugin(plugin_id: str): plugin_id, _global_plugin_registry.register_service_plugin ) + def library_plugin(plugin_id: str): """支持库插件装饰器""" diff --git a/old-things/Musicreater/old_main.py b/old-things/Musicreater/old_main.py index 24f18fc..0491553 100644 --- a/old-things/Musicreater/old_main.py +++ b/old-things/Musicreater/old_main.py @@ -36,12 +36,15 @@ from itertools import chain import mido -from .constants import * +from Musicreater.constants import * from .old_exceptions import * from .subclass import * from .old_types import * from .utils import * +from Musicreater.builtin_plugins.midi_read.constants import * +from Musicreater.builtin_plugins.midi_read.utils import * + """ 学习笔记: tempo: microseconds per quarter note 毫秒每四分音符,换句话说就是一拍占多少微秒 @@ -796,7 +799,7 @@ class MusicSequence: def add_note(self, channel_no: int, note: MineNote, is_sort: bool = True): """ 在指定通道添加一个音符 - 值得注意:在版本 2.2.3 及之前 is_sort 参数默认为 False ;在此之后为 True + 值得注意:在版本 2.2.3 及之前 is_sort 参数默认为 False;在此之后为 True """ self.channels[channel_no].append(note) self.total_note_count += 1 diff --git a/old-things/Musicreater/utils.py b/old-things/Musicreater/utils.py index da39d25..f59d434 100644 --- a/old-things/Musicreater/utils.py +++ b/old-things/Musicreater/utils.py @@ -15,7 +15,6 @@ Terms & Conditions: License.md in the root directory # Email TriM-Organization@hotmail.com # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md -import math import random # from io import BytesIO @@ -34,7 +33,7 @@ from typing import ( from xxhash import xxh3_64, xxh3_128, xxh32 -from .constants import ( +from Musicreater.constants import ( MC_INSTRUMENT_BLOCKS_TABLE, MC_PITCHED_INSTRUMENT_LIST, MM_INSTRUMENT_DEVIATION_TABLE, @@ -127,90 +126,6 @@ def midi_inst_to_mc_sound( ) -def velocity_2_distance_natural( - vol: float, -) -> float: - """ - midi力度值拟合成的距离函数 - - Parameters - ---------- - vol: int - midi 音符力度值 - - Returns - ------- - float播放中心到玩家的距离 - """ - return ( - -8.081720684086314 - * math.log( - vol + 14.579508825070013, - ) - + 37.65806375944386 - if vol < 60.64 - else 0.2721359356095803 * ((vol + 2592.272889454798) ** 1.358571233418649) - + -6.313841334963396 * (vol + 2592.272889454798) - + 4558.496367823575 - ) - - -def velocity_2_distance_straight(vol: float) -> float: - """ - midi力度值拟合成的距离函数 - - Parameters - ---------- - vol: int - midi 音符力度值 - - Returns - ------- - float播放中心到玩家的距离 - """ - return vol / -8 + 16 - - -def panning_2_rotation_linear(pan_: float) -> float: - """ - Midi 左右平衡偏移值线性转为声源旋转角度 - - Parameters - ---------- - pan_: int - Midi 左右平衡偏移值 - 注:此参数为int,范围从 0 到 127,当为 64 时,声源居中 - - Returns - ------- - float - 声源旋转角度 - """ - return (pan_ - 64) * 90 / 63 - - -def panning_2_rotation_trigonometric(pan_: float) -> float: - """ - Midi 左右平衡偏移值,依照圆的声场定位,转为声源旋转角度 - - Parameters - ---------- - pan_: int - Midi 左右平衡偏移值 - 注:此参数为int,范围从 0 到 127,当为 64 时,声源居中 - - Returns - ------- - float - 声源旋转角度 - """ - if pan_ <= 0: - return -90 - elif pan_ >= 127: - return 90 - else: - return math.degrees(math.acos((64 - pan_) / 63)) - 90 - def minenote_to_command_parameters( mine_note: MineNote, diff --git a/pyproject.toml b/pyproject.toml index bfcb4b0..96d4bd1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,10 +3,9 @@ dynamic = ["version"] requires-python = ">= 3.8, < 4.0" dependencies = [ - "mido >= 1.3", - "tomli >= 2.4.0; python_version < '3.11'", - "tomli-w >= 1.0.0", - "xxhash >= 3", + "tomli >= 2.4.0, < 3.0 ; python_version < '3.11'", + "tomli-w >= 1.0.0, < 2.0", + "xxhash >= 3, < 4.0", ] authors = [ @@ -46,14 +45,23 @@ [project.optional-dependencies] - full = [ - "TrimMCStruct <= 0.0.5.9", - "brotli >= 1.0.0", + midi = [ + "mido >= 1.3, < 2.0", + ] + structure = [ "numpy", + "TrimMCStruct <= 0.0.5.9", + "brotli >= 1.0.0, < 2.0", + ] + full = [ + "mido >= 1.3", + "numpy", + "TrimMCStruct <= 0.0.5.9", + "brotli >= 1.0.0, < 2.0", ] dev = [ "TrimMCStruct <= 0.0.5.9", - "brotli >= 1.0.0", + "brotli >= 1.0.0, < 2.0", "dill", "rich", "pyinstaller", diff --git a/test_read.py b/test_read.py index 71d34eb..493866a 100644 --- a/test_read.py +++ b/test_read.py @@ -1,4 +1,3 @@ - # 一个简单的项目实践测试 from pathlib import Path from Musicreater import load_plugin_module, MusiCreater @@ -6,19 +5,31 @@ from Musicreater.plugins import _global_plugin_registry load_plugin_module("Musicreater.builtin_plugins.midi_read") +from Musicreater.builtin_plugins.midi_read import MidiImportConfig + print("当前支持的导入格式:", _global_plugin_registry.supported_input_formats()) print("当前支持的导出格式:", _global_plugin_registry.supported_output_formats()) -print(msct:=MusiCreater.import_music(Path("./resources/测试片段.mid"))) +print(msct := MusiCreater.import_music(Path("./resources/测试片段.mid"))) print(msct.music) -# 为了让类型检查器满意,以下方法不建议使用,因为这本质上是越过了 MusiCreater 类而直接执行插件的函数 +# 为了确保类型安全,以下方法不建议使用,因为这本质上是越过了 MusiCreater 类而直接执行插件的函数 print(t := msct.midi_2_music_plugin.load(Path("./resources/测试片段.mid"), None)) # 我们建议用这种方式来代替 -t = _global_plugin_registry._music_input_plugins["midi_2_music_plugin"].load(Path("./resources/测试片段.mid"), None) +t = _global_plugin_registry._music_input_plugins["midi_2_music_plugin"].load( + Path("./resources/测试片段.mid"), + MidiImportConfig( + speed=1.0, + ), +) +# 或者 +from Musicreater.plugins import MusicInputPluginBase + +if isinstance((p := msct.midi_2_music_plugin), MusicInputPluginBase): + # 但是,我们不建议用这样的方式读取操作配置类,尽管这也是可以的 + t = p.load(Path("./resources/测试片段.mid"), None) print(_global_plugin_registry) print(msct._plugin_cache) - diff --git a/uv.lock b/uv.lock index 61e308d..003b1ef 100644 --- a/uv.lock +++ b/uv.lock @@ -577,8 +577,7 @@ wheels = [ name = "musicreater" source = { editable = "." } dependencies = [ - { name = "mido" }, - { name = "tomli" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "tomli-w", version = "1.0.0", source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }, marker = "python_full_version < '3.9'" }, { name = "tomli-w", version = "1.2.0", source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }, marker = "python_full_version >= '3.9'" }, { name = "xxhash" }, @@ -594,6 +593,17 @@ dev = [ { name = "twine" }, ] full = [ + { name = "brotli" }, + { name = "mido" }, + { name = "numpy", version = "1.24.4", source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }, marker = "python_full_version < '3.9'" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }, marker = "python_full_version == '3.9.*'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "trimmcstruct" }, +] +midi = [ + { name = "mido" }, +] +structure = [ { name = "brotli" }, { name = "numpy", version = "1.24.4", source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }, marker = "python_full_version < '3.9'" }, { name = "numpy", version = "2.0.2", source = { registry = "https://mirror.nju.edu.cn/pypi/web/simple" }, marker = "python_full_version == '3.9.*'" }, @@ -605,19 +615,23 @@ full = [ requires-dist = [ { name = "brotli", marker = "extra == 'dev'", specifier = ">=1.0.0" }, { name = "brotli", marker = "extra == 'full'", specifier = ">=1.0.0" }, + { name = "brotli", marker = "extra == 'structure'", specifier = ">=1.0.0" }, { name = "dill", marker = "extra == 'dev'" }, - { name = "mido", specifier = ">=1.3" }, + { name = "mido", marker = "extra == 'full'", specifier = ">=1.3" }, + { name = "mido", marker = "extra == 'midi'", specifier = ">=1.3" }, { name = "numpy", marker = "extra == 'full'" }, + { name = "numpy", marker = "extra == 'structure'" }, { name = "pyinstaller", marker = "extra == 'dev'" }, { name = "rich", marker = "extra == 'dev'" }, - { name = "tomli" }, - { name = "tomli-w" }, + { name = "tomli", marker = "python_full_version < '3.11'", specifier = ">=2.4.0,<3.0" }, + { name = "tomli-w", specifier = ">=1.0.0,<2.0" }, { name = "trimmcstruct", marker = "extra == 'dev'", specifier = "<=0.0.5.9" }, { name = "trimmcstruct", marker = "extra == 'full'", specifier = "<=0.0.5.9" }, + { name = "trimmcstruct", marker = "extra == 'structure'", specifier = "<=0.0.5.9" }, { name = "twine", marker = "extra == 'dev'" }, { name = "xxhash", specifier = ">=3" }, ] -provides-extras = ["full", "dev"] +provides-extras = ["midi", "structure", "full", "dev"] [[package]] name = "mutf8"