mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2026-05-01 05:35:58 +00:00
完整使用流程已经测试一遍了,完整流程是没问题的,接下来就是每个节点的那些小功能可能会有一些没有测试到的地方。这些功能虽然细枝末节,但也都举足轻重,应当在开发出了伶伦工作站的时候测试。所以目前的开发重心转移到伶伦工作站上,相关插件从 v2 到 v3 的移植,交由其他人来处理。
This commit is contained in:
@@ -15,3 +15,11 @@ Terms & Conditions: License.md in the root directory
|
|||||||
# 睿乐组织 开发交流群 861684859
|
# 睿乐组织 开发交流群 861684859
|
||||||
# Email TriM-Organization@hotmail.com
|
# Email TriM-Organization@hotmail.com
|
||||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
from .main import McstructureExportConfig, NoteDataConvert2CommandPlugin
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"McstructureExportConfig",
|
||||||
|
"NoteDataConvert2CommandPlugin",
|
||||||
|
]
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
存放有关BDX结构操作的内容
|
音·创 v3 内置的 Minecraft 结构生成插件中有关 BDX 结构操作的内容
|
||||||
"""
|
"""
|
||||||
|
|
||||||
"""
|
"""
|
||||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
版权所有 © 2026 金羿、玉衡Alioth
|
||||||
Copyright © 2025 Eilles & bgArray
|
Copyright © 2026 Eilles, YuhengAlioth
|
||||||
|
|
||||||
开源相关声明请见 仓库根目录下的 License.md
|
开源相关声明请见 仓库根目录下的 License.md
|
||||||
Terms & Conditions: License.md in the root directory
|
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
|
# Email TriM-Organization@hotmail.com
|
||||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from Musicreater.builtin_plugins.to_commands import MineCommand
|
from Musicreater.builtin_plugins.to_commands import MineCommand
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
"""
|
"""
|
||||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
版权所有 © 2026 金羿、玉衡Alioth
|
||||||
Copyright © 2025 Eilles & bgArray
|
Copyright © 2026 Eilles, YuhengAlioth
|
||||||
|
|
||||||
开源相关声明请见 仓库根目录下的 License.md
|
开源相关声明请见 仓库根目录下的 License.md
|
||||||
Terms & Conditions: License.md in the root directory
|
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 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
存放有关MCSTRUCTURE结构操作的内容
|
音·创 v3 内置的 Minecraft 结构生成插件中有关 MCSTRUCTURE 结构操作的内容
|
||||||
"""
|
"""
|
||||||
|
|
||||||
"""
|
"""
|
||||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
版权所有 © 2026 金羿、玉衡Alioth
|
||||||
Copyright © 2025 Eilles & bgArray
|
Copyright © 2026 Eilles, YuhengAlioth
|
||||||
|
|
||||||
开源相关声明请见 仓库根目录下的 License.md
|
开源相关声明请见 仓库根目录下的 License.md
|
||||||
Terms & Conditions: License.md in the root directory
|
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"]):
|
def antiaxis(axis: Literal["x", "z", "X", "Z"]):
|
||||||
|
"""
|
||||||
|
在 x-z 平面上,返回指定轴的另一个轴
|
||||||
|
"""
|
||||||
|
|
||||||
return z if axis == x else x
|
return z if axis == x else x
|
||||||
|
|
||||||
|
|
||||||
def forward_IER(forward: bool):
|
def forward_IER(forward: bool):
|
||||||
|
"""
|
||||||
|
把用逻辑值标记的方向值缓存正负数,用以乘上增量
|
||||||
|
"""
|
||||||
return 1 if forward else -1
|
return 1 if forward else -1
|
||||||
|
|
||||||
|
|
||||||
@@ -46,6 +53,9 @@ AXIS_PARTICULAR_VALUE = {
|
|||||||
False: 2,
|
False: 2,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
"""
|
||||||
|
指令方块朝向对应特殊值
|
||||||
|
"""
|
||||||
|
|
||||||
# 1.19的结构兼容版本号
|
# 1.19的结构兼容版本号
|
||||||
COMPABILITY_VERSION_119: int = 17959425
|
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
|
len(commands), max_height
|
||||||
)
|
)
|
||||||
|
|
||||||
struct = Structure(
|
struct = Structure(
|
||||||
size=(_sideLength, max_height, _sideLength), # 声明结构大小
|
size=(_side_length, max_height, _side_length), # 声明结构大小
|
||||||
compability_version=compability_version_,
|
compability_version=compability_version_,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -311,7 +321,7 @@ def commands_to_structure(
|
|||||||
(3 if z_forward else 2)
|
(3 if z_forward else 2)
|
||||||
if (
|
if (
|
||||||
((now_z != 0) and (not z_forward))
|
((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
|
else 5
|
||||||
)
|
)
|
||||||
@@ -336,7 +346,7 @@ def commands_to_structure(
|
|||||||
|
|
||||||
now_z += 1 if z_forward else -1
|
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 < 0) and (not z_forward)
|
||||||
):
|
):
|
||||||
now_z -= 1 if z_forward else -1
|
now_z -= 1 if z_forward else -1
|
||||||
@@ -348,7 +358,7 @@ def commands_to_structure(
|
|||||||
(
|
(
|
||||||
now_x + 1,
|
now_x + 1,
|
||||||
max_height if now_x or now_z else now_y,
|
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),
|
(now_x, now_y, now_z),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ class TrackDivisionDict(
|
|||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
音轨分轨字典
|
音轨分轨字典
|
||||||
键为音轨信息元组[音轨编号, 通道编号, 音符名称, 音量, 声相]
|
键为音轨信息元组[音轨编号, 通道编号, 乐器名称, 音量, 声相]
|
||||||
值为音轨对象
|
值为音轨对象
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ class TrackDivisionDict(
|
|||||||
return self[key]
|
return self[key]
|
||||||
|
|
||||||
|
|
||||||
@music_input_plugin("midi_2_music_plugin")
|
@music_input_plugin("midi_to_music_plugin")
|
||||||
class MidiImport2MusicPlugin(MusicInputPluginBase):
|
class MidiImport2MusicPlugin(MusicInputPluginBase):
|
||||||
"""Midi 音乐数据导入插件"""
|
"""Midi 音乐数据导入插件"""
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
|
|||||||
config: Optional[MidiImportConfig] = MidiImportConfig(),
|
config: Optional[MidiImportConfig] = MidiImportConfig(),
|
||||||
) -> SingleMusic:
|
) -> SingleMusic:
|
||||||
return self.midifile_2_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(),
|
config if config else MidiImportConfig(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -202,7 +202,8 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
|
|||||||
) -> SingleMusic:
|
) -> SingleMusic:
|
||||||
"""从 Midi 文件导入音乐数据"""
|
"""从 Midi 文件导入音乐数据"""
|
||||||
return self.midifile_2_singlemusic(
|
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
|
@staticmethod
|
||||||
@@ -264,17 +265,15 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
|
|||||||
"""音符计数"""
|
"""音符计数"""
|
||||||
note_count_per_instrument: Dict[str, int] = {}
|
note_count_per_instrument: Dict[str, int] = {}
|
||||||
"""乐器使用统计"""
|
"""乐器使用统计"""
|
||||||
microseconds = 0
|
|
||||||
"""当前的微妙时间"""
|
|
||||||
|
|
||||||
note_queue_A: Dict[int, List[Tuple[int, int]]] = (
|
note_queue_A: Dict[int, List[Tuple[int, int]]] = (
|
||||||
enumerated_stuffcopy_dictionary(staff=[])
|
enumerated_stuffcopy_dictionary(staff=[])
|
||||||
)
|
)
|
||||||
"""音符队列甲 Dict[通道, List[Tuple[int音高, int乐器, int轨道]]]"""
|
"""音符队列甲 Dict[通道, List[Tuple[int音高, int轨道]]]"""
|
||||||
note_queue_B: Dict[int, List[Tuple[int, int, int]]] = (
|
note_queue_B: Dict[int, List[Tuple[int, int, int, int, int]]] = (
|
||||||
enumerated_stuffcopy_dictionary(staff=[])
|
enumerated_stuffcopy_dictionary(staff=[])
|
||||||
)
|
)
|
||||||
"""音符队列乙 Dict[通道, List[Tuple[int音高, int微秒时间]]]"""
|
"""音符队列乙 Dict[通道, List[Tuple[int力度, int乐器, int音量, int偏移, int微秒时间]]]"""
|
||||||
|
|
||||||
midi_lyric_cache: List[Tuple[int, str]] = []
|
midi_lyric_cache: List[Tuple[int, str]] = []
|
||||||
"""歌词缓存 List[Tuple[int微秒时间, str歌词内容]]"""
|
"""歌词缓存 List[Tuple[int微秒时间, str歌词内容]]"""
|
||||||
@@ -287,8 +286,14 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
|
|||||||
"""轨道名称字典 Dict[int轨道编号, str轨道名称]"""
|
"""轨道名称字典 Dict[int轨道编号, str轨道名称]"""
|
||||||
|
|
||||||
for track_no, message_track in enumerate(midi.tracks):
|
for track_no, message_track in enumerate(midi.tracks):
|
||||||
|
# 每个音轨单独重置
|
||||||
|
|
||||||
|
microseconds = 0
|
||||||
|
"""当前的微妙时间"""
|
||||||
for msg in message_track:
|
for msg in message_track:
|
||||||
if msg.type == "set_tempo":
|
if msg.type == "set_tempo":
|
||||||
|
# Tempo 改变是一个全局的控制
|
||||||
|
# 而且应该是很早出现的一个 Midi 消息
|
||||||
midi_tempo = msg.tempo
|
midi_tempo = msg.tempo
|
||||||
|
|
||||||
if msg.time != 0:
|
if msg.time != 0:
|
||||||
@@ -304,6 +309,7 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
|
|||||||
|
|
||||||
elif msg.is_cc(7):
|
elif msg.is_cc(7):
|
||||||
# Control Change 更改当前通道的 音量 的事件(大幅度,最高有效位)
|
# Control Change 更改当前通道的 音量 的事件(大幅度,最高有效位)
|
||||||
|
# print("通道、轨道、音量修改:",msg.channel, track_no, msg.value)
|
||||||
value_controler_per_channel[msg.channel][
|
value_controler_per_channel[msg.channel][
|
||||||
ControlerKeys.MIDI_VOLUME
|
ControlerKeys.MIDI_VOLUME
|
||||||
] = msg.value
|
] = msg.value
|
||||||
@@ -333,13 +339,19 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
|
|||||||
# (音高, 轨道)
|
# (音高, 轨道)
|
||||||
note_queue_A[msg.channel].append((msg.note, track_no))
|
note_queue_A[msg.channel].append((msg.note, track_no))
|
||||||
# 音符队列乙(按通道分隔)
|
# 音符队列乙(按通道分隔)
|
||||||
# (乐器, 力度, 微秒)
|
# (力度, 乐器, 音量, 偏移, 微秒)
|
||||||
note_queue_B[msg.channel].append(
|
note_queue_B[msg.channel].append(
|
||||||
(
|
(
|
||||||
|
msg.velocity,
|
||||||
value_controler_per_channel[msg.channel][
|
value_controler_per_channel[msg.channel][
|
||||||
ControlerKeys.MIDI_PROGRAM
|
ControlerKeys.MIDI_PROGRAM
|
||||||
],
|
],
|
||||||
msg.velocity,
|
value_controler_per_channel[msg.channel][
|
||||||
|
ControlerKeys.MIDI_VOLUME
|
||||||
|
],
|
||||||
|
value_controler_per_channel[msg.channel][
|
||||||
|
ControlerKeys.MIDI_PAN
|
||||||
|
],
|
||||||
microseconds,
|
microseconds,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -351,26 +363,28 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
msg.note,
|
msg.note,
|
||||||
value_controler_per_channel[msg.channel][
|
track_no,
|
||||||
ControlerKeys.MIDI_PROGRAM
|
|
||||||
],
|
|
||||||
) in note_queue_A[msg.channel]:
|
) in note_queue_A[msg.channel]:
|
||||||
# 在甲队列中发现了同一个 音高和乐器且在同轨道 的音符
|
# 在甲队列中发现了同一个 音高和乐器且在同轨道 的音符
|
||||||
|
|
||||||
# 获取其音符力度和微秒数
|
# 获取其音符力度和微秒数
|
||||||
_velocity, _program, _ms = note_queue_B[msg.channel][
|
_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].index((msg.note, track_no))
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# 在队列中删除此音符
|
# 在队列中删除此音符
|
||||||
note_queue_A[msg.channel].remove((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 = ""
|
_lyric = ""
|
||||||
# 找一找歌词吧
|
# 找一找歌词吧
|
||||||
if midi_lyric_cache:
|
if midi_lyric_cache:
|
||||||
for i in range(len(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]
|
_lyric = midi_lyric_cache.pop(i)[1]
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -385,15 +399,11 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
|
|||||||
),
|
),
|
||||||
note=(_program if _is_percussion else msg.note),
|
note=(_program if _is_percussion else msg.note),
|
||||||
percussive=_is_percussion,
|
percussive=_is_percussion,
|
||||||
volume=value_controler_per_channel[msg.channel][
|
volume=_volume,
|
||||||
ControlerKeys.MIDI_VOLUME
|
|
||||||
],
|
|
||||||
velocity=_velocity,
|
velocity=_velocity,
|
||||||
panning=value_controler_per_channel[msg.channel][
|
panning=_panning,
|
||||||
ControlerKeys.MIDI_PAN
|
start_time=_start_ms, # 微秒
|
||||||
],
|
duration=microseconds - _start_ms, # 微秒
|
||||||
start_time=_ms, # 微秒
|
|
||||||
duration=microseconds - _ms, # 微秒
|
|
||||||
play_speed=config.speed_multiplier,
|
play_speed=config.speed_multiplier,
|
||||||
midi_reference_table=(
|
midi_reference_table=(
|
||||||
config.percussion_note_reference_table
|
config.percussion_note_reference_table
|
||||||
@@ -407,6 +417,8 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# print(that_note.start_time, end=", ")
|
||||||
|
|
||||||
divided_tracks[
|
divided_tracks[
|
||||||
(
|
(
|
||||||
track_no,
|
track_no,
|
||||||
@@ -465,15 +477,16 @@ class MidiImport2MusicPlugin(MusicInputPluginBase):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
for track_properties, every_single_track in divided_tracks.items():
|
for track_properties, every_single_track in divided_tracks.items():
|
||||||
|
# [音轨编号, 通道编号, 乐器名称, 音量, 声相]
|
||||||
if track_properties[0] and (
|
if track_properties[0] and (
|
||||||
track_name := midi_track_name_dict.get(track_properties[0])
|
track_name := midi_track_name_dict.get(track_properties[0])
|
||||||
):
|
): # 音轨编号
|
||||||
every_single_track.name = track_name
|
every_single_track.name = track_name
|
||||||
if track_properties[2]:
|
if track_properties[2]: # 乐器名称
|
||||||
every_single_track.instrument = 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]
|
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]
|
every_single_track.sound_position.sound_azimuth = track_properties[4]
|
||||||
final_music.append(every_single_track)
|
final_music.append(every_single_track)
|
||||||
|
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ def midi_msgs_to_noteinfo(
|
|||||||
return (
|
return (
|
||||||
SingleNote(
|
SingleNote(
|
||||||
note_pitch=note,
|
note_pitch=note,
|
||||||
note_volume=int((velocity / 127) + 0.5),
|
note_volume=velocity,
|
||||||
start_tick=(tk := int(start_time / float(play_speed) / 50000)),
|
start_tick=(tk := int(start_time / float(play_speed) / 50000)),
|
||||||
keep_tick=round(duration / float(play_speed) / 50000),
|
keep_tick=round(duration / float(play_speed) / 50000),
|
||||||
mass_precision_time=round(
|
mass_precision_time=round(
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ class MineCommand:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@library_plugin("notedata_2_command_plugin")
|
@library_plugin("notedata_to_command_plugin")
|
||||||
class NoteDataConvert2CommandPlugin(LibraryPluginBase):
|
class NoteDataConvert2CommandPlugin(LibraryPluginBase):
|
||||||
metainfo = PluginMetaInformation(
|
metainfo = PluginMetaInformation(
|
||||||
name="音符数据指令支持插件",
|
name="音符数据指令支持插件",
|
||||||
@@ -481,8 +481,8 @@ class NoteDataConvert2CommandPlugin(LibraryPluginBase):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def to_command_list_in_score(
|
def to_command_list_in_score(
|
||||||
music: SingleMusic,
|
music: SingleMusic,
|
||||||
music_deviation: int = 0,
|
music_deviation: float = 0,
|
||||||
minimum_volume: float = 0,
|
minimum_volume: float = 0.01,
|
||||||
scoreboard_name: str = "mscplay",
|
scoreboard_name: str = "mscplay",
|
||||||
execute_command_head: str = "execute as {} at @s positioned ~ ~ ~ run ",
|
execute_command_head: str = "execute as {} at @s positioned ~ ~ ~ run ",
|
||||||
) -> Tuple[List[List[MineCommand]], int, int]:
|
) -> Tuple[List[List[MineCommand]], int, int]:
|
||||||
@@ -565,8 +565,8 @@ class NoteDataConvert2CommandPlugin(LibraryPluginBase):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def to_command_list_in_delay(
|
def to_command_list_in_delay(
|
||||||
music: SingleMusic,
|
music: SingleMusic,
|
||||||
music_deviation: int = 0,
|
music_deviation: float = 0,
|
||||||
minimum_volume: float = 0,
|
minimum_volume: float = 0.01,
|
||||||
player_selector: str = "@a",
|
player_selector: str = "@a",
|
||||||
execute_command_head: str = "execute as {} at @s positioned ~ ~ ~ run ",
|
execute_command_head: str = "execute as {} at @s positioned ~ ~ ~ run ",
|
||||||
) -> Tuple[List[MineCommand], int, int]:
|
) -> Tuple[List[MineCommand], int, int]:
|
||||||
@@ -583,7 +583,7 @@ class NoteDataConvert2CommandPlugin(LibraryPluginBase):
|
|||||||
tuple( list[MineCommand指令,...], int音乐时长游戏刻, int最大同时播放的指令数量 )
|
tuple( list[MineCommand指令,...], int音乐时长游戏刻, int最大同时播放的指令数量 )
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 此处 我们把通道视为音轨
|
# 音轨判断
|
||||||
music_command_list = []
|
music_command_list = []
|
||||||
multi = max_multi = 0
|
multi = max_multi = 0
|
||||||
delaytime_previous = 0
|
delaytime_previous = 0
|
||||||
@@ -611,7 +611,7 @@ class NoteDataConvert2CommandPlugin(LibraryPluginBase):
|
|||||||
MineCommand(
|
MineCommand(
|
||||||
command=(
|
command=(
|
||||||
execute_command_head.format(player_selector)
|
execute_command_head.format(player_selector)
|
||||||
+ r"playsound {} @s ^{} ^{} ^{} {} {} {}".format(
|
+ "playsound {} @s ^{} ^{} ^{} {} {} {}".format(
|
||||||
note.instrument,
|
note.instrument,
|
||||||
*relative_coordinates,
|
*relative_coordinates,
|
||||||
volume_percentage,
|
volume_percentage,
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ def minenote_to_command_parameters(
|
|||||||
----
|
----
|
||||||
mine_note: MineNote
|
mine_note: MineNote
|
||||||
我的世界音符对象
|
我的世界音符对象
|
||||||
deviation: float
|
pitch_deviation: float
|
||||||
音调偏移量
|
音调偏移量
|
||||||
|
|
||||||
返回
|
返回
|
||||||
@@ -59,7 +59,7 @@ def minenote_to_command_parameters(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
mine_note.position.position_displacement,
|
mine_note.position.position_displacement,
|
||||||
mine_note.volume / 100,
|
mine_note.volume / 127,
|
||||||
(
|
(
|
||||||
None
|
None
|
||||||
if mine_note.percussive
|
if mine_note.percussive
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ class SingleNote:
|
|||||||
"""Midi 音高"""
|
"""Midi 音高"""
|
||||||
|
|
||||||
volume: int
|
volume: int
|
||||||
"""力度/播放响度 0~100 百分比"""
|
"""力度/播放响度 0~127 百廿七分比"""
|
||||||
|
|
||||||
start_time: int
|
start_time: int
|
||||||
"""开始之时 命令刻"""
|
"""开始之时 命令刻"""
|
||||||
@@ -166,7 +166,7 @@ class SingleNote:
|
|||||||
midi_pitch: int
|
midi_pitch: int
|
||||||
Midi 音高
|
Midi 音高
|
||||||
note_volume: int
|
note_volume: int
|
||||||
响度/力度(百分比, 0~100)
|
响度/力度(百廿七分比, 0~127)
|
||||||
start_time: int
|
start_time: int
|
||||||
开始之时(命令刻)
|
开始之时(命令刻)
|
||||||
注:此处的时间是用从乐曲开始到当前的刻数
|
注:此处的时间是用从乐曲开始到当前的刻数
|
||||||
@@ -367,7 +367,7 @@ class MineNote:
|
|||||||
instrument: str
|
instrument: str
|
||||||
"""乐器 ID"""
|
"""乐器 ID"""
|
||||||
volume: float
|
volume: float
|
||||||
"""力度/播放音量 0~100 百分比"""
|
"""力度/播放音量 0~127 百廿七分比"""
|
||||||
start_tick: int
|
start_tick: int
|
||||||
"""开始之时 命令刻"""
|
"""开始之时 命令刻"""
|
||||||
duration_tick: int
|
duration_tick: int
|
||||||
@@ -697,7 +697,8 @@ class SingleMusic(List[SingleTrack]):
|
|||||||
归并后的每个元素,按 sort_key 升序
|
归并后的每个元素,按 sort_key 升序
|
||||||
"""
|
"""
|
||||||
if is_subseq_sorted:
|
if is_subseq_sorted:
|
||||||
return heapq.merge(*tracks, key=sort_key)
|
# 必须这样处理,不能 return 这个 merge,测试过了
|
||||||
|
yield from heapq.merge(*tracks, key=sort_key)
|
||||||
else:
|
else:
|
||||||
# 初始化堆
|
# 初始化堆
|
||||||
heap_pool: List[Tuple[Any, int, T]] = []
|
heap_pool: List[Tuple[Any, int, T]] = []
|
||||||
@@ -765,11 +766,11 @@ class SingleMusic(List[SingleTrack]):
|
|||||||
|
|
||||||
def get_minenotes(
|
def get_minenotes(
|
||||||
self, start_time: float, end_time: float = inf
|
self, start_time: float, end_time: float = inf
|
||||||
) -> Generator[MineNote, Any, None]:
|
) -> Iterator[MineNote]:
|
||||||
"""获取指定时间段所有的,供我的世界播放的音符数据类,按照时间顺序"""
|
"""获取指定时间段所有的,供我的世界播放的音符数据类,按照时间顺序"""
|
||||||
if self.track_amount == 0:
|
if self.track_amount == 0:
|
||||||
return
|
return iter(())
|
||||||
yield from self.yield_from_tracks(
|
return self.yield_from_tracks(
|
||||||
[track.get_minenotes(start_time, end_time) for track in self.music_tracks],
|
[track.get_minenotes(start_time, end_time) for track in self.music_tracks],
|
||||||
sort_key=lambda x: x.start_tick,
|
sort_key=lambda x: x.start_tick,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -116,9 +116,16 @@ class MusiCreater:
|
|||||||
__plugin = __plg
|
__plugin = __plg
|
||||||
if __plugin:
|
if __plugin:
|
||||||
return __plugin
|
return __plugin
|
||||||
|
else:
|
||||||
|
if plg_id:
|
||||||
|
raise PluginNotFoundError(
|
||||||
|
"无法找到惟一识别码为`{}`、处理`{}`格式的插件".format(
|
||||||
|
plg_id, fpath.suffix.upper()
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise FileFormatNotSupportedError(
|
raise FileFormatNotSupportedError(
|
||||||
"无法找到处理`{}`类型文件的插件".format(fpath.suffix.upper())
|
"无法找到处理`{}`格式的插件".format(fpath.suffix.upper())
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -159,7 +166,7 @@ class MusiCreater:
|
|||||||
plugin_id: Optional[str] = None,
|
plugin_id: Optional[str] = None,
|
||||||
plugin_config: Optional[PluginConfig] = None,
|
plugin_config: Optional[PluginConfig] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._get_plugin_within_iousage(
|
return self._get_plugin_within_iousage(
|
||||||
self.__plugin_registry.get_music_output_plugin_by_format,
|
self.__plugin_registry.get_music_output_plugin_by_format,
|
||||||
file_path,
|
file_path,
|
||||||
self.__plugin_registry._music_output_plugins,
|
self.__plugin_registry._music_output_plugins,
|
||||||
@@ -173,7 +180,7 @@ class MusiCreater:
|
|||||||
plugin_id: Optional[str] = None,
|
plugin_id: Optional[str] = None,
|
||||||
plugin_config: Optional[PluginConfig] = None,
|
plugin_config: Optional[PluginConfig] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._get_plugin_within_iousage(
|
return self._get_plugin_within_iousage(
|
||||||
self.__plugin_registry.get_track_output_plugin_by_format,
|
self.__plugin_registry.get_track_output_plugin_by_format,
|
||||||
file_path,
|
file_path,
|
||||||
self.__plugin_registry._track_output_plugins,
|
self.__plugin_registry._track_output_plugins,
|
||||||
|
|||||||
@@ -231,6 +231,7 @@ class PluginRegistry:
|
|||||||
if plugin.can_handle_format(fpath_or_format)
|
if plugin.can_handle_format(fpath_or_format)
|
||||||
)
|
)
|
||||||
elif isinstance(fpath_or_format, Path):
|
elif isinstance(fpath_or_format, Path):
|
||||||
|
# print("在",plugin_regdict,"中,查找可用于处理",fpath_or_format,"的插件")
|
||||||
return (
|
return (
|
||||||
plugin
|
plugin
|
||||||
for plugin in plugin_regdict.values()
|
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
|
from typing import BinaryIO, Optional, Iterator, Generator, Any, Tuple
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ Copyright © 2026 Eilles
|
|||||||
则无需标注原作者,允许该使用者自行署名
|
则无需标注原作者,允许该使用者自行署名
|
||||||
|
|
||||||
本声明仅限于包含此声明的本文件,本声明与项目内其他文件无关。
|
本声明仅限于包含此声明的本文件,本声明与项目内其他文件无关。
|
||||||
|
本声明同样适用于所有直接转载的内容。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import BinaryIO, Optional
|
from typing import BinaryIO, Optional
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ from typing import Literal
|
|||||||
|
|
||||||
from ...old_main import MidiConvert
|
from ...old_main import MidiConvert
|
||||||
from ...subclass import MineCommand
|
from ...subclass import MineCommand
|
||||||
from ..mcstructure import (
|
from Musicreater.builtin_plugins.commands_to_structure.mcstructure import (
|
||||||
COMPABILITY_VERSION_117,
|
COMPABILITY_VERSION_117,
|
||||||
COMPABILITY_VERSION_119,
|
COMPABILITY_VERSION_119,
|
||||||
commands_to_redstone_delay_structure,
|
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 类而直接执行插件的函数
|
# 为了确保类型安全,以下方法不建议使用,因为这本质上是越过了 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"),
|
Path("./resources/测试片段.mid"),
|
||||||
MidiImportConfig(
|
MidiImportConfig(
|
||||||
speed_multiplier=1.0,
|
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
|
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)
|
t = p.load(Path("./resources/测试片段.mid"), None)
|
||||||
|
|
||||||
# 但是说实话,既然已经在 MusiCreater 类中提供了
|
# 但是说实话,既然已经在 MusiCreater 类中提供了
|
||||||
|
|||||||
Reference in New Issue
Block a user