基本数据结构做好了,接下来就是解析功能了

This commit is contained in:
2026-01-24 05:37:38 +08:00
parent 583ca04ac9
commit 32b7930b26
2 changed files with 175 additions and 38 deletions

View File

@@ -20,7 +20,21 @@ Terms & Conditions: License.md in the root directory
from math import sin, cos, asin, radians, degrees, sqrt, atan, inf, ceil from math import sin, cos, asin, radians, degrees, sqrt, atan, inf, ceil
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional, Any, List, Tuple, Union, Dict, Sequence, Callable, Generator from typing import (
Optional,
Any,
List,
Tuple,
Union,
Dict,
Set,
Sequence,
Callable,
Generator,
Iterable,
Iterator,
Literal,
)
from enum import Enum from enum import Enum
from .types import FittingFunctionType from .types import FittingFunctionType
@@ -36,7 +50,7 @@ class SoundAtmos:
"""声源距离 方块""" """声源距离 方块"""
sound_azimuth: Tuple[float, float] sound_azimuth: Tuple[float, float]
"""声源方位 角度""" """声源方位 角度(rV左右 rH上下)"""
def __init__( def __init__(
self, self,
@@ -113,13 +127,13 @@ class SingleNote:
velocity: int velocity: int
"""力度""" """力度"""
start_tick: int start_time: int
"""开始之时 命令刻""" """开始之时 命令刻"""
duration: int duration: int
"""音符持续时间 命令刻""" """音符持续时间 命令刻"""
high_precision_time: int high_precision_start_time: int
"""高精度开始时间偏量 1/1250 秒""" """高精度开始时间偏量 1/1250 秒"""
extra_info: Dict[str, Any] extra_info: Dict[str, Any]
@@ -164,11 +178,11 @@ class SingleNote:
"""midi音高""" """midi音高"""
self.velocity: int = midi_velocity self.velocity: int = midi_velocity
"""响度(力度)""" """响度(力度)"""
self.start_tick: int = start_time self.start_time: int = start_time
"""开始之时 命令刻""" """开始之时 命令刻"""
self.duration: int = last_time self.duration: int = last_time
"""音符持续时间 命令刻""" """音符持续时间 命令刻"""
self.high_precision_time: int = mass_precision_time self.high_precision_start_time: int = mass_precision_time
"""高精度开始时间偏量 0.4 毫秒""" """高精度开始时间偏量 0.4 毫秒"""
self.extra_info = extra_information if extra_information else {} self.extra_info = extra_information if extra_information else {}
@@ -227,14 +241,14 @@ class SingleNote:
return ( return (
( (
( (
((((self.note_pitch << 7) + self.velocity) << 17) + self.start_tick) ((((self.note_pitch << 7) + self.velocity) << 17) + self.start_time)
<< 17 << 17
) )
+ self.duration + self.duration
).to_bytes(6, "big") ).to_bytes(6, "big")
# + self.track_no.to_bytes(1, "big") # + self.track_no.to_bytes(1, "big")
+ ( + (
self.high_precision_time.to_bytes(1, "big") self.high_precision_start_time.to_bytes(1, "big")
if is_high_time_precision if is_high_time_precision
else b"" else b""
) )
@@ -263,9 +277,9 @@ class SingleNote:
return "TrackedNote(Pitch = {}, Velocity = {}, StartTick = {}, Duration = {}, TimeOffset = {}".format( return "TrackedNote(Pitch = {}, Velocity = {}, StartTick = {}, Duration = {}, TimeOffset = {}".format(
self.note_pitch, self.note_pitch,
self.velocity, self.velocity,
self.start_tick, self.start_time,
self.duration, self.duration,
self.high_precision_time, self.high_precision_start_time,
) + ( ) + (
", ExtraData = {})".format(self.extra_info) if include_extra_data else ")" ", ExtraData = {})".format(self.extra_info) if include_extra_data else ")"
) )
@@ -279,18 +293,18 @@ class SingleNote:
return ( return (
self.note_pitch, self.note_pitch,
self.velocity, self.velocity,
self.start_tick, self.start_time,
self.duration, self.duration,
self.high_precision_time, self.high_precision_start_time,
) )
def __dict__(self): def __dict__(self):
return { return {
"Pitch": self.note_pitch, "Pitch": self.note_pitch,
"Velocity": self.velocity, "Velocity": self.velocity,
"StartTick": self.start_tick, "StartTick": self.start_time,
"Duration": self.duration, "Duration": self.duration,
"TimeOffset": self.high_precision_time, "TimeOffset": self.high_precision_start_time,
"ExtraData": self.extra_info, "ExtraData": self.extra_info,
} }
@@ -302,31 +316,88 @@ class SingleNote:
def __lt__(self, other) -> bool: def __lt__(self, other) -> bool:
"""比较自己是否在开始时间上早于另一个音符""" """比较自己是否在开始时间上早于另一个音符"""
if self.start_tick == other.start_tick: if self.start_time == other.start_tick:
return self.high_precision_time < other.high_precision_time return self.high_precision_start_time < other.high_precision_time
else: else:
return self.start_tick < other.start_tick return self.start_time < other.start_tick
def __gt__(self, other) -> bool: def __gt__(self, other) -> bool:
"""比较自己是否在开始时间上晚于另一个音符""" """比较自己是否在开始时间上晚于另一个音符"""
if self.start_tick == other.start_tick: if self.start_time == other.start_tick:
return self.high_precision_time > other.high_precision_time return self.high_precision_start_time > other.high_precision_time
else: else:
return self.start_tick > other.start_tick return self.start_time > other.start_tick
class CurvableParam(str, Enum): class CurvableParam(str, Enum):
"""可曲线化的参数枚举类""" """可曲线化的参数枚举类"""
PITCH = "note-pitch"
VELOCITY = "note-velocity" PITCH = "adjust_note_pitch"
VOLUME = "note-volume" VELOCITY = "adjust_note_velocity"
DISTANCE = "note-sound-distance" VOLUME = "adjust_note_volume"
HORIZONTAL_PANNING = "note-H-panning-degree" DISTANCE = "adjust_note_sound_distance"
VERTICAL_PANNING = "note-V-panning-degree" LR_PANNING = "adjust_note_leftright_panning_degree"
UD_PANNING = "adjust_note_updown_panning_degree"
@dataclass
class MineNote:
"""我的世界音符对象(仅提供我的世界相关接口)"""
class SingleTrack(list): pitch: float
"""midi音高"""
instrument: str
"""乐器ID"""
velocity: float
"""力度"""
volume: float
"""音量"""
start_tick: int
"""开始之时 命令刻"""
duration_tick: int
"""音符持续时间 命令刻"""
persiced_time: int
"""高精度开始时间偏量 1/1250 秒"""
percussive: bool
"""是否作为打击乐器启用"""
position: SoundAtmos
"""声像方位"""
@classmethod
def from_single_note(
cls,
note: SingleNote,
note_instrument: str,
sound_volume: float,
is_percussive_note: bool,
sound_position: SoundAtmos,
adjust_note_pitch: float = 0.0,
adjust_note_velocity: float = 0.0,
adjust_note_volume: float = 0.0,
adjust_note_sound_distance: float = 0.0,
adjust_note_leftright_panning_degree: float = 0.0,
adjust_note_updown_panning_degree: float = 0.0,
) -> "MineNote":
"""从SingleNote对象创建MineNote对象"""
sound_position.sound_distance += adjust_note_sound_distance
sound_position.sound_azimuth = (
sound_position.sound_azimuth[0] + adjust_note_leftright_panning_degree,
sound_position.sound_azimuth[1] + adjust_note_updown_panning_degree,
)
return cls(
pitch=note.note_pitch + adjust_note_pitch,
instrument=note_instrument,
velocity=note.velocity + adjust_note_velocity,
volume=sound_volume + adjust_note_volume,
start_tick=note.start_time,
duration_tick=note.duration,
persiced_time=note.high_precision_start_time,
percussive=is_percussive_note,
position=sound_position,
)
class SingleTrack(List[SingleNote]):
"""存储单个轨道的类""" """存储单个轨道的类"""
track_name: str track_name: str
@@ -347,7 +418,7 @@ class SingleTrack(list):
sound_position: SoundAtmos sound_position: SoundAtmos
"""声像方位""" """声像方位"""
argument_curves: Dict[CurvableParam, ParamCurve] argument_curves: Dict[CurvableParam, Union[ParamCurve, Literal[None]]]
"""参数曲线""" """参数曲线"""
extra_info: Dict[str, Any] extra_info: Dict[str, Any]
@@ -384,7 +455,77 @@ class SingleTrack(list):
self.extra_info = extra_information if extra_information else {} self.extra_info = extra_information if extra_information else {}
self.argument_curves = {
CurvableParam.PITCH: None,
CurvableParam.VELOCITY: None,
CurvableParam.VOLUME: None,
CurvableParam.DISTANCE: None,
CurvableParam.LR_PANNING: None,
CurvableParam.UD_PANNING: None,
}
super().__init__(*args) super().__init__(*args)
super().sort()
def append(self, object: SingleNote) -> None:
"""
添加一个音符,推荐使用 add 方法
"""
return self.add(object)
def add(self, item: SingleNote) -> None:
"""
在音轨里添加一个音符
"""
if not isinstance(item, SingleNote):
raise TypeError(
"单音轨类的元素类型须为单音符SingleNote不可为{}".format(
type(item).__name__
)
)
super().append(item)
super().sort()
def update(self, items: Iterable[SingleNote]):
"""
拼接两个音轨
"""
super().extend(items)
super().sort()
def get(self, time: int) -> Iterator[SingleNote]:
"""通过开始时间来获取音符"""
return filter(lambda x: x.start_time == time, self)
def get_range(
self, start_time: float, end_time: float = inf
) -> Iterator[SingleNote]:
"""通过开始时间和结束时间来获取音符"""
return filter(
lambda x: (x.start_time >= start_time) and (x.start_time <= end_time), self
)
def get_minenotes(
self, range_start_time: float = 0, range_end_time: float = inf
) -> Generator[MineNote, Any, None]:
for note in self.get_range(range_start_time, range_end_time):
yield MineNote.from_single_note(
note,
self.track_instrument,
self.track_volume,
self.is_percussive,
self.sound_position,
**{
item.value: self.argument_curves[item].value_at(note.start_time) # type: ignore
for item in CurvableParam
if self.argument_curves[item]
},
)
@property @property
def note_amount(self) -> int: def note_amount(self) -> int:
@@ -416,14 +557,7 @@ class SingleTrack(list):
return self.extra_info.get(key, default) return self.extra_info.get(key, default)
def get_notes(self) -> Generator[SingleNote, Any, None]: class SingleMusic(List[SingleTrack]):
# TODO : 添加其他参数以及参数曲线到这里来
for note in self:
yield note
class SingleMusic(list):
"""存储单个曲子的类""" """存储单个曲子的类"""
music_name: str music_name: str

View File

@@ -237,7 +237,7 @@ class ParamCurve:
---------- ----------
base_value : float base_value : float
边界外默认值(当 boundary_mode 为 BoundaryBehaviour.CONSTANT 时使用)。 边界外默认值(当 boundary_mode 为 BoundaryBehaviour.CONSTANT 时使用)。
default_interp : FittingFunctionType default_interpolation_function : FittingFunctionType
新关键帧的默认 out_interp。 新关键帧的默认 out_interp。
boundary_mode : BoundaryBehaviour boundary_mode : BoundaryBehaviour
范围外行为: 范围外行为:
@@ -250,6 +250,9 @@ class ParamCurve:
self._keys: List[Keyframe] = [] self._keys: List[Keyframe] = []
def __bool__(self) -> bool:
return bool(self._keys) or (self.base_line != 0)
def add_key( def add_key(
self, self,
time: float, time: float,