mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2025-01-31 15:31:55 +08:00
新的转换算法测试,更好的声音适配
This commit is contained in:
parent
0b0328bc44
commit
fc138f1dbf
@ -17,8 +17,8 @@ Terms & Conditions: License.md in the root directory
|
|||||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
|
||||||
__version__ = "1.6.1"
|
__version__ = "1.6.2"
|
||||||
__vername__ = "更新乐器对照表"
|
__vername__ = "新的转换算法测试,更好的声音适配"
|
||||||
__author__ = (
|
__author__ = (
|
||||||
("金羿", "Eilles Wan"),
|
("金羿", "Eilles Wan"),
|
||||||
("诸葛亮与八卦阵", "bgArray"),
|
("诸葛亮与八卦阵", "bgArray"),
|
||||||
|
@ -25,6 +25,7 @@ from .subclass import *
|
|||||||
from .utils import *
|
from .utils import *
|
||||||
from .types import Tuple, List, Dict
|
from .types import Tuple, List, Dict
|
||||||
|
|
||||||
|
|
||||||
class FutureMidiConvertRSNB(MidiConvert):
|
class FutureMidiConvertRSNB(MidiConvert):
|
||||||
"""
|
"""
|
||||||
加入红石音乐适配
|
加入红石音乐适配
|
||||||
@ -57,101 +58,6 @@ class FutureMidiConvertRSNB(MidiConvert):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
return "air"
|
return "air"
|
||||||
|
|
||||||
def to_note_list_in_delay(
|
|
||||||
self,
|
|
||||||
) -> Tuple[Dict[int, SingleNoteBox], int]:
|
|
||||||
"""
|
|
||||||
使用金羿的转换思路,将midi转换为我的世界音符盒音高、乐器及延迟列表,并输出每个音符之后的延迟
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
tuple( Dict[int, SingleNoteBox], int音乐时长游戏刻 )
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.to_music_channels()
|
|
||||||
|
|
||||||
tracks = {}
|
|
||||||
note_range = {}
|
|
||||||
InstID = -1
|
|
||||||
# cmd_amount = 0
|
|
||||||
|
|
||||||
# 此处 我们把通道视为音轨
|
|
||||||
for i in self.channels.keys():
|
|
||||||
# 如果当前通道为空 则跳过
|
|
||||||
if not self.channels[i]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 第十通道是打击乐通道
|
|
||||||
SpecialBits = True if i == 9 else False
|
|
||||||
|
|
||||||
for track_no, track in self.channels[i].items():
|
|
||||||
for msg in track:
|
|
||||||
if msg[0] == "PgmC":
|
|
||||||
InstID = msg[1]
|
|
||||||
|
|
||||||
elif msg[0] == "NoteS":
|
|
||||||
# block_id = self.soundID_to_block((
|
|
||||||
# self.perc_inst_to_soundID_withX(msg[1])
|
|
||||||
# if SpecialBits
|
|
||||||
# else self.inst_to_souldID_withX(InstID)
|
|
||||||
# )[0])
|
|
||||||
|
|
||||||
# delaytime_now = round(msg[-1] / 50)
|
|
||||||
|
|
||||||
note_ = 2 ** ((msg[1] - 60) / 12)
|
|
||||||
|
|
||||||
try:
|
|
||||||
tracks[msg[-1]].append(
|
|
||||||
(
|
|
||||||
InstID,
|
|
||||||
note_,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except KeyError:
|
|
||||||
tracks[msg[-1]] = [(InstID, note_)]
|
|
||||||
|
|
||||||
try:
|
|
||||||
note_range[InstID]["max"] = max(
|
|
||||||
note_range[InstID]["max"], note_
|
|
||||||
)
|
|
||||||
note_range[InstID]["min"] = min(
|
|
||||||
note_range[InstID]["min"], note_
|
|
||||||
)
|
|
||||||
except KeyError:
|
|
||||||
note_range[InstID] = {"max": note_, "min": note_}
|
|
||||||
|
|
||||||
del InstID
|
|
||||||
all_ticks = list(tracks.keys())
|
|
||||||
all_ticks.sort()
|
|
||||||
results = []
|
|
||||||
print(note_range)
|
|
||||||
|
|
||||||
exit()
|
|
||||||
|
|
||||||
for i in range(len(all_ticks)):
|
|
||||||
for j in range(len(tracks[all_ticks[i]])):
|
|
||||||
results.append(
|
|
||||||
SingleCommand(
|
|
||||||
tracks[all_ticks[i]][j],
|
|
||||||
tick_delay=(
|
|
||||||
0
|
|
||||||
if j != 0
|
|
||||||
else (
|
|
||||||
all_ticks[i] - all_ticks[i - 1]
|
|
||||||
if i != 0
|
|
||||||
else all_ticks[i]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
annotation="在{}播放{}%的{}音".format(
|
|
||||||
mctick2timestr(i), max_volume * 100, ""
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.music_command_list = results
|
|
||||||
self.music_tick_num = max(all_ticks)
|
|
||||||
return results, self.music_tick_num
|
|
||||||
|
|
||||||
|
|
||||||
class FutureMidiConvertM4(MidiConvert):
|
class FutureMidiConvertM4(MidiConvert):
|
||||||
"""
|
"""
|
||||||
@ -163,172 +69,50 @@ class FutureMidiConvertM4(MidiConvert):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _linear_note(
|
def _linear_note(
|
||||||
_note: SingleNote,
|
_note: SingleNote,
|
||||||
_apply_time_division: int = 100,
|
_apply_time_division: float = 100,
|
||||||
) -> List[Tuple[int, int, int, int, float],]:
|
) -> List[SingleNote]:
|
||||||
"""传入音符数据,返回以半秒为分割的插值列表
|
"""传入音符数据,返回以半秒为分割的插值列表
|
||||||
:param _note: SingleNote 音符
|
:param _note: SingleNote 音符
|
||||||
|
:param _apply_time_division: int 间隔毫秒数
|
||||||
:return list[tuple(int开始时间(毫秒), int乐器, int音符, int力度(内置), float音量(播放)),]"""
|
:return list[tuple(int开始时间(毫秒), int乐器, int音符, int力度(内置), float音量(播放)),]"""
|
||||||
|
|
||||||
|
if _note.percussive:
|
||||||
|
return [
|
||||||
|
_note,
|
||||||
|
]
|
||||||
|
|
||||||
totalCount = int(_note.duration / _apply_time_division)
|
totalCount = int(_note.duration / _apply_time_division)
|
||||||
|
|
||||||
if totalCount == 0:
|
if totalCount == 0:
|
||||||
return [
|
return [
|
||||||
(_note.start_time, _note.inst, _note.pitch, _note.velocity, 1),
|
_note,
|
||||||
]
|
]
|
||||||
# print(totalCount)
|
# print(totalCount)
|
||||||
|
|
||||||
result: List[Tuple[int, int, int, int, float],] = []
|
result: List[SingleNote] = []
|
||||||
|
|
||||||
for _i in range(totalCount):
|
for _i in range(totalCount):
|
||||||
result.append(
|
result.append(
|
||||||
(
|
SingleNote(
|
||||||
_note.start_time + _i * _apply_time_division,
|
instrument=_note.inst,
|
||||||
_note.instrument,
|
pitch=_note.pitch,
|
||||||
_note.pitch,
|
velocity=_note.velocity,
|
||||||
_note.velocity,
|
startime=int(_note.start_time + _i * (_note.duration / totalCount)),
|
||||||
((totalCount - _i) / totalCount),
|
lastime=int(_note.duration / totalCount),
|
||||||
|
track_number=_note.track_no,
|
||||||
|
is_percussion=_note.percussive,
|
||||||
)
|
)
|
||||||
|
# (
|
||||||
|
# _note.start_time + _i * _apply_time_division,
|
||||||
|
# _note.instrument,
|
||||||
|
# _note.pitch,
|
||||||
|
# _note.velocity,
|
||||||
|
# ((totalCount - _i) / totalCount),
|
||||||
|
# )
|
||||||
)
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# 简单的单音填充
|
|
||||||
def to_command_list_in_score(
|
|
||||||
self,
|
|
||||||
scoreboard_name: str = "mscplay",
|
|
||||||
max_volume: float = 1.0,
|
|
||||||
speed: float = 1.0,
|
|
||||||
) -> Tuple[List[List[SingleCommand]], int, int]:
|
|
||||||
"""
|
|
||||||
使用金羿的转换思路,使用完全填充算法优化音感后,将midi转换为我的世界命令列表
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
scoreboard_name: str
|
|
||||||
我的世界的计分板名称
|
|
||||||
max_volume: float
|
|
||||||
最大播放音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放
|
|
||||||
speed: float
|
|
||||||
速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
tuple( list[list[SingleCommand指令,... ],... ], int指令数量, int音乐时长游戏刻 )
|
|
||||||
"""
|
|
||||||
|
|
||||||
if speed == 0:
|
|
||||||
raise ZeroSpeedError("播放速度仅可为正实数")
|
|
||||||
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
|
|
||||||
|
|
||||||
self.to_music_channels()
|
|
||||||
|
|
||||||
note_channels: Dict[int, List[SingleNote]] = empty_midi_channels(staff=[])
|
|
||||||
InstID = -1
|
|
||||||
|
|
||||||
# 此处 我们把通道视为音轨
|
|
||||||
for i in self.channels.keys():
|
|
||||||
# 如果当前通道为空 则跳过
|
|
||||||
if not self.channels[i]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# nowChannel = []
|
|
||||||
for track_no, track in self.channels[i].items():
|
|
||||||
noteMsgs = []
|
|
||||||
MsgIndex = []
|
|
||||||
|
|
||||||
for msg in track:
|
|
||||||
if msg[0] == "PgmC":
|
|
||||||
InstID = msg[1]
|
|
||||||
|
|
||||||
elif msg[0] == "NoteS":
|
|
||||||
noteMsgs.append(msg[1:])
|
|
||||||
MsgIndex.append(msg[1])
|
|
||||||
|
|
||||||
elif msg[0] == "NoteE":
|
|
||||||
if msg[1] in MsgIndex:
|
|
||||||
note_channels[i].append(
|
|
||||||
SingleNote(
|
|
||||||
InstID,
|
|
||||||
msg[1],
|
|
||||||
noteMsgs[MsgIndex.index(msg[1])][1],
|
|
||||||
noteMsgs[MsgIndex.index(msg[1])][2],
|
|
||||||
msg[-1] - noteMsgs[MsgIndex.index(msg[1])][2],
|
|
||||||
track_number=track_no,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
noteMsgs.pop(MsgIndex.index(msg[1]))
|
|
||||||
MsgIndex.pop(MsgIndex.index(msg[1]))
|
|
||||||
|
|
||||||
del InstID
|
|
||||||
|
|
||||||
tracks = []
|
|
||||||
cmd_amount = 0
|
|
||||||
max_score = 0
|
|
||||||
|
|
||||||
# 此处 我们把通道视为音轨
|
|
||||||
for no, track in note_channels.items():
|
|
||||||
# 如果当前通道为空 则跳过
|
|
||||||
if not track:
|
|
||||||
continue
|
|
||||||
|
|
||||||
SpecialBits = True if no == 9 else False
|
|
||||||
|
|
||||||
track_now = []
|
|
||||||
|
|
||||||
for note in track:
|
|
||||||
for every_note in self._linear_note(
|
|
||||||
note, 100 if note.track_no == 0 else 500
|
|
||||||
):
|
|
||||||
soundID, _X = (
|
|
||||||
self.perc_inst_to_soundID_withX(note.pitch)
|
|
||||||
if SpecialBits
|
|
||||||
else self.inst_to_souldID_withX(note.inst)
|
|
||||||
)
|
|
||||||
|
|
||||||
score_now = round(every_note[0] / speed / 50)
|
|
||||||
|
|
||||||
max_score = max(max_score, score_now)
|
|
||||||
mc_pitch = 2 ** ((note.pitch - 60 - _X) / 12)
|
|
||||||
blockmeter = (
|
|
||||||
1
|
|
||||||
/ (1 if note.track_no == 0 else 0.9)
|
|
||||||
/ max_volume
|
|
||||||
/ every_note[4]
|
|
||||||
- 1
|
|
||||||
)
|
|
||||||
|
|
||||||
track_now.append(
|
|
||||||
SingleCommand(
|
|
||||||
self.execute_cmd_head.format(
|
|
||||||
"@a[scores=({}={})]".format(scoreboard_name, score_now)
|
|
||||||
.replace("(", r"{")
|
|
||||||
.replace(")", r"}")
|
|
||||||
)
|
|
||||||
+ "playsound {} @s ^ ^ ^{} {} {}".format(
|
|
||||||
soundID,
|
|
||||||
blockmeter,
|
|
||||||
note.velocity / 128,
|
|
||||||
"" if SpecialBits else mc_pitch,
|
|
||||||
),
|
|
||||||
annotation="在{}播放{}%({}BM)的{}音".format(
|
|
||||||
mctick2timestr(score_now),
|
|
||||||
max_volume * 100,
|
|
||||||
blockmeter,
|
|
||||||
"{}:{}".format(soundID, note.pitch),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
cmd_amount += 1
|
|
||||||
|
|
||||||
if track_now:
|
|
||||||
self.music_command_list.extend(track_now)
|
|
||||||
tracks.append(track_now)
|
|
||||||
|
|
||||||
self.music_tick_num = max_score
|
|
||||||
return (tracks, cmd_amount, max_score)
|
|
||||||
|
|
||||||
# 简单的单音填充的延迟应用
|
|
||||||
def to_command_list_in_delay(
|
def to_command_list_in_delay(
|
||||||
self,
|
self,
|
||||||
max_volume: float = 1.0,
|
max_volume: float = 1.0,
|
||||||
@ -336,7 +120,7 @@ class FutureMidiConvertM4(MidiConvert):
|
|||||||
player_selector: str = "@a",
|
player_selector: str = "@a",
|
||||||
) -> Tuple[List[SingleCommand], int, int]:
|
) -> Tuple[List[SingleCommand], int, int]:
|
||||||
"""
|
"""
|
||||||
使用金羿的转换思路,使用完全填充算法优化音感后,将midi转换为我的世界命令列表,并输出每个音符之后的延迟
|
将midi转换为我的世界命令列表,并输出每个音符之后的延迟
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
@ -353,130 +137,48 @@ class FutureMidiConvertM4(MidiConvert):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if speed == 0:
|
if speed == 0:
|
||||||
raise ZeroSpeedError("播放速度仅可为正实数")
|
raise ZeroSpeedError("播放速度仅可为(0,1]范围内的正实数")
|
||||||
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
|
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
|
||||||
|
|
||||||
self.to_music_channels()
|
notes_list: List[SingleNote] = []
|
||||||
|
|
||||||
note_channels: Dict[int, List[SingleNote]] = empty_midi_channels(staff=[])
|
|
||||||
InstID = -1
|
|
||||||
|
|
||||||
# 此处 我们把通道视为音轨
|
# 此处 我们把通道视为音轨
|
||||||
for i in self.channels.keys():
|
for channel in self.to_music_note_channels().values():
|
||||||
# 如果当前通道为空 则跳过
|
for note in channel:
|
||||||
if not self.channels[i]:
|
if not note.percussive:
|
||||||
continue
|
notes_list.extend(self._linear_note(note, note.get_mc_pitch * 500))
|
||||||
|
|
||||||
# nowChannel = []
|
|
||||||
for track_no, track in self.channels[i].items():
|
|
||||||
noteMsgs = []
|
|
||||||
MsgIndex = []
|
|
||||||
|
|
||||||
for msg in track:
|
|
||||||
if msg[0] == "PgmC":
|
|
||||||
InstID = msg[1]
|
|
||||||
|
|
||||||
elif msg[0] == "NoteS":
|
|
||||||
noteMsgs.append(msg[1:])
|
|
||||||
MsgIndex.append(msg[1])
|
|
||||||
|
|
||||||
elif msg[0] == "NoteE":
|
|
||||||
if msg[1] in MsgIndex:
|
|
||||||
note_channels[i].append(
|
|
||||||
SingleNote(
|
|
||||||
InstID,
|
|
||||||
msg[1],
|
|
||||||
noteMsgs[MsgIndex.index(msg[1])][1],
|
|
||||||
noteMsgs[MsgIndex.index(msg[1])][2],
|
|
||||||
msg[-1] - noteMsgs[MsgIndex.index(msg[1])][2],
|
|
||||||
track_number=track_no,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
noteMsgs.pop(MsgIndex.index(msg[1]))
|
|
||||||
MsgIndex.pop(MsgIndex.index(msg[1]))
|
|
||||||
|
|
||||||
del InstID
|
|
||||||
|
|
||||||
tracks = {}
|
|
||||||
InstID = -1
|
|
||||||
# open("RES.TXT", "w", encoding="utf-8").write(str(note_channels))
|
|
||||||
|
|
||||||
# 此处 我们把通道视为音轨
|
|
||||||
for no, track in note_channels.items():
|
|
||||||
# 如果当前通道为空 则跳过
|
|
||||||
if not track:
|
|
||||||
continue
|
|
||||||
|
|
||||||
SpecialBits = True if no == 9 else False
|
|
||||||
|
|
||||||
for note in track:
|
|
||||||
liner_list = self._linear_note(note, 100 if note.track_no == 0 else 500)
|
|
||||||
for every_note in liner_list:
|
|
||||||
soundID, _X = (
|
|
||||||
self.perc_inst_to_soundID_withX(note.pitch)
|
|
||||||
if SpecialBits
|
|
||||||
else self.inst_to_souldID_withX(note.inst)
|
|
||||||
)
|
|
||||||
|
|
||||||
score_now = round(every_note[0] / speed / 50)
|
|
||||||
|
|
||||||
try:
|
|
||||||
tracks[score_now].append(
|
|
||||||
self.execute_cmd_head.format(player_selector)
|
|
||||||
+ f"playsound {soundID} @s ^ ^ ^{1 / (1 if note.track_no == 0 else 0.9) / max_volume / every_note[4] - 1} {note.velocity / 128} "
|
|
||||||
+ (
|
|
||||||
""
|
|
||||||
if SpecialBits
|
|
||||||
else f"{2 ** ((note.pitch - 60 - _X) / 12)}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except KeyError:
|
|
||||||
tracks[score_now] = [
|
|
||||||
self.execute_cmd_head.format(player_selector)
|
|
||||||
+ f"playsound {soundID} @s ^ ^ ^{1 / (1 if note.track_no == 0 else 0.9) / max_volume / every_note[4] - 1} {note.velocity / 128} "
|
|
||||||
+ (
|
|
||||||
""
|
|
||||||
if SpecialBits
|
|
||||||
else f"{2 ** ((note.pitch - 60 - _X) / 12)}"
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
all_ticks = list(tracks.keys())
|
|
||||||
all_ticks.sort()
|
|
||||||
results: List[SingleCommand] = []
|
|
||||||
max_multi = 0
|
|
||||||
now_multi_delay = 0
|
|
||||||
now_multi = 0
|
|
||||||
|
|
||||||
for i in range(len(all_ticks)):
|
|
||||||
l = len(tracks[all_ticks[i]])
|
|
||||||
for j in range(l):
|
|
||||||
results.append(
|
|
||||||
SingleCommand(
|
|
||||||
tracks[all_ticks[i]][j],
|
|
||||||
tick_delay=(
|
|
||||||
0
|
|
||||||
if j != 0
|
|
||||||
else (
|
|
||||||
all_ticks[i] - all_ticks[i - 1]
|
|
||||||
if i != 0
|
|
||||||
else all_ticks[i]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
annotation="由 音·创 生成",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if results[-1].delay + now_multi_delay <= 1:
|
|
||||||
now_multi += 1
|
|
||||||
now_multi_delay += results[-1].delay
|
|
||||||
else:
|
else:
|
||||||
max_multi = max(max_multi, now_multi)
|
notes_list.append(note)
|
||||||
now_multi = 0
|
|
||||||
now_multi_delay = 0
|
|
||||||
|
|
||||||
self.music_command_list = results
|
notes_list.sort(key=lambda a: a.start_time)
|
||||||
self.music_tick_num = max(all_ticks)
|
|
||||||
return results, self.music_tick_num, max_multi
|
self.music_command_list = []
|
||||||
|
multi = max_multi = 0
|
||||||
|
delaytime_previous = 0
|
||||||
|
|
||||||
|
for note in notes_list:
|
||||||
|
delaytime_now = round(note.start_time / speed / 50)
|
||||||
|
if (tickdelay := (delaytime_now - delaytime_previous)) == 0:
|
||||||
|
multi += 1
|
||||||
|
else:
|
||||||
|
max_multi = max(max_multi, multi)
|
||||||
|
multi = 0
|
||||||
|
self.music_command_list.append(
|
||||||
|
SingleCommand(
|
||||||
|
self.execute_cmd_head.format(player_selector)
|
||||||
|
+ note.to_command(max_volume),
|
||||||
|
tick_delay=tickdelay,
|
||||||
|
annotation="在{}播放{}%的{}音".format(
|
||||||
|
mctick2timestr(delaytime_now),
|
||||||
|
max_volume * 100,
|
||||||
|
"{}:{}".format(note.mc_sound_ID, note.mc_pitch),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
delaytime_previous = delaytime_now
|
||||||
|
|
||||||
|
self.music_tick_num = round(notes_list[-1].start_time / speed / 50)
|
||||||
|
return self.music_command_list, self.music_tick_num, max_multi + 1
|
||||||
|
|
||||||
|
|
||||||
class FutureMidiConvertM5(MidiConvert):
|
class FutureMidiConvertM5(MidiConvert):
|
||||||
|
@ -631,7 +631,9 @@ class MidiConvert:
|
|||||||
.replace("(", r"{")
|
.replace("(", r"{")
|
||||||
.replace(")", r"}")
|
.replace(")", r"}")
|
||||||
)
|
)
|
||||||
+ note.to_command(max_volume),
|
+ note.to_command(
|
||||||
|
(max_volume) if note.track_no == 0 else (max_volume * 0.9)
|
||||||
|
),
|
||||||
annotation="在{}播放{}%的{}音".format(
|
annotation="在{}播放{}%的{}音".format(
|
||||||
mctick2timestr(score_now),
|
mctick2timestr(score_now),
|
||||||
max_volume * 100,
|
max_volume * 100,
|
||||||
@ -698,7 +700,9 @@ class MidiConvert:
|
|||||||
self.music_command_list.append(
|
self.music_command_list.append(
|
||||||
SingleCommand(
|
SingleCommand(
|
||||||
self.execute_cmd_head.format(player_selector)
|
self.execute_cmd_head.format(player_selector)
|
||||||
+ note.to_command(max_volume),
|
+ note.to_command(
|
||||||
|
(max_volume) if note.track_no == 0 else (max_volume * 0.9)
|
||||||
|
),
|
||||||
tick_delay=tickdelay,
|
tick_delay=tickdelay,
|
||||||
annotation="在{}播放{}%的{}音".format(
|
annotation="在{}播放{}%的{}音".format(
|
||||||
mctick2timestr(delaytime_now),
|
mctick2timestr(delaytime_now),
|
||||||
|
@ -21,7 +21,7 @@ from dataclasses import dataclass
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from .constants import PERCUSSION_INSTRUMENT_LIST
|
from .constants import PERCUSSION_INSTRUMENT_LIST
|
||||||
from .utils import inst_to_souldID_withX, perc_inst_to_soundID_withX
|
from .utils import inst_to_souldID_withX, perc_inst_to_soundID_withX, volume2distance
|
||||||
|
|
||||||
|
|
||||||
@dataclass(init=False)
|
@dataclass(init=False)
|
||||||
@ -78,8 +78,6 @@ class SingleNote:
|
|||||||
"""音符持续时间 ms"""
|
"""音符持续时间 ms"""
|
||||||
self.track_no: int = track_number
|
self.track_no: int = track_number
|
||||||
"""音符所处的音轨"""
|
"""音符所处的音轨"""
|
||||||
self.track_no: int = track_number
|
|
||||||
"""音符所处的音轨"""
|
|
||||||
|
|
||||||
self.percussive = (
|
self.percussive = (
|
||||||
(is_percussion in PERCUSSION_INSTRUMENT_LIST)
|
(is_percussion in PERCUSSION_INSTRUMENT_LIST)
|
||||||
@ -89,31 +87,48 @@ class SingleNote:
|
|||||||
"""是否为打击乐器"""
|
"""是否为打击乐器"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def inst(self):
|
def inst(self) -> int:
|
||||||
"""乐器编号"""
|
"""乐器编号"""
|
||||||
return self.instrument
|
return self.instrument
|
||||||
|
|
||||||
@inst.setter
|
@inst.setter
|
||||||
def inst(self, inst_):
|
def inst(self, inst_: int):
|
||||||
self.instrument = inst_
|
self.instrument = inst_
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pitch(self):
|
def pitch(self) -> int:
|
||||||
"""音符编号"""
|
"""音符编号"""
|
||||||
return self.note
|
return self.note
|
||||||
|
|
||||||
def __str__(self):
|
@property
|
||||||
return "{}Note(Instrument = {}, {}Velocity = {}, StartTime = {}, Duration = {},)".format(
|
def get_mc_pitch(self) -> float:
|
||||||
|
self.mc_sound_ID, _X = (
|
||||||
|
perc_inst_to_soundID_withX(self.inst)
|
||||||
|
if self.percussive
|
||||||
|
else inst_to_souldID_withX(self.inst)
|
||||||
|
)
|
||||||
|
return -1 if self.percussive else 2 ** ((self.note - 60 - _X) / 12)
|
||||||
|
|
||||||
|
def __str__(self, is_track: bool = False):
|
||||||
|
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 "",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __tuple__(self):
|
def __tuple__(self):
|
||||||
return (
|
return (
|
||||||
(self.percussive, self.inst, self.velocity, self.start_time, self.duration)
|
(
|
||||||
|
self.percussive,
|
||||||
|
self.inst,
|
||||||
|
self.velocity,
|
||||||
|
self.start_time,
|
||||||
|
self.duration,
|
||||||
|
self.track_no,
|
||||||
|
)
|
||||||
if self.percussive
|
if self.percussive
|
||||||
else (
|
else (
|
||||||
self.percussive,
|
self.percussive,
|
||||||
@ -122,6 +137,7 @@ class SingleNote:
|
|||||||
self.velocity,
|
self.velocity,
|
||||||
self.start_time,
|
self.start_time,
|
||||||
self.duration,
|
self.duration,
|
||||||
|
self.track_no,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -133,6 +149,7 @@ class SingleNote:
|
|||||||
"Velocity": self.velocity,
|
"Velocity": self.velocity,
|
||||||
"StartTime": self.start_time,
|
"StartTime": self.start_time,
|
||||||
"Duration": self.duration,
|
"Duration": self.duration,
|
||||||
|
"Track": self.track_no,
|
||||||
}
|
}
|
||||||
if self.percussive
|
if self.percussive
|
||||||
else {
|
else {
|
||||||
@ -142,6 +159,7 @@ class SingleNote:
|
|||||||
"Velocity": self.velocity,
|
"Velocity": self.velocity,
|
||||||
"StartTime": self.start_time,
|
"StartTime": self.start_time,
|
||||||
"Duration": self.duration,
|
"Duration": self.duration,
|
||||||
|
"Track": self.track_no,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -165,14 +183,13 @@ class SingleNote:
|
|||||||
|
|
||||||
# delaytime_now = round(self.start_time / float(speed) / 50)
|
# delaytime_now = round(self.start_time / float(speed) / 50)
|
||||||
self.mc_pitch = "" if self.percussive else 2 ** ((self.note - 60 - _X) / 12)
|
self.mc_pitch = "" if self.percussive else 2 ** ((self.note - 60 - _X) / 12)
|
||||||
self.mc_distance_volume = 128 / volume_percentage / self.velocity + (
|
|
||||||
1 if self.percussive else self.velocity / 32
|
self.mc_distance_volume = volume2distance(self.velocity * volume_percentage)
|
||||||
)
|
|
||||||
|
|
||||||
return "playsound {} @s ^ ^ ^{} {} {}".format(
|
return "playsound {} @s ^ ^ ^{} {} {}".format(
|
||||||
self.mc_sound_ID,
|
self.mc_sound_ID,
|
||||||
self.mc_distance_volume,
|
self.mc_distance_volume,
|
||||||
self.velocity / 128,
|
volume_percentage,
|
||||||
self.mc_pitch,
|
self.mc_pitch,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ ChannelType = Dict[
|
|||||||
],
|
],
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
以字典所标记的频道信息类型(即将弃用)
|
以字典所标记的频道信息类型(已弃用)
|
||||||
|
|
||||||
Dict[int,Dict[int,List[Union[Tuple[Literal["PgmC"], int, int],Tuple[Literal["NoteS"], int, int, int],Tuple[Literal["NoteE"], int, int],]],],]
|
Dict[int,Dict[int,List[Union[Tuple[Literal["PgmC"], int, int],Tuple[Literal["NoteS"], int, int, int],Tuple[Literal["NoteE"], int, int],]],],]
|
||||||
"""
|
"""
|
||||||
|
@ -15,6 +15,8 @@ Terms & Conditions: License.md in the root directory
|
|||||||
# Email TriM-Organization@hotmail.com
|
# Email TriM-Organization@hotmail.com
|
||||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
from .constants import PERCUSSION_INSTRUMENT_TABLE, PITCHED_INSTRUMENT_TABLE
|
from .constants import PERCUSSION_INSTRUMENT_TABLE, PITCHED_INSTRUMENT_TABLE
|
||||||
from typing import Any, Dict, Tuple
|
from typing import Any, Dict, Tuple
|
||||||
|
|
||||||
@ -89,3 +91,29 @@ def perc_inst_to_soundID_withX(instrumentID: int) -> Tuple[str, int]:
|
|||||||
|
|
||||||
# 明明已经走了
|
# 明明已经走了
|
||||||
# 凭什么还要在我心里留下缠绵缱绻
|
# 凭什么还要在我心里留下缠绵缱绻
|
||||||
|
|
||||||
|
|
||||||
|
def volume2distance(vol: float) -> float:
|
||||||
|
"""
|
||||||
|
midi力度值拟合成的距离函数
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
vol: int
|
||||||
|
midi音符力度值
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
float播放中心到玩家的距离
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
-8.081720684086314
|
||||||
|
* math.log(
|
||||||
|
vol + 14.579508825070013,
|
||||||
|
)
|
||||||
|
+ 37.65806375944386
|
||||||
|
if vol < 60.64
|
||||||
|
else 0.2721359356095803 * ((vol + 2592.272889454798) ** 1.358571233418649)
|
||||||
|
+ -6.313841334963396 * (vol + 2592.272889454798)
|
||||||
|
+ 4558.496367823575
|
||||||
|
)
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import Musicreater.experiment
|
import Musicreater.experiment
|
||||||
import Musicreater.plugin
|
import Musicreater.plugin
|
||||||
import Musicreater.plugin.addonpack
|
import Musicreater.plugin.mcstructfile
|
||||||
|
|
||||||
print(
|
print(
|
||||||
Musicreater.plugin.addonpack.to_addon_pack_in_delay(
|
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay(
|
||||||
Musicreater.experiment.FutureMidiConvertM4.from_midi_file(input("midi路径:"), old_exe_format=False),
|
Musicreater.experiment.FutureMidiConvertM4.from_midi_file(
|
||||||
Musicreater.plugin.ConvertConfig(
|
input("midi路径:"), old_exe_format=False
|
||||||
input("输出路径:"),
|
|
||||||
volume=1
|
|
||||||
),
|
),
|
||||||
|
Musicreater.plugin.ConvertConfig(input("输出路径:"), volume=1),
|
||||||
|
max_height=32,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -6,5 +6,6 @@ print(
|
|||||||
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay(
|
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay(
|
||||||
Musicreater.MidiConvert.from_midi_file(input("midi路径:"), old_exe_format=False),
|
Musicreater.MidiConvert.from_midi_file(input("midi路径:"), old_exe_format=False),
|
||||||
Musicreater.plugin.ConvertConfig(input("输出路径:"), volume=1),
|
Musicreater.plugin.ConvertConfig(input("输出路径:"), volume=1),
|
||||||
|
max_height=32,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
42
resources/test/volumn_function_fit.py
Normal file
42
resources/test/volumn_function_fit.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import numpy as np
|
||||||
|
from scipy.optimize import curve_fit
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
|
||||||
|
def q_function1(x, a, a2, c1,):
|
||||||
|
return a * np.log( x + a2,)+ c1
|
||||||
|
|
||||||
|
def q_function2(x, b, b2, b3, b4, c2):
|
||||||
|
return b * ((x + b2) ** b3) + b4 * (x+b2) + c2
|
||||||
|
|
||||||
|
|
||||||
|
x_data = np.array([0, 16, 32, 48, 64, 80, 96, 112, 128])
|
||||||
|
y_data = np.array([16, 10, 6.75, 4, 2.5, 1.6, 0.8, 0.3, 0])
|
||||||
|
|
||||||
|
|
||||||
|
p_est1, err_est1 = curve_fit(q_function1, x_data[:5], y_data[:5], maxfev=1000000)
|
||||||
|
p_est2, err_est2 = curve_fit(q_function2, x_data[4:], y_data[4:], maxfev=1000000)
|
||||||
|
|
||||||
|
|
||||||
|
print(q_function1(x_data[:5], *p_est1))
|
||||||
|
print(q_function2(x_data[4:], *p_est2))
|
||||||
|
|
||||||
|
print("参数一:",*p_est1)
|
||||||
|
print("参数二:",*p_est2)
|
||||||
|
|
||||||
|
# 绘制图像
|
||||||
|
plt.plot(
|
||||||
|
np.arange(0, 64.1, 0.1), q_function1(np.arange(0, 64.1, 0.1), *p_est1), label=r"FIT1"
|
||||||
|
)
|
||||||
|
plt.plot(
|
||||||
|
np.arange(64, 128.1, 0.1), q_function2(np.arange(64, 128.1, 0.1), *p_est2), label=r"FIT2"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
plt.scatter(x_data, y_data, color="red") # 标记给定的点
|
||||||
|
# plt.xlabel('x')
|
||||||
|
# plt.ylabel('y')
|
||||||
|
plt.title("Function Fit")
|
||||||
|
plt.legend()
|
||||||
|
# plt.grid(True)
|
||||||
|
plt.show()
|
36
resources/test/volumn_function_test.py
Normal file
36
resources/test/volumn_function_test.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# 定义对数函数
|
||||||
|
def q_function1(vol):
|
||||||
|
# return -23.65060754864053*((x+508.2130392724084)**0.8433764630986903) + 7.257078620637543 * (x+407.86870598508153) + 1585.6201108739122
|
||||||
|
# return -58.863374003875954 *((x+12.41481943150274 )**0.9973316187745871 ) +57.92341268595151 * (x+ 13.391132186222036) + -32.92986286030519
|
||||||
|
return -8.081720684086314 * np.log( vol + 14.579508825070013,)+ 37.65806375944386
|
||||||
|
|
||||||
|
|
||||||
|
def q_function2(vol):
|
||||||
|
return 0.2721359356095803 * ((vol + 2592.272889454798) ** 1.358571233418649) + -6.313841334963396 * (vol + 2592.272889454798) + 4558.496367823575
|
||||||
|
|
||||||
|
# 生成 x 值
|
||||||
|
x_values = np.linspace(0, 128, 1000)
|
||||||
|
|
||||||
|
|
||||||
|
x_data = np.array([0,16,32,48,64,80,96,112,128])
|
||||||
|
y_data = np.array([16, 10, 6.75, 4, 2.5, 1.6, 0.8, 0.3, 0])
|
||||||
|
|
||||||
|
|
||||||
|
print(q_function1(x_data))
|
||||||
|
print(q_function2(x_data))
|
||||||
|
|
||||||
|
# 绘制图像
|
||||||
|
plt.plot(x_values, q_function1(x_values,),label = "fit1")
|
||||||
|
plt.plot(x_values, q_function2(x_values,),label = "fit2")
|
||||||
|
plt.scatter(x_data, y_data, color='red') # 标记给定的点
|
||||||
|
# plt.scatter(x_data, y_data2, color='green') # 标记给定的点
|
||||||
|
# plt.scatter(x_data, y_data3, color='blue') # 标记给定的点
|
||||||
|
plt.xlabel('x')
|
||||||
|
plt.ylabel('y')
|
||||||
|
plt.title('Function')
|
||||||
|
plt.legend()
|
||||||
|
plt.grid(True)
|
||||||
|
plt.show()
|
Loading…
x
Reference in New Issue
Block a user