mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2025-02-07 11:26:30 +08:00
重大更新:优化算法+红石指令音乐生成
This commit is contained in:
parent
5c86a28b44
commit
83c9750db3
@ -17,8 +17,8 @@ Terms & Conditions: License.md in the root directory
|
|||||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
|
||||||
__version__ = "1.2.1"
|
__version__ = "1.4.0"
|
||||||
__vername__ = "扩大对有问题的Midi文件的兼容性"
|
__vername__ = "红石指令音乐的生成"
|
||||||
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"), ("鸣凤鸽子", "MingFengPigeon"))
|
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"), ("鸣凤鸽子", "MingFengPigeon"))
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# 主要类
|
# 主要类
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from typing import Dict, List, Tuple
|
||||||
|
|
||||||
"""
|
"""
|
||||||
存放常量与数值性内容
|
存放常量与数值性内容
|
||||||
"""
|
"""
|
||||||
@ -40,30 +42,34 @@ DEFAULT_PROGRESSBAR_STYLE = (
|
|||||||
默认的进度条样式组
|
默认的进度条样式组
|
||||||
"""
|
"""
|
||||||
|
|
||||||
PITCHED_INSTRUMENT_LIST = {
|
|
||||||
|
# 以下是由 Touch “偷吃” 带来的高准确率音效对照表
|
||||||
|
# 包括乐音乐器对照和打击乐器对照
|
||||||
|
|
||||||
|
PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
|
||||||
0: ("note.harp", 6),
|
0: ("note.harp", 6),
|
||||||
1: ("note.harp", 6),
|
1: ("note.harp", 6),
|
||||||
2: ("note.pling", 6),
|
2: ("note.pling", 6),
|
||||||
3: ("note.harp", 6),
|
3: ("note.harp", 6),
|
||||||
4: ("note.pling", 6),
|
4: ("note.pling", 6),
|
||||||
5: ("note.pling", 6),
|
5: ("note.pling", 6),
|
||||||
6: ("note.harp", 6),
|
6: ("note.guitar", 7),
|
||||||
7: ("note.harp", 6),
|
7: ("note.harp", 6),
|
||||||
8: ("note.share", 7), # 打击乐器无音域
|
8: ("note.bell", 4), # 打击乐器无音域
|
||||||
9: ("note.harp", 6),
|
9: ("note.bell", 4),
|
||||||
10: ("note.didgeridoo", 8),
|
10: ("note.iron_xylophone", 6),
|
||||||
11: ("note.harp", 6),
|
11: ("note.iron_xylophone", 6),
|
||||||
12: ("note.xylophone", 4),
|
12: ("note.iron_xylophone", 6),
|
||||||
13: ("note.chime", 4),
|
13: ("note.xylophone", 4),
|
||||||
14: ("note.harp", 6),
|
14: ("note.chime", 4),
|
||||||
15: ("note.harp", 6),
|
15: ("note.harp", 6),
|
||||||
16: ("note.bass", 8),
|
16: ("note.bass", 8),
|
||||||
17: ("note.harp", 6),
|
17: ("note.harp", 6),
|
||||||
18: ("note.harp", 6),
|
18: ("note.flute", 5),
|
||||||
19: ("note.harp", 6),
|
19: ("note.harp", 6),
|
||||||
20: ("note.harp", 6),
|
20: ("note.harp", 6),
|
||||||
21: ("note.harp", 6),
|
21: ("note.flute", 5),
|
||||||
22: ("note.harp", 6),
|
22: ("note.flute", 5),
|
||||||
23: ("note.guitar", 7),
|
23: ("note.guitar", 7),
|
||||||
24: ("note.guitar", 7),
|
24: ("note.guitar", 7),
|
||||||
25: ("note.guitar", 7),
|
25: ("note.guitar", 7),
|
||||||
@ -81,21 +87,21 @@ PITCHED_INSTRUMENT_LIST = {
|
|||||||
37: ("note.bass", 8),
|
37: ("note.bass", 8),
|
||||||
38: ("note.bass", 8),
|
38: ("note.bass", 8),
|
||||||
39: ("note.bass", 8),
|
39: ("note.bass", 8),
|
||||||
40: ("note.harp", 6),
|
40: ("note.flute", 5),
|
||||||
41: ("note.harp", 6),
|
41: ("note.flute", 5),
|
||||||
42: ("note.harp", 6),
|
42: ("note.flute", 5),
|
||||||
43: ("note.harp", 6),
|
43: ("note.flute", 5),
|
||||||
44: ("note.iron_xylophone", 6),
|
44: ("note.iron_xylophone", 6),
|
||||||
45: ("note.guitar", 7),
|
45: ("note.guitar", 7),
|
||||||
46: ("note.harp", 6),
|
46: ("note.harp", 6),
|
||||||
47: ("note.harp", 6),
|
47: ("note.bd", 7),
|
||||||
48: ("note.guitar", 7),
|
48: ("note.guitar", 7),
|
||||||
49: ("note.guitar", 7),
|
49: ("note.guitar", 7),
|
||||||
50: ("note.bit", 6),
|
50: ("note.bit", 6),
|
||||||
51: ("note.bit", 6),
|
51: ("note.bit", 6),
|
||||||
52: ("note.harp", 6),
|
52: ("note.flute", 5),
|
||||||
53: ("note.harp", 6),
|
53: ("note.flute", 5),
|
||||||
54: ("note.bit", 6),
|
54: ("note.flute", 5),
|
||||||
55: ("note.flute", 5),
|
55: ("note.flute", 5),
|
||||||
56: ("note.flute", 5),
|
56: ("note.flute", 5),
|
||||||
57: ("note.flute", 5),
|
57: ("note.flute", 5),
|
||||||
@ -110,16 +116,16 @@ PITCHED_INSTRUMENT_LIST = {
|
|||||||
66: ("note.bit", 6),
|
66: ("note.bit", 6),
|
||||||
67: ("note.bit", 6),
|
67: ("note.bit", 6),
|
||||||
68: ("note.flute", 5),
|
68: ("note.flute", 5),
|
||||||
69: ("note.harp", 6),
|
69: ("note.bit", 6),
|
||||||
70: ("note.harp", 6),
|
70: ("note.banjo", 6),
|
||||||
71: ("note.flute", 5),
|
71: ("note.flute", 5),
|
||||||
72: ("note.flute", 5),
|
72: ("note.flute", 5),
|
||||||
73: ("note.flute", 5),
|
73: ("note.flute", 5),
|
||||||
74: ("note.harp", 6),
|
74: ("note.flute", 5),
|
||||||
75: ("note.flute", 5),
|
75: ("note.flute", 5),
|
||||||
76: ("note.harp", 6),
|
76: ("note.harp", 6),
|
||||||
77: ("note.harp", 6),
|
77: ("note.harp", 6),
|
||||||
78: ("note.harp", 6),
|
78: ("note.flute", 5),
|
||||||
79: ("note.harp", 6),
|
79: ("note.harp", 6),
|
||||||
80: ("note.bit", 6),
|
80: ("note.bit", 6),
|
||||||
81: ("note.bit", 6),
|
81: ("note.bit", 6),
|
||||||
@ -149,35 +155,35 @@ PITCHED_INSTRUMENT_LIST = {
|
|||||||
105: ("note.banjo", 6),
|
105: ("note.banjo", 6),
|
||||||
106: ("note.harp", 6),
|
106: ("note.harp", 6),
|
||||||
107: ("note.harp", 6),
|
107: ("note.harp", 6),
|
||||||
108: ("note.harp", 6),
|
108: ("note.bell", 4),
|
||||||
109: ("note.harp", 6),
|
109: ("note.flute", 5),
|
||||||
110: ("note.harp", 6),
|
110: ("note.flute", 5),
|
||||||
111: ("note.guitar", 7),
|
111: ("note.guitar", 7),
|
||||||
112: ("note.harp", 6),
|
112: ("note.bell", 4),
|
||||||
113: ("note.bell", 4),
|
113: ("note.bell", 4),
|
||||||
114: ("note.harp", 6),
|
114: ("note.flute", 5),
|
||||||
115: ("note.cow_bell", 5),
|
115: ("note.cow_bell", 5),
|
||||||
116: ("note.bd", 7), # 打击乐器无音域
|
116: ("note.bd", 7), # 打击乐器无音域
|
||||||
117: ("note.bass", 8),
|
117: ("note.bass", 8),
|
||||||
118: ("note.bit", 6),
|
118: ("note.bit", 6),
|
||||||
119: ("note.bd", 7), # 打击乐器无音域
|
119: ("firework.blast", 7), # 打击乐器无音域
|
||||||
120: ("note.guitar", 7),
|
120: ("note.guitar", 7),
|
||||||
121: ("note.harp", 6),
|
121: ("note.harp", 6),
|
||||||
122: ("note.harp", 6),
|
122: ("note.harp", 6),
|
||||||
123: ("note.harp", 6),
|
123: ("note.harp", 6),
|
||||||
124: ("note.harp", 6),
|
124: ("note.harp", 6),
|
||||||
125: ("note.hat", 7), # 打击乐器无音域
|
125: ("note.hat", 7), # 打击乐器无音域
|
||||||
126: ("note.bd", 7), # 打击乐器无音域
|
126: ("firework.twinkle", 7), # 打击乐器无音域
|
||||||
127: ("note.snare", 7), # 打击乐器无音域
|
127: ("note.snare", 7), # 打击乐器无音域
|
||||||
}
|
}
|
||||||
|
|
||||||
PERCUSSION_INSTRUMENT_LIST = {
|
PERCUSSION_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
|
||||||
34: ("note.bd", 7),
|
34: ("note.bd", 7),
|
||||||
35: ("note.bd", 7),
|
35: ("note.bd", 7),
|
||||||
36: ("note.hat", 7),
|
36: ("note.hat", 7),
|
||||||
37: ("note.snare", 7),
|
37: ("note.snare", 7),
|
||||||
38: ("note.snare", 7),
|
38: ("note.snare", 7),
|
||||||
39: ("note.snare", 7),
|
39: ("fire.ignite", 7),
|
||||||
40: ("note.hat", 7),
|
40: ("note.hat", 7),
|
||||||
41: ("note.snare", 7),
|
41: ("note.snare", 7),
|
||||||
42: ("note.hat", 7),
|
42: ("note.hat", 7),
|
||||||
@ -221,11 +227,22 @@ PERCUSSION_INSTRUMENT_LIST = {
|
|||||||
80: ("note.bell", 4),
|
80: ("note.bell", 4),
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTRUMENT_BLOCKS_LIST = {
|
PERCUSSION_INSTRUMENT_LIST: List[str] = [
|
||||||
|
"note.snare",
|
||||||
|
"note.bd",
|
||||||
|
"note.hat",
|
||||||
|
"note.basedrum",
|
||||||
|
"firework.blast",
|
||||||
|
"firework.twinkle",
|
||||||
|
"fire.ignite",
|
||||||
|
]
|
||||||
|
|
||||||
|
INSTRUMENT_BLOCKS_TABLE: Dict[str, Tuple[str]] = {
|
||||||
"note.bass": ("planks",),
|
"note.bass": ("planks",),
|
||||||
"note.snare": ("sand",),
|
"note.snare": ("sand",),
|
||||||
"note.hat": ("glass",),
|
"note.hat": ("glass",),
|
||||||
"note.bd": ("stone",),
|
"note.bd": ("stone",),
|
||||||
|
"note.basedrum": ("stone",),
|
||||||
"note.bell": ("gold_block",),
|
"note.bell": ("gold_block",),
|
||||||
"note.flute": ("clay",),
|
"note.flute": ("clay",),
|
||||||
"note.chime": ("packed_ice",),
|
"note.chime": ("packed_ice",),
|
||||||
@ -238,7 +255,11 @@ INSTRUMENT_BLOCKS_LIST = {
|
|||||||
"note.banjo": ("hay_block",),
|
"note.banjo": ("hay_block",),
|
||||||
"note.pling": ("glowstone",),
|
"note.pling": ("glowstone",),
|
||||||
"note.bassattack": ("command_block",), # 无法找到此音效
|
"note.bassattack": ("command_block",), # 无法找到此音效
|
||||||
"note.harp": ("glass",),
|
"note.harp": ("dirt",),
|
||||||
|
# 呃……
|
||||||
|
"firework.blast": ("sandstone",),
|
||||||
|
"firework.twinkle": ("red_sandstone",),
|
||||||
|
"fire.ignite": ("concrete_powder",),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,14 +16,144 @@ Terms & Conditions: License.md in the root directory
|
|||||||
# Email TriM-Organization@hotmail.com
|
# Email TriM-Organization@hotmail.com
|
||||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
import random
|
||||||
from typing import Dict, List, Tuple, Union
|
from typing import Dict, List, Tuple, Union
|
||||||
|
|
||||||
|
from .constants import INSTRUMENT_BLOCKS_TABLE
|
||||||
from .exceptions import *
|
from .exceptions import *
|
||||||
from .main import MidiConvert
|
from .main import MidiConvert
|
||||||
from .subclass import *
|
from .subclass import *
|
||||||
from .utils import *
|
from .utils import *
|
||||||
|
|
||||||
|
|
||||||
|
class FutureMidiConvertRSNB(MidiConvert):
|
||||||
|
"""
|
||||||
|
加入红石音乐适配
|
||||||
|
"""
|
||||||
|
|
||||||
|
music_command_list: Dict[int, SingleNoteBox]
|
||||||
|
"""音乐指令列表"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def soundID_to_block(sound_id: str, random_select: bool = False) -> str:
|
||||||
|
"""
|
||||||
|
将我的世界乐器名改作音符盒所需的对应方块名称
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
sound_id: str
|
||||||
|
将我的世界乐器名
|
||||||
|
random_select: bool
|
||||||
|
是否随机选取对应方块
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str方块名称
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if random_select:
|
||||||
|
return random.choice(INSTRUMENT_BLOCKS_TABLE[sound_id])
|
||||||
|
else:
|
||||||
|
return INSTRUMENT_BLOCKS_TABLE[sound_id][0]
|
||||||
|
except KeyError:
|
||||||
|
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):
|
||||||
"""
|
"""
|
||||||
加入插值算法优化音感
|
加入插值算法优化音感
|
||||||
@ -47,9 +177,7 @@ class FutureMidiConvertM4(MidiConvert):
|
|||||||
]
|
]
|
||||||
# print(totalCount)
|
# print(totalCount)
|
||||||
|
|
||||||
result: List[
|
result: List[Tuple[int, int, int, int, float],] = []
|
||||||
Tuple[int, int, int, int, float],
|
|
||||||
] = []
|
|
||||||
|
|
||||||
for _i in range(totalCount):
|
for _i in range(totalCount):
|
||||||
result.append(
|
result.append(
|
||||||
@ -105,7 +233,6 @@ class FutureMidiConvertM4(MidiConvert):
|
|||||||
|
|
||||||
# nowChannel = []
|
# nowChannel = []
|
||||||
for track_no, track in self.channels[i].items():
|
for track_no, track in self.channels[i].items():
|
||||||
|
|
||||||
noteMsgs = []
|
noteMsgs = []
|
||||||
MsgIndex = []
|
MsgIndex = []
|
||||||
|
|
||||||
@ -152,7 +279,6 @@ class FutureMidiConvertM4(MidiConvert):
|
|||||||
for every_note in self._linear_note(
|
for every_note in self._linear_note(
|
||||||
note, 50 if note.track_no == 0 else 500
|
note, 50 if note.track_no == 0 else 500
|
||||||
):
|
):
|
||||||
|
|
||||||
soundID, _X = (
|
soundID, _X = (
|
||||||
self.perc_inst_to_soundID_withX(note.pitch)
|
self.perc_inst_to_soundID_withX(note.pitch)
|
||||||
if SpecialBits
|
if SpecialBits
|
||||||
@ -163,7 +289,13 @@ class FutureMidiConvertM4(MidiConvert):
|
|||||||
|
|
||||||
max_score = max(max_score, score_now)
|
max_score = max(max_score, score_now)
|
||||||
mc_pitch = 2 ** ((note.pitch - 60 - _X) / 12)
|
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
|
blockmeter = (
|
||||||
|
1
|
||||||
|
/ (1 if note.track_no == 0 else 0.9)
|
||||||
|
/ max_volume
|
||||||
|
/ every_note[4]
|
||||||
|
- 1
|
||||||
|
)
|
||||||
|
|
||||||
track_now.append(
|
track_now.append(
|
||||||
SingleCommand(
|
SingleCommand(
|
||||||
@ -237,7 +369,6 @@ class FutureMidiConvertM4(MidiConvert):
|
|||||||
|
|
||||||
# nowChannel = []
|
# nowChannel = []
|
||||||
for track_no, track in self.channels[i].items():
|
for track_no, track in self.channels[i].items():
|
||||||
|
|
||||||
noteMsgs = []
|
noteMsgs = []
|
||||||
MsgIndex = []
|
MsgIndex = []
|
||||||
|
|
||||||
@ -282,7 +413,6 @@ class FutureMidiConvertM4(MidiConvert):
|
|||||||
for every_note in self._linear_note(
|
for every_note in self._linear_note(
|
||||||
note, 50 if note.track_no == 0 else 500
|
note, 50 if note.track_no == 0 else 500
|
||||||
):
|
):
|
||||||
|
|
||||||
soundID, _X = (
|
soundID, _X = (
|
||||||
self.perc_inst_to_soundID_withX(note.pitch)
|
self.perc_inst_to_soundID_withX(note.pitch)
|
||||||
if SpecialBits
|
if SpecialBits
|
||||||
|
@ -31,69 +31,6 @@ Terms & Conditions: ../License.md
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _toCmdList_m1(
|
|
||||||
self,
|
|
||||||
scoreboardname: str = "mscplay",
|
|
||||||
volume: float = 1.0,
|
|
||||||
speed: float = 1.0) -> list:
|
|
||||||
"""
|
|
||||||
使用Dislink Sforza的转换思路,将midi转换为我的世界命令列表
|
|
||||||
:param scoreboardname: 我的世界的计分板名称
|
|
||||||
:param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
|
||||||
:param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
|
||||||
:return: tuple(命令列表, 命令个数, 计分板最大值)
|
|
||||||
"""
|
|
||||||
tracks = []
|
|
||||||
if volume > 1:
|
|
||||||
volume = 1
|
|
||||||
if volume <= 0:
|
|
||||||
volume = 0.001
|
|
||||||
|
|
||||||
commands = 0
|
|
||||||
maxscore = 0
|
|
||||||
|
|
||||||
for i, track in enumerate(self.midi.tracks):
|
|
||||||
|
|
||||||
ticks = 0
|
|
||||||
instrumentID = 0
|
|
||||||
singleTrack = []
|
|
||||||
|
|
||||||
for msg in track:
|
|
||||||
ticks += msg.time
|
|
||||||
# print(msg)
|
|
||||||
if msg.is_meta:
|
|
||||||
if msg.type == "set_tempo":
|
|
||||||
tempo = msg.tempo
|
|
||||||
else:
|
|
||||||
if msg.type == "program_change":
|
|
||||||
# print("TT")
|
|
||||||
instrumentID = msg.program
|
|
||||||
if msg.type == "note_on" and msg.velocity != 0:
|
|
||||||
nowscore = round(
|
|
||||||
(ticks * tempo)
|
|
||||||
/ ((self.midi.ticks_per_beat * float(speed)) * 50000)
|
|
||||||
)
|
|
||||||
maxscore = max(maxscore, nowscore)
|
|
||||||
soundID, _X = self.__Inst2soundID_withX(instrumentID)
|
|
||||||
singleTrack.append(
|
|
||||||
"execute @a[scores={" +
|
|
||||||
str(scoreboardname) +
|
|
||||||
"=" +
|
|
||||||
str(nowscore) +
|
|
||||||
"}" +
|
|
||||||
f"] ~ ~ ~ playsound {soundID} @s ~ ~{1 / volume - 1} ~ {msg.velocity * (0.7 if msg.channel == 0 else 0.9)} {2 ** ((msg.note - 60 - _X) / 12)}")
|
|
||||||
commands += 1
|
|
||||||
if len(singleTrack) != 0:
|
|
||||||
tracks.append(singleTrack)
|
|
||||||
|
|
||||||
return [tracks, commands, maxscore]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ============================
|
# ============================
|
||||||
|
|
||||||
|
|
||||||
@ -209,16 +146,13 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
|
|
||||||
# ============================
|
# ============================
|
||||||
|
from typing import Literal
|
||||||
|
from ..constants import x,y,z
|
||||||
from typing import Union
|
|
||||||
from .plugin import x,y,z,bottem_side_length_of_smallest_square_bottom_box,form_note_block_in_NBT_struct,form_repeater_in_NBT_struct
|
|
||||||
|
|
||||||
# 不要用 没写完
|
# 不要用 没写完
|
||||||
def delay_to_note_blocks(
|
def delay_to_note_blocks(
|
||||||
baseblock: str = "stone",
|
baseblock: str = "stone",
|
||||||
position_forward: Union(x, y, z) = z,
|
position_forward: Literal['x','y','z'] = z,
|
||||||
max_height: int = 64,
|
|
||||||
):
|
):
|
||||||
"""传入音符,生成以音符盒存储的红石音乐
|
"""传入音符,生成以音符盒存储的红石音乐
|
||||||
:param:
|
:param:
|
||||||
@ -229,10 +163,6 @@ def delay_to_note_blocks(
|
|||||||
|
|
||||||
from TrimMCStruct import Structure, Block
|
from TrimMCStruct import Structure, Block
|
||||||
|
|
||||||
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
|
|
||||||
len(commands), max_height
|
|
||||||
)
|
|
||||||
|
|
||||||
struct = Structure(
|
struct = Structure(
|
||||||
(_sideLength, max_height, _sideLength), # 声明结构大小
|
(_sideLength, max_height, _sideLength), # 声明结构大小
|
||||||
)
|
)
|
||||||
@ -292,5 +222,4 @@ def delay_to_note_blocks(
|
|||||||
struct.set_block(Block("universal_minecraft", baseblock), startpos)
|
struct.set_block(Block("universal_minecraft", baseblock), startpos)
|
||||||
startpos[0] += posadder[0]
|
startpos[0] += posadder[0]
|
||||||
startpos[1] += posadder[1]
|
startpos[1] += posadder[1]
|
||||||
startpos[2] += posadder[2]
|
startpos[2] += posadder[2]
|
||||||
|
|
@ -25,7 +25,6 @@ Terms & Conditions: License.md in the root directory
|
|||||||
# 赶快呼叫 程序员!Let's Go! 直ぐに呼びましょプログラマ レッツゴー!
|
# 赶快呼叫 程序员!Let's Go! 直ぐに呼びましょプログラマ レッツゴー!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
from typing import List, Literal, Tuple, Union
|
from typing import List, Literal, Tuple, Union
|
||||||
@ -183,12 +182,19 @@ class MidiConvert:
|
|||||||
"""文件名,不含路径且不含后缀"""
|
"""文件名,不含路径且不含后缀"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return cls(mido.MidiFile(midi_file_path,clip=True), midi_music_name, old_exe_format)
|
return cls(
|
||||||
|
mido.MidiFile(midi_file_path, clip=True),
|
||||||
|
midi_music_name,
|
||||||
|
old_exe_format,
|
||||||
|
)
|
||||||
except (ValueError, TypeError) as E:
|
except (ValueError, TypeError) as E:
|
||||||
raise MidiDestroyedError(f"文件{midi_file_path}损坏:{E}")
|
raise MidiDestroyedError(f"文件{midi_file_path}损坏:{E}")
|
||||||
except FileNotFoundError as E:
|
except FileNotFoundError as E:
|
||||||
raise FileNotFoundError(f"文件{midi_file_path}不存在:{E}")
|
raise FileNotFoundError(f"文件{midi_file_path}不存在:{E}")
|
||||||
|
|
||||||
|
# ……真的那么重要吗
|
||||||
|
# 我又几曾何时,知道祂真的会抛下我
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def inst_to_souldID_withX(
|
def inst_to_souldID_withX(
|
||||||
instrumentID: int,
|
instrumentID: int,
|
||||||
@ -214,7 +220,7 @@ class MidiConvert:
|
|||||||
tuple(str我的世界乐器名, int转换算法中的X)
|
tuple(str我的世界乐器名, int转换算法中的X)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return PITCHED_INSTRUMENT_LIST[instrumentID]
|
return PITCHED_INSTRUMENT_TABLE[instrumentID]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return "note.flute", 5
|
return "note.flute", 5
|
||||||
|
|
||||||
@ -233,17 +239,12 @@ class MidiConvert:
|
|||||||
tuple(str我的世界乐器名, int转换算法中的X)
|
tuple(str我的世界乐器名, int转换算法中的X)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return PERCUSSION_INSTRUMENT_LIST[instrumentID]
|
return PERCUSSION_INSTRUMENT_TABLE[instrumentID]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print("WARN", f"无法使用打击乐器列表库,或者使用了不存在的乐器,打击乐器使用Dislink算法代替。{instrumentID}")
|
return "note.bd", 7
|
||||||
if instrumentID == 55:
|
|
||||||
return "note.cow_bell", 5
|
# 明明已经走了
|
||||||
elif instrumentID in [41, 43, 45]:
|
# 凭什么还要在我心里留下缠绵缱绻
|
||||||
return "note.hat", 7
|
|
||||||
elif instrumentID in [36, 37, 39]:
|
|
||||||
return "note.snare", 7
|
|
||||||
else:
|
|
||||||
return "note.bd", 7
|
|
||||||
|
|
||||||
def form_progress_bar(
|
def form_progress_bar(
|
||||||
self,
|
self,
|
||||||
@ -356,6 +357,10 @@ class MidiConvert:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 那是假的
|
||||||
|
# 一切都并未留下痕迹啊
|
||||||
|
# 那梦又是多么的真实……
|
||||||
|
|
||||||
if r"%%t" in pgs_style:
|
if r"%%t" in pgs_style:
|
||||||
result.append(
|
result.append(
|
||||||
SingleCommand(
|
SingleCommand(
|
||||||
@ -552,13 +557,12 @@ class MidiConvert:
|
|||||||
if msg.type == "set_tempo":
|
if msg.type == "set_tempo":
|
||||||
tempo = msg.tempo
|
tempo = msg.tempo
|
||||||
else:
|
else:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not track_no in midi_channels[msg.channel].keys():
|
if not track_no in midi_channels[msg.channel].keys():
|
||||||
midi_channels[msg.channel][track_no] = []
|
midi_channels[msg.channel][track_no] = []
|
||||||
except AttributeError as E:
|
except AttributeError as E:
|
||||||
print(msg,E)
|
print(msg, E)
|
||||||
|
|
||||||
if msg.type == "program_change":
|
if msg.type == "program_change":
|
||||||
midi_channels[msg.channel][track_no].append(
|
midi_channels[msg.channel][track_no].append(
|
||||||
("PgmC", msg.program, microseconds)
|
("PgmC", msg.program, microseconds)
|
||||||
@ -576,7 +580,6 @@ class MidiConvert:
|
|||||||
("NoteE", msg.note, microseconds)
|
("NoteE", msg.note, microseconds)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
"""整合后的音乐通道格式
|
"""整合后的音乐通道格式
|
||||||
每个通道包括若干消息元素其中逃不过这三种:
|
每个通道包括若干消息元素其中逃不过这三种:
|
||||||
|
|
||||||
@ -689,7 +692,7 @@ class MidiConvert:
|
|||||||
max_volume: float = 1.0,
|
max_volume: float = 1.0,
|
||||||
speed: float = 1.0,
|
speed: float = 1.0,
|
||||||
player_selector: str = "@a",
|
player_selector: str = "@a",
|
||||||
) -> Tuple[List[SingleCommand], int]:
|
) -> Tuple[List[SingleCommand], int, int]:
|
||||||
"""
|
"""
|
||||||
使用金羿的转换思路,将midi转换为我的世界命令列表,并输出每个音符之后的延迟
|
使用金羿的转换思路,将midi转换为我的世界命令列表,并输出每个音符之后的延迟
|
||||||
|
|
||||||
@ -704,7 +707,7 @@ class MidiConvert:
|
|||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
tuple( list[SingleCommand,...], int音乐时长游戏刻 )
|
tuple( list[SingleCommand,...], int音乐时长游戏刻, int最大同时播放的指令数量 )
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if speed == 0:
|
if speed == 0:
|
||||||
@ -740,10 +743,10 @@ class MidiConvert:
|
|||||||
else self.inst_to_souldID_withX(InstID)
|
else self.inst_to_souldID_withX(InstID)
|
||||||
)
|
)
|
||||||
|
|
||||||
score_now = round(msg[-1] / float(speed) / 50)
|
delaytime_now = round(msg[-1] / float(speed) / 50)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tracks[score_now].append(
|
tracks[delaytime_now].append(
|
||||||
self.execute_cmd_head.format(player_selector)
|
self.execute_cmd_head.format(player_selector)
|
||||||
+ f"playsound {soundID} @s ^ ^ ^{128 / max_volume / msg[2] - 1} {msg[2] / 128} "
|
+ f"playsound {soundID} @s ^ ^ ^{128 / max_volume / msg[2] - 1} {msg[2] / 128} "
|
||||||
+ (
|
+ (
|
||||||
@ -753,7 +756,7 @@ class MidiConvert:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
tracks[score_now] = [
|
tracks[delaytime_now] = [
|
||||||
self.execute_cmd_head.format(player_selector)
|
self.execute_cmd_head.format(player_selector)
|
||||||
+ f"playsound {soundID} @s ^ ^ ^{128 / max_volume / msg[2] - 1} {msg[2] / 128} "
|
+ f"playsound {soundID} @s ^ ^ ^{128 / max_volume / msg[2] - 1} {msg[2] / 128} "
|
||||||
+ (
|
+ (
|
||||||
@ -771,8 +774,10 @@ class MidiConvert:
|
|||||||
all_ticks = list(tracks.keys())
|
all_ticks = list(tracks.keys())
|
||||||
all_ticks.sort()
|
all_ticks.sort()
|
||||||
results = []
|
results = []
|
||||||
|
max_multi = 0
|
||||||
|
|
||||||
for i in range(len(all_ticks)):
|
for i in range(len(all_ticks)):
|
||||||
|
max_multi = max(max_multi, len(tracks[all_ticks[i]]))
|
||||||
for j in range(len(tracks[all_ticks[i]])):
|
for j in range(len(tracks[all_ticks[i]])):
|
||||||
results.append(
|
results.append(
|
||||||
SingleCommand(
|
SingleCommand(
|
||||||
@ -794,7 +799,7 @@ class MidiConvert:
|
|||||||
|
|
||||||
self.music_command_list = results
|
self.music_command_list = results
|
||||||
self.music_tick_num = max(all_ticks)
|
self.music_tick_num = max(all_ticks)
|
||||||
return results, self.music_tick_num
|
return results, self.music_tick_num, max_multi
|
||||||
|
|
||||||
def copy_important(self):
|
def copy_important(self):
|
||||||
dst = MidiConvert(
|
dst = MidiConvert(
|
||||||
|
@ -148,7 +148,6 @@ def commands_to_BDX_bytes(
|
|||||||
now_x = 0
|
now_x = 0
|
||||||
|
|
||||||
for command in commands_list:
|
for command in commands_list:
|
||||||
|
|
||||||
_bytes += form_command_block_in_BDX_bytes(
|
_bytes += form_command_block_in_BDX_bytes(
|
||||||
command.command_text,
|
command.command_text,
|
||||||
(1 if y_forward else 0)
|
(1 if y_forward else 0)
|
||||||
@ -156,12 +155,14 @@ def commands_to_BDX_bytes(
|
|||||||
((now_y != 0) and (not y_forward))
|
((now_y != 0) and (not y_forward))
|
||||||
or (y_forward and (now_y != (max_height - 1)))
|
or (y_forward and (now_y != (max_height - 1)))
|
||||||
)
|
)
|
||||||
else (3 if z_forward else 2)
|
else (
|
||||||
if (
|
(3 if z_forward else 2)
|
||||||
((now_z != 0) and (not z_forward))
|
if (
|
||||||
or (z_forward and (now_z != _sideLength - 1))
|
((now_z != 0) and (not z_forward))
|
||||||
)
|
or (z_forward and (now_z != _sideLength - 1))
|
||||||
else 5,
|
)
|
||||||
|
else 5
|
||||||
|
),
|
||||||
impluse=2,
|
impluse=2,
|
||||||
condition=command.conditional,
|
condition=command.conditional,
|
||||||
needRedstone=False,
|
needRedstone=False,
|
||||||
@ -171,6 +172,16 @@ def commands_to_BDX_bytes(
|
|||||||
trackOutput=True,
|
trackOutput=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# (1 if y_forward else 0) if ( # 如果y+则向上,反之向下
|
||||||
|
# ((now_y != 0) and (not y_forward)) # 如果不是y轴上首个方块
|
||||||
|
# or (y_forward and (now_y != (max_height - 1))) # 如果不是y轴上末端方块
|
||||||
|
# ) else ( # 否则,即是y轴末端或首个方块
|
||||||
|
# (3 if z_forward else 2) if ( # 如果z+则向z轴正方向,反之负方向
|
||||||
|
# ((now_z != 0) and (not z_forward)) # 如果不是z轴上的首个方块
|
||||||
|
# or (z_forward and (now_z != _sideLength - 1)) # 如果不是z轴上的末端方块
|
||||||
|
# ) else 5 # 否则,则要面向x轴正方向
|
||||||
|
# )
|
||||||
|
|
||||||
now_y += 1 if y_forward else -1
|
now_y += 1 if y_forward else -1
|
||||||
|
|
||||||
if ((now_y >= max_height) and y_forward) or ((now_y < 0) and (not y_forward)):
|
if ((now_y >= max_height) and y_forward) or ((now_y < 0) and (not y_forward)):
|
||||||
|
@ -154,7 +154,7 @@ def to_BDX_file_in_delay(
|
|||||||
data_cfg.volume_ratio,
|
data_cfg.volume_ratio,
|
||||||
data_cfg.speed_multiplier,
|
data_cfg.speed_multiplier,
|
||||||
player,
|
player,
|
||||||
)
|
)[:2]
|
||||||
|
|
||||||
if not os.path.exists(data_cfg.dist_path):
|
if not os.path.exists(data_cfg.dist_path):
|
||||||
os.makedirs(data_cfg.dist_path)
|
os.makedirs(data_cfg.dist_path)
|
||||||
|
@ -25,3 +25,5 @@ def bottem_side_length_of_smallest_square_bottom_box(total: int, maxHeight: int)
|
|||||||
:param maxHeight: 最大高度
|
:param maxHeight: 最大高度
|
||||||
:return: 外切正方形的边长 int"""
|
:return: 外切正方形的边长 int"""
|
||||||
return math.ceil(math.sqrt(math.ceil(total / maxHeight)))
|
return math.ceil(math.sqrt(math.ceil(total / maxHeight)))
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,7 +15,8 @@ Terms & Conditions: License.md in the root directory
|
|||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"to_mcstructure_file_in_delay"
|
"to_mcstructure_file_in_delay",
|
||||||
|
"to_mcstructure_file_in_redstone_CD",
|
||||||
]
|
]
|
||||||
__author__ = (("金羿", "Eilles Wan"),)
|
__author__ = (("金羿", "Eilles Wan"),)
|
||||||
|
|
||||||
|
@ -13,10 +13,12 @@ Terms & Conditions: License.md in the root directory
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
from ...exceptions import CommandFormatError
|
from ...exceptions import CommandFormatError
|
||||||
from ...main import MidiConvert
|
from ...main import MidiConvert
|
||||||
from ..main import ConvertConfig
|
from ..main import ConvertConfig
|
||||||
from ..mcstructure import commands_to_structure
|
from ..mcstructure import commands_to_structure,commands_to_redstone_delay_structure
|
||||||
|
|
||||||
|
|
||||||
def to_mcstructure_file_in_delay(
|
def to_mcstructure_file_in_delay(
|
||||||
@ -51,7 +53,7 @@ def to_mcstructure_file_in_delay(
|
|||||||
data_cfg.volume_ratio,
|
data_cfg.volume_ratio,
|
||||||
data_cfg.speed_multiplier,
|
data_cfg.speed_multiplier,
|
||||||
player,
|
player,
|
||||||
)
|
)[:2]
|
||||||
|
|
||||||
if not os.path.exists(data_cfg.dist_path):
|
if not os.path.exists(data_cfg.dist_path):
|
||||||
os.makedirs(data_cfg.dist_path)
|
os.makedirs(data_cfg.dist_path)
|
||||||
@ -69,8 +71,56 @@ def to_mcstructure_file_in_delay(
|
|||||||
return size, max_delay
|
return size, max_delay
|
||||||
|
|
||||||
|
|
||||||
def to_mcstructure_file_in_redstone(
|
|
||||||
|
def to_mcstructure_file_in_redstone_CD(
|
||||||
midi_cvt: MidiConvert,
|
midi_cvt: MidiConvert,
|
||||||
data_cfg: ConvertConfig,
|
data_cfg: ConvertConfig,
|
||||||
|
player: str = "@a",
|
||||||
|
axis_side: Literal["z+","z-","Z+","Z-","x+","x-","X+","X-"] = "z+",
|
||||||
|
basement_block: str = "concrete",
|
||||||
):
|
):
|
||||||
pass
|
"""
|
||||||
|
将midi以延迟播放器形式转换为mcstructure结构文件
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
midi_cvt: MidiConvert 对象
|
||||||
|
用于转换的MidiConvert对象
|
||||||
|
data_cfg: ConvertConfig 对象
|
||||||
|
部分转换通用参数
|
||||||
|
player: str
|
||||||
|
玩家选择器,默认为`@a`
|
||||||
|
axis_side: Literal["z+","z-","Z+","Z-","x+","x-","X+","X-"]
|
||||||
|
生成结构的延展方向
|
||||||
|
basement_block: str
|
||||||
|
结构的基底方块
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
tuple[tuple[int,]结构大小, int音乐总延迟]
|
||||||
|
"""
|
||||||
|
|
||||||
|
if midi_cvt.enable_old_exe_format:
|
||||||
|
raise CommandFormatError("使用mcstructure结构文件导出时不支持旧版本的指令格式。")
|
||||||
|
|
||||||
|
cmd_list, max_delay, max_multiple_cmd = midi_cvt.to_command_list_in_delay(
|
||||||
|
data_cfg.volume_ratio,
|
||||||
|
data_cfg.speed_multiplier,
|
||||||
|
player,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not os.path.exists(data_cfg.dist_path):
|
||||||
|
os.makedirs(data_cfg.dist_path)
|
||||||
|
|
||||||
|
struct, size, end_pos = commands_to_redstone_delay_structure(cmd_list,max_delay,max_multiple_cmd, basement_block, axis_side)
|
||||||
|
|
||||||
|
with open(
|
||||||
|
os.path.abspath(
|
||||||
|
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.mcstructure")
|
||||||
|
),
|
||||||
|
"wb+",
|
||||||
|
) as f:
|
||||||
|
struct.dump(f)
|
||||||
|
|
||||||
|
return size, max_delay
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ def to_mcstructure_addon_in_delay(
|
|||||||
data_cfg.volume_ratio,
|
data_cfg.volume_ratio,
|
||||||
data_cfg.speed_multiplier,
|
data_cfg.speed_multiplier,
|
||||||
player,
|
player,
|
||||||
)
|
)[:2]
|
||||||
|
|
||||||
if not os.path.exists(data_cfg.dist_path):
|
if not os.path.exists(data_cfg.dist_path):
|
||||||
os.makedirs(data_cfg.dist_path)
|
os.makedirs(data_cfg.dist_path)
|
||||||
|
@ -16,12 +16,40 @@ Terms & Conditions: License.md in the root directory
|
|||||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
|
||||||
from typing import List, Tuple
|
from typing import List, Literal, Tuple
|
||||||
|
|
||||||
from TrimMCStruct import Block, Structure, TAG_Byte, TAG_Long
|
from TrimMCStruct import Block, Structure, TAG_Byte, TAG_Long
|
||||||
|
|
||||||
|
from ..constants import x, y, z
|
||||||
from ..subclass import SingleCommand
|
from ..subclass import SingleCommand
|
||||||
from .common import bottem_side_length_of_smallest_square_bottom_box
|
from .common import bottem_side_length_of_smallest_square_bottom_box
|
||||||
|
import struct
|
||||||
|
|
||||||
|
|
||||||
|
def antiaxis(axis: Literal['x','z','X','Z']):
|
||||||
|
return z if axis == x else x
|
||||||
|
|
||||||
|
|
||||||
|
def forward_IER(forward: bool):
|
||||||
|
return 1 if forward else -1
|
||||||
|
|
||||||
|
AXIS_PARTICULAR_VALUE = {
|
||||||
|
x: {
|
||||||
|
True: 5,
|
||||||
|
False: 4,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
True: 1,
|
||||||
|
False: 0,
|
||||||
|
},
|
||||||
|
z: {
|
||||||
|
True: 3,
|
||||||
|
False: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def command_statevalue(axis_: Literal['x','y','z','X','Y','Z'],forward_:bool):
|
||||||
|
return AXIS_PARTICULAR_VALUE[axis_.lower()][forward_]
|
||||||
|
|
||||||
|
|
||||||
def form_note_block_in_NBT_struct(
|
def form_note_block_in_NBT_struct(
|
||||||
@ -64,8 +92,12 @@ def form_note_block_in_NBT_struct(
|
|||||||
|
|
||||||
def form_repeater_in_NBT_struct(delay: int, facing: int):
|
def form_repeater_in_NBT_struct(delay: int, facing: int):
|
||||||
"""生成中继器方块
|
"""生成中继器方块
|
||||||
:param facing:
|
:param facing: 朝向:
|
||||||
:param delay: 1~4
|
Z- 北 0
|
||||||
|
X- 东 1
|
||||||
|
Z+ 南 2
|
||||||
|
X+ 西 3
|
||||||
|
:param delay: 0~3
|
||||||
:return Block()"""
|
:return Block()"""
|
||||||
|
|
||||||
return Block(
|
return Block(
|
||||||
@ -165,7 +197,7 @@ def form_command_block_in_NBT_struct(
|
|||||||
"x": coordinate[0],
|
"x": coordinate[0],
|
||||||
"y": coordinate[1],
|
"y": coordinate[1],
|
||||||
"z": coordinate[2],
|
"z": coordinate[2],
|
||||||
} # type: ignore
|
} # type: ignore
|
||||||
},
|
},
|
||||||
compability_version=17959425,
|
compability_version=17959425,
|
||||||
)
|
)
|
||||||
@ -251,3 +283,109 @@ def commands_to_structure(
|
|||||||
),
|
),
|
||||||
(now_x, now_y, now_z),
|
(now_x, now_y, now_z),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def commands_to_redstone_delay_structure(
|
||||||
|
commands: List[SingleCommand],
|
||||||
|
delay_length: int,
|
||||||
|
max_multicmd_length: int,
|
||||||
|
base_block: str = "concrete",
|
||||||
|
axis_: Literal["z+","z-","Z+","Z-","x+","x-","X+","X-"] = "z+",
|
||||||
|
) -> Tuple[Structure, Tuple[int, int, int], Tuple[int, int, int]]:
|
||||||
|
"""
|
||||||
|
:param commands: 指令列表
|
||||||
|
:param delay_length: 延时总长
|
||||||
|
:param max_multicmd_length: 最大同时播放的音符数量
|
||||||
|
:param base_block: 生成结构的基底方块
|
||||||
|
:param axis_: 生成结构的延展方向
|
||||||
|
:return 结构类,结构占用大小,终点坐标
|
||||||
|
"""
|
||||||
|
if axis_ in ["z+", "Z+"]:
|
||||||
|
extensioon_direction = z
|
||||||
|
aside_direction = x
|
||||||
|
repeater_facing = 2
|
||||||
|
forward = True
|
||||||
|
elif axis_ in ["z-", "Z-"]:
|
||||||
|
extensioon_direction = z
|
||||||
|
aside_direction = x
|
||||||
|
repeater_facing = 0
|
||||||
|
forward = False
|
||||||
|
elif axis_ in ["x+", "X+"]:
|
||||||
|
extensioon_direction = x
|
||||||
|
aside_direction = z
|
||||||
|
repeater_facing = 3
|
||||||
|
forward = True
|
||||||
|
elif axis_ in ["x-", "X-"]:
|
||||||
|
extensioon_direction = x
|
||||||
|
aside_direction = z
|
||||||
|
repeater_facing = 1
|
||||||
|
forward = False
|
||||||
|
else:
|
||||||
|
raise ValueError(f"axis_({axis_}) 参数错误。")
|
||||||
|
|
||||||
|
|
||||||
|
goahead = forward_IER(forward)
|
||||||
|
|
||||||
|
struct = Structure(
|
||||||
|
(round(delay_length/2+0.5+len(commands)) if extensioon_direction == x else max_multicmd_length, 3, round(delay_length/2+0.5+len(commands)) if extensioon_direction == z else max_multicmd_length)
|
||||||
|
)
|
||||||
|
|
||||||
|
pos_now = {x:(0 if forward else struct.size[0]),y:0,z:(0 if forward else struct.size[2])}
|
||||||
|
|
||||||
|
first_impluse = True
|
||||||
|
|
||||||
|
for cmd in commands:
|
||||||
|
single_repeater_value = round(cmd.delay / 2) % 4 - 1
|
||||||
|
additional_repeater = round(cmd.delay / 2)// 4
|
||||||
|
for i in range(additional_repeater):
|
||||||
|
struct.set_block(
|
||||||
|
tuple(pos_now.values()),
|
||||||
|
Block("minecraft", base_block,),
|
||||||
|
)
|
||||||
|
struct.set_block(
|
||||||
|
(pos_now[x],1,pos_now[z]),
|
||||||
|
form_repeater_in_NBT_struct(
|
||||||
|
delay=3,
|
||||||
|
facing=repeater_facing,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
pos_now[extensioon_direction] += goahead
|
||||||
|
first_impluse = True
|
||||||
|
if single_repeater_value >= 0:
|
||||||
|
struct.set_block(
|
||||||
|
tuple(pos_now.values()),
|
||||||
|
Block("minecraft", base_block,),
|
||||||
|
)
|
||||||
|
struct.set_block(
|
||||||
|
(pos_now[x],1,pos_now[z]),
|
||||||
|
form_repeater_in_NBT_struct(
|
||||||
|
delay=single_repeater_value,
|
||||||
|
facing=repeater_facing,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
pos_now[extensioon_direction] += goahead
|
||||||
|
first_impluse = True
|
||||||
|
struct.set_block(
|
||||||
|
(pos_now[x],1,pos_now[z]),
|
||||||
|
form_command_block_in_NBT_struct(
|
||||||
|
command=cmd.command_text,
|
||||||
|
coordinate=(pos_now[x],1,pos_now[z]),
|
||||||
|
particularValue=command_statevalue(extensioon_direction, forward),
|
||||||
|
# impluse= (0 if first_impluse else 2),
|
||||||
|
impluse=0,
|
||||||
|
condition=False,
|
||||||
|
alwaysRun=False,
|
||||||
|
tickDelay=0,
|
||||||
|
customName=cmd.annotation_text,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
struct.set_block(
|
||||||
|
(pos_now[x],2,pos_now[z]),
|
||||||
|
Block("minecraft", "redstone_wire"),
|
||||||
|
)
|
||||||
|
pos_now[extensioon_direction] += goahead
|
||||||
|
|
||||||
|
first_impluse = False
|
||||||
|
|
||||||
|
return struct, struct.size, tuple(pos_now.values())
|
||||||
|
|
||||||
|
|
||||||
|
19
Musicreater/plugin/schematic.py
Normal file
19
Musicreater/plugin/schematic.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
存放有关Schematic结构生成的内容
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
版权所有 © 2023 音·创 开发者
|
||||||
|
Copyright © 2023 all the developers of Musicreater
|
||||||
|
|
||||||
|
开源相关声明请见 仓库根目录下的 License.md
|
||||||
|
Terms & Conditions: License.md in the root directory
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 睿穆组织 开发交流群 861684859
|
||||||
|
# Email TriM-Organization@hotmail.com
|
||||||
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
import nbtschematic
|
||||||
|
|
22
Musicreater/plugin/schematic/__init__.py
Normal file
22
Musicreater/plugin/schematic/__init__.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
用以生成Schematic结构的附加功能
|
||||||
|
|
||||||
|
版权所有 © 2023 音·创 开发者
|
||||||
|
Copyright © 2023 all the developers of Musicreater
|
||||||
|
|
||||||
|
开源相关声明请见 仓库根目录下的 License.md
|
||||||
|
Terms & Conditions: License.md in the root directory
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 睿穆组织 开发交流群 861684859
|
||||||
|
# Email TriM-Organization@hotmail.com
|
||||||
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
]
|
||||||
|
__author__ = (("金羿", "Eilles Wan"),)
|
||||||
|
|
||||||
|
from .main import *
|
||||||
|
|
14
Musicreater/plugin/schematic/main.py
Normal file
14
Musicreater/plugin/schematic/main.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
版权所有 © 2023 音·创 开发者
|
||||||
|
Copyright © 2023 all the developers of Musicreater
|
||||||
|
|
||||||
|
开源相关声明请见 仓库根目录下的 License.md
|
||||||
|
Terms & Conditions: License.md in the root directory
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 睿穆组织 开发交流群 861684859
|
||||||
|
# Email TriM-Organization@hotmail.com
|
||||||
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
from ..schematic import *
|
@ -98,6 +98,63 @@ class ObsoleteMidiConvert(MidiConvert):
|
|||||||
|
|
||||||
return [tracks, commands, maxscore]
|
return [tracks, commands, maxscore]
|
||||||
|
|
||||||
|
def _toCmdList_m1(
|
||||||
|
self,
|
||||||
|
scoreboardname: str = "mscplay",
|
||||||
|
volume: float = 1.0,
|
||||||
|
speed: float = 1.0) -> list:
|
||||||
|
"""
|
||||||
|
使用Dislink Sforza的转换思路,将midi转换为我的世界命令列表
|
||||||
|
:param scoreboardname: 我的世界的计分板名称
|
||||||
|
:param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||||
|
:param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||||
|
:return: tuple(命令列表, 命令个数, 计分板最大值)
|
||||||
|
"""
|
||||||
|
tracks = []
|
||||||
|
if volume > 1:
|
||||||
|
volume = 1
|
||||||
|
if volume <= 0:
|
||||||
|
volume = 0.001
|
||||||
|
|
||||||
|
commands = 0
|
||||||
|
maxscore = 0
|
||||||
|
|
||||||
|
for i, track in enumerate(self.midi.tracks):
|
||||||
|
|
||||||
|
ticks = 0
|
||||||
|
instrumentID = 0
|
||||||
|
singleTrack = []
|
||||||
|
|
||||||
|
for msg in track:
|
||||||
|
ticks += msg.time
|
||||||
|
# print(msg)
|
||||||
|
if msg.is_meta:
|
||||||
|
if msg.type == "set_tempo":
|
||||||
|
tempo = msg.tempo
|
||||||
|
else:
|
||||||
|
if msg.type == "program_change":
|
||||||
|
# print("TT")
|
||||||
|
instrumentID = msg.program
|
||||||
|
if msg.type == "note_on" and msg.velocity != 0:
|
||||||
|
nowscore = round(
|
||||||
|
(ticks * tempo)
|
||||||
|
/ ((self.midi.ticks_per_beat * float(speed)) * 50000)
|
||||||
|
)
|
||||||
|
maxscore = max(maxscore, nowscore)
|
||||||
|
soundID, _X = self.__Inst2soundID_withX(instrumentID)
|
||||||
|
singleTrack.append(
|
||||||
|
"execute @a[scores={" +
|
||||||
|
str(scoreboardname) +
|
||||||
|
"=" +
|
||||||
|
str(nowscore) +
|
||||||
|
"}" +
|
||||||
|
f"] ~ ~ ~ playsound {soundID} @s ~ ~{1 / volume - 1} ~ {msg.velocity * (0.7 if msg.channel == 0 else 0.9)} {2 ** ((msg.note - 60 - _X) / 12)}")
|
||||||
|
commands += 1
|
||||||
|
if len(singleTrack) != 0:
|
||||||
|
tracks.append(singleTrack)
|
||||||
|
|
||||||
|
return [tracks, commands, maxscore]
|
||||||
|
|
||||||
# 原本这个算法的转换效果应该和上面的算法相似的
|
# 原本这个算法的转换效果应该和上面的算法相似的
|
||||||
def _toCmdList_m2(
|
def _toCmdList_m2(
|
||||||
self: MidiConvert,
|
self: MidiConvert,
|
||||||
|
@ -18,6 +18,9 @@ Terms & Conditions: License.md in the root directory
|
|||||||
|
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from .constants import PERCUSSION_INSTRUMENT_LIST
|
||||||
|
|
||||||
|
|
||||||
@dataclass(init=False)
|
@dataclass(init=False)
|
||||||
@ -43,7 +46,13 @@ class SingleNote:
|
|||||||
"""音符所处的音轨"""
|
"""音符所处的音轨"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, instrument: int, pitch: int, velocity: int, startTime: int, lastTime: int, track_number:int = 0
|
self,
|
||||||
|
instrument: int,
|
||||||
|
pitch: int,
|
||||||
|
velocity: int,
|
||||||
|
startTime: int,
|
||||||
|
lastTime: int,
|
||||||
|
track_number: int = 0,
|
||||||
):
|
):
|
||||||
"""用于存储单个音符的类
|
"""用于存储单个音符的类
|
||||||
:param instrument 乐器编号
|
:param instrument 乐器编号
|
||||||
@ -97,6 +106,11 @@ class SingleNote:
|
|||||||
"lastTime": self.duration,
|
"lastTime": self.duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def __eq__(self, other) -> bool:
|
||||||
|
if not isinstance(other, self.__class__):
|
||||||
|
return False
|
||||||
|
return self.__str__() == other.__str__()
|
||||||
|
|
||||||
|
|
||||||
@dataclass(init=False)
|
@dataclass(init=False)
|
||||||
class SingleCommand:
|
class SingleCommand:
|
||||||
@ -170,3 +184,96 @@ class SingleCommand:
|
|||||||
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)
|
||||||
|
class SingleNoteBox:
|
||||||
|
"""存储单个音符盒"""
|
||||||
|
|
||||||
|
instrument_block: str
|
||||||
|
"""乐器方块"""
|
||||||
|
|
||||||
|
note_value: int
|
||||||
|
"""音符盒音高"""
|
||||||
|
|
||||||
|
annotation_text: str
|
||||||
|
"""音符注释"""
|
||||||
|
|
||||||
|
is_percussion: bool
|
||||||
|
"""是否为打击乐器"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
instrument_block_: str,
|
||||||
|
note_value_: int,
|
||||||
|
percussion: Optional[bool] = None,
|
||||||
|
annotation: str = "",
|
||||||
|
):
|
||||||
|
"""用于存储单个音符盒的类
|
||||||
|
:param instrument_block_ 音符盒演奏所使用的乐器方块
|
||||||
|
:param note_value_ 音符盒的演奏音高
|
||||||
|
:param percussion 此音符盒乐器是否作为打击乐处理
|
||||||
|
注:若为空,则自动识别是否为打击乐器
|
||||||
|
:param annotation 音符注释"""
|
||||||
|
self.instrument_block = instrument_block_
|
||||||
|
"""乐器方块"""
|
||||||
|
self.note_value = note_value_
|
||||||
|
"""音符盒音高"""
|
||||||
|
self.annotation_text = annotation
|
||||||
|
"""音符注释"""
|
||||||
|
if percussion is None:
|
||||||
|
self.is_percussion = percussion in PERCUSSION_INSTRUMENT_LIST
|
||||||
|
else:
|
||||||
|
self.is_percussion = percussion
|
||||||
|
|
||||||
|
@property
|
||||||
|
def inst(self) -> str:
|
||||||
|
"""获取音符盒下的乐器方块"""
|
||||||
|
return self.instrument_block
|
||||||
|
|
||||||
|
@inst.setter
|
||||||
|
def inst(self, inst_):
|
||||||
|
self.instrument_block = inst_
|
||||||
|
|
||||||
|
@property
|
||||||
|
def note(self) -> int:
|
||||||
|
"""获取音符盒音调特殊值"""
|
||||||
|
return self.note_value
|
||||||
|
|
||||||
|
@note.setter
|
||||||
|
def note(self, note_):
|
||||||
|
self.note_value = note_
|
||||||
|
|
||||||
|
@property
|
||||||
|
def annotation(self) -> str:
|
||||||
|
"""获取音符盒的备注"""
|
||||||
|
return self.annotation_text
|
||||||
|
|
||||||
|
@annotation.setter
|
||||||
|
def annotation(self, annotation_):
|
||||||
|
self.annotation_text = annotation_
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return SingleNoteBox(
|
||||||
|
instrument_block_=self.instrument_block,
|
||||||
|
note_value_=self.note_value,
|
||||||
|
annotation=self.annotation_text,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"Note(inst = {self.inst}, note = {self.note}, )"
|
||||||
|
|
||||||
|
def __tuple__(self) -> tuple:
|
||||||
|
return self.inst, self.note, self.annotation
|
||||||
|
|
||||||
|
def __dict__(self) -> dict:
|
||||||
|
return {
|
||||||
|
"inst": self.inst,
|
||||||
|
"note": self.note,
|
||||||
|
"annotation": self.annotation,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __eq__(self, other) -> bool:
|
||||||
|
if not isinstance(other, self.__class__):
|
||||||
|
return False
|
||||||
|
return self.__str__() == other.__str__()
|
||||||
|
8
RSMC_test.py
Normal file
8
RSMC_test.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import Musicreater.experiment
|
||||||
|
|
||||||
|
|
||||||
|
print(
|
||||||
|
Musicreater.experiment.FutureMidiConvertRSNB.from_midi_file(
|
||||||
|
input("midi路径:"), old_exe_format=False
|
||||||
|
).to_note_list_in_delay()
|
||||||
|
)
|
13
example_mcstructure_rcd.py
Normal file
13
example_mcstructure_rcd.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import Musicreater
|
||||||
|
import Musicreater.plugin
|
||||||
|
import Musicreater.plugin.mcstructfile
|
||||||
|
|
||||||
|
print(
|
||||||
|
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_redstone_CD(
|
||||||
|
Musicreater.MidiConvert.from_midi_file(input("midi路径:"), old_exe_format=False),
|
||||||
|
Musicreater.plugin.ConvertConfig(
|
||||||
|
input("输出路径:"),
|
||||||
|
volume=1
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
@ -1,2 +1 @@
|
|||||||
LyricLib>=0.0.3
|
|
||||||
mido>=1.3
|
mido>=1.3
|
||||||
|
Loading…
x
Reference in New Issue
Block a user