mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2024-11-13 10:57:43 +08:00
高精度时间支持,修复tempo设置错误的问题
This commit is contained in:
parent
fe7c11636b
commit
bf1b7b99d8
@ -17,20 +17,20 @@ Terms & Conditions: License.md in the root directory
|
|||||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
|
||||||
__version__ = "2.1.0.1"
|
__version__ = "2.2.0"
|
||||||
__vername__ = "Websocket支持"
|
__vername__ = "高精度时间支持"
|
||||||
__author__ = (
|
__author__ = (
|
||||||
("金羿", "Eilles Wan"),
|
("金羿", "Eilles Wan"),
|
||||||
("诸葛亮与八卦阵", "bgArray"),
|
("诸葛亮与八卦阵", "bgArray"),
|
||||||
("偷吃不是Touch", "Touch"),
|
|
||||||
("鱼旧梦", "ElapsingDreams"),
|
("鱼旧梦", "ElapsingDreams"),
|
||||||
|
("偷吃不是Touch", "Touch"),
|
||||||
)
|
)
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# 主要类
|
# 主要类
|
||||||
"MusicSequence",
|
"MusicSequence",
|
||||||
"MidiConvert",
|
"MidiConvert",
|
||||||
# 附加类
|
# 附加类
|
||||||
"SingleNote",
|
# "SingleNote",
|
||||||
"MineNote",
|
"MineNote",
|
||||||
"MineCommand",
|
"MineCommand",
|
||||||
"SingleNoteBox",
|
"SingleNoteBox",
|
||||||
|
@ -51,7 +51,7 @@ class FutureMidiConvertM4(MidiConvert):
|
|||||||
_apply_time_division: float = 10,
|
_apply_time_division: float = 10,
|
||||||
) -> List[MineNote]:
|
) -> List[MineNote]:
|
||||||
"""传入音符数据,返回分割后的插值列表
|
"""传入音符数据,返回分割后的插值列表
|
||||||
:param _note: SingleNote 音符
|
:param _note: MineNote 音符
|
||||||
:param _apply_time_division: int 间隔帧数
|
:param _apply_time_division: int 间隔帧数
|
||||||
:return list[tuple(int开始时间(毫秒), int乐器, int音符, int力度(内置), float音量(播放)),]"""
|
:return list[tuple(int开始时间(毫秒), int乐器, int音符, int力度(内置), float音量(播放)),]"""
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ class FutureMidiConvertM4(MidiConvert):
|
|||||||
_note.start_tick + _i * (_note.duration / totalCount)
|
_note.start_tick + _i * (_note.duration / totalCount)
|
||||||
),
|
),
|
||||||
last_time=int(_note.duration / totalCount),
|
last_time=int(_note.duration / totalCount),
|
||||||
track_number=_note.track_no,
|
# track_number=_note.track_no,
|
||||||
is_percussion=_note.percussive,
|
is_percussion=_note.percussive,
|
||||||
extra_information=_note.extra_info,
|
extra_information=_note.extra_info,
|
||||||
)
|
)
|
||||||
@ -126,7 +126,7 @@ class FutureMidiConvertM4(MidiConvert):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not note.percussive:
|
if not note.percussive:
|
||||||
notes_list.extend(self._linear_note(note,1 * note.extra_info[3]))
|
notes_list.extend(self._linear_note(note, 1 * note.extra_info[3]))
|
||||||
else:
|
else:
|
||||||
notes_list.append(note)
|
notes_list.append(note)
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ from .utils import *
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
学习笔记:
|
学习笔记:
|
||||||
tempo: microseconds per quarter note 毫秒每四分音符,换句话说就是一拍占多少毫秒
|
tempo: microseconds per quarter note 毫秒每四分音符,换句话说就是一拍占多少微秒
|
||||||
tick: midi帧
|
tick: midi帧
|
||||||
ticks_per_beat: 帧每拍,即一拍多少帧
|
ticks_per_beat: 帧每拍,即一拍多少帧
|
||||||
|
|
||||||
@ -46,11 +46,11 @@ ticks_per_beat: 帧每拍,即一拍多少帧
|
|||||||
|
|
||||||
tick / ticks_per_beat => amount_of_beats 拍数(四分音符数)
|
tick / ticks_per_beat => amount_of_beats 拍数(四分音符数)
|
||||||
|
|
||||||
tempo * amount_of_beats => 毫秒数
|
tempo * amount_of_beats => 微秒数
|
||||||
|
|
||||||
所以:
|
所以:
|
||||||
|
|
||||||
tempo * tick / ticks_per_beat => 毫秒数
|
tempo * tick / ticks_per_beat => 微秒数
|
||||||
|
|
||||||
###########
|
###########
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ seconds per tick:
|
|||||||
seconds:
|
seconds:
|
||||||
tick * tempo / 1000000.0 / ticks_per_beat
|
tick * tempo / 1000000.0 / ticks_per_beat
|
||||||
|
|
||||||
microseconds:
|
milliseconds:
|
||||||
tick * tempo / 1000.0 / ticks_per_beat
|
tick * tempo / 1000.0 / ticks_per_beat
|
||||||
|
|
||||||
gameticks:
|
gameticks:
|
||||||
@ -225,8 +225,13 @@ class MusicSequence:
|
|||||||
"""从字节码导入音乐序列"""
|
"""从字节码导入音乐序列"""
|
||||||
|
|
||||||
group_1 = int.from_bytes(bytes_buffer_in[4:6], "big")
|
group_1 = int.from_bytes(bytes_buffer_in[4:6], "big")
|
||||||
|
group_2 = int.from_bytes(bytes_buffer_in[6:8], "big", signed=False)
|
||||||
|
|
||||||
|
high_quantity = bool(group_2 & 0b1000000000000000)
|
||||||
|
# print(group_2, high_quantity)
|
||||||
|
|
||||||
music_name_ = bytes_buffer_in[8 : (stt_index := 8 + (group_1 >> 10))].decode(
|
music_name_ = bytes_buffer_in[8 : (stt_index := 8 + (group_1 >> 10))].decode(
|
||||||
"utf-8"
|
"GB18030"
|
||||||
)
|
)
|
||||||
channels_: MineNoteChannelType = empty_midi_channels(staff=[])
|
channels_: MineNoteChannelType = empty_midi_channels(staff=[])
|
||||||
for channel_index in channels_.keys():
|
for channel_index in channels_.keys():
|
||||||
@ -236,9 +241,17 @@ class MusicSequence:
|
|||||||
)
|
)
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
end_index = stt_index + 14 + (bytes_buffer_in[stt_index] >> 2)
|
end_index = (
|
||||||
|
stt_index
|
||||||
|
+ 13
|
||||||
|
+ high_quantity
|
||||||
|
+ (bytes_buffer_in[stt_index] >> 2)
|
||||||
|
)
|
||||||
channels_[channel_index].append(
|
channels_[channel_index].append(
|
||||||
MineNote.decode(bytes_buffer_in[stt_index:end_index])
|
MineNote.decode(
|
||||||
|
code_buffer=bytes_buffer_in[stt_index:end_index],
|
||||||
|
is_high_time_precision=high_quantity,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
stt_index = end_index
|
stt_index = end_index
|
||||||
except:
|
except:
|
||||||
@ -249,15 +262,21 @@ class MusicSequence:
|
|||||||
name_of_music=music_name_,
|
name_of_music=music_name_,
|
||||||
channels_of_notes=channels_,
|
channels_of_notes=channels_,
|
||||||
minimum_volume_of_music=(group_1 & 0b1111111111) / 1000,
|
minimum_volume_of_music=(group_1 & 0b1111111111) / 1000,
|
||||||
deviation_value=int.from_bytes(bytes_buffer_in[6:8], "big", signed=True)
|
deviation_value=(
|
||||||
/ 1000,
|
(-1 if group_2 & 0b100000000000000 else 1)
|
||||||
|
* (group_2 & 0b11111111111111)
|
||||||
|
/ 1000
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def encode_dump(
|
def encode_dump(
|
||||||
self,
|
self,
|
||||||
|
high_time_precision: bool = True,
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
"""将音乐序列转为二进制字节码"""
|
"""将音乐序列转为二进制字节码"""
|
||||||
|
|
||||||
|
# 第一版的码头: MSQ# 字串编码: UTF-8
|
||||||
|
# 第一版格式
|
||||||
# 音乐名称长度 6 位 支持到 63
|
# 音乐名称长度 6 位 支持到 63
|
||||||
# 最小音量 minimum_volume 10 位 最大支持 1023 即三位小数
|
# 最小音量 minimum_volume 10 位 最大支持 1023 即三位小数
|
||||||
# 共 16 位 合 2 字节
|
# 共 16 位 合 2 字节
|
||||||
@ -266,20 +285,59 @@ class MusicSequence:
|
|||||||
# 共 16 位 合 2 字节
|
# 共 16 位 合 2 字节
|
||||||
# +++
|
# +++
|
||||||
# 音乐名称 music_name 长度最多63 支持到 21 个中文字符 或 63 个西文字符
|
# 音乐名称 music_name 长度最多63 支持到 21 个中文字符 或 63 个西文字符
|
||||||
|
|
||||||
|
# bytes_buffer = (
|
||||||
|
# b"MSQ#"
|
||||||
|
# + (
|
||||||
|
# (len(r := self.music_name.encode("utf-8")) << 10)
|
||||||
|
# + round(self.minimum_volume * 1000)
|
||||||
|
# ).to_bytes(2, "big")
|
||||||
|
# + round(self.music_deviation * 1000).to_bytes(2, "big", signed=True)
|
||||||
|
# + r
|
||||||
|
# )
|
||||||
|
|
||||||
|
# for channel_index, note_list in self.channels.items():
|
||||||
|
# bytes_buffer += len(note_list).to_bytes(4, "big")
|
||||||
|
# for note_ in note_list:
|
||||||
|
# bytes_buffer += note_.encode()
|
||||||
|
|
||||||
|
# 第二版的码头: MSQ@ 字串编码: GB18030
|
||||||
|
|
||||||
|
# 音乐名称长度 6 位 支持到 63
|
||||||
|
# 最小音量 minimum_volume 10 位 最大支持 1023 即三位小数
|
||||||
|
# 共 16 位 合 2 字节
|
||||||
|
# +++
|
||||||
|
# 是否启用“高精度”音符时间控制 1 位
|
||||||
|
# 总音调偏移 music_deviation 15 位 最大支持 -16383 ~ 16383 即 三位小数
|
||||||
|
# 共 16 位 合 2 字节
|
||||||
|
# +++
|
||||||
|
# 音乐名称 music_name 长度最多63 支持到 31 个中文字符 或 63 个西文字符
|
||||||
|
|
||||||
bytes_buffer = (
|
bytes_buffer = (
|
||||||
b"MSQ#"
|
b"MSQ@"
|
||||||
+ (
|
+ (
|
||||||
(len(r := self.music_name.encode("utf-8")) << 10)
|
(len(r := self.music_name.encode("GB18030")) << 10)
|
||||||
+ round(self.minimum_volume * 1000)
|
+ round(self.minimum_volume * 1000)
|
||||||
).to_bytes(2, "big")
|
).to_bytes(2, "big")
|
||||||
+ round(self.music_deviation * 1000).to_bytes(2, "big", signed=True)
|
+ (
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(high_time_precision << 1)
|
||||||
|
+ (1 if (k := round(self.music_deviation * 1000)) < 0 else 0)
|
||||||
|
)
|
||||||
|
<< 14
|
||||||
|
)
|
||||||
|
+ abs(k)
|
||||||
|
).to_bytes(2, "big", signed=False)
|
||||||
+ r
|
+ r
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 若启用“高精度”,则在每个音符前添加一个字节,用于存储音符时间控制精度偏移
|
||||||
|
# 此值每增加 1,则音符向后播放时长增加 1/1250 秒
|
||||||
for channel_index, note_list in self.channels.items():
|
for channel_index, note_list in self.channels.items():
|
||||||
bytes_buffer += len(note_list).to_bytes(4, "big")
|
bytes_buffer += len(note_list).to_bytes(4, "big")
|
||||||
for note_ in note_list:
|
for note_ in note_list:
|
||||||
bytes_buffer += note_.encode()
|
bytes_buffer += note_.encode(is_high_time_precision=high_time_precision)
|
||||||
|
|
||||||
return bytes_buffer
|
return bytes_buffer
|
||||||
|
|
||||||
@ -374,8 +432,6 @@ class MusicSequence:
|
|||||||
----------
|
----------
|
||||||
midi: mido.MidiFile 对象
|
midi: mido.MidiFile 对象
|
||||||
需要处理的midi对象
|
需要处理的midi对象
|
||||||
ignore_mismatch_error: bool
|
|
||||||
是否在导入时忽略音符不匹配错误
|
|
||||||
speed: float
|
speed: float
|
||||||
音乐播放速度倍数
|
音乐播放速度倍数
|
||||||
default_tempo_value: int
|
default_tempo_value: int
|
||||||
@ -398,115 +454,110 @@ class MusicSequence:
|
|||||||
|
|
||||||
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
||||||
midi_channels: MineNoteChannelType = empty_midi_channels(staff=[])
|
midi_channels: MineNoteChannelType = empty_midi_channels(staff=[])
|
||||||
|
channel_program: Dict[int, int] = empty_midi_channels(staff=-1)
|
||||||
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] = {}
|
||||||
|
microseconds = 0
|
||||||
|
|
||||||
# 我们来用通道统计音乐信息
|
note_queue_A: Dict[
|
||||||
# 但是是用分轨的思路的
|
int,
|
||||||
for track_no, track in enumerate(midi.tracks):
|
List[
|
||||||
microseconds = 0
|
Tuple[
|
||||||
if not track:
|
int,
|
||||||
continue
|
int,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
] = empty_midi_channels(staff=[])
|
||||||
|
note_queue_B: Dict[
|
||||||
|
int,
|
||||||
|
List[
|
||||||
|
Tuple[
|
||||||
|
int,
|
||||||
|
int,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
] = empty_midi_channels(staff=[])
|
||||||
|
|
||||||
note_queue_A: Dict[
|
# 直接使用mido.midifiles.tracks.merge_tracks转为单轨
|
||||||
int,
|
# 采用的时遍历信息思路
|
||||||
List[
|
for msg in midi.merged_track:
|
||||||
Tuple[
|
if msg.time != 0:
|
||||||
int,
|
# 微秒
|
||||||
int,
|
microseconds += msg.time * tempo / midi.ticks_per_beat
|
||||||
]
|
|
||||||
],
|
|
||||||
] = empty_midi_channels(staff=[])
|
|
||||||
note_queue_B: Dict[
|
|
||||||
int,
|
|
||||||
List[
|
|
||||||
Tuple[
|
|
||||||
int,
|
|
||||||
int,
|
|
||||||
]
|
|
||||||
],
|
|
||||||
] = empty_midi_channels(staff=[])
|
|
||||||
|
|
||||||
channel_program: Dict[int, int] = empty_midi_channels(staff=-1)
|
# 简化
|
||||||
|
if msg.type == "set_tempo":
|
||||||
|
tempo = msg.tempo
|
||||||
|
else:
|
||||||
|
if msg.type == "program_change":
|
||||||
|
channel_program[msg.channel] = msg.program
|
||||||
|
|
||||||
for msg in track:
|
elif msg.type == "note_on" and msg.velocity != 0:
|
||||||
if msg.time != 0:
|
note_queue_A[msg.channel].append(
|
||||||
microseconds += msg.time * tempo / midi.ticks_per_beat / 1000
|
(msg.note, channel_program[msg.channel])
|
||||||
|
)
|
||||||
|
note_queue_B[msg.channel].append((msg.velocity, microseconds))
|
||||||
|
|
||||||
if msg.is_meta:
|
elif (msg.type == "note_off") or (
|
||||||
if msg.type == "set_tempo":
|
msg.type == "note_on" and msg.velocity == 0
|
||||||
tempo = msg.tempo
|
):
|
||||||
else:
|
if (msg.note, channel_program[msg.channel]) in note_queue_A[
|
||||||
if msg.type == "program_change":
|
msg.channel
|
||||||
channel_program[msg.channel] = msg.program
|
]:
|
||||||
|
_velocity, _ms = note_queue_B[msg.channel][
|
||||||
elif msg.type == "note_on" and msg.velocity != 0:
|
note_queue_A[msg.channel].index(
|
||||||
note_queue_A[msg.channel].append(
|
|
||||||
(msg.note, channel_program[msg.channel])
|
|
||||||
)
|
|
||||||
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_program[msg.channel]) in note_queue_A[
|
|
||||||
msg.channel
|
|
||||||
]:
|
|
||||||
_velocity, _ms = note_queue_B[msg.channel][
|
|
||||||
note_queue_A[msg.channel].index(
|
|
||||||
(msg.note, channel_program[msg.channel])
|
|
||||||
)
|
|
||||||
]
|
|
||||||
note_queue_A[msg.channel].remove(
|
|
||||||
(msg.note, channel_program[msg.channel])
|
(msg.note, channel_program[msg.channel])
|
||||||
)
|
)
|
||||||
note_queue_B[msg.channel].remove((_velocity, _ms))
|
]
|
||||||
|
note_queue_A[msg.channel].remove(
|
||||||
|
(msg.note, channel_program[msg.channel])
|
||||||
|
)
|
||||||
|
note_queue_B[msg.channel].remove((_velocity, _ms))
|
||||||
|
|
||||||
midi_channels[msg.channel].append(
|
midi_channels[msg.channel].append(
|
||||||
that_note := midi_msgs_to_minenote(
|
that_note := midi_msgs_to_minenote(
|
||||||
inst_=(
|
inst_=(
|
||||||
msg.note
|
msg.note
|
||||||
if msg.channel == 9
|
if msg.channel == 9
|
||||||
else channel_program[msg.channel]
|
else channel_program[msg.channel]
|
||||||
),
|
),
|
||||||
note_=(
|
note_=(
|
||||||
channel_program[msg.channel]
|
channel_program[msg.channel]
|
||||||
if msg.channel == 9
|
if msg.channel == 9
|
||||||
else msg.note
|
else msg.note
|
||||||
),
|
),
|
||||||
velocity_=_velocity,
|
velocity_=_velocity,
|
||||||
start_time_=_ms,
|
start_time_=_ms, # 微秒
|
||||||
duration_=microseconds - _ms,
|
duration_=microseconds - _ms, # 微秒
|
||||||
track_no_=track_no,
|
percussive_=(msg.channel == 9),
|
||||||
percussive_=(msg.channel == 9),
|
play_speed=speed,
|
||||||
play_speed=speed,
|
midi_reference_table=(
|
||||||
midi_reference_table=(
|
percussion_note_rtable
|
||||||
percussion_note_rtable
|
if msg.channel == 9
|
||||||
if msg.channel == 9
|
else pitched_note_rtable
|
||||||
else pitched_note_rtable
|
),
|
||||||
),
|
volume_processing_method_=vol_processing_function,
|
||||||
volume_processing_method_=vol_processing_function,
|
)
|
||||||
|
)
|
||||||
|
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:
|
||||||
|
if ignore_mismatch_error:
|
||||||
|
print(
|
||||||
|
"[WARRING] MIDI格式错误 音符不匹配 {} 无法在上文中找到与之匹配的音符开音消息".format(
|
||||||
|
msg
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
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:
|
raise NoteOnOffMismatchError(
|
||||||
print(
|
"当前的MIDI很可能有损坏之嫌……",
|
||||||
"[WARRING] MIDI格式错误 音符不匹配 {} 无法在上文中找到与之匹配的音符开音消息".format(
|
msg,
|
||||||
msg
|
"无法在上文中找到与之匹配的音符开音消息。",
|
||||||
)
|
)
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise NoteOnOffMismatchError(
|
|
||||||
"当前的MIDI很可能有损坏之嫌……",
|
|
||||||
msg,
|
|
||||||
"无法在上文中找到与之匹配的音符开音消息。",
|
|
||||||
)
|
|
||||||
|
|
||||||
"""整合后的音乐通道格式
|
"""整合后的音乐通道格式
|
||||||
每个通道包括若干消息元素其中逃不过这三种:
|
每个通道包括若干消息元素其中逃不过这三种:
|
||||||
|
@ -272,3 +272,27 @@ def to_mcstructure_files_in_repeater_divided_by_instruments(
|
|||||||
struct.dump(f)
|
struct.dump(f)
|
||||||
|
|
||||||
return max_delay
|
return max_delay
|
||||||
|
|
||||||
|
|
||||||
|
def to_mcstructure_file_in_blocks(
|
||||||
|
midi_cvt: MidiConvert,
|
||||||
|
dist_path: str,
|
||||||
|
player: str = "@a",
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
将midi以方块形式转换为mcstructure结构文件
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
midi_cvt: MidiConvert 对象
|
||||||
|
用于转换的MidiConvert对象
|
||||||
|
dist_path: str
|
||||||
|
转换结果输出的目标路径
|
||||||
|
player: str
|
||||||
|
玩家选择器,默认为`@a`
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
int音乐总延迟
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
@ -42,8 +42,8 @@ class MineNote:
|
|||||||
duration: int
|
duration: int
|
||||||
"""音符持续时间 命令刻"""
|
"""音符持续时间 命令刻"""
|
||||||
|
|
||||||
track_no: int
|
high_precision_time: int
|
||||||
"""音符所处的音轨"""
|
"""高精度开始时间偏量 1/1250 秒"""
|
||||||
|
|
||||||
percussive: bool
|
percussive: bool
|
||||||
"""是否作为打击乐器启用"""
|
"""是否作为打击乐器启用"""
|
||||||
@ -61,7 +61,7 @@ class MineNote:
|
|||||||
midi_velocity: int,
|
midi_velocity: int,
|
||||||
start_time: int,
|
start_time: int,
|
||||||
last_time: int,
|
last_time: int,
|
||||||
track_number: int = 0,
|
mass_precision_time: int = 0,
|
||||||
is_percussion: Optional[bool] = None,
|
is_percussion: Optional[bool] = None,
|
||||||
displacement: Optional[Tuple[float, float, float]] = None,
|
displacement: Optional[Tuple[float, float, float]] = None,
|
||||||
extra_information: Optional[Any] = None,
|
extra_information: Optional[Any] = None,
|
||||||
@ -73,7 +73,7 @@ class MineNote:
|
|||||||
:param start_time:`int` 开始之时(命令刻)
|
:param start_time:`int` 开始之时(命令刻)
|
||||||
注:此处的时间是用从乐曲开始到当前的毫秒数
|
注:此处的时间是用从乐曲开始到当前的毫秒数
|
||||||
:param last_time:`int` 音符延续时间(命令刻)
|
:param last_time:`int` 音符延续时间(命令刻)
|
||||||
:param track_number:`int` 音轨编号
|
:param mass_precision_time:`int` 高精度的开始时间偏移量(1/1250秒)
|
||||||
:param is_percussion:`bool` 是否作为打击乐器
|
:param is_percussion:`bool` 是否作为打击乐器
|
||||||
:param displacement:`tuple[int,int,int]` 声像位移
|
:param displacement:`tuple[int,int,int]` 声像位移
|
||||||
:param extra_information:`Any` 附加信息"""
|
:param extra_information:`Any` 附加信息"""
|
||||||
@ -87,8 +87,8 @@ class MineNote:
|
|||||||
"""开始之时 tick"""
|
"""开始之时 tick"""
|
||||||
self.duration: int = last_time
|
self.duration: int = last_time
|
||||||
"""音符持续时间 tick"""
|
"""音符持续时间 tick"""
|
||||||
self.track_no: int = track_number
|
self.high_precision_time: int = mass_precision_time
|
||||||
"""音符所处的音轨"""
|
"""高精度开始时间偏量 0.4 毫秒"""
|
||||||
|
|
||||||
self.percussive = (
|
self.percussive = (
|
||||||
(mc_sound_name in MC_PERCUSSION_INSTRUMENT_LIST)
|
(mc_sound_name in MC_PERCUSSION_INSTRUMENT_LIST)
|
||||||
@ -105,7 +105,7 @@ class MineNote:
|
|||||||
self.extra_info = extra_information
|
self.extra_info = extra_information
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def decode(cls, code_buffer: bytes):
|
def decode(cls, code_buffer: bytes, is_high_time_precision: bool = True):
|
||||||
"""自字节码析出MineNote类"""
|
"""自字节码析出MineNote类"""
|
||||||
group_1 = int.from_bytes(code_buffer[:6], "big")
|
group_1 = int.from_bytes(code_buffer[:6], "big")
|
||||||
percussive_ = bool(group_1 & 0b1)
|
percussive_ = bool(group_1 & 0b1)
|
||||||
@ -117,17 +117,31 @@ class MineNote:
|
|||||||
if code_buffer[6] & 0b1:
|
if code_buffer[6] & 0b1:
|
||||||
position_displacement_ = (
|
position_displacement_ = (
|
||||||
int.from_bytes(
|
int.from_bytes(
|
||||||
code_buffer[8 + sound_name_length : 10 + sound_name_length],
|
(
|
||||||
|
code_buffer[8 + sound_name_length : 10 + sound_name_length]
|
||||||
|
if is_high_time_precision
|
||||||
|
else code_buffer[7 + sound_name_length : 9 + sound_name_length]
|
||||||
|
),
|
||||||
"big",
|
"big",
|
||||||
)
|
)
|
||||||
/ 1000,
|
/ 1000,
|
||||||
int.from_bytes(
|
int.from_bytes(
|
||||||
code_buffer[10 + sound_name_length : 12 + sound_name_length],
|
(
|
||||||
|
code_buffer[10 + sound_name_length : 12 + sound_name_length]
|
||||||
|
if is_high_time_precision
|
||||||
|
else code_buffer[9 + sound_name_length : 11 + sound_name_length]
|
||||||
|
),
|
||||||
"big",
|
"big",
|
||||||
)
|
)
|
||||||
/ 1000,
|
/ 1000,
|
||||||
int.from_bytes(
|
int.from_bytes(
|
||||||
code_buffer[12 + sound_name_length : 14 + sound_name_length],
|
(
|
||||||
|
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",
|
"big",
|
||||||
)
|
)
|
||||||
/ 1000,
|
/ 1000,
|
||||||
@ -137,22 +151,28 @@ class MineNote:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
return cls(
|
return cls(
|
||||||
mc_sound_name=code_buffer[8 : 8 + sound_name_length].decode(
|
mc_sound_name=(
|
||||||
encoding="utf-8"
|
o := (
|
||||||
),
|
code_buffer[8 : 8 + sound_name_length]
|
||||||
|
if is_high_time_precision
|
||||||
|
else code_buffer[7 : 7 + sound_name_length]
|
||||||
|
)
|
||||||
|
).decode(encoding="GB18030"),
|
||||||
midi_pitch=note_pitch_,
|
midi_pitch=note_pitch_,
|
||||||
midi_velocity=code_buffer[6] >> 1,
|
midi_velocity=code_buffer[6] >> 1,
|
||||||
start_time=start_tick_,
|
start_time=start_tick_,
|
||||||
last_time=duration_,
|
last_time=duration_,
|
||||||
track_number=code_buffer[7],
|
mass_precision_time=code_buffer[7] if is_high_time_precision else 0,
|
||||||
is_percussion=percussive_,
|
is_percussion=percussive_,
|
||||||
displacement=position_displacement_,
|
displacement=position_displacement_,
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
print(code_buffer, "\n", code_buffer[8 : 8 + sound_name_length])
|
print(code_buffer, "\n", o)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def encode(self, is_displacement_included: bool = True) -> bytes:
|
def encode(
|
||||||
|
self, is_displacement_included: bool = True, is_high_time_precision: bool = True
|
||||||
|
) -> bytes:
|
||||||
"""
|
"""
|
||||||
将数据打包为字节码
|
将数据打包为字节码
|
||||||
|
|
||||||
@ -172,9 +192,14 @@ class MineNote:
|
|||||||
# is_displacement_included 长度 1 位 支持到 1
|
# is_displacement_included 长度 1 位 支持到 1
|
||||||
# 共 8 位 合 1 字节
|
# 共 8 位 合 1 字节
|
||||||
# +++
|
# +++
|
||||||
|
# (在第二版中已舍弃)
|
||||||
# track_no 长度 8 位 支持到 255 合 1 字节
|
# track_no 长度 8 位 支持到 255 合 1 字节
|
||||||
|
# (在第二版中新增)
|
||||||
|
# high_time_precision(可选)长度 8 位 支持到 255 合 1 字节 支持 1/1250 秒
|
||||||
# +++
|
# +++
|
||||||
# sound_name 长度最多63 支持到 21 个中文字符 或 63 个西文字符
|
# sound_name 长度最多 63 支持到 31 个中文字符 或 63 个西文字符
|
||||||
|
# 第一版编码: UTF-8
|
||||||
|
# 第二版编码: GB18030
|
||||||
# +++
|
# +++
|
||||||
# position_displacement 每个元素长 16 位 合 2 字节
|
# position_displacement 每个元素长 16 位 合 2 字节
|
||||||
# 共 48 位 合 6 字节 支持存储三位小数和两位整数,其值必须在 [0, 65.535] 之间
|
# 共 48 位 合 6 字节 支持存储三位小数和两位整数,其值必须在 [0, 65.535] 之间
|
||||||
@ -190,7 +215,7 @@ class MineNote:
|
|||||||
(
|
(
|
||||||
len(
|
len(
|
||||||
r := self.sound_name.encode(
|
r := self.sound_name.encode(
|
||||||
encoding="utf-8"
|
encoding="GB18030"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
<< 7
|
<< 7
|
||||||
@ -210,7 +235,12 @@ class MineNote:
|
|||||||
+ self.percussive
|
+ self.percussive
|
||||||
).to_bytes(6, "big")
|
).to_bytes(6, "big")
|
||||||
+ ((self.velocity << 1) + is_displacement_included).to_bytes(1, "big")
|
+ ((self.velocity << 1) + is_displacement_included).to_bytes(1, "big")
|
||||||
+ self.track_no.to_bytes(1, "big")
|
# + self.track_no.to_bytes(1, "big")
|
||||||
|
+ (
|
||||||
|
self.high_precision_time.to_bytes(1, "big")
|
||||||
|
if is_high_time_precision
|
||||||
|
else b""
|
||||||
|
)
|
||||||
+ r
|
+ r
|
||||||
+ (
|
+ (
|
||||||
(
|
(
|
||||||
@ -227,7 +257,7 @@ class MineNote:
|
|||||||
"""设置附加信息"""
|
"""设置附加信息"""
|
||||||
self.extra_info = sth
|
self.extra_info = sth
|
||||||
|
|
||||||
def __str__(self, is_displacement: bool = False, is_track: bool = False):
|
def __str__(self, is_displacement: bool = False):
|
||||||
return "{}Note(Instrument = {}, {}Velocity = {}, StartTick = {}, Duration = {}{}{})".format(
|
return "{}Note(Instrument = {}, {}Velocity = {}, StartTick = {}, Duration = {}{}{})".format(
|
||||||
"Percussive" if self.percussive else "",
|
"Percussive" if self.percussive else "",
|
||||||
self.sound_name,
|
self.sound_name,
|
||||||
@ -235,7 +265,6 @@ class MineNote:
|
|||||||
self.velocity,
|
self.velocity,
|
||||||
self.start_tick,
|
self.start_tick,
|
||||||
self.duration,
|
self.duration,
|
||||||
", Track = {}".format(self.track_no) if is_track else "",
|
|
||||||
(
|
(
|
||||||
", PositionDisplacement = {}".format(self.position_displacement)
|
", PositionDisplacement = {}".format(self.position_displacement)
|
||||||
if is_displacement
|
if is_displacement
|
||||||
@ -243,13 +272,9 @@ class MineNote:
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def tuplize(self, is_displacement: bool = False, is_track: bool = False):
|
def tuplize(self, is_displacement: bool = False):
|
||||||
tuplized = self.__tuple__()
|
tuplized = self.__tuple__()
|
||||||
return (
|
return tuplized[:-2] + ((tuplized[-1],) if is_displacement else ())
|
||||||
tuplized[:-2]
|
|
||||||
+ ((tuplized[-2],) if is_track else ())
|
|
||||||
+ ((tuplized[-1],) if is_displacement else ())
|
|
||||||
)
|
|
||||||
|
|
||||||
def __list__(self) -> List:
|
def __list__(self) -> List:
|
||||||
return (
|
return (
|
||||||
@ -259,7 +284,6 @@ class MineNote:
|
|||||||
self.velocity,
|
self.velocity,
|
||||||
self.start_tick,
|
self.start_tick,
|
||||||
self.duration,
|
self.duration,
|
||||||
self.track_no,
|
|
||||||
self.position_displacement,
|
self.position_displacement,
|
||||||
]
|
]
|
||||||
if self.percussive
|
if self.percussive
|
||||||
@ -270,7 +294,6 @@ class MineNote:
|
|||||||
self.velocity,
|
self.velocity,
|
||||||
self.start_tick,
|
self.start_tick,
|
||||||
self.duration,
|
self.duration,
|
||||||
self.track_no,
|
|
||||||
self.position_displacement,
|
self.position_displacement,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -278,8 +301,8 @@ class MineNote:
|
|||||||
def __tuple__(
|
def __tuple__(
|
||||||
self,
|
self,
|
||||||
) -> Union[
|
) -> Union[
|
||||||
Tuple[bool, str, int, int, int, int, int, Tuple[float, float, float]],
|
|
||||||
Tuple[bool, str, int, int, int, int, Tuple[float, float, float]],
|
Tuple[bool, str, int, int, int, int, Tuple[float, float, float]],
|
||||||
|
Tuple[bool, str, int, int, int, Tuple[float, float, float]],
|
||||||
]:
|
]:
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
@ -288,7 +311,6 @@ class MineNote:
|
|||||||
self.velocity,
|
self.velocity,
|
||||||
self.start_tick,
|
self.start_tick,
|
||||||
self.duration,
|
self.duration,
|
||||||
self.track_no,
|
|
||||||
self.position_displacement,
|
self.position_displacement,
|
||||||
)
|
)
|
||||||
if self.percussive
|
if self.percussive
|
||||||
@ -299,7 +321,6 @@ class MineNote:
|
|||||||
self.velocity,
|
self.velocity,
|
||||||
self.start_tick,
|
self.start_tick,
|
||||||
self.duration,
|
self.duration,
|
||||||
self.track_no,
|
|
||||||
self.position_displacement,
|
self.position_displacement,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -312,7 +333,6 @@ class MineNote:
|
|||||||
"Velocity": self.velocity,
|
"Velocity": self.velocity,
|
||||||
"StartTick": self.start_tick,
|
"StartTick": self.start_tick,
|
||||||
"Duration": self.duration,
|
"Duration": self.duration,
|
||||||
"Track": self.track_no,
|
|
||||||
"PositionDisplacement": self.position_displacement,
|
"PositionDisplacement": self.position_displacement,
|
||||||
}
|
}
|
||||||
if self.percussive
|
if self.percussive
|
||||||
@ -323,7 +343,6 @@ class MineNote:
|
|||||||
"Velocity": self.velocity,
|
"Velocity": self.velocity,
|
||||||
"StartTick": self.start_tick,
|
"StartTick": self.start_tick,
|
||||||
"Duration": self.duration,
|
"Duration": self.duration,
|
||||||
"Track": self.track_no,
|
|
||||||
"PositionDisplacement": self.position_displacement,
|
"PositionDisplacement": self.position_displacement,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -334,150 +353,150 @@ class MineNote:
|
|||||||
return self.tuplize() == other.tuplize()
|
return self.tuplize() == other.tuplize()
|
||||||
|
|
||||||
|
|
||||||
@dataclass(init=False)
|
# @dataclass(init=False)
|
||||||
class SingleNote:
|
# class SingleNote:
|
||||||
"""存储单个音符的类"""
|
# """存储单个音符的类"""
|
||||||
|
|
||||||
instrument: int
|
# instrument: int
|
||||||
"""乐器编号"""
|
# """乐器编号"""
|
||||||
|
|
||||||
note: int
|
# note: int
|
||||||
"""音符编号"""
|
# """音符编号"""
|
||||||
|
|
||||||
velocity: int
|
# velocity: int
|
||||||
"""力度/响度"""
|
# """力度/响度"""
|
||||||
|
|
||||||
start_time: int
|
# start_time: int
|
||||||
"""开始之时 ms"""
|
# """开始之时 ms"""
|
||||||
|
|
||||||
duration: int
|
# duration: int
|
||||||
"""音符持续时间 ms"""
|
# """音符持续时间 ms"""
|
||||||
|
|
||||||
track_no: int
|
# track_no: int
|
||||||
"""音符所处的音轨"""
|
# """音符所处的音轨"""
|
||||||
|
|
||||||
percussive: bool
|
# percussive: bool
|
||||||
"""是否为打击乐器"""
|
# """是否为打击乐器"""
|
||||||
|
|
||||||
extra_info: Any
|
# extra_info: Any
|
||||||
"""你觉得放什么好?"""
|
# """你觉得放什么好?"""
|
||||||
|
|
||||||
def __init__(
|
# def __init__(
|
||||||
self,
|
# self,
|
||||||
instrument: int,
|
# instrument: int,
|
||||||
pitch: int,
|
# pitch: int,
|
||||||
velocity: int,
|
# velocity: int,
|
||||||
startime: int,
|
# startime: int,
|
||||||
lastime: int,
|
# lastime: int,
|
||||||
is_percussion: bool,
|
# is_percussion: bool,
|
||||||
track_number: int = 0,
|
# track_number: int = 0,
|
||||||
extra_information: Any = None,
|
# extra_information: Any = None,
|
||||||
):
|
# ):
|
||||||
"""用于存储单个音符的类
|
# """用于存储单个音符的类
|
||||||
:param instrument 乐器编号
|
# :param instrument 乐器编号
|
||||||
:param pitch 音符编号
|
# :param pitch 音符编号
|
||||||
:param velocity 力度/响度
|
# :param velocity 力度/响度
|
||||||
:param startTime 开始之时(ms)
|
# :param startTime 开始之时(ms)
|
||||||
注:此处的时间是用从乐曲开始到当前的毫秒数
|
# 注:此处的时间是用从乐曲开始到当前的毫秒数
|
||||||
:param lastTime 音符延续时间(ms)"""
|
# :param lastTime 音符延续时间(ms)"""
|
||||||
self.instrument: int = instrument
|
# self.instrument: int = instrument
|
||||||
"""乐器编号"""
|
# """乐器编号"""
|
||||||
self.note: int = pitch
|
# self.note: int = pitch
|
||||||
"""音符编号"""
|
# """音符编号"""
|
||||||
self.velocity: int = velocity
|
# self.velocity: int = velocity
|
||||||
"""力度/响度"""
|
# """力度/响度"""
|
||||||
self.start_time: int = startime
|
# self.start_time: int = startime
|
||||||
"""开始之时 ms"""
|
# """开始之时 ms"""
|
||||||
self.duration: int = lastime
|
# self.duration: int = lastime
|
||||||
"""音符持续时间 ms"""
|
# """音符持续时间 ms"""
|
||||||
self.track_no: int = track_number
|
# self.track_no: int = track_number
|
||||||
"""音符所处的音轨"""
|
# """音符所处的音轨"""
|
||||||
self.percussive: bool = is_percussion
|
# self.percussive: bool = is_percussion
|
||||||
"""是否为打击乐器"""
|
# """是否为打击乐器"""
|
||||||
|
|
||||||
self.extra_info = extra_information
|
# self.extra_info = extra_information
|
||||||
|
|
||||||
@property
|
# @property
|
||||||
def inst(self) -> int:
|
# def inst(self) -> int:
|
||||||
"""乐器编号"""
|
# """乐器编号"""
|
||||||
return self.instrument
|
# return self.instrument
|
||||||
|
|
||||||
@inst.setter
|
# @inst.setter
|
||||||
def inst(self, inst_: int):
|
# def inst(self, inst_: int):
|
||||||
self.instrument = inst_
|
# self.instrument = inst_
|
||||||
|
|
||||||
@property
|
# @property
|
||||||
def pitch(self) -> int:
|
# def pitch(self) -> int:
|
||||||
"""音符编号"""
|
# """音符编号"""
|
||||||
return self.note
|
# return self.note
|
||||||
|
|
||||||
# @property
|
# # @property
|
||||||
# def get_mc_pitch(self,table: Dict[int, Tuple[str, int]]) -> float:
|
# # def get_mc_pitch(self,table: Dict[int, Tuple[str, int]]) -> float:
|
||||||
# self.mc_sound_ID, _X = inst_to_sould_with_deviation(self.inst,table,"note.bd" if self.percussive else "note.flute",)
|
# # self.mc_sound_ID, _X = inst_to_sould_with_deviation(self.inst,table,"note.bd" if self.percussive else "note.flute",)
|
||||||
# return -1 if self.percussive else 2 ** ((self.note - 60 - _X) / 12)
|
# # return -1 if self.percussive else 2 ** ((self.note - 60 - _X) / 12)
|
||||||
|
|
||||||
def set_info(self, sth: Any):
|
# def set_info(self, sth: Any):
|
||||||
"""设置附加信息"""
|
# """设置附加信息"""
|
||||||
self.extra_info = sth
|
# self.extra_info = sth
|
||||||
|
|
||||||
def __str__(self, is_track: bool = False):
|
# def __str__(self, is_track: bool = False):
|
||||||
return "{}Note(Instrument = {}, {}Velocity = {}, StartTime = {}, Duration = {}{})".format(
|
# return "{}Note(Instrument = {}, {}Velocity = {}, StartTime = {}, Duration = {}{})".format(
|
||||||
"Percussive" if self.percussive else "",
|
# "Percussive" if self.percussive else "",
|
||||||
self.inst,
|
# self.inst,
|
||||||
"" if self.percussive else "Pitch = {}, ".format(self.pitch),
|
# "" if self.percussive else "Pitch = {}, ".format(self.pitch),
|
||||||
self.start_time,
|
# self.start_time,
|
||||||
self.duration,
|
# self.duration,
|
||||||
", Track = {}".format(self.track_no) if is_track else "",
|
# ", Track = {}".format(self.track_no) if is_track else "",
|
||||||
)
|
# )
|
||||||
|
|
||||||
def __tuple__(self):
|
# def __tuple__(self):
|
||||||
return (
|
# return (
|
||||||
(
|
# (
|
||||||
self.percussive,
|
# self.percussive,
|
||||||
self.inst,
|
# self.inst,
|
||||||
self.velocity,
|
# self.velocity,
|
||||||
self.start_time,
|
# self.start_time,
|
||||||
self.duration,
|
# self.duration,
|
||||||
self.track_no,
|
# self.track_no,
|
||||||
)
|
# )
|
||||||
if self.percussive
|
# if self.percussive
|
||||||
else (
|
# else (
|
||||||
self.percussive,
|
# self.percussive,
|
||||||
self.inst,
|
# self.inst,
|
||||||
self.note,
|
# self.note,
|
||||||
self.velocity,
|
# self.velocity,
|
||||||
self.start_time,
|
# self.start_time,
|
||||||
self.duration,
|
# self.duration,
|
||||||
self.track_no,
|
# self.track_no,
|
||||||
)
|
# )
|
||||||
)
|
# )
|
||||||
|
|
||||||
def __dict__(self):
|
# def __dict__(self):
|
||||||
return (
|
# return (
|
||||||
{
|
# {
|
||||||
"Percussive": self.percussive,
|
# "Percussive": self.percussive,
|
||||||
"Instrument": self.inst,
|
# "Instrument": self.inst,
|
||||||
"Velocity": self.velocity,
|
# "Velocity": self.velocity,
|
||||||
"StartTime": self.start_time,
|
# "StartTime": self.start_time,
|
||||||
"Duration": self.duration,
|
# "Duration": self.duration,
|
||||||
"Track": self.track_no,
|
# "Track": self.track_no,
|
||||||
}
|
# }
|
||||||
if self.percussive
|
# if self.percussive
|
||||||
else {
|
# else {
|
||||||
"Percussive": self.percussive,
|
# "Percussive": self.percussive,
|
||||||
"Instrument": self.inst,
|
# "Instrument": self.inst,
|
||||||
"Pitch": self.note,
|
# "Pitch": self.note,
|
||||||
"Velocity": self.velocity,
|
# "Velocity": self.velocity,
|
||||||
"StartTime": self.start_time,
|
# "StartTime": self.start_time,
|
||||||
"Duration": self.duration,
|
# "Duration": self.duration,
|
||||||
"Track": self.track_no,
|
# "Track": self.track_no,
|
||||||
}
|
# }
|
||||||
)
|
# )
|
||||||
|
|
||||||
def __eq__(self, other) -> bool:
|
# def __eq__(self, other) -> bool:
|
||||||
if not isinstance(other, self.__class__):
|
# if not isinstance(other, self.__class__):
|
||||||
return False
|
# return False
|
||||||
return self.__str__() == other.__str__()
|
# return self.__str__() == other.__str__()
|
||||||
|
|
||||||
|
|
||||||
@dataclass(init=False)
|
@dataclass(init=False)
|
||||||
@ -736,7 +755,10 @@ class ProgressBarStyle:
|
|||||||
.replace(r"%^s", str(total_delays))
|
.replace(r"%^s", str(total_delays))
|
||||||
.replace(r"%%t", mctick2timestr(played_delays))
|
.replace(r"%%t", mctick2timestr(played_delays))
|
||||||
.replace(r"%^t", mctick2timestr(total_delays))
|
.replace(r"%^t", mctick2timestr(total_delays))
|
||||||
.replace(r"%%%", "{:0>5.2f}%".format(int(10000 * played_delays / total_delays) / 100))
|
.replace(
|
||||||
|
r"%%%",
|
||||||
|
"{:0>5.2f}%".format(int(10000 * played_delays / total_delays) / 100),
|
||||||
|
)
|
||||||
.replace(
|
.replace(
|
||||||
"_",
|
"_",
|
||||||
self.played_style,
|
self.played_style,
|
||||||
@ -762,15 +784,15 @@ DEFAULT_PROGRESSBAR_STYLE = ProgressBarStyle(
|
|||||||
默认的进度条样式
|
默认的进度条样式
|
||||||
"""
|
"""
|
||||||
|
|
||||||
NoteChannelType = Mapping[
|
# NoteChannelType = Mapping[
|
||||||
int,
|
# int,
|
||||||
List[SingleNote,],
|
# List[SingleNote,],
|
||||||
]
|
# ]
|
||||||
"""
|
# """
|
||||||
频道信息类型
|
# 频道信息类型
|
||||||
|
|
||||||
Dict[int,Dict[int,List[SingleNote,],],]
|
# Dict[int,Dict[int,List[SingleNote,],],]
|
||||||
"""
|
# """
|
||||||
|
|
||||||
|
|
||||||
MineNoteChannelType = Mapping[
|
MineNoteChannelType = Mapping[
|
||||||
|
@ -24,7 +24,7 @@ from .constants import (
|
|||||||
MC_PITCHED_INSTRUMENT_LIST,
|
MC_PITCHED_INSTRUMENT_LIST,
|
||||||
MM_INSTRUMENT_RANGE_TABLE,
|
MM_INSTRUMENT_RANGE_TABLE,
|
||||||
)
|
)
|
||||||
from .subclass import SingleNote, MineNote, mctick2timestr
|
from .subclass import MineNote, mctick2timestr
|
||||||
|
|
||||||
from .types import (
|
from .types import (
|
||||||
Any,
|
Any,
|
||||||
@ -38,7 +38,6 @@ from .types import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def empty_midi_channels(channel_count: int = 17, staff: Any = {}) -> Dict[int, Any]:
|
def empty_midi_channels(channel_count: int = 17, staff: Any = {}) -> Dict[int, Any]:
|
||||||
"""
|
"""
|
||||||
空MIDI通道字典
|
空MIDI通道字典
|
||||||
@ -199,41 +198,41 @@ def minenote_to_command_paramaters(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def single_note_to_command_parameters(
|
# def single_note_to_command_parameters(
|
||||||
note_: SingleNote,
|
# note_: SingleNote,
|
||||||
reference_table: MidiInstrumentTableType,
|
# reference_table: MidiInstrumentTableType,
|
||||||
deviation: float = 0,
|
# deviation: float = 0,
|
||||||
volume_processing_method: Callable[[float], float] = natural_curve,
|
# volume_processing_method: Callable[[float], float] = natural_curve,
|
||||||
) -> Tuple[
|
# ) -> Tuple[
|
||||||
str,
|
# str,
|
||||||
Tuple[float, float, float],
|
# Tuple[float, float, float],
|
||||||
float,
|
# float,
|
||||||
Union[float, Literal[None]],
|
# Union[float, Literal[None]],
|
||||||
]:
|
# ]:
|
||||||
"""
|
# """
|
||||||
将音符转为播放的指令之参数
|
# 将音符转为播放的指令之参数
|
||||||
:param note_:int 音符对象
|
# :param note_:int 音符对象
|
||||||
:param reference_table:Dict[int, str] 转换对照表
|
# :param reference_table:Dict[int, str] 转换对照表
|
||||||
:param deviation:float 音调偏移量
|
# :param deviation:float 音调偏移量
|
||||||
:param volume_proccessing_method:Callable[[float], float] 音量处理函数
|
# :param volume_proccessing_method:Callable[[float], float] 音量处理函数
|
||||||
|
|
||||||
:return str[我的世界音符ID], Tuple[float,float,float]播放视角坐标, float[指令音量参数], float[指令音调参数]
|
# :return str[我的世界音符ID], Tuple[float,float,float]播放视角坐标, float[指令音量参数], float[指令音调参数]
|
||||||
"""
|
# """
|
||||||
|
|
||||||
mc_sound_ID, _X = inst_to_sould_with_deviation(
|
# mc_sound_ID, _X = inst_to_sould_with_deviation(
|
||||||
note_.inst,
|
# note_.inst,
|
||||||
reference_table,
|
# reference_table,
|
||||||
"note.bd" if note_.percussive else "note.flute",
|
# "note.bd" if note_.percussive else "note.flute",
|
||||||
)
|
# )
|
||||||
|
|
||||||
mc_distance_volume = volume_processing_method(note_.velocity)
|
# mc_distance_volume = volume_processing_method(note_.velocity)
|
||||||
|
|
||||||
return (
|
# return (
|
||||||
mc_sound_ID,
|
# mc_sound_ID,
|
||||||
(0, mc_distance_volume, 0),
|
# (0, mc_distance_volume, 0),
|
||||||
note_.velocity / 127,
|
# note_.velocity / 127,
|
||||||
None if note_.percussive else 2 ** ((note_.pitch - 60 - _X + deviation) / 12),
|
# None if note_.percussive else 2 ** ((note_.pitch - 60 - _X + deviation) / 12),
|
||||||
)
|
# )
|
||||||
|
|
||||||
|
|
||||||
def midi_msgs_to_minenote(
|
def midi_msgs_to_minenote(
|
||||||
@ -243,7 +242,6 @@ def midi_msgs_to_minenote(
|
|||||||
velocity_: int,
|
velocity_: int,
|
||||||
start_time_: int,
|
start_time_: int,
|
||||||
duration_: int,
|
duration_: int,
|
||||||
track_no_: 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],
|
||||||
@ -254,9 +252,8 @@ def midi_msgs_to_minenote(
|
|||||||
:param note_: int 音高编号(音符编号)
|
:param note_: int 音高编号(音符编号)
|
||||||
:param percussive_: bool 是否作为打击乐器启用
|
:param percussive_: bool 是否作为打击乐器启用
|
||||||
:param velocity_: int 力度(响度)
|
:param velocity_: int 力度(响度)
|
||||||
:param start_time_: int 音符起始时间(毫秒数)
|
:param start_time_: int 音符起始时间(微秒)
|
||||||
:param duration_: int 音符持续时间(毫秒数)
|
:param duration_: int 音符持续时间(微秒)
|
||||||
:param track_no_: 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_proccessing_method_: Callable[[float], float] 音量处理函数
|
||||||
@ -275,48 +272,47 @@ def midi_msgs_to_minenote(
|
|||||||
mc_sound_name=mc_sound_ID,
|
mc_sound_name=mc_sound_ID,
|
||||||
midi_pitch=note_,
|
midi_pitch=note_,
|
||||||
midi_velocity=velocity_,
|
midi_velocity=velocity_,
|
||||||
start_time=round(start_time_ / float(play_speed) / 50),
|
start_time=(tk := int(start_time_ / float(play_speed) / 50000)),
|
||||||
last_time=round(duration_ / float(play_speed) / 50),
|
last_time=round(duration_ / float(play_speed) / 50000),
|
||||||
track_number=track_no_,
|
mass_precision_time=round((start_time_ / float(play_speed) - tk * 50000) / 800),
|
||||||
is_percussion=percussive_,
|
is_percussion=percussive_,
|
||||||
displacement=(0, mc_distance_volume, 0),
|
displacement=(0, mc_distance_volume, 0),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def single_note_to_minenote(
|
# def single_note_to_minenote(
|
||||||
note_: SingleNote,
|
# note_: SingleNote,
|
||||||
reference_table: MidiInstrumentTableType,
|
# reference_table: MidiInstrumentTableType,
|
||||||
play_speed: float = 0,
|
# play_speed: float = 0,
|
||||||
volume_processing_method: Callable[[float], float] = natural_curve,
|
# volume_processing_method: Callable[[float], float] = natural_curve,
|
||||||
) -> MineNote:
|
# ) -> MineNote:
|
||||||
"""
|
# """
|
||||||
将音符转为我的世界音符对象
|
# 将音符转为我的世界音符对象
|
||||||
:param note_:SingleNote 音符对象
|
# :param note_:SingleNote 音符对象
|
||||||
:param reference_table:Dict[int, str] 转换对照表
|
# :param reference_table:Dict[int, str] 转换对照表
|
||||||
:param play_speed:float 播放速度
|
# :param play_speed:float 播放速度
|
||||||
:param volume_proccessing_method:Callable[[float], float] 音量处理函数
|
# :param volume_proccessing_method:Callable[[float], float] 音量处理函数
|
||||||
|
|
||||||
:return MineNote我的世界音符对象
|
# :return MineNote我的世界音符对象
|
||||||
"""
|
# """
|
||||||
mc_sound_ID = midi_inst_to_mc_sound(
|
# mc_sound_ID = midi_inst_to_mc_sound(
|
||||||
note_.inst,
|
# note_.inst,
|
||||||
reference_table,
|
# reference_table,
|
||||||
"note.bd" if note_.percussive else "note.flute",
|
# "note.bd" if note_.percussive else "note.flute",
|
||||||
)
|
# )
|
||||||
|
|
||||||
mc_distance_volume = volume_processing_method(note_.velocity)
|
# mc_distance_volume = volume_processing_method(note_.velocity)
|
||||||
|
|
||||||
return MineNote(
|
# return MineNote(
|
||||||
mc_sound_ID,
|
# mc_sound_name=mc_sound_ID,
|
||||||
note_.pitch,
|
# midi_pitch=note_.pitch,
|
||||||
note_.velocity,
|
# midi_velocity=note_.velocity,
|
||||||
round(note_.start_time / float(play_speed) / 50),
|
# start_time=round(note_.start_time / float(play_speed) / 50),
|
||||||
round(note_.duration / float(play_speed) / 50),
|
# last_time=round(note_.duration / float(play_speed) / 50),
|
||||||
note_.track_no,
|
# is_percussion=note_.percussive,
|
||||||
note_.percussive,
|
# displacement=(0, mc_distance_volume, 0),
|
||||||
(0, mc_distance_volume, 0),
|
# extra_information=note_.extra_info,
|
||||||
note_.extra_info,
|
# )
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def is_in_diapason(note_pitch: int, instrument: str) -> bool:
|
def is_in_diapason(note_pitch: int, instrument: str) -> bool:
|
||||||
@ -337,7 +333,7 @@ def note_to_redstone_block(
|
|||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
note_: SingleNote
|
note_: MineNote
|
||||||
音符类
|
音符类
|
||||||
random_select: bool
|
random_select: bool
|
||||||
是否随机选取对应方块
|
是否随机选取对应方块
|
||||||
|
@ -10,8 +10,12 @@ print(
|
|||||||
Musicreater.plugin.websocket.to_websocket_server(
|
Musicreater.plugin.websocket.to_websocket_server(
|
||||||
[
|
[
|
||||||
Musicreater.MidiConvert.from_midi_file(
|
Musicreater.MidiConvert.from_midi_file(
|
||||||
os.path.join(dire,names), old_exe_format=False
|
os.path.join(dire, names), old_exe_format=False
|
||||||
) for names in os.listdir(dire,) if names.endswith((".mid",".midi"))
|
)
|
||||||
|
for names in os.listdir(
|
||||||
|
dire,
|
||||||
|
)
|
||||||
|
if names.endswith((".mid", ".midi"))
|
||||||
],
|
],
|
||||||
input("服务器地址:"),
|
input("服务器地址:"),
|
||||||
int(input("服务器端口:")),
|
int(input("服务器端口:")),
|
||||||
|
Loading…
Reference in New Issue
Block a user