10 Commits

Author SHA1 Message Date
EillesWan
fc138f1dbf 新的转换算法测试,更好的声音适配 2024-01-08 00:08:52 +08:00
EillesWan
0b0328bc44 更新乐器对照表 2023-10-22 12:37:53 +08:00
EillesWan
72dfdfeb34 改进Channel的应用,减少内存占用,优化结构生成,提高MIDI兼容,修改API结构 2023-10-02 18:24:09 +08:00
EillesWan
9a580132e5 新增midi对应钢琴键盘的音符表 2023-09-30 02:27:51 +08:00
EillesWan
29380d4151 优化生成结构 2023-09-24 12:44:00 +08:00
EillesWan
76eff25a1d 文档全面升级,部分架构更新 2023-09-24 00:38:31 +08:00
EillesWan
7b60d3f9ea 新增红石包子 2023-09-10 19:26:42 +08:00
EillesWan
483e45dcc2 成了吗?如成! 2023-09-10 19:13:39 +08:00
EillesWan
627a26f64a 支持mcstructure结构导出旧版指令 2023-09-10 11:43:39 +08:00
EillesWan
357cb18c5b 提交文档,增加作者署名 2023-08-12 15:10:42 +08:00
50 changed files with 2062 additions and 1534 deletions

13
.gitignore vendored
View File

