mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2025-09-03 19:06:23 +00:00
Merge branch 'master' of https://gitee.com/EillesWan/Musicreater
This commit is contained in:
@ -22,8 +22,8 @@ The Licensor of Musicreater("this project") is Eilles, bgArray.
|
|||||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
|
||||||
__version__ = "2.3.2"
|
__version__ = "2.4.0"
|
||||||
__vername__ = "支持神羽资源包"
|
__vername__ = "全景声支持、音量调节修复"
|
||||||
__author__ = (
|
__author__ = (
|
||||||
("金羿", "Eilles"),
|
("金羿", "Eilles"),
|
||||||
("诸葛亮与八卦阵", "bgArray"),
|
("诸葛亮与八卦阵", "bgArray"),
|
||||||
@ -42,7 +42,11 @@ __all__ = [
|
|||||||
"ProgressBarStyle",
|
"ProgressBarStyle",
|
||||||
# "TimeStamp", 未来功能
|
# "TimeStamp", 未来功能
|
||||||
# 默认值
|
# 默认值
|
||||||
|
"MIDI_PROGRAM",
|
||||||
|
"MIDI_VOLUME",
|
||||||
|
"MIDI_PAN",
|
||||||
"MIDI_DEFAULT_PROGRAM_VALUE",
|
"MIDI_DEFAULT_PROGRAM_VALUE",
|
||||||
|
"MIDI_DEFAULT_VOLUME_VALUE",
|
||||||
"DEFAULT_PROGRESSBAR_STYLE",
|
"DEFAULT_PROGRESSBAR_STYLE",
|
||||||
"MM_INSTRUMENT_RANGE_TABLE",
|
"MM_INSTRUMENT_RANGE_TABLE",
|
||||||
"MM_CLASSIC_PITCHED_INSTRUMENT_TABLE",
|
"MM_CLASSIC_PITCHED_INSTRUMENT_TABLE",
|
||||||
@ -56,6 +60,8 @@ __all__ = [
|
|||||||
# 操作性函数
|
# 操作性函数
|
||||||
"natural_curve",
|
"natural_curve",
|
||||||
"straight_line",
|
"straight_line",
|
||||||
|
"panning_2_rotation_linear",
|
||||||
|
"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",
|
||||||
|
@ -34,9 +34,22 @@ z = "z"
|
|||||||
z
|
z
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
MIDI_PROGRAM = "program"
|
||||||
|
"""Midi乐器编号"""
|
||||||
|
|
||||||
|
MIDI_VOLUME = "volume"
|
||||||
|
"""Midi通道音量"""
|
||||||
|
|
||||||
|
MIDI_PAN = "pan"
|
||||||
|
"""Midi通道立体声场偏移"""
|
||||||
|
|
||||||
|
|
||||||
# Midi用对照表
|
# Midi用对照表
|
||||||
|
|
||||||
|
MIDI_DEFAULT_VOLUME_VALUE: int = (
|
||||||
|
64 # Midi默认音量,当用户未指定时,默认使用折中默认音量
|
||||||
|
)
|
||||||
|
|
||||||
MIDI_DEFAULT_PROGRAM_VALUE: int = (
|
MIDI_DEFAULT_PROGRAM_VALUE: int = (
|
||||||
74 # 当 Midi 本身与用户皆未指定音色时,默认 Flute 长笛
|
74 # 当 Midi 本身与用户皆未指定音色时,默认 Flute 长笛
|
||||||
)
|
)
|
||||||
|
@ -27,6 +27,8 @@ from .main import (
|
|||||||
MidiConvert,
|
MidiConvert,
|
||||||
mido,
|
mido,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .constants import MIDI_PAN, MIDI_PROGRAM, MIDI_VOLUME
|
||||||
from .subclass import *
|
from .subclass import *
|
||||||
from .types import ChannelType, FittingFunctionType
|
from .types import ChannelType, FittingFunctionType
|
||||||
from .utils import *
|
from .utils import *
|
||||||
@ -36,16 +38,19 @@ class FutureMidiConvertKamiRES(MidiConvert):
|
|||||||
"""
|
"""
|
||||||
神羽资源包之测试支持
|
神羽资源包之测试支持
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def to_music_note_channels(
|
def to_music_note_channels(
|
||||||
midi: mido.MidiFile,
|
midi: mido.MidiFile,
|
||||||
ignore_mismatch_error: bool = True,
|
ignore_mismatch_error: bool = True,
|
||||||
speed: float = 1.0,
|
speed: float = 1.0,
|
||||||
default_program_value: int = -1,
|
default_program_value: int = -1,
|
||||||
|
default_volume_value: int = 64,
|
||||||
default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO,
|
default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO,
|
||||||
pitched_note_rtable: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
pitched_note_rtable: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
||||||
percussion_note_rtable: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
percussion_note_rtable: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
||||||
vol_processing_function: FittingFunctionType = natural_curve,
|
vol_processing_function: FittingFunctionType = natural_curve,
|
||||||
|
pan_processing_function: FittingFunctionType = panning_2_rotation_trigonometric,
|
||||||
note_rtable_replacement: Dict[str, str] = {},
|
note_rtable_replacement: Dict[str, str] = {},
|
||||||
) -> Tuple[MineNoteChannelType, int, Dict[str, int]]:
|
) -> Tuple[MineNoteChannelType, int, Dict[str, int]]:
|
||||||
"""
|
"""
|
||||||
@ -59,6 +64,8 @@ class FutureMidiConvertKamiRES(MidiConvert):
|
|||||||
音乐播放速度倍数
|
音乐播放速度倍数
|
||||||
default_program_value: int
|
default_program_value: int
|
||||||
默认的 MIDI 乐器值
|
默认的 MIDI 乐器值
|
||||||
|
default_volume_value: int
|
||||||
|
默认的通道音量值
|
||||||
default_tempo_value: int
|
default_tempo_value: int
|
||||||
默认的 MIDI TEMPO 值
|
默认的 MIDI TEMPO 值
|
||||||
pitched_note_rtable: Dict[int, Tuple[str, int]]
|
pitched_note_rtable: Dict[int, Tuple[str, int]]
|
||||||
@ -66,7 +73,9 @@ class FutureMidiConvertKamiRES(MidiConvert):
|
|||||||
percussion_note_rtable: Dict[int, Tuple[str, int]]
|
percussion_note_rtable: Dict[int, Tuple[str, int]]
|
||||||
打击乐器Midi-MC对照表
|
打击乐器Midi-MC对照表
|
||||||
vol_processing_function: Callable[[float], float]
|
vol_processing_function: Callable[[float], float]
|
||||||
声像偏移拟合函数
|
音量对播放距离的拟合函数
|
||||||
|
pan_processing_function: Callable[[float], float]
|
||||||
|
声像偏移对播放旋转角度的拟合函数
|
||||||
note_rtable_replacement: Dict[str, str]
|
note_rtable_replacement: Dict[str, str]
|
||||||
音符名称替换表,此表用于对 Minecraft 乐器名称进行替换,而非 Midi Program 的替换
|
音符名称替换表,此表用于对 Minecraft 乐器名称进行替换,而非 Midi Program 的替换
|
||||||
|
|
||||||
@ -81,9 +90,15 @@ class FutureMidiConvertKamiRES(MidiConvert):
|
|||||||
|
|
||||||
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
||||||
midi_channels: MineNoteChannelType = empty_midi_channels(default_staff=[])
|
midi_channels: MineNoteChannelType = empty_midi_channels(default_staff=[])
|
||||||
channel_program: Dict[int, int] = empty_midi_channels(
|
|
||||||
default_staff=default_program_value
|
channel_controler: Dict[int, Dict[str, int]] = empty_midi_channels(
|
||||||
|
default_staff={
|
||||||
|
MIDI_PROGRAM: default_program_value,
|
||||||
|
MIDI_VOLUME: default_volume_value,
|
||||||
|
MIDI_PAN: 64,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
tempo = default_tempo_value
|
tempo = default_tempo_value
|
||||||
note_count = 0
|
note_count = 0
|
||||||
note_count_per_instrument: Dict[str, int] = {}
|
note_count_per_instrument: Dict[str, int] = {}
|
||||||
@ -118,76 +133,84 @@ class FutureMidiConvertKamiRES(MidiConvert):
|
|||||||
# 简化
|
# 简化
|
||||||
if msg.type == "set_tempo":
|
if msg.type == "set_tempo":
|
||||||
tempo = msg.tempo
|
tempo = msg.tempo
|
||||||
else:
|
elif msg.type == "program_change":
|
||||||
if msg.type == "program_change":
|
channel_controler[msg.channel][MIDI_PROGRAM] = msg.program
|
||||||
channel_program[msg.channel] = msg.program
|
|
||||||
|
|
||||||
elif msg.type == "note_on" and msg.velocity != 0:
|
elif msg.is_cc(7):
|
||||||
note_queue_A[msg.channel].append(
|
channel_controler[msg.channel][MIDI_VOLUME] = msg.value
|
||||||
(msg.note, channel_program[msg.channel])
|
elif msg.is_cc(10):
|
||||||
|
channel_controler[msg.channel][MIDI_PAN] = msg.value
|
||||||
|
|
||||||
|
elif msg.type == "note_on" and msg.velocity != 0:
|
||||||
|
note_queue_A[msg.channel].append(
|
||||||
|
(msg.note, channel_controler[msg.channel][MIDI_PROGRAM])
|
||||||
|
)
|
||||||
|
note_queue_B[msg.channel].append((msg.velocity, microseconds))
|
||||||
|
|
||||||
|
elif (msg.type == "note_off") or (
|
||||||
|
msg.type == "note_on" and msg.velocity == 0
|
||||||
|
):
|
||||||
|
if (
|
||||||
|
msg.note,
|
||||||
|
channel_controler[msg.channel][MIDI_PROGRAM],
|
||||||
|
) in note_queue_A[msg.channel]:
|
||||||
|
_velocity, _ms = note_queue_B[msg.channel][
|
||||||
|
note_queue_A[msg.channel].index(
|
||||||
|
(msg.note, channel_controler[msg.channel][MIDI_PROGRAM])
|
||||||
|
)
|
||||||
|
]
|
||||||
|
note_queue_A[msg.channel].remove(
|
||||||
|
(msg.note, channel_controler[msg.channel][MIDI_PROGRAM])
|
||||||
)
|
)
|
||||||
note_queue_B[msg.channel].append((msg.velocity, microseconds))
|
note_queue_B[msg.channel].remove((_velocity, _ms))
|
||||||
|
|
||||||
elif (msg.type == "note_off") or (
|
midi_channels[msg.channel].append(
|
||||||
msg.type == "note_on" and msg.velocity == 0
|
that_note := midi_msgs_to_minenote_using_kami_respack(
|
||||||
):
|
inst_=(
|
||||||
if (msg.note, channel_program[msg.channel]) in note_queue_A[
|
msg.note
|
||||||
msg.channel
|
if msg.channel == 9
|
||||||
]:
|
else channel_controler[msg.channel][MIDI_PROGRAM]
|
||||||
_velocity, _ms = note_queue_B[msg.channel][
|
),
|
||||||
note_queue_A[msg.channel].index(
|
note_=(
|
||||||
(msg.note, channel_program[msg.channel])
|
channel_controler[msg.channel][MIDI_PROGRAM]
|
||||||
)
|
if msg.channel == 9
|
||||||
]
|
else msg.note
|
||||||
note_queue_A[msg.channel].remove(
|
),
|
||||||
(msg.note, channel_program[msg.channel])
|
percussive_=(msg.channel == 9),
|
||||||
|
volume_=channel_controler[msg.channel][MIDI_VOLUME],
|
||||||
|
velocity_=_velocity,
|
||||||
|
panning_=channel_controler[msg.channel][MIDI_PAN],
|
||||||
|
start_time_=_ms, # 微秒
|
||||||
|
duration_=microseconds - _ms, # 微秒
|
||||||
|
play_speed=speed,
|
||||||
|
midi_reference_table=(
|
||||||
|
percussion_note_rtable
|
||||||
|
if msg.channel == 9
|
||||||
|
else pitched_note_rtable
|
||||||
|
),
|
||||||
|
volume_processing_method_=vol_processing_function,
|
||||||
|
panning_processing_method_=pan_processing_function,
|
||||||
|
note_table_replacement=note_rtable_replacement,
|
||||||
)
|
)
|
||||||
note_queue_B[msg.channel].remove((_velocity, _ms))
|
)
|
||||||
|
note_count += 1
|
||||||
midi_channels[msg.channel].append(
|
if that_note.sound_name in note_count_per_instrument.keys():
|
||||||
that_note := midi_msgs_to_minenote_using_kami_respack(
|
note_count_per_instrument[that_note.sound_name] += 1
|
||||||
inst_=(
|
|
||||||
msg.note
|
|
||||||
if msg.channel == 9
|
|
||||||
else channel_program[msg.channel]
|
|
||||||
),
|
|
||||||
note_=(
|
|
||||||
channel_program[msg.channel]
|
|
||||||
if msg.channel == 9
|
|
||||||
else msg.note
|
|
||||||
),
|
|
||||||
percussive_=(msg.channel == 9),
|
|
||||||
velocity_=_velocity,
|
|
||||||
start_time_=_ms, # 微秒
|
|
||||||
duration_=microseconds - _ms, # 微秒
|
|
||||||
play_speed=speed,
|
|
||||||
midi_reference_table=(
|
|
||||||
percussion_note_rtable
|
|
||||||
if msg.channel == 9
|
|
||||||
else pitched_note_rtable
|
|
||||||
),
|
|
||||||
volume_processing_method_=vol_processing_function,
|
|
||||||
note_table_replacement=note_rtable_replacement,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
note_count += 1
|
|
||||||
if that_note.sound_name in note_count_per_instrument.keys():
|
|
||||||
note_count_per_instrument[that_note.sound_name] += 1
|
|
||||||
else:
|
|
||||||
note_count_per_instrument[that_note.sound_name] = 1
|
|
||||||
else:
|
else:
|
||||||
if ignore_mismatch_error:
|
note_count_per_instrument[that_note.sound_name] = 1
|
||||||
print(
|
else:
|
||||||
"[WARRING] MIDI格式错误 音符不匹配 {} 无法在上文中找到与之匹配的音符开音消息".format(
|
if ignore_mismatch_error:
|
||||||
msg
|
print(
|
||||||
)
|
"[WARRING] MIDI格式错误 音符不匹配 {} 无法在上文中找到与之匹配的音符开音消息".format(
|
||||||
)
|
msg
|
||||||
else:
|
|
||||||
raise NoteOnOffMismatchError(
|
|
||||||
"当前的MIDI很可能有损坏之嫌……",
|
|
||||||
msg,
|
|
||||||
"无法在上文中找到与之匹配的音符开音消息。",
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise NoteOnOffMismatchError(
|
||||||
|
"当前的MIDI很可能有损坏之嫌……",
|
||||||
|
msg,
|
||||||
|
"无法在上文中找到与之匹配的音符开音消息。",
|
||||||
|
)
|
||||||
|
|
||||||
"""整合后的音乐通道格式
|
"""整合后的音乐通道格式
|
||||||
每个通道包括若干消息元素其中逃不过这三种:
|
每个通道包括若干消息元素其中逃不过这三种:
|
||||||
@ -214,7 +237,6 @@ class FutureMidiConvertKamiRES(MidiConvert):
|
|||||||
note_count_per_instrument,
|
note_count_per_instrument,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_command_list_in_score(
|
def to_command_list_in_score(
|
||||||
self,
|
self,
|
||||||
scoreboard_name: str = "mscplay",
|
scoreboard_name: str = "mscplay",
|
||||||
@ -362,7 +384,6 @@ class FutureMidiConvertKamiRES(MidiConvert):
|
|||||||
return self.music_command_list, notes_list[-1].start_tick, max_multi + 1
|
return self.music_command_list, notes_list[-1].start_tick, max_multi + 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FutureMidiConvertJavaE(MidiConvert):
|
class FutureMidiConvertJavaE(MidiConvert):
|
||||||
|
|
||||||
def form_java_progress_bar(
|
def form_java_progress_bar(
|
||||||
|
@ -164,11 +164,13 @@ class MusicSequence:
|
|||||||
mismatch_error_ignorance: bool = True,
|
mismatch_error_ignorance: bool = True,
|
||||||
speed_multiplier: float = 1,
|
speed_multiplier: float = 1,
|
||||||
default_midi_program: int = MIDI_DEFAULT_PROGRAM_VALUE,
|
default_midi_program: int = MIDI_DEFAULT_PROGRAM_VALUE,
|
||||||
|
default_midi_volume: int = MIDI_DEFAULT_VOLUME_VALUE,
|
||||||
default_tempo: int = mido.midifiles.midifiles.DEFAULT_TEMPO,
|
default_tempo: int = mido.midifiles.midifiles.DEFAULT_TEMPO,
|
||||||
pitched_note_referance_table: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
pitched_note_referance_table: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
||||||
percussion_note_referance_table: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
percussion_note_referance_table: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
||||||
minimum_vol: float = 0.1,
|
minimum_vol: float = 0.1,
|
||||||
volume_processing_function: FittingFunctionType = natural_curve,
|
volume_processing_function: FittingFunctionType = natural_curve,
|
||||||
|
panning_processing_function: FittingFunctionType = panning_2_rotation_linear,
|
||||||
deviation: float = 0,
|
deviation: float = 0,
|
||||||
note_referance_table_replacement: Dict[str, str] = {},
|
note_referance_table_replacement: Dict[str, str] = {},
|
||||||
):
|
):
|
||||||
@ -177,7 +179,7 @@ class MusicSequence:
|
|||||||
|
|
||||||
Paramaters
|
Paramaters
|
||||||
==========
|
==========
|
||||||
mido_file: mido.MidiFile 对象
|
mido_file: mido.MidiFile
|
||||||
需要处理的midi对象
|
需要处理的midi对象
|
||||||
midi_music_name: str
|
midi_music_name: str
|
||||||
音乐名称
|
音乐名称
|
||||||
@ -186,7 +188,9 @@ class MusicSequence:
|
|||||||
speed_multiplier: float
|
speed_multiplier: float
|
||||||
音乐播放速度倍数
|
音乐播放速度倍数
|
||||||
default_midi_program: int
|
default_midi_program: int
|
||||||
默认的MIDI Program值
|
默认的 MIDI Program值
|
||||||
|
default_midi_volume: int
|
||||||
|
默认的 MIDI 音量
|
||||||
default_tempo: int
|
default_tempo: int
|
||||||
默认的MIDI TEMPO值
|
默认的MIDI TEMPO值
|
||||||
pitched_note_referance_table: Dict[int, Tuple[str, int]]
|
pitched_note_referance_table: Dict[int, Tuple[str, int]]
|
||||||
@ -196,7 +200,9 @@ class MusicSequence:
|
|||||||
minimum_vol: float
|
minimum_vol: float
|
||||||
播放的最小音量 应为 (0,1] 范围内的小数
|
播放的最小音量 应为 (0,1] 范围内的小数
|
||||||
volume_processing_function: Callable[[float], float]
|
volume_processing_function: Callable[[float], float]
|
||||||
声像偏移拟合函数
|
音量对播放距离的拟合函数
|
||||||
|
panning_processing_function: Callable[[float], float]
|
||||||
|
声像偏移对播放旋转角度的拟合函数
|
||||||
deviation: float
|
deviation: float
|
||||||
全曲音调偏移值
|
全曲音调偏移值
|
||||||
note_referance_table_replacement: Dict[str, str]
|
note_referance_table_replacement: Dict[str, str]
|
||||||
@ -210,13 +216,15 @@ class MusicSequence:
|
|||||||
inst_note_count,
|
inst_note_count,
|
||||||
) = cls.to_music_note_channels(
|
) = cls.to_music_note_channels(
|
||||||
midi=mido_file,
|
midi=mido_file,
|
||||||
|
ignore_mismatch_error=mismatch_error_ignorance,
|
||||||
speed=speed_multiplier,
|
speed=speed_multiplier,
|
||||||
|
default_program_value=default_midi_program,
|
||||||
|
default_volume_value=default_midi_volume,
|
||||||
|
default_tempo_value=default_tempo,
|
||||||
pitched_note_rtable=pitched_note_referance_table,
|
pitched_note_rtable=pitched_note_referance_table,
|
||||||
percussion_note_rtable=percussion_note_referance_table,
|
percussion_note_rtable=percussion_note_referance_table,
|
||||||
default_program_value=default_midi_program,
|
|
||||||
default_tempo_value=default_tempo,
|
|
||||||
vol_processing_function=volume_processing_function,
|
vol_processing_function=volume_processing_function,
|
||||||
ignore_mismatch_error=mismatch_error_ignorance,
|
pan_processing_function=panning_processing_function,
|
||||||
note_rtable_replacement=note_referance_table_replacement,
|
note_rtable_replacement=note_referance_table_replacement,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@ -239,7 +247,7 @@ class MusicSequence:
|
|||||||
verify: bool = True,
|
verify: bool = True,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
从字节码导入音乐序列,目前支持 MSQ 第二、三版和 FSQ 第一版。
|
从字节码导入音乐序列,目前支持 MSQ 第二、三、四版和 FSQ 第一、二版。
|
||||||
|
|
||||||
Paramaters
|
Paramaters
|
||||||
==========
|
==========
|
||||||
@ -250,7 +258,9 @@ class MusicSequence:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if bytes_buffer_in[:4] == b"MSQ!":
|
if bytes_buffer_in[:4] in (b"MSQ!", b"MSQ$"):
|
||||||
|
|
||||||
|
note_format_v3 = bytes_buffer_in[0] == b"MSQ$"
|
||||||
|
|
||||||
group_1 = int.from_bytes(bytes_buffer_in[4:6], "big", signed=False)
|
group_1 = int.from_bytes(bytes_buffer_in[4:6], "big", signed=False)
|
||||||
group_2 = int.from_bytes(bytes_buffer_in[6:8], "big", signed=False)
|
group_2 = int.from_bytes(bytes_buffer_in[6:8], "big", signed=False)
|
||||||
@ -289,6 +299,11 @@ class MusicSequence:
|
|||||||
code_buffer=bytes_buffer_in[stt_index:end_index],
|
code_buffer=bytes_buffer_in[stt_index:end_index],
|
||||||
is_high_time_precision=high_quantity,
|
is_high_time_precision=high_quantity,
|
||||||
)
|
)
|
||||||
|
if note_format_v3
|
||||||
|
else decode_note_bytes_v2(
|
||||||
|
bytes_buffer_in[stt_index:end_index],
|
||||||
|
is_high_time_precision=high_quantity,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
channel_note_count += 1
|
channel_note_count += 1
|
||||||
stt_index = end_index
|
stt_index = end_index
|
||||||
@ -371,7 +386,9 @@ class MusicSequence:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
elif bytes_buffer_in[:4] == b"FSQ!":
|
elif bytes_buffer_in[:4] in (b"FSQ!", b"FSQ$"):
|
||||||
|
|
||||||
|
note_format_v3 = bytes_buffer_in[:4] == b"FSQ$"
|
||||||
|
|
||||||
group_1 = int.from_bytes(bytes_buffer_in[4:6], "big", signed=False)
|
group_1 = int.from_bytes(bytes_buffer_in[4:6], "big", signed=False)
|
||||||
group_2 = int.from_bytes(bytes_buffer_in[6:8], "big", signed=False)
|
group_2 = int.from_bytes(bytes_buffer_in[6:8], "big", signed=False)
|
||||||
@ -436,9 +453,16 @@ class MusicSequence:
|
|||||||
+ high_quantity
|
+ high_quantity
|
||||||
+ (bytes_buffer_in[stt_index] >> 2)
|
+ (bytes_buffer_in[stt_index] >> 2)
|
||||||
)
|
)
|
||||||
_read_note = MineNote.decode(
|
_read_note = (
|
||||||
code_buffer=bytes_buffer_in[stt_index:end_index],
|
MineNote.decode(
|
||||||
is_high_time_precision=high_quantity,
|
code_buffer=bytes_buffer_in[stt_index:end_index],
|
||||||
|
is_high_time_precision=high_quantity,
|
||||||
|
)
|
||||||
|
if note_format_v3
|
||||||
|
else decode_note_bytes_v2(
|
||||||
|
code_buffer_bytes=bytes_buffer_in[stt_index:end_index],
|
||||||
|
is_high_time_precision=high_quantity,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
stt_index = end_index
|
stt_index = end_index
|
||||||
except Exception as _err:
|
except Exception as _err:
|
||||||
@ -515,8 +539,8 @@ class MusicSequence:
|
|||||||
+ (bytes_buffer_in[stt_index] >> 2)
|
+ (bytes_buffer_in[stt_index] >> 2)
|
||||||
)
|
)
|
||||||
channels_[channel_index].append(
|
channels_[channel_index].append(
|
||||||
MineNote.decode(
|
decode_note_bytes_v2(
|
||||||
code_buffer=bytes_buffer_in[stt_index:end_index],
|
code_buffer_bytes=bytes_buffer_in[stt_index:end_index],
|
||||||
is_high_time_precision=high_quantity,
|
is_high_time_precision=high_quantity,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -553,7 +577,7 @@ class MusicSequence:
|
|||||||
try:
|
try:
|
||||||
end_index = stt_index + 14 + (bytes_buffer_in[stt_index] >> 2)
|
end_index = stt_index + 14 + (bytes_buffer_in[stt_index] >> 2)
|
||||||
channels_[channel_index].append(
|
channels_[channel_index].append(
|
||||||
MineNote.decode(bytes_buffer_in[stt_index:end_index])
|
decode_note_bytes_v1(bytes_buffer_in[stt_index:end_index])
|
||||||
)
|
)
|
||||||
stt_index = end_index
|
stt_index = end_index
|
||||||
except:
|
except:
|
||||||
@ -631,8 +655,12 @@ class MusicSequence:
|
|||||||
# (已废弃)
|
# (已废弃)
|
||||||
# 第二版 MSQ 的码头: MSQ@ 字串编码: GB18030
|
# 第二版 MSQ 的码头: MSQ@ 字串编码: GB18030
|
||||||
|
|
||||||
|
#
|
||||||
# 第三版 MSQ 的码头: MSQ! 字串编码: GB18030 大端字节序
|
# 第三版 MSQ 的码头: MSQ! 字串编码: GB18030 大端字节序
|
||||||
# 第一版 FSQ 的码头: FSQ!
|
# 第一版 FSQ 的码头: FSQ!
|
||||||
|
# 第四版 MSQ 和 第二版 FSQ 的码头分别为 MSQ$ 和 FSQ$
|
||||||
|
# 其序列存储格式与第三版一致,但在每个音频的识别上做了调整
|
||||||
|
# 音频内容的调整见 subclass.py
|
||||||
|
|
||||||
# 音乐名称长度 6 位 支持到 63
|
# 音乐名称长度 6 位 支持到 63
|
||||||
# 最小音量 minimum_volume 10 位 最大支持 1023 即三位小数
|
# 最小音量 minimum_volume 10 位 最大支持 1023 即三位小数
|
||||||
@ -786,11 +814,13 @@ class MusicSequence:
|
|||||||
midi: mido.MidiFile,
|
midi: mido.MidiFile,
|
||||||
ignore_mismatch_error: bool = True,
|
ignore_mismatch_error: bool = True,
|
||||||
speed: float = 1.0,
|
speed: float = 1.0,
|
||||||
default_program_value: int = -1,
|
default_program_value: int = MIDI_DEFAULT_PROGRAM_VALUE,
|
||||||
|
default_volume_value: int = MIDI_DEFAULT_VOLUME_VALUE,
|
||||||
default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO,
|
default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO,
|
||||||
pitched_note_rtable: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
pitched_note_rtable: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
||||||
percussion_note_rtable: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
percussion_note_rtable: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
||||||
vol_processing_function: FittingFunctionType = natural_curve,
|
vol_processing_function: FittingFunctionType = natural_curve,
|
||||||
|
pan_processing_function: FittingFunctionType = panning_2_rotation_trigonometric,
|
||||||
note_rtable_replacement: Dict[str, str] = {},
|
note_rtable_replacement: Dict[str, str] = {},
|
||||||
) -> Tuple[MineNoteChannelType, int, Dict[str, int]]:
|
) -> Tuple[MineNoteChannelType, int, Dict[str, int]]:
|
||||||
"""
|
"""
|
||||||
@ -804,6 +834,8 @@ class MusicSequence:
|
|||||||
音乐播放速度倍数
|
音乐播放速度倍数
|
||||||
default_program_value: int
|
default_program_value: int
|
||||||
默认的 MIDI 乐器值
|
默认的 MIDI 乐器值
|
||||||
|
default_volume_value: int
|
||||||
|
默认的通道音量值
|
||||||
default_tempo_value: int
|
default_tempo_value: int
|
||||||
默认的 MIDI TEMPO 值
|
默认的 MIDI TEMPO 值
|
||||||
pitched_note_rtable: Dict[int, Tuple[str, int]]
|
pitched_note_rtable: Dict[int, Tuple[str, int]]
|
||||||
@ -811,7 +843,9 @@ class MusicSequence:
|
|||||||
percussion_note_rtable: Dict[int, Tuple[str, int]]
|
percussion_note_rtable: Dict[int, Tuple[str, int]]
|
||||||
打击乐器Midi-MC对照表
|
打击乐器Midi-MC对照表
|
||||||
vol_processing_function: Callable[[float], float]
|
vol_processing_function: Callable[[float], float]
|
||||||
声像偏移拟合函数
|
音量对播放距离的拟合函数
|
||||||
|
pan_processing_function: Callable[[float], float]
|
||||||
|
声像偏移对播放旋转角度的拟合函数
|
||||||
note_rtable_replacement: Dict[str, str]
|
note_rtable_replacement: Dict[str, str]
|
||||||
音符名称替换表,此表用于对 Minecraft 乐器名称进行替换,而非 Midi Program 的替换
|
音符名称替换表,此表用于对 Minecraft 乐器名称进行替换,而非 Midi Program 的替换
|
||||||
|
|
||||||
@ -826,9 +860,15 @@ class MusicSequence:
|
|||||||
|
|
||||||
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
||||||
midi_channels: MineNoteChannelType = empty_midi_channels(default_staff=[])
|
midi_channels: MineNoteChannelType = empty_midi_channels(default_staff=[])
|
||||||
channel_program: Dict[int, int] = empty_midi_channels(
|
|
||||||
default_staff=default_program_value
|
channel_controler: Dict[int, Dict[str, int]] = empty_midi_channels(
|
||||||
|
default_staff={
|
||||||
|
MIDI_PROGRAM: default_program_value,
|
||||||
|
MIDI_VOLUME: default_volume_value,
|
||||||
|
MIDI_PAN: 64,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
tempo = default_tempo_value
|
tempo = default_tempo_value
|
||||||
note_count = 0
|
note_count = 0
|
||||||
note_count_per_instrument: Dict[str, int] = {}
|
note_count_per_instrument: Dict[str, int] = {}
|
||||||
@ -863,76 +903,84 @@ class MusicSequence:
|
|||||||
# 简化
|
# 简化
|
||||||
if msg.type == "set_tempo":
|
if msg.type == "set_tempo":
|
||||||
tempo = msg.tempo
|
tempo = msg.tempo
|
||||||
else:
|
elif msg.type == "program_change":
|
||||||
if msg.type == "program_change":
|
channel_controler[msg.channel][MIDI_PROGRAM] = msg.program
|
||||||
channel_program[msg.channel] = msg.program
|
|
||||||
|
|
||||||
elif msg.type == "note_on" and msg.velocity != 0:
|
elif msg.is_cc(7):
|
||||||
note_queue_A[msg.channel].append(
|
channel_controler[msg.channel][MIDI_VOLUME] = msg.value
|
||||||
(msg.note, channel_program[msg.channel])
|
elif msg.is_cc(10):
|
||||||
|
channel_controler[msg.channel][MIDI_PAN] = msg.value
|
||||||
|
|
||||||
|
elif msg.type == "note_on" and msg.velocity != 0:
|
||||||
|
note_queue_A[msg.channel].append(
|
||||||
|
(msg.note, channel_controler[msg.channel][MIDI_PROGRAM])
|
||||||
|
)
|
||||||
|
note_queue_B[msg.channel].append((msg.velocity, microseconds))
|
||||||
|
|
||||||
|
elif (msg.type == "note_off") or (
|
||||||
|
msg.type == "note_on" and msg.velocity == 0
|
||||||
|
):
|
||||||
|
if (
|
||||||
|
msg.note,
|
||||||
|
channel_controler[msg.channel][MIDI_PROGRAM],
|
||||||
|
) in note_queue_A[msg.channel]:
|
||||||
|
_velocity, _ms = note_queue_B[msg.channel][
|
||||||
|
note_queue_A[msg.channel].index(
|
||||||
|
(msg.note, channel_controler[msg.channel][MIDI_PROGRAM])
|
||||||
|
)
|
||||||
|
]
|
||||||
|
note_queue_A[msg.channel].remove(
|
||||||
|
(msg.note, channel_controler[msg.channel][MIDI_PROGRAM])
|
||||||
)
|
)
|
||||||
note_queue_B[msg.channel].append((msg.velocity, microseconds))
|
note_queue_B[msg.channel].remove((_velocity, _ms))
|
||||||
|
|
||||||
elif (msg.type == "note_off") or (
|
midi_channels[msg.channel].append(
|
||||||
msg.type == "note_on" and msg.velocity == 0
|
that_note := midi_msgs_to_minenote(
|
||||||
):
|
inst_=(
|
||||||
if (msg.note, channel_program[msg.channel]) in note_queue_A[
|
msg.note
|
||||||
msg.channel
|
if msg.channel == 9
|
||||||
]:
|
else channel_controler[msg.channel][MIDI_PROGRAM]
|
||||||
_velocity, _ms = note_queue_B[msg.channel][
|
),
|
||||||
note_queue_A[msg.channel].index(
|
note_=(
|
||||||
(msg.note, channel_program[msg.channel])
|
channel_controler[msg.channel][MIDI_PROGRAM]
|
||||||
)
|
if msg.channel == 9
|
||||||
]
|
else msg.note
|
||||||
note_queue_A[msg.channel].remove(
|
),
|
||||||
(msg.note, channel_program[msg.channel])
|
percussive_=(msg.channel == 9),
|
||||||
|
volume_=channel_controler[msg.channel][MIDI_VOLUME],
|
||||||
|
velocity_=_velocity,
|
||||||
|
panning_=channel_controler[msg.channel][MIDI_PAN],
|
||||||
|
start_time_=_ms, # 微秒
|
||||||
|
duration_=microseconds - _ms, # 微秒
|
||||||
|
play_speed=speed,
|
||||||
|
midi_reference_table=(
|
||||||
|
percussion_note_rtable
|
||||||
|
if msg.channel == 9
|
||||||
|
else pitched_note_rtable
|
||||||
|
),
|
||||||
|
volume_processing_method_=vol_processing_function,
|
||||||
|
panning_processing_method_=pan_processing_function,
|
||||||
|
note_table_replacement=note_rtable_replacement,
|
||||||
)
|
)
|
||||||
note_queue_B[msg.channel].remove((_velocity, _ms))
|
)
|
||||||
|
note_count += 1
|
||||||
midi_channels[msg.channel].append(
|
if that_note.sound_name in note_count_per_instrument.keys():
|
||||||
that_note := midi_msgs_to_minenote(
|
note_count_per_instrument[that_note.sound_name] += 1
|
||||||
inst_=(
|
|
||||||
msg.note
|
|
||||||
if msg.channel == 9
|
|
||||||
else channel_program[msg.channel]
|
|
||||||
),
|
|
||||||
note_=(
|
|
||||||
channel_program[msg.channel]
|
|
||||||
if msg.channel == 9
|
|
||||||
else msg.note
|
|
||||||
),
|
|
||||||
velocity_=_velocity,
|
|
||||||
start_time_=_ms, # 微秒
|
|
||||||
duration_=microseconds - _ms, # 微秒
|
|
||||||
percussive_=(msg.channel == 9),
|
|
||||||
play_speed=speed,
|
|
||||||
midi_reference_table=(
|
|
||||||
percussion_note_rtable
|
|
||||||
if msg.channel == 9
|
|
||||||
else pitched_note_rtable
|
|
||||||
),
|
|
||||||
volume_processing_method_=vol_processing_function,
|
|
||||||
note_table_replacement=note_rtable_replacement,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
note_count += 1
|
|
||||||
if that_note.sound_name in note_count_per_instrument.keys():
|
|
||||||
note_count_per_instrument[that_note.sound_name] += 1
|
|
||||||
else:
|
|
||||||
note_count_per_instrument[that_note.sound_name] = 1
|
|
||||||
else:
|
else:
|
||||||
if ignore_mismatch_error:
|
note_count_per_instrument[that_note.sound_name] = 1
|
||||||
print(
|
else:
|
||||||
"[WARRING] MIDI格式错误 音符不匹配 {} 无法在上文中找到与之匹配的音符开音消息".format(
|
if ignore_mismatch_error:
|
||||||
msg
|
print(
|
||||||
)
|
"[WARRING] MIDI格式错误 音符不匹配 {} 无法在上文中找到与之匹配的音符开音消息".format(
|
||||||
)
|
msg
|
||||||
else:
|
|
||||||
raise NoteOnOffMismatchError(
|
|
||||||
"当前的MIDI很可能有损坏之嫌……",
|
|
||||||
msg,
|
|
||||||
"无法在上文中找到与之匹配的音符开音消息。",
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise NoteOnOffMismatchError(
|
||||||
|
"当前的MIDI很可能有损坏之嫌……",
|
||||||
|
msg,
|
||||||
|
"无法在上文中找到与之匹配的音符开音消息。",
|
||||||
|
)
|
||||||
|
|
||||||
"""整合后的音乐通道格式
|
"""整合后的音乐通道格式
|
||||||
每个通道包括若干消息元素其中逃不过这三种:
|
每个通道包括若干消息元素其中逃不过这三种:
|
||||||
@ -985,12 +1033,14 @@ class MidiConvert(MusicSequence):
|
|||||||
ignore_mismatch_error: bool = True,
|
ignore_mismatch_error: bool = True,
|
||||||
playment_speed: float = 1,
|
playment_speed: float = 1,
|
||||||
default_midi_program_value: int = MIDI_DEFAULT_PROGRAM_VALUE,
|
default_midi_program_value: int = MIDI_DEFAULT_PROGRAM_VALUE,
|
||||||
|
default_midi_volume_value: int = MIDI_DEFAULT_VOLUME_VALUE,
|
||||||
default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO,
|
default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO,
|
||||||
pitched_note_rtable: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
pitched_note_rtable: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
||||||
percussion_note_rtable: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
percussion_note_rtable: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
||||||
enable_old_exe_format: bool = False,
|
enable_old_exe_format: bool = False,
|
||||||
minimum_volume: float = 0.1,
|
minimum_volume: float = 0.1,
|
||||||
vol_processing_function: FittingFunctionType = natural_curve,
|
vol_processing_function: FittingFunctionType = natural_curve,
|
||||||
|
pan_processing_function: FittingFunctionType = panning_2_rotation_trigonometric,
|
||||||
pitch_deviation: float = 0,
|
pitch_deviation: float = 0,
|
||||||
note_rtable_replacement: Dict[str, str] = {},
|
note_rtable_replacement: Dict[str, str] = {},
|
||||||
):
|
):
|
||||||
@ -1009,6 +1059,8 @@ class MidiConvert(MusicSequence):
|
|||||||
音乐播放速度倍数
|
音乐播放速度倍数
|
||||||
default_midi_program_value: int
|
default_midi_program_value: int
|
||||||
默认的 MIDI Program 值,当 Midi 文件没有指定 Program 值时,使用此值
|
默认的 MIDI Program 值,当 Midi 文件没有指定 Program 值时,使用此值
|
||||||
|
default_midi_volume_value: int
|
||||||
|
默认的 MIDI 音量值,当 Midi 文件没有指定此值时,使用此值
|
||||||
default_tempo_value: int
|
default_tempo_value: int
|
||||||
默认的 MIDI TEMPO 值,同上理
|
默认的 MIDI TEMPO 值,同上理
|
||||||
pitched_note_rtable: Dict[int, Tuple[str, int]]
|
pitched_note_rtable: Dict[int, Tuple[str, int]]
|
||||||
@ -1020,7 +1072,9 @@ class MidiConvert(MusicSequence):
|
|||||||
minimum_volume: float
|
minimum_volume: float
|
||||||
最小播放音量
|
最小播放音量
|
||||||
vol_processing_function: Callable[[float], float]
|
vol_processing_function: Callable[[float], float]
|
||||||
声像偏移拟合函数
|
音量对播放距离的拟合函数
|
||||||
|
pan_processing_function: Callable[[float], float]
|
||||||
|
声像偏移对播放旋转角度的拟合函数
|
||||||
pitch_deviation: float
|
pitch_deviation: float
|
||||||
音调偏移量,手动指定全曲音调偏移量
|
音调偏移量,手动指定全曲音调偏移量
|
||||||
note_rtable_replacement: Dict[str, str]
|
note_rtable_replacement: Dict[str, str]
|
||||||
@ -1044,11 +1098,13 @@ class MidiConvert(MusicSequence):
|
|||||||
mismatch_error_ignorance=ignore_mismatch_error,
|
mismatch_error_ignorance=ignore_mismatch_error,
|
||||||
speed_multiplier=playment_speed,
|
speed_multiplier=playment_speed,
|
||||||
default_midi_program=default_midi_program_value,
|
default_midi_program=default_midi_program_value,
|
||||||
|
default_midi_volume=default_midi_volume_value,
|
||||||
default_tempo=default_tempo_value,
|
default_tempo=default_tempo_value,
|
||||||
pitched_note_referance_table=pitched_note_rtable,
|
pitched_note_referance_table=pitched_note_rtable,
|
||||||
percussion_note_referance_table=percussion_note_rtable,
|
percussion_note_referance_table=percussion_note_rtable,
|
||||||
minimum_vol=minimum_volume,
|
minimum_vol=minimum_volume,
|
||||||
volume_processing_function=vol_processing_function,
|
volume_processing_function=vol_processing_function,
|
||||||
|
panning_processing_function=pan_processing_function,
|
||||||
deviation=pitch_deviation,
|
deviation=pitch_deviation,
|
||||||
note_referance_table_replacement=note_rtable_replacement,
|
note_referance_table_replacement=note_rtable_replacement,
|
||||||
)
|
)
|
||||||
@ -1060,12 +1116,14 @@ class MidiConvert(MusicSequence):
|
|||||||
mismatch_error_ignorance: bool = True,
|
mismatch_error_ignorance: bool = True,
|
||||||
play_speed: float = 1,
|
play_speed: float = 1,
|
||||||
default_midi_program: int = MIDI_DEFAULT_PROGRAM_VALUE,
|
default_midi_program: int = MIDI_DEFAULT_PROGRAM_VALUE,
|
||||||
|
default_midi_volume: int = MIDI_DEFAULT_VOLUME_VALUE,
|
||||||
default_tempo: int = mido.midifiles.midifiles.DEFAULT_TEMPO,
|
default_tempo: int = mido.midifiles.midifiles.DEFAULT_TEMPO,
|
||||||
pitched_note_table: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
pitched_note_table: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
||||||
percussion_note_table: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
percussion_note_table: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
||||||
old_exe_format: bool = False,
|
old_exe_format: bool = False,
|
||||||
min_volume: float = 0.1,
|
min_volume: float = 0.1,
|
||||||
vol_processing_func: FittingFunctionType = natural_curve,
|
vol_processing_func: FittingFunctionType = natural_curve,
|
||||||
|
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] = {},
|
||||||
):
|
):
|
||||||
@ -1082,6 +1140,8 @@ class MidiConvert(MusicSequence):
|
|||||||
音乐播放速度倍数
|
音乐播放速度倍数
|
||||||
default_midi_program: int
|
default_midi_program: int
|
||||||
默认的 MIDI Program 值,当 Midi 文件没有指定 Program 值时,使用此值
|
默认的 MIDI Program 值,当 Midi 文件没有指定 Program 值时,使用此值
|
||||||
|
default_midi_volume: int
|
||||||
|
默认每个通道的音量值,当 Midi 文件没有指定音量值时,使用此值
|
||||||
default_tempo: int
|
default_tempo: int
|
||||||
默认的MIDI TEMPO值
|
默认的MIDI TEMPO值
|
||||||
pitched_note_table: Dict[int, Tuple[str, int]]
|
pitched_note_table: Dict[int, Tuple[str, int]]
|
||||||
@ -1093,7 +1153,9 @@ class MidiConvert(MusicSequence):
|
|||||||
min_volume: float
|
min_volume: float
|
||||||
最小播放音量
|
最小播放音量
|
||||||
vol_processing_func: Callable[[float], float]
|
vol_processing_func: Callable[[float], float]
|
||||||
声像偏移拟合函数
|
音量对播放距离的拟合函数
|
||||||
|
pan_processing_func: Callable[[float], float]
|
||||||
|
声像偏移对播放旋转角度的拟合函数
|
||||||
music_pitch_deviation: float
|
music_pitch_deviation: float
|
||||||
全曲音符的音调偏移量
|
全曲音符的音调偏移量
|
||||||
note_table_replacement: Dict[str, str]
|
note_table_replacement: Dict[str, str]
|
||||||
@ -1115,12 +1177,14 @@ class MidiConvert(MusicSequence):
|
|||||||
ignore_mismatch_error=mismatch_error_ignorance,
|
ignore_mismatch_error=mismatch_error_ignorance,
|
||||||
playment_speed=play_speed,
|
playment_speed=play_speed,
|
||||||
default_midi_program_value=default_midi_program,
|
default_midi_program_value=default_midi_program,
|
||||||
|
default_midi_volume_value=default_midi_volume,
|
||||||
default_tempo_value=default_tempo,
|
default_tempo_value=default_tempo,
|
||||||
pitched_note_rtable=pitched_note_table,
|
pitched_note_rtable=pitched_note_table,
|
||||||
percussion_note_rtable=percussion_note_table,
|
percussion_note_rtable=percussion_note_table,
|
||||||
enable_old_exe_format=old_exe_format,
|
enable_old_exe_format=old_exe_format,
|
||||||
minimum_volume=min_volume,
|
minimum_volume=min_volume,
|
||||||
vol_processing_function=vol_processing_func,
|
vol_processing_function=vol_processing_func,
|
||||||
|
pan_processing_function=pan_processing_func,
|
||||||
pitch_deviation=music_pitch_deviation,
|
pitch_deviation=music_pitch_deviation,
|
||||||
note_rtable_replacement=note_table_replacement,
|
note_rtable_replacement=note_table_replacement,
|
||||||
)
|
)
|
||||||
|
@ -16,9 +16,9 @@ Terms & Conditions: License.md in the root directory
|
|||||||
# Email TriM-Organization@hotmail.com
|
# Email TriM-Organization@hotmail.com
|
||||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
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
|
from typing import Optional, Any, List, Tuple, Union, Dict
|
||||||
|
|
||||||
from .constants import MC_PITCHED_INSTRUMENT_LIST
|
from .constants import MC_PITCHED_INSTRUMENT_LIST
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ class MineNote:
|
|||||||
"""midi音高"""
|
"""midi音高"""
|
||||||
|
|
||||||
velocity: int
|
velocity: int
|
||||||
"""响度(力度)"""
|
"""力度"""
|
||||||
|
|
||||||
start_tick: int
|
start_tick: int
|
||||||
"""开始之时 命令刻"""
|
"""开始之时 命令刻"""
|
||||||
@ -48,8 +48,11 @@ class MineNote:
|
|||||||
percussive: bool
|
percussive: bool
|
||||||
"""是否作为打击乐器启用"""
|
"""是否作为打击乐器启用"""
|
||||||
|
|
||||||
position_displacement: Tuple[float, float, float]
|
sound_distance: float
|
||||||
"""声像位移"""
|
"""声源距离"""
|
||||||
|
|
||||||
|
sound_azimuth: Tuple[float, float]
|
||||||
|
"""声源方位"""
|
||||||
|
|
||||||
extra_info: Any
|
extra_info: Any
|
||||||
"""你觉得放什么好?"""
|
"""你觉得放什么好?"""
|
||||||
@ -63,19 +66,26 @@ class MineNote:
|
|||||||
last_time: int,
|
last_time: int,
|
||||||
mass_precision_time: int = 0,
|
mass_precision_time: int = 0,
|
||||||
is_percussion: Optional[bool] = None,
|
is_percussion: Optional[bool] = None,
|
||||||
displacement: Optional[Tuple[float, float, float]] = None,
|
distance: Optional[float] = None,
|
||||||
extra_information: Optional[Any] = None,
|
azimuth: Optional[Tuple[float, float]] = None,
|
||||||
|
extra_information: Optional[Dict[str, Any]] = None,
|
||||||
):
|
):
|
||||||
"""用于存储单个音符的类
|
"""用于存储单个音符的类
|
||||||
|
|
||||||
:param mc_sound_name:`str` 《我的世界》声音ID
|
:param mc_sound_name:`str` 《我的世界》声音ID
|
||||||
:param midi_pitch:`int` midi音高
|
:param midi_pitch:`int` midi音高
|
||||||
:param midi_velocity:`int` midi响度(力度)
|
:param midi_velocity:`int` midi响度(力度)
|
||||||
:param start_time:`int` 开始之时(命令刻)
|
:param start_time:`int` 开始之时(命令刻)
|
||||||
注:此处的时间是用从乐曲开始到当前的毫秒数
|
注:此处的时间是用从乐曲开始到当前的刻数
|
||||||
:param last_time:`int` 音符延续时间(命令刻)
|
:param last_time:`int` 音符延续时间(命令刻)
|
||||||
:param mass_precision_time:`int` 高精度的开始时间偏移量(1/1250秒)
|
:param mass_precision_time:`int` 高精度的开始时间偏移量(1/1250秒)
|
||||||
:param is_percussion:`bool` 是否作为打击乐器
|
:param is_percussion:`bool` 是否作为打击乐器
|
||||||
:param displacement:`tuple[int,int,int]` 声像位移
|
:param distance: `float` 发声源距离玩家的距离(半径 `r`)
|
||||||
|
注:距离越近,音量越高,默认为 0。此参数可以与音量成某种函数关系。
|
||||||
|
:param azimuth:`tuple[float, float]` 声源方位
|
||||||
|
注:此参数为tuple,包含两个元素,分别表示:
|
||||||
|
`rV` 发声源在竖直(上下)轴上,从玩家视角正前方开始,向顺时针旋转的角度
|
||||||
|
`rH` 发声源在水平(左右)轴上,从玩家视角正前方开始,向上(到达玩家正上方顶点后变为向下,以此类推的旋转)旋转的角度
|
||||||
:param extra_information:`Any` 附加信息"""
|
:param extra_information:`Any` 附加信息"""
|
||||||
self.sound_name: str = mc_sound_name
|
self.sound_name: str = mc_sound_name
|
||||||
"""乐器ID"""
|
"""乐器ID"""
|
||||||
@ -97,13 +107,85 @@ class MineNote:
|
|||||||
)
|
)
|
||||||
"""是否为打击乐器"""
|
"""是否为打击乐器"""
|
||||||
|
|
||||||
self.position_displacement = (
|
self.sound_distance = (
|
||||||
(0, 0, 0) if (displacement is None) else displacement
|
(16 if distance > 16 else (distance if distance > 0 else 0))
|
||||||
|
if distance
|
||||||
|
else 0
|
||||||
)
|
)
|
||||||
"""声像位移"""
|
"""声源距离"""
|
||||||
|
|
||||||
|
self.sound_azimuth = (azimuth[0] % 360, azimuth[1] % 360) if azimuth else (0, 0)
|
||||||
|
"""声源方位"""
|
||||||
|
|
||||||
self.extra_info = extra_information
|
self.extra_info = extra_information
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_traditional(
|
||||||
|
cls,
|
||||||
|
mc_sound_name: str,
|
||||||
|
midi_pitch: Optional[int],
|
||||||
|
midi_velocity: int,
|
||||||
|
start_time: int,
|
||||||
|
last_time: int,
|
||||||
|
mass_precision_time: int = 0,
|
||||||
|
is_percussion: Optional[bool] = None,
|
||||||
|
displacement: Optional[Tuple[float, float, float]] = None,
|
||||||
|
extra_information: Optional[Any] = None,
|
||||||
|
):
|
||||||
|
"""用于存储单个音符的类
|
||||||
|
:param mc_sound_name:`str` 《我的世界》声音ID
|
||||||
|
:param midi_pitch:`int` midi音高
|
||||||
|
:param midi_velocity:`int` midi响度(力度)
|
||||||
|
:param start_time:`int` 开始之时(命令刻)
|
||||||
|
注:此处的时间是用从乐曲开始到当前的刻数
|
||||||
|
:param last_time:`int` 音符延续时间(命令刻)
|
||||||
|
:param mass_precision_time:`int` 高精度的开始时间偏移量(1/1250秒)
|
||||||
|
:param is_percussion:`bool` 是否作为打击乐器
|
||||||
|
:param displacement:`tuple[float,float,float]` 声像位移
|
||||||
|
:param extra_information:`Any` 附加信息"""
|
||||||
|
|
||||||
|
if displacement is None:
|
||||||
|
displacement = (0, 0, 0)
|
||||||
|
r = 0
|
||||||
|
alpha_v = 0
|
||||||
|
beta_h = 0
|
||||||
|
else:
|
||||||
|
r = sqrt(displacement[0] ** 2 + displacement[1] ** 2 + displacement[2] ** 2)
|
||||||
|
if r == 0:
|
||||||
|
alpha_v = 0
|
||||||
|
beta_h = 0
|
||||||
|
else:
|
||||||
|
beta_h = round(degrees(asin(displacement[1] / r)), 8)
|
||||||
|
if displacement[2] == 0:
|
||||||
|
alpha_v = -90 if displacement[0] > 0 else 90
|
||||||
|
else:
|
||||||
|
alpha_v = round(
|
||||||
|
degrees(atan(-displacement[0] / displacement[2])), 8
|
||||||
|
)
|
||||||
|
|
||||||
|
return cls(
|
||||||
|
mc_sound_name=mc_sound_name,
|
||||||
|
midi_pitch=midi_pitch,
|
||||||
|
midi_velocity=midi_velocity,
|
||||||
|
start_time=start_time,
|
||||||
|
last_time=last_time,
|
||||||
|
mass_precision_time=mass_precision_time,
|
||||||
|
is_percussion=is_percussion,
|
||||||
|
distance=r,
|
||||||
|
azimuth=(alpha_v, beta_h),
|
||||||
|
extra_information=extra_information,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def position_displacement(self) -> Tuple[float, float, float]:
|
||||||
|
"""声像位移"""
|
||||||
|
dk1 = self.sound_distance * round(cos(radians(self.sound_azimuth[1])), 8)
|
||||||
|
return (
|
||||||
|
-dk1 * round(sin(radians(self.sound_azimuth[0])), 8),
|
||||||
|
self.sound_distance * round(sin(radians(self.sound_azimuth[1])), 8),
|
||||||
|
dk1 * round(cos(radians(self.sound_azimuth[0])), 8),
|
||||||
|
)
|
||||||
|
|
||||||
@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类"""
|
||||||
@ -115,39 +197,25 @@ class MineNote:
|
|||||||
sound_name_length = group_1 >> 7
|
sound_name_length = group_1 >> 7
|
||||||
|
|
||||||
if code_buffer[6] & 0b1:
|
if code_buffer[6] & 0b1:
|
||||||
position_displacement_ = (
|
distance_ = (
|
||||||
int.from_bytes(
|
code_buffer[8 + sound_name_length]
|
||||||
(
|
if is_high_time_precision
|
||||||
code_buffer[8 + sound_name_length : 10 + sound_name_length]
|
else code_buffer[7 + sound_name_length]
|
||||||
if is_high_time_precision
|
) / 15
|
||||||
else code_buffer[7 + sound_name_length : 9 + sound_name_length]
|
|
||||||
),
|
group_2 = int.from_bytes(
|
||||||
"big",
|
(
|
||||||
)
|
code_buffer[9 + sound_name_length : 14 + sound_name_length]
|
||||||
/ 1000,
|
if is_high_time_precision
|
||||||
int.from_bytes(
|
else code_buffer[8 + sound_name_length : 13 + sound_name_length]
|
||||||
(
|
),
|
||||||
code_buffer[10 + sound_name_length : 12 + sound_name_length]
|
"big",
|
||||||
if is_high_time_precision
|
|
||||||
else code_buffer[9 + sound_name_length : 11 + sound_name_length]
|
|
||||||
),
|
|
||||||
"big",
|
|
||||||
)
|
|
||||||
/ 1000,
|
|
||||||
int.from_bytes(
|
|
||||||
(
|
|
||||||
code_buffer[12 + sound_name_length : 14 + sound_name_length]
|
|
||||||
if is_high_time_precision
|
|
||||||
else code_buffer[
|
|
||||||
11 + sound_name_length : 13 + sound_name_length
|
|
||||||
]
|
|
||||||
),
|
|
||||||
"big",
|
|
||||||
)
|
|
||||||
/ 1000,
|
|
||||||
)
|
)
|
||||||
|
azimuth_ = ((group_2 >> 20) / 2912, (group_2 & 0xFFFFF) / 2912)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
position_displacement_ = (0, 0, 0)
|
distance_ = 0
|
||||||
|
azimuth_ = (0, 0)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return cls(
|
return cls(
|
||||||
@ -164,7 +232,8 @@ class MineNote:
|
|||||||
last_time=duration_,
|
last_time=duration_,
|
||||||
mass_precision_time=code_buffer[7] if is_high_time_precision else 0,
|
mass_precision_time=code_buffer[7] if is_high_time_precision else 0,
|
||||||
is_percussion=percussive_,
|
is_percussion=percussive_,
|
||||||
displacement=position_displacement_,
|
distance=distance_,
|
||||||
|
azimuth=azimuth_,
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
print(code_buffer, "\n", o)
|
print(code_buffer, "\n", o)
|
||||||
@ -182,6 +251,8 @@ class MineNote:
|
|||||||
:return bytes 打包好的字节码
|
:return bytes 打包好的字节码
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# MineNote 的字节码共有三个顺次版本分别如下
|
||||||
|
|
||||||
# 字符串长度 6 位 支持到 63
|
# 字符串长度 6 位 支持到 63
|
||||||
# note_pitch 7 位 支持到 127
|
# note_pitch 7 位 支持到 127
|
||||||
# start_tick 17 位 支持到 131071 即 109.22583 分钟 合 1.8204305 小时
|
# start_tick 17 位 支持到 131071 即 109.22583 分钟 合 1.8204305 小时
|
||||||
@ -202,8 +273,12 @@ class MineNote:
|
|||||||
# 第一版编码: UTF-8
|
# 第一版编码: UTF-8
|
||||||
# 第二版编码: GB18030
|
# 第二版编码: GB18030
|
||||||
# +++
|
# +++
|
||||||
|
# (在第三版中已废弃)
|
||||||
# position_displacement 每个元素长 16 位 合 2 字节
|
# position_displacement 每个元素长 16 位 合 2 字节
|
||||||
# 共 48 位 合 6 字节 支持存储三位小数和两位整数,其值必须在 [0, 65.535] 之间
|
# 共 48 位 合 6 字节 支持存储三位小数和两位整数,其值必须在 [0, 65.535] 之间
|
||||||
|
# (在第三版中新增)
|
||||||
|
# sound_distance 8 位 支持到 255 即 16 格 合 1 字节(按值放大 15 倍存储,精度可达 1 / 15)
|
||||||
|
# sound_azimuth 每个元素长 20 位 共 40 位 合 5 字节。每个值放大 2912 倍存储,即支持到 360.08756868131866 度,精度同理
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
@ -245,9 +320,11 @@ class MineNote:
|
|||||||
+ r
|
+ r
|
||||||
+ (
|
+ (
|
||||||
(
|
(
|
||||||
round(self.position_displacement[0] * 1000).to_bytes(2, "big")
|
round(self.sound_distance * 15).to_bytes(1, "big")
|
||||||
+ round(self.position_displacement[1] * 1000).to_bytes(2, "big")
|
+ (
|
||||||
+ round(self.position_displacement[2] * 1000).to_bytes(2, "big")
|
(round(self.sound_azimuth[0] * 2912) << 20)
|
||||||
|
+ round(self.sound_azimuth[1] * 2912)
|
||||||
|
).to_bytes(5, "big")
|
||||||
)
|
)
|
||||||
if is_displacement_included
|
if is_displacement_included
|
||||||
else b""
|
else b""
|
||||||
@ -258,24 +335,32 @@ class MineNote:
|
|||||||
"""设置附加信息"""
|
"""设置附加信息"""
|
||||||
self.extra_info = sth
|
self.extra_info = sth
|
||||||
|
|
||||||
def __str__(self, is_displacement: bool = False):
|
def stringize(
|
||||||
return "{}Note(Instrument = {}, {}Velocity = {}, StartTick = {}, Duration = {}{}{})".format(
|
self, include_displacement: bool = False, include_extra_data: bool = False
|
||||||
"Percussive" if self.percussive else "",
|
) -> str:
|
||||||
self.sound_name,
|
return (
|
||||||
"" if self.percussive else "NotePitch = {}, ".format(self.note_pitch),
|
"{}Note(Instrument = {}, {}Velocity = {}, StartTick = {}, Duration = {}{}".format(
|
||||||
self.velocity,
|
"Percussive" if self.percussive else "",
|
||||||
self.start_tick,
|
self.sound_name,
|
||||||
self.duration,
|
"" if self.percussive else "NotePitch = {}, ".format(self.note_pitch),
|
||||||
(
|
self.velocity,
|
||||||
", PositionDisplacement = {}".format(self.position_displacement)
|
self.start_tick,
|
||||||
if is_displacement
|
self.duration,
|
||||||
|
)
|
||||||
|
+ (
|
||||||
|
", SoundDistance = `r`{}, SoundAzimuth = (`αV`{}, `βH`{})".format(
|
||||||
|
self.sound_distance, *self.sound_azimuth
|
||||||
|
)
|
||||||
|
if include_displacement
|
||||||
else ""
|
else ""
|
||||||
),
|
)
|
||||||
|
+ (", ExtraData = {}".format(self.extra_info) if include_extra_data else "")
|
||||||
|
+ ")"
|
||||||
)
|
)
|
||||||
|
|
||||||
def tuplize(self, is_displacement: bool = False):
|
def tuplize(self, is_displacement: bool = False):
|
||||||
tuplized = self.__tuple__()
|
tuplized = self.__tuple__()
|
||||||
return tuplized[:-2] + ((tuplized[-1],) if is_displacement else ())
|
return tuplized[:-2] + (tuplized[-2:] if is_displacement else ())
|
||||||
|
|
||||||
def __list__(self) -> List:
|
def __list__(self) -> List:
|
||||||
return (
|
return (
|
||||||
@ -285,7 +370,8 @@ class MineNote:
|
|||||||
self.velocity,
|
self.velocity,
|
||||||
self.start_tick,
|
self.start_tick,
|
||||||
self.duration,
|
self.duration,
|
||||||
self.position_displacement,
|
self.sound_distance,
|
||||||
|
self.sound_azimuth,
|
||||||
]
|
]
|
||||||
if self.percussive
|
if self.percussive
|
||||||
else [
|
else [
|
||||||
@ -295,15 +381,16 @@ class MineNote:
|
|||||||
self.velocity,
|
self.velocity,
|
||||||
self.start_tick,
|
self.start_tick,
|
||||||
self.duration,
|
self.duration,
|
||||||
self.position_displacement,
|
self.sound_distance,
|
||||||
|
self.sound_azimuth,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
def __tuple__(
|
def __tuple__(
|
||||||
self,
|
self,
|
||||||
) -> Union[
|
) -> Union[
|
||||||
Tuple[bool, str, int, int, int, int, Tuple[float, float, float]],
|
Tuple[bool, str, int, int, int, int, float, Tuple[float, float]],
|
||||||
Tuple[bool, str, int, int, int, Tuple[float, float, float]],
|
Tuple[bool, str, int, int, int, float, Tuple[float, float]],
|
||||||
]:
|
]:
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
@ -312,7 +399,8 @@ class MineNote:
|
|||||||
self.velocity,
|
self.velocity,
|
||||||
self.start_tick,
|
self.start_tick,
|
||||||
self.duration,
|
self.duration,
|
||||||
self.position_displacement,
|
self.sound_distance,
|
||||||
|
self.sound_azimuth,
|
||||||
)
|
)
|
||||||
if self.percussive
|
if self.percussive
|
||||||
else (
|
else (
|
||||||
@ -322,7 +410,8 @@ class MineNote:
|
|||||||
self.velocity,
|
self.velocity,
|
||||||
self.start_tick,
|
self.start_tick,
|
||||||
self.duration,
|
self.duration,
|
||||||
self.position_displacement,
|
self.sound_distance,
|
||||||
|
self.sound_azimuth,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -334,7 +423,9 @@ class MineNote:
|
|||||||
"Velocity": self.velocity,
|
"Velocity": self.velocity,
|
||||||
"StartTick": self.start_tick,
|
"StartTick": self.start_tick,
|
||||||
"Duration": self.duration,
|
"Duration": self.duration,
|
||||||
"PositionDisplacement": self.position_displacement,
|
"SoundDistance": self.sound_distance,
|
||||||
|
"SoundAzimuth": self.sound_azimuth,
|
||||||
|
"ExtraData": self.extra_info,
|
||||||
}
|
}
|
||||||
if self.percussive
|
if self.percussive
|
||||||
else {
|
else {
|
||||||
@ -344,7 +435,9 @@ class MineNote:
|
|||||||
"Velocity": self.velocity,
|
"Velocity": self.velocity,
|
||||||
"StartTick": self.start_tick,
|
"StartTick": self.start_tick,
|
||||||
"Duration": self.duration,
|
"Duration": self.duration,
|
||||||
"PositionDisplacement": self.position_displacement,
|
"SoundDistance": self.sound_distance,
|
||||||
|
"SoundAzimuth": self.sound_azimuth,
|
||||||
|
"ExtraData": self.extra_info,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ Midi乐器对照表类型
|
|||||||
|
|
||||||
FittingFunctionType = Callable[[float], float]
|
FittingFunctionType = Callable[[float], float]
|
||||||
"""
|
"""
|
||||||
声像偏移音量拟合函数类型
|
拟合函数类型
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ChannelType = Dict[
|
ChannelType = Dict[
|
||||||
|
@ -42,7 +42,7 @@ from .constants import (
|
|||||||
)
|
)
|
||||||
from .exceptions import MusicSequenceDecodeError
|
from .exceptions import MusicSequenceDecodeError
|
||||||
from .subclass import MineNote, mctick2timestr
|
from .subclass import MineNote, mctick2timestr
|
||||||
from .types import MidiInstrumentTableType, MineNoteChannelType
|
from .types import MidiInstrumentTableType, MineNoteChannelType, FittingFunctionType
|
||||||
|
|
||||||
|
|
||||||
def empty_midi_channels(
|
def empty_midi_channels(
|
||||||
@ -171,6 +171,47 @@ def straight_line(vol: float) -> float:
|
|||||||
return vol / -8 + 16
|
return vol / -8 + 16
|
||||||
|
|
||||||
|
|
||||||
|
def panning_2_rotation_linear(pan_: float) -> float:
|
||||||
|
"""
|
||||||
|
Midi 左右平衡偏移值线性转为声源旋转角度
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
pan_: int
|
||||||
|
Midi 左右平衡偏移值
|
||||||
|
注:此参数为int,范围从0到127,当为 64 时,声源居中
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
float
|
||||||
|
声源旋转角度
|
||||||
|
"""
|
||||||
|
return (pan_ - 64) * 90 / 63
|
||||||
|
|
||||||
|
|
||||||
|
def panning_2_rotation_trigonometric(pan_: float) -> float:
|
||||||
|
"""
|
||||||
|
Midi 左右平衡偏移值,依照圆的声场定位,转为声源旋转角度
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
pan_: int
|
||||||
|
Midi 左右平衡偏移值
|
||||||
|
注:此参数为int,范围从0到127,当为 64 时,声源居中
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
float
|
||||||
|
声源旋转角度
|
||||||
|
"""
|
||||||
|
if pan_ <= 0:
|
||||||
|
return -90
|
||||||
|
elif pan_ >= 127:
|
||||||
|
return 90
|
||||||
|
else:
|
||||||
|
return math.degrees(math.acos((64 - pan_) / 63)) - 90
|
||||||
|
|
||||||
|
|
||||||
def minenote_to_command_paramaters(
|
def minenote_to_command_paramaters(
|
||||||
note_: MineNote,
|
note_: MineNote,
|
||||||
pitch_deviation: float = 0,
|
pitch_deviation: float = 0,
|
||||||
@ -252,12 +293,15 @@ def midi_msgs_to_minenote(
|
|||||||
inst_: int, # 乐器编号
|
inst_: int, # 乐器编号
|
||||||
note_: int,
|
note_: int,
|
||||||
percussive_: bool, # 是否作为打击乐器启用
|
percussive_: bool, # 是否作为打击乐器启用
|
||||||
|
volume_: int,
|
||||||
velocity_: int,
|
velocity_: int,
|
||||||
|
panning_: int,
|
||||||
start_time_: int,
|
start_time_: int,
|
||||||
duration_: int,
|
duration_: int,
|
||||||
play_speed: float,
|
play_speed: float,
|
||||||
midi_reference_table: MidiInstrumentTableType,
|
midi_reference_table: MidiInstrumentTableType,
|
||||||
volume_processing_method_: Callable[[float], float],
|
volume_processing_method_: FittingFunctionType,
|
||||||
|
panning_processing_method_: FittingFunctionType,
|
||||||
note_table_replacement: Dict[str, str] = {},
|
note_table_replacement: Dict[str, str] = {},
|
||||||
) -> MineNote:
|
) -> MineNote:
|
||||||
"""
|
"""
|
||||||
@ -265,12 +309,15 @@ def midi_msgs_to_minenote(
|
|||||||
:param inst_: int 乐器编号
|
:param inst_: int 乐器编号
|
||||||
:param note_: int 音高编号(音符编号)
|
:param note_: int 音高编号(音符编号)
|
||||||
:param percussive_: bool 是否作为打击乐器启用
|
:param percussive_: bool 是否作为打击乐器启用
|
||||||
:param velocity_: int 力度(响度)
|
:param volume_: int 音量
|
||||||
|
:param velocity_: int 力度
|
||||||
|
:param panning_: int 声相偏移
|
||||||
:param start_time_: int 音符起始时间(微秒)
|
:param start_time_: int 音符起始时间(微秒)
|
||||||
:param duration_: int 音符持续时间(微秒)
|
:param duration_: int 音符持续时间(微秒)
|
||||||
:param play_speed: float 曲目播放速度
|
:param play_speed: float 曲目播放速度
|
||||||
:param midi_reference_table: Dict[int, str] 转换对照表
|
:param midi_reference_table: Dict[int, str] 转换对照表
|
||||||
:param volume_proccessing_method_: Callable[[float], float] 音量处理函数
|
:param volume_processing_method_: Callable[[float], float] 音量处理函数
|
||||||
|
:param panning_processing_method_: Callable[[float], float] 立体声相偏移处理函数
|
||||||
:param note_table_replacement: Dict[str, str] 音符替换表,定义 Minecraft 音符字串的替换
|
:param note_table_replacement: Dict[str, str] 音符替换表,定义 Minecraft 音符字串的替换
|
||||||
|
|
||||||
:return MineNote我的世界音符对象
|
:return MineNote我的世界音符对象
|
||||||
@ -281,8 +328,6 @@ def midi_msgs_to_minenote(
|
|||||||
"note.bd" if percussive_ else "note.flute",
|
"note.bd" if percussive_ else "note.flute",
|
||||||
)
|
)
|
||||||
|
|
||||||
mc_distance_volume = volume_processing_method_(velocity_)
|
|
||||||
|
|
||||||
return MineNote(
|
return MineNote(
|
||||||
mc_sound_name=note_table_replacement.get(mc_sound_ID, mc_sound_ID),
|
mc_sound_name=note_table_replacement.get(mc_sound_ID, mc_sound_ID),
|
||||||
midi_pitch=note_,
|
midi_pitch=note_,
|
||||||
@ -291,7 +336,8 @@ def midi_msgs_to_minenote(
|
|||||||
last_time=round(duration_ / float(play_speed) / 50000),
|
last_time=round(duration_ / float(play_speed) / 50000),
|
||||||
mass_precision_time=round((start_time_ / float(play_speed) - tk * 50000) / 800),
|
mass_precision_time=round((start_time_ / float(play_speed) - tk * 50000) / 800),
|
||||||
is_percussion=percussive_,
|
is_percussion=percussive_,
|
||||||
displacement=(0, mc_distance_volume, 0),
|
distance=volume_processing_method_(volume_),
|
||||||
|
azimuth=(panning_processing_method_(panning_), 0),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -299,12 +345,15 @@ def midi_msgs_to_minenote_using_kami_respack(
|
|||||||
inst_: int, # 乐器编号
|
inst_: int, # 乐器编号
|
||||||
note_: int,
|
note_: int,
|
||||||
percussive_: bool, # 是否作为打击乐器启用
|
percussive_: bool, # 是否作为打击乐器启用
|
||||||
|
volume_: int,
|
||||||
velocity_: int,
|
velocity_: int,
|
||||||
|
panning_: int,
|
||||||
start_time_: int,
|
start_time_: int,
|
||||||
duration_: int,
|
duration_: int,
|
||||||
play_speed: float,
|
play_speed: float,
|
||||||
midi_reference_table: MidiInstrumentTableType,
|
midi_reference_table: MidiInstrumentTableType,
|
||||||
volume_processing_method_: Callable[[float], float],
|
volume_processing_method_: Callable[[float], float],
|
||||||
|
panning_processing_method_: FittingFunctionType,
|
||||||
note_table_replacement: Dict[str, str] = {},
|
note_table_replacement: Dict[str, str] = {},
|
||||||
) -> MineNote:
|
) -> MineNote:
|
||||||
"""
|
"""
|
||||||
@ -312,12 +361,15 @@ def midi_msgs_to_minenote_using_kami_respack(
|
|||||||
:param inst_: int 乐器编号
|
:param inst_: int 乐器编号
|
||||||
:param note_: int 音高编号(音符编号)
|
:param note_: int 音高编号(音符编号)
|
||||||
:param percussive_: bool 是否作为打击乐器启用
|
:param percussive_: bool 是否作为打击乐器启用
|
||||||
:param velocity_: int 力度(响度)
|
:param volume_: int 音量
|
||||||
|
:param velocity_: int 力度
|
||||||
|
:param panning_: int 声相偏移
|
||||||
:param start_time_: int 音符起始时间(微秒)
|
:param start_time_: int 音符起始时间(微秒)
|
||||||
:param duration_: int 音符持续时间(微秒)
|
:param duration_: int 音符持续时间(微秒)
|
||||||
:param play_speed: float 曲目播放速度
|
:param play_speed: float 曲目播放速度
|
||||||
:param midi_reference_table: Dict[int, str] 转换对照表
|
:param midi_reference_table: Dict[int, str] 转换对照表
|
||||||
:param volume_proccessing_method_: Callable[[float], float] 音量处理函数
|
:param volume_processing_method_: Callable[[float], float] 音量处理函数
|
||||||
|
:param panning_processing_method_: Callable[[float], float] 立体声相偏移处理函数
|
||||||
:param note_table_replacement: Dict[str, str] 音符替换表,定义 Minecraft 音符字串的替换
|
:param note_table_replacement: Dict[str, str] 音符替换表,定义 Minecraft 音符字串的替换
|
||||||
|
|
||||||
:return MineNote我的世界音符对象
|
:return MineNote我的世界音符对象
|
||||||
@ -327,7 +379,9 @@ def midi_msgs_to_minenote_using_kami_respack(
|
|||||||
if not percussive_ and (0 <= inst_ <= 119):
|
if not percussive_ and (0 <= inst_ <= 119):
|
||||||
mc_sound_ID = "{}{}.{}".format(
|
mc_sound_ID = "{}{}.{}".format(
|
||||||
# inst_, "d" if duration_ < 500_000 else "c", note_
|
# inst_, "d" if duration_ < 500_000 else "c", note_
|
||||||
inst_, "d", note_
|
inst_,
|
||||||
|
"d",
|
||||||
|
note_,
|
||||||
)
|
)
|
||||||
elif percussive_ and (27 <= inst_ <= 87):
|
elif percussive_ and (27 <= inst_ <= 87):
|
||||||
mc_sound_ID = "-1d.{}".format(inst_)
|
mc_sound_ID = "-1d.{}".format(inst_)
|
||||||
@ -339,8 +393,6 @@ def midi_msgs_to_minenote_using_kami_respack(
|
|||||||
"note.bd" if percussive_ else "note.flute",
|
"note.bd" if percussive_ else "note.flute",
|
||||||
)
|
)
|
||||||
|
|
||||||
mc_distance_volume = volume_processing_method_(velocity_)
|
|
||||||
|
|
||||||
return MineNote(
|
return MineNote(
|
||||||
mc_sound_name=note_table_replacement.get(mc_sound_ID, mc_sound_ID),
|
mc_sound_name=note_table_replacement.get(mc_sound_ID, mc_sound_ID),
|
||||||
midi_pitch=note_ if using_original else 1,
|
midi_pitch=note_ if using_original else 1,
|
||||||
@ -349,11 +401,14 @@ def midi_msgs_to_minenote_using_kami_respack(
|
|||||||
last_time=round(duration_ / float(play_speed) / 50000),
|
last_time=round(duration_ / float(play_speed) / 50000),
|
||||||
mass_precision_time=round((start_time_ / float(play_speed) - tk * 50000) / 800),
|
mass_precision_time=round((start_time_ / float(play_speed) - tk * 50000) / 800),
|
||||||
is_percussion=percussive_,
|
is_percussion=percussive_,
|
||||||
displacement=(0, mc_distance_volume, 0),
|
distance=volume_processing_method_(volume_),
|
||||||
|
azimuth=(panning_processing_method_(panning_), 0),
|
||||||
extra_information={
|
extra_information={
|
||||||
"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_,
|
||||||
|
"VOLUME_VALUE": volume_,
|
||||||
|
"PIN_VALUE": panning_,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -456,7 +511,7 @@ def soundID_to_blockID(
|
|||||||
|
|
||||||
def load_decode_musicsequence_metainfo(
|
def load_decode_musicsequence_metainfo(
|
||||||
buffer_in: BinaryIO,
|
buffer_in: BinaryIO,
|
||||||
) -> Tuple[str, float, float, bool, int]:
|
) -> Tuple[str, float, float, bool, int, bool]:
|
||||||
"""
|
"""
|
||||||
以流的方式解码音乐序列元信息
|
以流的方式解码音乐序列元信息
|
||||||
|
|
||||||
@ -468,10 +523,10 @@ def load_decode_musicsequence_metainfo(
|
|||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Tuple[str, float, float, bool, int]
|
Tuple[str, float, float, bool, int]
|
||||||
音乐名称,最小音量,音调偏移,是否启用高精度,最后的流指针位置
|
音乐名称,最小音量,音调偏移,是否启用高精度,最后的流指针位置,是否使用新的音符存储格式(MineNote第三版)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
buffer_in.seek(4, 0)
|
note_format_v3 = buffer_in.read(4) in (b"MSQ$", b"FSQ$")
|
||||||
group_1 = int.from_bytes(buffer_in.read(2), "big")
|
group_1 = int.from_bytes(buffer_in.read(2), "big")
|
||||||
group_2 = int.from_bytes(buffer_in.read(2), "big", signed=False)
|
group_2 = int.from_bytes(buffer_in.read(2), "big", signed=False)
|
||||||
|
|
||||||
@ -490,6 +545,7 @@ def load_decode_musicsequence_metainfo(
|
|||||||
),
|
),
|
||||||
bool(group_2 & 0b1000000000000000),
|
bool(group_2 & 0b1000000000000000),
|
||||||
stt_index + 8,
|
stt_index + 8,
|
||||||
|
note_format_v3,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -497,6 +553,7 @@ def load_decode_fsq_flush_release(
|
|||||||
buffer_in: BinaryIO,
|
buffer_in: BinaryIO,
|
||||||
starter_index: int,
|
starter_index: int,
|
||||||
high_quantity_note: bool,
|
high_quantity_note: bool,
|
||||||
|
new_note_format: bool,
|
||||||
) -> Generator[MineNote, Any, None]:
|
) -> Generator[MineNote, Any, None]:
|
||||||
"""
|
"""
|
||||||
以流的方式解码FSQ音乐序列的音符序列并流式返回
|
以流的方式解码FSQ音乐序列的音符序列并流式返回
|
||||||
@ -509,6 +566,8 @@ def load_decode_fsq_flush_release(
|
|||||||
字节流中,音符序列的起始索引
|
字节流中,音符序列的起始索引
|
||||||
high_quantity_note : bool
|
high_quantity_note : bool
|
||||||
是否启用高精度音符解析
|
是否启用高精度音符解析
|
||||||
|
new_note_format : bool
|
||||||
|
是否启用新音符格式解析(MineNote第三版)
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@ -540,9 +599,16 @@ def load_decode_fsq_flush_release(
|
|||||||
12 + high_quantity_note + ((_first_byte := (buffer_in.read(1)))[0] >> 2)
|
12 + high_quantity_note + ((_first_byte := (buffer_in.read(1)))[0] >> 2)
|
||||||
)
|
)
|
||||||
|
|
||||||
yield MineNote.decode(
|
yield (
|
||||||
code_buffer=_first_byte + buffer_in.read(_note_bytes_length),
|
MineNote.decode(
|
||||||
is_high_time_precision=high_quantity_note,
|
code_buffer=_first_byte + buffer_in.read(_note_bytes_length),
|
||||||
|
is_high_time_precision=high_quantity_note,
|
||||||
|
)
|
||||||
|
if new_note_format
|
||||||
|
else decode_note_bytes_v2(
|
||||||
|
code_buffer_bytes=_first_byte + buffer_in.read(_note_bytes_length),
|
||||||
|
is_high_time_precision=high_quantity_note,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
except Exception as _err:
|
except Exception as _err:
|
||||||
# print(bytes_buffer_in[stt_index:end_index])
|
# print(bytes_buffer_in[stt_index:end_index])
|
||||||
@ -557,6 +623,7 @@ def load_decode_msq_flush_release(
|
|||||||
buffer_in: BinaryIO,
|
buffer_in: BinaryIO,
|
||||||
starter_index: int,
|
starter_index: int,
|
||||||
high_quantity_note: bool,
|
high_quantity_note: bool,
|
||||||
|
new_note_format: bool,
|
||||||
) -> Generator[Tuple[int, MineNote], Any, None]:
|
) -> Generator[Tuple[int, MineNote], Any, None]:
|
||||||
"""以流的方式解码MSQ音乐序列的音符序列并流式返回
|
"""以流的方式解码MSQ音乐序列的音符序列并流式返回
|
||||||
|
|
||||||
@ -568,6 +635,8 @@ def load_decode_msq_flush_release(
|
|||||||
字节流中,音符序列的起始索引
|
字节流中,音符序列的起始索引
|
||||||
high_quantity_note : bool
|
high_quantity_note : bool
|
||||||
是否启用高精度音符解析
|
是否启用高精度音符解析
|
||||||
|
new_note_format : bool
|
||||||
|
是否启用新音符格式解析(MineNote第三版)
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@ -703,9 +772,18 @@ def load_decode_msq_flush_release(
|
|||||||
# print("读取音符字节串", _bytes_buffer_in[_stt_index:_end_index])
|
# print("读取音符字节串", _bytes_buffer_in[_stt_index:_end_index])
|
||||||
_read_in_note_list.append(
|
_read_in_note_list.append(
|
||||||
(
|
(
|
||||||
MineNote.decode(
|
(
|
||||||
code_buffer=_bytes_buffer_in[_stt_index:_end_index],
|
MineNote.decode(
|
||||||
is_high_time_precision=high_quantity_note,
|
code_buffer=_bytes_buffer_in[_stt_index:_end_index],
|
||||||
|
is_high_time_precision=high_quantity_note,
|
||||||
|
)
|
||||||
|
if new_note_format
|
||||||
|
else decode_note_bytes_v2(
|
||||||
|
code_buffer_bytes=_bytes_buffer_in[
|
||||||
|
_stt_index:_end_index
|
||||||
|
],
|
||||||
|
is_high_time_precision=high_quantity_note,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
__channel_index,
|
__channel_index,
|
||||||
)
|
)
|
||||||
@ -802,3 +880,128 @@ def guess_deviation(
|
|||||||
/ total_instrument_count
|
/ total_instrument_count
|
||||||
/ total_note_count
|
/ total_note_count
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# 延长支持用
|
||||||
|
|
||||||
|
|
||||||
|
def decode_note_bytes_v1(
|
||||||
|
code_buffer_bytes: bytes,
|
||||||
|
) -> MineNote:
|
||||||
|
"""使用第一版的 MineNote 字节码标准析出MineNote类"""
|
||||||
|
group_1 = int.from_bytes(code_buffer_bytes[:6], "big")
|
||||||
|
percussive_ = bool(group_1 & 0b1)
|
||||||
|
duration_ = (group_1 := group_1 >> 1) & 0b11111111111111111
|
||||||
|
start_tick_ = (group_1 := group_1 >> 17) & 0b11111111111111111
|
||||||
|
note_pitch_ = (group_1 := group_1 >> 17) & 0b1111111
|
||||||
|
sound_name_length = group_1 >> 7
|
||||||
|
|
||||||
|
if code_buffer_bytes[6] & 0b1:
|
||||||
|
position_displacement_ = (
|
||||||
|
int.from_bytes(
|
||||||
|
code_buffer_bytes[8 + sound_name_length : 10 + sound_name_length],
|
||||||
|
"big",
|
||||||
|
)
|
||||||
|
/ 1000,
|
||||||
|
int.from_bytes(
|
||||||
|
code_buffer_bytes[10 + sound_name_length : 12 + sound_name_length],
|
||||||
|
"big",
|
||||||
|
)
|
||||||
|
/ 1000,
|
||||||
|
int.from_bytes(
|
||||||
|
code_buffer_bytes[12 + sound_name_length : 14 + sound_name_length],
|
||||||
|
"big",
|
||||||
|
)
|
||||||
|
/ 1000,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
position_displacement_ = (0, 0, 0)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return MineNote.from_traditional(
|
||||||
|
mc_sound_name=code_buffer_bytes[8 : 8 + sound_name_length].decode(
|
||||||
|
encoding="utf-8"
|
||||||
|
),
|
||||||
|
midi_pitch=note_pitch_,
|
||||||
|
midi_velocity=code_buffer_bytes[6] >> 1,
|
||||||
|
start_time=start_tick_,
|
||||||
|
last_time=duration_,
|
||||||
|
is_percussion=percussive_,
|
||||||
|
displacement=position_displacement_,
|
||||||
|
extra_information={"track_number": code_buffer_bytes[7]},
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
print(code_buffer_bytes, "\n", code_buffer_bytes[8 : 8 + sound_name_length])
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def decode_note_bytes_v2(
|
||||||
|
code_buffer_bytes: bytes, is_high_time_precision: bool = True
|
||||||
|
) -> MineNote:
|
||||||
|
"""使用第二版的 MineNote 字节码标准析出MineNote类"""
|
||||||
|
group_1 = int.from_bytes(code_buffer_bytes[:6], "big")
|
||||||
|
percussive_ = bool(group_1 & 0b1)
|
||||||
|
duration_ = (group_1 := group_1 >> 1) & 0b11111111111111111
|
||||||
|
start_tick_ = (group_1 := group_1 >> 17) & 0b11111111111111111
|
||||||
|
note_pitch_ = (group_1 := group_1 >> 17) & 0b1111111
|
||||||
|
sound_name_length = group_1 >> 7
|
||||||
|
|
||||||
|
if code_buffer_bytes[6] & 0b1:
|
||||||
|
position_displacement_ = (
|
||||||
|
int.from_bytes(
|
||||||
|
(
|
||||||
|
code_buffer_bytes[8 + sound_name_length : 10 + sound_name_length]
|
||||||
|
if is_high_time_precision
|
||||||
|
else code_buffer_bytes[
|
||||||
|
7 + sound_name_length : 9 + sound_name_length
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"big",
|
||||||
|
)
|
||||||
|
/ 1000,
|
||||||
|
int.from_bytes(
|
||||||
|
(
|
||||||
|
code_buffer_bytes[10 + sound_name_length : 12 + sound_name_length]
|
||||||
|
if is_high_time_precision
|
||||||
|
else code_buffer_bytes[
|
||||||
|
9 + sound_name_length : 11 + sound_name_length
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"big",
|
||||||
|
)
|
||||||
|
/ 1000,
|
||||||
|
int.from_bytes(
|
||||||
|
(
|
||||||
|
code_buffer_bytes[12 + sound_name_length : 14 + sound_name_length]
|
||||||
|
if is_high_time_precision
|
||||||
|
else code_buffer_bytes[
|
||||||
|
11 + sound_name_length : 13 + sound_name_length
|
||||||
|
]
|
||||||
|
),
|
||||||
|
"big",
|
||||||
|
)
|
||||||
|
/ 1000,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
position_displacement_ = (0, 0, 0)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return MineNote.from_traditional(
|
||||||
|
mc_sound_name=(
|
||||||
|
o := (
|
||||||
|
code_buffer_bytes[8 : 8 + sound_name_length]
|
||||||
|
if is_high_time_precision
|
||||||
|
else code_buffer_bytes[7 : 7 + sound_name_length]
|
||||||
|
)
|
||||||
|
).decode(encoding="GB18030"),
|
||||||
|
midi_pitch=note_pitch_,
|
||||||
|
midi_velocity=code_buffer_bytes[6] >> 1,
|
||||||
|
start_time=start_tick_,
|
||||||
|
last_time=duration_,
|
||||||
|
mass_precision_time=code_buffer_bytes[7] if is_high_time_precision else 0,
|
||||||
|
is_percussion=percussive_,
|
||||||
|
displacement=position_displacement_,
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
print(code_buffer_bytes, "\n", o)
|
||||||
|
raise
|
||||||
|
@ -93,3 +93,6 @@
|
|||||||
source = "file"
|
source = "file"
|
||||||
path = "Musicreater/__init__.py"
|
path = "Musicreater/__init__.py"
|
||||||
|
|
||||||
|
|
||||||
|
[tool.pyright]
|
||||||
|
typeCheckingMode = "basic"
|
||||||
|
@ -31,7 +31,7 @@ with open("test.fsq", "rb") as f:
|
|||||||
pprint(metas := load_decode_musicsequence_metainfo(f))
|
pprint(metas := load_decode_musicsequence_metainfo(f))
|
||||||
pprint("流式 FSQ 音符序列:")
|
pprint("流式 FSQ 音符序列:")
|
||||||
cnt = 0
|
cnt = 0
|
||||||
for i in load_decode_fsq_flush_release(f, metas[-1], metas[-2]):
|
for i in load_decode_fsq_flush_release(f, metas[-2], metas[-3], metas[-1]):
|
||||||
pprint(
|
pprint(
|
||||||
i,
|
i,
|
||||||
)
|
)
|
||||||
|
@ -12,8 +12,8 @@ print(
|
|||||||
"乐器使用情况",
|
"乐器使用情况",
|
||||||
)
|
)
|
||||||
|
|
||||||
for name in set(
|
for name in sorted(
|
||||||
sorted(
|
set(
|
||||||
[
|
[
|
||||||
n.split(".")[0].replace("c", "").replace("d", "")
|
n.split(".")[0].replace("c", "").replace("d", "")
|
||||||
for n in msct.note_count_per_instrument.keys()
|
for n in msct.note_count_per_instrument.keys()
|
||||||
|
@ -30,5 +30,5 @@ with open("test.msq", "rb") as f:
|
|||||||
pprint("流式 MSQ 元数据:")
|
pprint("流式 MSQ 元数据:")
|
||||||
pprint(metas := load_decode_musicsequence_metainfo(f))
|
pprint(metas := load_decode_musicsequence_metainfo(f))
|
||||||
pprint("流式 MSQ 音符序列:")
|
pprint("流式 MSQ 音符序列:")
|
||||||
for i in load_decode_msq_flush_release(f, metas[-1], metas[-2]):
|
for i in load_decode_msq_flush_release(f, metas[-2], metas[-3], metas[-1]):
|
||||||
pprint(i)
|
pprint(i)
|
||||||
|
Reference in New Issue
Block a user