mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2025-09-05 20:06:23 +00:00
Midi歌词支持,文档格式更新
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -26,6 +26,7 @@ RES.txt
|
|||||||
/Packer/checksum.txt
|
/Packer/checksum.txt
|
||||||
/bgArrayLib
|
/bgArrayLib
|
||||||
/fcwslib
|
/fcwslib
|
||||||
|
test_lyric-mido.py
|
||||||
|
|
||||||
# Byte-compiled / optimized
|
# Byte-compiled / optimized
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
@ -22,8 +22,8 @@ The Licensor of Musicreater("this project") is Eilles, bgArray.
|
|||||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
|
||||||
__version__ = "2.4.0.1"
|
__version__ = "2.4.1"
|
||||||
__vername__ = "全景声支持、音量调节修复"
|
__vername__ = "Midi 歌词支持,文档格式更新"
|
||||||
__author__ = (
|
__author__ = (
|
||||||
("金羿", "Eilles"),
|
("金羿", "Eilles"),
|
||||||
("诸葛亮与八卦阵", "bgArray"),
|
("诸葛亮与八卦阵", "bgArray"),
|
||||||
@ -41,13 +41,26 @@ __all__ = [
|
|||||||
"SingleNoteBox",
|
"SingleNoteBox",
|
||||||
"ProgressBarStyle",
|
"ProgressBarStyle",
|
||||||
# "TimeStamp", 未来功能
|
# "TimeStamp", 未来功能
|
||||||
# 默认值
|
# 字典键
|
||||||
"MIDI_PROGRAM",
|
"MIDI_PROGRAM",
|
||||||
"MIDI_VOLUME",
|
"MIDI_VOLUME",
|
||||||
"MIDI_PAN",
|
"MIDI_PAN",
|
||||||
|
# 默认值
|
||||||
"MIDI_DEFAULT_PROGRAM_VALUE",
|
"MIDI_DEFAULT_PROGRAM_VALUE",
|
||||||
"MIDI_DEFAULT_VOLUME_VALUE",
|
"MIDI_DEFAULT_VOLUME_VALUE",
|
||||||
"DEFAULT_PROGRESSBAR_STYLE",
|
"DEFAULT_PROGRESSBAR_STYLE",
|
||||||
|
# Midi 自己的对照表
|
||||||
|
"MIDI_PITCH_NAME_TABLE",
|
||||||
|
"MIDI_PITCHED_NOTE_NAME_GROUP",
|
||||||
|
"MIDI_PITCHED_NOTE_NAME_TABLE",
|
||||||
|
"MIDI_PERCUSSION_NOTE_NAME_TABLE",
|
||||||
|
# Minecraft 自己的对照表
|
||||||
|
"MC_PERCUSSION_INSTRUMENT_LIST",
|
||||||
|
"MC_PITCHED_INSTRUMENT_LIST",
|
||||||
|
"MC_INSTRUMENT_BLOCKS_TABLE",
|
||||||
|
"MC_EILLES_RTJE12_INSTRUMENT_REPLACE_TABLE",
|
||||||
|
"MC_EILLES_RTBETA_INSTRUMENT_REPLACE_TABLE",
|
||||||
|
# Midi 与 游戏 的对照表
|
||||||
"MM_INSTRUMENT_RANGE_TABLE",
|
"MM_INSTRUMENT_RANGE_TABLE",
|
||||||
"MM_CLASSIC_PITCHED_INSTRUMENT_TABLE",
|
"MM_CLASSIC_PITCHED_INSTRUMENT_TABLE",
|
||||||
"MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE",
|
"MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE",
|
||||||
@ -62,10 +75,68 @@ __all__ = [
|
|||||||
"velocity_2_distance_straight",
|
"velocity_2_distance_straight",
|
||||||
"panning_2_rotation_linear",
|
"panning_2_rotation_linear",
|
||||||
"panning_2_rotation_trigonometric",
|
"panning_2_rotation_trigonometric",
|
||||||
|
# 工具函数
|
||||||
"load_decode_musicsequence_metainfo",
|
"load_decode_musicsequence_metainfo",
|
||||||
"load_decode_msq_flush_release",
|
"load_decode_msq_flush_release",
|
||||||
"load_decode_fsq_flush_release",
|
"load_decode_fsq_flush_release",
|
||||||
"guess_deviation",
|
"guess_deviation",
|
||||||
|
"mctick2timestr",
|
||||||
|
"midi_inst_to_mc_sound",
|
||||||
]
|
]
|
||||||
|
|
||||||
from .main import *
|
from .main import MusicSequence, MidiConvert
|
||||||
|
|
||||||
|
from .subclass import (
|
||||||
|
MineNote,
|
||||||
|
MineCommand,
|
||||||
|
SingleNoteBox,
|
||||||
|
ProgressBarStyle,
|
||||||
|
mctick2timestr,
|
||||||
|
DEFAULT_PROGRESSBAR_STYLE,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .utils import (
|
||||||
|
# 兼容性函数
|
||||||
|
load_decode_musicsequence_metainfo,
|
||||||
|
load_decode_msq_flush_release,
|
||||||
|
load_decode_fsq_flush_release,
|
||||||
|
# 工具函数
|
||||||
|
guess_deviation,
|
||||||
|
midi_inst_to_mc_sound,
|
||||||
|
# 处理用函数
|
||||||
|
velocity_2_distance_natural,
|
||||||
|
velocity_2_distance_straight,
|
||||||
|
panning_2_rotation_linear,
|
||||||
|
panning_2_rotation_trigonometric,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .constants import (
|
||||||
|
# 字典键
|
||||||
|
MIDI_PROGRAM,
|
||||||
|
MIDI_PAN,
|
||||||
|
MIDI_VOLUME,
|
||||||
|
# 默认值
|
||||||
|
MIDI_DEFAULT_PROGRAM_VALUE,
|
||||||
|
MIDI_DEFAULT_VOLUME_VALUE,
|
||||||
|
# MIDI 表
|
||||||
|
MIDI_PITCH_NAME_TABLE,
|
||||||
|
MIDI_PITCHED_NOTE_NAME_GROUP,
|
||||||
|
MIDI_PITCHED_NOTE_NAME_TABLE,
|
||||||
|
MIDI_PERCUSSION_NOTE_NAME_TABLE,
|
||||||
|
# 我的世界 表
|
||||||
|
MC_EILLES_RTBETA_INSTRUMENT_REPLACE_TABLE,
|
||||||
|
MC_EILLES_RTJE12_INSTRUMENT_REPLACE_TABLE,
|
||||||
|
MC_INSTRUMENT_BLOCKS_TABLE,
|
||||||
|
MC_PERCUSSION_INSTRUMENT_LIST,
|
||||||
|
MC_PITCHED_INSTRUMENT_LIST,
|
||||||
|
# MIDI 到 我的世界 表
|
||||||
|
MM_INSTRUMENT_RANGE_TABLE,
|
||||||
|
MM_CLASSIC_PITCHED_INSTRUMENT_TABLE,
|
||||||
|
MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE,
|
||||||
|
MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
||||||
|
MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
||||||
|
MM_DISLINK_PITCHED_INSTRUMENT_TABLE,
|
||||||
|
MM_DISLINK_PERCUSSION_INSTRUMENT_TABLE,
|
||||||
|
MM_NBS_PITCHED_INSTRUMENT_TABLE,
|
||||||
|
MM_NBS_PERCUSSION_INSTRUMENT_TABLE,
|
||||||
|
)
|
||||||
|
@ -113,13 +113,20 @@ class NoteOnOffMismatchError(MidiFormatException):
|
|||||||
"""音符开音和停止不匹配的错误"""
|
"""音符开音和停止不匹配的错误"""
|
||||||
super().__init__("音符不匹配", *args)
|
super().__init__("音符不匹配", *args)
|
||||||
|
|
||||||
|
class LyricMismatchError(MSCTBaseException):
|
||||||
|
"""歌词匹配解析错误"""
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
"""有可能产生了错误的歌词解析"""
|
||||||
|
super().__init__("歌词解析错误", *args)
|
||||||
|
|
||||||
|
|
||||||
class ZeroSpeedError(MSCTBaseException, ZeroDivisionError):
|
class ZeroSpeedError(MSCTBaseException, ZeroDivisionError):
|
||||||
"""以0作为播放速度的错误"""
|
"""以0作为播放速度的错误"""
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
"""以0作为播放速度的错误"""
|
"""以0作为播放速度的错误"""
|
||||||
super().__init__("播放速度为0", *args)
|
super().__init__("播放速度为零", *args)
|
||||||
|
|
||||||
|
|
||||||
class IllegalMinimumVolumeError(MSCTBaseException, ValueError):
|
class IllegalMinimumVolumeError(MSCTBaseException, ValueError):
|
||||||
|
@ -34,6 +34,99 @@ from .types import ChannelType, FittingFunctionType
|
|||||||
from .utils import *
|
from .utils import *
|
||||||
|
|
||||||
|
|
||||||
|
class FutureMidiConvertLyricSupport(MidiConvert):
|
||||||
|
"""
|
||||||
|
歌词测试支持
|
||||||
|
"""
|
||||||
|
|
||||||
|
def to_command_list_in_delay(
|
||||||
|
self,
|
||||||
|
player_selector: str = "@a",
|
||||||
|
using_lyric: bool = True,
|
||||||
|
) -> Tuple[List[MineCommand], int, int]:
|
||||||
|
"""
|
||||||
|
将midi转换为我的世界命令列表,并输出每个音符之后的延迟
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
player_selector: str
|
||||||
|
玩家选择器,默认为`@a`
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
tuple( list[MineCommand指令,...], int音乐时长游戏刻, int最大同时播放的指令数量 )
|
||||||
|
"""
|
||||||
|
|
||||||
|
notes_list: List[MineNote] = sorted(
|
||||||
|
[i for j in self.channels.values() for i in j],
|
||||||
|
key=lambda note: note.start_tick,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 此处 我们把通道视为音轨
|
||||||
|
self.music_command_list = []
|
||||||
|
multi = max_multi = 0
|
||||||
|
delaytime_previous = 0
|
||||||
|
|
||||||
|
for note in notes_list:
|
||||||
|
if (tickdelay := (note.start_tick - delaytime_previous)) == 0:
|
||||||
|
multi += 1
|
||||||
|
else:
|
||||||
|
max_multi = max(max_multi, multi)
|
||||||
|
multi = 0
|
||||||
|
|
||||||
|
(
|
||||||
|
mc_sound_ID,
|
||||||
|
relative_coordinates,
|
||||||
|
volume_percentage,
|
||||||
|
mc_pitch,
|
||||||
|
) = minenote_to_command_paramaters(
|
||||||
|
note,
|
||||||
|
pitch_deviation=self.music_deviation,
|
||||||
|
)
|
||||||
|
self.music_command_list.append(
|
||||||
|
MineCommand(
|
||||||
|
command=(
|
||||||
|
self.execute_cmd_head.format(player_selector)
|
||||||
|
+ r"playsound {} @s ^{} ^{} ^{} {} {} {}".format(
|
||||||
|
mc_sound_ID,
|
||||||
|
*relative_coordinates,
|
||||||
|
volume_percentage,
|
||||||
|
1.0 if note.percussive else mc_pitch,
|
||||||
|
self.minimum_volume,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
annotation=(
|
||||||
|
"在{}播放噪音{}".format(
|
||||||
|
mctick2timestr(note.start_tick),
|
||||||
|
mc_sound_ID,
|
||||||
|
)
|
||||||
|
if note.percussive
|
||||||
|
else "在{}播放乐音{}".format(
|
||||||
|
mctick2timestr(note.start_tick),
|
||||||
|
"{}:{:.2f}".format(mc_sound_ID, mc_pitch),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
tick_delay=tickdelay,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if using_lyric and note.extra_info["LYRIC_TEXT"]:
|
||||||
|
self.music_command_list.append(
|
||||||
|
MineCommand(
|
||||||
|
self.execute_cmd_head.format(player_selector)
|
||||||
|
+ 'title @s title " "'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.music_command_list.append(
|
||||||
|
MineCommand(
|
||||||
|
self.execute_cmd_head.format(player_selector)
|
||||||
|
+ "title @s subtitle {}".format(note.extra_info["LYRIC_TEXT"])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
delaytime_previous = note.start_tick
|
||||||
|
|
||||||
|
return self.music_command_list, notes_list[-1].start_tick, max_multi + 1
|
||||||
|
|
||||||
|
|
||||||
class FutureMidiConvertKamiRES(MidiConvert):
|
class FutureMidiConvertKamiRES(MidiConvert):
|
||||||
"""
|
"""
|
||||||
神羽资源包之测试支持
|
神羽资源包之测试支持
|
||||||
@ -758,10 +851,21 @@ class FutureMidiConvertM4(MidiConvert):
|
|||||||
_note: MineNote,
|
_note: MineNote,
|
||||||
_apply_time_division: float = 10,
|
_apply_time_division: float = 10,
|
||||||
) -> List[MineNote]:
|
) -> List[MineNote]:
|
||||||
"""传入音符数据,返回分割后的插值列表
|
"""
|
||||||
:param _note: MineNote 音符
|
传入音符数据,返回分割后的插值列表
|
||||||
:param _apply_time_division: int 间隔帧数
|
|
||||||
:return list[tuple(int开始时间(毫秒), int乐器, int音符, int力度(内置), float音量(播放)),]"""
|
Parameters
|
||||||
|
------------
|
||||||
|
_note: MineNote
|
||||||
|
音符
|
||||||
|
_apply_time_division: int
|
||||||
|
间隔帧数
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
list[tuple[int, int, int, int, float]]
|
||||||
|
分割后的插值列表,每个元素为 (开始时间(毫秒), 乐器, 音符, 力度(内置), 音量(播放))
|
||||||
|
"""
|
||||||
|
|
||||||
if _note.percussive:
|
if _note.percussive:
|
||||||
return [
|
return [
|
||||||
|
@ -130,9 +130,7 @@ class MusicSequence:
|
|||||||
|
|
||||||
if minimum_volume_of_music > 1 or minimum_volume_of_music <= 0:
|
if minimum_volume_of_music > 1 or minimum_volume_of_music <= 0:
|
||||||
raise IllegalMinimumVolumeError(
|
raise IllegalMinimumVolumeError(
|
||||||
"自订的最小音量参数错误:{},应在 (0,1] 范围内。".format(
|
"最小音量不得为 {},应在 (0,1] 范围内。".format(minimum_volume_of_music)
|
||||||
minimum_volume_of_music
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
# max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
|
# max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
|
||||||
|
|
||||||
@ -594,7 +592,7 @@ class MusicSequence:
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
raise MusicSequenceTypeError(
|
raise MusicSequenceTypeError(
|
||||||
"输入的二进制字节码不是合法的音符序列格式,无法解码,码头前 10 字节为:",
|
"输入的二进制字节码不是正确的音符序列格式,无法解码,码前十字节为:",
|
||||||
bytes_buffer_in[:10],
|
bytes_buffer_in[:10],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -851,12 +849,12 @@ class MusicSequence:
|
|||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
以频道作为分割的Midi音符列表字典, 音符总数, 乐器使用统计:
|
|
||||||
Tuple[MineNoteChannelType, int, Dict[str, int]]
|
Tuple[MineNoteChannelType, int, Dict[str, int]]
|
||||||
|
以通道作为分割的Midi音符列表字典, 音符总数, 乐器使用统计
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if speed == 0:
|
if speed == 0:
|
||||||
raise ZeroSpeedError("播放速度为 0 ,其需要(0,1]范围内的实数。")
|
raise ZeroSpeedError("播放速度不得为零,应为 (0,1] 范围内的实数。")
|
||||||
|
|
||||||
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
||||||
midi_channels: MineNoteChannelType = empty_midi_channels(default_staff=[])
|
midi_channels: MineNoteChannelType = empty_midi_channels(default_staff=[])
|
||||||
@ -893,8 +891,15 @@ class MusicSequence:
|
|||||||
],
|
],
|
||||||
] = empty_midi_channels(default_staff=[])
|
] = empty_midi_channels(default_staff=[])
|
||||||
|
|
||||||
|
lyric_cache: List[Tuple[int, str]] = []
|
||||||
|
|
||||||
# 直接使用mido.midifiles.tracks.merge_tracks转为单轨
|
# 直接使用mido.midifiles.tracks.merge_tracks转为单轨
|
||||||
# 采用的时遍历信息思路
|
# 采用的是遍历信息思路
|
||||||
|
|
||||||
|
# 来自 202508 的留言
|
||||||
|
# 该处代码有点问题
|
||||||
|
# merged track 丢失了 track 信息,会导致音符不匹配的问题出现
|
||||||
|
# 应该用遍历 Track 的方式来处理
|
||||||
for msg in midi.merged_track:
|
for msg in midi.merged_track:
|
||||||
if msg.time != 0:
|
if msg.time != 0:
|
||||||
# 微秒
|
# 微秒
|
||||||
@ -904,36 +909,66 @@ class MusicSequence:
|
|||||||
if msg.type == "set_tempo":
|
if msg.type == "set_tempo":
|
||||||
tempo = msg.tempo
|
tempo = msg.tempo
|
||||||
elif msg.type == "program_change":
|
elif msg.type == "program_change":
|
||||||
|
# 检测 乐器变化 之 midi 事件
|
||||||
channel_controler[msg.channel][MIDI_PROGRAM] = msg.program
|
channel_controler[msg.channel][MIDI_PROGRAM] = msg.program
|
||||||
|
|
||||||
elif msg.is_cc(7):
|
elif msg.is_cc(7):
|
||||||
|
# Control Change 更改当前通道的 音量 的事件(大幅度)
|
||||||
channel_controler[msg.channel][MIDI_VOLUME] = msg.value
|
channel_controler[msg.channel][MIDI_VOLUME] = msg.value
|
||||||
elif msg.is_cc(10):
|
elif msg.is_cc(10):
|
||||||
|
# Control Change 更改当前通道的 音调偏移 的事件(大幅度)
|
||||||
channel_controler[msg.channel][MIDI_PAN] = msg.value
|
channel_controler[msg.channel][MIDI_PAN] = msg.value
|
||||||
|
|
||||||
|
elif msg.type == "lyrics":
|
||||||
|
# 歌词事件
|
||||||
|
lyric_cache.append((microseconds, msg.text))
|
||||||
|
# print(lyric_cache, flush=True)
|
||||||
|
|
||||||
elif msg.type == "note_on" and msg.velocity != 0:
|
elif msg.type == "note_on" and msg.velocity != 0:
|
||||||
|
# 一个音符开始弹奏
|
||||||
|
|
||||||
|
# 加入音符队列甲(按通道分隔)
|
||||||
|
# (音高,乐器)
|
||||||
note_queue_A[msg.channel].append(
|
note_queue_A[msg.channel].append(
|
||||||
(msg.note, channel_controler[msg.channel][MIDI_PROGRAM])
|
(msg.note, channel_controler[msg.channel][MIDI_PROGRAM])
|
||||||
)
|
)
|
||||||
|
# 音符队列乙(按通道分隔)
|
||||||
|
# (力度,微秒)
|
||||||
note_queue_B[msg.channel].append((msg.velocity, microseconds))
|
note_queue_B[msg.channel].append((msg.velocity, microseconds))
|
||||||
|
|
||||||
elif (msg.type == "note_off") or (
|
elif (msg.type == "note_off") or (
|
||||||
msg.type == "note_on" and msg.velocity == 0
|
msg.type == "note_on" and msg.velocity == 0
|
||||||
):
|
):
|
||||||
|
# 一个音符结束弹奏
|
||||||
|
|
||||||
if (
|
if (
|
||||||
msg.note,
|
msg.note,
|
||||||
channel_controler[msg.channel][MIDI_PROGRAM],
|
channel_controler[msg.channel][MIDI_PROGRAM],
|
||||||
) in note_queue_A[msg.channel]:
|
) in note_queue_A[msg.channel]:
|
||||||
|
# 在甲队列中发现了同一个 音高和乐器 的音符
|
||||||
|
|
||||||
|
# 获取其音符力度和微秒数
|
||||||
_velocity, _ms = note_queue_B[msg.channel][
|
_velocity, _ms = note_queue_B[msg.channel][
|
||||||
note_queue_A[msg.channel].index(
|
note_queue_A[msg.channel].index(
|
||||||
(msg.note, channel_controler[msg.channel][MIDI_PROGRAM])
|
(msg.note, channel_controler[msg.channel][MIDI_PROGRAM])
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# 在队列中删除此音符
|
||||||
note_queue_A[msg.channel].remove(
|
note_queue_A[msg.channel].remove(
|
||||||
(msg.note, channel_controler[msg.channel][MIDI_PROGRAM])
|
(msg.note, channel_controler[msg.channel][MIDI_PROGRAM])
|
||||||
)
|
)
|
||||||
note_queue_B[msg.channel].remove((_velocity, _ms))
|
note_queue_B[msg.channel].remove((_velocity, _ms))
|
||||||
|
|
||||||
|
_lyric = ""
|
||||||
|
# 找一找歌词吧
|
||||||
|
if lyric_cache:
|
||||||
|
for i in range(len(lyric_cache)):
|
||||||
|
if lyric_cache[i][0] >= _ms:
|
||||||
|
_lyric = lyric_cache.pop(i)[1]
|
||||||
|
break
|
||||||
|
|
||||||
|
# 更新结果信息
|
||||||
midi_channels[msg.channel].append(
|
midi_channels[msg.channel].append(
|
||||||
that_note := midi_msgs_to_minenote(
|
that_note := midi_msgs_to_minenote(
|
||||||
inst_=(
|
inst_=(
|
||||||
@ -961,14 +996,19 @@ class MusicSequence:
|
|||||||
volume_processing_method_=vol_processing_function,
|
volume_processing_method_=vol_processing_function,
|
||||||
panning_processing_method_=pan_processing_function,
|
panning_processing_method_=pan_processing_function,
|
||||||
note_table_replacement=note_rtable_replacement,
|
note_table_replacement=note_rtable_replacement,
|
||||||
|
lyric_line=_lyric,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 更新统计信息
|
||||||
note_count += 1
|
note_count += 1
|
||||||
if that_note.sound_name in note_count_per_instrument.keys():
|
if that_note.sound_name in note_count_per_instrument.keys():
|
||||||
note_count_per_instrument[that_note.sound_name] += 1
|
note_count_per_instrument[that_note.sound_name] += 1
|
||||||
else:
|
else:
|
||||||
note_count_per_instrument[that_note.sound_name] = 1
|
note_count_per_instrument[that_note.sound_name] = 1
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
# 什么?找不到 note on 消息??
|
||||||
if ignore_mismatch_error:
|
if ignore_mismatch_error:
|
||||||
print(
|
print(
|
||||||
"[WARRING] MIDI格式错误 音符不匹配 {} 无法在上文中找到与之匹配的音符开音消息".format(
|
"[WARRING] MIDI格式错误 音符不匹配 {} 无法在上文中找到与之匹配的音符开音消息".format(
|
||||||
@ -993,7 +1033,24 @@ class MusicSequence:
|
|||||||
|
|
||||||
3 音符结束消息
|
3 音符结束消息
|
||||||
("NoteE", 结束的音符ID, 距离演奏开始的毫秒)"""
|
("NoteE", 结束的音符ID, 距离演奏开始的毫秒)"""
|
||||||
|
|
||||||
del tempo
|
del tempo
|
||||||
|
|
||||||
|
if lyric_cache:
|
||||||
|
# 怎么有歌词多啊
|
||||||
|
if ignore_mismatch_error:
|
||||||
|
print(
|
||||||
|
"[WARRING] MIDI 解析错误 歌词对应错误,以下歌词未能填入音符之中,已经填入的仍可能有误 {}".format(
|
||||||
|
lyric_cache
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise LyricMismatchError(
|
||||||
|
"MIDI 解析产生错误",
|
||||||
|
"歌词解析过程中无法对应音符,已填入的音符仍可能有误",
|
||||||
|
lyric_cache,
|
||||||
|
)
|
||||||
|
|
||||||
channels = dict(
|
channels = dict(
|
||||||
[
|
[
|
||||||
(channel_no, sorted(channel_notes, key=lambda note: note.start_tick))
|
(channel_no, sorted(channel_notes, key=lambda note: note.start_tick))
|
||||||
@ -1126,6 +1183,7 @@ class MidiConvert(MusicSequence):
|
|||||||
pan_processing_func: FittingFunctionType = panning_2_rotation_linear,
|
pan_processing_func: FittingFunctionType = panning_2_rotation_linear,
|
||||||
music_pitch_deviation: float = 0,
|
music_pitch_deviation: float = 0,
|
||||||
note_table_replacement: Dict[str, str] = {},
|
note_table_replacement: Dict[str, str] = {},
|
||||||
|
midi_charset: str = "utf-8",
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
直接输入文件地址,将 midi 文件读入
|
直接输入文件地址,将 midi 文件读入
|
||||||
@ -1171,6 +1229,7 @@ class MidiConvert(MusicSequence):
|
|||||||
return cls.from_mido_obj(
|
return cls.from_mido_obj(
|
||||||
midi_obj=mido.MidiFile(
|
midi_obj=mido.MidiFile(
|
||||||
midi_file_path,
|
midi_file_path,
|
||||||
|
charset=midi_charset,
|
||||||
clip=True,
|
clip=True,
|
||||||
),
|
),
|
||||||
midi_name=midi_music_name,
|
midi_name=midi_music_name,
|
||||||
|
@ -25,12 +25,28 @@ from typing import List, Literal, Union
|
|||||||
|
|
||||||
|
|
||||||
def compress_zipfile(sourceDir, outFilename, compression=8, exceptFile=None):
|
def compress_zipfile(sourceDir, outFilename, compression=8, exceptFile=None):
|
||||||
"""使用compression指定的算法打包目录为zip文件\n
|
"""
|
||||||
默认算法为DEFLATED(8),可用算法如下:\n
|
使用指定的压缩算法将目录打包为zip文件
|
||||||
STORED = 0\n
|
|
||||||
DEFLATED = 8\n
|
Parameters
|
||||||
BZIP2 = 12\n
|
------------
|
||||||
LZMA = 14\n
|
sourceDir: str
|
||||||
|
要压缩的源目录路径
|
||||||
|
outFilename: str
|
||||||
|
输出的zip文件路径
|
||||||
|
compression: int, 可选
|
||||||
|
压缩算法,默认为8 (DEFLATED)
|
||||||
|
可用算法:
|
||||||
|
STORED = 0
|
||||||
|
DEFLATED = 8 (默认)
|
||||||
|
BZIP2 = 12
|
||||||
|
LZMA = 14
|
||||||
|
exceptFile: list[str], 可选
|
||||||
|
需要排除在压缩包外的文件名称列表(可选)
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
zipf = zipfile.ZipFile(outFilename, "w", compression)
|
zipf = zipfile.ZipFile(outFilename, "w", compression)
|
||||||
|
@ -60,50 +60,55 @@ def form_command_block_in_BDX_bytes(
|
|||||||
customName: str = "",
|
customName: str = "",
|
||||||
executeOnFirstTick: bool = False,
|
executeOnFirstTick: bool = False,
|
||||||
trackOutput: bool = True,
|
trackOutput: bool = True,
|
||||||
):
|
) -> bytes:
|
||||||
"""
|
"""
|
||||||
使用指定项目返回指定的指令方块放置指令项
|
使用指定参数生成指定的指令方块放置指令项
|
||||||
:param command: `str`
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
command: str
|
||||||
指令
|
指令
|
||||||
:param particularValue:
|
particularValue: int
|
||||||
方块特殊值,即朝向
|
方块特殊值,即朝向
|
||||||
:0 下 无条件
|
:0 下 无条件
|
||||||
:1 上 无条件
|
:1 上 无条件
|
||||||
:2 z轴负方向 无条件
|
:2 z轴负方向 无条件
|
||||||
:3 z轴正方向 无条件
|
:3 z轴正方向 无条件
|
||||||
:4 x轴负方向 无条件
|
:4 x轴负方向 无条件
|
||||||
:5 x轴正方向 无条件
|
:5 x轴正方向 无条件
|
||||||
:6 下 无条件
|
:6 下 无条件
|
||||||
:7 下 无条件
|
:7 下 无条件
|
||||||
|
:8 下 有条件
|
||||||
:8 下 有条件
|
:9 上 有条件
|
||||||
:9 上 有条件
|
:10 z轴负方向 有条件
|
||||||
:10 z轴负方向 有条件
|
:11 z轴正方向 有条件
|
||||||
:11 z轴正方向 有条件
|
:12 x轴负方向 有条件
|
||||||
:12 x轴负方向 有条件
|
:13 x轴正方向 有条件
|
||||||
:13 x轴正方向 有条件
|
:14 下 有条件
|
||||||
:14 下 有条件
|
:14 下 有条件
|
||||||
:14 下 有条件
|
|
||||||
注意!此处特殊值中的条件会被下面condition参数覆写
|
注意!此处特殊值中的条件会被下面condition参数覆写
|
||||||
:param impluse: `int 0|1|2`
|
impluse: int (0|1|2)
|
||||||
方块类型
|
方块类型
|
||||||
0脉冲 1循环 2连锁
|
0脉冲 1循环 2连锁
|
||||||
:param condition: `bool`
|
condition: bool
|
||||||
是否有条件
|
是否有条件
|
||||||
:param needRedstone: `bool`
|
needRedstone: bool
|
||||||
是否需要红石
|
是否需要红石
|
||||||
:param tickDelay: `int`
|
tickDelay: int
|
||||||
执行延时
|
执行延时
|
||||||
:param customName: `str`
|
customName: str
|
||||||
悬浮字
|
悬浮字
|
||||||
lastOutput: `str`
|
lastOutput: str
|
||||||
上次输出字符串,注意此处需要留空
|
命令方块的上次输出字符串,注意此处需要留空
|
||||||
:param executeOnFirstTick: `bool`
|
executeOnFirstTick: bool
|
||||||
首刻执行(循环指令方块是否激活后立即执行,若为False,则从激活时起延迟后第一次执行)
|
是否启用首刻执行(循环指令方块是否激活后立即执行,若为False,则从激活时起延迟后第一次执行)
|
||||||
:param trackOutput: `bool`
|
trackOutput: bool
|
||||||
是否输出
|
是否启用命令方块输出
|
||||||
|
|
||||||
:return:str
|
Returns
|
||||||
|
---------
|
||||||
|
bytes
|
||||||
|
用以生成 bdx 结构的字节码
|
||||||
"""
|
"""
|
||||||
block = b"\x24" + particularValue.to_bytes(2, byteorder="big", signed=False)
|
block = b"\x24" + particularValue.to_bytes(2, byteorder="big", signed=False)
|
||||||
|
|
||||||
@ -127,9 +132,19 @@ def commands_to_BDX_bytes(
|
|||||||
max_height: int = 64,
|
max_height: int = 64,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
:param commands: 指令列表(指令, 延迟)
|
指令列表转换为用以生成 bdx 结构的字节码
|
||||||
:param max_height: 生成结构最大高度
|
|
||||||
:return 成功与否,成功返回(True,未经过压缩的源,结构占用大小),失败返回(False,str失败原因)
|
Parameters
|
||||||
|
------------
|
||||||
|
commands: list[tuple[str, int]]
|
||||||
|
指令列表,每个元素为 (指令, 延迟)
|
||||||
|
max_height: int
|
||||||
|
生成结构最大高度
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
tuple[bool, bytes, int] or tuple[bool, str]
|
||||||
|
成功与否,成功返回 (True, 未经过压缩的源, 结构占用大小),失败返回 (False, str失败原因)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
|
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
|
||||||
|
@ -19,9 +19,22 @@ Terms & Conditions: License.md in the root directory
|
|||||||
import math
|
import math
|
||||||
|
|
||||||
|
|
||||||
def bottem_side_length_of_smallest_square_bottom_box(total: int, maxHeight: int):
|
def bottem_side_length_of_smallest_square_bottom_box(
|
||||||
"""给定总方块数量和最大高度,返回所构成的图形外切正方形的边长
|
_total_block_count: int, _max_height: int
|
||||||
:param total: 总方块数量
|
):
|
||||||
:param maxHeight: 最大高度
|
"""
|
||||||
:return: 外切正方形的边长 int"""
|
给定结构的总方块数量和规定的最大高度,返回该结构应当构成的图形,在底面的外切正方形之边长
|
||||||
return math.ceil(math.sqrt(math.ceil(total / maxHeight)))
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
_total_block_count: int
|
||||||
|
总方块数量
|
||||||
|
_max_height: int
|
||||||
|
规定的结构最大高度
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
int
|
||||||
|
外切正方形的边长
|
||||||
|
"""
|
||||||
|
return math.ceil(math.sqrt(math.ceil(_total_block_count / _max_height)))
|
||||||
|
@ -70,17 +70,25 @@ def form_note_block_in_NBT_struct(
|
|||||||
instrument: str = "note.harp",
|
instrument: str = "note.harp",
|
||||||
powered: bool = False,
|
powered: bool = False,
|
||||||
compability_version_number: int = COMPABILITY_VERSION_119,
|
compability_version_number: int = COMPABILITY_VERSION_119,
|
||||||
):
|
) -> Block:
|
||||||
"""生成音符盒方块
|
"""
|
||||||
:param note: `int`(0~24)
|
生成音符盒方块
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
note: int (0~24)
|
||||||
音符的音高
|
音符的音高
|
||||||
:param coordinate: `tuple[int,int,int]`
|
coordinate: tuple[int, int, int]
|
||||||
此方块所在之相对坐标
|
此方块所在之相对坐标
|
||||||
:param instrument: `str`
|
instrument: str
|
||||||
音符盒的乐器
|
音符盒的乐器
|
||||||
:param powered: `bool`
|
powered: bool
|
||||||
是否已被激活
|
是否已被激活
|
||||||
:return Block
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Block
|
||||||
|
生成的方块对象
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return Block(
|
return Block(
|
||||||
@ -108,15 +116,26 @@ def form_repeater_in_NBT_struct(
|
|||||||
delay: int,
|
delay: int,
|
||||||
facing: int,
|
facing: int,
|
||||||
compability_version_number: int = COMPABILITY_VERSION_119,
|
compability_version_number: int = COMPABILITY_VERSION_119,
|
||||||
):
|
) -> Block:
|
||||||
"""生成中继器方块
|
"""
|
||||||
:param facing: 朝向:
|
生成中继器方块
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
facing: int (0~3)
|
||||||
|
朝向:
|
||||||
Z- 北 0
|
Z- 北 0
|
||||||
X- 东 1
|
X- 东 1
|
||||||
Z+ 南 2
|
Z+ 南 2
|
||||||
X+ 西 3
|
X+ 西 3
|
||||||
:param delay: 0~3
|
delay: int (0~3)
|
||||||
:return Block()"""
|
信号延迟
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Block
|
||||||
|
生成的方块对象
|
||||||
|
"""
|
||||||
|
|
||||||
return Block(
|
return Block(
|
||||||
"minecraft",
|
"minecraft",
|
||||||
@ -141,50 +160,58 @@ def form_command_block_in_NBT_struct(
|
|||||||
executeOnFirstTick: bool = False,
|
executeOnFirstTick: bool = False,
|
||||||
trackOutput: bool = True,
|
trackOutput: bool = True,
|
||||||
compability_version_number: int = COMPABILITY_VERSION_119,
|
compability_version_number: int = COMPABILITY_VERSION_119,
|
||||||
):
|
) -> Block:
|
||||||
"""
|
"""
|
||||||
使用指定项目返回指定的指令方块结构
|
使用指定参数生成指令方块
|
||||||
:param command: `str`
|
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
command: str
|
||||||
指令
|
指令
|
||||||
:param coordinate: `tuple[int,int,int]`
|
coordinate: tuple[int,int,int]
|
||||||
此方块所在之相对坐标
|
此方块所在之相对坐标
|
||||||
:param particularValue:
|
particularValue: int
|
||||||
方块特殊值,即朝向
|
方块特殊值,即朝向
|
||||||
:0 下 无条件
|
:0 下 无条件
|
||||||
:1 上 无条件
|
:1 上 无条件
|
||||||
:2 z轴负方向 无条件
|
:2 z轴负方向 无条件
|
||||||
:3 z轴正方向 无条件
|
:3 z轴正方向 无条件
|
||||||
:4 x轴负方向 无条件
|
:4 x轴负方向 无条件
|
||||||
:5 x轴正方向 无条件
|
:5 x轴正方向 无条件
|
||||||
:6 下 无条件
|
:6 下 无条件
|
||||||
:7 下 无条件
|
:7 下 无条件
|
||||||
|
:8 下 有条件
|
||||||
:8 下 有条件
|
:9 上 有条件
|
||||||
:9 上 有条件
|
:10 z轴负方向 有条件
|
||||||
:10 z轴负方向 有条件
|
:11 z轴正方向 有条件
|
||||||
:11 z轴正方向 有条件
|
:12 x轴负方向 有条件
|
||||||
:12 x轴负方向 有条件
|
:13 x轴正方向 有条件
|
||||||
:13 x轴正方向 有条件
|
:14 下 有条件
|
||||||
:14 下 有条件
|
:14 下 有条件
|
||||||
:14 下 有条件
|
|
||||||
注意!此处特殊值中的条件会被下面condition参数覆写
|
注意!此处特殊值中的条件会被下面condition参数覆写
|
||||||
:param impluse: `int 0|1|2`
|
impluse: int (0|1|2)
|
||||||
方块类型
|
方块类型
|
||||||
0脉冲 1循环 2连锁
|
0脉冲 1循环 2连锁
|
||||||
:param condition: `bool`
|
condition: bool
|
||||||
是否有条件
|
是否有条件
|
||||||
:param alwaysRun: `bool`
|
alwaysRun: bool
|
||||||
是否始终执行
|
是否始终执行
|
||||||
:param tickDelay: `int`
|
tickDelay: int
|
||||||
执行延时
|
执行延时
|
||||||
:param customName: `str`
|
customName: str
|
||||||
悬浮字
|
悬浮字
|
||||||
:param executeOnFirstTick: `bool`
|
executeOnFirstTick: bool
|
||||||
首刻执行(循环指令方块是否激活后立即执行,若为False,则从激活时起延迟后第一次执行)
|
是否启用首刻执行(循环指令方块是否激活后立即执行,若为False,则从激活时起延迟后第一次执行)
|
||||||
:param trackOutput: `bool`
|
trackOutput: bool
|
||||||
是否输出
|
是否启用命令方块输出
|
||||||
|
compability_version_number: int
|
||||||
|
版本兼容代号
|
||||||
|
|
||||||
:return:str
|
Returns
|
||||||
|
-------
|
||||||
|
Block
|
||||||
|
生成的方块对象
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return Block(
|
return Block(
|
||||||
@ -231,9 +258,19 @@ def commands_to_structure(
|
|||||||
compability_version_: int = COMPABILITY_VERSION_119,
|
compability_version_: int = COMPABILITY_VERSION_119,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
:param commands: 指令列表
|
由指令列表生成(纯指令方块)结构
|
||||||
:param max_height: 生成结构最大高度
|
|
||||||
:return 结构类,结构占用大小,终点坐标
|
Parameters
|
||||||
|
------------
|
||||||
|
commands: list
|
||||||
|
指令列表
|
||||||
|
max_height: int
|
||||||
|
生成结构最大高度
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
Structure, tuple[int, int, int], tuple[int, int, int]
|
||||||
|
结构类, 结构占用大小, 终点坐标
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
|
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
|
||||||
@ -321,12 +358,25 @@ def commands_to_redstone_delay_structure(
|
|||||||
compability_version_: int = COMPABILITY_VERSION_119,
|
compability_version_: int = COMPABILITY_VERSION_119,
|
||||||
) -> Tuple[Structure, Tuple[int, int, int], Tuple[int, int, int]]:
|
) -> Tuple[Structure, Tuple[int, int, int], Tuple[int, int, int]]:
|
||||||
"""
|
"""
|
||||||
:param commands: 指令列表
|
由指令列表生成由红石中继器延迟的结构
|
||||||
:param delay_length: 延时总长
|
|
||||||
:param max_multicmd_length: 最大同时播放的音符数量
|
Parameters
|
||||||
:param base_block: 生成结构的基底方块
|
------------
|
||||||
:param axis_: 生成结构的延展方向
|
commands: list
|
||||||
:return 结构类,结构占用大小,终点坐标
|
指令列表
|
||||||
|
delay_length: int
|
||||||
|
延时总长
|
||||||
|
max_multicmd_length: int
|
||||||
|
最大同时播放的音符数量
|
||||||
|
base_block: Block
|
||||||
|
生成结构的基底方块
|
||||||
|
axis_: str
|
||||||
|
生成结构的延展方向
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
Structure, tuple[int, int, int], tuple[int, int, int]
|
||||||
|
结构类, 结构占用大小, 终点坐标
|
||||||
"""
|
"""
|
||||||
if axis_ in ["z+", "Z+"]:
|
if axis_ in ["z+", "Z+"]:
|
||||||
extensioon_direction = z
|
extensioon_direction = z
|
||||||
|
@ -70,23 +70,41 @@ class MineNote:
|
|||||||
azimuth: Optional[Tuple[float, float]] = None,
|
azimuth: Optional[Tuple[float, float]] = None,
|
||||||
extra_information: Optional[Dict[str, Any]] = None,
|
extra_information: Optional[Dict[str, Any]] = None,
|
||||||
):
|
):
|
||||||
"""用于存储单个音符的类
|
"""
|
||||||
|
用于存储单个音符的类
|
||||||
|
|
||||||
:param mc_sound_name:`str` 《我的世界》声音ID
|
Parameters
|
||||||
:param midi_pitch:`int` midi音高
|
------------
|
||||||
:param midi_velocity:`int` midi响度(力度)
|
mc_sound_name: str
|
||||||
:param start_time:`int` 开始之时(命令刻)
|
《我的世界》声音ID
|
||||||
|
midi_pitch: int
|
||||||
|
midi音高
|
||||||
|
midi_velocity: int
|
||||||
|
midi响度(力度)
|
||||||
|
start_time: int
|
||||||
|
开始之时(命令刻)
|
||||||
注:此处的时间是用从乐曲开始到当前的刻数
|
注:此处的时间是用从乐曲开始到当前的刻数
|
||||||
:param last_time:`int` 音符延续时间(命令刻)
|
last_time: int
|
||||||
:param mass_precision_time:`int` 高精度的开始时间偏移量(1/1250秒)
|
音符延续时间(命令刻)
|
||||||
:param is_percussion:`bool` 是否作为打击乐器
|
mass_precision_time: int
|
||||||
:param distance: `float` 发声源距离玩家的距离(半径 `r`)
|
高精度的开始时间偏移量(1/1250秒)
|
||||||
|
is_percussion: bool
|
||||||
|
是否作为打击乐器
|
||||||
|
distance: float
|
||||||
|
发声源距离玩家的距离(半径 `r`)
|
||||||
注:距离越近,音量越高,默认为 0。此参数可以与音量成某种函数关系。
|
注:距离越近,音量越高,默认为 0。此参数可以与音量成某种函数关系。
|
||||||
:param azimuth:`tuple[float, float]` 声源方位
|
azimuth: tuple[float, float]
|
||||||
|
声源方位
|
||||||
注:此参数为tuple,包含两个元素,分别表示:
|
注:此参数为tuple,包含两个元素,分别表示:
|
||||||
`rV` 发声源在竖直(上下)轴上,从玩家视角正前方开始,向顺时针旋转的角度
|
`rV` 发声源在竖直(上下)轴上,从玩家视角正前方开始,向顺时针旋转的角度
|
||||||
`rH` 发声源在水平(左右)轴上,从玩家视角正前方开始,向上(到达玩家正上方顶点后变为向下,以此类推的旋转)旋转的角度
|
`rH` 发声源在水平(左右)轴上,从玩家视角正前方开始,向上(到达玩家正上方顶点后变为向下,以此类推的旋转)旋转的角度
|
||||||
:param extra_information:`Any` 附加信息"""
|
extra_information: Any
|
||||||
|
附加信息,尽量存储为字典
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
MineNote 类
|
||||||
|
"""
|
||||||
self.sound_name: str = mc_sound_name
|
self.sound_name: str = mc_sound_name
|
||||||
"""乐器ID"""
|
"""乐器ID"""
|
||||||
self.note_pitch: int = 66 if midi_pitch is None else midi_pitch
|
self.note_pitch: int = 66 if midi_pitch is None else midi_pitch
|
||||||
@ -94,9 +112,9 @@ class MineNote:
|
|||||||
self.velocity: int = midi_velocity
|
self.velocity: int = midi_velocity
|
||||||
"""响度(力度)"""
|
"""响度(力度)"""
|
||||||
self.start_tick: int = start_time
|
self.start_tick: int = start_time
|
||||||
"""开始之时 tick"""
|
"""开始之时 命令刻"""
|
||||||
self.duration: int = last_time
|
self.duration: int = last_time
|
||||||
"""音符持续时间 tick"""
|
"""音符持续时间 命令刻"""
|
||||||
self.high_precision_time: int = mass_precision_time
|
self.high_precision_time: int = mass_precision_time
|
||||||
"""高精度开始时间偏量 0.4 毫秒"""
|
"""高精度开始时间偏量 0.4 毫秒"""
|
||||||
|
|
||||||
@ -133,17 +151,35 @@ class MineNote:
|
|||||||
displacement: Optional[Tuple[float, float, float]] = None,
|
displacement: Optional[Tuple[float, float, float]] = None,
|
||||||
extra_information: Optional[Any] = None,
|
extra_information: Optional[Any] = None,
|
||||||
):
|
):
|
||||||
"""用于存储单个音符的类
|
"""
|
||||||
:param mc_sound_name:`str` 《我的世界》声音ID
|
从传统音像位移格式传参,写入用于存储单个音符的类
|
||||||
:param midi_pitch:`int` midi音高
|
|
||||||
:param midi_velocity:`int` midi响度(力度)
|
Parameters
|
||||||
:param start_time:`int` 开始之时(命令刻)
|
------------
|
||||||
|
mc_sound_name: str
|
||||||
|
《我的世界》声音ID
|
||||||
|
midi_pitch: int
|
||||||
|
midi音高
|
||||||
|
midi_velocity: int
|
||||||
|
midi响度(力度)
|
||||||
|
start_time: int
|
||||||
|
开始之时(命令刻)
|
||||||
注:此处的时间是用从乐曲开始到当前的刻数
|
注:此处的时间是用从乐曲开始到当前的刻数
|
||||||
:param last_time:`int` 音符延续时间(命令刻)
|
last_time: int
|
||||||
:param mass_precision_time:`int` 高精度的开始时间偏移量(1/1250秒)
|
音符延续时间(命令刻)
|
||||||
:param is_percussion:`bool` 是否作为打击乐器
|
mass_precision_time: int
|
||||||
:param displacement:`tuple[float,float,float]` 声像位移
|
高精度的开始时间偏移量(1/1250秒)
|
||||||
:param extra_information:`Any` 附加信息"""
|
is_percussion: bool
|
||||||
|
是否作为打击乐器
|
||||||
|
displacement: tuple[float, float, float]
|
||||||
|
声像位移
|
||||||
|
extra_information: Any
|
||||||
|
附加信息,尽量为字典。
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
MineNote 类
|
||||||
|
"""
|
||||||
|
|
||||||
if displacement is None:
|
if displacement is None:
|
||||||
displacement = (0, 0, 0)
|
displacement = (0, 0, 0)
|
||||||
@ -246,10 +282,17 @@ class MineNote:
|
|||||||
"""
|
"""
|
||||||
将数据打包为字节码
|
将数据打包为字节码
|
||||||
|
|
||||||
:param is_displacement_included:`bool` 是否包含声像偏移数据,默认为**是**
|
Parameters
|
||||||
:param is_high_time_precision:`bool` 是否启用高精度,默认为**是**
|
------------
|
||||||
|
is_displacement_included: bool
|
||||||
|
是否包含声像偏移数据,默认为**是**
|
||||||
|
is_high_time_precision: bool
|
||||||
|
是否启用高精度,默认为**是**
|
||||||
|
|
||||||
:return bytes 打包好的字节码
|
Returns
|
||||||
|
---------
|
||||||
|
bytes
|
||||||
|
打包好的字节码
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# MineNote 的字节码共有三个顺次版本分别如下
|
# MineNote 的字节码共有三个顺次版本分别如下
|
||||||
@ -545,12 +588,26 @@ class SingleNoteBox:
|
|||||||
percussion: Optional[bool] = None,
|
percussion: Optional[bool] = None,
|
||||||
annotation: str = "",
|
annotation: str = "",
|
||||||
):
|
):
|
||||||
"""用于存储单个音符盒的类
|
"""
|
||||||
:param instrument_block_ 音符盒演奏所使用的乐器方块
|
用于存储单个音符盒的类
|
||||||
:param note_value_ 音符盒的演奏音高
|
|
||||||
:param percussion 此音符盒乐器是否作为打击乐处理
|
Parameters
|
||||||
|
------------
|
||||||
|
instrument_block_: str
|
||||||
|
音符盒演奏所使用的乐器方块
|
||||||
|
note_value_: int
|
||||||
|
音符盒的演奏音高
|
||||||
|
percussion: bool
|
||||||
|
此音符盒乐器是否作为打击乐处理
|
||||||
注:若为空,则自动识别是否为打击乐器
|
注:若为空,则自动识别是否为打击乐器
|
||||||
:param annotation 音符注释"""
|
annotation: Any
|
||||||
|
音符注释
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
SingleNoteBox 类
|
||||||
|
"""
|
||||||
|
|
||||||
self.instrument_block = instrument_block_
|
self.instrument_block = instrument_block_
|
||||||
"""乐器方块"""
|
"""乐器方块"""
|
||||||
self.note_value = note_value_
|
self.note_value = note_value_
|
||||||
@ -634,7 +691,8 @@ class ProgressBarStyle:
|
|||||||
to_play_s: Optional[str] = None,
|
to_play_s: Optional[str] = None,
|
||||||
played_s: Optional[str] = None,
|
played_s: Optional[str] = None,
|
||||||
):
|
):
|
||||||
"""用于存储进度条样式的类
|
"""
|
||||||
|
用于存储进度条样式的类
|
||||||
|
|
||||||
| 标识符 | 指定的可变量 |
|
| 标识符 | 指定的可变量 |
|
||||||
|---------|----------------|
|
|---------|----------------|
|
||||||
@ -646,9 +704,18 @@ class ProgressBarStyle:
|
|||||||
| `%%%` | 当前进度比率 |
|
| `%%%` | 当前进度比率 |
|
||||||
| `_` | 用以表示进度条占位|
|
| `_` | 用以表示进度条占位|
|
||||||
|
|
||||||
:param base_s 基础样式,用以定义进度条整体
|
Parameters
|
||||||
:param to_play_s 进度条样式:尚未播放的样子
|
------------
|
||||||
:param played_s 已经播放的样子
|
base_s: str
|
||||||
|
基础样式,用以定义进度条整体
|
||||||
|
to_play_s: str
|
||||||
|
进度条样式:尚未播放的样子
|
||||||
|
played_s: str
|
||||||
|
已经播放的样子
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
ProgressBarStyle 类
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.base_style = (
|
self.base_style = (
|
||||||
@ -709,9 +776,19 @@ class ProgressBarStyle:
|
|||||||
"""
|
"""
|
||||||
直接依照此格式输出一个进度条
|
直接依照此格式输出一个进度条
|
||||||
|
|
||||||
:param played_delays: int 当前播放进度积分值
|
Parameters
|
||||||
:param total_delays: int 乐器总延迟数(积分数)
|
------------
|
||||||
:param music_name: str 曲名
|
played_delays: int
|
||||||
|
当前播放进度积分值
|
||||||
|
total_delays: int
|
||||||
|
乐器总延迟数(计分板值)
|
||||||
|
music_name: str
|
||||||
|
曲名
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
str
|
||||||
|
进度条字符串
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -136,7 +136,7 @@ def velocity_2_distance_natural(
|
|||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
vol: int
|
vol: int
|
||||||
midi音符力度值
|
midi 音符力度值
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@ -162,7 +162,7 @@ def velocity_2_distance_straight(vol: float) -> float:
|
|||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
vol: int
|
vol: int
|
||||||
midi音符力度值
|
midi 音符力度值
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@ -179,7 +179,7 @@ def panning_2_rotation_linear(pan_: float) -> float:
|
|||||||
----------
|
----------
|
||||||
pan_: int
|
pan_: int
|
||||||
Midi 左右平衡偏移值
|
Midi 左右平衡偏移值
|
||||||
注:此参数为int,范围从0到127,当为 64 时,声源居中
|
注:此参数为int,范围从 0 到 127,当为 64 时,声源居中
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@ -197,7 +197,7 @@ def panning_2_rotation_trigonometric(pan_: float) -> float:
|
|||||||
----------
|
----------
|
||||||
pan_: int
|
pan_: int
|
||||||
Midi 左右平衡偏移值
|
Midi 左右平衡偏移值
|
||||||
注:此参数为int,范围从0到127,当为 64 时,声源居中
|
注:此参数为int,范围从 0 到 127,当为 64 时,声源居中
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@ -222,11 +222,19 @@ def minenote_to_command_paramaters(
|
|||||||
Union[float, Literal[None]],
|
Union[float, Literal[None]],
|
||||||
]:
|
]:
|
||||||
"""
|
"""
|
||||||
将MineNote对象转为《我的世界》音符播放所需之参数
|
将 MineNote 对象转为《我的世界》音符播放所需之参数
|
||||||
:param note_:MineNote 音符对象
|
|
||||||
:param deviation:float 音调偏移量
|
|
||||||
|
|
||||||
:return str[我的世界音符ID], Tuple[float,float,float]播放视角坐标, float[指令音量参数], float[指令音调参数]
|
Parameters
|
||||||
|
------------
|
||||||
|
note_: MineNote
|
||||||
|
音符对象
|
||||||
|
deviation: float
|
||||||
|
音调偏移量
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
str, tuple[float, float, float], float, float
|
||||||
|
我的世界音符ID, 播放视角坐标, 指令音量参数, 指令音调参数
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -252,7 +260,6 @@ def minenote_to_command_paramaters(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def midi_msgs_to_minenote(
|
def midi_msgs_to_minenote(
|
||||||
inst_: int, # 乐器编号
|
inst_: int, # 乐器编号
|
||||||
note_: int,
|
note_: int,
|
||||||
@ -267,24 +274,46 @@ def midi_msgs_to_minenote(
|
|||||||
volume_processing_method_: FittingFunctionType,
|
volume_processing_method_: FittingFunctionType,
|
||||||
panning_processing_method_: FittingFunctionType,
|
panning_processing_method_: FittingFunctionType,
|
||||||
note_table_replacement: Dict[str, str] = {},
|
note_table_replacement: Dict[str, str] = {},
|
||||||
|
lyric_line: str = "",
|
||||||
) -> MineNote:
|
) -> MineNote:
|
||||||
"""
|
"""
|
||||||
将Midi信息转为我的世界音符对象
|
将Midi信息转为我的世界音符对象
|
||||||
:param inst_: int 乐器编号
|
|
||||||
:param note_: int 音高编号(音符编号)
|
|
||||||
:param percussive_: bool 是否作为打击乐器启用
|
|
||||||
:param volume_: int 音量
|
|
||||||
:param velocity_: int 力度
|
|
||||||
:param panning_: int 声相偏移
|
|
||||||
:param start_time_: int 音符起始时间(微秒)
|
|
||||||
:param duration_: int 音符持续时间(微秒)
|
|
||||||
:param play_speed: float 曲目播放速度
|
|
||||||
:param midi_reference_table: Dict[int, str] 转换对照表
|
|
||||||
:param volume_processing_method_: Callable[[float], float] 音量处理函数
|
|
||||||
:param panning_processing_method_: Callable[[float], float] 立体声相偏移处理函数
|
|
||||||
:param note_table_replacement: Dict[str, str] 音符替换表,定义 Minecraft 音符字串的替换
|
|
||||||
|
|
||||||
:return MineNote我的世界音符对象
|
Parameters
|
||||||
|
------------
|
||||||
|
inst_: int
|
||||||
|
乐器编号
|
||||||
|
note_: int
|
||||||
|
音高编号(音符编号)
|
||||||
|
percussive_: bool
|
||||||
|
是否作为打击乐器启用
|
||||||
|
volume_: int
|
||||||
|
音量
|
||||||
|
velocity_: int
|
||||||
|
力度
|
||||||
|
panning_: int
|
||||||
|
声相偏移
|
||||||
|
start_time_: int
|
||||||
|
音符起始时间(微秒)
|
||||||
|
duration_: int
|
||||||
|
音符持续时间(微秒)
|
||||||
|
play_speed: float
|
||||||
|
曲目播放速度
|
||||||
|
midi_reference_table: Dict[int, str]
|
||||||
|
转换对照表
|
||||||
|
volume_processing_method_: Callable[[float], float]
|
||||||
|
音量处理函数
|
||||||
|
panning_processing_method_: Callable[[float], float]
|
||||||
|
立体声相偏移处理函数
|
||||||
|
note_table_replacement: Dict[str, str]
|
||||||
|
音符替换表,定义 Minecraft 音符字串的替换
|
||||||
|
lyric_line: str
|
||||||
|
该音符的歌词
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
MineNote
|
||||||
|
我的世界音符对象
|
||||||
"""
|
"""
|
||||||
mc_sound_ID = midi_inst_to_mc_sound(
|
mc_sound_ID = midi_inst_to_mc_sound(
|
||||||
inst_,
|
inst_,
|
||||||
@ -302,6 +331,11 @@ def midi_msgs_to_minenote(
|
|||||||
is_percussion=percussive_,
|
is_percussion=percussive_,
|
||||||
distance=volume_processing_method_(volume_),
|
distance=volume_processing_method_(volume_),
|
||||||
azimuth=(panning_processing_method_(panning_), 0),
|
azimuth=(panning_processing_method_(panning_), 0),
|
||||||
|
extra_information={
|
||||||
|
"LYRIC_TEXT": lyric_line,
|
||||||
|
"VOLUME_VALUE": volume_,
|
||||||
|
"PIN_VALUE": panning_,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -319,24 +353,46 @@ def midi_msgs_to_minenote_using_kami_respack(
|
|||||||
volume_processing_method_: Callable[[float], float],
|
volume_processing_method_: Callable[[float], float],
|
||||||
panning_processing_method_: FittingFunctionType,
|
panning_processing_method_: FittingFunctionType,
|
||||||
note_table_replacement: Dict[str, str] = {},
|
note_table_replacement: Dict[str, str] = {},
|
||||||
|
lyric_line: str = "",
|
||||||
) -> MineNote:
|
) -> MineNote:
|
||||||
"""
|
"""
|
||||||
将Midi信息转为我的世界音符对象
|
将Midi信息转为我的世界音符对象,使用神羽资源包兼容格式
|
||||||
:param inst_: int 乐器编号
|
|
||||||
:param note_: int 音高编号(音符编号)
|
|
||||||
:param percussive_: bool 是否作为打击乐器启用
|
|
||||||
:param volume_: int 音量
|
|
||||||
:param velocity_: int 力度
|
|
||||||
:param panning_: int 声相偏移
|
|
||||||
:param start_time_: int 音符起始时间(微秒)
|
|
||||||
:param duration_: int 音符持续时间(微秒)
|
|
||||||
:param play_speed: float 曲目播放速度
|
|
||||||
:param midi_reference_table: Dict[int, str] 转换对照表
|
|
||||||
:param volume_processing_method_: Callable[[float], float] 音量处理函数
|
|
||||||
:param panning_processing_method_: Callable[[float], float] 立体声相偏移处理函数
|
|
||||||
:param note_table_replacement: Dict[str, str] 音符替换表,定义 Minecraft 音符字串的替换
|
|
||||||
|
|
||||||
:return MineNote我的世界音符对象
|
Parameters
|
||||||
|
------------
|
||||||
|
inst_: int
|
||||||
|
乐器编号
|
||||||
|
note_: int
|
||||||
|
音高编号(音符编号)
|
||||||
|
percussive_: bool
|
||||||
|
是否作为打击乐器启用
|
||||||
|
volume_: int
|
||||||
|
音量
|
||||||
|
velocity_: int
|
||||||
|
力度
|
||||||
|
panning_: int
|
||||||
|
声相偏移
|
||||||
|
start_time_: int
|
||||||
|
音符起始时间(微秒)
|
||||||
|
duration_: int
|
||||||
|
音符持续时间(微秒)
|
||||||
|
play_speed: float
|
||||||
|
曲目播放速度
|
||||||
|
midi_reference_table: Dict[int, str]
|
||||||
|
转换对照表
|
||||||
|
volume_processing_method_: Callable[[float], float]
|
||||||
|
音量处理函数
|
||||||
|
panning_processing_method_: Callable[[float], float]
|
||||||
|
立体声相偏移处理函数
|
||||||
|
note_table_replacement: Dict[str, str]
|
||||||
|
音符替换表,定义 Minecraft 音符字串的替换
|
||||||
|
lyric_line: str
|
||||||
|
该音符的歌词
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
MineNote
|
||||||
|
我的世界音符对象
|
||||||
"""
|
"""
|
||||||
|
|
||||||
using_original = False
|
using_original = False
|
||||||
@ -371,6 +427,7 @@ def midi_msgs_to_minenote_using_kami_respack(
|
|||||||
"USING_ORIGINAL_SOUND": using_original, # 判断 extra_information 中是否有 USING_ORIGINAL_SOUND 键是判断是否使用神羽资源包解析的一个显著方法
|
"USING_ORIGINAL_SOUND": using_original, # 判断 extra_information 中是否有 USING_ORIGINAL_SOUND 键是判断是否使用神羽资源包解析的一个显著方法
|
||||||
"INST_VALUE": note_ if percussive_ else inst_,
|
"INST_VALUE": note_ if percussive_ else inst_,
|
||||||
"NOTE_VALUE": inst_ if percussive_ else note_,
|
"NOTE_VALUE": inst_ if percussive_ else note_,
|
||||||
|
"LYRIC_TEXT": lyric_line,
|
||||||
"VOLUME_VALUE": volume_,
|
"VOLUME_VALUE": volume_,
|
||||||
"PIN_VALUE": panning_,
|
"PIN_VALUE": panning_,
|
||||||
},
|
},
|
||||||
|
33
test_future_lyric.py
Normal file
33
test_future_lyric.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import Musicreater.experiment
|
||||||
|
import Musicreater.plugin
|
||||||
|
import Musicreater.plugin.mcstructfile
|
||||||
|
|
||||||
|
msct = Musicreater.experiment.FutureMidiConvertLyricSupport.from_midi_file(
|
||||||
|
input("midi路径:"), old_exe_format=False
|
||||||
|
)
|
||||||
|
|
||||||
|
opt = input("输出路径:")
|
||||||
|
|
||||||
|
# print(
|
||||||
|
# "乐器使用情况",
|
||||||
|
# )
|
||||||
|
|
||||||
|
# for name in sorted(
|
||||||
|
# set(
|
||||||
|
# [
|
||||||
|
# n.split(".")[0].replace("c", "").replace("d", "")
|
||||||
|
# for n in msct.note_count_per_instrument.keys()
|
||||||
|
# ]
|
||||||
|
# )
|
||||||
|
# ):
|
||||||
|
# print("\t", name, flush=True)
|
||||||
|
|
||||||
|
print(
|
||||||
|
"\n输出:",
|
||||||
|
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay(
|
||||||
|
msct,
|
||||||
|
opt,
|
||||||
|
# Musicreater.plugin.ConvertConfig(input("输出路径:"),),
|
||||||
|
max_height=32,
|
||||||
|
),
|
||||||
|
)
|
Reference in New Issue
Block a user