mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2024-11-11 01:27:35 +08:00
史诗级更新!支持导出mcstructure库,更改部分API和文档,修改了部分代码格式,新增红乐测试
This commit is contained in:
parent
b758a2f967
commit
4874ace92d
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,6 +10,7 @@
|
||||
*.mcpack
|
||||
*.bdx
|
||||
*.json
|
||||
*.mcstructure
|
||||
|
||||
# Byte-compiled / optimized
|
||||
__pycache__/
|
||||
|
@ -19,8 +19,6 @@ Terms & Conditions: ../License.md
|
||||
|
||||
from .main import *
|
||||
|
||||
__version__ = "0.4.0"
|
||||
__version__ = "0.5.0"
|
||||
__all__ = []
|
||||
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"))
|
||||
|
||||
|
||||
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"), ("鸣凤鸽子", "MingFengPigeon"))
|
||||
|
@ -25,6 +25,7 @@ class MSCTBaseException(Exception):
|
||||
"""音·创库版本的所有错误均继承于此"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""音·创库版本的所有错误均继承于此"""
|
||||
super().__init__(*args)
|
||||
|
||||
def miao(
|
||||
@ -37,37 +38,65 @@ class MSCTBaseException(Exception):
|
||||
raise self
|
||||
|
||||
|
||||
class CrossNoteError(MSCTBaseException):
|
||||
"""同通道下同音符交叉出现所产生的错误"""
|
||||
class MidiFormatException(MSCTBaseException):
|
||||
"""音·创库版本的所有MIDI格式错误均继承于此"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class NotDefineTempoError(MSCTBaseException):
|
||||
"""没有Tempo设定导致时间无法计算的错误"""
|
||||
|
||||
pass
|
||||
def __init__(self, *args):
|
||||
"""音·创库版本的所有MIDI格式错误均继承于此"""
|
||||
super().__init__("MIDI格式错误", *args)
|
||||
|
||||
|
||||
class MidiDestroyedError(MSCTBaseException):
|
||||
"""Midi文件损坏"""
|
||||
|
||||
pass
|
||||
def __init__(self, *args):
|
||||
"""Midi文件损坏"""
|
||||
super().__init__("MIDI文件损坏:无法读取MIDI文件", *args)
|
||||
|
||||
|
||||
class ChannelOverFlowError(MSCTBaseException):
|
||||
"""一个midi中含有过多的通道(数量应≤16)"""
|
||||
class CommandFormatError(RuntimeError):
|
||||
"""指令格式与目标格式不匹配而引起的错误"""
|
||||
|
||||
pass
|
||||
def __init__(self, *args):
|
||||
"""指令格式与目标格式不匹配而引起的错误"""
|
||||
super().__init__("指令格式不匹配", *args)
|
||||
|
||||
|
||||
class NotDefineProgramError(MSCTBaseException):
|
||||
class CrossNoteError(MidiFormatException):
|
||||
"""同通道下同音符交叉出现所产生的错误"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""同通道下同音符交叉出现所产生的错误"""
|
||||
super().__init__("同通道下同音符交叉", *args)
|
||||
|
||||
|
||||
class NotDefineTempoError(MidiFormatException):
|
||||
"""没有Tempo设定导致时间无法计算的错误"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""没有Tempo设定导致时间无法计算的错误"""
|
||||
super().__init__("在曲目开始时没有声明Tempo(未指定拍长)", *args)
|
||||
|
||||
|
||||
class ChannelOverFlowError(MidiFormatException):
|
||||
"""一个midi中含有过多的通道"""
|
||||
|
||||
def __init__(self, max_channel = 16, *args):
|
||||
"""一个midi中含有过多的通道"""
|
||||
super().__init__("含有过多的通道(数量应≤{})".format(max_channel), *args)
|
||||
|
||||
|
||||
class NotDefineProgramError(MidiFormatException):
|
||||
"""没有Program设定导致没有乐器可以选择的错误"""
|
||||
|
||||
pass
|
||||
def __init__(self, *args):
|
||||
"""没有Program设定导致没有乐器可以选择的错误"""
|
||||
super().__init__("未指定演奏乐器", *args)
|
||||
|
||||
|
||||
class ZeroSpeedError(MSCTBaseException):
|
||||
class ZeroSpeedError(MidiFormatException):
|
||||
"""以0作为播放速度的错误"""
|
||||
|
||||
pass
|
||||
def __init__(self, *args):
|
||||
"""以0作为播放速度的错误"""
|
||||
super().__init__("播放速度为0", *args)
|
||||
|
@ -176,4 +176,25 @@ percussion_instrument_list = {
|
||||
77: ("note.xylophone", 4),
|
||||
78: ("note.xylophone", 4),
|
||||
79: ("note.bell", 4),
|
||||
80: ("note.bell", 4), }
|
||||
80: ("note.bell", 4),
|
||||
}
|
||||
|
||||
instrument_to_blocks_list = {
|
||||
"note.bass": ("planks",),
|
||||
"note.snare": ("sand",),
|
||||
"note.hat": ("glass",),
|
||||
"note.bd": ("stone",),
|
||||
"note.bell": ("gold_block",),
|
||||
"note.flute": ("clay",),
|
||||
"note.chime": ("packed_ice",),
|
||||
"note.guitar": ("wool",),
|
||||
"note.xylobone": ("bone_block",),
|
||||
"note.iron_xylophone": ("iron_block",),
|
||||
"note.cow_bell": ("soul_sand",),
|
||||
"note.didgeridoo": ("pumpkin",),
|
||||
"note.bit": ("emerald_block",),
|
||||
"note.banjo": ("hay_block",),
|
||||
"note.pling": ("glowstone",),
|
||||
"note.bassattack": ("command_block",), # 无法找到此音效
|
||||
"note.harp": ("glass",),
|
||||
}
|
||||
|
@ -1,35 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
功能测试 若非已知 请勿更改
|
||||
此文件仅供功能测试,并非实际调用的文件
|
||||
请注意,此处的文件均为测试使用
|
||||
不要更改 不要更改 不要更改
|
||||
请注意这里的一切均需要其原作者更改
|
||||
这里用于放置一些新奇的点子
|
||||
用于测试
|
||||
不要更改 不要更改 不要更改!
|
||||
"""
|
||||
|
||||
# 音·创 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||
# 若需使用或借鉴 请依照 Apache 2.0 许可证进行许可
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
"""
|
||||
音·创 库版 (Musicreater Package Version)
|
||||
是一款免费开源的针对《我的世界:基岩版》的midi音乐转换库
|
||||
注意!除了此源文件以外,任何属于此仓库以及此项目的文件均依照Apache许可证进行许可
|
||||
Musicreater pkgver (Package Version 音·创 库版)
|
||||
A free open source library used for convert midi file into formats that is suitable for **Minecraft: Bedrock Edition**.
|
||||
Note! Except for this source file, all the files in this repository and this project are licensed under Apache License 2.0
|
||||
音·创 (Musicreater)
|
||||
是一款免费开源的针对《我的世界》的midi音乐转换库
|
||||
Musicreater (音·创)
|
||||
A free open source library used for convert midi file into formats that is suitable for **Minecraft**.
|
||||
|
||||
Copyright 2022 all the developers of Musicreater
|
||||
版权所有 © 2023 音·创 开发者
|
||||
Copyright © 2023 all the developers of Musicreater
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
开源相关声明请见 ../License.md
|
||||
Terms & Conditions: ../License.md
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _toCmdList_m1(
|
||||
self,
|
||||
scoreboardname: str = "mscplay",
|
||||
@ -91,10 +94,6 @@ def _toCmdList_m1(
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ============================
|
||||
|
||||
|
||||
@ -102,10 +101,6 @@ def _toCmdList_m1(
|
||||
|
||||
import mido
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class NoteMessage:
|
||||
def __init__(self, channel, pitch, velocity, startT, lastT, midi, now_bpm, change_bpm=None):
|
||||
self.channel = channel
|
||||
@ -213,5 +208,98 @@ if __name__ == '__main__':
|
||||
|
||||
|
||||
|
||||
# ============================
|
||||
|
||||
|
||||
from typing import Union
|
||||
from .utils import x,y,z,bottem_side_length_of_smallest_square_bottom_box,form_note_block_in_NBT_struct,form_repeater_in_NBT_struct
|
||||
|
||||
# 不要用 没写完
|
||||
def delay_to_note_blocks(
|
||||
baseblock: str = "stone",
|
||||
position_forward: Union(x, y, z) = z,
|
||||
max_height: int = 64,
|
||||
):
|
||||
"""传入音符,生成以音符盒存储的红石音乐
|
||||
:param:
|
||||
baseblock: 中继器的下垫方块
|
||||
position_forward: 结构延长方向
|
||||
:return 是否生成成功
|
||||
"""
|
||||
|
||||
from TrimMCStruct import Structure, Block
|
||||
|
||||
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
|
||||
len(commands), max_height
|
||||
)
|
||||
|
||||
struct = Structure(
|
||||
(_sideLength, max_height, _sideLength), # 声明结构大小
|
||||
)
|
||||
|
||||
log = print
|
||||
|
||||
startpos = [0,0,0]
|
||||
|
||||
|
||||
# 1拍 x 2.5 rt
|
||||
def placeNoteBlock():
|
||||
for i in notes:
|
||||
error = True
|
||||
try:
|
||||
struct.set_block(
|
||||
[startpos[0], startpos[1] + 1, startpos[2]],
|
||||
form_note_block_in_NBT_struct(height2note[i[0]], instrument),
|
||||
)
|
||||
struct.set_block(startpos, Block("universal_minecraft", instuments[i[0]][1]),)
|
||||
error = False
|
||||
except ValueError:
|
||||
log("无法放置音符:" + str(i) + "于" + str(startpos))
|
||||
struct.set_block(Block("universal_minecraft", baseblock), startpos)
|
||||
struct.set_block(
|
||||
Block("universal_minecraft", baseblock),
|
||||
[startpos[0], startpos[1] + 1, startpos[2]],
|
||||
)
|
||||
finally:
|
||||
if error is True:
|
||||
log("无法放置音符:" + str(i) + "于" + str(startpos))
|
||||
struct.set_block(Block("universal_minecraft", baseblock), startpos)
|
||||
struct.set_block(
|
||||
Block("universal_minecraft", baseblock),
|
||||
[startpos[0], startpos[1] + 1, startpos[2]],
|
||||
)
|
||||
delay = int(i[1] * speed + 0.5)
|
||||
if delay <= 4:
|
||||
startpos[0] += 1
|
||||
struct.set_block(
|
||||
form_repeater_in_NBT_struct(delay, "west"),
|
||||
[startpos[0], startpos[1] + 1, startpos[2]],
|
||||
)
|
||||
struct.set_block(Block("universal_minecraft", baseblock), startpos)
|
||||
else:
|
||||
for j in range(int(delay / 4)):
|
||||
startpos[0] += 1
|
||||
struct.set_block(
|
||||
form_repeater_in_NBT_struct(4, "west"),
|
||||
[startpos[0], startpos[1] + 1, startpos[2]],
|
||||
)
|
||||
struct.set_block(Block("universal_minecraft", baseblock), startpos)
|
||||
if delay % 4 != 0:
|
||||
startpos[0] += 1
|
||||
struct.set_block(
|
||||
form_repeater_in_NBT_struct(delay % 4, "west"),
|
||||
[startpos[0], startpos[1] + 1, startpos[2]],
|
||||
)
|
||||
struct.set_block(Block("universal_minecraft", baseblock), startpos)
|
||||
startpos[0] += posadder[0]
|
||||
startpos[1] += posadder[1]
|
||||
startpos[2] += posadder[2]
|
||||
|
||||
# e = True
|
||||
try:
|
||||
placeNoteBlock()
|
||||
# e = False
|
||||
except: # ValueError
|
||||
log("无法放置方块了,可能是因为区块未加载叭")
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,9 @@
|
||||
import math
|
||||
import os
|
||||
from typing import Union
|
||||
from TrimMCStruct import Structure, Block, TAG_Long, TAG_Byte
|
||||
|
||||
key = {
|
||||
bdx_key = {
|
||||
"x": [b"\x0f", b"\x0e", b"\x1c", b"\x14", b"\x15"],
|
||||
"y": [b"\x11", b"\x10", b"\x1d", b"\x16", b"\x17"],
|
||||
"z": [b"\x13", b"\x12", b"\x1e", b"\x18", b"\x19"],
|
||||
@ -18,7 +20,7 @@ def bdx_move(axis: str, value: int):
|
||||
if value == 0:
|
||||
return b""
|
||||
if abs(value) == 1:
|
||||
return key[axis][0 if value == -1 else 1]
|
||||
return bdx_key[axis][0 if value == -1 else 1]
|
||||
|
||||
pointer = sum(
|
||||
[
|
||||
@ -32,7 +34,9 @@ def bdx_move(axis: str, value: int):
|
||||
]
|
||||
)
|
||||
|
||||
return key[axis][pointer] + value.to_bytes(2 ** (pointer - 2), "big", signed=True)
|
||||
return bdx_key[axis][pointer] + value.to_bytes(
|
||||
2 ** (pointer - 2), "big", signed=True
|
||||
)
|
||||
|
||||
|
||||
def compress_zipfile(sourceDir, outFilename, compression=8, exceptFile=None):
|
||||
@ -112,8 +116,7 @@ def form_command_block_in_BDX_bytes(
|
||||
|
||||
:return:str
|
||||
"""
|
||||
block = b"\x24" + \
|
||||
particularValue.to_bytes(2, byteorder="big", signed=False)
|
||||
block = b"\x24" + particularValue.to_bytes(2, byteorder="big", signed=False)
|
||||
|
||||
for i in [
|
||||
impluse.to_bytes(4, byteorder="big", signed=False),
|
||||
@ -138,7 +141,7 @@ def bottem_side_length_of_smallest_square_bottom_box(total: int, maxHeight: int)
|
||||
return math.ceil(math.sqrt(math.ceil(total / maxHeight)))
|
||||
|
||||
|
||||
def to_BDX_bytes(
|
||||
def commands_to_BDX_bytes(
|
||||
commands: list,
|
||||
max_height: int = 64,
|
||||
):
|
||||
@ -178,7 +181,7 @@ def to_BDX_bytes(
|
||||
else (3 if z_forward else 2)
|
||||
if (
|
||||
((now_z != 0) and (not z_forward))
|
||||
or (z_forward and (now_z != _sideLength))
|
||||
or (z_forward and (now_z != _sideLength - 1))
|
||||
)
|
||||
else 5,
|
||||
impluse=impluse,
|
||||
@ -204,15 +207,13 @@ def to_BDX_bytes(
|
||||
):
|
||||
now_z -= 1 if z_forward else -1
|
||||
z_forward = not z_forward
|
||||
_bytes += key[x][1]
|
||||
_bytes += bdx_key[x][1]
|
||||
now_x += 1
|
||||
else:
|
||||
|
||||
_bytes += key[z][int(z_forward)]
|
||||
_bytes += bdx_key[z][int(z_forward)]
|
||||
|
||||
else:
|
||||
|
||||
_bytes += key[y][int(y_forward)]
|
||||
_bytes += bdx_key[y][int(y_forward)]
|
||||
|
||||
return (
|
||||
_bytes,
|
||||
@ -225,6 +226,60 @@ def to_BDX_bytes(
|
||||
)
|
||||
|
||||
|
||||
def form_note_block_in_NBT_struct(
|
||||
note: int, coordinate: tuple, instrument: str = "note.harp", powered: bool = False
|
||||
):
|
||||
"""生成音符盒方块
|
||||
:param note: `int`(0~24)
|
||||
音符的音高
|
||||
:param coordinate: `tuple[int,int,int]`
|
||||
此方块所在之相对坐标
|
||||
:param instrument: `str`
|
||||
音符盒的乐器
|
||||
:param powered: `bool`
|
||||
是否已被激活
|
||||
:return Block
|
||||
"""
|
||||
|
||||
return Block(
|
||||
"minecraft",
|
||||
"noteblock",
|
||||
{
|
||||
"instrument": instrument.replace("note.", ""),
|
||||
"note": note,
|
||||
"powered": powered,
|
||||
},
|
||||
{
|
||||
"block_entity_data": {
|
||||
"note": TAG_Byte(note),
|
||||
"id": "noteblock",
|
||||
"x": coordinate[0],
|
||||
"y": coordinate[1],
|
||||
"z": coordinate[2],
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def form_repeater_in_NBT_struct(
|
||||
delay: int, facing: int
|
||||
):
|
||||
"""生成中继器方块
|
||||
:param powered:
|
||||
:param locked:
|
||||
:param facing:
|
||||
:param delay: 1~4
|
||||
:return Block()"""
|
||||
|
||||
return Block(
|
||||
"minecraft",
|
||||
"unpowered_repeater",
|
||||
{
|
||||
"repeater_delay": delay,
|
||||
"direction": facing,
|
||||
},
|
||||
)
|
||||
|
||||
def form_command_block_in_NBT_struct(
|
||||
command: str,
|
||||
coordinate: tuple,
|
||||
@ -281,42 +336,43 @@ def form_command_block_in_NBT_struct(
|
||||
|
||||
:return:str
|
||||
"""
|
||||
from TrimMCStruct import Block, TAG_Long
|
||||
block = Block(
|
||||
|
||||
return Block(
|
||||
"minecraft",
|
||||
"command_block" if impluse == 0 else (
|
||||
"repeating_command_block" if impluse == 1 else "chain_command_block"),
|
||||
states={"conditional_bit": condition,
|
||||
"facing_direction": particularValue},
|
||||
"command_block"
|
||||
if impluse == 0
|
||||
else ("repeating_command_block" if impluse == 1 else "chain_command_block"),
|
||||
states={"conditional_bit": condition, "facing_direction": particularValue},
|
||||
extra_data={
|
||||
'Command': command,
|
||||
'CustomName': customName,
|
||||
'ExecuteOnFirstTick': executeOnFirstTick,
|
||||
'LPCommandMode': 0,
|
||||
'LPCondionalMode': False,
|
||||
'LPRedstoneMode': False,
|
||||
'LastExecution': TAG_Long(0),
|
||||
'LastOutput': '',
|
||||
'LastOutputParams': [],
|
||||
'SuccessCount': 0,
|
||||
'TickDelay': tickDelay,
|
||||
'TrackOutput': trackOutput,
|
||||
'Version': 25,
|
||||
'auto': alwaysRun,
|
||||
'conditionMet': False, # 是否已经满足条件
|
||||
'conditionalMode': condition,
|
||||
'id': 'CommandBlock',
|
||||
'isMovable': True,
|
||||
'powered': False, # 是否已激活
|
||||
'x': coordinate[0],
|
||||
'y': coordinate[1],
|
||||
'z': coordinate[2],
|
||||
}
|
||||
"block_entity_data": {
|
||||
"Command": command,
|
||||
"CustomName": customName,
|
||||
"ExecuteOnFirstTick": executeOnFirstTick,
|
||||
"LPCommandMode": 0,
|
||||
"LPCondionalMode": False,
|
||||
"LPRedstoneMode": False,
|
||||
"LastExecution": TAG_Long(0),
|
||||
"LastOutput": "",
|
||||
"LastOutputParams": [],
|
||||
"SuccessCount": 0,
|
||||
"TickDelay": tickDelay,
|
||||
"TrackOutput": trackOutput,
|
||||
"Version": 25,
|
||||
"auto": alwaysRun,
|
||||
"conditionMet": False, # 是否已经满足条件
|
||||
"conditionalMode": condition,
|
||||
"id": "CommandBlock",
|
||||
"isMovable": True,
|
||||
"powered": False, # 是否已激活
|
||||
"x": coordinate[0],
|
||||
"y": coordinate[1],
|
||||
"z": coordinate[2],
|
||||
}
|
||||
},
|
||||
compability_version=17959425,
|
||||
)
|
||||
return block
|
||||
|
||||
|
||||
def to_structure(
|
||||
def commands_to_structure(
|
||||
commands: list,
|
||||
max_height: int = 64,
|
||||
):
|
||||
@ -325,8 +381,6 @@ def to_structure(
|
||||
:param max_height: 生成结构最大高度
|
||||
:return 成功与否,成功返回(结构类,结构占用大小),失败返回(False,str失败原因)
|
||||
"""
|
||||
# 导入库
|
||||
from TrimMCStruct import Structure
|
||||
|
||||
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
|
||||
len(commands), max_height
|
||||
@ -345,29 +399,33 @@ def to_structure(
|
||||
|
||||
for cmd, delay in commands:
|
||||
coordinate = (now_x, now_y, now_z)
|
||||
struct.set_block(coordinate,
|
||||
form_command_block_in_NBT_struct(
|
||||
command=cmd,
|
||||
coordinate=coordinate,
|
||||
particularValue=(1 if y_forward else 0)
|
||||
if (
|
||||
((now_y != 0) and (not y_forward))
|
||||
or (y_forward and (now_y != (max_height - 1)))
|
||||
)
|
||||
else (3 if z_forward else 2)
|
||||
if (
|
||||
((now_z != 0) and (not z_forward))
|
||||
or (z_forward and (now_z != _sideLength))
|
||||
)
|
||||
else 5,
|
||||
impluse=2,
|
||||
condition=False,
|
||||
alwaysRun=True,
|
||||
tickDelay=delay,
|
||||
customName="",
|
||||
executeOnFirstTick=False,
|
||||
trackOutput=True,
|
||||
))
|
||||
struct.set_block(
|
||||
coordinate,
|
||||
form_command_block_in_NBT_struct(
|
||||
command=cmd,
|
||||
coordinate=coordinate,
|
||||
particularValue=(1 if y_forward else 0)
|
||||
if (
|
||||
((now_y != 0) and (not y_forward))
|
||||
or (y_forward and (now_y != (max_height - 1)))
|
||||
)
|
||||
else (
|
||||
(3 if z_forward else 2)
|
||||
if (
|
||||
((now_z != 0) and (not z_forward))
|
||||
or (z_forward and (now_z != _sideLength - 1))
|
||||
)
|
||||
else 5
|
||||
),
|
||||
impluse=2,
|
||||
condition=False,
|
||||
alwaysRun=True,
|
||||
tickDelay=delay,
|
||||
customName="",
|
||||
executeOnFirstTick=False,
|
||||
trackOutput=True,
|
||||
),
|
||||
)
|
||||
|
||||
now_y += 1 if y_forward else -1
|
||||
|
||||
@ -387,9 +445,11 @@ def to_structure(
|
||||
|
||||
return (
|
||||
struct,
|
||||
[
|
||||
(
|
||||
now_x + 1,
|
||||
max_height if now_x or now_z else now_y,
|
||||
_sideLength if now_x else now_z,
|
||||
],
|
||||
),
|
||||
(now_x, now_y, now_z),
|
||||
)
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
</h1>
|
||||
|
||||
<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://gitee.com/TriM-Organization/Musicreater/raw/master/resources/msctIcon.ico">
|
||||
</img>
|
||||
</p>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
|
||||
</p>
|
||||
|
||||
<h3 align="center">a free open-source library of converting midi files into *Minecraft* formats.</h3>
|
||||
<h3 align="center">a free open-source library of converting midi files into _Minecraft_ formats.</h3>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://forthebadge.com/images/badges/built-with-love.svg">
|
||||
|
@ -15,8 +15,10 @@
|
||||
```python
|
||||
import Musicreater # 导入转换库
|
||||
|
||||
old_execute_format = False # 指定是否使用旧的execute指令语法(即1.18及以前的《我的世界:基岩版》语法)
|
||||
|
||||
# 首先新建转换对象。
|
||||
conversion = Musicreater.midiConvert()
|
||||
conversion = Musicreater.midiConvert(enable_old_exe_format = old_execute_format)
|
||||
# 值得注意的是,一个转换对象可以转换多个文件。
|
||||
# 也就是在实例化的时候不进行对文件的绑定。
|
||||
# 如果有调试需要,可以在实例化时传入参数 debug = True
|
||||
@ -26,10 +28,9 @@ conversion = Musicreater.midiConvert()
|
||||
# 地址都为字符串类型,不能传入文件流
|
||||
midi_path = "./where/you/place/.midi/files.mid"
|
||||
output_folder = "./where/you/want2/convert/into/"
|
||||
old_execute_format = False # 指定是否使用旧的execute指令语法(即1.18及以前的《我的世界:基岩版》语法)
|
||||
|
||||
# 设定基本转换参数
|
||||
conversion.convert(midi_path,output_folder,old_execute_format)
|
||||
conversion.convert(midi_path,output_folder)
|
||||
|
||||
# 进行转换并接受输出,具体的参数均在代码之文档中有相关说明
|
||||
method_id = 3 # 指定使用的转换算法
|
||||
@ -92,13 +93,13 @@ print(convertion_result)
|
||||
|
||||
考虑到进行《我的世界》游戏开发时,为了节约常加载区域,很多游戏会将指令区设立为一种层叠式的结构。这种结构会限制每一层的指令系统的高度,但是虽然长宽也是有限的,却仍然比其纵轴延伸得更加自由。
|
||||
|
||||
所以,结构的生成形状依照给定的高度和内含指令的数量决定。其$Z$轴延伸长度为指令方块数量对于给定高度之商的向下取整结果的平方根的向下取整。用数学公式的方式表达,则是:
|
||||
所以,结构的生成形状依照给定的高度和内含指令的数量决定。其 $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`。如此往复,直至指令链堆叠完成。
|
||||
我们的结构生成器在生成指令链时,将首先以相对坐标系 $(0, 0, 0)$ (即相对原点)开始,自下向上堆叠高度轴(即 $Y$ 轴)的长,当高度轴达到了限制的高度时,便将 $Z$ 轴向正方向堆叠`1`个方块,并开始自上向下重新堆叠,直至高度轴坐标达到相对为`0`。若当所生成结构的 $Z$ 轴长达到了其最大延伸长度,则此结构生成器将反转 $Z$ 轴的堆叠方向,直至 $Z$ 轴坐标相对为`0`。如此往复,直至指令链堆叠完成。
|
||||
|
||||
## 播放器
|
||||
|
||||
@ -121,9 +122,9 @@ print(convertion_result)
|
||||
|`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$|
|
||||
|`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}$|
|
||||
|`Ptc`|音符的音高|这是决定音调的参数。以 $P$ 表示此参数, $n$ 表示其在MIDI中的编号, $x$ 表示一定的音域偏移,则计算公式为: $P = 2^\frac{n-60-x}{12}$ |
|
||||
|
||||
后四个参数决定了这个音的性质,而前两个参数仅仅是为了决定音播放的时间。
|
||||
|
||||
@ -141,9 +142,9 @@ print(convertion_result)
|
||||
|--------|-----------|----------|
|
||||
|`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$|
|
||||
|`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}$|
|
||||
|`Ptc`|音符的音高|这是决定音调的参数。以 $P$ 表示此参数, $n$ 表示其在MIDI中的编号,$x$表示一定的音域偏移,则计算公式为: $P = 2^\frac{n-60-x}{12}$ |
|
||||
|
||||
其中后四个参数决定了这个音的性质。
|
||||
|
||||
|
24
example.py
24
example.py
@ -20,8 +20,6 @@ import os
|
||||
import Musicreater
|
||||
|
||||
|
||||
|
||||
|
||||
# 获取midi列表
|
||||
midi_path = input(f"请输入MIDI路径:")
|
||||
|
||||
@ -46,9 +44,6 @@ fileFormat = int(input(f"请输入输出格式[BDX(1) 或 MCPACK(0)]:").lower(
|
||||
playerFormat = int(input(f"请选择播放方式[计分板(1) 或 延迟(0)]:").lower())
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 真假字符串判断
|
||||
def bool_str(sth: str) -> bool:
|
||||
try:
|
||||
@ -61,6 +56,7 @@ def bool_str(sth: str) -> bool:
|
||||
else:
|
||||
raise ValueError("布尔字符串啊?")
|
||||
|
||||
|
||||
debug = False
|
||||
|
||||
if os.path.exists("./demo_config.json"):
|
||||
@ -75,40 +71,40 @@ else:
|
||||
# 提示语 检测函数 错误提示语
|
||||
for args in [
|
||||
(
|
||||
f'输入音量:',
|
||||
f"输入音量:",
|
||||
float,
|
||||
),
|
||||
(
|
||||
f'输入播放速度:',
|
||||
f"输入播放速度:",
|
||||
float,
|
||||
),
|
||||
(
|
||||
f'是否启用进度条:',
|
||||
f"是否启用进度条:",
|
||||
bool_str,
|
||||
),
|
||||
(
|
||||
f'计分板名称:',
|
||||
f"计分板名称:",
|
||||
str,
|
||||
)
|
||||
if playerFormat == 1
|
||||
else (
|
||||
f'玩家选择器:',
|
||||
f"玩家选择器:",
|
||||
str,
|
||||
),
|
||||
(
|
||||
f'是否自动重置计分板:',
|
||||
f"是否自动重置计分板:",
|
||||
bool_str,
|
||||
)
|
||||
if playerFormat == 1
|
||||
else (),
|
||||
(
|
||||
f'作者名称:',
|
||||
f"作者名称:",
|
||||
str,
|
||||
)
|
||||
if fileFormat == 1
|
||||
else (),
|
||||
(
|
||||
f'最大结构高度:',
|
||||
f"最大结构高度:",
|
||||
int,
|
||||
)
|
||||
if fileFormat == 1
|
||||
@ -117,7 +113,7 @@ else:
|
||||
if args:
|
||||
prompts.append(args[1](input(args[0])))
|
||||
|
||||
conversion = Musicreater.midiConvert(debug)
|
||||
conversion = Musicreater.midiConvert(debug=debug)
|
||||
|
||||
|
||||
print(f"正在处理 {midi_path} :")
|
||||
|
@ -1,6 +1,8 @@
|
||||
|
||||
from Musicreater import midiConvert
|
||||
|
||||
conversion = midiConvert()
|
||||
conversion.convert(input("midi path:"),input("out path:"))
|
||||
conversion.to_mcstructure_file_with_delay(3,)
|
||||
conversion = midiConvert(enable_old_exe_format=False)
|
||||
conversion.convert(input("midi路径:"), input("输出路径:"))
|
||||
|
||||
conversion.to_mcstructure_file_with_delay(
|
||||
3,
|
||||
)
|
||||
|
@ -1,3 +1,3 @@
|
||||
Brotli>=1.0.9
|
||||
mido>=1.2.10
|
||||
TrimMCStruct>=0.0.5.5
|
||||
TrimMCStruct>=0.0.5.6
|
Loading…
Reference in New Issue
Block a user