更丰富的音符附加信息支持

This commit is contained in:
2025-08-20 18:57:21 +08:00
parent 774f78359c
commit b796a363d8
6 changed files with 102 additions and 42 deletions

View File

@ -22,8 +22,8 @@ The Licensor of Musicreater("this project") is Eilles, bgArray.
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
__version__ = "2.4.1" __version__ = "2.4.2"
__vername__ = "Midi 歌词支持,文档格式更新" __vername__ = "音符附加信息升级"
__author__ = ( __author__ = (
("金羿", "Eilles"), ("金羿", "Eilles"),
("诸葛亮与八卦阵", "bgArray"), ("诸葛亮与八卦阵", "bgArray"),

View File

@ -79,7 +79,7 @@ class FutureMidiConvertLyricSupport(MidiConvert):
relative_coordinates, relative_coordinates,
volume_percentage, volume_percentage,
mc_pitch, mc_pitch,
) = minenote_to_command_paramaters( ) = minenote_to_command_parameters(
note, note,
pitch_deviation=self.music_deviation, pitch_deviation=self.music_deviation,
) )
@ -367,7 +367,7 @@ class FutureMidiConvertKamiRES(MidiConvert):
relative_coordinates, relative_coordinates,
volume_percentage, volume_percentage,
mc_pitch, mc_pitch,
) = minenote_to_command_paramaters( ) = minenote_to_command_parameters(
note, note,
pitch_deviation=self.music_deviation, pitch_deviation=self.music_deviation,
) )
@ -446,7 +446,7 @@ class FutureMidiConvertKamiRES(MidiConvert):
relative_coordinates, relative_coordinates,
volume_percentage, volume_percentage,
mc_pitch, mc_pitch,
) = minenote_to_command_paramaters( ) = minenote_to_command_parameters(
note, note,
pitch_deviation=self.music_deviation, pitch_deviation=self.music_deviation,
) )
@ -783,7 +783,7 @@ class FutureMidiConvertJavaE(MidiConvert):
relative_coordinates, relative_coordinates,
volume_percentage, volume_percentage,
mc_pitch, mc_pitch,
) = minenote_to_command_paramaters( ) = minenote_to_command_parameters(
note, note,
pitch_deviation=self.music_deviation, pitch_deviation=self.music_deviation,
) )
@ -874,8 +874,14 @@ class FutureMidiConvertM4(MidiConvert):
totalCount = int(_note.duration / _apply_time_division) totalCount = int(_note.duration / _apply_time_division)
if totalCount == 0: if (
print(_note.extra_info) totalCount == 0
or (_note.get_info("PITCH") > 2 and _note.sound_name != "note.bass")
):
from rich import print as prt
prt("[INFO] 音符太短或音调太高,无法生成插值")
prt(_note)
return [ return [
_note, _note,
] ]
@ -883,18 +889,24 @@ class FutureMidiConvertM4(MidiConvert):
result: List[MineNote] = [] result: List[MineNote] = []
_slide = _note.duration / totalCount
_distance_slide = 20 / totalCount
for _i in range(totalCount): for _i in range(totalCount):
result.append( result.append(
MineNote( MineNote(
mc_sound_name=_note.sound_name, mc_sound_name=_note.sound_name,
midi_pitch=_note.note_pitch, midi_pitch=_note.note_pitch,
midi_velocity=_note.velocity, midi_velocity=_note.velocity,
start_time=int( start_time=int(_note.start_tick + _i * _slide),
_note.start_tick + _i * (_note.duration / totalCount) last_time=int(_slide),
),
last_time=int(_note.duration / totalCount),
# track_number=_note.track_no, # track_number=_note.track_no,
is_percussion=_note.percussive, is_percussion=_note.percussive,
distance=_note.sound_distance + _i * _distance_slide,
azimuth=(
_note.sound_azimuth[0],
_note.sound_azimuth[1] + 5 * random.random(),
),
extra_information=_note.extra_info, extra_information=_note.extra_info,
) )
# ( # (
@ -931,14 +943,17 @@ class FutureMidiConvertM4(MidiConvert):
for channel in self.channels.values(): for channel in self.channels.values():
for note in channel: for note in channel:
note.set_info( note.set_info(
minenote_to_command_paramaters( ["NOTE_ID", "COODINATES", "VOLUME", "PITCH"],
minenote_to_command_parameters(
note, note,
pitch_deviation=self.music_deviation, pitch_deviation=self.music_deviation,
) ),
) )
if not note.percussive: if not note.percussive:
notes_list.extend(self._linear_note(note, 1 * note.extra_info[3])) notes_list.extend(
self._linear_note(note, 2 * note.get_info("PITCH"))
)
else: else:
notes_list.append(note) notes_list.append(note)
@ -954,12 +969,17 @@ class FutureMidiConvertM4(MidiConvert):
else: else:
max_multi = max(max_multi, multi) max_multi = max(max_multi, multi)
multi = 0 multi = 0
( (
mc_sound_ID, mc_sound_ID,
relative_coordinates, relative_coordinates,
volume_percentage, volume_percentage,
mc_pitch, mc_pitch,
) = note.extra_info ) = minenote_to_command_parameters(
note,
pitch_deviation=self.music_deviation,
)
self.music_command_list.append( self.music_command_list.append(
MineCommand( MineCommand(
command=( command=(

View File

@ -112,7 +112,7 @@ class MusicSequence:
""" """
音符序列类 音符序列类
Paramaters Parameters
========== ==========
name_of_music: str name_of_music: str
乐曲名称 乐曲名称
@ -175,7 +175,7 @@ class MusicSequence:
""" """
自mido对象导入一个音符序列类 自mido对象导入一个音符序列类
Paramaters Parameters
========== ==========
mido_file: mido.MidiFile mido_file: mido.MidiFile
需要处理的midi对象 需要处理的midi对象
@ -247,7 +247,7 @@ class MusicSequence:
""" """
从字节码导入音乐序列,目前支持 MSQ 第二、三、四版和 FSQ 第一、二版。 从字节码导入音乐序列,目前支持 MSQ 第二、三、四版和 FSQ 第一、二版。
Paramaters Parameters
========== ==========
bytes_buffer_in: bytes bytes_buffer_in: bytes
字节码 字节码
@ -1588,7 +1588,7 @@ class MidiConvert(MusicSequence):
relative_coordinates, relative_coordinates,
volume_percentage, volume_percentage,
mc_pitch, mc_pitch,
) = minenote_to_command_paramaters( ) = minenote_to_command_parameters(
note, note,
pitch_deviation=self.music_deviation, pitch_deviation=self.music_deviation,
) )
@ -1672,7 +1672,7 @@ class MidiConvert(MusicSequence):
relative_coordinates, relative_coordinates,
volume_percentage, volume_percentage,
mc_pitch, mc_pitch,
) = minenote_to_command_paramaters( ) = minenote_to_command_parameters(
note, note,
pitch_deviation=self.music_deviation, pitch_deviation=self.music_deviation,
) )
@ -1758,7 +1758,7 @@ class MidiConvert(MusicSequence):
relative_coordinates, relative_coordinates,
volume_percentage, volume_percentage,
mc_pitch, mc_pitch,
) = minenote_to_command_paramaters( ) = minenote_to_command_parameters(
note, note,
pitch_deviation=self.music_deviation, pitch_deviation=self.music_deviation,
) )

View File

@ -18,7 +18,7 @@ Terms & Conditions: License.md in the root directory
from math import sin, cos, asin, radians, degrees, sqrt, atan from math import sin, cos, asin, radians, degrees, sqrt, atan
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional, Any, List, Tuple, Union, Dict from typing import Optional, Any, List, Tuple, Union, Dict, Sequence
from .constants import MC_PITCHED_INSTRUMENT_LIST from .constants import MC_PITCHED_INSTRUMENT_LIST
@ -54,7 +54,7 @@ class MineNote:
sound_azimuth: Tuple[float, float] sound_azimuth: Tuple[float, float]
"""声源方位 角度""" """声源方位 角度"""
extra_info: Any extra_info: Dict[str, Any]
"""你觉得放什么好?""" """你觉得放什么好?"""
def __init__( def __init__(
@ -68,7 +68,7 @@ class MineNote:
is_percussion: Optional[bool] = None, is_percussion: Optional[bool] = None,
distance: Optional[float] = None, distance: Optional[float] = None,
azimuth: Optional[Tuple[float, float]] = None, azimuth: Optional[Tuple[float, float]] = None,
extra_information: Optional[Dict[str, Any]] = None, extra_information: Dict[str, Any] = {},
): ):
""" """
用于存储单个音符的类 用于存储单个音符的类
@ -98,7 +98,7 @@ class MineNote:
此参数为tuple包含两个元素分别表示 此参数为tuple包含两个元素分别表示
`rV` 发声源在竖直(上下)轴上,从玩家视角正前方开始,向顺时针旋转的角度 `rV` 发声源在竖直(上下)轴上,从玩家视角正前方开始,向顺时针旋转的角度
`rH` 发声源在水平(左右)轴上,从玩家视角正前方开始,向上(到达玩家正上方顶点后变为向下,以此类推的旋转)旋转的角度 `rH` 发声源在水平(左右)轴上,从玩家视角正前方开始,向上(到达玩家正上方顶点后变为向下,以此类推的旋转)旋转的角度
extra_information: Any extra_information: Dict[str, Any]
附加信息,尽量存储为字典 附加信息,尽量存储为字典
Returns Returns
@ -136,7 +136,7 @@ class MineNote:
) )
"""声源距离""" """声源距离"""
self.extra_info = extra_information self.extra_info = extra_information if extra_information else {}
@classmethod @classmethod
def from_traditional( def from_traditional(
@ -210,7 +210,15 @@ class MineNote:
is_percussion=is_percussion, is_percussion=is_percussion,
distance=r, distance=r,
azimuth=(alpha_v, beta_h), azimuth=(alpha_v, beta_h),
extra_information=extra_information, extra_information=(
(
extra_information
if isinstance(extra_information, dict)
else {"EXTRA_INFO": extra_information}
)
if extra_information
else {}
),
) )
@property @property
@ -225,7 +233,7 @@ class MineNote:
@classmethod @classmethod
def decode(cls, code_buffer: bytes, is_high_time_precision: bool = True): def decode(cls, code_buffer: bytes, is_high_time_precision: bool = True):
"""自字节码析出MineNote类""" """自字节码析出 MineNote """
group_1 = int.from_bytes(code_buffer[:6], "big") group_1 = int.from_bytes(code_buffer[:6], "big")
percussive_ = bool(group_1 & 0b1) percussive_ = bool(group_1 & 0b1)
duration_ = (group_1 := group_1 >> 1) & 0b11111111111111111 duration_ = (group_1 := group_1 >> 1) & 0b11111111111111111
@ -375,9 +383,34 @@ class MineNote:
) )
) )
def set_info(self, sth: Any): def set_info(self, key: Union[str, Sequence[str]], value: Any):
"""设置附加信息""" """设置附加信息"""
self.extra_info = sth if isinstance(key, str):
self.extra_info[key] = value
elif (
isinstance(key, Sequence)
and isinstance(value, Sequence)
and (k := len(key)) == len(value)
):
for i in range(k):
self.extra_info[key[i]] = value[i]
else:
raise TypeError("参数类型错误")
def get_info(self, key: str) -> Any:
"""获取附加信息"""
if key in self.extra_info:
return self.extra_info[key]
elif "EXTRA_INFO" in self.extra_info:
if (
isinstance(self.extra_info["EXTRA_INFO"], dict)
and key in self.extra_info["EXTRA_INFO"]
):
return self.extra_info["EXTRA_INFO"].get(key)
else:
return self.extra_info["EXTRA_INFO"]
else:
return None
def stringize( def stringize(
self, include_displacement: bool = False, include_extra_data: bool = False self, include_displacement: bool = False, include_extra_data: bool = False

View File

@ -49,7 +49,7 @@ ChannelType = Dict[
], ],
] ]
""" """
以字典所标记的道信息类型(已弃用) 以字典所标记的道信息类型(已弃用)
Dict[int,Dict[int,List[Union[Tuple[Literal["PgmC"], int, int],Tuple[Literal["NoteS"], int, int, int],Tuple[Literal["NoteE"], int, int],]],],] Dict[int,Dict[int,List[Union[Tuple[Literal["PgmC"], int, int],Tuple[Literal["NoteS"], int, int, int],Tuple[Literal["NoteE"], int, int],]],],]
""" """
@ -60,7 +60,14 @@ MineNoteChannelType = Mapping[
List[MineNote,], List[MineNote,],
] ]
""" """
我的世界道信息类型 我的世界道信息类型
Dict[int,Dict[int,List[MineNote,],],] Dict[int,Dict[int,List[MineNote,],],]
""" """
MineNoteTrackType = Mapping[
int,
List[MineNote,],
]

