mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2026-01-25 13:12:25 +00:00
基本数据结构做好了,接下来就是解析功能了
This commit is contained in:
@@ -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,
|
||||||
@@ -45,7 +59,7 @@ class SoundAtmos:
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
定义一个发声方位
|
定义一个发声方位
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
------------
|
------------
|
||||||
distance: float
|
distance: float
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user