mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2024-11-11 01:27:35 +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
|
||||
|
||||
|
||||
__version__ = "1.2.1"
|
||||
__vername__ = "扩大对有问题的Midi文件的兼容性"
|
||||
__version__ = "1.4.0"
|
||||
__vername__ = "红石指令音乐的生成"
|
||||
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"), ("鸣凤鸽子", "MingFengPigeon"))
|
||||
__all__ = [
|
||||
# 主要类
|
||||
|
@ -1,5 +1,7 @@
|
||||
# -*- 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),
|
||||
1: ("note.harp", 6),
|
||||
2: ("note.pling", 6),
|
||||
3: ("note.harp", 6),
|
||||
4: ("note.pling", 6),
|
||||
5: ("note.pling", 6),
|
||||
6: ("note.harp", 6),
|
||||
6: ("note.guitar", 7),
|
||||
7: ("note.harp", 6),
|
||||
8: ("note.share", 7), # 打击乐器无音域
|
||||
9: ("note.harp", 6),
|
||||
10: ("note.didgeridoo", 8),
|
||||
11: ("note.harp", 6),
|
||||
12: ("note.xylophone", 4),
|
||||
13: ("note.chime", 4),
|
||||
14: ("note.harp", 6),
|
||||
8: ("note.bell", 4), # 打击乐器无音域
|
||||
9: ("note.bell", 4),
|
||||
10: ("note.iron_xylophone", 6),
|
||||
11: ("note.iron_xylophone", 6),
|
||||
12: ("note.iron_xylophone", 6),
|
||||
13: ("note.xylophone", 4),
|
||||
14: ("note.chime", 4),
|
||||
15: ("note.harp", 6),
|
||||
16: ("note.bass", 8),
|
||||
17: ("note.harp", 6),
|
||||
18: ("note.harp", 6),
|
||||
18: ("note.flute", 5),
|
||||
19: ("note.harp", 6),
|
||||
20: ("note.harp", 6),
|
||||
21: ("note.harp", 6),
|
||||
22: ("note.harp", 6),
|
||||
21: ("note.flute", 5),
|
||||
22: ("note.flute", 5),
|
||||
23: ("note.guitar", 7),
|
||||
24: ("note.guitar", 7),
|
||||
25: ("note.guitar", 7),
|
||||
@ -81,21 +87,21 @@ PITCHED_INSTRUMENT_LIST = {
|
||||
37: ("note.bass", 8),
|
||||
38: ("note.bass", 8),
|
||||
39: ("note.bass", 8),
|
||||
40: ("note.harp", 6),
|
||||
41: ("note.harp", 6),
|
||||
42: ("note.harp", 6),
|
||||
43: ("note.harp", 6),
|
||||
40: ("note.flute", 5),
|
||||
41: ("note.flute", 5),
|
||||
42: ("note.flute", 5),
|
||||
43: ("note.flute", 5),
|
||||
44: ("note.iron_xylophone", 6),
|
||||
45: ("note.guitar", 7),
|
||||
46: ("note.harp", 6),
|
||||
47: ("note.harp", 6),
|
||||
47: ("note.bd", 7),
|
||||
48: ("note.guitar", 7),
|
||||
49: ("note.guitar", 7),
|
||||
50: ("note.bit", 6),
|
||||
51: ("note.bit", 6),
|
||||
52: ("note.harp", 6),
|
||||
53: ("note.harp", 6),
|
||||
54: ("note.bit", 6),
|
||||
52: ("note.flute", 5),
|
||||
53: ("note.flute", 5),
|
||||
54: ("note.flute", 5),
|
||||
55: ("note.flute", 5),
|
||||
56: ("note.flute", 5),
|
||||
57: ("note.flute", 5),
|
||||
@ -110,16 +116,16 @@ PITCHED_INSTRUMENT_LIST = {
|
||||
66: ("note.bit", 6),
|
||||
67: ("note.bit", 6),
|
||||
68: ("note.flute", 5),
|
||||
69: ("note.harp", 6),
|
||||
70: ("note.harp", 6),
|
||||
69: ("note.bit", 6),
|
||||
70: ("note.banjo", 6),
|
||||
71: ("note.flute", 5),
|
||||
72: ("note.flute", 5),
|
||||
73: ("note.flute", 5),
|
||||
74: ("note.harp", 6),
|
||||
74: ("note.flute", 5),
|
||||
75: ("note.flute", 5),
|
||||
76: ("note.harp", 6),
|
||||
77: ("note.harp", 6),
|
||||
78: ("note.harp", 6),
|
||||
78: ("note.flute", 5),
|
||||
79: ("note.harp", 6),
|
||||
80: ("note.bit", 6),
|
||||
81: ("note.bit", 6),
|
||||
@ -149,35 +155,35 @@ PITCHED_INSTRUMENT_LIST = {
|
||||
105: ("note.banjo", 6),
|
||||
106: ("note.harp", 6),
|
||||
107: ("note.harp", 6),
|
||||
108: ("note.harp", 6),
|
||||
109: ("note.harp", 6),
|
||||
110: ("note.harp", 6),
|
||||
108: ("note.bell", 4),
|
||||
109: ("note.flute", 5),
|
||||
110: ("note.flute", 5),
|
||||
111: ("note.guitar", 7),
|
||||
112: ("note.harp", 6),
|
||||
112: ("note.bell", 4),
|
||||
113: ("note.bell", 4),
|
||||
114: ("note.harp", 6),
|
||||
114: ("note.flute", 5),
|
||||
115: ("note.cow_bell", 5),
|
||||
116: ("note.bd", 7), # 打击乐器无音域
|
||||
117: ("note.bass", 8),
|
||||
118: ("note.bit", 6),
|
||||
119: ("note.bd", 7), # 打击乐器无音域
|
||||
119: ("firework.blast", 7), # 打击乐器无音域
|
||||
120: ("note.guitar", 7),
|
||||
121: ("note.harp", 6),
|
||||
122: ("note.harp", 6),
|
||||
123: ("note.harp", 6),
|
||||
124: ("note.harp", 6),
|
||||
125: ("note.hat", 7), # 打击乐器无音域
|
||||
126: ("note.bd", 7), # 打击乐器无音域
|
||||
126: ("firework.twinkle", 7), # 打击乐器无音域
|
||||
127: ("note.snare", 7), # 打击乐器无音域
|
||||
}
|
||||
|
||||
PERCUSSION_INSTRUMENT_LIST = {
|
||||
PERCUSSION_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
|
||||
34: ("note.bd", 7),
|
||||
35: ("note.bd", 7),
|
||||
36: ("note.hat", 7),
|
||||
37: ("note.snare", 7),
|
||||
38: ("note.snare", 7),
|
||||
39: ("note.snare", 7),
|
||||
39: ("fire.ignite", 7),
|
||||
40: ("note.hat", 7),
|
||||
41: ("note.snare", 7),
|
||||
42: ("note.hat", 7),
|
||||
@ -221,11 +227,22 @@ PERCUSSION_INSTRUMENT_LIST = {
|
||||
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.snare": ("sand",),
|
||||
"note.hat": ("glass",),
|
||||
"note.bd": ("stone",),
|
||||
"note.basedrum": ("stone",),
|
||||
"note.bell": ("gold_block",),
|
||||
"note.flute": ("clay",),
|
||||
"note.chime": ("packed_ice",),
|
||||
@ -238,7 +255,11 @@ INSTRUMENT_BLOCKS_LIST = {
|
||||
"note.banjo": ("hay_block",),
|
||||
"note.pling": ("glowstone",),
|
||||
"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
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
import random
|
||||
from typing import Dict, List, Tuple, Union
|
||||
|
||||
from .constants import INSTRUMENT_BLOCKS_TABLE
|
||||
from .exceptions import *
|
||||
from .main import MidiConvert
|
||||
from .subclass 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):
|
||||
"""
|
||||
加入插值算法优化音感
|
||||
@ -47,9 +177,7 @@ class FutureMidiConvertM4(MidiConvert):
|
||||
]
|
||||
# print(totalCount)
|
||||
|
||||
result: List[
|
||||
Tuple[int, int, int, int, float],
|
||||
] = []
|
||||
result: List[Tuple[int, int, int, int, float],] = []
|
||||
|
||||
for _i in range(totalCount):
|
||||
result.append(
|
||||
@ -105,7 +233,6 @@ class FutureMidiConvertM4(MidiConvert):
|
||||
|
||||
# nowChannel = []
|
||||
for track_no, track in self.channels[i].items():
|
||||
|
||||
noteMsgs = []
|
||||
MsgIndex = []
|
||||
|
||||
@ -152,7 +279,6 @@ class FutureMidiConvertM4(MidiConvert):
|
||||
for every_note in self._linear_note(
|
||||
note, 50 if note.track_no == 0 else 500
|
||||
):
|
||||
|
||||
soundID, _X = (
|
||||
self.perc_inst_to_soundID_withX(note.pitch)
|
||||
if SpecialBits
|
||||
@ -163,7 +289,13 @@ class FutureMidiConvertM4(MidiConvert):
|
||||
|
||||
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
|
||||
blockmeter = (
|
||||
1
|
||||
/ (1 if note.track_no == 0 else 0.9)
|
||||
/ max_volume
|
||||
/ every_note[4]
|
||||
- 1
|
||||
)
|
||||
|
||||
track_now.append(
|
||||
SingleCommand(
|
||||
@ -237,7 +369,6 @@ class FutureMidiConvertM4(MidiConvert):
|
||||
|
||||
# nowChannel = []
|
||||
for track_no, track in self.channels[i].items():
|
||||
|
||||
noteMsgs = []
|
||||
MsgIndex = []
|
||||
|
||||
@ -282,7 +413,6 @@ class FutureMidiConvertM4(MidiConvert):
|
||||
for every_note in self._linear_note(
|
||||
note, 50 if note.track_no == 0 else 500
|
||||
):
|
||||
|
||||
soundID, _X = (
|
||||
self.perc_inst_to_soundID_withX(note.pitch)
|
||||
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 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
|
||||
from typing import Literal
|
||||
from ..constants import x,y,z
|
||||
|
||||
# 不要用 没写完
|
||||
def delay_to_note_blocks(
|
||||
baseblock: str = "stone",
|
||||
position_forward: Union(x, y, z) = z,
|
||||
max_height: int = 64,
|
||||
position_forward: Literal['x','y','z'] = z,
|
||||
):
|
||||
"""传入音符,生成以音符盒存储的红石音乐
|
||||
:param:
|
||||
@ -229,10 +163,6 @@ def delay_to_note_blocks(
|
||||
|
||||
from TrimMCStruct import Structure, Block
|
||||
|
||||
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
|
||||
len(commands), max_height
|
||||
)
|
||||
|
||||
struct = Structure(
|
||||
(_sideLength, max_height, _sideLength), # 声明结构大小
|
||||
)
|
||||
@ -293,4 +223,3 @@ def delay_to_note_blocks(
|
||||
startpos[0] += posadder[0]
|
||||
startpos[1] += posadder[1]
|
||||
startpos[2] += posadder[2]
|
||||
|
||||
|
@ -25,7 +25,6 @@ Terms & Conditions: License.md in the root directory
|
||||
# 赶快呼叫 程序员!Let's Go! 直ぐに呼びましょプログラマ レッツゴー!
|
||||
|
||||
|
||||
|
||||
import math
|
||||
import os
|
||||
from typing import List, Literal, Tuple, Union
|
||||
@ -183,12 +182,19 @@ class MidiConvert:
|
||||
"""文件名,不含路径且不含后缀"""
|
||||
|
||||
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:
|
||||
raise MidiDestroyedError(f"文件{midi_file_path}损坏:{E}")
|
||||
except FileNotFoundError as E:
|
||||
raise FileNotFoundError(f"文件{midi_file_path}不存在:{E}")
|
||||
|
||||
# ……真的那么重要吗
|
||||
# 我又几曾何时,知道祂真的会抛下我
|
||||
|
||||
@staticmethod
|
||||
def inst_to_souldID_withX(
|
||||
instrumentID: int,
|
||||
@ -214,7 +220,7 @@ class MidiConvert:
|
||||
tuple(str我的世界乐器名, int转换算法中的X)
|
||||
"""
|
||||
try:
|
||||
return PITCHED_INSTRUMENT_LIST[instrumentID]
|
||||
return PITCHED_INSTRUMENT_TABLE[instrumentID]
|
||||
except KeyError:
|
||||
return "note.flute", 5
|
||||
|
||||
@ -233,18 +239,13 @@ class MidiConvert:
|
||||
tuple(str我的世界乐器名, int转换算法中的X)
|
||||
"""
|
||||
try:
|
||||
return PERCUSSION_INSTRUMENT_LIST[instrumentID]
|
||||
return PERCUSSION_INSTRUMENT_TABLE[instrumentID]
|
||||
except KeyError:
|
||||
print("WARN", f"无法使用打击乐器列表库,或者使用了不存在的乐器,打击乐器使用Dislink算法代替。{instrumentID}")
|
||||
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(
|
||||
self,
|
||||
max_score: int,
|
||||
@ -356,6 +357,10 @@ class MidiConvert:
|
||||
)
|
||||
)
|
||||
|
||||
# 那是假的
|
||||
# 一切都并未留下痕迹啊
|
||||
# 那梦又是多么的真实……
|
||||
|
||||
if r"%%t" in pgs_style:
|
||||
result.append(
|
||||
SingleCommand(
|
||||
@ -552,7 +557,6 @@ class MidiConvert:
|
||||
if msg.type == "set_tempo":
|
||||
tempo = msg.tempo
|
||||
else:
|
||||
|
||||
try:
|
||||
if not track_no in midi_channels[msg.channel].keys():
|
||||
midi_channels[msg.channel][track_no] = []
|
||||
@ -576,7 +580,6 @@ class MidiConvert:
|
||||
("NoteE", msg.note, microseconds)
|
||||
)
|
||||
|
||||
|
||||
"""整合后的音乐通道格式
|
||||
每个通道包括若干消息元素其中逃不过这三种:
|
||||
|
||||
@ -689,7 +692,7 @@ class MidiConvert:
|
||||
max_volume: float = 1.0,
|
||||
speed: float = 1.0,
|
||||
player_selector: str = "@a",
|
||||
) -> Tuple[List[SingleCommand], int]:
|
||||
) -> Tuple[List[SingleCommand], int, int]:
|
||||
"""
|
||||
使用金羿的转换思路,将midi转换为我的世界命令列表,并输出每个音符之后的延迟
|
||||
|
||||
@ -704,7 +707,7 @@ class MidiConvert:
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple( list[SingleCommand,...], int音乐时长游戏刻 )
|
||||
tuple( list[SingleCommand,...], int音乐时长游戏刻, int最大同时播放的指令数量 )
|
||||
"""
|
||||
|
||||
if speed == 0:
|
||||
@ -740,10 +743,10 @@ class MidiConvert:
|
||||
else self.inst_to_souldID_withX(InstID)
|
||||
)
|
||||
|
||||
score_now = round(msg[-1] / float(speed) / 50)
|
||||
delaytime_now = round(msg[-1] / float(speed) / 50)
|
||||
|
||||
try:
|
||||
tracks[score_now].append(
|
||||
tracks[delaytime_now].append(
|
||||
self.execute_cmd_head.format(player_selector)
|
||||
+ f"playsound {soundID} @s ^ ^ ^{128 / max_volume / msg[2] - 1} {msg[2] / 128} "
|
||||
+ (
|
||||
@ -753,7 +756,7 @@ class MidiConvert:
|
||||
)
|
||||
)
|
||||
except KeyError:
|
||||
tracks[score_now] = [
|
||||
tracks[delaytime_now] = [
|
||||
self.execute_cmd_head.format(player_selector)
|
||||
+ 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.sort()
|
||||
results = []
|
||||
max_multi = 0
|
||||
|
||||
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]])):
|
||||
results.append(
|
||||
SingleCommand(
|
||||
@ -794,7 +799,7 @@ class MidiConvert:
|
||||
|
||||
self.music_command_list = results
|
||||
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):
|
||||
dst = MidiConvert(
|
||||
|
@ -148,7 +148,6 @@ def commands_to_BDX_bytes(
|
||||
now_x = 0
|
||||
|
||||
for command in commands_list:
|
||||
|
||||
_bytes += form_command_block_in_BDX_bytes(
|
||||
command.command_text,
|
||||
(1 if y_forward else 0)
|
||||
@ -156,12 +155,14 @@ def commands_to_BDX_bytes(
|
||||
((now_y != 0) and (not y_forward))
|
||||
or (y_forward and (now_y != (max_height - 1)))
|
||||
)
|
||||
else (3 if z_forward else 2)
|
||||
else (
|
||||
(3 if z_forward else 2)
|
||||
if (
|
||||
((now_z != 0) and (not z_forward))
|
||||
or (z_forward and (now_z != _sideLength - 1))
|
||||
)
|
||||
else 5,
|
||||
else 5
|
||||
),
|
||||
impluse=2,
|
||||
condition=command.conditional,
|
||||
needRedstone=False,
|
||||
@ -171,6 +172,16 @@ def commands_to_BDX_bytes(
|
||||
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
|
||||
|
||||
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.speed_multiplier,
|
||||
player,
|
||||
)
|
||||
)[:2]
|
||||
|
||||
if not os.path.exists(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: 最大高度
|
||||
:return: 外切正方形的边长 int"""
|
||||
return math.ceil(math.sqrt(math.ceil(total / maxHeight)))
|
||||
|
||||
|
||||
|
@ -15,7 +15,8 @@ Terms & Conditions: License.md in the root directory
|
||||
|
||||
|
||||
__all__ = [
|
||||
"to_mcstructure_file_in_delay"
|
||||
"to_mcstructure_file_in_delay",
|
||||
"to_mcstructure_file_in_redstone_CD",
|
||||
]
|
||||
__author__ = (("金羿", "Eilles Wan"),)
|
||||
|
||||
|
@ -13,10 +13,12 @@ Terms & Conditions: License.md in the root directory
|
||||
|
||||
import os
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from ...exceptions import CommandFormatError
|
||||
from ...main import MidiConvert
|
||||
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(
|
||||
@ -51,7 +53,7 @@ def to_mcstructure_file_in_delay(
|
||||
data_cfg.volume_ratio,
|
||||
data_cfg.speed_multiplier,
|
||||
player,
|
||||
)
|
||||
)[:2]
|
||||
|
||||
if not os.path.exists(data_cfg.dist_path):
|
||||
os.makedirs(data_cfg.dist_path)
|
||||
@ -69,8 +71,56 @@ def to_mcstructure_file_in_delay(
|
||||
return size, max_delay
|
||||
|
||||
|
||||
def to_mcstructure_file_in_redstone(
|
||||
|
||||
def to_mcstructure_file_in_redstone_CD(
|
||||
midi_cvt: MidiConvert,
|
||||
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.speed_multiplier,
|
||||
player,
|
||||
)
|
||||
)[:2]
|
||||
|
||||
if not os.path.exists(data_cfg.dist_path):
|
||||
os.makedirs(data_cfg.dist_path)
|
||||
|
@ -16,12 +16,40 @@ Terms & Conditions: License.md in the root directory
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
from typing import List, Tuple
|
||||
from typing import List, Literal, Tuple
|
||||
|
||||
from TrimMCStruct import Block, Structure, TAG_Byte, TAG_Long
|
||||
|
||||
from ..constants import x, y, z
|
||||
from ..subclass import SingleCommand
|
||||
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(
|
||||
@ -64,8 +92,12 @@ def form_note_block_in_NBT_struct(
|
||||
|
||||
def form_repeater_in_NBT_struct(delay: int, facing: int):
|
||||
"""生成中继器方块
|
||||
:param facing:
|
||||
:param delay: 1~4
|
||||
:param facing: 朝向:
|
||||
Z- 北 0
|
||||
X- 东 1
|
||||
Z+ 南 2
|
||||
X+ 西 3
|
||||
:param delay: 0~3
|
||||
:return Block()"""
|
||||
|
||||
return Block(
|
||||
@ -251,3 +283,109 @@ def commands_to_structure(
|
||||
),
|
||||
(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]
|
||||
|
||||
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(
|
||||
self: MidiConvert,
|
||||
|
@ -18,6 +18,9 @@ Terms & Conditions: License.md in the root directory
|
||||
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
from .constants import PERCUSSION_INSTRUMENT_LIST
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
@ -43,7 +46,13 @@ class SingleNote:
|
||||
"""音符所处的音轨"""
|
||||
|
||||
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 乐器编号
|
||||
@ -97,6 +106,11 @@ class SingleNote:
|
||||
"lastTime": self.duration,
|
||||
}
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
return self.__str__() == other.__str__()
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class SingleCommand:
|
||||
@ -170,3 +184,96 @@ class SingleCommand:
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user