mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2026-04-17 06:08:00 +00:00
完整使用流程已经测试一遍了,完整流程是没问题的,接下来就是每个节点的那些小功能可能会有一些没有测试到的地方。这些功能虽然细枝末节,但也都举足轻重,应当在开发出了伶伦工作站的时候测试。所以目前的开发重心转移到伶伦工作站上,相关插件从 v2 到 v3 的移植,交由其他人来处理。
This commit is contained in:
@@ -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",
|
||||
]
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -17,6 +17,7 @@ Email [TriM-Organization@hotmail.com](mailto:TriM-Organization@hotmail.com)
|
||||
则无需标注原作者,允许该使用者自行署名
|
||||
|
||||
本声明仅限于包含此声明的本文件,本声明与项目内其他文件无关。
|
||||
本声明同样适用于所有直接转载的内容。
|
||||
```
|
||||
|
||||
本教程文档的关联文件是:
|
||||
|
||||
@@ -22,6 +22,7 @@ Copyright © 2026 Eilles
|
||||
则无需标注原作者,允许该使用者自行署名
|
||||
|
||||
本声明仅限于包含此声明的本文件,本声明与项目内其他文件无关。
|
||||
本声明同样适用于所有直接转载的内容。
|
||||
"""
|
||||
|
||||
from typing import BinaryIO, Optional, Iterator, Generator, Any, Tuple
|
||||
|
||||
@@ -22,6 +22,7 @@ Copyright © 2026 Eilles
|
||||
则无需标注原作者,允许该使用者自行署名
|
||||
|
||||
本声明仅限于包含此声明的本文件,本声明与项目内其他文件无关。
|
||||
本声明同样适用于所有直接转载的内容。
|
||||
"""
|
||||
|
||||
from typing import BinaryIO, Optional
|
||||
|
||||
@@ -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
34
test_convert_midi.py
Normal 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(),
|
||||
),
|
||||
)
|
||||
@@ -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 类中提供了
|
||||
|
||||
Reference in New Issue
Block a user