完整使用流程已经测试一遍了,完整流程是没问题的,接下来就是每个节点的那些小功能可能会有一些没有测试到的地方。这些功能虽然细枝末节,但也都举足轻重,应当在开发出了伶伦工作站的时候测试。所以目前的开发重心转移到伶伦工作站上,相关插件从 v2 到 v3 的移植,交由其他人来处理。

This commit is contained in:
Eilles
2026-03-13 22:56:51 +08:00
parent 307feb9b24
commit ba7b10a25f
18 changed files with 296 additions and 70 deletions

View File

@@ -15,3 +15,11 @@ Terms & Conditions: License.md in the root directory
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from .main import McstructureExportConfig, NoteDataConvert2CommandPlugin
__all__ = [
"McstructureExportConfig",
"NoteDataConvert2CommandPlugin",
]

View File

@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
"""
存放有关BDX结构操作的内容
音·创 v3 内置的 Minecraft 结构生成插件中有关 BDX 结构操作的内容
"""
"""
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
Copyright © 2025 Eilles & bgArray
版权所有 © 2026 金羿、玉衡Alioth
Copyright © 2026 Eilles, YuhengAlioth
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
@@ -15,6 +15,7 @@ Terms & Conditions: License.md in the root directory
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from typing import List
from Musicreater.builtin_plugins.to_commands import MineCommand

View File

@@ -4,8 +4,8 @@
"""
"""
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
Copyright © 2025 Eilles & bgArray
版权所有 © 2026 金羿、玉衡Alioth
Copyright © 2026 Eilles, YuhengAlioth
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory

View File

@@ -0,0 +1,148 @@
# -*- coding: utf-8 -*-
"""
音·创 v3 内置的 Minecraft 结构生成插件
"""
"""
版权所有 © 2026 金羿、玉衡Alioth
Copyright © 2026 Eilles, YuhengAlioth
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
"""
# 睿乐组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from io import BytesIO
from typing import BinaryIO, Optional, Iterator, Generator, Any, Tuple
from pathlib import Path
from dataclasses import dataclass
from TrimMCStruct import Structure
from Musicreater import SingleMusic, SingleTrack
from Musicreater.plugins import (
PluginConfig,
PluginMetaInformation,
PluginTypes,
music_output_plugin,
MusicOutputPluginBase,
track_output_plugin,
TrackOutputPluginBase,
)
from Musicreater.builtin_plugins.to_commands import (
MineCommand,
NoteDataConvert2CommandPlugin,
)
from .mcstructure import (
COMPABILITY_VERSION_117,
COMPABILITY_VERSION_119,
commands_to_structure,
commands_to_redstone_delay_structure,
)
@dataclass
class McstructureExportConfig(PluginConfig):
"""
导出 MCSTRUCTURE 结构插件的配置类
"""
music_deviation: float = 0
"""
全曲音调偏移调整,单位为 Midi Pitch
"""
minimum_volume: float = 0.01
"""
指令参数:最小音量
"""
player_selector: str = "@a"
"""
玩家选择器
"""
max_height: int = 64
"""
生成结构的最大高度
"""
enable_old_execute_format: bool = False
"""
是否使用旧版指令格式
"""
@property
def execute_command_head(self) -> str:
return (
"execute {} ~ ~ ~ "
if self.enable_old_execute_format
else "execute as {} at @s positioned ~ ~ ~ run "
)
@music_output_plugin("music_to_mcstructure_in_delay_plugin")
class MusicExportToMcstructureWithDelayPlayerPlugin(MusicOutputPluginBase):
metainfo = PluginMetaInformation(
name="导出全曲结构插件mcstructure结构、延迟播放器",
author="金羿、玉衡Alioth",
description="将音·创 v3 的整首音乐数据,以指令方块延迟的播放形式,导出为基岩版 MCSTRUCTURE 结构",
version=(0, 0, 1),
type=PluginTypes.FUNCTION_MUSIC_EXPORT,
license="Same as Musicreater",
dependencies=("notedata_to_command_plugin",),
)
supported_formats = ("MCSTRUCTURE",)
@staticmethod
def _go_convertion(
data: SingleMusic, config: McstructureExportConfig
) -> Tuple[Structure, Tuple[int, int, int], int]:
command_list, max_delay = (
NoteDataConvert2CommandPlugin.to_command_list_in_delay(
music=data,
music_deviation=config.music_deviation,
minimum_volume=config.minimum_volume,
player_selector=config.player_selector,
execute_command_head=config.execute_command_head,
)[:2]
)
struct, size, end_pos = commands_to_structure(
command_list,
config.max_height - 1,
compability_version_=(
COMPABILITY_VERSION_117
if config.enable_old_execute_format
else COMPABILITY_VERSION_119
),
)
return struct, size, max_delay
def dump(self, data: SingleMusic, file_path: Path, config: McstructureExportConfig):
struct, size, max_delay = self._go_convertion(data, config)
file_path.parent.mkdir(parents=True, exist_ok=True)
with file_path.open("wb") as f:
struct.dump(f)
return size, max_delay
def stream_dump(
self, data: SingleMusic, config: McstructureExportConfig
) -> Iterator[bytes]:
struct, size, max_delay = self._go_convertion(data, config)
b_out = BytesIO()
struct.dump(b_out)
b_out.seek(0)
yield from b_out

View File

@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
"""
存放有关MCSTRUCTURE结构操作的内容
音·创 v3 内置的 Minecraft 结构生成插件中有关 MCSTRUCTURE 结构操作的内容
"""
"""
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
Copyright © 2025 Eilles & bgArray
版权所有 © 2026 金羿、玉衡Alioth
Copyright © 2026 Eilles, YuhengAlioth
开源相关声明请见 仓库根目录下的 License.md
Terms & Conditions: License.md in the root directory
@@ -25,10 +25,17 @@ from .common import bottem_side_length_of_smallest_square_bottom_box, x, y, z
def antiaxis(axis: Literal["x", "z", "X", "Z"]):
"""
在 x-z 平面上,返回指定轴的另一个轴
"""
return z if axis == x else x
def forward_IER(forward: bool):
"""
把用逻辑值标记的方向值缓存正负数,用以乘上增量
"""
return 1 if forward else -1
@@ -46,6 +53,9 @@ AXIS_PARTICULAR_VALUE = {
False: 2,
},
}
"""
指令方块朝向对应特殊值
"""
# 1.19的结构兼容版本号
COMPABILITY_VERSION_119: int = 17959425
@@ -278,12 +288,12 @@ def commands_to_structure(
结构类, 结构占用大小, 终点坐标
"""
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
_side_length = bottem_side_length_of_smallest_square_bottom_box(
len(commands), max_height
)
struct = Structure(
size=(_sideLength, max_height, _sideLength), # 声明结构大小
size=(_side_length, max_height, _side_length), # 声明结构大小
compability_version=compability_version_,
)
@@ -311,7 +321,7 @@ def commands_to_structure(
(3 if z_forward else 2)
if (
((now_z != 0) and (not z_forward))
or (z_forward and (now_z != _sideLength - 1))
or (z_forward and (now_z != _side_length - 1))
)
else 5
)
@@ -336,7 +346,7 @@ def commands_to_structure(
now_z += 1 if z_forward else -1
if ((now_z >= _sideLength) and z_forward) or (
if ((now_z >= _side_length) and z_forward) or (
(now_z < 0) and (not z_forward)
):
now_z -= 1 if z_forward else -1
@@ -348,7 +358,7 @@ def commands_to_structure(
(
now_x + 1,
max_height if now_x or now_z else now_y,
_sideLength if now_x else now_z,
_side_length if now_x else now_z,
),
(now_x, now_y, now_z),
)

View File

@@ -125,7 +125,7 @@ class TrackDivisionDict(
):
"""
音轨分轨字典
键为音轨信息元组[音轨编号, 通道编号, 音符名称, 音量, 声相]
键为音轨信息元组[音轨编号, 通道编号, 乐器名称, 音量, 声相]
值为音轨对象
"""
@@ -172,7 +172,7 @@ class TrackDivisionDict(
return self[key]
@music_input_plugin("midi_2_music_plugin")
@music_input_plugin("midi_to_music_plugin")
class MidiImport2MusicPlugin(MusicInputPluginBase):
"""Midi 音乐数据导入插件"""
@@ -193,7 +193,7 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
config: Optional[MidiImportConfig] = MidiImportConfig(),
) -> SingleMusic:
return self.midifile_2_singlemusic(
mido.MidiFile(file=bytes_buffer_in),
mido.MidiFile(file=bytes_buffer_in, clip=True),
config if config else MidiImportConfig(),
)
@@ -202,7 +202,8 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
) -> SingleMusic:
"""从 Midi 文件导入音乐数据"""
return self.midifile_2_singlemusic(
mido.MidiFile(filename=file_path), config if config else MidiImportConfig()
mido.MidiFile(filename=file_path, clip=True),
config if config else MidiImportConfig(),
)
@staticmethod
@@ -264,17 +265,15 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
"""音符计数"""
note_count_per_instrument: Dict[str, int] = {}
"""乐器使用统计"""
microseconds = 0
"""当前的微妙时间"""
note_queue_A: Dict[int, List[Tuple[int, int]]] = (
enumerated_stuffcopy_dictionary(staff=[])
)
"""音符队列甲 Dict[通道, List[Tuple[int音高, int乐器, int轨道]]]"""
note_queue_B: Dict[int, List[Tuple[int, int, int]]] = (
"""音符队列甲 Dict[通道, List[Tuple[int音高, int轨道]]]"""
note_queue_B: Dict[int, List[Tuple[int, int, int, int, int]]] = (
enumerated_stuffcopy_dictionary(staff=[])
)
"""音符队列乙 Dict[通道, List[Tuple[int音高, int微秒时间]]]"""
"""音符队列乙 Dict[通道, List[Tuple[int力度, int乐器, int音量, int偏移, int微秒时间]]]"""
midi_lyric_cache: List[Tuple[int, str]] = []
"""歌词缓存 List[Tuple[int微秒时间, str歌词内容]]"""
@@ -287,8 +286,14 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
"""轨道名称字典 Dict[int轨道编号, str轨道名称]"""
for track_no, message_track in enumerate(midi.tracks):
# 每个音轨单独重置
microseconds = 0
"""当前的微妙时间"""
for msg in message_track:
if msg.type == "set_tempo":
# Tempo 改变是一个全局的控制
# 而且应该是很早出现的一个 Midi 消息
midi_tempo = msg.tempo
if msg.time != 0:
@@ -304,6 +309,7 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
elif msg.is_cc(7):
# Control Change 更改当前通道的 音量 的事件(大幅度,最高有效位)
# print("通道、轨道、音量修改:",msg.channel, track_no, msg.value)
value_controler_per_channel[msg.channel][
ControlerKeys.MIDI_VOLUME
] = msg.value
@@ -333,13 +339,19 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
# (音高, 轨道)
note_queue_A[msg.channel].append((msg.note, track_no))
# 音符队列乙(按通道分隔)
# (乐器, 力度, 微秒)
# (力度, 乐器, 音量, 偏移, 微秒)
note_queue_B[msg.channel].append(
(
msg.velocity,
value_controler_per_channel[msg.channel][
ControlerKeys.MIDI_PROGRAM
],
msg.velocity,
value_controler_per_channel[msg.channel][
ControlerKeys.MIDI_VOLUME
],
value_controler_per_channel[msg.channel][
ControlerKeys.MIDI_PAN
],
microseconds,
)
)
@@ -351,26 +363,28 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
if (
msg.note,
value_controler_per_channel[msg.channel][
ControlerKeys.MIDI_PROGRAM
],
track_no,
) in note_queue_A[msg.channel]:
# 在甲队列中发现了同一个 音高和乐器且在同轨道 的音符
# 获取其音符力度和微秒数
_velocity, _program, _ms = note_queue_B[msg.channel][
note_queue_A[msg.channel].index((msg.note, track_no))
]
_velocity, _program, _volume, _panning, _start_ms = (
note_queue_B[msg.channel][
note_queue_A[msg.channel].index((msg.note, track_no))
]
)
# 在队列中删除此音符
note_queue_A[msg.channel].remove((msg.note, track_no))
note_queue_B[msg.channel].remove((_velocity, _program, _ms))
note_queue_B[msg.channel].remove(
(_velocity, _program, _volume, _panning, _start_ms)
)
_lyric = ""
# 找一找歌词吧
if midi_lyric_cache:
for i in range(len(midi_lyric_cache)):
if midi_lyric_cache[i][0] >= _ms:
if midi_lyric_cache[i][0] >= _start_ms:
_lyric = midi_lyric_cache.pop(i)[1]
break
@@ -385,15 +399,11 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
),
note=(_program if _is_percussion else msg.note),
percussive=_is_percussion,
volume=value_controler_per_channel[msg.channel][
ControlerKeys.MIDI_VOLUME
],
volume=_volume,
velocity=_velocity,
panning=value_controler_per_channel[msg.channel][
ControlerKeys.MIDI_PAN
],
start_time=_ms, # 微秒
duration=microseconds - _ms, # 微秒
panning=_panning,
start_time=_start_ms, # 微秒
duration=microseconds - _start_ms, # 微秒
play_speed=config.speed_multiplier,
midi_reference_table=(
config.percussion_note_reference_table
@@ -407,6 +417,8 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
)
)
# print(that_note.start_time, end=", ")
divided_tracks[
(
track_no,
@@ -465,15 +477,16 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
},
)
for track_properties, every_single_track in divided_tracks.items():
# [音轨编号, 通道编号, 乐器名称, 音量, 声相]
if track_properties[0] and (
track_name := midi_track_name_dict.get(track_properties[0])
):
): # 音轨编号
every_single_track.name = track_name
if track_properties[2]:
if track_properties[2]: # 乐器名称
every_single_track.instrument = track_properties[2]
if track_properties[3]:
if track_properties[3]: # 音量
every_single_track.sound_position.sound_distance = track_properties[3]
if track_properties[4]:
if track_properties[4]: # 声相
every_single_track.sound_position.sound_azimuth = track_properties[4]
final_music.append(every_single_track)

View File

@@ -204,7 +204,7 @@ def midi_msgs_to_noteinfo(
return (
SingleNote(
note_pitch=note,
note_volume=int((velocity / 127) + 0.5),
note_volume=velocity,
start_tick=(tk := int(start_time / float(play_speed) / 50000)),
keep_tick=round(duration / float(play_speed) / 50000),
mass_precision_time=round(

View File

@@ -94,7 +94,7 @@ class MineCommand:
return False
@library_plugin("notedata_2_command_plugin")
@library_plugin("notedata_to_command_plugin")
class NoteDataConvert2CommandPlugin(LibraryPluginBase):
metainfo = PluginMetaInformation(
name="音符数据指令支持插件",
@@ -481,8 +481,8 @@ class NoteDataConvert2CommandPlugin(LibraryPluginBase):
@staticmethod
def to_command_list_in_score(
music: SingleMusic,
music_deviation: int = 0,
minimum_volume: float = 0,
music_deviation: float = 0,
minimum_volume: float = 0.01,
scoreboard_name: str = "mscplay",
execute_command_head: str = "execute as {} at @s positioned ~ ~ ~ run ",
) -> Tuple[List[List[MineCommand]], int, int]:
@@ -565,8 +565,8 @@ class NoteDataConvert2CommandPlugin(LibraryPluginBase):
@staticmethod
def to_command_list_in_delay(
music: SingleMusic,
music_deviation: int = 0,
minimum_volume: float = 0,
music_deviation: float = 0,
minimum_volume: float = 0.01,
player_selector: str = "@a",
execute_command_head: str = "execute as {} at @s positioned ~ ~ ~ run ",
) -> Tuple[List[MineCommand], int, int]:
@@ -583,7 +583,7 @@ class NoteDataConvert2CommandPlugin(LibraryPluginBase):
tuple( list[MineCommand指令,...], int音乐时长游戏刻, int最大同时播放的指令数量 )
"""
# 此处 我们把通道视为音轨
# 音轨判断
music_command_list = []
multi = max_multi = 0
delaytime_previous = 0
@@ -611,7 +611,7 @@ class NoteDataConvert2CommandPlugin(LibraryPluginBase):
MineCommand(
command=(
execute_command_head.format(player_selector)
+ r"playsound {} @s ^{} ^{} ^{} {} {} {}".format(
+ "playsound {} @s ^{} ^{} ^{} {} {} {}".format(
note.instrument,
*relative_coordinates,
volume_percentage,

View File

@@ -48,7 +48,7 @@ def minenote_to_command_parameters(
----
mine_note: MineNote
我的世界音符对象
deviation: float
pitch_deviation: float
音调偏移量
返回
@@ -59,7 +59,7 @@ def minenote_to_command_parameters(
return (
mine_note.position.position_displacement,
mine_note.volume / 100,
mine_note.volume / 127,
(
None
if mine_note.percussive

View File

@@ -135,7 +135,7 @@ class SingleNote:
"""Midi 音高"""
volume: int
"""力度/播放响度 0~100 百分比"""
"""力度/播放响度 0~127 百廿七分比"""
start_time: int
"""开始之时 命令刻"""
@@ -166,7 +166,7 @@ class SingleNote:
midi_pitch: int
Midi 音高
note_volume: int
响度/力度(百分比, 0~100)
响度/力度(百廿七分比, 0~127)
start_time: int
开始之时(命令刻)
注:此处的时间是用从乐曲开始到当前的刻数
@@ -367,7 +367,7 @@ class MineNote:
instrument: str
"""乐器 ID"""
volume: float
"""力度/播放音量 0~100 百分比"""
"""力度/播放音量 0~127 百廿七分比"""
start_tick: int
"""开始之时 命令刻"""
duration_tick: int
@@ -697,7 +697,8 @@ class SingleMusic(List[SingleTrack]):
归并后的每个元素,按 sort_key 升序
"""
if is_subseq_sorted:
return heapq.merge(*tracks, key=sort_key)
# 必须这样处理,不能 return 这个 merge测试过了
yield from heapq.merge(*tracks, key=sort_key)
else:
# 初始化堆
heap_pool: List[Tuple[Any, int, T]] = []
@@ -765,11 +766,11 @@ class SingleMusic(List[SingleTrack]):
def get_minenotes(
self, start_time: float, end_time: float = inf
) -> Generator[MineNote, Any, None]:
) -> Iterator[MineNote]:
"""获取指定时间段所有的,供我的世界播放的音符数据类,按照时间顺序"""
if self.track_amount == 0:
return
yield from self.yield_from_tracks(
return iter(())
return self.yield_from_tracks(
[track.get_minenotes(start_time, end_time) for track in self.music_tracks],
sort_key=lambda x: x.start_tick,
)

View File

@@ -117,9 +117,16 @@ class MusiCreater:
if __plugin:
return __plugin
else:
raise FileFormatNotSupportedError(
"无法找到处理`{}`类型文件的插件".format(fpath.suffix.upper())
)
if plg_id:
raise PluginNotFoundError(
"无法找到惟一识别码为`{}`、处理`{}`格式的插件".format(
plg_id, fpath.suffix.upper()
)
)
else:
raise FileFormatNotSupportedError(
"无法找到处理`{}`格式的插件".format(fpath.suffix.upper())
)
@classmethod
def import_music(
@@ -159,7 +166,7 @@ class MusiCreater:
plugin_id: Optional[str] = None,
plugin_config: Optional[PluginConfig] = None,
) -> None:
self._get_plugin_within_iousage(
return self._get_plugin_within_iousage(
self.__plugin_registry.get_music_output_plugin_by_format,
file_path,
self.__plugin_registry._music_output_plugins,
@@ -173,7 +180,7 @@ class MusiCreater:
plugin_id: Optional[str] = None,
plugin_config: Optional[PluginConfig] = None,
) -> None:
self._get_plugin_within_iousage(
return self._get_plugin_within_iousage(
self.__plugin_registry.get_track_output_plugin_by_format,
file_path,
self.__plugin_registry._track_output_plugins,

View File

@@ -231,6 +231,7 @@ class PluginRegistry:
if plugin.can_handle_format(fpath_or_format)
)
elif isinstance(fpath_or_format, Path):
# print("在",plugin_regdict,"中,查找可用于处理",fpath_or_format,"的插件")
return (
plugin
for plugin in plugin_regdict.values()

View File

@@ -17,6 +17,7 @@ Email [TriM-Organization@hotmail.com](mailto:TriM-Organization@hotmail.com)
则无需标注原作者允许该使用者自行署名
本声明仅限于包含此声明的本文件本声明与项目内其他文件无关
本声明同样适用于所有直接转载的内容
```
本教程文档的关联文件是

View File

@@ -22,6 +22,7 @@ Copyright © 2026 Eilles
则无需标注原作者,允许该使用者自行署名
本声明仅限于包含此声明的本文件,本声明与项目内其他文件无关。
本声明同样适用于所有直接转载的内容。
"""
from typing import BinaryIO, Optional, Iterator, Generator, Any, Tuple

View File

@@ -22,6 +22,7 @@ Copyright © 2026 Eilles
则无需标注原作者,允许该使用者自行署名
本声明仅限于包含此声明的本文件,本声明与项目内其他文件无关。
本声明同样适用于所有直接转载的内容。
"""
from typing import BinaryIO, Optional

View File

@@ -16,7 +16,7 @@ from typing import Literal
from ...old_main import MidiConvert
from ...subclass import MineCommand
from ..mcstructure import (
from Musicreater.builtin_plugins.commands_to_structure.mcstructure import (
COMPABILITY_VERSION_117,
COMPABILITY_VERSION_119,
commands_to_redstone_delay_structure,

34
test_convert_midi.py Normal file
View File

@@ -0,0 +1,34 @@
# 一个简单的项目实践测试
from pathlib import Path
from Musicreater import load_plugin_module, MusiCreater
from Musicreater.plugins import _global_plugin_registry
load_plugin_module("Musicreater.builtin_plugins.midi_read")
load_plugin_module("Musicreater.builtin_plugins.to_commands")
load_plugin_module("Musicreater.builtin_plugins.commands_to_structure")
from Musicreater.builtin_plugins.midi_read import MidiImportConfig
from Musicreater.builtin_plugins.commands_to_structure import McstructureExportConfig
print("当前支持的导入格式:", _global_plugin_registry.supported_input_formats())
print("当前支持的导出格式:", _global_plugin_registry.supported_output_formats())
msct = MusiCreater.import_music(
Path("./resources/测试片段.mid"), plugin_config=MidiImportConfig()
)
print("全局插件注册表:", _global_plugin_registry)
print("插件缓存字典:", msct._plugin_cache)
print(msct.music.music_name)
print(
"大小、音乐总长:",
msct.export_music(
Path("./output.mcstructure"),
plugin_id="music_to_mcstructure_in_delay_plugin",
plugin_config=McstructureExportConfig(),
),
)

View File

@@ -16,9 +16,9 @@ print(msct.music)
# 如果要直接访问插件里面的函数:
# 为了确保类型安全,以下方法不建议使用,因为这本质上是越过了 MusiCreater 类而直接执行插件的函数
print(t := msct.midi_2_music_plugin.load(Path("./resources/测试片段.mid"), None)) # type: ignore
print(t := msct.midi_to_music_plugin.load(Path("./resources/测试片段.mid"), None)) # type: ignore
# 我们建议用这种方式来代替
t = _global_plugin_registry._music_input_plugins["midi_2_music_plugin"].load(
t = _global_plugin_registry._music_input_plugins["midi_to_music_plugin"].load(
Path("./resources/测试片段.mid"),
MidiImportConfig(
speed_multiplier=1.0,
@@ -27,7 +27,7 @@ t = _global_plugin_registry._music_input_plugins["midi_2_music_plugin"].load(
# 或者
from Musicreater.plugins import MusicInputPluginBase
if isinstance((p := msct.midi_2_music_plugin), MusicInputPluginBase):
if isinstance((p := msct.midi_to_music_plugin), MusicInputPluginBase):
t = p.load(Path("./resources/测试片段.mid"), None)
# 但是说实话,既然已经在 MusiCreater 类中提供了