@@ -5,12 +5,13 @@
# mystuff # mystuff
/.vscode /.vscode
*.mid /*.mid
*.midi /*.midi
*.mcpack /*.mcpack
*.bdx /*.bdx
*.json /*.json
*.mcstructure /*.mcstructure
.mscbackup
/logs /logs
/languages /languages
/llc_cli.py /llc_cli.py

View File

@@ -203,7 +203,7 @@
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
Copyright 2022 TriM-Organization 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & all the developers of Musicreater Copyright 2023 TriM-Organization 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & all the developers of Musicreater
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -12,19 +12,24 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
__version__ = "1.4.0" __version__ = "1.6.2"
__vername__ = "红石指令音乐的生成" __vername__ = "新的转换算法测试,更好的声音适配"
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"), ("鸣凤鸽子", "MingFengPigeon")) __author__ = (
("金羿", "Eilles Wan"),
("诸葛亮与八卦阵", "bgArray"),
("偷吃不是Touch", "Touch"),
("鸣凤鸽子", "MingFengPigeon"),
)
__all__ = [ __all__ = [
# 主要类 # 主要类
"MidiConvert", "MidiConvert",
# 附加类 # 附加类
# "SingleNote", "SingleNote",
"SingleCommand", "SingleCommand",
# "TimeStamp", 未来功能 # "TimeStamp", 未来功能
] ]

View File

@@ -1,7 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from typing import Dict, List, Tuple
""" """
存放常量与数值性内容 存放常量与数值性内容
""" """
@@ -14,10 +12,11 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from typing import Dict, List, Tuple
x = "x" x = "x"
""" """
@@ -42,6 +41,137 @@ DEFAULT_PROGRESSBAR_STYLE = (
默认的进度条样式组 默认的进度条样式组
""" """
MIDI_PITCH_NAME_TABLE: Dict[int, str] = {
0: "C",
1: "C#",
2: "D",
3: "D#",
4: "E",
5: "F",
6: "F#",
7: "G",
8: "G#",
9: "A",
10: "A#",
11: "B",
12: "C",
13: "C#",
14: "D",
15: "D#",
16: "E",
17: "F",
18: "F#",
19: "G",
20: "G#",
21: "A",
22: "A#",
23: "B",
24: "C",
25: "C#",
26: "D",
27: "D#",
28: "E",
29: "F",
30: "F#",
31: "G",
32: "G#",
33: "A",
34: "A#",
35: "B",
36: "C",
37: "C#",
38: "D",
39: "D#",
40: "E",
41: "F",
42: "F#",
43: "G",
44: "G#",
45: "A",
46: "A#",
47: "B",
48: "C",
49: "C#",
50: "D",
51: "D#",
52: "E",
53: "F",
54: "F#",
55: "G",
56: "G#",
57: "A",
58: "A#",
59: "B",
60: "C",
61: "C#",
62: "D",
63: "D#",
64: "E",
65: "F",
66: "F#",
67: "G",
68: "G#",
69: "A",
70: "A#",
71: "B",
72: "C",
73: "C#",
74: "D",
75: "D#",
76: "E",
77: "F",
78: "F#",
79: "G",
80: "G#",
81: "A",
82: "A#",
83: "B",
84: "C",
85: "C#",
86: "D",
87: "D#",
88: "E",
89: "F",
90: "F#",
91: "G",
92: "G#",
93: "A",
94: "A#",
95: "B",
96: "C",
97: "C#",
98: "D",
99: "D#",
100: "E",
101: "F",
102: "F#",
103: "G",
104: "G#",
105: "A",
106: "A#",
107: "B",
108: "C",
109: "C#",
110: "D",
111: "D#",
112: "E",
113: "F",
114: "F#",
115: "G",
116: "G#",
117: "A",
118: "A#",
119: "B",
120: "C",
121: "C#",
122: "D",
123: "D#",
124: "E",
125: "F",
126: "F#",
127: "G",
}
# 以下是由 Touch “偷吃” 带来的高准确率音效对照表 # 以下是由 Touch “偷吃” 带来的高准确率音效对照表
# 包括乐音乐器对照和打击乐器对照 # 包括乐音乐器对照和打击乐器对照
@@ -54,23 +184,23 @@ PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
4: ("note.pling", 6), 4: ("note.pling", 6),
5: ("note.pling", 6), 5: ("note.pling", 6),
6: ("note.guitar", 7), 6: ("note.guitar", 7),
7: ("note.harp", 6), 7: ("note.guitar", 7),
8: ("note.bell", 4), # 打击乐器无音域 8: ("note.iron_xylophone", 8),
9: ("note.bell", 4), 9: ("note.bell", 4), # 打击乐器无音域
10: ("note.iron_xylophone", 6), 10: ("note.iron_xylophone", 6),
11: ("note.iron_xylophone", 6), 11: ("note.iron_xylophone", 6),
12: ("note.iron_xylophone", 6), 12: ("note.iron_xylophone", 6),
13: ("note.xylophone", 4), 13: ("note.xylophone", 4),
14: ("note.chime", 4), 14: ("note.chime", 4),
15: ("note.harp", 6), 15: ("note.banjo", 6),
16: ("note.bass", 8), 16: ("note.xylophone", 6),
17: ("note.harp", 6), 17: ("note.iron_xylophone", 6),
18: ("note.flute", 5), 18: ("note.flute", 5),
19: ("note.harp", 6), 19: ("note.flute", 5),
20: ("note.harp", 6), 20: ("note.flute", 5),
21: ("note.flute", 5), 21: ("note.flute", 5),
22: ("note.flute", 5), 22: ("note.flute", 5),
23: ("note.guitar", 7), 23: ("note.flute", 5),
24: ("note.guitar", 7), 24: ("note.guitar", 7),
25: ("note.guitar", 7), 25: ("note.guitar", 7),
26: ("note.guitar", 7), 26: ("note.guitar", 7),
@@ -90,19 +220,19 @@ PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
40: ("note.flute", 5), 40: ("note.flute", 5),
41: ("note.flute", 5), 41: ("note.flute", 5),
42: ("note.flute", 5), 42: ("note.flute", 5),
43: ("note.flute", 5), 43: ("note.bass", 8),
44: ("note.iron_xylophone", 6), 44: ("note.flute", 5),
45: ("note.guitar", 7), 45: ("note.iron_xylophone", 6),
46: ("note.harp", 6), 46: ("note.harp", 6),
47: ("note.bd", 7), 47: ("note.snare", 7),
48: ("note.guitar", 7), 48: ("note.flute", 5),
49: ("note.guitar", 7), 49: ("note.flute", 5),
50: ("note.bit", 6), 50: ("note.flute", 5),
51: ("note.bit", 6), 51: ("note.flute", 5),
52: ("note.flute", 5), 52: ("note.didgeridoo", 5),
53: ("note.flute", 5), 53: ("note.flute", 5), # 合唱“啊”音
54: ("note.flute", 5), 54: ("note.flute", 5), # 人声“嘟”音
55: ("note.flute", 5), 55: ("mob.zombie.wood", 7), # 合成人声
56: ("note.flute", 5), 56: ("note.flute", 5),
57: ("note.flute", 5), 57: ("note.flute", 5),
58: ("note.flute", 5), 58: ("note.flute", 5),
@@ -123,73 +253,73 @@ PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
73: ("note.flute", 5), 73: ("note.flute", 5),
74: ("note.flute", 5), 74: ("note.flute", 5),
75: ("note.flute", 5), 75: ("note.flute", 5),
76: ("note.harp", 6), 76: ("note.iron_xylophone", 6),
77: ("note.harp", 6), 77: ("note.iron_xylophone", 6),
78: ("note.flute", 5), 78: ("note.flute", 5),
79: ("note.harp", 6), 79: ("note.flute", 5),
80: ("note.bit", 6), 80: ("note.bit", 6),
81: ("note.bit", 6), 81: ("note.bit", 6),
82: ("note.bit", 6), 82: ("note.flute", 5),
83: ("note.bit", 6), 83: ("note.flute", 5),
84: ("note.bit", 6), 84: ("note.guitar", 7),
85: ("note.bit", 6), 85: ("note.flute", 5),
86: ("note.bit", 6), 86: ("note.bass", 8),
87: ("note.bit", 6), 87: ("note.bass", 8),
88: ("note.bit", 6), 88: ("note.bit", 6),
89: ("note.bit", 6), 89: ("note.flute", 5),
90: ("note.bit", 6), 90: ("note.bit", 6),
91: ("note.bit", 6), 91: ("note.flute", 5),
92: ("note.bit", 6), 92: ("note.bell", 4),
93: ("note.bit", 6), 93: ("note.guitar", 7),
94: ("note.bit", 6), 94: ("note.flute", 5),
95: ("note.bit", 6), 95: ("note.bit", 6),
96: ("note.bit", 6), 96: ("note.bit", 6), # 雨声
97: ("note.bit", 6), 97: ("note.flute", 5),
98: ("note.bit", 6), 98: ("note.bell", 4),
99: ("note.bit", 6), 99: ("note.bit", 6), # 大气
100: ("note.bit", 6), 100: ("note.bit", 6), # 明亮
101: ("note.bit", 6), 101: ("note.bit", 6), # 鬼怪
102: ("note.bit", 6), 102: ("note.bit", 6), # 回声
103: ("note.bit", 6), 103: ("note.bit", 6), # 科幻
104: ("note.harp", 6), 104: ("note.iron_xylophone", 6),
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.bell", 4), 108: ("note.bell", 4),
109: ("note.flute", 5), 109: ("note.flute", 5),
110: ("note.flute", 5), 110: ("note.flute", 5),
111: ("note.guitar", 7), 111: ("note.flute", 5),
112: ("note.bell", 4), 112: ("note.bell", 4),
113: ("note.bell", 4), 113: ("note.xylophone", 4),
114: ("note.flute", 5), 114: ("note.flute", 5),
115: ("note.cow_bell", 5), 115: ("note.hat", 7), # 打击乐器无音域
116: ("note.bd", 7), # 打击乐器无音域 116: ("note.snare", 7), # 打击乐器无音域
117: ("note.bass", 8), 117: ("note.snare", 7), # 打击乐器无音域
118: ("note.bit", 6), 118: ("note.bd", 7), # 打击乐器无音域
119: ("firework.blast", 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.bit", 6),
125: ("note.hat", 7), # 打击乐器无音域 125: ("note.hat", 7), # 直升机
126: ("firework.twinkle", 7), # 打击乐器无音域 126: ("firework.twinkle", 7), # 打击乐器无音域
127: ("note.snare", 7), # 打击乐器无音域 127: ("mob.zombie.wood", 7), # 打击乐器无音域
} }
PERCUSSION_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = { PERCUSSION_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
34: ("note.bd", 7), 34: ("note.hat", 7),
35: ("note.bd", 7), 35: ("note.bd", 7),
36: ("note.hat", 7), 36: ("note.bd", 7),
37: ("note.snare", 7), 37: ("note.snare", 7),
38: ("note.snare", 7), 38: ("note.snare", 7),
39: ("fire.ignite", 7), 39: ("fire.ignite", 7),
40: ("note.hat", 7), 40: ("note.snare", 7),
41: ("note.snare", 7), 41: ("note.hat", 7),
42: ("note.hat", 7), 42: ("note.hat", 7),
43: ("note.snare", 7), 43: ("firework.blast", 7),
44: ("note.snare", 7), 44: ("note.hat", 7),
45: ("note.bell", 4), 45: ("note.snare", 4),
46: ("note.snare", 7), 46: ("note.snare", 7),
47: ("note.snare", 7), 47: ("note.snare", 7),
48: ("note.bell", 4), 48: ("note.bell", 4),
@@ -235,9 +365,10 @@ PERCUSSION_INSTRUMENT_LIST: List[str] = [
"firework.blast", "firework.blast",
"firework.twinkle", "firework.twinkle",
"fire.ignite", "fire.ignite",
"mob.zombie.wood",
] ]
INSTRUMENT_BLOCKS_TABLE: Dict[str, Tuple[str]] = { 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",),
@@ -260,6 +391,7 @@ INSTRUMENT_BLOCKS_TABLE: Dict[str, Tuple[str]] = {
"firework.blast": ("sandstone",), "firework.blast": ("sandstone",),
"firework.twinkle": ("red_sandstone",), "firework.twinkle": ("red_sandstone",),
"fire.ignite": ("concrete_powder",), "fire.ignite": ("concrete_powder",),
"mob.zombie.wood": ("sand",),
} }

View File

@@ -12,7 +12,7 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
@@ -24,7 +24,7 @@ class MSCTBaseException(Exception):
"""音·创库版本的所有错误均继承于此""" """音·创库版本的所有错误均继承于此"""
super().__init__(*args) super().__init__(*args)
def miao( def meow(
self, self,
): ):
for i in self.args: for i in self.args:
@@ -66,12 +66,15 @@ class CommandFormatError(RuntimeError):
super().__init__("指令格式不匹配", *args) super().__init__("指令格式不匹配", *args)
class CrossNoteError(MidiFormatException): # class CrossNoteError(MidiFormatException):
"""同通道下同音符交叉出现所产生的错误""" # """同通道下同音符交叉出现所产生的错误"""
def __init__(self, *args): # def __init__(self, *args):
"""同通道下同音符交叉出现所产生的错误""" # """同通道下同音符交叉出现所产生的错误"""
super().__init__("同通道下同音符交叉", *args) # super().__init__("同通道下同音符交叉", *args)
# 这TM是什么错误
# 我什么时候写的这玩意?
# 我哪知道这说的是啥?
class NotDefineTempoError(MidiFormatException): class NotDefineTempoError(MidiFormatException):
@@ -98,7 +101,15 @@ class NotDefineProgramError(MidiFormatException):
super().__init__("未指定演奏乐器", *args) super().__init__("未指定演奏乐器", *args)
class ZeroSpeedError(MidiFormatException): class NoteOnOffMismatchError(MidiFormatException):
"""音符开音和停止不匹配的错误"""
def __init__(self, *args):
"""音符开音和停止不匹配的错误"""
super().__init__("音符不匹配", *args)
class ZeroSpeedError(ZeroDivisionError):
"""以0作为播放速度的错误""" """以0作为播放速度的错误"""
def __init__(self, *args): def __init__(self, *args):

View File

@@ -12,18 +12,18 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import random import random
from typing import Dict, List, Tuple, Union
from .constants import INSTRUMENT_BLOCKS_TABLE 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 *
from .types import Tuple, List, Dict
class FutureMidiConvertRSNB(MidiConvert): class FutureMidiConvertRSNB(MidiConvert):
@@ -58,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):
""" """
@@ -164,179 +69,58 @@ 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, 50 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,
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转换为我的世界命令列表并输出每个音符之后的延迟
Parameters Parameters
---------- ----------
@@ -349,126 +133,52 @@ class FutureMidiConvertM4(MidiConvert):
Returns Returns
------- -------
tuple( list[SingleCommand,...], int音乐时长游戏刻 ) tuple( list[SingleCommand,...], int音乐时长游戏刻, int最大同时播放的指令数量 )
""" """
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))
else:
notes_list.append(note)
# nowChannel = [] notes_list.sort(key=lambda a: a.start_time)
for track_no, track in self.channels[i].items():
noteMsgs = []
MsgIndex = []
for msg in track: self.music_command_list = []
if msg[0] == "PgmC": multi = max_multi = 0
InstID = msg[1] delaytime_previous = 0
elif msg[0] == "NoteS": for note in notes_list:
noteMsgs.append(msg[1:]) delaytime_now = round(note.start_time / speed / 50)
MsgIndex.append(msg[1]) if (tickdelay := (delaytime_now - delaytime_previous)) == 0:
multi += 1
elif msg[0] == "NoteE": else:
if msg[1] in MsgIndex: max_multi = max(max_multi, multi)
note_channels[i].append( multi = 0
SingleNote( self.music_command_list.append(
InstID, SingleCommand(
msg[1], self.execute_cmd_head.format(player_selector)
noteMsgs[MsgIndex.index(msg[1])][1], + note.to_command(max_volume),
noteMsgs[MsgIndex.index(msg[1])][2], tick_delay=tickdelay,
msg[-1] - noteMsgs[MsgIndex.index(msg[1])][2], annotation="{}播放{}%{}".format(
track_number=track_no, mctick2timestr(delaytime_now),
) max_volume * 100,
) "{}:{}".format(note.mc_sound_ID, note.mc_pitch),
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:
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
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 = []
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, ""
),
)
) )
)
delaytime_previous = delaytime_now
self.music_command_list = results self.music_tick_num = round(notes_list[-1].start_time / speed / 50)
self.music_tick_num = max(all_ticks) return self.music_command_list, self.music_tick_num, max_multi + 1
return results, self.music_tick_num
class FutureMidiConvertM5(MidiConvert): class FutureMidiConvertM5(MidiConvert):

View File

@@ -27,39 +27,13 @@ Terms & Conditions: License.md in the root directory
import math import math
import os import os
from typing import List, Literal, Tuple, Union
import mido
from .constants import * from .constants import *
from .exceptions import * from .exceptions import *
from .subclass import * from .subclass import *
from .types import *
from .utils import * from .utils import *
VoidMido = Union[mido.MidiFile, None] # void mido
"""
空Midi类类型
"""
ChannelType = 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],]],],]
"""
""" """
学习笔记: 学习笔记:
tempo: microseconds per quarter note 毫秒每四分音符,换句话说就是一拍占多少毫秒 tempo: microseconds per quarter note 毫秒每四分音符,换句话说就是一拍占多少毫秒
@@ -112,7 +86,7 @@ class MidiConvert:
execute_cmd_head: str execute_cmd_head: str
"""execute指令头部""" """execute指令头部"""
channels: ChannelType channels: Union[ChannelType, NoteChannelType]
"""频道信息字典""" """频道信息字典"""
music_command_list: List[SingleCommand] music_command_list: List[SingleCommand]
@@ -195,57 +169,6 @@ class MidiConvert:
# ……真的那么重要吗 # ……真的那么重要吗
# 我又几曾何时,知道祂真的会抛下我 # 我又几曾何时,知道祂真的会抛下我
@staticmethod
def inst_to_souldID_withX(
instrumentID: int,
) -> Tuple[str, int]:
"""
返回midi的乐器ID对应的我的世界乐器名对于音域转换算法如下
2**( ( msg.note - 60 - X ) / 12 ) 即为MC的音高其中
X的取值随乐器不同而变化
竖琴harp、电钢琴pling、班卓琴banjo、方波bit、颤音琴iron_xylophone 的时候为6
吉他的时候为7
贝斯bass、迪吉里杜管didgeridoo的时候为8
长笛flute、牛铃cou_bell的时候为5
钟琴bell、管钟chime、木琴xylophone的时候为4
而存在一些打击乐器bd(basedrum)、hat、snare没有音域则没有X那么我们返回7即可
Parameters
----------
instrumentID: int
midi的乐器ID
Returns
-------
tuple(str我的世界乐器名, int转换算法中的X)
"""
try:
return PITCHED_INSTRUMENT_TABLE[instrumentID]
except KeyError:
return "note.flute", 5
@staticmethod
def perc_inst_to_soundID_withX(instrumentID: int) -> Tuple[str, int]:
"""
对于Midi第10通道所对应的打击乐器返回我的世界乐器名
Parameters
----------
instrumentID: int
midi的乐器ID
Returns
-------
tuple(str我的世界乐器名, int转换算法中的X)
"""
try:
return PERCUSSION_INSTRUMENT_TABLE[instrumentID]
except KeyError:
return "note.bd", 7
# 明明已经走了
# 凭什么还要在我心里留下缠绵缱绻
def form_progress_bar( def form_progress_bar(
self, self,
max_score: int, max_score: int,
@@ -522,24 +445,26 @@ class MidiConvert:
self.progress_bar_command = result self.progress_bar_command = result
return result return result
def to_music_channels( def to_music_note_channels(
self, self,
) -> ChannelType: ignore_mismatch_error: bool = True,
) -> NoteChannelType:
""" """
使用金羿的转换思路,将midi解析并转换为频道信息字典 将midi解析并转换为频道音符字典
Returns Returns
------- -------
以频道作为分割的Midi信息字典: 以频道作为分割的Midi音符列表字典:
Dict[int,Dict[int,List[Union[Tuple[Literal["PgmC"], int, int],Tuple[Literal["NoteS"], int, int, int],Tuple[Literal["NoteE"], int, int],]],],] Dict[int,List[SingleNote,]]
""" """
if self.midi is None: if self.midi is None:
raise MidiUnboundError( raise MidiUnboundError(
"你是否正在使用的是一个由 copy_important 生成的MidiConvert对象这是不可复用的。" "你是否正在使用的是一个由 copy_important 生成的MidiConvert对象这是不可复用的。"
) )
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨 # 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
midi_channels: ChannelType = empty_midi_channels() midi_channels: NoteChannelType = empty_midi_channels(staff=[])
tempo = mido.midifiles.midifiles.DEFAULT_TEMPO tempo = mido.midifiles.midifiles.DEFAULT_TEMPO
# 我们来用通道统计音乐信息 # 我们来用通道统计音乐信息
@@ -549,6 +474,27 @@ class MidiConvert:
if not track: if not track:
continue continue
note_queue_A: Dict[
int,
List[
Tuple[
int,
int,
]
],
] = empty_midi_channels(staff=[])
note_queue_B: Dict[
int,
List[
Tuple[
int,
int,
]
],
] = empty_midi_channels(staff=[])
channel_program: Dict[int, int] = empty_midi_channels(staff=-1)
for msg in track: for msg in track:
if msg.time != 0: if msg.time != 0:
microseconds += msg.time * tempo / self.midi.ticks_per_beat / 1000 microseconds += msg.time * tempo / self.midi.ticks_per_beat / 1000
@@ -557,28 +503,62 @@ class MidiConvert:
if msg.type == "set_tempo": if msg.type == "set_tempo":
tempo = msg.tempo tempo = msg.tempo
else: else:
try:
if not track_no in midi_channels[msg.channel].keys():
midi_channels[msg.channel][track_no] = []
except AttributeError as E:
print(msg, E)
if msg.type == "program_change": if msg.type == "program_change":
midi_channels[msg.channel][track_no].append( channel_program[msg.channel] = msg.program
("PgmC", msg.program, microseconds)
)
elif msg.type == "note_on" and msg.velocity != 0: elif msg.type == "note_on" and msg.velocity != 0:
midi_channels[msg.channel][track_no].append( note_queue_A[msg.channel].append(
("NoteS", msg.note, msg.velocity, microseconds) (msg.note, channel_program[msg.channel])
) )
note_queue_B[msg.channel].append((msg.velocity, microseconds))
elif (msg.type == "note_on" and msg.velocity == 0) or ( elif (msg.type == "note_off") or (
msg.type == "note_off" msg.type == "note_on" and msg.velocity == 0
): ):
midi_channels[msg.channel][track_no].append( if (msg.note, channel_program[msg.channel]) in note_queue_A[
("NoteE", msg.note, microseconds) msg.channel
) ]:
_velocity, _ms = note_queue_B[msg.channel][
note_queue_A[msg.channel].index(
(msg.note, channel_program[msg.channel])
)
]
note_queue_A[msg.channel].remove(
(msg.note, channel_program[msg.channel])
)
note_queue_B[msg.channel].remove((_velocity, _ms))
midi_channels[msg.channel].append(
SingleNote(
instrument=msg.note,
pitch=channel_program[msg.channel],
velocity=_velocity,
startime=_ms,
lastime=microseconds - _ms,
track_number=track_no,
is_percussion=True,
)
if msg.channel == 9
else SingleNote(
instrument=channel_program[msg.channel],
pitch=msg.note,
velocity=_velocity,
startime=_ms,
lastime=microseconds - _ms,
track_number=track_no,
is_percussion=False,
)
)
else:
if ignore_mismatch_error:
print(
"[WARRING] MIDI格式错误 音符不匹配 {} 无法在上文中找到与之匹配的音符开音消息".format(
msg
)
)
else:
raise NoteOnOffMismatchError(
"当前的MIDI很可能有损坏之嫌……", msg, "无法在上文中找到与之匹配的音符开音消息。"
)
"""整合后的音乐通道格式 """整合后的音乐通道格式
每个通道包括若干消息元素其中逃不过这三种: 每个通道包括若干消息元素其中逃不过这三种:
@@ -592,9 +572,14 @@ class MidiConvert:
3 音符结束消息 3 音符结束消息
("NoteE", 结束的音符ID, 距离演奏开始的毫秒)""" ("NoteE", 结束的音符ID, 距离演奏开始的毫秒)"""
del tempo, self.channels del tempo, self.channels
self.channels = midi_channels self.channels = dict(
# [print([print(no,tno,sum([True if i[0] == 'NoteS' else False for i in track])) for tno,track in cna.items()]) if cna else False for no,cna in midi_channels.items()] [
return midi_channels (channel_no, sorted(channel_notes, key=lambda note: note.start_time))
for channel_no, channel_notes in midi_channels.items()
]
)
return self.channels
def to_command_list_in_score( def to_command_list_in_score(
self, self,
@@ -603,7 +588,7 @@ class MidiConvert:
speed: float = 1.0, speed: float = 1.0,
) -> Tuple[List[List[SingleCommand]], int, int]: ) -> Tuple[List[List[SingleCommand]], int, int]:
""" """
使用金羿的转换思路,将midi转换为我的世界命令列表 将midi转换为我的世界命令列表
Parameters Parameters
---------- ----------
@@ -620,72 +605,51 @@ class 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)
tracks = [] command_channels = []
cmdAmount = 0 command_amount = 0
maxScore = 0 max_score = 0
InstID = -1
self.to_music_channels()
# 此处 我们把通道视为音轨 # 此处 我们把通道视为音轨
for i in self.channels.keys(): for channel in self.to_music_note_channels().values():
# 如果当前通道为空 则跳过 # 如果当前通道为空 则跳过
if not self.channels[i]: if not channel:
continue continue
# 第十通道是打击乐通道 this_channel = []
SpecialBits = True if i == 9 else False
for track_no, track in self.channels[i].items(): for note in channel:
nowTrack = [] score_now = round(note.start_time / float(speed) / 50)
max_score = max(max_score, score_now)
for msg in track: this_channel.append(
if msg[0] == "PgmC": SingleCommand(
InstID = msg[1] self.execute_cmd_head.format(
"@a[scores=({}={})]".format(scoreboard_name, score_now)
elif msg[0] == "NoteS": .replace("(", r"{")
soundID, _X = ( .replace(")", r"}")
self.perc_inst_to_soundID_withX(msg[1])
if SpecialBits
else self.inst_to_souldID_withX(InstID)
) )
score_now = round(msg[-1] / float(speed) / 50) + note.to_command(
maxScore = max(maxScore, score_now) (max_volume) if note.track_no == 0 else (max_volume * 0.9)
mc_pitch = "" if SpecialBits else 2 ** ((msg[1] - 60 - _X) / 12) ),
annotation="{}播放{}%{}".format(
mctick2timestr(score_now),
max_volume * 100,
"{}:{}".format(note.mc_sound_ID, note.mc_pitch),
),
),
)
nowTrack.append( command_amount += 1
SingleCommand(
self.execute_cmd_head.format(
"@a[scores=({}={})]".format(
scoreboard_name, score_now
)
.replace("(", r"{")
.replace(")", r"}")
)
+ "playsound {} @s ^ ^ ^{} {} {}".format(
soundID, 1 / max_volume - 1, msg[2] / 128, mc_pitch
),
annotation="{}播放{}%{}".format(
mctick2timestr(score_now),
max_volume * 100,
"{}:{}".format(soundID, mc_pitch),
),
),
)
cmdAmount += 1 if this_channel:
self.music_command_list.extend(this_channel)
command_channels.append(this_channel)
if nowTrack: self.music_tick_num = max_score
self.music_command_list.extend(nowTrack) return (command_channels, command_amount, max_score)
tracks.append(nowTrack)
# print(cmdAmount)
del InstID
self.music_tick_num = maxScore
return (tracks, cmdAmount, maxScore)
def to_command_list_in_delay( def to_command_list_in_delay(
self, self,
@@ -694,7 +658,7 @@ class MidiConvert:
player_selector: str = "@a", player_selector: str = "@a",
) -> Tuple[List[SingleCommand], int, int]: ) -> Tuple[List[SingleCommand], int, int]:
""" """
使用金羿的转换思路,将midi转换为我的世界命令列表并输出每个音符之后的延迟 将midi转换为我的世界命令列表并输出每个音符之后的延迟
Parameters Parameters
---------- ----------
@@ -711,95 +675,46 @@ class 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] = []
tracks = {}
InstID = -1
# cmd_amount = 0
# 此处 我们把通道视为音轨 # 此处 我们把通道视为音轨
for i in self.channels.keys(): for channel in self.to_music_note_channels().values():
# 如果当前通道为空 则跳过 notes_list.extend(channel)
if not self.channels[i]:
continue
# 第十通道是打击乐通道 notes_list.sort(key=lambda a: a.start_time)
SpecialBits = True if i == 9 else False
# nowChannel = [] self.music_command_list = []
multi = max_multi = 0
delaytime_previous = 0
for track_no, track in self.channels[i].items(): for note in notes_list:
for msg in track: delaytime_now = round(note.start_time / speed / 50)
if msg[0] == "PgmC": if (tickdelay := (delaytime_now - delaytime_previous)) == 0:
InstID = msg[1] multi += 1
else:
elif msg[0] == "NoteS": max_multi = max(max_multi, multi)
soundID, _X = ( multi = 0
self.perc_inst_to_soundID_withX(msg[1]) self.music_command_list.append(
if SpecialBits SingleCommand(
else self.inst_to_souldID_withX(InstID) self.execute_cmd_head.format(player_selector)
) + note.to_command(
(max_volume) if note.track_no == 0 else (max_volume * 0.9)
delaytime_now = round(msg[-1] / float(speed) / 50) ),
tick_delay=tickdelay,
try: annotation="{}播放{}%{}".format(
tracks[delaytime_now].append( mctick2timestr(delaytime_now),
self.execute_cmd_head.format(player_selector) max_volume * 100,
+ f"playsound {soundID} @s ^ ^ ^{128 / max_volume / msg[2] - 1} {msg[2] / 128} " "{}:{}".format(note.mc_sound_ID, note.mc_pitch),
+ ( ),
""
if SpecialBits
else f"{2 ** ((msg[1] - 60 - _X) / 12)}"
)
)
except KeyError:
tracks[delaytime_now] = [
self.execute_cmd_head.format(player_selector)
+ f"playsound {soundID} @s ^ ^ ^{128 / max_volume / msg[2] - 1} {msg[2] / 128} "
+ (
""
if SpecialBits
else f"{2 ** ((msg[1] - 60 - _X) / 12)}"
)
]
# cmd_amount += 1
# print(cmd_amount)
del InstID
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(
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, ""
),
)
) )
)
delaytime_previous = delaytime_now
self.music_command_list = results self.music_tick_num = round(notes_list[-1].start_time / speed / 50)
self.music_tick_num = max(all_ticks) return self.music_command_list, self.music_tick_num, max_multi + 1
return results, self.music_tick_num, max_multi
def copy_important(self): def copy_important(self):
dst = MidiConvert( dst = MidiConvert(

View File

@@ -9,7 +9,7 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
@@ -19,4 +19,4 @@ __all__ = [
] ]
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray")) __author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"))
from .main import * from .main import *

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
用以生成函数附加包的附加功能 用以生成附加包的附加功能
版权所有 © 2023 · 开发者 版权所有 © 2023 · 开发者
Copyright © 2023 all the developers of Musicreater Copyright © 2023 all the developers of Musicreater
@@ -9,15 +9,20 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
__all__ = [ __all__ = [
"to_function_addon_in_score" "to_addon_pack_in_delay",
"to_addon_pack_in_score",
"to_addon_pack_in_repeater",
] ]
__author__ = (("金羿", "Eilles Wan"),) __author__ = (("金羿", "Eilles Wan"),)
from .main import * from .main import (
to_addon_pack_in_delay,
to_addon_pack_in_repeater,
to_addon_pack_in_score,
)

View File

@@ -0,0 +1,541 @@
# -*- 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
import json
import os
import shutil
from typing import Tuple
from TrimMCStruct import Structure
from ...main import MidiConvert
from ..archive import behavior_mcpack_manifest, compress_zipfile
from ..main import ConvertConfig
from ..mcstructure import (
COMPABILITY_VERSION_117,
COMPABILITY_VERSION_119,
commands_to_redstone_delay_structure,
commands_to_structure,
form_command_block_in_NBT_struct,
)
def to_addon_pack_in_score(
midi_cvt: MidiConvert,
data_cfg: ConvertConfig,
scoreboard_name: str = "mscplay",
auto_reset: bool = False,
) -> Tuple[int, int]:
"""
将midi以计分播放器形式转换为我的世界函数附加包
Parameters
----------
midi_cvt: MidiConvert 对象
用于转换的MidiConvert对象
data_cfg: ConvertConfig 对象
部分转换通用参数
scoreboard_name: str
我的世界的计分板名称
auto_reset: bool
是否自动重置计分板
Returns
-------
tuple[int指令数量, int音乐总延迟]
"""
cmdlist, maxlen, maxscore = midi_cvt.to_command_list_in_score(
scoreboard_name, data_cfg.volume_ratio, data_cfg.speed_multiplier
)
# 当文件f夹{self.outputPath}/temp/functions存在时清空其下所有项目然后创建
if os.path.exists(f"{data_cfg.dist_path}/temp/functions/"):
shutil.rmtree(f"{data_cfg.dist_path}/temp/functions/")
os.makedirs(f"{data_cfg.dist_path}/temp/functions/mscplay")
# 写入manifest.json
with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f:
json.dump(
behavior_mcpack_manifest(
pack_description=f"{midi_cvt.midi_music_name} 音乐播放包MCFUNCTION(MCPACK) 计分播放器 - 由 音·创 生成",
pack_name=midi_cvt.midi_music_name + "播放",
modules_description=f"无 - 由 音·创 生成",
),
fp=f,
indent=4,
)
# 写入stop.mcfunction
with open(
f"{data_cfg.dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8"
) as f:
f.write("scoreboard players reset @a {}".format(scoreboard_name))
# 将命令列表写入文件
index_file = open(
f"{data_cfg.dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8"
)
for i in range(len(cmdlist)):
index_file.write(f"function mscplay/track{i + 1}\n")
with open(
f"{data_cfg.dist_path}/temp/functions/mscplay/track{i + 1}.mcfunction",
"w",
encoding="utf-8",
) as f:
f.write("\n".join([single_cmd.cmd for single_cmd in cmdlist[i]]))
index_file.writelines(
(
"scoreboard players add @a[scores={"
+ scoreboard_name
+ "=1..}] "
+ scoreboard_name
+ " 1\n",
(
"scoreboard players reset @a[scores={"
+ scoreboard_name
+ "="
+ str(maxscore + 20)
+ "..}]"
+ f" {scoreboard_name}\n"
)
if auto_reset
else "",
f"function mscplay/progressShow\n" if data_cfg.progressbar_style else "",
)
)
if data_cfg.progressbar_style:
with open(
f"{data_cfg.dist_path}/temp/functions/mscplay/progressShow.mcfunction",
"w",
encoding="utf-8",
) as f:
f.writelines(
"\n".join(
[
single_cmd.cmd
for single_cmd in midi_cvt.form_progress_bar(
maxscore, scoreboard_name, data_cfg.progressbar_style
)
]
)
)
index_file.close()
if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack"):
os.remove(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack")
compress_zipfile(
f"{data_cfg.dist_path}/temp/",
f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack",
)
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
return maxlen, maxscore
def to_addon_pack_in_delay(
midi_cvt: MidiConvert,
data_cfg: ConvertConfig,
player: str = "@a",
max_height: int = 64,
) -> Tuple[int, int]:
"""
将midi以延迟播放器形式转换为mcstructure结构文件后打包成附加包并在附加包中生成相应地导入函数
Parameters
----------
midi_cvt: MidiConvert 对象
用于转换的MidiConvert对象
data_cfg: ConvertConfig 对象
部分转换通用参数
player: str
玩家选择器,默认为`@a`
max_height: int
生成结构最大高度
Returns
-------
tuple[int指令数量, int音乐总延迟]
"""
compability_ver = (
COMPABILITY_VERSION_117
if midi_cvt.enable_old_exe_format
else COMPABILITY_VERSION_119
)
command_list, max_delay = midi_cvt.to_command_list_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)
# 当文件f夹{self.outputPath}/temp/存在时清空其下所有项目,然后创建
if os.path.exists(f"{data_cfg.dist_path}/temp/"):
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
os.makedirs(f"{data_cfg.dist_path}/temp/functions/")
os.makedirs(f"{data_cfg.dist_path}/temp/structures/")
# 写入manifest.json
with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f:
json.dump(
behavior_mcpack_manifest(
pack_description=f"{midi_cvt.midi_music_name} 音乐播放包MCSTRUCTURE(MCPACK) 延迟播放器 - 由 音·创 生成",
pack_name=midi_cvt.midi_music_name + "播放",
modules_description=f"无 - 由 音·创 生成",
),
fp=f,
indent=4,
)
# 写入stop.mcfunction
with open(
f"{data_cfg.dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8"
) as f:
f.write(
"gamerule commandblocksenabled false\ngamerule commandblocksenabled true"
)
# 将命令列表写入文件
index_file = open(
f"{data_cfg.dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8"
)
struct, size, end_pos = commands_to_structure(
command_list,
max_height - 1,
compability_version_=compability_ver,
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_main.mcstructure",
)
),
"wb+",
) as f:
struct.dump(f)
del struct
if data_cfg.progressbar_style:
scb_name = midi_cvt.midi_music_name[:3] + "Pgb"
index_file.write("scoreboard objectives add {0} dummy {0}\n".format(scb_name))
struct_a = Structure((1, 1, 1), compability_version=compability_ver)
struct_a.set_block(
(0, 0, 0),
form_command_block_in_NBT_struct(
r"scoreboard players add {} {} 1".format(player, scb_name),
(0, 0, 0),
1,
1,
alwaysRun=False,
customName="显示进度条并加分",
compability_version_number=compability_ver,
),
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_start.mcstructure",
)
),
"wb+",
) as f:
struct_a.dump(f)
index_file.write(f"structure load {midi_cvt.midi_music_name}_start ~ ~ ~1\n")
pgb_struct, pgbSize, pgbNowPos = commands_to_structure(
midi_cvt.form_progress_bar(max_delay, scb_name, data_cfg.progressbar_style),
max_height - 1,
compability_version_=compability_ver,
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_pgb.mcstructure",
)
),
"wb+",
) as f:
pgb_struct.dump(f)
index_file.write(f"structure load {midi_cvt.midi_music_name}_pgb ~ ~1 ~1\n")
struct_a = Structure(
(1, 1, 1),
)
struct_a.set_block(
(0, 0, 0),
form_command_block_in_NBT_struct(
r"scoreboard players reset {} {}".format(player, scb_name),
(0, 0, 0),
1,
0,
alwaysRun=False,
customName="重置进度条计分板",
compability_version_number=compability_ver,
),
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_reset.mcstructure",
)
),
"wb+",
) as f:
struct_a.dump(f)
del struct_a, pgb_struct
index_file.write(
f"structure load {midi_cvt.midi_music_name}_reset ~{pgbSize[0] + 2} ~ ~1\n"
)
index_file.write(
f"structure load {midi_cvt.midi_music_name}_main ~{pgbSize[0] + 2} ~1 ~1\n"
)
else:
index_file.write(f"structure load {midi_cvt.midi_music_name}_main ~ ~ ~1\n")
index_file.close()
if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack"):
os.remove(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack")
compress_zipfile(
f"{data_cfg.dist_path}/temp/",
f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack",
)
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
return len(command_list), max_delay
def to_addon_pack_in_repeater(
midi_cvt: MidiConvert,
data_cfg: ConvertConfig,
player: str = "@a",
max_height: int = 65,
) -> Tuple[int, int]:
"""
将midi以中继器播放器形式转换为mcstructure结构文件后打包成附加包并在附加包中生成相应地导入函数
Parameters
----------
midi_cvt: MidiConvert 对象
用于转换的MidiConvert对象
data_cfg: ConvertConfig 对象
部分转换通用参数
player: str
玩家选择器,默认为`@a`
max_height: int
生成结构最大高度
Returns
-------
tuple[int指令数量, int音乐总延迟]
"""
compability_ver = (
COMPABILITY_VERSION_117
if midi_cvt.enable_old_exe_format
else COMPABILITY_VERSION_119
)
command_list, max_delay, max_together = 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)
# 当文件f夹{self.outputPath}/temp/存在时清空其下所有项目,然后创建
if os.path.exists(f"{data_cfg.dist_path}/temp/"):
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
os.makedirs(f"{data_cfg.dist_path}/temp/functions/")
os.makedirs(f"{data_cfg.dist_path}/temp/structures/")
# 写入manifest.json
with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f:
json.dump(
behavior_mcpack_manifest(
pack_description=f"{midi_cvt.midi_music_name} 音乐播放包MCSTRUCTURE(MCPACK) 中继器播放器 - 由 音·创 生成",
pack_name=midi_cvt.midi_music_name + "播放",
modules_description=f"无 - 由 音·创 生成",
),
fp=f,
indent=4,
)
# 写入stop.mcfunction
with open(
f"{data_cfg.dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8"
) as f:
f.write(
"gamerule commandblocksenabled false\ngamerule commandblocksenabled true"
)
# 将命令列表写入文件
index_file = open(
f"{data_cfg.dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8"
)
struct, size, end_pos = commands_to_redstone_delay_structure(
command_list,
max_delay,
max_together,
compability_version_=compability_ver,
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_main.mcstructure",
)
),
"wb+",
) as f:
struct.dump(f)
del struct
if data_cfg.progressbar_style:
scb_name = midi_cvt.midi_music_name[:3] + "Pgb"
index_file.write("scoreboard objectives add {0} dummy {0}\n".format(scb_name))
struct_a = Structure((1, 1, 1), compability_version=compability_ver)
struct_a.set_block(
(0, 0, 0),
form_command_block_in_NBT_struct(
r"scoreboard players add {} {} 1".format(player, scb_name),
(0, 0, 0),
1,
1,
alwaysRun=False,
customName="显示进度条并加分",
compability_version_number=compability_ver,
),
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_start.mcstructure",
)
),
"wb+",
) as f:
struct_a.dump(f)
index_file.write(f"structure load {midi_cvt.midi_music_name}_start ~ ~ ~1\n")
pgb_struct, pgbSize, pgbNowPos = commands_to_structure(
midi_cvt.form_progress_bar(max_delay, scb_name, data_cfg.progressbar_style),
max_height - 1,
compability_version_=compability_ver,
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_pgb.mcstructure",
)
),
"wb+",
) as f:
pgb_struct.dump(f)
index_file.write(f"structure load {midi_cvt.midi_music_name}_pgb ~ ~1 ~1\n")
struct_a = Structure(
(1, 1, 1),
)
struct_a.set_block(
(0, 0, 0),
form_command_block_in_NBT_struct(
r"scoreboard players reset {} {}".format(player, scb_name),
(0, 0, 0),
1,
0,
alwaysRun=False,
customName="重置进度条计分板",
compability_version_number=compability_ver,
),
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_reset.mcstructure",
)
),
"wb+",
) as f:
struct_a.dump(f)
del struct_a, pgb_struct
index_file.write(
f"structure load {midi_cvt.midi_music_name}_reset ~{pgbSize[0] + 2} ~ ~1\n"
)
index_file.write(
f"structure load {midi_cvt.midi_music_name}_main ~{pgbSize[0] + 2} ~1 ~1\n"
)
else:
index_file.write(f"structure load {midi_cvt.midi_music_name}_main ~ ~ ~1\n")
index_file.close()
if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack"):
os.remove(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack")
compress_zipfile(
f"{data_cfg.dist_path}/temp/",
f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack",
)
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
return len(command_list), max_delay

View File

@@ -12,16 +12,16 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import datetime
import os import os
import uuid import uuid
import zipfile import zipfile
import datetime from typing import List, Literal, Union
from typing import List, Union, Literal
def compress_zipfile(sourceDir, outFilename, compression=8, exceptFile=None): def compress_zipfile(sourceDir, outFilename, compression=8, exceptFile=None):

View File

@@ -11,7 +11,7 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md

View File

@@ -9,16 +9,12 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
__all__ = [ __all__ = ["to_BDX_file_in_score", "to_BDX_file_in_delay"]
"to_BDX_file_in_score",
"to_BDX_file_in_delay"
]
__author__ = (("金羿", "Eilles Wan"),) __author__ = (("金羿", "Eilles Wan"),)
from .main import * from .main import to_BDX_file_in_delay, to_BDX_file_in_score

View File

@@ -7,7 +7,7 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
@@ -18,8 +18,14 @@ import brotli
from ...main import MidiConvert from ...main import MidiConvert
from ...subclass import SingleCommand from ...subclass import SingleCommand
from ..bdx import (bdx_move, commands_to_BDX_bytes, from ..bdx import (
form_command_block_in_BDX_bytes, x, y, z) bdx_move,
commands_to_BDX_bytes,
form_command_block_in_BDX_bytes,
x,
y,
z,
)
from ..main import ConvertConfig from ..main import ConvertConfig

View File

@@ -11,7 +11,7 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
@@ -25,5 +25,3 @@ 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)))

View File

@@ -1,132 +0,0 @@
# -*- 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
import json
import os
import shutil
from typing import Tuple
from ...main import MidiConvert
from ..archive import behavior_mcpack_manifest, compress_zipfile
from ..main import ConvertConfig
def to_function_addon_in_score(
midi_cvt: MidiConvert,
data_cfg: ConvertConfig,
scoreboard_name: str = "mscplay",
auto_reset: bool = False,
) -> Tuple[int, int]:
"""
将midi以计分播放器形式转换为我的世界函数附加包
Parameters
----------
midi_cvt: MidiConvert 对象
用于转换的MidiConvert对象
data_cfg: ConvertConfig 对象
部分转换通用参数
scoreboard_name: str
我的世界的计分板名称
auto_reset: bool
是否自动重置计分板
Returns
-------
tuple[int指令数量, int音乐总延迟]
"""
cmdlist, maxlen, maxscore = midi_cvt.to_command_list_in_score(
scoreboard_name, data_cfg.volume_ratio, data_cfg.speed_multiplier
)
# 当文件f夹{self.outputPath}/temp/functions存在时清空其下所有项目然后创建
if os.path.exists(f"{data_cfg.dist_path}/temp/functions/"):
shutil.rmtree(f"{data_cfg.dist_path}/temp/functions/")
os.makedirs(f"{data_cfg.dist_path}/temp/functions/mscplay")
# 写入manifest.json
with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f:
json.dump(
behavior_mcpack_manifest(
pack_description=f"{midi_cvt.midi_music_name} 音乐播放包MCFUNCTION(MCPACK) 计分播放器 - 由 音·创 生成",
pack_name=midi_cvt.midi_music_name + "播放",
modules_description=f"无 - 由 音·创 生成",
),
fp=f,
indent=4,
)
# 将命令列表写入文件
index_file = open(
f"{data_cfg.dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8"
)
for i in range(len(cmdlist)):
index_file.write(f"function mscplay/track{i + 1}\n")
with open(
f"{data_cfg.dist_path}/temp/functions/mscplay/track{i + 1}.mcfunction",
"w",
encoding="utf-8",
) as f:
f.write("\n".join([single_cmd.cmd for single_cmd in cmdlist[i]]))
index_file.writelines(
(
"scoreboard players add @a[scores={"
+ scoreboard_name
+ "=1..}] "
+ scoreboard_name
+ " 1\n",
(
"scoreboard players reset @a[scores={"
+ scoreboard_name
+ "="
+ str(maxscore + 20)
+ "..}]"
+ f" {scoreboard_name}\n"
)
if auto_reset
else "",
f"function mscplay/progressShow\n" if data_cfg.progressbar_style else "",
)
)
if data_cfg.progressbar_style:
with open(
f"{data_cfg.dist_path}/temp/functions/mscplay/progressShow.mcfunction",
"w",
encoding="utf-8",
) as f:
f.writelines(
"\n".join(
[
single_cmd.cmd
for single_cmd in midi_cvt.form_progress_bar(
maxscore, scoreboard_name, data_cfg.progressbar_style
)
]
)
)
index_file.close()
if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack"):
os.remove(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack")
compress_zipfile(
f"{data_cfg.dist_path}/temp/",
f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack",
)
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
return maxlen, maxscore

View File

@@ -11,13 +11,13 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from dataclasses import dataclass from dataclasses import dataclass
from typing import Tuple, Union, Literal from typing import Literal, Tuple, Union
from ..constants import DEFAULT_PROGRESSBAR_STYLE from ..constants import DEFAULT_PROGRESSBAR_STYLE

View File

@@ -9,16 +9,15 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
__all__ = [ __all__ = [
"to_mcstructure_file_in_delay", "to_mcstructure_file_in_delay",
"to_mcstructure_file_in_redstone_CD", "to_mcstructure_file_in_repeater",
] ]
__author__ = (("金羿", "Eilles Wan"),) __author__ = (("金羿", "Eilles Wan"),)
from .main import * from .main import to_mcstructure_file_in_delay, to_mcstructure_file_in_repeater

View File

@@ -7,18 +7,22 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import os import os
from typing import Literal 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,commands_to_redstone_delay_structure from ..mcstructure import (
COMPABILITY_VERSION_117,
COMPABILITY_VERSION_119,
commands_to_redstone_delay_structure,
commands_to_structure,
)
def to_mcstructure_file_in_delay( def to_mcstructure_file_in_delay(
@@ -46,8 +50,11 @@ def to_mcstructure_file_in_delay(
tuple[tuple[int,]结构大小, int音乐总延迟] tuple[tuple[int,]结构大小, int音乐总延迟]
""" """
if midi_cvt.enable_old_exe_format: compability_ver = (
raise CommandFormatError("使用mcstructure结构文件导出时不支持旧版本的指令格式。") COMPABILITY_VERSION_117
if midi_cvt.enable_old_exe_format
else COMPABILITY_VERSION_119
)
cmd_list, max_delay = midi_cvt.to_command_list_in_delay( cmd_list, max_delay = midi_cvt.to_command_list_in_delay(
data_cfg.volume_ratio, data_cfg.volume_ratio,
@@ -58,7 +65,9 @@ def to_mcstructure_file_in_delay(
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)
struct, size, end_pos = commands_to_structure(cmd_list, max_height - 1) struct, size, end_pos = commands_to_structure(
cmd_list, max_height - 1, compability_version_=compability_ver
)
with open( with open(
os.path.abspath( os.path.abspath(
@@ -71,12 +80,11 @@ def to_mcstructure_file_in_delay(
return size, max_delay return size, max_delay
def to_mcstructure_file_in_repeater(
def to_mcstructure_file_in_redstone_CD(
midi_cvt: MidiConvert, midi_cvt: MidiConvert,
data_cfg: ConvertConfig, data_cfg: ConvertConfig,
player: str = "@a", player: str = "@a",
axis_side: Literal["z+","z-","Z+","Z-","x+","x-","X+","X-"] = "z+", axis_side: Literal["z+", "z-", "Z+", "Z-", "x+", "x-", "X+", "X-"] = "z+",
basement_block: str = "concrete", basement_block: str = "concrete",
): ):
""" """
@@ -100,8 +108,11 @@ def to_mcstructure_file_in_redstone_CD(
tuple[tuple[int,]结构大小, int音乐总延迟] tuple[tuple[int,]结构大小, int音乐总延迟]
""" """
if midi_cvt.enable_old_exe_format: compability_ver = (
raise CommandFormatError("使用mcstructure结构文件导出时不支持旧版本的指令格式。") COMPABILITY_VERSION_117
if midi_cvt.enable_old_exe_format
else COMPABILITY_VERSION_119
)
cmd_list, max_delay, max_multiple_cmd = midi_cvt.to_command_list_in_delay( cmd_list, max_delay, max_multiple_cmd = midi_cvt.to_command_list_in_delay(
data_cfg.volume_ratio, data_cfg.volume_ratio,
@@ -112,7 +123,14 @@ def to_mcstructure_file_in_redstone_CD(
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)
struct, size, end_pos = commands_to_redstone_delay_structure(cmd_list,max_delay,max_multiple_cmd, basement_block, axis_side) struct, size, end_pos = commands_to_redstone_delay_structure(
cmd_list,
max_delay,
max_multiple_cmd,
basement_block,
axis_side,
compability_version_=compability_ver,
)
with open( with open(
os.path.abspath( os.path.abspath(
@@ -123,4 +141,3 @@ def to_mcstructure_file_in_redstone_CD(
struct.dump(f) struct.dump(f)
return size, max_delay return size, max_delay

View File

@@ -1,23 +0,0 @@
# -*- 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
__all__ = [
"to_mcstructure_addon_in_delay"
]
__author__ = (("金羿", "Eilles Wan"),)
from .main import *

View File

@@ -1,205 +0,0 @@
# -*- 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
import json
import os
import shutil
from TrimMCStruct import Structure
from ...exceptions import CommandFormatError
from ...main import MidiConvert
from ..archive import behavior_mcpack_manifest, compress_zipfile
from ..main import ConvertConfig
from ..mcstructure import commands_to_structure, form_command_block_in_NBT_struct
def to_mcstructure_addon_in_delay(
midi_cvt: MidiConvert,
data_cfg: ConvertConfig,
player: str = "@a",
max_height: int = 64,
):
"""
将midi以延迟播放器形式转换为mcstructure结构文件后打包成附加包并在附加包中生成相应地导入函数
Parameters
----------
midi_cvt: MidiConvert 对象
用于转换的MidiConvert对象
data_cfg: ConvertConfig 对象
部分转换通用参数
player: str
玩家选择器,默认为`@a`
max_height: int
生成结构最大高度
Returns
-------
tuple[int指令数量, int音乐总延迟]
"""
if midi_cvt.enable_old_exe_format:
raise CommandFormatError("使用mcstructure结构文件导出时不支持旧版本的指令格式。")
command_list, max_delay = midi_cvt.to_command_list_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)
# 当文件f夹{self.outputPath}/temp/存在时清空其下所有项目,然后创建
if os.path.exists(f"{data_cfg.dist_path}/temp/"):
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
os.makedirs(f"{data_cfg.dist_path}/temp/functions/")
os.makedirs(f"{data_cfg.dist_path}/temp/structures/")
# 写入manifest.json
with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f:
json.dump(
behavior_mcpack_manifest(
pack_description=f"{midi_cvt.midi_music_name} 音乐播放包MCSTRUCTURE(MCPACK) 延迟播放器 - 由 音·创 生成",
pack_name=midi_cvt.midi_music_name + "播放",
modules_description=f"无 - 由 音·创 生成",
),
fp=f,
indent=4,
)
# 将命令列表写入文件
index_file = open(
f"{data_cfg.dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8"
)
struct, size, end_pos = commands_to_structure(command_list, max_height - 1)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_main.mcstructure",
)
),
"wb+",
) as f:
struct.dump(f)
del struct
if data_cfg.progressbar_style:
scb_name = midi_cvt.midi_music_name[:3] + "Pgb"
index_file.write("scoreboard objectives add {0} dummy {0}\n".format(scb_name))
struct_a = Structure(
(1, 1, 1),
)
struct_a.set_block(
(0, 0, 0),
form_command_block_in_NBT_struct(
r"scoreboard players add {} {} 1".format(player, scb_name),
(0, 0, 0),
1,
1,
alwaysRun=False,
customName="显示进度条并加分",
),
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_start.mcstructure",
)
),
"wb+",
) as f:
struct_a.dump(f)
index_file.write(f"structure load {midi_cvt.midi_music_name}_start ~ ~ ~1\n")
pgb_struct, pgbSize, pgbNowPos = commands_to_structure(
midi_cvt.form_progress_bar(max_delay, scb_name, data_cfg.progressbar_style),
max_height - 1,
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_pgb.mcstructure",
)
),
"wb+",
) as f:
pgb_struct.dump(f)
index_file.write(f"structure load {midi_cvt.midi_music_name}_pgb ~ ~1 ~1\n")
struct_a = Structure(
(1, 1, 1),
)
struct_a.set_block(
(0, 0, 0),
form_command_block_in_NBT_struct(
r"scoreboard players reset {} {}".format(player, scb_name),
(0, 0, 0),
1,
0,
alwaysRun=False,
customName="重置进度条计分板",
),
)
with open(
os.path.abspath(
os.path.join(
data_cfg.dist_path,
"temp/structures/",
f"{midi_cvt.midi_music_name}_reset.mcstructure",
)
),
"wb+",
) as f:
struct_a.dump(f)
del struct_a, pgb_struct
index_file.write(
f"structure load {midi_cvt.midi_music_name}_reset ~{pgbSize[0] + 2} ~ ~1\n"
)
index_file.write(
f"structure load {midi_cvt.midi_music_name}_main ~{pgbSize[0] + 2} ~1 ~1\n"
)
else:
index_file.write(f"structure load {midi_cvt.midi_music_name}_main ~ ~ ~1\n")
index_file.close()
if os.path.exists(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack"):
os.remove(f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack")
compress_zipfile(
f"{data_cfg.dist_path}/temp/",
f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack",
)
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
return len(command_list), max_delay

View File

@@ -11,7 +11,7 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
@@ -23,16 +23,16 @@ from TrimMCStruct import Block, Structure, TAG_Byte, TAG_Long
from ..constants import x, y, z 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']): def antiaxis(axis: Literal["x", "z", "X", "Z"]):
return z if axis == x else x return z if axis == x else x
def forward_IER(forward: bool): def forward_IER(forward: bool):
return 1 if forward else -1 return 1 if forward else -1
AXIS_PARTICULAR_VALUE = { AXIS_PARTICULAR_VALUE = {
x: { x: {
True: 5, True: 5,
@@ -48,7 +48,19 @@ AXIS_PARTICULAR_VALUE = {
}, },
} }
def command_statevalue(axis_: Literal['x','y','z','X','Y','Z'],forward_:bool): # 1.19的结构兼容版本号
COMPABILITY_VERSION_119: int = 17959425
"""
Minecraft 1.19 兼容版本号
"""
# 1.17的结构兼容版本号
COMPABILITY_VERSION_117: int = 17879555
"""
Minecraft 1.17 兼容版本号
"""
def command_statevalue(axis_: Literal["x", "y", "z", "X", "Y", "Z"], forward_: bool):
return AXIS_PARTICULAR_VALUE[axis_.lower()][forward_] return AXIS_PARTICULAR_VALUE[axis_.lower()][forward_]
@@ -57,6 +69,7 @@ def form_note_block_in_NBT_struct(
coordinate: Tuple[int, int, int], coordinate: Tuple[int, int, int],
instrument: str = "note.harp", instrument: str = "note.harp",
powered: bool = False, powered: bool = False,
compability_version_number: int = COMPABILITY_VERSION_119,
): ):
"""生成音符盒方块 """生成音符盒方块
:param note: `int`(0~24) :param note: `int`(0~24)
@@ -87,10 +100,15 @@ def form_note_block_in_NBT_struct(
"z": coordinate[2], "z": coordinate[2],
} # type: ignore } # type: ignore
}, },
compability_version=compability_version_number,
) )
def form_repeater_in_NBT_struct(delay: int, facing: int): def form_repeater_in_NBT_struct(
delay: int,
facing: int,
compability_version_number: int = COMPABILITY_VERSION_119,
):
"""生成中继器方块 """生成中继器方块
:param facing: 朝向: :param facing: 朝向:
Z- 北 0 Z- 北 0
@@ -107,6 +125,7 @@ def form_repeater_in_NBT_struct(delay: int, facing: int):
"repeater_delay": delay, "repeater_delay": delay,
"direction": facing, "direction": facing,
}, },
compability_version=compability_version_number,
) )
@@ -121,6 +140,7 @@ def form_command_block_in_NBT_struct(
customName: str = "", customName: str = "",
executeOnFirstTick: bool = False, executeOnFirstTick: bool = False,
trackOutput: bool = True, trackOutput: bool = True,
compability_version_number: int = COMPABILITY_VERSION_119,
): ):
""" """
使用指定项目返回指定的指令方块结构 使用指定项目返回指定的指令方块结构
@@ -199,13 +219,14 @@ def form_command_block_in_NBT_struct(
"z": coordinate[2], "z": coordinate[2],
} # type: ignore } # type: ignore
}, },
compability_version=17959425, compability_version=compability_version_number,
) )
def commands_to_structure( def commands_to_structure(
commands: List[SingleCommand], commands: List[SingleCommand],
max_height: int = 64, max_height: int = 64,
compability_version_: int = COMPABILITY_VERSION_119,
): ):
""" """
:param commands: 指令列表 :param commands: 指令列表
@@ -218,7 +239,8 @@ def commands_to_structure(
) )
struct = Structure( struct = Structure(
(_sideLength, max_height, _sideLength), # 声明结构大小 size=(_sideLength, max_height, _sideLength), # 声明结构大小
compability_version=compability_version_,
) )
y_forward = True y_forward = True
@@ -255,6 +277,7 @@ def commands_to_structure(
customName=command.annotation_text, customName=command.annotation_text,
executeOnFirstTick=False, executeOnFirstTick=False,
trackOutput=True, trackOutput=True,
compability_version_number=compability_version_,
), ),
) )
@@ -284,12 +307,14 @@ def commands_to_structure(
(now_x, now_y, now_z), (now_x, now_y, now_z),
) )
def commands_to_redstone_delay_structure( def commands_to_redstone_delay_structure(
commands: List[SingleCommand], commands: List[SingleCommand],
delay_length: int, delay_length: int,
max_multicmd_length: int, max_multicmd_length: int,
base_block: str = "concrete", base_block: str = "concrete",
axis_: Literal["z+","z-","Z+","Z-","x+","x-","X+","X-"] = "z+", axis_: Literal["z+", "z-", "Z+", "Z-", "x+", "x-", "X+", "X-"] = "z+",
compability_version_: int = COMPABILITY_VERSION_119,
) -> Tuple[Structure, Tuple[int, int, int], Tuple[int, int, int]]: ) -> Tuple[Structure, Tuple[int, int, int], Tuple[int, int, int]]:
""" """
:param commands: 指令列表 :param commands: 指令列表
@@ -322,70 +347,141 @@ def commands_to_redstone_delay_structure(
else: else:
raise ValueError(f"axis_({axis_}) 参数错误。") raise ValueError(f"axis_({axis_}) 参数错误。")
goahead = forward_IER(forward) goahead = forward_IER(forward)
struct = Structure( command_actually_length = sum([int(bool(cmd.delay)) for cmd in commands])
(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 a = 1
a_max = 0
total_cmd = 0
for cmd in commands:
# print("\r 正在进行处理:",end="")
if cmd.delay > 2:
a_max = max(a,a_max)
total_cmd += (a := 1)
else:
a += 1
struct = Structure(
size=(
round(delay_length / 2 + total_cmd)
if extensioon_direction == x
else a_max,
3,
round(delay_length / 2 + total_cmd)
if extensioon_direction == z
else a_max,
),
fill=Block("minecraft", "air", compability_version=compability_version_),
compability_version=compability_version_,
)
pos_now = {
x: ((1 if extensioon_direction == x else 0) if forward else struct.size[0]),
y: 0,
z: ((1 if extensioon_direction == z else 0) if forward else struct.size[2]),
}
chain_list = 0
# print("结构元信息设定完毕")
for cmd in commands: for cmd in commands:
single_repeater_value = round(cmd.delay / 2) % 4 - 1 # print("\r 正在进行处理:",end="")
additional_repeater = round(cmd.delay / 2)// 4 if cmd.delay > 1:
for i in range(additional_repeater): # print("\rdelay > 0",end='')
single_repeater_value = int(cmd.delay / 2) % 4 - 1
additional_repeater = int(cmd.delay / 2 // 4)
for i in range(additional_repeater):
struct.set_block(
tuple(pos_now.values()), # type: ignore
Block(
"minecraft",
base_block,
compability_version=compability_version_,
),
)
struct.set_block(
(pos_now[x], 1, pos_now[z]),
form_repeater_in_NBT_struct(
delay=3,
facing=repeater_facing,
compability_version_number=compability_version_,
),
)
pos_now[extensioon_direction] += goahead
if single_repeater_value >= 0:
struct.set_block(
tuple(pos_now.values()), # type: ignore
Block(
"minecraft",
base_block,
compability_version=compability_version_,
),
)
struct.set_block(
(pos_now[x], 1, pos_now[z]),
form_repeater_in_NBT_struct(
delay=single_repeater_value,
facing=repeater_facing,
compability_version_number=compability_version_,
),
)
pos_now[extensioon_direction] += goahead
struct.set_block( struct.set_block(
tuple(pos_now.values()), (pos_now[x], 1, pos_now[z]),
Block("minecraft", base_block,), 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=cmd.delay % 2,
customName=cmd.annotation_text,
compability_version_number=compability_version_,
),
) )
struct.set_block( struct.set_block(
(pos_now[x],1,pos_now[z]), (pos_now[x], 2, pos_now[z]),
form_repeater_in_NBT_struct( Block(
delay=3, "minecraft",
facing=repeater_facing, "redstone_wire",
compability_version=compability_version_,
), ),
) )
pos_now[extensioon_direction] += goahead pos_now[extensioon_direction] += goahead
first_impluse = True chain_list = 1
if single_repeater_value >= 0:
else:
# print(pos_now)
now_pos_copy = pos_now.copy()
now_pos_copy[extensioon_direction] -= goahead
now_pos_copy[aside_direction] += chain_list
# print(pos_now,"\n=========")
struct.set_block( struct.set_block(
tuple(pos_now.values()), (now_pos_copy[x], 1, now_pos_copy[z]),
Block("minecraft", base_block,), form_command_block_in_NBT_struct(
) command=cmd.command_text,
struct.set_block( coordinate=(now_pos_copy[x], 1, now_pos_copy[z]),
(pos_now[x],1,pos_now[z]), particularValue=command_statevalue(extensioon_direction, forward),
form_repeater_in_NBT_struct( # impluse= (0 if first_impluse else 2),
delay=single_repeater_value, impluse=0,
facing=repeater_facing, condition=False,
alwaysRun=False,
tickDelay=cmd.delay % 2,
customName=cmd.annotation_text,
compability_version_number=compability_version_,
), ),
) )
pos_now[extensioon_direction] += goahead struct.set_block(
first_impluse = True (now_pos_copy[x], 2, now_pos_copy[z]),
struct.set_block( Block(
(pos_now[x],1,pos_now[z]), "minecraft",
form_command_block_in_NBT_struct( "redstone_wire",
command=cmd.command_text, compability_version=compability_version_,
coordinate=(pos_now[x],1,pos_now[z]), ),
particularValue=command_statevalue(extensioon_direction, forward), )
# impluse= (0 if first_impluse else 2), chain_list += 1
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())
return struct, struct.size, tuple(pos_now.values()) # type: ignore

View File

@@ -11,7 +11,7 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
@@ -19,6 +19,7 @@ Terms & Conditions: License.md in the root directory
from ..exceptions import NotDefineProgramError, ZeroSpeedError from ..exceptions import NotDefineProgramError, ZeroSpeedError
from ..main import MidiConvert from ..main import MidiConvert
from ..subclass import SingleCommand from ..subclass import SingleCommand
from ..utils import inst_to_souldID_withX, perc_inst_to_soundID_withX
# 你以为写完了吗?其实并没有 # 你以为写完了吗?其实并没有
@@ -68,15 +69,15 @@ def to_note_list(
elif msg[0] == "NoteS": elif msg[0] == "NoteS":
try: try:
soundID, _X = ( soundID, _X = (
midi_cvt.perc_inst_to_soundID_withX(InstID) perc_inst_to_soundID_withX(InstID)
if SpecialBits if SpecialBits
else midi_cvt.inst_to_souldID_withX(InstID) else inst_to_souldID_withX(InstID)
) )
except UnboundLocalError as E: except UnboundLocalError as E:
soundID, _X = ( soundID, _X = (
midi_cvt.perc_inst_to_soundID_withX(-1) perc_inst_to_soundID_withX(-1)
if SpecialBits if SpecialBits
else midi_cvt.inst_to_souldID_withX(-1) else inst_to_souldID_withX(-1)
) )
score_now = round(msg[-1] / float(speed) / 50) score_now = round(msg[-1] / float(speed) / 50)
# print(score_now) # print(score_now)

View File

@@ -11,9 +11,8 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import nbtschematic import nbtschematic

View File

@@ -9,7 +9,7 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md

View File

@@ -7,7 +7,7 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md

View File

@@ -11,31 +11,30 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
import fcwslib import fcwslib
# 这个库有问题,正在检修 # 这个库有问题,正在检修
class Plugin(fcwslib.Plugin): class Plugin(fcwslib.Plugin):
async def on_connect(self) -> None: async def on_connect(self) -> None:
print('对象已被连接') print("对象已被连接")
await self.send_command('list', callback=self.list) await self.send_command("list", callback=self.list)
await self.subscribe('PlayerMessage', callback=self.player_message) await self.subscribe("PlayerMessage", callback=self.player_message)
async def on_disconnect(self) -> None: async def on_disconnect(self) -> None:
print('对象停止连接') print("对象停止连接")
async def on_receive(self, response) -> None: async def on_receive(self, response) -> None:
print('已接收非常规回复 {}'.format(response)) print("已接收非常规回复 {}".format(response))
async def list(self, response) -> None: async def list(self, response) -> None:
print('已收取指令执行回复 {}'.format(response)) print("已收取指令执行回复 {}".format(response))
async def player_message(self, response) -> None: async def player_message(self, response) -> None:
print('已收取玩家事件回复 {}'.format(response)) print("已收取玩家事件回复 {}".format(response))

View File

@@ -9,7 +9,7 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md

View File

@@ -7,7 +7,7 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md

View File

@@ -11,15 +11,14 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from typing import Dict, List, Tuple, Union
from .exceptions import * from .exceptions import *
from .main import MidiConvert, mido from .main import MidiConvert, mido
from .subclass import * from .subclass import *
from .types import ChannelType
from .utils import * from .utils import *
@@ -29,6 +28,82 @@ class ObsoleteMidiConvert(MidiConvert):
这些破烂老代码能跑得起来就是谢天谢地,你们还指望我怎么样?这玩意真的不会再维护了,我发誓! 这些破烂老代码能跑得起来就是谢天谢地,你们还指望我怎么样?这玩意真的不会再维护了,我发誓!
""" """
def to_music_channels(
self,
) -> ChannelType:
"""
使用金羿的转换思路将midi解析并转换为频道信息字典
Returns
-------
以频道作为分割的Midi信息字典:
Dict[int,Dict[int,List[Union[Tuple[Literal["PgmC"], int, int],Tuple[Literal["NoteS"], int, int, int],Tuple[Literal["NoteE"], int, int],]],],]
"""
if self.midi is None:
raise MidiUnboundError(
"你是否正在使用的是一个由 copy_important 生成的MidiConvert对象这是不可复用的。"
)
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
midi_channels: ChannelType = empty_midi_channels()
tempo = mido.midifiles.midifiles.DEFAULT_TEMPO
# 我们来用通道统计音乐信息
# 但是是用分轨的思路的
for track_no, track in enumerate(self.midi.tracks):
microseconds = 0
if not track:
continue
note_queue = empty_midi_channels(staff=[])
for msg in track:
if msg.time != 0:
microseconds += msg.time * tempo / self.midi.ticks_per_beat / 1000
if msg.is_meta:
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] = []
except AttributeError as E:
print(msg, E)
if msg.type == "program_change":
midi_channels[msg.channel][track_no].append(
("PgmC", msg.program, microseconds)
)
elif msg.type == "note_on" and msg.velocity != 0:
midi_channels[msg.channel][track_no].append(
("NoteS", msg.note, msg.velocity, microseconds)
)
elif (msg.type == "note_on" and msg.velocity == 0) or (
msg.type == "note_off"
):
midi_channels[msg.channel][track_no].append(
("NoteE", msg.note, microseconds)
)
"""整合后的音乐通道格式
每个通道包括若干消息元素其中逃不过这三种:
1 切换乐器消息
("PgmC", 切换后的乐器ID: int, 距离演奏开始的毫秒)
2 音符开始消息
("NoteS", 开始的音符ID, 力度(响度), 距离演奏开始的毫秒)
3 音符结束消息
("NoteE", 结束的音符ID, 距离演奏开始的毫秒)"""
del tempo, self.channels
self.channels = midi_channels
# [print([print(no,tno,sum([True if i[0] == 'NoteS' else False for i in track])) for tno,track in cna.items()]) if cna else False for no,cna in midi_channels.items()]
return midi_channels
def to_command_list_method1( def to_command_list_method1(
self, self,
scoreboard_name: str = "mscplay", scoreboard_name: str = "mscplay",
@@ -78,9 +153,9 @@ class ObsoleteMidiConvert(MidiConvert):
) )
maxscore = max(maxscore, nowscore) maxscore = max(maxscore, nowscore)
if msg.channel == 9: if msg.channel == 9:
soundID, _X = self.perc_inst_to_soundID_withX(instrumentID) soundID, _X = perc_inst_to_soundID_withX(instrumentID)
else: else:
soundID, _X = self.inst_to_souldID_withX(instrumentID) soundID, _X = inst_to_souldID_withX(instrumentID)
singleTrack.append( singleTrack.append(
"execute @a[scores={" "execute @a[scores={"
@@ -99,10 +174,8 @@ class ObsoleteMidiConvert(MidiConvert):
return [tracks, commands, maxscore] return [tracks, commands, maxscore]
def _toCmdList_m1( def _toCmdList_m1(
self, self, scoreboardname: str = "mscplay", volume: float = 1.0, speed: float = 1.0
scoreboardname: str = "mscplay", ) -> list:
volume: float = 1.0,
speed: float = 1.0) -> list:
""" """
使用Dislink Sforza的转换思路将midi转换为我的世界命令列表 使用Dislink Sforza的转换思路将midi转换为我的世界命令列表
:param scoreboardname: 我的世界的计分板名称 :param scoreboardname: 我的世界的计分板名称
@@ -119,8 +192,7 @@ class ObsoleteMidiConvert(MidiConvert):
commands = 0 commands = 0
maxscore = 0 maxscore = 0
for i, track in enumerate(self.midi.tracks): for i, track in enumerate(self.midi.tracks): # type:ignore
ticks = 0 ticks = 0
instrumentID = 0 instrumentID = 0
singleTrack = [] singleTrack = []
@@ -137,18 +209,18 @@ class ObsoleteMidiConvert(MidiConvert):
instrumentID = msg.program instrumentID = msg.program
if msg.type == "note_on" and msg.velocity != 0: if msg.type == "note_on" and msg.velocity != 0:
nowscore = round( nowscore = round(
(ticks * tempo) (ticks * tempo) / ((self.midi.ticks_per_beat * float(speed)) * 50000) # type: ignore
/ ((self.midi.ticks_per_beat * float(speed)) * 50000)
) )
maxscore = max(maxscore, nowscore) maxscore = max(maxscore, nowscore)
soundID, _X = self.__Inst2soundID_withX(instrumentID) soundID, _X = inst_to_souldID_withX(instrumentID)
singleTrack.append( singleTrack.append(
"execute @a[scores={" + "execute @a[scores={"
str(scoreboardname) + + str(scoreboardname)
"=" + + "="
str(nowscore) + + 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)}") + 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 commands += 1
if len(singleTrack) != 0: if len(singleTrack) != 0:
tracks.append(singleTrack) tracks.append(singleTrack)
@@ -157,7 +229,7 @@ class ObsoleteMidiConvert(MidiConvert):
# 原本这个算法的转换效果应该和上面的算法相似的 # 原本这个算法的转换效果应该和上面的算法相似的
def _toCmdList_m2( def _toCmdList_m2(
self: MidiConvert, self,
scoreboard_name: str = "mscplay", scoreboard_name: str = "mscplay",
MaxVolume: float = 1.0, MaxVolume: float = 1.0,
speed: float = 1.0, speed: float = 1.0,
@@ -194,16 +266,16 @@ class ObsoleteMidiConvert(MidiConvert):
nowTrack = [] nowTrack = []
for track_no, track in self.channels[i].items(): for track_no, track in self.channels[i].items(): # type: ignore
for msg in track: for msg in track:
if msg[0] == "PgmC": if msg[0] == "PgmC":
InstID = msg[1] InstID = msg[1]
elif msg[0] == "NoteS": elif msg[0] == "NoteS":
soundID, _X = ( soundID, _X = (
self.perc_inst_to_soundID_withX(msg[1]) perc_inst_to_soundID_withX(msg[1])
if SpecialBits if SpecialBits
else self.inst_to_souldID_withX(InstID) else inst_to_souldID_withX(InstID)
) )
score_now = round(msg[-1] / float(speed) / 50) score_now = round(msg[-1] / float(speed) / 50)
maxScore = max(maxScore, score_now) maxScore = max(maxScore, score_now)
@@ -226,7 +298,7 @@ class ObsoleteMidiConvert(MidiConvert):
return tracks, cmdAmount, maxScore return tracks, cmdAmount, maxScore
def _toCmdList_withDelay_m1( def _toCmdList_withDelay_m1(
self: MidiConvert, self,
MaxVolume: float = 1.0, MaxVolume: float = 1.0,
speed: float = 1.0, speed: float = 1.0,
player: str = "@a", player: str = "@a",
@@ -267,7 +339,7 @@ class ObsoleteMidiConvert(MidiConvert):
(ticks * tempo) (ticks * tempo)
/ ((self.midi.ticks_per_beat * float(speed)) * 50000) / ((self.midi.ticks_per_beat * float(speed)) * 50000)
) )
soundID, _X = self.inst_to_souldID_withX(instrumentID) soundID, _X = inst_to_souldID_withX(instrumentID)
try: try:
tracks[now_tick].append( tracks[now_tick].append(
self.execute_cmd_head.format(player) self.execute_cmd_head.format(player)
@@ -302,7 +374,7 @@ class ObsoleteMidiConvert(MidiConvert):
return [results, max(all_ticks)] return [results, max(all_ticks)]
def _toCmdList_withDelay_m2( def _toCmdList_withDelay_m2(
self: MidiConvert, self,
MaxVolume: float = 1.0, MaxVolume: float = 1.0,
speed: float = 1.0, speed: float = 1.0,
player: str = "@a", player: str = "@a",
@@ -334,16 +406,16 @@ class ObsoleteMidiConvert(MidiConvert):
else: else:
SpecialBits = False SpecialBits = False
for track_no, track in self.channels[i].items(): for track_no, track in self.channels[i].items(): # type: ignore
for msg in track: for msg in track:
if msg[0] == "PgmC": if msg[0] == "PgmC":
InstID = msg[1] InstID = msg[1]
elif msg[0] == "NoteS": elif msg[0] == "NoteS":
soundID, _X = ( soundID, _X = (
self.perc_inst_to_soundID_withX(msg[1]) perc_inst_to_soundID_withX(msg[1])
if SpecialBits if SpecialBits
else self.inst_to_souldID_withX(InstID) else inst_to_souldID_withX(InstID)
) )
score_now = round(msg[-1] / float(speed) / 50) score_now = round(msg[-1] / float(speed) / 50)

View File

@@ -12,7 +12,7 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
@@ -21,6 +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, volume2distance
@dataclass(init=False) @dataclass(init=False)
@@ -45,14 +46,18 @@ class SingleNote:
track_no: int track_no: int
"""音符所处的音轨""" """音符所处的音轨"""
percussive: bool
"""是否为打击乐器"""
def __init__( def __init__(
self, self,
instrument: int, instrument: int,
pitch: int, pitch: int,
velocity: int, velocity: int,
startTime: int, startime: int,
lastTime: int, lastime: int,
track_number: int = 0, track_number: int = 0,
is_percussion: Optional[bool] = None,
): ):
"""用于存储单个音符的类 """用于存储单个音符的类
:param instrument 乐器编号 :param instrument 乐器编号
@@ -67,50 +72,127 @@ class SingleNote:
"""音符编号""" """音符编号"""
self.velocity: int = velocity self.velocity: int = velocity
"""力度/响度""" """力度/响度"""
self.start_time: int = startTime self.start_time: int = startime
"""开始之时 ms""" """开始之时 ms"""
self.duration: int = lastTime self.duration: int = lastime
"""音符持续时间 ms""" """音符持续时间 ms"""
self.track_no: int = track_number self.track_no: int = track_number
"""音符所处的音轨""" """音符所处的音轨"""
self.percussive = (
(is_percussion in PERCUSSION_INSTRUMENT_LIST)
if (is_percussion is None)
else is_percussion
)
"""是否为打击乐器"""
@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 ( def get_mc_pitch(self) -> float:
f"Note(inst = {self.inst}, pitch = {self.note}, velocity = {self.velocity}, " self.mc_sound_ID, _X = (
f"startTime = {self.start_time}, lastTime = {self.duration}, )" 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 "",
self.inst,
"" if self.percussive else "Pitch = {}, ".format(self.pitch),
self.start_time,
self.duration,
", Track = {}".format(self.track_no) if is_track else "",
) )
def __tuple__(self): def __tuple__(self):
return self.inst, self.note, self.velocity, self.start_time, self.duration return (
(
self.percussive,
self.inst,
self.velocity,
self.start_time,
self.duration,
self.track_no,
)
if self.percussive
else (
self.percussive,
self.inst,
self.note,
self.velocity,
self.start_time,
self.duration,
self.track_no,
)
)
def __dict__(self): def __dict__(self):
return { return (
"inst": self.inst, {
"pitch": self.note, "Percussive": self.percussive,
"velocity": self.velocity, "Instrument": self.inst,
"startTime": self.start_time, "Velocity": self.velocity,
"lastTime": self.duration, "StartTime": self.start_time,
} "Duration": self.duration,
"Track": self.track_no,
}
if self.percussive
else {
"Percussive": self.percussive,
"Instrument": self.inst,
"Pitch": self.note,
"Velocity": self.velocity,
"StartTime": self.start_time,
"Duration": self.duration,
"Track": self.track_no,
}
)
def __eq__(self, other) -> bool: def __eq__(self, other) -> bool:
if not isinstance(other, self.__class__): if not isinstance(other, self.__class__):
return False return False
return self.__str__() == other.__str__() return self.__str__() == other.__str__()
def to_command(self, volume_percentage: float = 1) -> str:
"""
将音符转为播放的指令
:param volume_percentage:int 音量占比(0,1]
:return str指令
"""
self.mc_sound_ID, _X = (
perc_inst_to_soundID_withX(self.inst)
if self.percussive
else inst_to_souldID_withX(self.inst)
)
# delaytime_now = round(self.start_time / float(speed) / 50)
self.mc_pitch = "" if self.percussive else 2 ** ((self.note - 60 - _X) / 12)
self.mc_distance_volume = volume2distance(self.velocity * volume_percentage)
return "playsound {} @s ^ ^ ^{} {} {}".format(
self.mc_sound_ID,
self.mc_distance_volume,
volume_percentage,
self.mc_pitch,
)
@dataclass(init=False) @dataclass(init=False)
class SingleCommand: class SingleCommand:

64
Musicreater/types.py Normal file
View File

@@ -0,0 +1,64 @@
# -*- 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 typing import Any, Dict, List, Literal, Optional, Tuple, Union
import mido
from .subclass import SingleNote
ProgressStyle = Tuple[str, Tuple[str, str]]
"""
进度条样式类型
"""
VoidMido = Union[mido.MidiFile, None] # void mido
"""
空Midi类类型
"""
NoteChannelType = Dict[
int,
List[SingleNote,],
]
"""
频道信息类型
Dict[int,Dict[int,List[SingleNote,],],]
"""
ChannelType = 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],]],],]
"""

View File

@@ -11,11 +11,14 @@ Copyright © 2023 all the developers of Musicreater
Terms & Conditions: License.md in the root directory Terms & Conditions: License.md in the root directory
""" """
# 睿组织 开发交流群 861684859 # 睿组织 开发交流群 861684859
# Email TriM-Organization@hotmail.com # Email TriM-Organization@hotmail.com
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md # 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
from typing import Any, Dict import math
from .constants import PERCUSSION_INSTRUMENT_TABLE, PITCHED_INSTRUMENT_TABLE
from typing import Any, Dict, Tuple
def mctick2timestr(mc_tick: int) -> str: def mctick2timestr(mc_tick: int) -> str:
@@ -37,3 +40,80 @@ def empty_midi_channels(channel_count: int = 17, staff: Any = {}) -> Dict[int, A
) # 这告诉我们你不能忽略任何一个复制的序列因为它真的我哭死折磨我一整天全在这个bug上了 ) # 这告诉我们你不能忽略任何一个复制的序列因为它真的我哭死折磨我一整天全在这个bug上了
for i in range(channel_count) for i in range(channel_count)
) )
def inst_to_souldID_withX(
instrumentID: int,
) -> Tuple[str, int]:
"""
返回midi的乐器ID对应的我的世界乐器名对于音域转换算法如下
2**( ( msg.note - 60 - X ) / 12 ) 即为MC的音高其中
X的取值随乐器不同而变化
竖琴harp、电钢琴pling、班卓琴banjo、方波bit、颤音琴iron_xylophone 的时候为6
吉他的时候为7
贝斯bass、迪吉里杜管didgeridoo的时候为8
长笛flute、牛铃cou_bell的时候为5
钟琴bell、管钟chime、木琴xylophone的时候为4
而存在一些打击乐器bd(basedrum)、hat、snare没有音域则没有X那么我们返回7即可
Parameters
----------
instrumentID: int
midi的乐器ID
Returns
-------
tuple(str我的世界乐器名, int转换算法中的X)
"""
try:
return PITCHED_INSTRUMENT_TABLE[instrumentID]
except KeyError:
return "note.flute", 5
def perc_inst_to_soundID_withX(instrumentID: int) -> Tuple[str, int]:
"""
对于Midi第10通道所对应的打击乐器返回我的世界乐器名
Parameters
----------
instrumentID: int
midi的乐器ID
Returns
-------
tuple(str我的世界乐器名, int转换算法中的X)
"""
try:
return PERCUSSION_INSTRUMENT_TABLE[instrumentID]
except KeyError:
return "note.bd", 7
# 明明已经走了
# 凭什么还要在我心里留下缠绵缱绻
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
)

View File

@@ -10,7 +10,7 @@
<h3 align="center">一款免费开源的我的世界数字音频转换库</h3> <h3 align="center">一款免费开源的我的世界数字音频转换库</h3>
<p align="center"> <p align="center">
<img src="https://forthebadge.com/images/badges/built-with-love.svg"> <img src="https://img.shields.io/badge/BUILD%20WITH%20LOVE-FF3432?style=for-the-badge">
<a href='https://gitee.com/TriM-Organization/Musicreater'> <a href='https://gitee.com/TriM-Organization/Musicreater'>
<img align="right" src='https://gitee.com/TriM-Organization/Musicreater/widgets/widget_1.svg' alt='Fork me on Gitee'> <img align="right" src='https://gitee.com/TriM-Organization/Musicreater/widgets/widget_1.svg' alt='Fork me on Gitee'>
</img> </img>
@@ -42,21 +42,15 @@
- 使用 pypi - 使用 pypi
```bash ```bash
pip install Musicreater pip install --upgrade Musicreater
``` ```
- 如果出现错误可以尝试 - 如果无法更新最新可以尝试
```bash ```bash
pip install -i https://pypi.python.org/simple Musicreater pip install --upgrade -i https://pypi.python.org/simple Musicreater
``` ```
- 升级 - 克隆仓库并安装**不推荐**
```bash
pip install -i https://pypi.python.org/simple Musicreater --upgrade
```
- 克隆仓库并安装
```bash ```bash
git clone https://gitee.com/TriM-Organization/Musicreater.git git clone https://gitee.com/TriM-Organization/Musicreater.git
cd Musicreater cd Musicreater
@@ -73,9 +67,11 @@
## 作者 ✒ ## 作者 ✒
金羿 Eilles我的世界基岩版指令师个人开发者B 站不知名 UP 江西在校高中生 **金羿 Eilles**我的世界基岩版指令师个人开发者B 站不知名 UP 江西在校高中生
诸葛亮与八卦阵 bgArray我的世界基岩版玩家喜欢编程和音乐深圳初二学生 **诸葛亮与八卦阵 bgArray**我的世界基岩版玩家喜欢编程和音乐深圳初二学生
**偷吃不是Touch Touch**我的世界基岩版指令师提供 BDX 导入测试支持
## 致谢 🙏 ## 致谢 🙏
@@ -84,18 +80,18 @@
- 感谢 **昀梦**\<QQ1515399885\> 找出指令生成错误 bug 并指正 - 感谢 **昀梦**\<QQ1515399885\> 找出指令生成错误 bug 并指正
- 感谢由 **Charlie_Ping 查理平** 带来的 BDX 文件转换参考以及 MIDI-我的世界对应乐器 参考表格 - 感谢由 **Charlie_Ping 查理平** 带来的 BDX 文件转换参考以及 MIDI-我的世界对应乐器 参考表格
- 感谢由 **[CMA_2401PT](https://github.com/CMA2401PT)** 为我们的软件开发的一些方面进行指导同时我们参考了他的 BDXworkshop 作为 BDX 结构编辑的参考 - 感谢由 **[CMA_2401PT](https://github.com/CMA2401PT)** 为我们的软件开发的一些方面进行指导同时我们参考了他的 BDXworkshop 作为 BDX 结构编辑的参考
- 感谢由 **[Dislink Sforza](https://github.com/Dislink) 断联·斯福尔扎**\<QQ1600515314\> 带来的 midi 音色解析以及转换指令的算法我们将其改编并应用同时感谢他的[网页版转换器](https://dislink.github.io/midi2bdx/)给我们的开发与更新带来巨大的压力和动力让我们在原本一骑绝尘的摸鱼道路上转向开发希望他能考上一个理想的大学 - 感谢由 **[Dislink Sforza](https://github.com/Dislink) 断联·斯福尔扎**\<QQ1600515314\> 带来的 midi 音色解析以及转换指令的算法我们将其改编并应用同时感谢他的[网页版转换器](https://dislink.github.io/midi2bdx/)给我们的开发与更新带来巨大的压力和动力让我们在原本一骑绝尘的摸鱼道路上转向开发
- 感谢 **Touch 偷吃**\<QQ1793537164\> 提供的 BDX 导入测试支持并对程序的改进提供了丰富的意见同时也感谢他的不断尝试新的内容使我们的排错更进一步
- 感谢 **Mono**\<QQ738893087\> 反馈安装时的问题辅助我们找到了视窗操作系统下的兼容性问题感谢其反馈延迟播放器出现的重大问题让我们得以修改全部延迟播放错误尤其感谢他对于我们的软件的大力宣传 - 感谢 **Mono**\<QQ738893087\> 反馈安装时的问题辅助我们找到了视窗操作系统下的兼容性问题感谢其反馈延迟播放器出现的重大问题让我们得以修改全部延迟播放错误尤其感谢他对于我们的软件的大力宣传
- 感谢 **Ammelia 艾米利亚**\<QQ2838334637\> 敦促我们进行新的功能开发并为新功能提出了非常优秀的大量建议以及提供的 BDX 导入测试支持为我们的新结构生成算法提供了大量的实际理论支持 - 感谢 **Ammelia 艾米利亚**\<QQ2838334637\> 敦促我们进行新的功能开发并为新功能提出了非常优秀的大量建议以及提供的 BDX 导入测试支持为我们的新结构生成算法提供了大量的实际理论支持
- 感谢 **[神羽](https://gitee.com/snowykami) [SnowyKami](https://github.com/snowyfirefly)** 对我们项目的支持与宣传希望他能考得一所优秀的大学 - 感谢 **[神羽](https://gitee.com/snowykami) [SnowyKami](https://github.com/snowyfirefly)** 对我们项目的支持与宣传非常感谢他为我们提供的服务器
- 感谢 **指令师\_苦力怕 playjuice123**\<QQ240667197\> 为我们的程序找出错误并提醒我们修复一个一直存在的大 bug - 感谢 **指令师\_苦力怕 playjuice123**\<QQ240667197\> 为我们的程序找出错误并提醒我们修复一个一直存在的大 bug
- 感谢 **雷霆**\<QQ3555268519\> 用他那令所有开发者都大为光火的操作方法为我们的程序找出错误并提醒修复 bug - 感谢 **雷霆**\<QQ3555268519\> 用他那令所有开发者都大为光火的操作方法为我们的程序找出错误并提醒修复 bug
- 感谢 **小埋**\<2039310975\> 反馈附加包生成时缺少描述和标题的问题 - 感谢 **小埋**\<QQ2039310975\> 反馈附加包生成时缺少描述和标题的问题
- <table><tr><td>感谢 **油炸**\<QQ2836146704\> 激励我们不断开发新的内容</td><td><img height="50" src="https://foruda.gitee.com/images/1695478907647543027/08ea9909_9911226.jpeg"></td></tr></table>
> 感谢广大群友为此程序提供的测试等支持 > 感谢广大群友为此提供的测试和建议
> >
> 若您对我们有所贡献但您的名字没有显示在此列表中请联系我们 > 若您对我们有所贡献但您的名字没有出现在此列表中请联系我们
## 联系 📞 ## 联系 📞
@@ -103,6 +99,8 @@
如果需要与开发组进行交流欢迎加入我们的[开发闲聊 Q ](https://jq.qq.com/?_wv=1027&k=hpeRxrYr) 如果需要与开发组进行交流欢迎加入我们的[开发闲聊 Q ](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)
亦可以联系我们[睿乐组织官方邮箱](mailto:TriM-Organization@hotmail.com)取得进一步联系
--- ---
此项目并非一个官方 我的世界_Minecraft_项目 此项目并非一个官方 我的世界_Minecraft_项目

View File

@@ -10,7 +10,7 @@
<h3 align="center">A free open-source library of converting digital music files into <i>Minecraft</i> formats.</h3> <h3 align="center">A free open-source library of converting digital music files into <i>Minecraft</i> formats.</h3>
<p align="center"> <p align="center">
<img src="https://forthebadge.com/images/badges/built-with-love.svg"> <img src="https://img.shields.io/badge/BUILD%20WITH%20LOVE-FF3432?style=for-the-badge">
</img> </img>
<p> <p>
@@ -28,7 +28,7 @@
[简体中文 🇨🇳](README.md) | English🇬🇧 [简体中文 🇨🇳](README.md) | English🇬🇧
**Notice that the language translation of _Musicreater_ may be a little SLOW.** **Notice that the localizations of documents may NOT be up-to-date.**
## Introduction🚀 ## Introduction🚀
@@ -41,21 +41,15 @@ Welcome to join our QQ group: [861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr
- Via pypi - Via pypi
```bash ```bash
pip install Musicreater pip install Musicreater --upgrade
``` ```
- If not work, also try: - If above command cannot fetch latest version, try:
```bash
pip install -i https://pypi.python.org/simple Musicreater
```
- Update:
```bash ```bash
pip install -i https://pypi.python.org/simple Musicreater --upgrade pip install -i https://pypi.python.org/simple Musicreater --upgrade
``` ```
- Clone repo and Install: - Clone repo and Install (**NOT RECOMMANDED**):
```bash ```bash
git clone https://github.com/TriM-Organization/Musicreater.git git clone https://github.com/TriM-Organization/Musicreater.git
cd Musicreater cd Musicreater
@@ -65,7 +59,7 @@ Welcome to join our QQ group: [861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr
Commands such as `python``pip` could be changed to some like `python3` or `pip3` according to the difference of platforms. Commands such as `python``pip` could be changed to some like `python3` or `pip3` according to the difference of platforms.
## Documentation📄 ## Documentation 📄
(Not in English yet) (Not in English yet)
@@ -73,37 +67,42 @@ Commands such as `python`、`pip` could be changed to some like `python3` or `pi
[仓库 API 文档](./docs/%E5%BA%93%E7%9A%84%E7%94%9F%E6%88%90%E4%B8%8E%E5%8A%9F%E8%83%BD%E6%96%87%E6%A1%A3.md) [仓库 API 文档](./docs/%E5%BA%93%E7%9A%84%E7%94%9F%E6%88%90%E4%B8%8E%E5%8A%9F%E8%83%BD%E6%96%87%E6%A1%A3.md)
### Authors✒ ### Authors
Eilles (金羿)A senior high school student, individual developer, unfamous Bilibili UPer, which knows a little about commands in _Minecraft: Bedrock Edition_ **Eilles (金羿)**A senior high school student, individual developer, unfamous Bilibili UPer, which knows a little about commands in _Minecraft: Bedrock Edition_
bgArray "诸葛亮与八卦阵": A junior high school student, player of _Minecraft: Bedrock Edition_, which is a fan of music and programming. **bgArray (诸葛亮与八卦阵)**: A junior high school student, player of _Minecraft: Bedrock Edition_, which is a fan of music and programming.
## Thanks🙏 **Touch (偷吃不是Touch)**: A man who is used to use command(s) in _Minecraft: Bedrock Edition_, who supported us of debugging and testing program and algorithm
## Thanks 🙏
This list is not in any order. This list is not in any order.
- Thank _昀梦_\<QQ1515399885\> for finding and correcting the bugs in the commands that _Musicreater_ generated. - Thank _昀梦_\<QQ1515399885\> for finding and correcting the bugs in the commands that _Musicreater_ generated.
- Thank _Charlie_Ping 查理平_ for the bdx convert function for reference, and the reference chart that's used to convert the mid's instruments into Minecraft's instruments. - Thank _Charlie_Ping 查理平_ for the bdx convert function for reference, and the reference chart that's used to convert the mid's instruments into Minecraft's instruments.
- Thank _[CMA_2401PT](https://github.com/CMA2401PT)_ for BDXWorkShop for reference of the .bdx structure's operation, and his guidance in some aspects of our development. - Thank _[CMA_2401PT](https://github.com/CMA2401PT)_ for BDXWorkShop for reference of the .bdx structure's operation, and his guidance in some aspects of our development.
- Thank _[Dislink Sforza](https://github.com/Dislink) 断联·斯福尔扎_ \<QQ1600515314\> for his midi analysis algorithm brought to us, we had adapted it and made it applied in one of our working method; Also, thank him for the [WebConvertor](https://dislink.github.io/midi2bdx/) which brought us so much pressure and power to develop as well as update our projects better, instead of loaf on our project. We hope he can get into a good university as he wantted to! - Thank _[Dislink Sforza](https://github.com/Dislink) 断联·斯福尔扎_ \<QQ1600515314\> for his midi analysis algorithm brought to us, we had adapted it and made it applied in one of our working method; Also, thank him for the [WebConvertor](https://dislink.github.io/midi2bdx/) which brought us so much pressure and power to develop as well as update our projects better, instead of loaf on our project.
- Thank _Touch 偷吃_\<QQ1793537164\> for support of debugging and testing program and algorithm, as well his/her suggestions to the improvement of our project
- Thank _Mono_\<QQ738893087\> for reporting problems while installing - Thank _Mono_\<QQ738893087\> for reporting problems while installing
- Thank _Ammelia 艾米利亚_\<QQ2838334637\> for urging us to develop new functions, and put forward a lot of excellent suggestions for new functions, as well as the BDX file's importing test support provided, which has given a lot of practical theoretical support for our new Structure Generating Algorithm - Thank _Ammelia 艾米利亚_\<QQ2838334637\> for urging us to develop new functions, and put forward a lot of excellent suggestions for new functions, as well as the BDX file's importing test support provided, which has given a lot of practical theoretical support for our new Structure Generating Algorithm
- Thank _[神羽](https://gitee.com/snowykami) [SnowyKami](https://github.com/snowyfirefly)_ for supporting and promoting our project. Hope he could get into a good university! - Thank _[神羽](https://gitee.com/snowykami) [SnowyKami](https://github.com/snowyfirefly)_ for supporting and promoting our project, and also thanks him for his server which given us to use for free.
- Thank **指令师\_苦力怕 playjuice123**\<QQ240667197\> for finding bugs within our code, and noticed us to repair a big problem. - Thank **指令师\_苦力怕 playjuice123**\<QQ240667197\> for finding bugs within our code, and noticed us to repair a big problem.
- Thank **雷霆**\<QQ3555268519\> for his annoying and provoking operations which may awake some problems within the program by chance and reminding us to repair. - Thank **雷霆**\<QQ3555268519\> for his annoying and provoking operations which may awake some problems within the program by chance and reminding us to repair.
- Thank **小埋**\<QQ2039310975\> for reporting the empty add-on packs title and description problem.
- <table><tr><td>Thank **油炸**\<QQ2836146704\> for inspiring us to constantly develop something new.</td><td><img width="260" src="https://foruda.gitee.com/images/1695478907647543027/08ea9909_9911226.jpeg" alt="The groupmate on the picture was saying that our convert-QQ-bot had once brought him great convinience but now it closed down by some reason so he was feeling regretful." title="&quot;It was once, a convert-QQ-bot is just in front my eyes&quot;&#10;&quot;Until lose, I finally know cannot chase back what I needs&quot;"></td><td><small>&quot;It was once, a convert-QQ-bot is just in front my eyes&quot;<br>&quot;Until lose, I finally know cannot chase back what I needs&quot;</small></td></tr></table>
> Thanks for a lot of groupmates' support and help > Thanks for the support and help of a lot of groupmates
> >
> If you have given contribution but haven't been in the list, please contact us! > If you have given contributions but have not been in the list, please contact us!
## Contact Us📞 ## Contact Us 📞
Meet problems? Welcome to give out your issue [here](https://github.com/EillesWan/Musicreater/issues/new)! Meet problems? Welcome to give out your issue [here](https://github.com/EillesWan/Musicreater/issues/new)!
Want to get in contact of developers? Welcome to join our [Chat QQ group](https://jq.qq.com/?_wv=1027&k=hpeRxrYr). Want to get in contact of developers? Welcome to join our [Chat QQ group](https://jq.qq.com/?_wv=1027&k=hpeRxrYr).
Or contact us via [TriM-Org Official Email](mailto:TriM-Organization@hotmail.com)!
--- ---
NOT AN OFFICIAL MINECRAFT PRODUCT. NOT AN OFFICIAL MINECRAFT PRODUCT.

13
docs/API.md Normal file
View File

@@ -0,0 +1,13 @@
<h1 align="center">· Musicreater</h1>
<p align="center">
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
</p>
**此为开发相关文档内容包括库的简单调用所生成文件结构的详细说明特殊参数的详细解释**
# [main.py](../Musicreater/main.py)
## [类] MidiConvert
### [类函数] from_midi_file

View File

@@ -1,12 +1,10 @@
<h1 align="center">· Musicreater</h1> <h1 align="center">· Musicreater</h1>
<h2 align="center">库版 Package Version</h2>
<p align="center"> <p align="center">
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" > <img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
</p> </p>
**此为开发相关文档内容包括所生成文件结构的详细说明特殊参数的详细解释** **此为开发相关文档内容包括库的简单调用所生成文件结构的详细说明特殊参数的详细解释**
# 库的简单调用 # 库的简单调用
@@ -91,7 +89,7 @@
progressbar_style, # 进度条样式组(详见下方) progressbar_style, # 进度条样式组(详见下方)
) )
# 同上面生成播放指令的理,进度条指令也存储至对象内 # 同上面生成播放指令的理,进度条指令也存储至对象内
print( print(
"进度条指令如下:\n{}".format( "进度条指令如下:\n{}".format(
cvt_mid.progress_bar_command cvt_mid.progress_bar_command
@@ -101,13 +99,15 @@
在上面的代码中进度条样式是可以自定义的详见[下方说明](%E5%BA%93%E7%9A%84%E7%94%9F%E6%88%90%E4%B8%8E%E5%8A%9F%E8%83%BD%E6%96%87%E6%A1%A3.md#进度条自定义)。 在上面的代码中进度条样式是可以自定义的详见[下方说明](%E5%BA%93%E7%9A%84%E7%94%9F%E6%88%90%E4%B8%8E%E5%8A%9F%E8%83%BD%E6%96%87%E6%A1%A3.md#进度条自定义)。
- 转换成指令是一个方面接下来是再转换为可以导入MC的格式我们提供了 · 内置的附加组件可以借助 `MidiConvert` 对象转换为相应格式 - 转换成指令是一个方面接下来是再转换为可以导入MC的格式我们提供了 **·** 内置的附加组件可以借助 `MidiConvert` 对象转换为相应格式
```python ```python
# 导入 Musicreater # 导入 Musicreater
import Musicreater import Musicreater
# 导入附加组件功能 # 导入附加组件功能
import Musicreater.plugin import Musicreater.plugin
# 导入相应的文件格式转换功能 # 导入相应的文件格式转换功能
# 转换为函数附加包 # 转换为函数附加包
@@ -122,7 +122,6 @@
import Musicreater.plugin.websocket import Musicreater.plugin.websocket
# 定义转换参数 # 定义转换参数
cvt_cfg = Musicreater.plugin.ConvertConfig( cvt_cfg = Musicreater.plugin.ConvertConfig(
output_path, output_path,
@@ -131,9 +130,17 @@
progressbar, # 进度条样式组(详见下方) progressbar, # 进度条样式组(详见下方)
) )
# 使用附加组件转换,其调用的函数应为:
# 懒得写了,等哪天有空写吧,后面的没用,是老的。 # Musicreater.plugin.输出格式.播放器格式
# 值得注意的是,并非所有输出格式都支持所有播放器格式
# 调用的时候还请注意甄别
# 例如,以下函数是将 MidiConvert 对象 cvt_mid
# 以 cvt_cfg 指定的参数
# 以延迟播放器转换为 mcstructure 文件
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay(
cvt_mid,
cvt_cfg,
)
``` ```
@@ -147,49 +154,29 @@
|指令链|与链式指令方块不同一个指令链通常指代的是一串由某种非链式指令方块作为开头后面连着一串链式指令方块的结构|通常的链都应用于需要单次激活而多指令的简单功能| |指令链|与链式指令方块不同一个指令链通常指代的是一串由某种非链式指令方块作为开头后面连着一串链式指令方块的结构|通常的链都应用于需要单次激活而多指令的简单功能|
|起始块|链最初的那个非链式指令方块|此方块为脉冲方块或重复方块皆可| |起始块|链最初的那个非链式指令方块|此方块为脉冲方块或重复方块皆可|
|指令系统系统|指令系统通常指的是由一个或多个指令链以及相关红石机构相互配合一同组成的为达到某种特定的功能而构建的整体结构|通常的系统都应用于需要综合调配指令的复杂功能可由多个实现不同功能的模块构成不同系统之间可以相互调用各自的模块| |指令系统系统|指令系统通常指的是由一个或多个指令链以及相关红石机构相互配合一同组成的为达到某种特定的功能而构建的整体结构|通常的系统都应用于需要综合调配指令的复杂功能可由多个实现不同功能的模块构成不同系统之间可以相互调用各自的模块|
|游戏刻|游戏的一刻是指我的世界的游戏循环运行一次所占用的时间[详见我的世界中文维基](https://minecraft.fandom.com/zh/wiki/%E5%88%BB#%E6%B8%B8%E6%88%8F%E5%88%BB))。指令方块的延迟功能(即指令方块的“延迟刻数”设置项,此项的名称被误译为“已选中项的延迟”)的单位即为`1`游戏刻。|正常情况下,游戏固定以每秒钟20刻的速率运行。但是,由于游戏内的绝大多数操作都是基于刻数而非现实中的时间来计时并进行的,一次游戏循环内也许会发生大量的操作,多情况下,一秒对应的游戏刻会更少。 | |游戏刻|游戏的一刻是指我的世界的游戏进程循环运行一次所占用的时间[详见我的世界中文维基](https://minecraft.fandom.com/zh/wiki/%E5%88%BB#%E6%B8%B8%E6%88%8F%E5%88%BB))。指令方块的延迟功能(即指令方块的“延迟刻数”设置项,此项的名称被误译为“已选中项的延迟”)的单位即为`1`游戏刻。|正常情况下,游戏固定以每秒钟 $20$ 刻的速率运行。但是,由于游戏内的绝大多数操作都是基于游戏进程循环而非现实中的时间来计时并进行的,一次游戏循环内也许会发生大量的操作,多情况下,一秒对应的游戏刻会更少。|
|红石刻|一个红石刻代表了两个游戏刻[详见我的世界中文维基](https://minecraft.fandom.com/zh/wiki/%E5%88%BB#%E7%BA%A2%E7%9F%B3%E5%88%BB))。红石中继器会带来 $1$~$4$ 个红石刻的延迟,其默认的延迟时间为 $1$ 红石刻。|正常情况下,红石信号在一个红石电路中传输回存在 $\frac{1}{10}$ 秒左右的延迟。但是,同理于游戏刻,一秒对应的红石刻是不定的。|
## 文件格式
1. 附加包格式`.mcpack`
使用附加包格式导出音乐则音乐会以指令函数文件`.mcfunction`存储于附加包内在附加包中函数文件的存储结构应为
- `functions\`
- `index.mcfunction`
- `mscply\`
- `progressShow.mcfunction`
- `track1.mcfunction`
- `track2.mcfunction`
- ...
- `trackN.mcfunction`
如图其中`index.mcfunction`文件和`mscply`文件夹存在于函数目录的根下`mscply`目录中包含音乐导出的众多音轨播放文件`trackX.mcfunction`同时若生成此包时选择了带有进度条的选项则会包含`progressShow.mcfunction`文件
`index.mcfunction`用于开始播放其中包含打开各个音轨对应函数的指令以及加分指令这里的加分是将**播放计分板的值大于等于`1`**的所有**玩家**的播放计分板分数增加`1`同时若生成此包时选择了自动重置计分板的选项则会包含一条重置计分板的指令
> 你知道吗·创的最早期版本我的世界函数音乐生成器正是用函数来播放不过这个版本采取的读入数据的形式大有不同
2. 结构格式
无论是音·创生成的是何种结构`MCSTRUCTURE`还是`BDX`都会依照此处的格式来生成此处我们想说明的结构的格式不是结构文件存储的格式而是结构导出之后方块将如何摆放的问题结构文件存储的格式这一点在各个我的世界开发的相关网站上都可能会有说明
考虑到进行我的世界游戏开发时为了节约常加载区域很多游戏会将指令区设立为一种层叠式的结构这种结构会限制每一层的指令系统的高度但是虽然长宽也是有限的却仍然比其纵轴延伸得更加自由
所以结构的生成形状依照给定的高度和内含指令的数量决定 $Z$ 轴延伸长度为指令方块数量对于给定高度之商的向下取整结果的平方根的向下取整用数学公式的方式表达则是
$$ MaxZ = \left\lfloor\sqrt{\left\lfloor{\frac{NoC}{MaxH}}\right\rfloor}\right\rfloor $$
其中$MaxZ$ 即生成结构的$Z$轴最大延伸长度$NoC$ 表示链结构中所含指令方块的个数$MaxH$ 表示给定的生成结构的最大高度
我们的结构生成器在生成指令链时将首先以相对坐标系 $(0, 0, 0)$ 即相对原点开始自下向上堆叠高度轴 $Y$ 的长当高度轴达到了限制的高度时便将 $Z$ 轴向正方向堆叠`1`个方块并开始自上向下重新堆叠直至高度轴坐标达到相对为 `0`若当所生成结构的 $Z$ 轴长达到了其最大延伸长度则此结构生成器将反转 $Z$ 轴的堆叠方向直至 $Z$ 轴坐标相对为 `0`如此往复直至指令链堆叠完成
## 播放器 ## 播放器
以结构生成的文件可以采用多种方式播放一类播放方式我们称其为**播放器**例如**延迟播放器****计分板播放器**等等以后推出的新的播放器届时也会在此处更新 **·**生成的文件可以采用多种方式播放一类播放方式我们称其为**播放器**例如**延迟播放器****计分板播放器**等等以后推出的新的播放器届时也会在此处更新
为什么要设计这么多播放器是为了适应不同的播放环境需要通常情况下一个音乐中含有多个音符音符与音符之间存在间隔这里就产生了不一样的实现音符间时间间隔的方式而不同的应用环境下又会产生不一样的要求接下来将对不同的播放器进行详细介绍 为什么要设计这么多播放器是为了适应不同的播放环境需要通常情况下一个音乐中含有多个音符音符与音符之间存在间隔这里就产生了不一样的实现音符间时间间隔的方式而不同的应用环境下又会产生不一样的要求接下来将对不同的播放器进行详细介绍
### 参数释义
|参数|说明|备注|
|--------|-----------|----------|
|`ScBd`|指定的计分板名称||
|`Tg`|播放对象|选择器或玩家名|
|`x`|音发出时对应的分数值||
|`InstID`|声音效果ID|不同的声音ID可以对应不同的乐器详见转换[乐器对照表](./%E8%BD%AC%E6%8D%A2%E4%B9%90%E5%99%A8%E5%AF%B9%E7%85%A7%E8%A1%A8.md)|
|`Ht`|播放点对玩家的距离|通过距离来表达声音的响度 $S$ 表示此参数`Ht`以Vol表示音量百分比则计算公式为 $S = \frac{1}{Vol}-1$ |
|`Vlct`|原生我的世界中规定的播放力度|这个参数是一个谜一样的存在似乎它的值毫不重要因为无论这个值是多少我们听起来都差不多当此音符所在MIDI通道为第一通道则这个值为 $0.7$ 倍MIDI指定力度其他则为 $0.9$ |
|`Ptc`|音符的音高|这是决定音调的参数 $P$ 表示此参数 $n$ 表示其在MIDI中的编号 $x$ 表示一定的音调偏移则计算公式为 $P = 2^\frac{n-60-x}{12}$之所以存在音调偏移是因为在我的世界不同的[乐器存在不同的音域](https://minecraft.fandom.com/zh/wiki/%E9%9F%B3%E7%AC%A6%E7%9B%92#%E4%B9%90%E5%99%A8),我们通过音调偏移来进行调整。|
### 播放器内容
1. 计分板播放器 1. 计分板播放器
计分板播放器是一种传统的我的世界音乐播放方式通过对于计分板加分来实现播放不同的音符一个很简单的原理就是**用不同的计分板分值对应不同的音符**再通过加分来达到那个分值即播放出来 计分板播放器是一种传统的我的世界音乐播放方式通过对于计分板加分来实现播放不同的音符一个很简单的原理就是**用不同的计分板分值对应不同的音符**再通过加分来达到那个分值即播放出来
@@ -200,14 +187,7 @@
execute @a[scores={ScBd=x}] ~ ~ ~ playsound InstID @s ^ ^ ^Ht Vlct Ptc execute @a[scores={ScBd=x}] ~ ~ ~ playsound InstID @s ^ ^ ^Ht Vlct Ptc
``` ```
|参数|说明|备注|
|--------|-----------|----------|
|`ScBd`|指定的计分板名称||
|`x`|音发出时对应的分数值||
|`InstID`|声音效果ID|不同的声音ID可以对应不同的乐器详见转换[乐器对照表](./%E8%BD%AC%E6%8D%A2%E4%B9%90%E5%99%A8%E5%AF%B9%E7%85%A7%E8%A1%A8.md)|
|`Ht`|播放点对玩家的距离|通过距离来表达声音的响度 $S$ 表示此参数`Ht`以Vol表示音量百分比则计算公式为 $S = \frac{1}{Vol}-1$ |
|`Vlct`|原生我的世界中规定的播放力度|这个参数是一个谜一样的存在似乎它的值毫不重要因为无论这个值是多少我们听起来都差不多当此音符所在MIDI通道为第一通道则这个值为0.7倍MIDI指定力度其他则为0.9|
|`Ptc`|音符的音高|这是决定音调的参数 $P$ 表示此参数 $n$ 表示其在MIDI中的编号 $x$ 表示一定的音域偏移则计算公式为 $P = 2^\frac{n-60-x}{12}$ |
后四个参数决定了这个音的性质而前两个参数仅仅是为了决定音播放的时间 后四个参数决定了这个音的性质而前两个参数仅仅是为了决定音播放的时间
@@ -215,32 +195,78 @@
延迟播放器是通过我的世界游戏中指令方块的设置项延迟刻数来达到定位音符的效果**将所有的音符依照其播放时距离乐曲开始时的时间毫秒放在一个序列内再计算音符两两之间对应的时间差值转换为我的世界内对应的游戏刻数之后填入指令方块的设置中** 延迟播放器是通过我的世界游戏中指令方块的设置项延迟刻数来达到定位音符的效果**将所有的音符依照其播放时距离乐曲开始时的时间毫秒放在一个序列内再计算音符两两之间对应的时间差值转换为我的世界内对应的游戏刻数之后填入指令方块的设置中**
·由于此方式播放的音乐不需要用计分板所以播放指令是这样的 **·**由于此方式播放的音乐不需要用计分板所以播放指令是这样的
```mcfunction ```mcfunction
execute Tg ~ ~ ~ playsound InstID @s ^ ^ ^Ht Vlct Ptc execute Tg ~ ~ ~ playsound InstID @s ^ ^ ^Ht Vlct Ptc
``` ```
|参数|说明|备注|
|--------|-----------|----------|
|`Tg`|播放对象|选择器或玩家名|
|`InstID`|声音效果ID|不同的声音ID可以对应不同的乐器详见转换[乐器对照表](./%E8%BD%AC%E6%8D%A2%E4%B9%90%E5%99%A8%E5%AF%B9%E7%85%A7%E8%A1%A8.md)|
|`Ht`|播放点对玩家的距离|通过距离来表达声音的响度 $S$ 表示此参数`Ht`以Vol表示音量百分比则计算公式为 $S = \frac{1}{Vol}-1$ |
|`Vlct`|原生我的世界中规定的播放力度|这个参数是一个谜一样的存在似乎它的值毫不重要因为无论这个值是多少我们听起来都差不多当此音符所在MIDI通道为第一通道则这个值为0.7倍MIDI指定力度其他则为0.9|
|`Ptc`|音符的音高|这是决定音调的参数 $P$ 表示此参数 $n$ 表示其在MIDI中的编号$x$表示一定的音域偏移则计算公式为 $P = 2^\frac{n-60-x}{12}$ |
其中后四个参数决定了这个音的性质 其中后四个参数决定了这个音的性质
由于这样的延迟数据是依赖于指令方块的设置项所以使用这种播放器所转换出的结果仅可以存储在包含方块NBT信息及方块实体NBT信息的结构文件中或者直接输出至世界 由于这样的延迟数据是依赖于指令方块的设置项所以使用这种播放器所转换出的结果仅可以存储在包含方块NBT信息及方块实体NBT信息的结构文件中或者直接输出至世界
3. 中继器播放器
中继器播放器是一种传统的我的世界红石音乐播放方式利用游戏内红石组件红石中继器以达到定位音符之用**但是中继器的延迟为1红石刻**
## 文件格式
1. 附加包格式`.mcpack`
使用附加包格式导出音乐若采用计分板 播放器则音乐会以指令函数文件`.mcfunction`存储于附加包内而若为延迟或中继器播放器则音乐回以结构文件`.mcstructure`存储在所生成的附加包中函数文件的存储结构应为
- `functions\`
- `index.mcfunction`
- `stop.mcfunction`
- `mscply\`
- `progressShow.mcfunction`
- `track1.mcfunction`
- `track2.mcfunction`
- ...
- `trackN.mcfunction`
- `structures\`
- `XXX_main.mcstructure`
- `XXX_start.mcstructure`
- `XXX_reset.mcstructure`
- `XXX_pgb.mcstructure`
如图其中`index.mcfunction`文件`stop.mcfunction`文件和`mscply`文件夹存在于函数目录的根下`mscply`目录中包含音乐导出的众多音轨播放文件`trackX.mcfunction`同时若使用计分板播放器生成此包时启用生成进度条则会包含`progressShow.mcfunction`文件若选择延迟或中继器播放器则会生成`structures`目录以及相关`.mcstructure`文件其中`mian`表示音乐播放用的主要结构`start`是用于初始化播放的部分仅包含一个指令方块即起始块`reset``pgb`仅在启用生成进度条时出现前者用于重置临时计分板后者用于显示进度条
`index.mcfunction`用于开始播放
1. 若为计分板播放器则其中包含打开各个音轨对应函数的指令以及加分指令这里的加分是将**播放计分板的值大于等于 $1$ 的所有玩家**的播放计分板分数增加 $1$同时若生成此包时选择了自动重置计分板的选项则会包含一条重置计分板的指令
2. 若为延迟或中继器播放器则其中的指令仅包含用以正确加载结构的`structure`指令
`stop.mcfunction`用于终止播放
1. 若为计分板播放器则其中包含将**全体玩家的播放计分板**重置的指令
2. 若为延迟或中继器播放器则其中包含**停用命令方块****启用命令方块**的指令~~然鹅实际上对于播放而言是一点用也没有~~
> 你知道吗·创的最早期版本我的世界函数音乐生成器正是用函数来播放不过这个版本采取的读入数据的形式大有不同
2. 生成结构的方式
无论是音·创生成的是何种结构`MCSTRUCTURE`还是`BDX`都会依照此处的格式来生成此处我们想说明的结构的格式不是结构文件存储的格式而是结构导出之后方块摆放的方式结构文件存储的格式这一点在各个我的世界开发的相关网站上都可能会有说明
考虑到进行我的世界游戏开发时为了节约常加载区域很多游戏会将指令区设立为一种层叠式的结构这种结构会限制每一层的指令系统的高度但是虽然长宽也是有限的却仍然比其纵轴延伸得更加自由
所以结构的生成形状依照给定的高度和内含指令的数量决定 $Z$ 轴延伸长度为指令方块数量对于给定高度之商的向下取整结果的平方根的向下取整用数学公式的方式表达则是
$$ MaxZ = \left\lfloor\sqrt{\left\lfloor{\frac{NoC}{MaxH}}\right\rfloor}\right\rfloor $$
其中$MaxZ$ 即生成结构的$Z$轴最大延伸长度$NoC$ 表示链结构中所含指令方块的个数$MaxH$ 表示给定的生成结构的最大高度
我们的结构生成器在生成指令链时将首先以相对坐标系 $(0, 0, 0)$ 即相对原点开始自下向上堆叠高度轴 $Y$ 的长当高度轴达到了限制的高度时便将 $Z$ 轴向正方向堆叠 $1$ 个方块并开始自上向下重新堆叠直至高度轴坐标达到相对为 $0$若当所生成结构的 $Z$ 轴长达到了其最大延伸长度则此结构生成器将反转 $Z$ 轴的堆叠方向直至 $Z$ 轴坐标相对为 $0$如此往复直至指令链堆叠完成
# 进度条自定义 # 进度条自定义
因为我们提供了可以自动转换进度条的功能因此在这里给出进度条自定义参数的详细解释 因为我们提供了可以自动转换进度条的功能因此在这里给出进度条自定义参数的详细解释
请注意并非所有的演示样例程序都支持自定义进度条
一个进度条明显地**固定部分****可变部分**来构成而可变部分又包括了文字和图形两种当然我的世界里头的进度条可变的图形也就是那个这一点你需要了解因为后文中包含了很多这方面的概念需要你了解 一个进度条明显地**固定部分****可变部分**来构成而可变部分又包括了文字和图形两种当然我的世界里头的进度条可变的图形也就是那个这一点你需要了解因为后文中包含了很多这方面的概念需要你了解
进度条的自定义功能使用一个字符串来定义自己的样式其中包含众多**标识符**来表示可变部分 进度条的自定义功能使用一个字符串来定义自己的样式其中包含众多**标识符**来表示可变部分

View File

@@ -1,16 +1,12 @@
<h1 align="center">· Musicreater</h1> <h1 align="center">· Musicreater</h1>
<h2 align="center">库版 Package Version</h2>
<p align="center"> <p align="center">
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" > <img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
</p> </p>
# 生成文件的使用 # 生成文件的使用
*由于先前的 **读我文件**(README.md) 过于冗杂现另辟蹊径来给大家全方位的教程* *这是本库所生成文件的使用声明不是使用本库的教程若要查看**本库的文档**可点击[此处](./%E5%BA%93%E7%9A%84%E7%94%9F%E6%88%90%E4%B8%8E%E5%8A%9F%E8%83%BD%E6%96%87%E6%A1%A3.md)若要查看有关文件结构的内容可以点击[此处](./%E7%94%9F%E6%88%90%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84%E8%AF%B4%E6%98%8E.md)*
*这是本库所生成文件的使用声明不是使用本库的教程若要查看**本库的大致文档**可点击[此处](./%E5%BA%93%E7%9A%84%E7%94%9F%E6%88%90%E4%B8%8E%E5%8A%9F%E8%83%BD%E6%96%87%E6%A1%A3.md)若要查看有关文件结构的内容可以点击[此处](./%E7%94%9F%E6%88%90%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84%E8%AF%B4%E6%98%8E.md)*
## 附加包格式 ## 附加包格式
@@ -24,6 +20,7 @@
4. 激活循环方块 4. 激活循环方块
5. 若想要暂停播放可以停止循环指令方块的激活状态 5. 若想要暂停播放可以停止循环指令方块的激活状态
6. 若想要重置某实体的播放可以将其播放用的计分板重置 6. 若想要重置某实体的播放可以将其播放用的计分板重置
7. 若要终止全部玩家的播放在聊天框输入指令 `function stop`
> 其中 步骤三 步骤四 的顺序可以调换 > 其中 步骤三 步骤四 的顺序可以调换
@@ -32,6 +29,7 @@
1. 导入附加包 1. 导入附加包
2. 在聊天框输入指令 `function index` 2. 在聊天框输入指令 `function index`
3. 同时激活所生成的循环和脉冲指令方块 3. 同时激活所生成的循环和脉冲指令方块
4. 若要终止播放在聊天框输入指令 `function stop` 试试看不确保有用
> 需要注意的是循环指令方块需要一直激活直到音乐结束 > 需要注意的是循环指令方块需要一直激活直到音乐结束

View File

@@ -1,6 +1,16 @@
<h1 align="center">· Musicreater</h1>
<p align="center">
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
</p>
# 转换乐器对照表
**_注意本文档中的对照表版权归属于音·创作者并按照本仓库根目录下 LICENSE.md 中规定开源_** **_注意本文档中的对照表版权归属于音·创作者并按照本仓库根目录下 LICENSE.md 中规定开源_**
**_使用时请遵循规定_** 原表格请见[constant.py](../Musicreater/constants.py#176)
**_使用时请遵循协议规定_**
- 版权所有 © 2023 · 开发者 - 版权所有 © 2023 · 开发者
- Copyright © 2023 all the developers of Musicreater - Copyright © 2023 all the developers of Musicreater
@@ -12,187 +22,196 @@
Email TriM-Organization@hotmail.com\ Email TriM-Organization@hotmail.com\
若需转载或借鉴 许可声明请查看仓库根目录下的 License.md 若需转载或借鉴 许可声明请查看仓库根目录下的 License.md
### 名词解释
| 名词 | 说明 |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| 音符名称 | 我的世界游戏内用于播放音乐的 `playsound` 指令所规定的 `Sound ID` |
| 音调偏移参数 | 我的世界不同乐器的音域不同对应的 `pitch` 值也不尽相同该参数的解释请参考[文档说明](库的生成与功能文档.md#参数释义) |
# 乐音乐器 # 乐音乐器
| Midi 乐器值 | 我的世界音符名称 | 我的世界音调偏移参数 | 对照表版本2023 0527
| ----------- | ------------------- | -------------------- |
| 0 | note.harp | 6 | | Midi 乐器值 | 音符名称 | 音调偏移参数 |
| 1 | note.harp | 6 | | ----------- | ------------------- | ------------ |
| 2 | note.pling | 6 | | 0 | note.harp | 6 |
| 3 | note.harp | 6 | | 1 | note.harp | 6 |
| 4 | note.pling | 6 | | 2 | note.pling | 6 |
| 5 | note.pling | 6 | | 3 | note.harp | 6 |
| 6 | note.harp | 6 | | 4 | note.pling | 6 |
| 7 | note.harp | 6 | | 5 | note.pling | 6 |
| 8 | note.share | 7 | | 6 | note.harp | 6 |
| 9 | note.harp | 6 | | 7 | note.harp | 6 |
| 10 | note.didgeridoo | 8 | | 8 | note.share | 7 |
| 11 | note.harp | 6 | | 9 | note.harp | 6 |
| 12 | note.xylophone | 4 | | 10 | note.didgeridoo | 8 |
| 13 | note.chime | 4 | | 11 | note.harp | 6 |
| 14 | note.harp | 6 | | 12 | note.xylophone | 4 |
| 15 | note.harp | 6 | | 13 | note.chime | 4 |
| 16 | note.bass | 8 | | 14 | note.harp | 6 |
| 17 | note.harp | 6 | | 15 | note.harp | 6 |
| 18 | note.harp | 6 | | 16 | note.bass | 8 |
| 19 | note.harp | 6 | | 17 | note.harp | 6 |
| 20 | note.harp | 6 | | 18 | note.harp | 6 |
| 21 | note.harp | 6 | | 19 | note.harp | 6 |
| 22 | note.harp | 6 | | 20 | note.harp | 6 |
| 23 | note.guitar | 7 | | 21 | note.harp | 6 |
| 24 | note.guitar | 7 | | 22 | note.harp | 6 |
| 25 | note.guitar | 7 | | 23 | note.guitar | 7 |
| 26 | note.guitar | 7 | | 24 | note.guitar | 7 |
| 27 | note.guitar | 7 | | 25 | note.guitar | 7 |
| 28 | note.guitar | 7 | | 26 | note.guitar | 7 |
| 29 | note.guitar | 7 | | 27 | note.guitar | 7 |
| 30 | note.guitar | 7 | | 28 | note.guitar | 7 |
| 31 | note.bass | 8 | | 29 | note.guitar | 7 |
| 32 | note.bass | 8 | | 30 | note.guitar | 7 |
| 33 | note.bass | 8 | | 31 | note.bass | 8 |
| 34 | note.bass | 8 | | 32 | note.bass | 8 |
| 35 | note.bass | 8 | | 33 | note.bass | 8 |
| 36 | note.bass | 8 | | 34 | note.bass | 8 |
| 37 | note.bass | 8 | | 35 | note.bass | 8 |
| 38 | note.bass | 8 | | 36 | note.bass | 8 |
| 39 | note.bass | 8 | | 37 | note.bass | 8 |
| 40 | note.harp | 6 | | 38 | note.bass | 8 |
| 41 | note.harp | 6 | | 39 | note.bass | 8 |
| 42 | note.harp | 6 | | 40 | note.harp | 6 |
| 43 | note.harp | 6 | | 41 | note.harp | 6 |
| 44 | note.iron_xylophone | 6 | | 42 | note.harp | 6 |
| 45 | note.guitar | 7 | | 43 | note.harp | 6 |
| 46 | note.harp | 6 | | 44 | note.iron_xylophone | 6 |
| 47 | note.harp | 6 | | 45 | note.guitar | 7 |
| 48 | note.guitar | 7 | | 46 | note.harp | 6 |
| 49 | note.guitar | 7 | | 47 | note.harp | 6 |
| 50 | note.bit | 6 | | 48 | note.guitar | 7 |
| 51 | note.bit | 6 | | 49 | note.guitar | 7 |
| 52 | note.harp | 6 | | 50 | note.bit | 6 |
| 53 | note.harp | 6 | | 51 | note.bit | 6 |
| 54 | note.bit | 6 | | 52 | note.harp | 6 |
| 55 | note.flute | 5 | | 53 | note.harp | 6 |
| 56 | note.flute | 5 | | 54 | note.bit | 6 |
| 57 | note.flute | 5 | | 55 | note.flute | 5 |
| 58 | note.flute | 5 | | 56 | note.flute | 5 |
| 59 | note.flute | 5 | | 57 | note.flute | 5 |
| 60 | note.flute | 5 | | 58 | note.flute | 5 |
| 61 | note.flute | 5 | | 59 | note.flute | 5 |
| 62 | note.flute | 5 | | 60 | note.flute | 5 |
| 63 | note.flute | 5 | | 61 | note.flute | 5 |
| 64 | note.bit | 6 | | 62 | note.flute | 5 |
| 65 | note.bit | 6 | | 63 | note.flute | 5 |
| 66 | note.bit | 6 | | 64 | note.bit | 6 |
| 67 | note.bit | 6 | | 65 | note.bit | 6 |
| 68 | note.flute | 5 | | 66 | note.bit | 6 |
| 69 | note.harp | 6 | | 67 | note.bit | 6 |
| 70 | note.harp | 6 | | 68 | note.flute | 5 |
| 71 | note.flute | 5 | | 69 | note.harp | 6 |
| 72 | note.flute | 5 | | 70 | note.harp | 6 |
| 73 | note.flute | 5 | | 71 | note.flute | 5 |
| 74 | note.harp | 6 | | 72 | note.flute | 5 |
| 75 | note.flute | 5 | | 73 | note.flute | 5 |
| 76 | note.harp | 6 | | 74 | note.harp | 6 |
| 77 | note.harp | 6 | | 75 | note.flute | 5 |
| 78 | note.harp | 6 | | 76 | note.harp | 6 |
| 79 | note.harp | 6 | | 77 | note.harp | 6 |
| 80 | note.bit | 6 | | 78 | note.harp | 6 |
| 81 | note.bit | 6 | | 79 | note.harp | 6 |
| 82 | note.bit | 6 | | 80 | note.bit | 6 |
| 83 | note.bit | 6 | | 81 | note.bit | 6 |
| 84 | note.bit | 6 | | 82 | note.bit | 6 |
| 85 | note.bit | 6 | | 83 | note.bit | 6 |
| 86 | note.bit | 6 | | 84 | note.bit | 6 |
| 87 | note.bit | 6 | | 85 | note.bit | 6 |
| 88 | note.bit | 6 | | 86 | note.bit | 6 |
| 89 | note.bit | 6 | | 87 | note.bit | 6 |
| 90 | note.bit | 6 | | 88 | note.bit | 6 |
| 91 | note.bit | 6 | | 89 | note.bit | 6 |
| 92 | note.bit | 6 | | 90 | note.bit | 6 |
| 93 | note.bit | 6 | | 91 | note.bit | 6 |
| 94 | note.bit | 6 | | 92 | note.bit | 6 |
| 95 | note.bit | 6 | | 93 | note.bit | 6 |
| 96 | note.bit | 6 | | 94 | note.bit | 6 |
| 97 | note.bit | 6 | | 95 | note.bit | 6 |
| 98 | note.bit | 6 | | 96 | note.bit | 6 |
| 99 | note.bit | 6 | | 97 | note.bit | 6 |
| 100 | note.bit | 6 | | 98 | note.bit | 6 |
| 101 | note.bit | 6 | | 99 | note.bit | 6 |
| 102 | note.bit | 6 | | 100 | note.bit | 6 |
| 103 | note.bit | 6 | | 101 | note.bit | 6 |
| 104 | note.harp | 6 | | 102 | note.bit | 6 |
| 105 | note.banjo | 6 | | 103 | note.bit | 6 |
| 106 | note.harp | 6 | | 104 | note.harp | 6 |
| 107 | note.harp | 6 | | 105 | note.banjo | 6 |
| 108 | note.harp | 6 | | 106 | note.harp | 6 |
| 109 | note.harp | 6 | | 107 | note.harp | 6 |
| 110 | note.harp | 6 | | 108 | note.harp | 6 |
| 111 | note.guitar | 7 | | 109 | note.harp | 6 |
| 112 | note.harp | 6 | | 110 | note.harp | 6 |
| 113 | note.bell | 4 | | 111 | note.guitar | 7 |
| 114 | note.harp | 6 | | 112 | note.harp | 6 |
| 115 | note.cow_bell | 5 | | 113 | note.bell | 4 |
| 116 | note.bd | 7 | | 114 | note.harp | 6 |
| 117 | note.bass | 8 | | 115 | note.cow_bell | 5 |
| 118 | note.bit | 6 | | 116 | note.bd | 7 |
| 119 | note.bd | 7 | | 117 | note.bass | 8 |
| 120 | note.guitar | 7 | | 118 | note.bit | 6 |
| 121 | note.harp | 6 | | 119 | note.bd | 7 |
| 122 | note.harp | 6 | | 120 | note.guitar | 7 |
| 123 | note.harp | 6 | | 121 | note.harp | 6 |
| 124 | note.harp | 6 | | 122 | note.harp | 6 |
| 125 | note.hat | 7 | | 123 | note.harp | 6 |
| 126 | note.bd | 7 | | 124 | note.harp | 6 |
| 127 | note.snare | 7 | | 125 | note.hat | 7 |
| 126 | note.bd | 7 |
| 127 | note.snare | 7 |
# 打击乐器 # 打击乐器
| Midi 打击乐器值 | 我的世界音符名称 | 我的世界音调偏移参数 | | Midi 打击乐器值 | 音符名称 | 音调偏移参数 |
| --------------- | ------------------- | -------------------- | | --------------- | ------------------- | ------------ |
| 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 | note.snare | 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 |
| 43 | note.snare | 7 | | 43 | note.snare | 7 |
| 44 | note.snare | 7 | | 44 | note.snare | 7 |
| 45 | note.bell | 4 | | 45 | note.bell | 4 |
| 46 | note.snare | 7 | | 46 | note.snare | 7 |
| 47 | note.snare | 7 | | 47 | note.snare | 7 |
| 48 | note.bell | 4 | | 48 | note.bell | 4 |
| 49 | note.hat | 7 | | 49 | note.hat | 7 |
| 50 | note.bell | 4 | | 50 | note.bell | 4 |
| 51 | note.bell | 4 | | 51 | note.bell | 4 |
| 52 | note.bell | 4 | | 52 | note.bell | 4 |
| 53 | note.bell | 4 | | 53 | note.bell | 4 |
| 54 | note.bell | 4 | | 54 | note.bell | 4 |
| 55 | note.bell | 4 | | 55 | note.bell | 4 |
| 56 | note.snare | 7 | | 56 | note.snare | 7 |
| 57 | note.hat | 7 | | 57 | note.hat | 7 |
| 58 | note.chime | 4 | | 58 | note.chime | 4 |
| 59 | note.iron_xylophone | 6 | | 59 | note.iron_xylophone | 6 |
| 60 | note.bd | 7 | | 60 | note.bd | 7 |
| 61 | note.bd | 7 | | 61 | note.bd | 7 |
| 62 | note.xylophone | 4 | | 62 | note.xylophone | 4 |
| 63 | note.xylophone | 4 | | 63 | note.xylophone | 4 |
| 64 | note.xylophone | 4 | | 64 | note.xylophone | 4 |
| 65 | note.hat | 7 | | 65 | note.hat | 7 |
| 66 | note.bell | 4 | | 66 | note.bell | 4 |
| 67 | note.bell | 4 | | 67 | note.bell | 4 |
| 68 | note.hat | 7 | | 68 | note.hat | 7 |
| 69 | note.hat | 7 | | 69 | note.hat | 7 |
| 70 | note.flute | 5 | | 70 | note.flute | 5 |
| 71 | note.flute | 5 | | 71 | note.flute | 5 |
| 72 | note.hat | 7 | | 72 | note.hat | 7 |
| 73 | note.hat | 7 | | 73 | note.hat | 7 |
| 74 | note.xylophone | 4 | | 74 | note.xylophone | 4 |
| 75 | note.hat | 7 | | 75 | note.hat | 7 |
| 76 | note.hat | 7 | | 76 | note.hat | 7 |
| 77 | note.xylophone | 4 | | 77 | note.xylophone | 4 |
| 78 | note.xylophone | 4 | | 78 | note.xylophone | 4 |
| 79 | note.bell | 4 | | 79 | note.bell | 4 |
| 80 | note.bell | 4 | | 80 | note.bell | 4 |

View File

@@ -20,9 +20,12 @@ import os
import Musicreater import Musicreater
from Musicreater.plugin import ConvertConfig from Musicreater.plugin import ConvertConfig
from Musicreater.plugin.addonpack import (
to_addon_pack_in_delay,
to_addon_pack_in_repeater,
to_addon_pack_in_score,
)
from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score
from Musicreater.plugin.funcpack import to_function_addon_in_score
from Musicreater.plugin.mcstructpack import to_mcstructure_addon_in_delay
# 获取midi列表 # 获取midi列表
midi_path = input(f"请输入MIDI路径") midi_path = input(f"请输入MIDI路径")
@@ -34,17 +37,17 @@ out_path = input(f"请输入输出路径:")
# 选择输出格式 # 选择输出格式
fileFormat = int(input(f"请输入输出格式[BDX(1) 或 MCPACK(0)]").lower()) fileFormat = int(input(f"请输入输出格式[BDX(1) 或 MCPACK(0)]").lower())
playerFormat = int(input(f"请选择播放方式[计分板(1) 或 延迟(0)]").lower()) playerFormat = int(input(f"请选择播放方式[红石(2) 或 计分板(1) 或 延迟(0)]").lower())
# 真假字符串判断 # 真假字符串判断
def bool_str(sth: str) -> bool: def bool_str(sth: str):
try: try:
return bool(float(sth)) return bool(float(sth))
except ValueError: except:
if str(sth).lower() == "true": if str(sth).lower() in ("true", "", "", "y", "t"):
return True return True
elif str(sth).lower() == "false": elif str(sth).lower() in ("false", "", "", "f", "n"):
return False return False
else: else:
raise ValueError("布尔字符串啊?") raise ValueError("布尔字符串啊?")
@@ -106,13 +109,17 @@ print(f"正在处理 {midi_path} ")
cvt_mid = Musicreater.MidiConvert.from_midi_file(midi_path, old_exe_format=False) cvt_mid = Musicreater.MidiConvert.from_midi_file(midi_path, old_exe_format=False)
cvt_cfg = ConvertConfig(out_path, *prompts[:3]) cvt_cfg = ConvertConfig(out_path, *prompts[:3])
if playerFormat == 1:
cvt_method = to_addon_pack_in_score
elif playerFormat == 0:
cvt_method = to_addon_pack_in_delay
elif playerFormat == 2:
cvt_method = to_addon_pack_in_repeater
print( print(
" 指令总长:{},最高延迟:{}".format( " 指令总长:{},最高延迟:{}".format(
*( *(cvt_method(cvt_mid, cvt_cfg, *prompts[3:])) # type: ignore
to_function_addon_in_score(cvt_mid, cvt_cfg, *prompts[3:])
if playerFormat == 1
else to_mcstructure_addon_in_delay(cvt_mid, cvt_cfg, *prompts[3:])
)
) )
if fileFormat == 0 if fileFormat == 0
else " 指令总长:{},最高延迟:{},结构大小{},终点坐标{}".format( else " 指令总长:{},最高延迟:{},结构大小{},终点坐标{}".format(

View File

@@ -1,13 +1,13 @@
import Musicreater.experiment import Musicreater.experiment
import Musicreater.plugin import Musicreater.plugin
import Musicreater.plugin.mcstructpack import Musicreater.plugin.mcstructfile
print( print(
Musicreater.plugin.mcstructpack.to_mcstructure_addon_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,
) )
) )

View File

@@ -5,9 +5,7 @@ import Musicreater.plugin.mcstructfile
print( 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( Musicreater.plugin.ConvertConfig(input("输出路径:"), volume=1),
input("输出路径:"), max_height=32,
volume=1
),
) )
) )

View File

@@ -3,11 +3,8 @@ import Musicreater.plugin
import Musicreater.plugin.mcstructfile import Musicreater.plugin.mcstructfile
print( print(
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_redstone_CD( Musicreater.plugin.mcstructfile.to_mcstructure_file_in_repeater(
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( Musicreater.plugin.ConvertConfig(input("输出路径:"), volume=1),
input("输出路径:"),
volume=1
),
) )
) )

View File

@@ -0,0 +1,12 @@
import Musicreater.experiment
import Musicreater.plugin
import Musicreater.plugin.mcstructfile
print(
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_repeater(
Musicreater.experiment.FutureMidiConvertM4.from_midi_file(
input("midi路径:"), old_exe_format=False
),
Musicreater.plugin.ConvertConfig(input("输出路径:"), volume=1),
)
)

View 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()

View 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()

BIN
resources/测试片段.mid Normal file

Binary file not shown.

BIN
resources/测试片段.mscz Normal file

Binary file not shown.

View File

@@ -0,0 +1,14 @@
音乐谱面文件
测试片段.mid、测试片段.mscz
皆系 音·创 库所用之连带软件
禁止在 音·创 库以外传播
不允许独立发行、独立副本
不允许未经允许的二次创作
不允许脱离于 音·创 独立存在
不得随任何其他非 音·创 软件连带发布
不得用于商业用途
若 音·创 库被用于商业用途,应当将其剔除
版权所有 © 2023 诸葛亮与八卦阵