View File

@ -212,8 +212,8 @@ def panning_2_rotation_trigonometric(pan_: float) -> float:
return math.degrees(math.acos((64 - pan_) / 63)) - 90 return math.degrees(math.acos((64 - pan_) / 63)) - 90
def minenote_to_command_paramaters( def minenote_to_command_parameters(
note_: MineNote, mine_note: MineNote,
pitch_deviation: float = 0, pitch_deviation: float = 0,
) -> Tuple[ ) -> Tuple[
str, str,
@ -226,7 +226,7 @@ def minenote_to_command_paramaters(
Parameters Parameters
------------ ------------
note_: MineNote mine_note: MineNote
音符对象 音符对象
deviation: float deviation: float
音调偏移量 音调偏移量
@ -238,19 +238,19 @@ def minenote_to_command_paramaters(
""" """
return ( return (
note_.sound_name, mine_note.sound_name,
note_.position_displacement, mine_note.position_displacement,
note_.velocity / 127, mine_note.velocity / 127,
( (
None None
if note_.percussive if mine_note.percussive
else ( else (
2 2
** ( ** (
( (
note_.note_pitch mine_note.note_pitch
- 60 - 60
- MM_INSTRUMENT_DEVIATION_TABLE.get(note_.sound_name, 6) - MM_INSTRUMENT_DEVIATION_TABLE.get(mine_note.sound_name, 6)
+ pitch_deviation + pitch_deviation
) )
/ 12 / 12