mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2026-01-08 12:52:11 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab3c55e7b7 | ||
|
|
082934d918 | ||
|
|
28d59f8be9 | ||
|
|
1103a06897 | ||
|
|
9734bf493a | ||
|
|
dc1efc9e84 |
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -1,3 +0,0 @@
|
||||
*.yaml linguist-language=Python
|
||||
*.xml linguist-language=Python
|
||||
*.md linguist-language=Python
|
||||
24
.gitignore
vendored
24
.gitignore
vendored
@@ -5,22 +5,8 @@
|
||||
|
||||
# mystuff
|
||||
/.vscode
|
||||
/*.mid
|
||||
/*.midi
|
||||
/*.mcpack
|
||||
/*.bdx
|
||||
/*.json
|
||||
/*.mcstructure
|
||||
.mscbackup
|
||||
/logs
|
||||
/languages
|
||||
/llc_cli.py
|
||||
/utils
|
||||
test.py
|
||||
RES.txt
|
||||
/MSCT_Packer.py
|
||||
/Packer/*.MPK
|
||||
/Packer/checksum.txt
|
||||
*.mid
|
||||
*.midi
|
||||
|
||||
# Byte-compiled / optimized
|
||||
__pycache__/
|
||||
@@ -160,9 +146,3 @@ cython_debug/
|
||||
|
||||
# Pycharm
|
||||
/.idea
|
||||
|
||||
# log
|
||||
/.log
|
||||
|
||||
# package
|
||||
.7z
|
||||
|
||||
88
1.py
Normal file
88
1.py
Normal file
@@ -0,0 +1,88 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
################################################################################
|
||||
## Form generated from reading UI file '1.ui'
|
||||
##
|
||||
## Created by: Qt User Interface Compiler version 6.4.1
|
||||
##
|
||||
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
################################################################################
|
||||
|
||||
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||
QMetaObject, QObject, QPoint, QRect,
|
||||
QSize, QTime, QUrl, Qt)
|
||||
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||
QFont, QFontDatabase, QGradient, QIcon,
|
||||
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||
from PySide6.QtWidgets import (QApplication, QMainWindow, QMenuBar, QSizePolicy,
|
||||
QStatusBar, QTabWidget, QToolButton, QVBoxLayout,
|
||||
QWidget)
|
||||
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, MainWindow):
|
||||
if not MainWindow.objectName():
|
||||
MainWindow.setObjectName(u"MainWindow")
|
||||
MainWindow.resize(800, 600)
|
||||
self.centralwidget = QWidget(MainWindow)
|
||||
self.centralwidget.setObjectName(u"centralwidget")
|
||||
self.verticalLayoutWidget_2 = QWidget(self.centralwidget)
|
||||
self.verticalLayoutWidget_2.setObjectName(u"verticalLayoutWidget_2")
|
||||
self.verticalLayoutWidget_2.setGeometry(QRect(0, 0, 861, 681))
|
||||
self.verticalLayout_2 = QVBoxLayout(self.verticalLayoutWidget_2)
|
||||
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
||||
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||
self.tabWidget = QTabWidget(self.verticalLayoutWidget_2)
|
||||
self.tabWidget.setObjectName(u"tabWidget")
|
||||
self.tab = QWidget()
|
||||
self.tab.setObjectName(u"tab")
|
||||
self.toolButton_2 = QToolButton(self.tab)
|
||||
self.toolButton_2.setObjectName(u"toolButton_2")
|
||||
self.toolButton_2.setGeometry(QRect(30, 10, 24, 22))
|
||||
self.tabWidget.addTab(self.tab, "")
|
||||
self.tab_2 = QWidget()
|
||||
self.tab_2.setObjectName(u"tab_2")
|
||||
self.tabWidget.addTab(self.tab_2, "")
|
||||
|
||||
self.verticalLayout_2.addWidget(self.tabWidget)
|
||||
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
self.menubar = QMenuBar(MainWindow)
|
||||
self.menubar.setObjectName(u"menubar")
|
||||
self.menubar.setGeometry(QRect(0, 0, 800, 26))
|
||||
MainWindow.setMenuBar(self.menubar)
|
||||
self.statusbar = QStatusBar(MainWindow)
|
||||
self.statusbar.setObjectName(u"statusbar")
|
||||
MainWindow.setStatusBar(self.statusbar)
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
self.toolButton_2.clicked.connect(self.toolButton_2.click)
|
||||
|
||||
self.tabWidget.setCurrentIndex(0)
|
||||
|
||||
QMetaObject.connectSlotsByName(MainWindow)
|
||||
|
||||
# setupUi
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
|
||||
self.toolButton_2.setText(QCoreApplication.translate("MainWindow", u"...", None))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab),
|
||||
QCoreApplication.translate("MainWindow", u"Tab 1", None))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2),
|
||||
QCoreApplication.translate("MainWindow", u"Tab 2", None))
|
||||
# retranslateUi
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv) # 创建一个QApplication,也就是你要开发的软件app
|
||||
MainWindow = QtWidgets.QMainWindow() # 创建一个QMainWindow,用来装载你需要的各种组件、控件
|
||||
# MainWindow = QtWidgets.QWidget() # 创建一个QMainWindow,用来装载你需要的各种组件、控件
|
||||
ui = Ui_MainWindow() # ui是你创建的ui类的实例化对象
|
||||
ui.setupUi(MainWindow) # 执行类中的setupUi方法,方法的参数是第二步中创建的QMainWindow
|
||||
MainWindow.show() # 执行QMainWindow的show()方法,显示这个QMainWindow
|
||||
sys.exit(app.exec_()) # 使用exit()或者点击关闭按钮退出QApplication
|
||||
91
1.ui
Normal file
91
1.ui
Normal file
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QWidget" name="verticalLayoutWidget_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>861</width>
|
||||
<height>681</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Tab 1</string>
|
||||
</attribute>
|
||||
<widget class="QToolButton" name="toolButton_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>10</y>
|
||||
<width>24</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Tab 2</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>26</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>toolButton_2</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>toolButton_2</receiver>
|
||||
<slot>click()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>44</x>
|
||||
<y>76</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>46</x>
|
||||
<y>71</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -3,7 +3,7 @@
|
||||
1. 音·创的全部开发者享有其完整版权,其开发者可以在任一时刻终止以后音·创源代码开放,若经由其开发者授予特殊权利,则授权对象可以将源代码进行特定的被特殊授权的操作
|
||||
2. 音·创或(及)其代码允许在 Apache2.0 协议的条款与说明下进行非商业使用
|
||||
3. 除部分代码特殊声明外,音·创允许对其或(及)其代码进行商业化使用,但是需要经过音·创主要开发者(诸葛亮与八卦阵、金羿)的一致授权,同时,授权对象在商业化授权的使用过程中必须依照 Apache2.0 协议的条款与说明
|
||||
4. 若存在对于音·创包含的部分代码的特殊开源声明,则此部分代码依照其特定的开源方式授权,但若此部分代码经由此部分代码的主要开发者一致特殊授权后商用,则授权对象在商用时依照此部分的开发者所准许的方式(或条款)进行商用
|
||||
4. 若存在对于音·创包含的部分代码的特殊开源声明,则此部分代码依照其特定的开源方式授权,但若此部分代码经由此部分代码的主要开发者一致特殊授权后商用,则授权对象在商用时依照此部分的开发者所准许的方式(或条款)进行商用,或默认依照 Apache2.0 协议进行商业化使用
|
||||
5. Apache2.0 协议的英文原文副本可见下文
|
||||
|
||||
> The English Translation of the TERMS AND CONDITIONS above is listed below
|
||||
@@ -203,7 +203,7 @@
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
|
||||
Copyright 2023 TriM-Organization 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & all the developers of Musicreater
|
||||
Copyright 2022 Team-Ryoun 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray")
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""一个简单的我的世界音频转换库
|
||||
音·创 (Musicreater)
|
||||
是一款免费开源的针对《我的世界》的midi音乐转换库
|
||||
Musicreater(音·创)
|
||||
A free open source library used for convert midi file into formats that is suitable for **Minecraft**.
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
__version__ = "1.7.3"
|
||||
__vername__ = "功能结构化"
|
||||
__author__ = (
|
||||
("金羿", "Eilles Wan"),
|
||||
("诸葛亮与八卦阵", "bgArray"),
|
||||
("偷吃不是Touch", "Touch"),
|
||||
("鸣凤鸽子", "MingFengPigeon"),
|
||||
)
|
||||
__all__ = [
|
||||
# 主要类
|
||||
"MidiConvert",
|
||||
# 附加类
|
||||
"SingleNote",
|
||||
"SingleCommand",
|
||||
"SingleNoteBox",
|
||||
# "TimeStamp", 未来功能
|
||||
# 默认值
|
||||
"DEFAULT_PROGRESSBAR_STYLE",
|
||||
"MM_INSTRUMENT_DEVIATION_TABLE",
|
||||
"MM_CLASSIC_PITCHED_INSTRUMENT_TABLE",
|
||||
"MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE",
|
||||
"MM_TOUCH_PITCHED_INSTRUMENT_TABLE",
|
||||
"MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE",
|
||||
]
|
||||
|
||||
from .main import *
|
||||
@@ -1,842 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
存放常量与数值性内容
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
from .types import Dict, List, Tuple, MidiInstrumentTableType, MidiNoteNameTableType
|
||||
|
||||
x = "x"
|
||||
"""
|
||||
x
|
||||
"""
|
||||
|
||||
y = "y"
|
||||
"""
|
||||
y
|
||||
"""
|
||||
|
||||
z = "z"
|
||||
"""
|
||||
z
|
||||
"""
|
||||
|
||||
|
||||
# Midi用对照表
|
||||
|
||||
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",
|
||||
}
|
||||
"""Midi音高名称对照表"""
|
||||
|
||||
MIDI_PITCHED_NOTE_NAME_GROUP: Dict[int, Tuple[str, str]] = {
|
||||
1: ("钢琴", "Piano"),
|
||||
9: ("半音阶打击乐器", "Chromatic Percussion"),
|
||||
17: ("风琴", "Organ"),
|
||||
25: ("吉他", "Guitar"),
|
||||
33: ("贝斯", "Bass"),
|
||||
41: ("弦乐器", "Strings"),
|
||||
49: ("合奏乐器", "Ensemble"),
|
||||
57: ("铜管乐器", "Brass"),
|
||||
65: ("簧乐器", "Reed"),
|
||||
73: ("吹管乐器", "Pipe"),
|
||||
81: ("合成主旋律", "Synth Lead"),
|
||||
89: ("合成和弦", "Synth Pad"),
|
||||
97: ("合成声效", "Synth Effects"),
|
||||
105: ("民族乐器", "Ethnic"),
|
||||
113: ("打击乐器", "Percussive"),
|
||||
121: ("特殊音效", "Sound Effects"),
|
||||
}
|
||||
"""Midi乐音乐器分组名称对照表"""
|
||||
|
||||
MIDI_PITCHED_NOTE_NAME_TABLE: Dict[int, Tuple[str, str]] = {
|
||||
1: ("原声平台钢琴", "Acoustic Grand Piano"),
|
||||
2: ("亮音原声钢琴", "Bright Acoustic Piano"),
|
||||
3: ("数码电钢琴", "Electric Grand Piano"),
|
||||
4: ("酒吧钢琴", "Honky-tonk Piano"),
|
||||
5: ("电气电钢琴", "Electric Piano 1(Rhodes Piano)"),
|
||||
6: ("合唱效果电钢琴", "Electric Piano 2(Chorused Piano)"),
|
||||
7: ("拨弦古钢琴(羽管键琴)", "Harpsichord"),
|
||||
8: ("古钢琴", "Clavi"),
|
||||
9: ("钢片琴", "Celesta"),
|
||||
10: ("钟琴", "Glockenspiel"),
|
||||
11: ("八音盒", "Music box"),
|
||||
12: ("颤音琴", "Vibraphone"),
|
||||
13: ("马林巴琴", "Marimba"),
|
||||
14: ("木琴", "Xylophone"),
|
||||
15: ("管钟", "Tubular Bells"),
|
||||
16: ("扬琴", "Dulcimer"),
|
||||
17: ("音栓风琴(击杆风琴)", "Drawbar Organ (Hammond Organ)"),
|
||||
18: ("打击风琴", "Percussive Organ"),
|
||||
19: ("摇滚管风琴", "Rock Organ"),
|
||||
20: ("教堂管风琴", "Church Organ"),
|
||||
21: ("簧风琴", "Reed Organ"),
|
||||
22: ("手风琴", "Accordion"),
|
||||
23: ("口琴", "Harmonica"),
|
||||
24: ("探戈手风琴", "Tango Accordion"),
|
||||
25: ("尼龙弦吉他", "Acoustic Guitar (nylon)"),
|
||||
26: ("钢弦吉他", "Acoustic Guitar (steel)"),
|
||||
27: ("爵士电吉他", "Electric Guitar (jazz)"),
|
||||
28: ("清音电吉他", "Electric Guitar (clean)"),
|
||||
29: ("弱音电吉他", "Electric Guitar (muted)"),
|
||||
30: ("过驱电吉他", "Overdriven Guitar"),
|
||||
31: ("失真电吉他", "Distortion Guitar"),
|
||||
32: ("吉他泛音", "Guitar harmonics"),
|
||||
33: ("原声贝斯", "Acoustic Bass"),
|
||||
34: ("指奏电贝斯", "Electric Bass (finger)"),
|
||||
35: ("拨奏电贝斯", "Electric Bass (pick)"),
|
||||
36: ("无品贝斯", "Fretless Bass"),
|
||||
37: ("击弦贝斯 1", "Slap Bass 1"),
|
||||
38: ("击弦贝斯 2", "Slap Bass 2"),
|
||||
39: ("合成贝斯 1", "Synth Bass 1"),
|
||||
40: ("合成贝斯 2", "Synth Bass 2"),
|
||||
41: ("小提琴", "Violin"),
|
||||
42: ("中提琴", "Viola"),
|
||||
43: ("大提琴", "Cello"),
|
||||
44: ("低音提琴", "Contrabass"),
|
||||
45: ("颤弓弦乐(弦乐震音)", "Tremolo Strings"),
|
||||
46: ("弹拨弦乐(弦乐拨奏)", "Pizzicato Strings"),
|
||||
47: ("竖琴", "Orchestral Harp"),
|
||||
48: ("定音鼓", "Timpani"),
|
||||
49: ("弦乐合奏 1", "String Ensemble 1"),
|
||||
50: ("弦乐合奏 2", "String Ensemble 2"),
|
||||
51: ("合成弦乐 1", "Synth Strings 1"),
|
||||
52: ("合成弦乐 2", "Synth Strings 2"),
|
||||
53: ("合唱“啊”音", "Choir Aahs"),
|
||||
54: ("人声“嘟”音", "Voice Oohs"),
|
||||
55: ("合成人声", "Synth Voice"),
|
||||
56: ("交响打击乐", "Orchestra Hit"),
|
||||
57: ("小号", "Trumpet"),
|
||||
58: ("长号", "Trombone"),
|
||||
59: ("大号", "Tuba"),
|
||||
60: ("弱音小号", "Muted Trumpet"),
|
||||
61: ("圆号(法国号)", "French Horn"),
|
||||
62: ("铜管乐组", "Brass Section"),
|
||||
63: ("合成铜管 1", "Synth Brass 1"),
|
||||
64: ("合成铜管 2", "Synth Brass 2"),
|
||||
65: ("高音萨克斯风", "Soprano Sax"),
|
||||
66: ("中音萨克斯风", "Alto Sax"),
|
||||
67: ("次中音萨克斯风", "Tenor Sax"),
|
||||
68: ("上低音萨克斯风", "Baritone Sax"),
|
||||
69: ("双簧管", "Oboe"),
|
||||
70: ("英国管", "English Horn"),
|
||||
71: ("大管(巴松管)", "Bassoon"),
|
||||
72: ("单簧管(黑管)", "Clarinet"),
|
||||
73: ("短笛", "Piccolo"),
|
||||
74: ("长笛", "Flute"),
|
||||
75: ("竖笛", "Recorder"),
|
||||
76: ("排笛", "Pan Flute"),
|
||||
77: ("瓶笛", "Blown Bottle"),
|
||||
78: ("尺八", "Shakuhachi"),
|
||||
79: ("哨子", "Whistle"),
|
||||
80: ("陶笛", "Ocarina"),
|
||||
81: ("方波", "Lead 1 (square)"),
|
||||
82: ("锯齿波", "Lead 2 (sawtooth)"),
|
||||
83: ("汽笛风琴", "Lead 3 (calliope)"),
|
||||
84: ("合成吹管", "Lead 4 (chiff)"),
|
||||
85: ("合成电吉他", "Lead 5 (charang)"),
|
||||
86: ("人声键盘", "Lead 6 (voice)"),
|
||||
87: ("五度音", "Lead 7 (fifths)"),
|
||||
88: ("低音+主音", "Lead 8 (bass+lead)"),
|
||||
89: ("新纪元", "Pad 1 (new age)"),
|
||||
90: ("暖温", "Pad 2 (warm)"),
|
||||
91: ("复合成音", "Pad 3 (polysynth)"),
|
||||
92: ("人声合唱", "Pad 4 (choir)"),
|
||||
93: ("弓弦", "Pad 5 (bowed)"),
|
||||
94: ("银铃", "Pad 6 (metallic)"),
|
||||
95: ("荣光", "Pad 7 (halo)"),
|
||||
96: ("轻扫", "Pad 8 (sweep)"),
|
||||
97: ("夏雨", "FX 1 (rain)"),
|
||||
98: ("音轨", "FX 2 (soundtrack)"),
|
||||
99: ("水晶", "FX 3 (crystal)"),
|
||||
100: ("大气", "FX 4 (atmosphere)"),
|
||||
101: ("轻曼", "FX 5 (light)"),
|
||||
102: ("魅影", "FX 6 (goblins)"),
|
||||
103: ("回响", "FX 7 (echoes)"),
|
||||
104: ("科幻", "FX 8 (sci-fi)"),
|
||||
105: ("西塔琴", "Sitar"),
|
||||
106: ("五弦琴(班卓琴)", "Banjo"),
|
||||
107: ("三味线", "Shamisen"),
|
||||
108: ("日本筝", "Koto"),
|
||||
109: ("卡林巴铁片琴", "Kalimba"),
|
||||
110: ("苏格兰风笛", "Bagpipe"),
|
||||
111: ("古提琴", "Fiddle"),
|
||||
112: ("唢呐", "Shanai"),
|
||||
113: ("铃铛", "Tinkle Bell"),
|
||||
114: ("拉丁打铃", "Agogo"),
|
||||
115: ("钢鼓", "Steel Drums"),
|
||||
116: ("木块", "Woodblock"),
|
||||
117: ("太鼓", "Taiko Drum"),
|
||||
118: ("古式高音鼓", "Melodic Tom"),
|
||||
119: ("合成鼓", "Synth Drum"),
|
||||
120: ("铜钹", "Reverse Cymbal"),
|
||||
121: ("吉他品格杂音", "Guitar Fret Noise"),
|
||||
122: ("呼吸杂音", "Breath Noise"),
|
||||
123: ("浪潮", "Seashore"),
|
||||
124: ("鸟鸣", "Bird Tweet"),
|
||||
125: ("电话", "Telephone"),
|
||||
126: ("直升机", "Helicopter"),
|
||||
127: ("鼓掌", "Applause"),
|
||||
128: ("射击", "Gunshot"),
|
||||
}
|
||||
"""Midi乐音乐器名称对照表"""
|
||||
|
||||
MIDI_PERCUSSION_NOTE_NAME_TABLE: Dict[int, Tuple[str, str]] = {
|
||||
35: ("原声大鼓", "Acoustic Bass Drum"),
|
||||
36: ("大鼓", "Bass Drum 1"),
|
||||
37: ("小鼓鼓边", "Side Stick"),
|
||||
38: ("原声小军鼓", "Acoustic Snare"),
|
||||
39: ("拍手", "Hand Clap"),
|
||||
40: ("电子小军鼓", "Electric Snare"),
|
||||
41: ("低音落地桶鼓", "Low Floor Tom"),
|
||||
42: ("闭镲", "Closed Hi-Hat"),
|
||||
43: ("高音落地桶鼓", "High Floor Tom"),
|
||||
44: ("脚踏踩镲", "Pedal Hi-Hat"),
|
||||
45: ("低桶鼓", "Low Tom"),
|
||||
46: ("开镲", "Open Hi-Hat"),
|
||||
47: ("低音中桶鼓", "Low-Mid Tom"),
|
||||
48: ("高音中桶鼓", "Hi Mid Tom 2"),
|
||||
49: ("强音钹 1", "Crash Cymbal 1"),
|
||||
50: ("高桶鼓", "High Tom"),
|
||||
51: ("打点钹 1", "Ride Cymbal 1"),
|
||||
52: ("钹", "Chinese Cymbal"),
|
||||
53: ("圆铃", "Ride Bell"),
|
||||
54: ("铃鼓", "Tambourine"),
|
||||
55: ("小钹铜钹", "Splash Cymbal"),
|
||||
56: ("牛铃", "Cowbell"),
|
||||
57: ("强音钹 2", "Crash Cymbal 2"),
|
||||
58: ("颤音器", "Vibra-Slap"),
|
||||
59: ("打点钹 2", "Ride Cymbal 2"),
|
||||
60: ("高音邦加鼓", "Hi Bongo"),
|
||||
61: ("低音邦加鼓", "Low Bongo"),
|
||||
62: ("弱音高音康加鼓", "Mute Hi Conga"),
|
||||
63: ("强音高音康加鼓", "Open Hi Conga"),
|
||||
64: ("低音康加鼓", "Low Conga"),
|
||||
65: ("高音天巴鼓", "High Timbale"),
|
||||
66: ("低音天巴鼓", "Low Timbale"),
|
||||
67: ("高音阿哥哥", "High Agogo"),
|
||||
68: ("低音阿哥哥", "Low Agogo"),
|
||||
69: ("串珠", "Cabasa"),
|
||||
70: ("沙铃", "Maracas"),
|
||||
71: ("短口哨", "Short Whistle"),
|
||||
72: ("长口哨", "Long Whistle"),
|
||||
73: ("短刮壶", "Short Guiro"),
|
||||
74: ("长刮壶", "Long Guiro"),
|
||||
75: ("梆子", "Claves"),
|
||||
76: ("高音木块", "Hi Wood Block"),
|
||||
77: ("低音木块", "Low Wood Block"),
|
||||
78: ("弱音锯加鼓", "Mute Cuica"),
|
||||
79: ("开音锯加鼓", "Open Cuica"),
|
||||
80: ("弱音三角铁", "Mute Triangle"),
|
||||
81: ("强音三角铁", "Open Triangle"),
|
||||
}
|
||||
"""Midi打击乐器名称对照表"""
|
||||
|
||||
# Minecraft用对照表
|
||||
|
||||
MC_PERCUSSION_INSTRUMENT_LIST: List[str] = [
|
||||
"note.snare",
|
||||
"note.bd",
|
||||
"note.hat",
|
||||
"note.basedrum",
|
||||
"firework.blast",
|
||||
"firework.twinkle",
|
||||
"fire.ignite",
|
||||
"mob.zombie.wood",
|
||||
]
|
||||
"""打击乐器列表"""
|
||||
|
||||
MC_INSTRUMENT_BLOCKS_TABLE: Dict[str, Tuple[str, ...]] = {
|
||||
"note.bass": ("planks",),
|
||||
"note.snare": ("sand",),
|
||||
"note.hat": ("glass",),
|
||||
"note.bd": ("stone",),
|
||||
"note.basedrum": ("stone",),
|
||||
"note.bell": ("gold_block",),
|
||||
"note.flute": ("clay",),
|
||||
"note.chime": ("packed_ice",),
|
||||
"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": ("stone",), # 无法找到此音效
|
||||
"note.harp": ("dirt",),
|
||||
# 呃……
|
||||
"firework.blast": ("sandstone",),
|
||||
"firework.twinkle": ("red_sandstone",),
|
||||
"fire.ignite": ("concrete_powder",),
|
||||
"mob.zombie.wood": ("sand",),
|
||||
}
|
||||
"""MC乐器对音符盒下垫方块对照表"""
|
||||
|
||||
# Midi对MC通用对照表
|
||||
|
||||
MM_INSTRUMENT_DEVIATION_TABLE: Dict[str, int] = {
|
||||
"note.harp": 6,
|
||||
"note.pling": 6,
|
||||
"note.guitar": 7,
|
||||
"note.iron_xylophone": 6,
|
||||
"note.bell": 4,
|
||||
"note.xylophone": 4,
|
||||
"note.chime": 4,
|
||||
"note.banjo": 6,
|
||||
"note.flute": 5,
|
||||
"note.bass": 8,
|
||||
"note.snare": -1,
|
||||
"note.didgeridoo": 8,
|
||||
"mob.zombie.wood": -1,
|
||||
"note.bit": 6,
|
||||
"note.hat": -1,
|
||||
"note.bd": -1,
|
||||
"firework.blast": -1,
|
||||
"firework.twinkle": -1,
|
||||
"fire.ignite": -1,
|
||||
"note.share": -1,
|
||||
"note.cow_bell": 5,
|
||||
}
|
||||
"""不同乐器的音调偏离对照表"""
|
||||
|
||||
# Midi乐器对MC乐器对照表
|
||||
|
||||
MM_CLASSIC_PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
|
||||
0: ("note.harp", 6),
|
||||
1: ("note.harp", 6),
|
||||
2: ("note.pling", 6),
|
||||
3: ("note.harp", 6),
|
||||
4: ("note.pling", 6),
|
||||
5: ("note.pling", 6),
|
||||
6: ("note.harp", 6),
|
||||
7: ("note.harp", 6),
|
||||
8: ("note.share", -1), # 打击乐器无音域
|
||||
9: ("note.harp", 6),
|
||||
10: ("note.didgeridoo", 8),
|
||||
11: ("note.harp", 6),
|
||||
12: ("note.xylophone", 4),
|
||||
13: ("note.chime", 4),
|
||||
14: ("note.harp", 6),
|
||||
15: ("note.harp", 6),
|
||||
16: ("note.bass", 8),
|
||||
17: ("note.harp", 6),
|
||||
18: ("note.harp", 6),
|
||||
19: ("note.harp", 6),
|
||||
20: ("note.harp", 6),
|
||||
21: ("note.harp", 6),
|
||||
22: ("note.harp", 6),
|
||||
23: ("note.guitar", 7),
|
||||
24: ("note.guitar", 7),
|
||||
25: ("note.guitar", 7),
|
||||
26: ("note.guitar", 7),
|
||||
27: ("note.guitar", 7),
|
||||
28: ("note.guitar", 7),
|
||||
29: ("note.guitar", 7),
|
||||
30: ("note.guitar", 7),
|
||||
31: ("note.bass", 8),
|
||||
32: ("note.bass", 8),
|
||||
33: ("note.bass", 8),
|
||||
34: ("note.bass", 8),
|
||||
35: ("note.bass", 8),
|
||||
36: ("note.bass", 8),
|
||||
37: ("note.bass", 8),
|
||||
38: ("note.bass", 8),
|
||||
39: ("note.bass", 8),
|
||||
40: ("note.harp", 6),
|
||||
41: ("note.harp", 6),
|
||||
42: ("note.harp", 6),
|
||||
43: ("note.harp", 6),
|
||||
44: ("note.iron_xylophone", 6),
|
||||
45: ("note.guitar", 7),
|
||||
46: ("note.harp", 6),
|
||||
47: ("note.harp", 6),
|
||||
48: ("note.guitar", 7),
|
||||
49: ("note.guitar", 7),
|
||||
50: ("note.bit", 6),
|
||||
51: ("note.bit", 6),
|
||||
52: ("note.harp", 6),
|
||||
53: ("note.harp", 6),
|
||||
54: ("note.bit", 6),
|
||||
55: ("note.flute", 5),
|
||||
56: ("note.flute", 5),
|
||||
57: ("note.flute", 5),
|
||||
58: ("note.flute", 5),
|
||||
59: ("note.flute", 5),
|
||||
60: ("note.flute", 5),
|
||||
61: ("note.flute", 5),
|
||||
62: ("note.flute", 5),
|
||||
63: ("note.flute", 5),
|
||||
64: ("note.bit", 6),
|
||||
65: ("note.bit", 6),
|
||||
66: ("note.bit", 6),
|
||||
67: ("note.bit", 6),
|
||||
68: ("note.flute", 5),
|
||||
69: ("note.harp", 6),
|
||||
70: ("note.harp", 6),
|
||||
71: ("note.flute", 5),
|
||||
72: ("note.flute", 5),
|
||||
73: ("note.flute", 5),
|
||||
74: ("note.harp", 6),
|
||||
75: ("note.flute", 5),
|
||||
76: ("note.harp", 6),
|
||||
77: ("note.harp", 6),
|
||||
78: ("note.harp", 6),
|
||||
79: ("note.harp", 6),
|
||||
80: ("note.bit", 6),
|
||||
81: ("note.bit", 6),
|
||||
82: ("note.bit", 6),
|
||||
83: ("note.bit", 6),
|
||||
84: ("note.bit", 6),
|
||||
85: ("note.bit", 6),
|
||||
86: ("note.bit", 6),
|
||||
87: ("note.bit", 6),
|
||||
88: ("note.bit", 6),
|
||||
89: ("note.bit", 6),
|
||||
90: ("note.bit", 6),
|
||||
91: ("note.bit", 6),
|
||||
92: ("note.bit", 6),
|
||||
93: ("note.bit", 6),
|
||||
94: ("note.bit", 6),
|
||||
95: ("note.bit", 6),
|
||||
96: ("note.bit", 6),
|
||||
97: ("note.bit", 6),
|
||||
98: ("note.bit", 6),
|
||||
99: ("note.bit", 6),
|
||||
100: ("note.bit", 6),
|
||||
101: ("note.bit", 6),
|
||||
102: ("note.bit", 6),
|
||||
103: ("note.bit", 6),
|
||||
104: ("note.harp", 6),
|
||||
105: ("note.banjo", 6),
|
||||
106: ("note.harp", 6),
|
||||
107: ("note.harp", 6),
|
||||
108: ("note.harp", 6),
|
||||
109: ("note.harp", 6),
|
||||
110: ("note.harp", 6),
|
||||
111: ("note.guitar", 7),
|
||||
112: ("note.harp", 6),
|
||||
113: ("note.bell", 4),
|
||||
114: ("note.harp", 6),
|
||||
115: ("note.cow_bell", 5),
|
||||
116: ("note.bd", -1), # 打击乐器无音域
|
||||
117: ("note.bass", 8),
|
||||
118: ("note.bit", 6),
|
||||
119: ("note.bd", -1), # 打击乐器无音域
|
||||
120: ("note.guitar", 7),
|
||||
121: ("note.harp", 6),
|
||||
122: ("note.harp", 6),
|
||||
123: ("note.harp", 6),
|
||||
124: ("note.harp", 6),
|
||||
125: ("note.hat", -1), # 打击乐器无音域
|
||||
126: ("note.bd", -1), # 打击乐器无音域
|
||||
127: ("note.snare", -1), # 打击乐器无音域
|
||||
}
|
||||
"""“经典”乐音乐器对照表"""
|
||||
|
||||
MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
|
||||
34: ("note.bd", -1),
|
||||
35: ("note.bd", -1),
|
||||
36: ("note.hat", -1),
|
||||
37: ("note.snare", -1),
|
||||
38: ("note.snare", -1),
|
||||
39: ("note.snare", -1),
|
||||
40: ("note.hat", -1),
|
||||
41: ("note.snare", -1),
|
||||
42: ("note.hat", -1),
|
||||
43: ("note.snare", -1),
|
||||
44: ("note.snare", -1),
|
||||
45: ("note.bell", 4),
|
||||
46: ("note.snare", -1),
|
||||
47: ("note.snare", -1),
|
||||
48: ("note.bell", 4),
|
||||
49: ("note.hat", -1),
|
||||
50: ("note.bell", 4),
|
||||
51: ("note.bell", 4),
|
||||
52: ("note.bell", 4),
|
||||
53: ("note.bell", 4),
|
||||
54: ("note.bell", 4),
|
||||
55: ("note.bell", 4),
|
||||
56: ("note.snare", -1),
|
||||
57: ("note.hat", -1),
|
||||
58: ("note.chime", 4),
|
||||
59: ("note.iron_xylophone", 6),
|
||||
60: ("note.bd", -1),
|
||||
61: ("note.bd", -1),
|
||||
62: ("note.xylophone", 4),
|
||||
63: ("note.xylophone", 4),
|
||||
64: ("note.xylophone", 4),
|
||||
65: ("note.hat", -1),
|
||||
66: ("note.bell", 4),
|
||||
67: ("note.bell", 4),
|
||||
68: ("note.hat", -1),
|
||||
69: ("note.hat", -1),
|
||||
70: ("note.flute", 5),
|
||||
71: ("note.flute", 5),
|
||||
72: ("note.hat", -1),
|
||||
73: ("note.hat", -1),
|
||||
74: ("note.xylophone", 4),
|
||||
75: ("note.hat", -1),
|
||||
76: ("note.hat", -1),
|
||||
77: ("note.xylophone", 4),
|
||||
78: ("note.xylophone", 4),
|
||||
79: ("note.bell", 4),
|
||||
80: ("note.bell", 4),
|
||||
}
|
||||
"""“经典”打击乐器对照表"""
|
||||
|
||||
# 以下是由 Touch “偷吃” 带来的高准确率音效对照表
|
||||
# 包括乐音乐器对照和打击乐器对照
|
||||
|
||||
MM_TOUCH_PITCHED_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
|
||||
0: ("note.harp", 6),
|
||||
1: ("note.harp", 6),
|
||||
2: ("note.pling", 6),
|
||||
3: ("note.harp", 6),
|
||||
4: ("note.pling", 6),
|
||||
5: ("note.pling", 6),
|
||||
6: ("note.guitar", 7),
|
||||
7: ("note.guitar", 7),
|
||||
8: ("note.iron_xylophone", 8),
|
||||
9: ("note.bell", 4), # 打击乐器无音域
|
||||
10: ("note.iron_xylophone", 6),
|
||||
11: ("note.iron_xylophone", 6),
|
||||
12: ("note.iron_xylophone", 6),
|
||||
13: ("note.xylophone", 4),
|
||||
14: ("note.chime", 4),
|
||||
15: ("note.banjo", 6),
|
||||
16: ("note.xylophone", 6),
|
||||
17: ("note.iron_xylophone", 6),
|
||||
18: ("note.flute", 5),
|
||||
19: ("note.flute", 5),
|
||||
20: ("note.flute", 5),
|
||||
21: ("note.flute", 5),
|
||||
22: ("note.flute", 5),
|
||||
23: ("note.flute", 5),
|
||||
24: ("note.guitar", 7),
|
||||
25: ("note.guitar", 7),
|
||||
26: ("note.guitar", 7),
|
||||
27: ("note.guitar", 7),
|
||||
28: ("note.guitar", 7),
|
||||
29: ("note.guitar", 7),
|
||||
30: ("note.guitar", 7),
|
||||
31: ("note.bass", 8),
|
||||
32: ("note.bass", 8),
|
||||
33: ("note.bass", 8),
|
||||
34: ("note.bass", 8),
|
||||
35: ("note.bass", 8),
|
||||
36: ("note.bass", 8),
|
||||
37: ("note.bass", 8),
|
||||
38: ("note.bass", 8),
|
||||
39: ("note.bass", 8),
|
||||
40: ("note.flute", 5),
|
||||
41: ("note.flute", 5),
|
||||
42: ("note.flute", 5),
|
||||
43: ("note.bass", 8),
|
||||
44: ("note.flute", 5),
|
||||
45: ("note.iron_xylophone", 6),
|
||||
46: ("note.harp", 6),
|
||||
47: ("note.snare", -1),
|
||||
48: ("note.flute", 5),
|
||||
49: ("note.flute", 5),
|
||||
50: ("note.flute", 5),
|
||||
51: ("note.flute", 5),
|
||||
52: ("note.didgeridoo", 5),
|
||||
53: ("note.flute", 5), # 合唱“啊”音
|
||||
54: ("note.flute", 5), # 人声“嘟”音
|
||||
55: ("mob.zombie.wood", -1), # 合成人声
|
||||
56: ("note.flute", 5),
|
||||
57: ("note.flute", 5),
|
||||
58: ("note.flute", 5),
|
||||
59: ("note.flute", 5),
|
||||
60: ("note.flute", 5),
|
||||
61: ("note.flute", 5),
|
||||
62: ("note.flute", 5),
|
||||
63: ("note.flute", 5),
|
||||
64: ("note.bit", 6),
|
||||
65: ("note.bit", 6),
|
||||
66: ("note.bit", 6),
|
||||
67: ("note.bit", 6),
|
||||
68: ("note.flute", 5),
|
||||
69: ("note.bit", 6),
|
||||
70: ("note.banjo", 6),
|
||||
71: ("note.flute", 5),
|
||||
72: ("note.flute", 5),
|
||||
73: ("note.flute", 5),
|
||||
74: ("note.flute", 5),
|
||||
75: ("note.flute", 5),
|
||||
76: ("note.iron_xylophone", 6),
|
||||
77: ("note.iron_xylophone", 6),
|
||||
78: ("note.flute", 5),
|
||||
79: ("note.flute", 5),
|
||||
80: ("note.bit", 6),
|
||||
81: ("note.bit", 6),
|
||||
82: ("note.flute", 5),
|
||||
83: ("note.flute", 5),
|
||||
84: ("note.guitar", 7),
|
||||
85: ("note.flute", 5),
|
||||
86: ("note.bass", 8),
|
||||
87: ("note.bass", 8),
|
||||
88: ("note.bit", 6),
|
||||
89: ("note.flute", 5),
|
||||
90: ("note.bit", 6),
|
||||
91: ("note.flute", 5),
|
||||
92: ("note.bell", 4),
|
||||
93: ("note.guitar", 7),
|
||||
94: ("note.flute", 5),
|
||||
95: ("note.bit", 6),
|
||||
96: ("note.bit", 6), # 雨声
|
||||
97: ("note.flute", 5),
|
||||
98: ("note.bell", 4),
|
||||
99: ("note.bit", 6), # 大气
|
||||
100: ("note.bit", 6), # 明亮
|
||||
101: ("note.bit", 6), # 鬼怪
|
||||
102: ("note.bit", 6), # 回声
|
||||
103: ("note.bit", 6), # 科幻
|
||||
104: ("note.iron_xylophone", 6),
|
||||
105: ("note.banjo", 6),
|
||||
106: ("note.harp", 6),
|
||||
107: ("note.harp", 6),
|
||||
108: ("note.bell", 4),
|
||||
109: ("note.flute", 5),
|
||||
110: ("note.flute", 5),
|
||||
111: ("note.flute", 5),
|
||||
112: ("note.bell", 4),
|
||||
113: ("note.xylophone", 4),
|
||||
114: ("note.flute", 5),
|
||||
115: ("note.hat", -1), # 打击乐器无音域
|
||||
116: ("note.snare", -1), # 打击乐器无音域
|
||||
117: ("note.snare", -1), # 打击乐器无音域
|
||||
118: ("note.bd", -1), # 打击乐器无音域
|
||||
119: ("firework.blast", -1), # 打击乐器无音域
|
||||
120: ("note.guitar", 7), # 吉他还把杂音
|
||||
121: ("note.harp", 6), # 呼吸声
|
||||
122: ("note.harp", 6), # 海浪声
|
||||
123: ("note.harp", 6), # 鸟鸣
|
||||
124: ("note.bit", 6),
|
||||
125: ("note.hat", -1), # 直升机
|
||||
126: ("firework.twinkle", -1), # 打击乐器无音域
|
||||
127: ("mob.zombie.wood", -1), # 打击乐器无音域
|
||||
}
|
||||
"""“偷吃”乐音乐器对照表"""
|
||||
|
||||
MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE: Dict[int, Tuple[str, int]] = {
|
||||
34: ("note.hat", -1),
|
||||
35: ("note.bd", -1),
|
||||
36: ("note.bd", -1),
|
||||
37: ("note.snare", -1),
|
||||
38: ("note.snare", -1),
|
||||
39: ("fire.ignite", 7),
|
||||
40: ("note.snare", -1),
|
||||
41: ("note.hat", -1),
|
||||
42: ("note.hat", -1),
|
||||
43: ("firework.blast", -1),
|
||||
44: ("note.hat", -1),
|
||||
45: ("note.snare", -1),
|
||||
46: ("note.snare", -1),
|
||||
47: ("note.snare", -1),
|
||||
48: ("note.bell", 4),
|
||||
49: ("note.hat", -1),
|
||||
50: ("note.bell", 4),
|
||||
51: ("note.bell", 4),
|
||||
52: ("note.bell", 4),
|
||||
53: ("note.bell", 4),
|
||||
54: ("note.bell", 4),
|
||||
55: ("note.bell", 4),
|
||||
56: ("note.snare", -1),
|
||||
57: ("note.hat", -1),
|
||||
58: ("note.chime", 4),
|
||||
59: ("note.iron_xylophone", 6),
|
||||
60: ("note.bd", -1),
|
||||
61: ("note.bd", -1),
|
||||
62: ("note.xylophone", 4),
|
||||
63: ("note.xylophone", 4),
|
||||
64: ("note.xylophone", 4),
|
||||
65: ("note.hat", -1),
|
||||
66: ("note.bell", 4),
|
||||
67: ("note.bell", 4),
|
||||
68: ("note.hat", -1),
|
||||
69: ("note.hat", -1),
|
||||
70: ("note.flute", 5),
|
||||
71: ("note.flute", 5),
|
||||
72: ("note.hat", -1),
|
||||
73: ("note.hat", -1),
|
||||
74: ("note.xylophone", 4),
|
||||
75: ("note.hat", -1),
|
||||
76: ("note.hat", -1),
|
||||
77: ("note.xylophone", 4),
|
||||
78: ("note.xylophone", 4),
|
||||
79: ("note.bell", 4),
|
||||
80: ("note.bell", 4),
|
||||
}
|
||||
"""“偷吃”打击乐器对照表"""
|
||||
|
||||
# 即将启用
|
||||
# height2note = {
|
||||
# 0.5: 0,
|
||||
# 0.53: 1,
|
||||
# 0.56: 2,
|
||||
# 0.6: 3,
|
||||
# 0.63: 4,
|
||||
# 0.67: 5,
|
||||
# 0.7: 6,
|
||||
# 0.75: 7,
|
||||
# 0.8: 8,
|
||||
# 0.84: 9,
|
||||
# 0.9: 10,
|
||||
# 0.94: 11,
|
||||
# 1.0: 12,
|
||||
# 1.05: 13,
|
||||
# 1.12: 14,
|
||||
# 1.2: 15,
|
||||
# 1.25: 16,
|
||||
# 1.33: 17,
|
||||
# 1.4: 18,
|
||||
# 1.5: 19,
|
||||
# 1.6: 20,
|
||||
# 1.7: 21,
|
||||
# 1.8: 22,
|
||||
# 1.9: 23,
|
||||
# 2.0: 24,
|
||||
# }
|
||||
# """音高对照表\n
|
||||
# MC音高:音符盒音调"""
|
||||
@@ -1,117 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
存放一堆报错类型
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
class MSCTBaseException(Exception):
|
||||
"""音·创库版本的所有错误均继承于此"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""音·创库版本的所有错误均继承于此"""
|
||||
super().__init__(*args)
|
||||
|
||||
def meow(
|
||||
self,
|
||||
):
|
||||
for i in self.args:
|
||||
print(i + "喵!")
|
||||
|
||||
def crash_it(self):
|
||||
raise self
|
||||
|
||||
|
||||
class MidiFormatException(MSCTBaseException):
|
||||
"""音·创库版本的所有MIDI格式错误均继承于此"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""音·创库版本的所有MIDI格式错误均继承于此"""
|
||||
super().__init__("MIDI格式错误", *args)
|
||||
|
||||
|
||||
class MidiDestroyedError(MSCTBaseException):
|
||||
"""Midi文件损坏"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""Midi文件损坏"""
|
||||
super().__init__("MIDI文件损坏:无法读取MIDI文件", *args)
|
||||
|
||||
|
||||
class MidiUnboundError(MSCTBaseException):
|
||||
"""未定义Midi对象"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""未绑定Midi对象"""
|
||||
super().__init__("未定义MidiFile对象:你甚至没有对象就想要生孩子?", *args)
|
||||
|
||||
|
||||
class CommandFormatError(RuntimeError):
|
||||
"""指令格式与目标格式不匹配而引起的错误"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""指令格式与目标格式不匹配而引起的错误"""
|
||||
super().__init__("指令格式不匹配", *args)
|
||||
|
||||
|
||||
# class CrossNoteError(MidiFormatException):
|
||||
# """同通道下同音符交叉出现所产生的错误"""
|
||||
|
||||
# def __init__(self, *args):
|
||||
# """同通道下同音符交叉出现所产生的错误"""
|
||||
# super().__init__("同通道下同音符交叉", *args)
|
||||
# 这TM是什么错误?
|
||||
# 我什么时候写的这玩意?
|
||||
# 我哪知道这说的是啥?
|
||||
|
||||
|
||||
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设定导致没有乐器可以选择的错误"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""没有Program设定导致没有乐器可以选择的错误"""
|
||||
super().__init__("未指定演奏乐器", *args)
|
||||
|
||||
|
||||
class NoteOnOffMismatchError(MidiFormatException):
|
||||
"""音符开音和停止不匹配的错误"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""音符开音和停止不匹配的错误"""
|
||||
super().__init__("音符不匹配", *args)
|
||||
|
||||
|
||||
class ZeroSpeedError(ZeroDivisionError):
|
||||
"""以0作为播放速度的错误"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""以0作为播放速度的错误"""
|
||||
super().__init__("播放速度为0", *args)
|
||||
@@ -1,405 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
新版本功能以及即将启用的函数
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
from .exceptions import *
|
||||
from .subclass import *
|
||||
from .utils import *
|
||||
from .main import (
|
||||
MidiConvert,
|
||||
MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE,
|
||||
MM_CLASSIC_PITCHED_INSTRUMENT_TABLE,
|
||||
)
|
||||
from .types import Tuple, List, Dict, ChannelType
|
||||
|
||||
|
||||
class FutureMidiConvertRSNB(MidiConvert):
|
||||
"""
|
||||
加入红石音乐适配
|
||||
"""
|
||||
|
||||
music_command_list: Dict[int, SingleNoteBox]
|
||||
"""音乐指令列表"""
|
||||
|
||||
|
||||
class FutureMidiConvertM4(MidiConvert):
|
||||
"""
|
||||
加入插值算法优化音感
|
||||
: 经测试,生成效果已经达到,感觉良好
|
||||
"""
|
||||
|
||||
# 临时用的插值计算函数
|
||||
@staticmethod
|
||||
def _linear_note(
|
||||
_note: SingleNote,
|
||||
_apply_time_division: float = 100,
|
||||
) -> List[SingleNote]:
|
||||
"""传入音符数据,返回以半秒为分割的插值列表
|
||||
:param _note: SingleNote 音符
|
||||
:param _apply_time_division: int 间隔毫秒数
|
||||
:return list[tuple(int开始时间(毫秒), int乐器, int音符, int力度(内置), float音量(播放)),]"""
|
||||
|
||||
if _note.percussive:
|
||||
return [
|
||||
_note,
|
||||
]
|
||||
|
||||
totalCount = int(_note.duration / _apply_time_division)
|
||||
|
||||
if totalCount == 0:
|
||||
return [
|
||||
_note,
|
||||
]
|
||||
# print(totalCount)
|
||||
|
||||
result: List[SingleNote] = []
|
||||
|
||||
for _i in range(totalCount):
|
||||
result.append(
|
||||
SingleNote(
|
||||
instrument=_note.inst,
|
||||
pitch=_note.pitch,
|
||||
velocity=_note.velocity,
|
||||
startime=int(_note.start_time + _i * (_note.duration / totalCount)),
|
||||
lastime=int(_note.duration / totalCount),
|
||||
track_number=_note.track_no,
|
||||
is_percussion=_note.percussive,
|
||||
extra_information=_note.extra_info,
|
||||
)
|
||||
# (
|
||||
# _note.start_time + _i * _apply_time_division,
|
||||
# _note.instrument,
|
||||
# _note.pitch,
|
||||
# _note.velocity,
|
||||
# ((totalCount - _i) / totalCount),
|
||||
# )
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
def to_command_list_in_delay(
|
||||
self,
|
||||
max_volume: float = 1.0,
|
||||
speed: float = 1.0,
|
||||
player_selector: str = "@a",
|
||||
) -> Tuple[List[SingleCommand], int, int]:
|
||||
"""
|
||||
将midi转换为我的世界命令列表,并输出每个音符之后的延迟
|
||||
|
||||
Parameters
|
||||
----------
|
||||
max_volume: float
|
||||
最大播放音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||
speed: float
|
||||
速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||
player_selector: str
|
||||
玩家选择器,默认为`@a`
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple( list[SingleCommand,...], int音乐时长游戏刻, int最大同时播放的指令数量 )
|
||||
"""
|
||||
|
||||
if speed == 0:
|
||||
raise ZeroSpeedError("播放速度仅可为(0,1]范围内的正实数")
|
||||
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
|
||||
|
||||
notes_list: List[SingleNote] = []
|
||||
|
||||
# 此处 我们把通道视为音轨
|
||||
for channel in self.to_music_note_channels().values():
|
||||
for note in channel:
|
||||
note.set_info(
|
||||
note_to_command_parameters(
|
||||
note,
|
||||
(
|
||||
self.percussion_note_referrence_table
|
||||
if note.percussive
|
||||
else self.pitched_note_reference_table
|
||||
),
|
||||
(max_volume) if note.track_no == 0 else (max_volume * 0.9),
|
||||
self.volume_processing_function,
|
||||
)
|
||||
)
|
||||
|
||||
if not note.percussive:
|
||||
notes_list.extend(self._linear_note(note, note.extra_info[3] * 500))
|
||||
else:
|
||||
notes_list.append(note)
|
||||
|
||||
notes_list.sort(key=lambda a: a.start_time)
|
||||
|
||||
self.music_command_list = []
|
||||
multi = max_multi = 0
|
||||
delaytime_previous = 0
|
||||
|
||||
for note in notes_list:
|
||||
delaytime_now = round(note.start_time / speed / 50)
|
||||
if (tickdelay := (delaytime_now - delaytime_previous)) == 0:
|
||||
multi += 1
|
||||
else:
|
||||
max_multi = max(max_multi, multi)
|
||||
multi = 0
|
||||
self.music_command_list.append(
|
||||
SingleCommand(
|
||||
self.execute_cmd_head.format(player_selector)
|
||||
+ r"playsound {} @s ^ ^ ^{} {} {}".format(*note.extra_info),
|
||||
tick_delay=tickdelay,
|
||||
annotation="在{}播放{}%的{}音".format(
|
||||
mctick2timestr(delaytime_now),
|
||||
max_volume * 100,
|
||||
"{}:{}".format(note.extra_info[0], note.extra_info[3]),
|
||||
),
|
||||
)
|
||||
)
|
||||
delaytime_previous = delaytime_now
|
||||
|
||||
self.music_tick_num = round(notes_list[-1].start_time / speed / 50)
|
||||
return self.music_command_list, self.music_tick_num, max_multi + 1
|
||||
|
||||
|
||||
class FutureMidiConvertM5(MidiConvert):
|
||||
"""
|
||||
加入同刻偏移算法优化音感
|
||||
"""
|
||||
|
||||
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 = 500000
|
||||
|
||||
# 我们来用通道统计音乐信息
|
||||
# 但是是用分轨的思路的
|
||||
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: ChannelType = 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_in_delay(
|
||||
self,
|
||||
max_volume: float = 1.0,
|
||||
speed: float = 1.0,
|
||||
player_selector: str = "@a",
|
||||
) -> Tuple[List[SingleCommand], int]:
|
||||
"""
|
||||
使用金羿的转换思路,使用同刻偏移算法优化音感后,将midi转换为我的世界命令列表,并输出每个音符之后的延迟
|
||||
|
||||
Parameters
|
||||
----------
|
||||
max_volume: float
|
||||
最大播放音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||
speed: float
|
||||
速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||
player_selector: str
|
||||
玩家选择器,默认为`@a`
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple( list[SingleCommand,...], 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()
|
||||
|
||||
tracks = {}
|
||||
InstID = -1
|
||||
|
||||
# 此处 我们把通道视为音轨
|
||||
for i in self.channels.keys():
|
||||
# 如果当前通道为空 则跳过
|
||||
if not self.channels[i]:
|
||||
continue
|
||||
|
||||
# 第十通道是打击乐通道
|
||||
SpecialBits = True if i == 9 else False
|
||||
|
||||
# nowChannel = []
|
||||
|
||||
for track_no, track in self.channels[i].items():
|
||||
for msg in track:
|
||||
if msg[0] == "PgmC":
|
||||
InstID = msg[1]
|
||||
|
||||
elif msg[0] == "NoteS":
|
||||
soundID, _X = (
|
||||
inst_to_sould_with_deviation(
|
||||
msg[1], MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE
|
||||
)
|
||||
if SpecialBits
|
||||
else inst_to_sould_with_deviation(
|
||||
InstID, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE
|
||||
)
|
||||
)
|
||||
|
||||
score_now = round(msg[-1] / float(speed) / 50)
|
||||
# print(score_now)
|
||||
|
||||
try:
|
||||
tracks[score_now].append(
|
||||
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)}"
|
||||
)
|
||||
)
|
||||
except KeyError:
|
||||
tracks[score_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)}"
|
||||
)
|
||||
]
|
||||
|
||||
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 (
|
||||
(all_ticks[i + 1] - all_ticks[i])
|
||||
/ len(tracks[all_ticks[i]])
|
||||
< 1
|
||||
)
|
||||
else 1
|
||||
)
|
||||
if j != 0
|
||||
else (
|
||||
(
|
||||
all_ticks[i]
|
||||
- all_ticks[i - 1]
|
||||
- (
|
||||
0
|
||||
if (
|
||||
(all_ticks[i] - all_ticks[i - 1])
|
||||
/ len(tracks[all_ticks[i - 1]])
|
||||
< 1
|
||||
)
|
||||
else (len(tracks[all_ticks[i - 1]]) - 1)
|
||||
)
|
||||
)
|
||||
if i != 0
|
||||
else all_ticks[i]
|
||||
)
|
||||
),
|
||||
annotation="在{}播放{}%的{}音".format(
|
||||
mctick2timestr(
|
||||
i + 0
|
||||
if (
|
||||
(all_ticks[i + 1] - all_ticks[i])
|
||||
/ len(tracks[all_ticks[i]])
|
||||
< 1
|
||||
)
|
||||
else j
|
||||
),
|
||||
max_volume * 100,
|
||||
"",
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
self.music_command_list = results
|
||||
self.music_tick_num = max(all_ticks)
|
||||
return results, self.music_tick_num
|
||||
|
||||
|
||||
class FutureMidiConvertM6(MidiConvert):
|
||||
"""
|
||||
加入插值算法优化音感,但仅用于第一音轨
|
||||
"""
|
||||
|
||||
# TODO 没写完的!!!!
|
||||
@@ -1,264 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
功能测试 若非已知 请勿更改
|
||||
此文件仅供功能测试,并非实际调用的文件
|
||||
请注意,此处的文件均为测试使用
|
||||
不要更改 不要更改 不要更改
|
||||
请注意这里的一切均需要其原作者更改
|
||||
这里用于放置一些新奇的点子
|
||||
用于测试
|
||||
不要更改 不要更改 不要更改!
|
||||
"""
|
||||
|
||||
# 音·创 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
"""
|
||||
音·创 (Musicreater)
|
||||
是一款免费开源的针对《我的世界》的midi音乐转换库
|
||||
Musicreater (音·创)
|
||||
A free open source library used for convert midi file into formats that is suitable for **Minecraft**.
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 ../License.md
|
||||
Terms & Conditions: ../License.md
|
||||
"""
|
||||
|
||||
|
||||
# ============================
|
||||
|
||||
|
||||
import mido
|
||||
|
||||
|
||||
class NoteMessage:
|
||||
def __init__(
|
||||
self, channel, pitch, velocity, startT, lastT, midi, now_bpm, change_bpm=None
|
||||
):
|
||||
self.channel = channel
|
||||
self.note = pitch
|
||||
self.velocity = velocity
|
||||
self.startTime = startT
|
||||
self.lastTime = lastT
|
||||
self.tempo = now_bpm # 这里要程序实现获取bpm可以参考我的程序
|
||||
|
||||
def mt2gt(mt, tpb_a, bpm_a):
|
||||
return mt / tpb_a / bpm_a * 60
|
||||
|
||||
self.startTrueTime = mt2gt(
|
||||
self.startTime, midi.ticks_per_beat, self.tempo
|
||||
) # / 20
|
||||
# delete_extra_zero(round_up())
|
||||
if change_bpm is not None:
|
||||
self.lastTrueTime = mt2gt(
|
||||
self.lastTime, midi.ticks_per_beat, change_bpm
|
||||
) # / 20
|
||||
else:
|
||||
self.lastTrueTime = mt2gt(
|
||||
self.lastTime, midi.ticks_per_beat, self.tempo
|
||||
) # / 20
|
||||
# delete_extra_zero(round_up())
|
||||
print((self.startTime * self.tempo) / (midi.ticks_per_beat * 50000))
|
||||
|
||||
def __str__(self):
|
||||
return (
|
||||
"noteMessage channel="
|
||||
+ str(self.channel)
|
||||
+ " note="
|
||||
+ str(self.note)
|
||||
+ " velocity="
|
||||
+ str(self.velocity)
|
||||
+ " startTime="
|
||||
+ str(self.startTime)
|
||||
+ " lastTime="
|
||||
+ str(self.lastTime)
|
||||
+ " startTrueTime="
|
||||
+ str(self.startTrueTime)
|
||||
+ " lastTrueTime="
|
||||
+ str(self.lastTrueTime)
|
||||
)
|
||||
|
||||
|
||||
def load(mid: mido.MidiFile):
|
||||
|
||||
type_ = [False, False, False] # note_off / note_on+0 / mixed
|
||||
|
||||
is_tempo = False
|
||||
|
||||
# 预检
|
||||
for i, track in enumerate(mid.tracks):
|
||||
for msg in track:
|
||||
# print(msg)
|
||||
if msg.is_meta is not True:
|
||||
if msg.type == "note_on" and msg.velocity == 0:
|
||||
type_[1] = True
|
||||
elif msg.type == "note_off":
|
||||
type_[0] = True
|
||||
if msg.is_meta is True and msg.type == "set_tempo":
|
||||
is_tempo = True
|
||||
|
||||
if is_tempo is not True:
|
||||
raise Exception("这个mid没有可供计算时间的tempo事件")
|
||||
|
||||
if type_[0] is True and type_[1] is True:
|
||||
type_[2] = True
|
||||
type_[1] = False
|
||||
type_[0] = False
|
||||
print(type_)
|
||||
|
||||
bpm = 0
|
||||
recent_change_bpm = 0
|
||||
is_change_bpm = False
|
||||
# 实检
|
||||
for i, track in enumerate(mid.tracks):
|
||||
noteOn = []
|
||||
trackS = []
|
||||
ticks = 0
|
||||
for msg in track:
|
||||
print(msg)
|
||||
ticks += msg.time
|
||||
print(ticks)
|
||||
if msg.is_meta is True and msg.type == "set_tempo":
|
||||
recent_change_bpm = bpm
|
||||
bpm = 60000000 / msg.tempo
|
||||
is_change_bpm = True
|
||||
|
||||
if msg.type == "note_on" and msg.velocity != 0:
|
||||
noteOn.append([msg, msg.note, ticks])
|
||||
if type_[1] is True:
|
||||
if msg.type == "note_on" and msg.velocity == 0:
|
||||
for u in noteOn:
|
||||
index = 0
|
||||
if u[1] == msg.note:
|
||||
lastMessage = u[0]
|
||||
lastTick = u[2]
|
||||
break
|
||||
index += 1
|
||||
print(lastTick)
|
||||
if is_change_bpm and recent_change_bpm != 0:
|
||||
trackS.append(
|
||||
NoteMessage(
|
||||
msg.channel,
|
||||
msg.note,
|
||||
lastMessage.velocity,
|
||||
lastTick,
|
||||
ticks - lastTick,
|
||||
mid,
|
||||
recent_change_bpm,
|
||||
bpm,
|
||||
)
|
||||
)
|
||||
is_change_bpm = False
|
||||
else:
|
||||
trackS.append(
|
||||
NoteMessage(
|
||||
msg.channel,
|
||||
msg.note,
|
||||
lastMessage.velocity,
|
||||
lastTick,
|
||||
ticks - lastTick,
|
||||
mid,
|
||||
bpm,
|
||||
)
|
||||
)
|
||||
# print(noteOn)
|
||||
# print(index)
|
||||
try:
|
||||
noteOn.pop(index)
|
||||
except IndexError:
|
||||
noteOn.pop(index - 1)
|
||||
print(trackS)
|
||||
for j in trackS:
|
||||
print(j)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
load(mido.MidiFile("test.mid"))
|
||||
|
||||
|
||||
# ============================
|
||||
from typing import Literal
|
||||
from ..constants import x, y, z
|
||||
|
||||
|
||||
# 不要用 没写完
|
||||
def delay_to_note_blocks(
|
||||
baseblock: str = "stone",
|
||||
position_forward: Literal["x", "y", "z"] = z,
|
||||
):
|
||||
"""传入音符,生成以音符盒存储的红石音乐
|
||||
:param:
|
||||
baseblock: 中继器的下垫方块
|
||||
position_forward: 结构延长方向
|
||||
:return 是否生成成功
|
||||
"""
|
||||
|
||||
from TrimMCStruct import Structure, Block
|
||||
|
||||
struct = Structure(
|
||||
(_sideLength, max_height, _sideLength), # 声明结构大小
|
||||
)
|
||||
|
||||
log = print
|
||||
|
||||
startpos = [0, 0, 0]
|
||||
|
||||
# 1拍 x 2.5 rt
|
||||
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]
|
||||
@@ -1,839 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
"""
|
||||
音·创 (Musicreater)
|
||||
是一款免费开源的针对《我的世界》的midi音乐转换库
|
||||
Musicreater (音·创)
|
||||
A free open source library used for convert midi file into formats that is suitable for **Minecraft**.
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 音·创 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库根目录下的 License.md
|
||||
|
||||
|
||||
# BUG退散!BUG退散! BUG退散!BUG退散!
|
||||
# 异常、错误作乱之时 異常、誤りが、困った時は
|
||||
# 二六字组!万国码合!二六字组!万国码合! グループ!コード#!グループ!コード#!
|
||||
# 赶快呼叫 程序员!Let's Go! 直ぐに呼びましょプログラマ レッツゴー!
|
||||
|
||||
|
||||
import math
|
||||
import os
|
||||
|
||||
import mido
|
||||
|
||||
from .constants import *
|
||||
from .exceptions import *
|
||||
from .subclass import *
|
||||
from .types import *
|
||||
from .utils import *
|
||||
|
||||
"""
|
||||
学习笔记:
|
||||
tempo: microseconds per quarter note 毫秒每四分音符,换句话说就是一拍占多少毫秒
|
||||
tick: midi帧
|
||||
ticks_per_beat: 帧每拍,即一拍多少帧
|
||||
|
||||
那么:
|
||||
|
||||
tick / ticks_per_beat => amount_of_beats 拍数(四分音符数)
|
||||
|
||||
tempo * amount_of_beats => 毫秒数
|
||||
|
||||
所以:
|
||||
|
||||
tempo * tick / ticks_per_beat => 毫秒数
|
||||
|
||||
###########
|
||||
|
||||
seconds per tick:
|
||||
(tempo / 1000000.0) / ticks_per_beat
|
||||
|
||||
seconds:
|
||||
tick * tempo / 1000000.0 / ticks_per_beat
|
||||
|
||||
microseconds:
|
||||
tick * tempo / 1000.0 / ticks_per_beat
|
||||
|
||||
gameticks:
|
||||
tick * tempo / 1000000.0 / ticks_per_beat * 一秒多少游戏刻
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
VoidMido = Union[mido.MidiFile, None] # void mido
|
||||
"""
|
||||
空Midi类类型
|
||||
"""
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class MidiConvert:
|
||||
"""
|
||||
将Midi文件转换为我的世界内容
|
||||
"""
|
||||
|
||||
midi: VoidMido
|
||||
"""MidiFile对象"""
|
||||
|
||||
pitched_note_reference_table: MidiInstrumentTableType
|
||||
"""乐音乐器Midi-MC对照表"""
|
||||
|
||||
percussion_note_referrence_table: MidiInstrumentTableType
|
||||
"""打击乐器Midi-MC对照表"""
|
||||
|
||||
volume_processing_function: FittingFunctionType
|
||||
"""音量处理函数"""
|
||||
|
||||
midi_music_name: str
|
||||
"""Midi乐曲名"""
|
||||
|
||||
enable_old_exe_format: bool
|
||||
"""是否启用旧版execute指令格式"""
|
||||
|
||||
execute_cmd_head: str
|
||||
"""execute指令头部"""
|
||||
|
||||
channels: Union[ChannelType, NoteChannelType]
|
||||
"""频道信息字典"""
|
||||
|
||||
music_command_list: List[SingleCommand]
|
||||
"""音乐指令列表"""
|
||||
|
||||
music_tick_num: int
|
||||
"""音乐总延迟"""
|
||||
|
||||
progress_bar_command: List[SingleCommand]
|
||||
"""进度条指令列表"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
midi_obj: VoidMido,
|
||||
midi_name: str,
|
||||
enable_old_exe_format: bool = False,
|
||||
pitched_note_rtable: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
||||
percussion_note_rtable: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
||||
vol_processing_function: FittingFunctionType = natural_curve,
|
||||
):
|
||||
"""
|
||||
简单的midi转换类,将midi对象转换为我的世界结构或者包
|
||||
|
||||
Parameters
|
||||
----------
|
||||
midi_obj: mido.MidiFile 对象
|
||||
需要处理的midi对象
|
||||
midi_name: MIDI乐曲名称
|
||||
此音乐之名
|
||||
enable_old_exe_format: bool
|
||||
是否启用旧版(≤1.19)指令格式,默认为否
|
||||
pitched_note_rtable: Dict[int, Tuple[str, int]]
|
||||
乐音乐器Midi-MC对照表
|
||||
percussion_note_rtable: Dict[int, Tuple[str, int]]
|
||||
打击乐器Midi-MC对照表
|
||||
"""
|
||||
|
||||
self.midi: VoidMido = midi_obj
|
||||
|
||||
self.midi_music_name: str = midi_name
|
||||
|
||||
self.enable_old_exe_format: bool = enable_old_exe_format
|
||||
|
||||
self.execute_cmd_head = (
|
||||
"execute {} ~ ~ ~ "
|
||||
if enable_old_exe_format
|
||||
else "execute as {} at @s positioned ~ ~ ~ run "
|
||||
)
|
||||
|
||||
self.pitched_note_reference_table = pitched_note_rtable
|
||||
self.percussion_note_referrence_table = percussion_note_rtable
|
||||
self.volume_processing_function = vol_processing_function
|
||||
|
||||
self.progress_bar_command = self.music_command_list = []
|
||||
self.channels = {}
|
||||
self.music_tick_num = 0
|
||||
|
||||
@classmethod
|
||||
def from_midi_file(
|
||||
cls,
|
||||
midi_file_path: str,
|
||||
old_exe_format: bool = False,
|
||||
pitched_note_table: MidiInstrumentTableType = MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
||||
percussion_note_table: MidiInstrumentTableType = MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
||||
vol_processing_func: FittingFunctionType = natural_curve,
|
||||
):
|
||||
"""
|
||||
直接输入文件地址,将midi文件读入
|
||||
|
||||
Parameters
|
||||
----------
|
||||
midi_file: str
|
||||
midi文件地址
|
||||
enable_old_exe_format: bool
|
||||
是否启用旧版(≤1.19)指令格式,默认为否
|
||||
pitched_note_table: Dict[int, Tuple[str, int]]
|
||||
乐音乐器Midi-MC对照表
|
||||
percussion_note_table: Dict[int, Tuple[str, int]]
|
||||
打击乐器Midi-MC对照表
|
||||
"""
|
||||
|
||||
midi_music_name = os.path.splitext(os.path.basename(midi_file_path))[0].replace(
|
||||
" ", "_"
|
||||
)
|
||||
"""文件名,不含路径且不含后缀"""
|
||||
|
||||
try:
|
||||
return cls(
|
||||
mido.MidiFile(midi_file_path, clip=True),
|
||||
midi_music_name,
|
||||
old_exe_format,
|
||||
pitched_note_table,
|
||||
percussion_note_table,
|
||||
vol_processing_func,
|
||||
)
|
||||
except (ValueError, TypeError) as E:
|
||||
raise MidiDestroyedError(f"文件{midi_file_path}损坏:{E}")
|
||||
except FileNotFoundError as E:
|
||||
raise FileNotFoundError(f"文件{midi_file_path}不存在:{E}")
|
||||
|
||||
# ……真的那么重要吗
|
||||
# 我又几曾何时,知道祂真的会抛下我
|
||||
|
||||
def form_progress_bar(
|
||||
self,
|
||||
max_score: int,
|
||||
scoreboard_name: str,
|
||||
progressbar_style: ProgressBarStyle = DEFAULT_PROGRESSBAR_STYLE,
|
||||
) -> List[SingleCommand]:
|
||||
"""
|
||||
生成进度条
|
||||
|
||||
Parameters
|
||||
----------
|
||||
maxscore: int
|
||||
midi的乐器ID
|
||||
|
||||
scoreboard_name: str
|
||||
所使用的计分板名称
|
||||
|
||||
progressbar_style: tuple
|
||||
此参数详见 ../docs/库的生成与功能文档.md#进度条自定义
|
||||
|
||||
Returns
|
||||
-------
|
||||
list[SingleCommand,]
|
||||
"""
|
||||
pgs_style = progressbar_style.base_style
|
||||
"""用于被替换的进度条原始样式"""
|
||||
|
||||
"""
|
||||
| 标识符 | 指定的可变量 |
|
||||
|---------|----------------|
|
||||
| `%%N` | 乐曲名(即传入的文件名)|
|
||||
| `%%s` | 当前计分板值 |
|
||||
| `%^s` | 计分板最大值 |
|
||||
| `%%t` | 当前播放时间 |
|
||||
| `%^t` | 曲目总时长 |
|
||||
| `%%%` | 当前进度比率 |
|
||||
| `_` | 用以表示进度条占位|
|
||||
"""
|
||||
perEach = max_score / pgs_style.count("_")
|
||||
"""每个进度条代表的分值"""
|
||||
|
||||
result: List[SingleCommand] = []
|
||||
|
||||
if r"%^s" in pgs_style:
|
||||
pgs_style = pgs_style.replace(r"%^s", str(max_score))
|
||||
|
||||
if r"%^t" in pgs_style:
|
||||
pgs_style = pgs_style.replace(r"%^t", mctick2timestr(max_score))
|
||||
|
||||
sbn_pc = scoreboard_name[:2]
|
||||
if r"%%%" in pgs_style:
|
||||
result.append(
|
||||
SingleCommand(
|
||||
'scoreboard objectives add {}PercT dummy "百分比计算"'.format(
|
||||
sbn_pc
|
||||
),
|
||||
annotation="新增临时百分比变量",
|
||||
)
|
||||
)
|
||||
result.append(
|
||||
SingleCommand(
|
||||
self.execute_cmd_head.format(
|
||||
"@a[scores={" + scoreboard_name + "=1..}]"
|
||||
)
|
||||
+ "scoreboard players set MaxScore {} {}".format(
|
||||
scoreboard_name, max_score
|
||||
),
|
||||
annotation="设定音乐最大延迟分数",
|
||||
)
|
||||
)
|
||||
result.append(
|
||||
SingleCommand(
|
||||
self.execute_cmd_head.format(
|
||||
"@a[scores={" + scoreboard_name + "=1..}]"
|
||||
)
|
||||
+ "scoreboard players set n100 {} 100".format(scoreboard_name),
|
||||
annotation="设置常量100",
|
||||
)
|
||||
)
|
||||
result.append(
|
||||
SingleCommand(
|
||||
self.execute_cmd_head.format(
|
||||
"@a[scores={" + scoreboard_name + "=1..}]"
|
||||
)
|
||||
+ "scoreboard players operation @s {} = @s {}".format(
|
||||
sbn_pc + "PercT", scoreboard_name
|
||||
),
|
||||
annotation="赋值临时百分比",
|
||||
)
|
||||
)
|
||||
result.append(
|
||||
SingleCommand(
|
||||
self.execute_cmd_head.format(
|
||||
"@a[scores={" + scoreboard_name + "=1..}]"
|
||||
)
|
||||
+ "scoreboard players operation @s {} *= n100 {}".format(
|
||||
sbn_pc + "PercT", scoreboard_name
|
||||
),
|
||||
annotation="转换临时百分比之单位至%(扩大精度)",
|
||||
)
|
||||
)
|
||||
result.append(
|
||||
SingleCommand(
|
||||
self.execute_cmd_head.format(
|
||||
"@a[scores={" + scoreboard_name + "=1..}]"
|
||||
)
|
||||
+ "scoreboard players operation @s {} /= MaxScore {}".format(
|
||||
sbn_pc + "PercT", scoreboard_name
|
||||
),
|
||||
annotation="计算百分比",
|
||||
)
|
||||
)
|
||||
|
||||
# 那是假的
|
||||
# 一切都并未留下痕迹啊
|
||||
# 那梦又是多么的真实……
|
||||
|
||||
if r"%%t" in pgs_style:
|
||||
result.append(
|
||||
SingleCommand(
|
||||
'scoreboard objectives add {}TMinT dummy "时间计算:分"'.format(
|
||||
sbn_pc
|
||||
),
|
||||
annotation="新增临时分变量",
|
||||
)
|
||||
)
|
||||
result.append(
|
||||
SingleCommand(
|
||||
'scoreboard objectives add {}TSecT dummy "时间计算:秒"'.format(
|
||||
sbn_pc
|
||||
),
|
||||
annotation="新增临时秒变量",
|
||||
)
|
||||
)
|
||||
result.append(
|
||||
SingleCommand(
|
||||
self.execute_cmd_head.format(
|
||||
"@a[scores={" + scoreboard_name + "=1..}]"
|
||||
)
|
||||
+ "scoreboard players set n20 {} 20".format(scoreboard_name),
|
||||
annotation="设置常量20",
|
||||
)
|
||||
)
|
||||
result.append(
|
||||
SingleCommand(
|
||||
self.execute_cmd_head.format(
|
||||
"@a[scores={" + scoreboard_name + "=1..}]"
|
||||
)
|
||||
+ "scoreboard players set n60 {} 60".format(scoreboard_name),
|
||||
annotation="设置常量60",
|
||||
)
|
||||
)
|
||||
|
||||
result.append(
|
||||
SingleCommand(
|
||||
self.execute_cmd_head.format(
|
||||
"@a[scores={" + scoreboard_name + "=1..}]"
|
||||
)
|
||||
+ "scoreboard players operation @s {} = @s {}".format(
|
||||
sbn_pc + "TMinT", scoreboard_name
|
||||
),
|
||||
annotation="赋值临时分",
|
||||
)
|
||||
)
|
||||
result.append(
|
||||
SingleCommand(
|
||||
self.execute_cmd_head.format(
|
||||
"@a[scores={" + scoreboard_name + "=1..}]"
|
||||
)
|
||||
+ "scoreboard players operation @s {} /= n20 {}".format(
|
||||
sbn_pc + "TMinT", scoreboard_name
|
||||
),
|
||||
annotation="转换临时分之单位为秒(缩减精度)",
|
||||
)
|
||||
)
|
||||
result.append(
|
||||
SingleCommand(
|
||||
self.execute_cmd_head.format(
|
||||
"@a[scores={" + scoreboard_name + "=1..}]"
|
||||
)
|
||||
+ "scoreboard players operation @s {} = @s {}".format(
|
||||
sbn_pc + "TSecT", sbn_pc + "TMinT"
|
||||
),
|
||||
annotation="赋值临时秒",
|
||||
)
|
||||
)
|
||||
|
||||
result.append(
|
||||
SingleCommand(
|
||||
self.execute_cmd_head.format(
|
||||
"@a[scores={" + scoreboard_name + "=1..}]"
|
||||
)
|
||||
+ "scoreboard players operation @s {} /= n60 {}".format(
|
||||
sbn_pc + "TMinT", scoreboard_name
|
||||
),
|
||||
annotation="转换临时分之单位为分(缩减精度)",
|
||||
)
|
||||
)
|
||||
|
||||
result.append(
|
||||
SingleCommand(
|
||||
self.execute_cmd_head.format(
|
||||
"@a[scores={" + scoreboard_name + "=1..}]"
|
||||
)
|
||||
+ "scoreboard players operation @s {} %= n60 {}".format(
|
||||
sbn_pc + "TSecT", scoreboard_name
|
||||
),
|
||||
annotation="确定临时秒(框定精度区间)",
|
||||
)
|
||||
)
|
||||
|
||||
for i in range(pgs_style.count("_")):
|
||||
npg_stl = (
|
||||
pgs_style.replace("_", progressbar_style.played_style, i + 1)
|
||||
.replace("_", progressbar_style.to_play_style)
|
||||
.replace(r"%%N", self.midi_music_name)
|
||||
.replace(
|
||||
r"%%s",
|
||||
'"},{"score":{"name":"*","objective":"'
|
||||
+ scoreboard_name
|
||||
+ '"}},{"text":"',
|
||||
)
|
||||
.replace(
|
||||
r"%%%",
|
||||
r'"},{"score":{"name":"*","objective":"'
|
||||
+ sbn_pc
|
||||
+ r'PercT"}},{"text":"%',
|
||||
)
|
||||
.replace(
|
||||
r"%%t",
|
||||
r'"},{"score":{"name":"*","objective":"{-}TMinT"}},{"text":":"},'
|
||||
r'{"score":{"name":"*","objective":"{-}TSecT"}},{"text":"'.replace(
|
||||
r"{-}", sbn_pc
|
||||
),
|
||||
)
|
||||
)
|
||||
result.append(
|
||||
SingleCommand(
|
||||
self.execute_cmd_head.format(
|
||||
r"@a[scores={"
|
||||
+ scoreboard_name
|
||||
+ f"={int(i * perEach)}..{math.ceil((i + 1) * perEach)}"
|
||||
+ r"}]"
|
||||
)
|
||||
+ r'titleraw @s actionbar {"rawtext":[{"text":"'
|
||||
+ npg_stl
|
||||
+ r'"}]}',
|
||||
annotation="进度条显示",
|
||||
)
|
||||
)
|
||||
|
||||
if r"%%%" in pgs_style:
|
||||
result.append(
|
||||
SingleCommand(
|
||||
"scoreboard objectives remove {}PercT".format(sbn_pc),
|
||||
annotation="移除临时百分比变量",
|
||||
)
|
||||
)
|
||||
if r"%%t" in pgs_style:
|
||||
result.append(
|
||||
SingleCommand(
|
||||
"scoreboard objectives remove {}TMinT".format(sbn_pc),
|
||||
annotation="移除临时分变量",
|
||||
)
|
||||
)
|
||||
result.append(
|
||||
SingleCommand(
|
||||
"scoreboard objectives remove {}TSecT".format(sbn_pc),
|
||||
annotation="移除临时秒变量",
|
||||
)
|
||||
)
|
||||
|
||||
self.progress_bar_command = result
|
||||
return result
|
||||
|
||||
def to_music_note_channels(
|
||||
self,
|
||||
default_tempo_value: int = mido.midifiles.midifiles.DEFAULT_TEMPO,
|
||||
ignore_mismatch_error: bool = True,
|
||||
) -> NoteChannelType:
|
||||
"""
|
||||
将midi解析并转换为频道音符字典
|
||||
|
||||
Returns
|
||||
-------
|
||||
以频道作为分割的Midi音符列表字典:
|
||||
Dict[int,List[SingleNote,]]
|
||||
"""
|
||||
|
||||
if self.midi is None:
|
||||
raise MidiUnboundError(
|
||||
"你是否正在使用的是一个由 copy_important 生成的MidiConvert对象?这是不可复用的。"
|
||||
)
|
||||
|
||||
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
||||
midi_channels: NoteChannelType = empty_midi_channels(staff=[])
|
||||
tempo = default_tempo_value
|
||||
|
||||
# 我们来用通道统计音乐信息
|
||||
# 但是是用分轨的思路的
|
||||
for track_no, track in enumerate(self.midi.tracks):
|
||||
microseconds = 0
|
||||
if not track:
|
||||
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:
|
||||
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:
|
||||
if msg.type == "program_change":
|
||||
channel_program[msg.channel] = msg.program
|
||||
|
||||
elif msg.type == "note_on" and msg.velocity != 0:
|
||||
note_queue_A[msg.channel].append(
|
||||
(msg.note, channel_program[msg.channel])
|
||||
)
|
||||
note_queue_B[msg.channel].append((msg.velocity, microseconds))
|
||||
|
||||
elif (msg.type == "note_off") or (
|
||||
msg.type == "note_on" and msg.velocity == 0
|
||||
):
|
||||
if (msg.note, channel_program[msg.channel]) in note_queue_A[
|
||||
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,
|
||||
"无法在上文中找到与之匹配的音符开音消息。",
|
||||
)
|
||||
|
||||
"""整合后的音乐通道格式
|
||||
每个通道包括若干消息元素其中逃不过这三种:
|
||||
|
||||
1 切换乐器消息
|
||||
("PgmC", 切换后的乐器ID: int, 距离演奏开始的毫秒)
|
||||
|
||||
2 音符开始消息
|
||||
("NoteS", 开始的音符ID, 力度(响度), 距离演奏开始的毫秒)
|
||||
|
||||
3 音符结束消息
|
||||
("NoteE", 结束的音符ID, 距离演奏开始的毫秒)"""
|
||||
del tempo, self.channels
|
||||
self.channels = dict(
|
||||
[
|
||||
(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(
|
||||
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("播放速度仅可为(0,1]范围内的正实数")
|
||||
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
|
||||
|
||||
command_channels = []
|
||||
command_amount = 0
|
||||
max_score = 0
|
||||
|
||||
# 此处 我们把通道视为音轨
|
||||
for channel in self.to_music_note_channels().values():
|
||||
# 如果当前通道为空 则跳过
|
||||
if not channel:
|
||||
continue
|
||||
|
||||
this_channel = []
|
||||
|
||||
for note in channel:
|
||||
score_now = round(note.start_time / float(speed) / 50)
|
||||
max_score = max(max_score, score_now)
|
||||
|
||||
(
|
||||
mc_sound_ID,
|
||||
mc_distance_volume,
|
||||
volume_percentage,
|
||||
mc_pitch,
|
||||
) = note_to_command_parameters(
|
||||
note,
|
||||
(
|
||||
self.percussion_note_referrence_table
|
||||
if note.percussive
|
||||
else self.pitched_note_reference_table
|
||||
),
|
||||
(max_volume) if note.track_no == 0 else (max_volume * 0.9),
|
||||
self.volume_processing_function,
|
||||
)
|
||||
|
||||
this_channel.append(
|
||||
SingleCommand(
|
||||
(
|
||||
self.execute_cmd_head.format(
|
||||
"@a[scores=({}={})]".format(scoreboard_name, score_now)
|
||||
.replace("(", r"{")
|
||||
.replace(")", r"}")
|
||||
)
|
||||
+ (
|
||||
r"playsound {} @s ^ ^ ^{} {}".format(
|
||||
mc_sound_ID, mc_distance_volume, volume_percentage
|
||||
)
|
||||
if note.percussive
|
||||
else r"playsound {} @s ^ ^ ^{} {} {}".format(
|
||||
mc_sound_ID,
|
||||
mc_distance_volume,
|
||||
volume_percentage,
|
||||
mc_pitch,
|
||||
)
|
||||
)
|
||||
),
|
||||
annotation=(
|
||||
"在{}播放{}%的{}噪音".format(
|
||||
mctick2timestr(score_now),
|
||||
max_volume * 100,
|
||||
mc_sound_ID,
|
||||
)
|
||||
if note.percussive
|
||||
else "在{}播放{}%的{}乐音".format(
|
||||
mctick2timestr(score_now),
|
||||
max_volume * 100,
|
||||
"{}:{:.2f}".format(mc_sound_ID, mc_pitch),
|
||||
)
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
command_amount += 1
|
||||
|
||||
if this_channel:
|
||||
self.music_command_list.extend(this_channel)
|
||||
command_channels.append(this_channel)
|
||||
|
||||
self.music_tick_num = max_score
|
||||
return command_channels, command_amount, max_score
|
||||
|
||||
def to_command_list_in_delay(
|
||||
self,
|
||||
max_volume: float = 1.0,
|
||||
speed: float = 1.0,
|
||||
player_selector: str = "@a",
|
||||
) -> Tuple[List[SingleCommand], int, int]:
|
||||
"""
|
||||
将midi转换为我的世界命令列表,并输出每个音符之后的延迟
|
||||
|
||||
Parameters
|
||||
----------
|
||||
max_volume: float
|
||||
最大播放音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||
speed: float
|
||||
速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||
player_selector: str
|
||||
玩家选择器,默认为`@a`
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple( list[SingleCommand,...], int音乐时长游戏刻, int最大同时播放的指令数量 )
|
||||
"""
|
||||
|
||||
if speed == 0:
|
||||
raise ZeroSpeedError("播放速度仅可为(0,1]范围内的正实数")
|
||||
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
|
||||
|
||||
notes_list: List[SingleNote] = []
|
||||
|
||||
# 此处 我们把通道视为音轨
|
||||
for channel in self.to_music_note_channels().values():
|
||||
notes_list.extend(channel)
|
||||
|
||||
notes_list.sort(key=lambda a: a.start_time)
|
||||
|
||||
self.music_command_list = []
|
||||
multi = max_multi = 0
|
||||
delaytime_previous = 0
|
||||
|
||||
for note in notes_list:
|
||||
delaytime_now = round(note.start_time / speed / 50)
|
||||
if (tickdelay := (delaytime_now - delaytime_previous)) == 0:
|
||||
multi += 1
|
||||
else:
|
||||
max_multi = max(max_multi, multi)
|
||||
multi = 0
|
||||
|
||||
(
|
||||
mc_sound_ID,
|
||||
mc_distance_volume,
|
||||
volume_percentage,
|
||||
mc_pitch,
|
||||
) = note_to_command_parameters(
|
||||
note,
|
||||
(
|
||||
self.percussion_note_referrence_table
|
||||
if note.percussive
|
||||
else self.pitched_note_reference_table
|
||||
),
|
||||
(max_volume) if note.track_no == 0 else (max_volume * 0.9),
|
||||
self.volume_processing_function,
|
||||
)
|
||||
|
||||
self.music_command_list.append(
|
||||
SingleCommand(
|
||||
command=(
|
||||
self.execute_cmd_head.format(player_selector)
|
||||
+ (
|
||||
r"playsound {} @s ^ ^ ^{} {}".format(
|
||||
mc_sound_ID, mc_distance_volume, volume_percentage
|
||||
)
|
||||
if note.percussive
|
||||
else r"playsound {} @s ^ ^ ^{} {} {}".format(
|
||||
mc_sound_ID,
|
||||
mc_distance_volume,
|
||||
volume_percentage,
|
||||
mc_pitch,
|
||||
)
|
||||
)
|
||||
),
|
||||
annotation=(
|
||||
"在{}播放{}%的{}噪音".format(
|
||||
mctick2timestr(delaytime_now),
|
||||
max_volume * 100,
|
||||
mc_sound_ID,
|
||||
)
|
||||
if note.percussive
|
||||
else "在{}播放{}%的{}乐音".format(
|
||||
mctick2timestr(delaytime_now),
|
||||
max_volume * 100,
|
||||
"{}:{:.2f}".format(mc_sound_ID, mc_pitch),
|
||||
)
|
||||
),
|
||||
tick_delay=tickdelay,
|
||||
),
|
||||
)
|
||||
delaytime_previous = delaytime_now
|
||||
|
||||
self.music_tick_num = round(notes_list[-1].start_time / speed / 50)
|
||||
return self.music_command_list, self.music_tick_num, max_multi + 1
|
||||
|
||||
def copy_important(self):
|
||||
dst = MidiConvert(
|
||||
midi_obj=None,
|
||||
midi_name=self.midi_music_name,
|
||||
enable_old_exe_format=self.enable_old_exe_format,
|
||||
)
|
||||
dst.music_command_list = [i.copy() for i in self.music_command_list]
|
||||
dst.progress_bar_command = [i.copy() for i in self.progress_bar_command]
|
||||
dst.music_tick_num = self.music_tick_num
|
||||
return dst
|
||||
@@ -1,71 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
存放非音·创本体的附加内容(插件?)
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
__all__ = [
|
||||
# 通用
|
||||
"ConvertConfig",
|
||||
"bottem_side_length_of_smallest_square_bottom_box",
|
||||
# 打包
|
||||
"compress_zipfile",
|
||||
"behavior_mcpack_manifest",
|
||||
# MCSTRUCTURE 函数
|
||||
"antiaxis",
|
||||
"forward_IER",
|
||||
"command_statevalue",
|
||||
"form_note_block_in_NBT_struct",
|
||||
"form_repeater_in_NBT_struct",
|
||||
"form_command_block_in_NBT_struct",
|
||||
"commands_to_structure",
|
||||
"commands_to_redstone_delay_structure",
|
||||
# MCSTRUCTURE 常量
|
||||
"AXIS_PARTICULAR_VALUE",
|
||||
"COMPABILITY_VERSION_117",
|
||||
"COMPABILITY_VERSION_119",
|
||||
# BDX 函数
|
||||
"bdx_move",
|
||||
"form_command_block_in_BDX_bytes",
|
||||
"commands_to_BDX_bytes",
|
||||
# BDX 常量
|
||||
"BDX_MOVE_KEY",
|
||||
]
|
||||
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"))
|
||||
|
||||
from .main import ConvertConfig
|
||||
|
||||
from .archive import compress_zipfile, behavior_mcpack_manifest
|
||||
|
||||
from .bdx import (
|
||||
BDX_MOVE_KEY,
|
||||
bdx_move,
|
||||
form_command_block_in_BDX_bytes,
|
||||
commands_to_BDX_bytes,
|
||||
)
|
||||
|
||||
from .common import bottem_side_length_of_smallest_square_bottom_box
|
||||
|
||||
from .mcstructure import (
|
||||
antiaxis,
|
||||
forward_IER,
|
||||
AXIS_PARTICULAR_VALUE,
|
||||
COMPABILITY_VERSION_119,
|
||||
COMPABILITY_VERSION_117,
|
||||
command_statevalue,
|
||||
form_note_block_in_NBT_struct,
|
||||
form_repeater_in_NBT_struct,
|
||||
form_command_block_in_NBT_struct,
|
||||
commands_to_structure,
|
||||
commands_to_redstone_delay_structure,
|
||||
)
|
||||
@@ -1,28 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
用以生成附加包的附加功能
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 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_addon_pack_in_delay",
|
||||
"to_addon_pack_in_score",
|
||||
"to_addon_pack_in_repeater",
|
||||
]
|
||||
__author__ = (("金羿", "Eilles Wan"),)
|
||||
|
||||
from .main import (
|
||||
to_addon_pack_in_delay,
|
||||
to_addon_pack_in_repeater,
|
||||
to_addon_pack_in_score,
|
||||
)
|
||||
@@ -1,543 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 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
|
||||
@@ -1,83 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
存放关于文件打包的内容
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import uuid
|
||||
import zipfile
|
||||
from typing import List, Literal, Union
|
||||
|
||||
|
||||
def compress_zipfile(sourceDir, outFilename, compression=8, exceptFile=None):
|
||||
"""使用compression指定的算法打包目录为zip文件\n
|
||||
默认算法为DEFLATED(8),可用算法如下:\n
|
||||
STORED = 0\n
|
||||
DEFLATED = 8\n
|
||||
BZIP2 = 12\n
|
||||
LZMA = 14\n
|
||||
"""
|
||||
|
||||
zipf = zipfile.ZipFile(outFilename, "w", compression)
|
||||
pre_len = len(os.path.dirname(sourceDir))
|
||||
for parent, dirnames, filenames in os.walk(sourceDir):
|
||||
for filename in filenames:
|
||||
if filename == exceptFile:
|
||||
continue
|
||||
pathfile = os.path.join(parent, filename)
|
||||
arc_name = pathfile[pre_len:].strip(os.path.sep) # 相对路径
|
||||
zipf.write(pathfile, arc_name)
|
||||
zipf.close()
|
||||
|
||||
|
||||
def behavior_mcpack_manifest(
|
||||
pack_description: str = "",
|
||||
pack_version: Union[List[int], Literal[None]] = None,
|
||||
pack_name: str = "",
|
||||
pack_uuid: Union[str, Literal[None]] = None,
|
||||
modules_description: str = "",
|
||||
modules_version: List[int] = [0, 0, 1],
|
||||
modules_uuid: Union[str, Literal[None]] = None,
|
||||
):
|
||||
"""
|
||||
生成一个我的世界行为包组件的定义清单文件
|
||||
"""
|
||||
if not pack_version:
|
||||
now_date = datetime.datetime.now()
|
||||
pack_version = [
|
||||
now_date.year,
|
||||
now_date.month * 100 + now_date.day,
|
||||
now_date.hour * 100 + now_date.minute,
|
||||
]
|
||||
return {
|
||||
"format_version": 1,
|
||||
"header": {
|
||||
"description": pack_description,
|
||||
"version": pack_version,
|
||||
"name": pack_name,
|
||||
"uuid": str(uuid.uuid4()) if not pack_uuid else pack_uuid,
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"description": modules_description,
|
||||
"type": "data",
|
||||
"version": modules_version,
|
||||
"uuid": str(uuid.uuid4()) if not modules_uuid else modules_uuid,
|
||||
}
|
||||
],
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
存放有关BDX结构操作的内容
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 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 List
|
||||
|
||||
from ..constants import x, y, z
|
||||
from ..subclass import SingleCommand
|
||||
from .common import bottem_side_length_of_smallest_square_bottom_box
|
||||
|
||||
BDX_MOVE_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"],
|
||||
}
|
||||
"""key存储了方块移动指令的数据,其中可以用key[x|y|z][0|1]来表示xyz的减或增
|
||||
而key[][2+]是用来增加指定数目的"""
|
||||
|
||||
|
||||
def bdx_move(axis: str, value: int):
|
||||
if value == 0:
|
||||
return b""
|
||||
if abs(value) == 1:
|
||||
return BDX_MOVE_KEY[axis][0 if value == -1 else 1]
|
||||
|
||||
pointer = sum(
|
||||
[
|
||||
value != -1,
|
||||
value < -1 or value > 1,
|
||||
value < -128 or value > 127,
|
||||
value < -32768 or value > 32767,
|
||||
]
|
||||
)
|
||||
|
||||
return BDX_MOVE_KEY[axis][pointer] + value.to_bytes(
|
||||
2 ** (pointer - 2), "big", signed=True
|
||||
)
|
||||
|
||||
|
||||
def form_command_block_in_BDX_bytes(
|
||||
command: str,
|
||||
particularValue: int,
|
||||
impluse: int = 0,
|
||||
condition: bool = False,
|
||||
needRedstone: bool = True,
|
||||
tickDelay: int = 0,
|
||||
customName: str = "",
|
||||
executeOnFirstTick: bool = False,
|
||||
trackOutput: bool = True,
|
||||
):
|
||||
"""
|
||||
使用指定项目返回指定的指令方块放置指令项
|
||||
:param command: `str`
|
||||
指令
|
||||
:param particularValue:
|
||||
方块特殊值,即朝向
|
||||
:0 下 无条件
|
||||
:1 上 无条件
|
||||
:2 z轴负方向 无条件
|
||||
:3 z轴正方向 无条件
|
||||
:4 x轴负方向 无条件
|
||||
:5 x轴正方向 无条件
|
||||
:6 下 无条件
|
||||
:7 下 无条件
|
||||
|
||||
:8 下 有条件
|
||||
:9 上 有条件
|
||||
:10 z轴负方向 有条件
|
||||
:11 z轴正方向 有条件
|
||||
:12 x轴负方向 有条件
|
||||
:13 x轴正方向 有条件
|
||||
:14 下 有条件
|
||||
:14 下 有条件
|
||||
注意!此处特殊值中的条件会被下面condition参数覆写
|
||||
:param impluse: `int 0|1|2`
|
||||
方块类型
|
||||
0脉冲 1循环 2连锁
|
||||
:param condition: `bool`
|
||||
是否有条件
|
||||
:param needRedstone: `bool`
|
||||
是否需要红石
|
||||
:param tickDelay: `int`
|
||||
执行延时
|
||||
:param customName: `str`
|
||||
悬浮字
|
||||
lastOutput: `str`
|
||||
上次输出字符串,注意此处需要留空
|
||||
:param executeOnFirstTick: `bool`
|
||||
首刻执行(循环指令方块是否激活后立即执行,若为False,则从激活时起延迟后第一次执行)
|
||||
:param trackOutput: `bool`
|
||||
是否输出
|
||||
|
||||
:return:str
|
||||
"""
|
||||
block = b"\x24" + particularValue.to_bytes(2, byteorder="big", signed=False)
|
||||
|
||||
for i in [
|
||||
impluse.to_bytes(4, byteorder="big", signed=False),
|
||||
bytes(command, encoding="utf-8") + b"\x00",
|
||||
bytes(customName, encoding="utf-8") + b"\x00",
|
||||
bytes("", encoding="utf-8") + b"\x00",
|
||||
tickDelay.to_bytes(4, byteorder="big", signed=True),
|
||||
executeOnFirstTick.to_bytes(1, byteorder="big"),
|
||||
trackOutput.to_bytes(1, byteorder="big"),
|
||||
condition.to_bytes(1, byteorder="big"),
|
||||
needRedstone.to_bytes(1, byteorder="big"),
|
||||
]:
|
||||
block += i
|
||||
return block
|
||||
|
||||
|
||||
def commands_to_BDX_bytes(
|
||||
commands_list: List[SingleCommand],
|
||||
max_height: int = 64,
|
||||
):
|
||||
"""
|
||||
:param commands: 指令列表(指令, 延迟)
|
||||
:param max_height: 生成结构最大高度
|
||||
:return 成功与否,成功返回(True,未经过压缩的源,结构占用大小),失败返回(False,str失败原因)
|
||||
"""
|
||||
|
||||
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
|
||||
len(commands_list), max_height
|
||||
)
|
||||
_bytes = b""
|
||||
|
||||
y_forward = True
|
||||
z_forward = True
|
||||
|
||||
now_y = 0
|
||||
now_z = 0
|
||||
now_x = 0
|
||||
|
||||
for command in commands_list:
|
||||
_bytes += form_command_block_in_BDX_bytes(
|
||||
command.command_text,
|
||||
(
|
||||
(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=command.conditional,
|
||||
needRedstone=False,
|
||||
tickDelay=command.delay,
|
||||
customName=command.annotation_text,
|
||||
executeOnFirstTick=False,
|
||||
trackOutput=True,
|
||||
)
|
||||
|
||||
# (1 if y_forward else 0) if ( # 如果y+则向上,反之向下
|
||||
# ((now_y != 0) and (not y_forward)) # 如果不是y轴上首个方块
|
||||
# or (y_forward and (now_y != (max_height - 1))) # 如果不是y轴上末端方块
|
||||
# ) else ( # 否则,即是y轴末端或首个方块
|
||||
# (3 if z_forward else 2) if ( # 如果z+则向z轴正方向,反之负方向
|
||||
# ((now_z != 0) and (not z_forward)) # 如果不是z轴上的首个方块
|
||||
# or (z_forward and (now_z != _sideLength - 1)) # 如果不是z轴上的末端方块
|
||||
# ) else 5 # 否则,则要面向x轴正方向
|
||||
# )
|
||||
|
||||
now_y += 1 if y_forward else -1
|
||||
|
||||
if ((now_y >= max_height) and y_forward) or ((now_y < 0) and (not y_forward)):
|
||||
now_y -= 1 if y_forward else -1
|
||||
|
||||
y_forward = not y_forward
|
||||
|
||||
now_z += 1 if z_forward else -1
|
||||
|
||||
if ((now_z >= _sideLength) and z_forward) or (
|
||||
(now_z < 0) and (not z_forward)
|
||||
):
|
||||
now_z -= 1 if z_forward else -1
|
||||
z_forward = not z_forward
|
||||
_bytes += BDX_MOVE_KEY[x][1]
|
||||
now_x += 1
|
||||
else:
|
||||
_bytes += BDX_MOVE_KEY[z][int(z_forward)]
|
||||
|
||||
else:
|
||||
_bytes += BDX_MOVE_KEY[y][int(y_forward)]
|
||||
|
||||
return (
|
||||
_bytes,
|
||||
[
|
||||
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],
|
||||
)
|
||||
@@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
用以生成BDX结构文件的附加功能
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 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_BDX_file_in_score", "to_BDX_file_in_delay"]
|
||||
__author__ = (("金羿", "Eilles Wan"),)
|
||||
|
||||
from .main import to_BDX_file_in_delay, to_BDX_file_in_score
|
||||
@@ -1,226 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
import os
|
||||
|
||||
import brotli
|
||||
|
||||
from ...main import MidiConvert
|
||||
from ...subclass import SingleCommand
|
||||
from ..bdx import (
|
||||
bdx_move,
|
||||
commands_to_BDX_bytes,
|
||||
form_command_block_in_BDX_bytes,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
)
|
||||
from ..main import ConvertConfig
|
||||
|
||||
|
||||
def to_BDX_file_in_score(
|
||||
midi_cvt: MidiConvert,
|
||||
data_cfg: ConvertConfig,
|
||||
scoreboard_name: str = "mscplay",
|
||||
auto_reset: bool = False,
|
||||
author: str = "Eilles",
|
||||
max_height: int = 64,
|
||||
):
|
||||
"""
|
||||
将midi以计分播放器形式转换为BDX结构文件
|
||||
|
||||
Parameters
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
data_cfg: ConvertConfig 对象
|
||||
部分转换通用参数
|
||||
scoreboard_name: str
|
||||
我的世界的计分板名称
|
||||
auto_reset: bool
|
||||
是否自动重置计分板
|
||||
author: str
|
||||
作者名称
|
||||
max_height: int
|
||||
生成结构最大高度
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple[int指令数量, int音乐总延迟, tuple[int,]结构大小, tuple[int,]终点坐标]
|
||||
"""
|
||||
|
||||
cmdlist, command_count, max_score = midi_cvt.to_command_list_in_score(
|
||||
scoreboard_name, data_cfg.volume_ratio, data_cfg.speed_multiplier
|
||||
)
|
||||
|
||||
if not os.path.exists(data_cfg.dist_path):
|
||||
os.makedirs(data_cfg.dist_path)
|
||||
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx")
|
||||
),
|
||||
"w+",
|
||||
) as f:
|
||||
f.write("BD@")
|
||||
|
||||
_bytes = (
|
||||
b"BDX\x00" + author.encode("utf-8") + b" & Musicreater\x00\x01command_block\x00"
|
||||
)
|
||||
|
||||
cmdBytes, size, finalPos = commands_to_BDX_bytes(
|
||||
midi_cvt.music_command_list
|
||||
+ (
|
||||
[
|
||||
SingleCommand(
|
||||
command="scoreboard players reset @a[scores={"
|
||||
+ scoreboard_name
|
||||
+ "="
|
||||
+ str(max_score + 20)
|
||||
+ "}] "
|
||||
+ scoreboard_name,
|
||||
annotation="自动重置计分板",
|
||||
)
|
||||
]
|
||||
if auto_reset
|
||||
else []
|
||||
),
|
||||
max_height - 1,
|
||||
)
|
||||
|
||||
if data_cfg.progressbar_style:
|
||||
pgbBytes, pgbSize, pgbNowPos = commands_to_BDX_bytes(
|
||||
midi_cvt.form_progress_bar(
|
||||
max_score, scoreboard_name, data_cfg.progressbar_style
|
||||
),
|
||||
max_height - 1,
|
||||
)
|
||||
_bytes += pgbBytes
|
||||
_bytes += bdx_move(y, -pgbNowPos[1])
|
||||
_bytes += bdx_move(z, -pgbNowPos[2])
|
||||
_bytes += bdx_move(x, 2)
|
||||
|
||||
size[0] += 2 + pgbSize[0]
|
||||
size[1] = max(size[1], pgbSize[1])
|
||||
size[2] = max(size[2], pgbSize[2])
|
||||
|
||||
_bytes += cmdBytes
|
||||
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx")
|
||||
),
|
||||
"ab+",
|
||||
) as f:
|
||||
f.write(brotli.compress(_bytes + b"XE"))
|
||||
|
||||
return command_count, max_score, size, finalPos
|
||||
|
||||
|
||||
def to_BDX_file_in_delay(
|
||||
midi_cvt: MidiConvert,
|
||||
data_cfg: ConvertConfig,
|
||||
player: str = "@a",
|
||||
author: str = "Eilles",
|
||||
max_height: int = 64,
|
||||
):
|
||||
"""
|
||||
使用method指定的转换算法,将midi转换为BDX结构文件
|
||||
|
||||
Parameters
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
data_cfg: ConvertConfig 对象
|
||||
部分转换通用参数
|
||||
player: str
|
||||
玩家选择器,默认为`@a`
|
||||
author: str
|
||||
作者名称
|
||||
max_height: int
|
||||
生成结构最大高度
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple[int指令数量, int音乐总延迟, tuple[int,]结构大小, tuple[int,]终点坐标]
|
||||
"""
|
||||
|
||||
cmdlist, 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)
|
||||
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx")
|
||||
),
|
||||
"w+",
|
||||
) as f:
|
||||
f.write("BD@")
|
||||
|
||||
_bytes = (
|
||||
b"BDX\x00" + author.encode("utf-8") + b" & Musicreater\x00\x01command_block\x00"
|
||||
)
|
||||
|
||||
cmdBytes, size, finalPos = commands_to_BDX_bytes(cmdlist, max_height - 1)
|
||||
|
||||
if data_cfg.progressbar_style:
|
||||
scb_name = midi_cvt.midi_music_name[:3] + "Pgb"
|
||||
_bytes += form_command_block_in_BDX_bytes(
|
||||
r"scoreboard objectives add {} dummy {}计".replace(r"{}", scb_name),
|
||||
1,
|
||||
customName="初始化进度条",
|
||||
)
|
||||
_bytes += bdx_move(z, 2)
|
||||
_bytes += form_command_block_in_BDX_bytes(
|
||||
r"scoreboard players add {} {} 1".format(player, scb_name),
|
||||
1,
|
||||
1,
|
||||
customName="显示进度条并加分",
|
||||
)
|
||||
_bytes += bdx_move(y, 1)
|
||||
pgbBytes, pgbSize, pgbNowPos = commands_to_BDX_bytes(
|
||||
midi_cvt.form_progress_bar(max_delay, scb_name, data_cfg.progressbar_style),
|
||||
max_height - 1,
|
||||
)
|
||||
_bytes += pgbBytes
|
||||
_bytes += bdx_move(y, -1 - pgbNowPos[1])
|
||||
_bytes += bdx_move(z, -2 - pgbNowPos[2])
|
||||
_bytes += bdx_move(x, 2)
|
||||
_bytes += form_command_block_in_BDX_bytes(
|
||||
r"scoreboard players reset {} {}".format(player, scb_name),
|
||||
1,
|
||||
customName="置零进度条",
|
||||
)
|
||||
_bytes += bdx_move(y, 1)
|
||||
size[0] += 2 + pgbSize[0]
|
||||
size[1] = max(size[1], pgbSize[1])
|
||||
size[2] = max(size[2], pgbSize[2])
|
||||
|
||||
size[1] += 1
|
||||
_bytes += cmdBytes
|
||||
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx")
|
||||
),
|
||||
"ab+",
|
||||
) as f:
|
||||
f.write(brotli.compress(_bytes + b"XE"))
|
||||
|
||||
return len(cmdlist), max_delay, size, finalPos
|
||||
@@ -1,27 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
存放通用的普遍性的插件内容
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
import math
|
||||
|
||||
|
||||
def bottem_side_length_of_smallest_square_bottom_box(total: int, maxHeight: int):
|
||||
"""给定总方块数量和最大高度,返回所构成的图形外切正方形的边长
|
||||
:param total: 总方块数量
|
||||
:param maxHeight: 最大高度
|
||||
:return: 外切正方形的边长 int"""
|
||||
return math.ceil(math.sqrt(math.ceil(total / maxHeight)))
|
||||
@@ -1,106 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
存放附加内容功能
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal, Tuple, Union
|
||||
|
||||
from ..subclass import DEFAULT_PROGRESSBAR_STYLE, ProgressBarStyle
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class ConvertConfig:
|
||||
"""
|
||||
转换通用设置存储类
|
||||
"""
|
||||
|
||||
volume_ratio: float
|
||||
"""音量比例"""
|
||||
|
||||
speed_multiplier: float
|
||||
"""速度倍率"""
|
||||
|
||||
progressbar_style: Union[ProgressBarStyle, None]
|
||||
"""进度条样式"""
|
||||
|
||||
dist_path: str
|
||||
"""输出目录"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
output_path: str,
|
||||
volume: float = 1.0,
|
||||
speed: float = 1.0,
|
||||
progressbar: Union[bool, Tuple[str, Tuple[str, str]], ProgressBarStyle] = True,
|
||||
ignore_progressbar_param_error: bool = False,
|
||||
):
|
||||
"""
|
||||
将已经转换好的数据内容指令载入MC可读格式
|
||||
|
||||
Parameters
|
||||
----------
|
||||
output_path: str
|
||||
生成内容的输出目录
|
||||
volume: float
|
||||
音量比率,范围为(0,1],其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||
speed: float
|
||||
速度倍率,注意:这里的速度指的是播放速度倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||
progressbar: bool|tuple[str, Tuple[str,]]
|
||||
进度条,当此参数为 `True` 时使用默认进度条,为其他的**值为真**的参数时识别为进度条自定义参数,为其他**值为假**的时候不生成进度条
|
||||
|
||||
"""
|
||||
|
||||
self.dist_path = output_path
|
||||
"""输出目录"""
|
||||
|
||||
self.volume_ratio = volume
|
||||
"""音量比例"""
|
||||
|
||||
self.speed_multiplier = speed
|
||||
"""速度倍率"""
|
||||
|
||||
if progressbar:
|
||||
# 此处是对于仅有 True 的参数和自定义参数的判断
|
||||
# 改这一段没🐎
|
||||
if progressbar is True:
|
||||
self.progressbar_style = DEFAULT_PROGRESSBAR_STYLE
|
||||
"""进度条样式"""
|
||||
return
|
||||
elif isinstance(progressbar, ProgressBarStyle):
|
||||
self.progressbar_style = progressbar
|
||||
"""进度条样式"""
|
||||
return
|
||||
elif isinstance(progressbar, tuple):
|
||||
if isinstance(progressbar[0], str) and isinstance(
|
||||
progressbar[1], tuple
|
||||
):
|
||||
if isinstance(progressbar[1][0], str) and isinstance(
|
||||
progressbar[1][1], str
|
||||
):
|
||||
self.progressbar_style = ProgressBarStyle(
|
||||
progressbar[0], progressbar[1][0], progressbar[1][1]
|
||||
)
|
||||
return
|
||||
if not ignore_progressbar_param_error:
|
||||
raise TypeError(
|
||||
"参数 {} 的类型 {} 与所需类型 Union[bool, Tuple[str, Tuple[str, str]], ProgressBarStyle] 不符。".format(
|
||||
progressbar, type(progressbar)
|
||||
)
|
||||
)
|
||||
|
||||
self.progressbar_style = None
|
||||
"""进度条样式组"""
|
||||
@@ -1,24 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
用以生成单个mcstructure文件的附加功能
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 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_file_in_delay",
|
||||
"to_mcstructure_file_in_repeater",
|
||||
"to_mcstructure_file_in_score",
|
||||
]
|
||||
__author__ = (("金羿", "Eilles Wan"),)
|
||||
|
||||
from .main import to_mcstructure_file_in_delay, to_mcstructure_file_in_repeater, to_mcstructure_file_in_score
|
||||
@@ -1,216 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
import os
|
||||
from typing import Literal
|
||||
|
||||
# from ...exceptions import CommandFormatError
|
||||
from ...main import MidiConvert
|
||||
from ..main import ConvertConfig
|
||||
from ...subclass import SingleCommand
|
||||
from ..mcstructure import (
|
||||
COMPABILITY_VERSION_117,
|
||||
COMPABILITY_VERSION_119,
|
||||
commands_to_redstone_delay_structure,
|
||||
commands_to_structure,
|
||||
)
|
||||
|
||||
|
||||
def to_mcstructure_file_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[tuple[int,int,int]结构大小, int音乐总延迟]
|
||||
"""
|
||||
|
||||
compability_ver = (
|
||||
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(
|
||||
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)
|
||||
|
||||
struct, size, end_pos = commands_to_structure(
|
||||
cmd_list, max_height - 1, compability_version_=compability_ver
|
||||
)
|
||||
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.mcstructure")
|
||||
),
|
||||
"wb+",
|
||||
) as f:
|
||||
struct.dump(f)
|
||||
|
||||
return size, max_delay
|
||||
|
||||
|
||||
def to_mcstructure_file_in_score(
|
||||
midi_cvt: MidiConvert,
|
||||
data_cfg: ConvertConfig,
|
||||
scoreboard_name: str = "mscplay",
|
||||
auto_reset: bool = False,
|
||||
max_height: int = 64,
|
||||
):
|
||||
"""
|
||||
将midi以延迟播放器形式转换为mcstructure结构文件
|
||||
|
||||
Parameters
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
data_cfg: ConvertConfig 对象
|
||||
部分转换通用参数
|
||||
scoreboard_name: str
|
||||
我的世界的计分板名称
|
||||
auto_reset: bool
|
||||
是否自动重置计分板
|
||||
max_height: int
|
||||
生成结构最大高度
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple[tuple[int,int,int]结构大小, int音乐总延迟, int指令数量
|
||||
"""
|
||||
|
||||
compability_ver = (
|
||||
COMPABILITY_VERSION_117
|
||||
if midi_cvt.enable_old_exe_format
|
||||
else COMPABILITY_VERSION_119
|
||||
)
|
||||
|
||||
cmd_list, cmd_count, max_delay = midi_cvt.to_command_list_in_score(
|
||||
scoreboard_name,
|
||||
data_cfg.volume_ratio,
|
||||
data_cfg.speed_multiplier,
|
||||
)
|
||||
|
||||
if not os.path.exists(data_cfg.dist_path):
|
||||
os.makedirs(data_cfg.dist_path)
|
||||
|
||||
struct, size, end_pos = commands_to_structure(
|
||||
midi_cvt.music_command_list+(
|
||||
[
|
||||
SingleCommand(
|
||||
command="scoreboard players reset @a[scores={"
|
||||
+ scoreboard_name
|
||||
+ "="
|
||||
+ str(max_delay + 20)
|
||||
+ "}] "
|
||||
+ scoreboard_name,
|
||||
annotation="自动重置计分板",
|
||||
)
|
||||
]
|
||||
if auto_reset
|
||||
else []
|
||||
), max_height - 1, compability_version_=compability_ver
|
||||
)
|
||||
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.mcstructure")
|
||||
),
|
||||
"wb+",
|
||||
) as f:
|
||||
struct.dump(f)
|
||||
|
||||
return size, max_delay, cmd_count
|
||||
|
||||
|
||||
def to_mcstructure_file_in_repeater(
|
||||
midi_cvt: MidiConvert,
|
||||
data_cfg: ConvertConfig,
|
||||
player: str = "@a",
|
||||
axis_side: Literal["z+", "z-", "Z+", "Z-", "x+", "x-", "X+", "X-"] = "z+",
|
||||
basement_block: str = "concrete",
|
||||
):
|
||||
"""
|
||||
将midi以延迟播放器形式转换为mcstructure结构文件
|
||||
|
||||
Parameters
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
data_cfg: ConvertConfig 对象
|
||||
部分转换通用参数
|
||||
player: str
|
||||
玩家选择器,默认为`@a`
|
||||
axis_side: Literal["z+","z-","Z+","Z-","x+","x-","X+","X-"]
|
||||
生成结构的延展方向
|
||||
basement_block: str
|
||||
结构的基底方块
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple[tuple[int,int,int]结构大小, int音乐总延迟]
|
||||
"""
|
||||
|
||||
compability_ver = (
|
||||
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(
|
||||
data_cfg.volume_ratio,
|
||||
data_cfg.speed_multiplier,
|
||||
player,
|
||||
)
|
||||
|
||||
if not os.path.exists(data_cfg.dist_path):
|
||||
os.makedirs(data_cfg.dist_path)
|
||||
|
||||
struct, size, end_pos = commands_to_redstone_delay_structure(
|
||||
cmd_list,
|
||||
max_delay,
|
||||
max_multiple_cmd,
|
||||
basement_block,
|
||||
axis_side,
|
||||
compability_version_=compability_ver,
|
||||
)
|
||||
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.mcstructure")
|
||||
),
|
||||
"wb+",
|
||||
) as f:
|
||||
struct.dump(f)
|
||||
|
||||
return size, max_delay
|
||||
@@ -1,487 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
存放有关MCSTRUCTURE结构操作的内容
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 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 List, Literal, Tuple
|
||||
|
||||
from TrimMCStruct import Block, Structure, TAG_Byte, TAG_Long
|
||||
|
||||
from ..constants import x, y, z
|
||||
from ..subclass import SingleCommand
|
||||
from .common import bottem_side_length_of_smallest_square_bottom_box
|
||||
|
||||
|
||||
def antiaxis(axis: Literal["x", "z", "X", "Z"]):
|
||||
return z if axis == x else x
|
||||
|
||||
|
||||
def forward_IER(forward: bool):
|
||||
return 1 if forward else -1
|
||||
|
||||
|
||||
AXIS_PARTICULAR_VALUE = {
|
||||
x: {
|
||||
True: 5,
|
||||
False: 4,
|
||||
},
|
||||
y: {
|
||||
True: 1,
|
||||
False: 0,
|
||||
},
|
||||
z: {
|
||||
True: 3,
|
||||
False: 2,
|
||||
},
|
||||
}
|
||||
|
||||
# 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_]
|
||||
|
||||
|
||||
def form_note_block_in_NBT_struct(
|
||||
note: int,
|
||||
coordinate: Tuple[int, int, int],
|
||||
instrument: str = "note.harp",
|
||||
powered: bool = False,
|
||||
compability_version_number: int = COMPABILITY_VERSION_119,
|
||||
):
|
||||
"""生成音符盒方块
|
||||
: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],
|
||||
} # type: ignore
|
||||
},
|
||||
compability_version=compability_version_number,
|
||||
)
|
||||
|
||||
|
||||
def form_repeater_in_NBT_struct(
|
||||
delay: int,
|
||||
facing: int,
|
||||
compability_version_number: int = COMPABILITY_VERSION_119,
|
||||
):
|
||||
"""生成中继器方块
|
||||
:param facing: 朝向:
|
||||
Z- 北 0
|
||||
X- 东 1
|
||||
Z+ 南 2
|
||||
X+ 西 3
|
||||
:param delay: 0~3
|
||||
:return Block()"""
|
||||
|
||||
return Block(
|
||||
"minecraft",
|
||||
"unpowered_repeater",
|
||||
{
|
||||
"repeater_delay": delay,
|
||||
"direction": facing,
|
||||
},
|
||||
compability_version=compability_version_number,
|
||||
)
|
||||
|
||||
|
||||
def form_command_block_in_NBT_struct(
|
||||
command: str,
|
||||
coordinate: tuple,
|
||||
particularValue: int,
|
||||
impluse: int = 0,
|
||||
condition: bool = False,
|
||||
alwaysRun: bool = True,
|
||||
tickDelay: int = 0,
|
||||
customName: str = "",
|
||||
executeOnFirstTick: bool = False,
|
||||
trackOutput: bool = True,
|
||||
compability_version_number: int = COMPABILITY_VERSION_119,
|
||||
):
|
||||
"""
|
||||
使用指定项目返回指定的指令方块结构
|
||||
:param command: `str`
|
||||
指令
|
||||
:param coordinate: `tuple[int,int,int]`
|
||||
此方块所在之相对坐标
|
||||
:param particularValue:
|
||||
方块特殊值,即朝向
|
||||
:0 下 无条件
|
||||
:1 上 无条件
|
||||
:2 z轴负方向 无条件
|
||||
:3 z轴正方向 无条件
|
||||
:4 x轴负方向 无条件
|
||||
:5 x轴正方向 无条件
|
||||
:6 下 无条件
|
||||
:7 下 无条件
|
||||
|
||||
:8 下 有条件
|
||||
:9 上 有条件
|
||||
:10 z轴负方向 有条件
|
||||
:11 z轴正方向 有条件
|
||||
:12 x轴负方向 有条件
|
||||
:13 x轴正方向 有条件
|
||||
:14 下 有条件
|
||||
:14 下 有条件
|
||||
注意!此处特殊值中的条件会被下面condition参数覆写
|
||||
:param impluse: `int 0|1|2`
|
||||
方块类型
|
||||
0脉冲 1循环 2连锁
|
||||
:param condition: `bool`
|
||||
是否有条件
|
||||
:param alwaysRun: `bool`
|
||||
是否始终执行
|
||||
:param tickDelay: `int`
|
||||
执行延时
|
||||
:param customName: `str`
|
||||
悬浮字
|
||||
:param executeOnFirstTick: `bool`
|
||||
首刻执行(循环指令方块是否激活后立即执行,若为False,则从激活时起延迟后第一次执行)
|
||||
:param trackOutput: `bool`
|
||||
是否输出
|
||||
|
||||
:return:str
|
||||
"""
|
||||
|
||||
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},
|
||||
extra_data={
|
||||
"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],
|
||||
} # type: ignore
|
||||
},
|
||||
compability_version=compability_version_number,
|
||||
)
|
||||
|
||||
|
||||
def commands_to_structure(
|
||||
commands: List[SingleCommand],
|
||||
max_height: int = 64,
|
||||
compability_version_: int = COMPABILITY_VERSION_119,
|
||||
):
|
||||
"""
|
||||
:param commands: 指令列表
|
||||
:param max_height: 生成结构最大高度
|
||||
:return 结构类,结构占用大小,终点坐标
|
||||
"""
|
||||
|
||||
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
|
||||
len(commands), max_height
|
||||
)
|
||||
|
||||
struct = Structure(
|
||||
size=(_sideLength, max_height, _sideLength), # 声明结构大小
|
||||
compability_version=compability_version_,
|
||||
)
|
||||
|
||||
y_forward = True
|
||||
z_forward = True
|
||||
|
||||
now_y = 0
|
||||
now_z = 0
|
||||
now_x = 0
|
||||
|
||||
for command in commands:
|
||||
coordinate = (now_x, now_y, now_z)
|
||||
struct.set_block(
|
||||
coordinate,
|
||||
form_command_block_in_NBT_struct(
|
||||
command=command.command_text,
|
||||
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=command.delay,
|
||||
customName=command.annotation_text,
|
||||
executeOnFirstTick=False,
|
||||
trackOutput=True,
|
||||
compability_version_number=compability_version_,
|
||||
),
|
||||
)
|
||||
|
||||
now_y += 1 if y_forward else -1
|
||||
|
||||
if ((now_y >= max_height) and y_forward) or ((now_y < 0) and (not y_forward)):
|
||||
now_y -= 1 if y_forward else -1
|
||||
|
||||
y_forward = not y_forward
|
||||
|
||||
now_z += 1 if z_forward else -1
|
||||
|
||||
if ((now_z >= _sideLength) and z_forward) or (
|
||||
(now_z < 0) and (not z_forward)
|
||||
):
|
||||
now_z -= 1 if z_forward else -1
|
||||
z_forward = not z_forward
|
||||
now_x += 1
|
||||
|
||||
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),
|
||||
)
|
||||
|
||||
|
||||
def commands_to_redstone_delay_structure(
|
||||
commands: List[SingleCommand],
|
||||
delay_length: int,
|
||||
max_multicmd_length: int,
|
||||
base_block: str = "concrete",
|
||||
axis_: Literal["z+", "z-", "Z+", "Z-", "x+", "x-", "X+", "X-"] = "z+",
|
||||
compability_version_: int = COMPABILITY_VERSION_119,
|
||||
) -> Tuple[Structure, Tuple[int, int, int], Tuple[int, int, int]]:
|
||||
"""
|
||||
:param commands: 指令列表
|
||||
:param delay_length: 延时总长
|
||||
:param max_multicmd_length: 最大同时播放的音符数量
|
||||
:param base_block: 生成结构的基底方块
|
||||
:param axis_: 生成结构的延展方向
|
||||
:return 结构类,结构占用大小,终点坐标
|
||||
"""
|
||||
if axis_ in ["z+", "Z+"]:
|
||||
extensioon_direction = z
|
||||
aside_direction = x
|
||||
repeater_facing = 2
|
||||
forward = True
|
||||
elif axis_ in ["z-", "Z-"]:
|
||||
extensioon_direction = z
|
||||
aside_direction = x
|
||||
repeater_facing = 0
|
||||
forward = False
|
||||
elif axis_ in ["x+", "X+"]:
|
||||
extensioon_direction = x
|
||||
aside_direction = z
|
||||
repeater_facing = 3
|
||||
forward = True
|
||||
elif axis_ in ["x-", "X-"]:
|
||||
extensioon_direction = x
|
||||
aside_direction = z
|
||||
repeater_facing = 1
|
||||
forward = False
|
||||
else:
|
||||
raise ValueError(f"axis_({axis_}) 参数错误。")
|
||||
|
||||
goahead = forward_IER(forward)
|
||||
|
||||
command_actually_length = sum([int(bool(cmd.delay)) for cmd in commands])
|
||||
|
||||
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:
|
||||
# print("\r 正在进行处理:",end="")
|
||||
if cmd.delay > 1:
|
||||
# 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(
|
||||
(pos_now[x], 1, pos_now[z]),
|
||||
form_command_block_in_NBT_struct(
|
||||
command=cmd.command_text,
|
||||
coordinate=(pos_now[x], 1, pos_now[z]),
|
||||
particularValue=command_statevalue(extensioon_direction, forward),
|
||||
# impluse= (0 if first_impluse else 2),
|
||||
impluse=0,
|
||||
condition=False,
|
||||
alwaysRun=False,
|
||||
tickDelay=cmd.delay % 2,
|
||||
customName=cmd.annotation_text,
|
||||
compability_version_number=compability_version_,
|
||||
),
|
||||
)
|
||||
struct.set_block(
|
||||
(pos_now[x], 2, pos_now[z]),
|
||||
Block(
|
||||
"minecraft",
|
||||
"redstone_wire",
|
||||
compability_version=compability_version_,
|
||||
),
|
||||
)
|
||||
pos_now[extensioon_direction] += goahead
|
||||
chain_list = 1
|
||||
|
||||
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(
|
||||
(now_pos_copy[x], 1, now_pos_copy[z]),
|
||||
form_command_block_in_NBT_struct(
|
||||
command=cmd.command_text,
|
||||
coordinate=(now_pos_copy[x], 1, now_pos_copy[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(
|
||||
(now_pos_copy[x], 2, now_pos_copy[z]),
|
||||
Block(
|
||||
"minecraft",
|
||||
"redstone_wire",
|
||||
compability_version=compability_version_,
|
||||
),
|
||||
)
|
||||
chain_list += 1
|
||||
|
||||
return struct, struct.size, tuple(pos_now.values()) # type: ignore
|
||||
@@ -1,117 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
存放有关红石音乐生成操作的内容
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
from ..exceptions import NotDefineProgramError, ZeroSpeedError
|
||||
from ..main import MidiConvert
|
||||
from ..subclass import SingleCommand
|
||||
from ..utils import inst_to_sould_with_deviation, perc_inst_to_soundID_withX
|
||||
|
||||
# 你以为写完了吗?其实并没有
|
||||
|
||||
|
||||
def to_note_list(
|
||||
midi_cvt: MidiConvert,
|
||||
speed: float = 1.0,
|
||||
) -> list:
|
||||
"""
|
||||
使用金羿的转换思路,将midi转换为我的世界音符盒所用的音高列表,并输出每个音符之后的延迟
|
||||
|
||||
Parameters
|
||||
----------
|
||||
speed: float
|
||||
速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple( list[tuple(str指令, int距离上一个指令的延迟 ),...], int音乐时长游戏刻 )
|
||||
"""
|
||||
|
||||
if speed == 0:
|
||||
raise ZeroSpeedError("播放速度仅可为正实数")
|
||||
|
||||
midi_channels = (
|
||||
midi_cvt.to_music_channels() if not midi_cvt.channels else midi_cvt.channels
|
||||
)
|
||||
|
||||
tracks = {}
|
||||
|
||||
# 此处 我们把通道视为音轨
|
||||
for i in midi_channels.keys():
|
||||
# 如果当前通道为空 则跳过
|
||||
if not midi_channels[i]:
|
||||
continue
|
||||
|
||||
# 第十通道是打击乐通道
|
||||
SpecialBits = True if i == 9 else False
|
||||
|
||||
# nowChannel = []
|
||||
|
||||
for track_no, track in midi_channels[i].items():
|
||||
for msg in track:
|
||||
if msg[0] == "PgmC":
|
||||
InstID = msg[1]
|
||||
|
||||
elif msg[0] == "NoteS":
|
||||
try:
|
||||
soundID, _X = (
|
||||
perc_inst_to_soundID_withX(InstID)
|
||||
if SpecialBits
|
||||
else inst_to_sould_with_deviation(InstID)
|
||||
)
|
||||
except UnboundLocalError as E:
|
||||
soundID, _X = (
|
||||
perc_inst_to_soundID_withX(-1)
|
||||
if SpecialBits
|
||||
else inst_to_sould_with_deviation(-1)
|
||||
)
|
||||
score_now = round(msg[-1] / float(speed) / 50)
|
||||
# print(score_now)
|
||||
|
||||
try:
|
||||
tracks[score_now].append(
|
||||
self.execute_cmd_head.format(player)
|
||||
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
||||
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
||||
)
|
||||
except KeyError:
|
||||
tracks[score_now] = [
|
||||
self.execute_cmd_head.format(player)
|
||||
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
||||
f"{2 ** ((msg[1] - 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(
|
||||
(
|
||||
tracks[all_ticks[i]][j],
|
||||
(
|
||||
0
|
||||
if j != 0
|
||||
else (
|
||||
all_ticks[i] - all_ticks[i - 1] if i != 0 else all_ticks[i]
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return [results, max(all_ticks)]
|
||||
@@ -1,18 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
存放有关Schematic结构生成的内容
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
import nbtschematic
|
||||
@@ -1,22 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
用以生成Schematic结构的附加功能
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
__all__ = [
|
||||
]
|
||||
__author__ = (("金羿", "Eilles Wan"),)
|
||||
|
||||
from .main import *
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
from ..schematic import *
|
||||
@@ -1,40 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
存放有关WebSocket服务器操作的内容
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
import fcwslib
|
||||
|
||||
# 这个库有问题,正在检修
|
||||
|
||||
|
||||
class Plugin(fcwslib.Plugin):
|
||||
async def on_connect(self) -> None:
|
||||
print("对象已被连接")
|
||||
await self.send_command("list", callback=self.list)
|
||||
await self.subscribe("PlayerMessage", callback=self.player_message)
|
||||
|
||||
async def on_disconnect(self) -> None:
|
||||
print("对象停止连接")
|
||||
|
||||
async def on_receive(self, response) -> None:
|
||||
print("已接收非常规回复 {}".format(response))
|
||||
|
||||
async def list(self, response) -> None:
|
||||
print("已收取指令执行回复 {}".format(response))
|
||||
|
||||
async def player_message(self, response) -> None:
|
||||
print("已收取玩家事件回复 {}".format(response))
|
||||
@@ -1,21 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
用以启动WebSocket服务器播放的附加功能
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
__all__ = []
|
||||
__author__ = (("金羿", "Eilles Wan"),)
|
||||
|
||||
from .main import *
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
import fcwslib
|
||||
|
||||
from ...main import MidiConvert
|
||||
|
||||
from ..main import ConvertConfig
|
||||
from ...subclass import SingleCommand
|
||||
|
||||
|
||||
def open_websocket_server(
|
||||
midi_cvt: MidiConvert,
|
||||
data_cfg: ConvertConfig,
|
||||
player: str = "@a",
|
||||
server_dist: str = "localhost",
|
||||
server_port: int = 8000,
|
||||
):
|
||||
wssever = fcwslib.Server(server=server_dist,port=server_port,debug_mode=False)
|
||||
@@ -1,483 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
旧版本转换功能以及已经弃用的函数
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
from .exceptions import *
|
||||
from .main import MidiConvert, mido
|
||||
from .subclass import *
|
||||
from .types import ChannelType
|
||||
from .utils import *
|
||||
from .constants import *
|
||||
|
||||
|
||||
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(
|
||||
self,
|
||||
scoreboard_name: str = "mscplay",
|
||||
MaxVolume: float = 1.0,
|
||||
speed: float = 1.0,
|
||||
) -> list:
|
||||
"""
|
||||
使用Dislink Sforza的转换思路,将midi转换为我的世界命令列表
|
||||
:param scoreboard_name: 我的世界的计分板名称
|
||||
:param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||
:return: tuple(命令列表, 命令个数, 计分板最大值)
|
||||
"""
|
||||
# :param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||
tracks = []
|
||||
if speed == 0:
|
||||
raise ZeroSpeedError("播放速度仅可为正实数")
|
||||
if not self.midi:
|
||||
raise MidiUnboundError(
|
||||
"你是否正在使用的是一个由 copy_important 生成的MidiConvert对象?这是不可复用的。"
|
||||
)
|
||||
|
||||
MaxVolume = 1 if MaxVolume > 1 else (0.001 if MaxVolume <= 0 else MaxVolume)
|
||||
commands = 0
|
||||
maxscore = 0
|
||||
tempo = mido.midifiles.midifiles.DEFAULT_TEMPO
|
||||
|
||||
# 分轨的思路其实并不好,但这个算法就是这样
|
||||
# 所以我建议用第二个方法 _toCmdList_m2
|
||||
for i, track in enumerate(self.midi.tracks):
|
||||
ticks = 0
|
||||
instrumentID = 0
|
||||
singleTrack = []
|
||||
|
||||
for msg in track:
|
||||
ticks += msg.time
|
||||
if msg.is_meta:
|
||||
if msg.type == "set_tempo":
|
||||
tempo = msg.tempo
|
||||
else:
|
||||
if msg.type == "program_change":
|
||||
instrumentID = msg.program
|
||||
|
||||
if msg.type == "note_on" and msg.velocity != 0:
|
||||
nowscore = round(
|
||||
(ticks * tempo)
|
||||
/ ((self.midi.ticks_per_beat * float(speed)) * 50000)
|
||||
)
|
||||
maxscore = max(maxscore, nowscore)
|
||||
if msg.channel == 9:
|
||||
soundID, _X = inst_to_sould_with_deviation(
|
||||
instrumentID, MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE
|
||||
)
|
||||
else:
|
||||
soundID, _X = inst_to_sould_with_deviation(
|
||||
instrumentID, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE
|
||||
)
|
||||
|
||||
singleTrack.append(
|
||||
"execute @a[scores={"
|
||||
+ str(scoreboard_name)
|
||||
+ "="
|
||||
+ str(nowscore)
|
||||
+ "}"
|
||||
+ f"] ~ ~ ~ playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg.velocity / 128} "
|
||||
f"{2 ** ((msg.note - 60 - _X) / 12)}"
|
||||
)
|
||||
commands += 1
|
||||
|
||||
if len(singleTrack) != 0:
|
||||
tracks.append(singleTrack)
|
||||
|
||||
return [tracks, commands, maxscore]
|
||||
|
||||
def _toCmdList_m1(
|
||||
self, scoreboardname: str = "mscplay", volume: float = 1.0, speed: float = 1.0
|
||||
) -> list:
|
||||
"""
|
||||
使用Dislink Sforza的转换思路,将midi转换为我的世界命令列表
|
||||
:param scoreboardname: 我的世界的计分板名称
|
||||
:param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||
:param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||
:return: tuple(命令列表, 命令个数, 计分板最大值)
|
||||
"""
|
||||
tracks = []
|
||||
if volume > 1:
|
||||
volume = 1
|
||||
if volume <= 0:
|
||||
volume = 0.001
|
||||
|
||||
commands = 0
|
||||
maxscore = 0
|
||||
|
||||
for i, track in enumerate(self.midi.tracks): # type:ignore
|
||||
ticks = 0
|
||||
instrumentID = 0
|
||||
singleTrack = []
|
||||
|
||||
for msg in track:
|
||||
ticks += msg.time
|
||||
# print(msg)
|
||||
if msg.is_meta:
|
||||
if msg.type == "set_tempo":
|
||||
tempo = msg.tempo
|
||||
else:
|
||||
if msg.type == "program_change":
|
||||
# print("TT")
|
||||
instrumentID = msg.program
|
||||
if msg.type == "note_on" and msg.velocity != 0:
|
||||
nowscore = round(
|
||||
(ticks * tempo) / ((self.midi.ticks_per_beat * float(speed)) * 50000) # type: ignore
|
||||
)
|
||||
maxscore = max(maxscore, nowscore)
|
||||
if msg.channel == 9:
|
||||
soundID, _X = inst_to_sould_with_deviation(
|
||||
instrumentID, MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE
|
||||
)
|
||||
else:
|
||||
soundID, _X = inst_to_sould_with_deviation(
|
||||
instrumentID, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE
|
||||
)
|
||||
singleTrack.append(
|
||||
"execute @a[scores={"
|
||||
+ str(scoreboardname)
|
||||
+ "="
|
||||
+ str(nowscore)
|
||||
+ "}"
|
||||
+ f"] ~ ~ ~ playsound {soundID} @s ~ ~{1 / volume - 1} ~ {msg.velocity * (0.7 if msg.channel == 0 else 0.9)} {2 ** ((msg.note - 60 - _X) / 12)}"
|
||||
)
|
||||
commands += 1
|
||||
if len(singleTrack) != 0:
|
||||
tracks.append(singleTrack)
|
||||
|
||||
return [tracks, commands, maxscore]
|
||||
|
||||
# 原本这个算法的转换效果应该和上面的算法相似的
|
||||
def _toCmdList_m2(
|
||||
self,
|
||||
scoreboard_name: str = "mscplay",
|
||||
MaxVolume: float = 1.0,
|
||||
speed: float = 1.0,
|
||||
) -> tuple:
|
||||
"""
|
||||
使用神羽和金羿的转换思路,将midi转换为我的世界命令列表
|
||||
:param scoreboard_name: 我的世界的计分板名称
|
||||
:param MaxVolume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||
:param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||
:return: tuple(命令列表, 命令个数, 计分板最大值)
|
||||
"""
|
||||
|
||||
if speed == 0:
|
||||
raise ZeroSpeedError("播放速度仅可为正实数")
|
||||
MaxVolume = 1 if MaxVolume > 1 else (0.001 if MaxVolume <= 0 else MaxVolume)
|
||||
|
||||
tracks = []
|
||||
cmdAmount = 0
|
||||
maxScore = 0
|
||||
InstID = -1
|
||||
|
||||
self.to_music_channels()
|
||||
|
||||
# 此处 我们把通道视为音轨
|
||||
for i in self.channels.keys():
|
||||
# 如果当前通道为空 则跳过
|
||||
if not self.channels[i]:
|
||||
continue
|
||||
|
||||
if i == 9:
|
||||
SpecialBits = True
|
||||
else:
|
||||
SpecialBits = False
|
||||
|
||||
nowTrack = []
|
||||
|
||||
for track_no, track in self.channels[i].items(): # type: ignore
|
||||
for msg in track:
|
||||
if msg[0] == "PgmC":
|
||||
InstID = msg[1]
|
||||
|
||||
elif msg[0] == "NoteS":
|
||||
soundID, _X = (
|
||||
inst_to_sould_with_deviation(
|
||||
msg[1], MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE
|
||||
)
|
||||
if SpecialBits
|
||||
else inst_to_sould_with_deviation(
|
||||
InstID, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE
|
||||
)
|
||||
)
|
||||
score_now = round(msg[-1] / float(speed) / 50)
|
||||
maxScore = max(maxScore, score_now)
|
||||
|
||||
nowTrack.append(
|
||||
self.execute_cmd_head.format(
|
||||
"@a[scores=({}={})]".format(scoreboard_name, score_now)
|
||||
.replace("(", r"{")
|
||||
.replace(")", r"}")
|
||||
)
|
||||
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
||||
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
||||
)
|
||||
|
||||
cmdAmount += 1
|
||||
|
||||
if nowTrack:
|
||||
tracks.append(nowTrack)
|
||||
|
||||
return tracks, cmdAmount, maxScore
|
||||
|
||||
def _toCmdList_withDelay_m1(
|
||||
self,
|
||||
MaxVolume: float = 1.0,
|
||||
speed: float = 1.0,
|
||||
player: str = "@a",
|
||||
) -> list:
|
||||
"""
|
||||
使用Dislink Sforza的转换思路,将midi转换为我的世界命令列表,并输出每个音符之后的延迟
|
||||
:param MaxVolume: 最大播放音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||
:param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||
:param player: 玩家选择器,默认为`@a`
|
||||
:return: 全部指令列表[ ( str指令, int距离上一个指令的延迟 ),...]
|
||||
"""
|
||||
tracks = {}
|
||||
|
||||
if speed == 0:
|
||||
raise ZeroSpeedError("播放速度仅可为正实数")
|
||||
if not self.midi:
|
||||
raise MidiUnboundError(
|
||||
"你是否正在使用的是一个由 copy_important 生成的MidiConvert对象?这是不可复用的。"
|
||||
)
|
||||
|
||||
MaxVolume = 1 if MaxVolume > 1 else (0.001 if MaxVolume <= 0 else MaxVolume)
|
||||
tempo = mido.midifiles.midifiles.DEFAULT_TEMPO
|
||||
|
||||
for i, track in enumerate(self.midi.tracks):
|
||||
instrumentID = 0
|
||||
ticks = 0
|
||||
|
||||
for msg in track:
|
||||
ticks += msg.time
|
||||
if msg.is_meta:
|
||||
if msg.type == "set_tempo":
|
||||
tempo = msg.tempo
|
||||
else:
|
||||
if msg.type == "program_change":
|
||||
instrumentID = msg.program
|
||||
if msg.type == "note_on" and msg.velocity != 0:
|
||||
now_tick = round(
|
||||
(ticks * tempo)
|
||||
/ ((self.midi.ticks_per_beat * float(speed)) * 50000)
|
||||
)
|
||||
|
||||
if msg.channel == 9:
|
||||
soundID, _X = inst_to_sould_with_deviation(
|
||||
instrumentID, MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE
|
||||
)
|
||||
else:
|
||||
soundID, _X = inst_to_sould_with_deviation(
|
||||
instrumentID, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE
|
||||
)
|
||||
try:
|
||||
tracks[now_tick].append(
|
||||
self.execute_cmd_head.format(player)
|
||||
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg.velocity / 128} "
|
||||
f"{2 ** ((msg.note - 60 - _X) / 12)}"
|
||||
)
|
||||
except KeyError:
|
||||
tracks[now_tick] = [
|
||||
self.execute_cmd_head.format(player)
|
||||
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg.velocity / 128} "
|
||||
f"{2 ** ((msg.note - 60 - _X) / 12)}"
|
||||
]
|
||||
|
||||
results = []
|
||||
|
||||
all_ticks = list(tracks.keys())
|
||||
all_ticks.sort()
|
||||
|
||||
for i in range(len(all_ticks)):
|
||||
if i != 0:
|
||||
for j in range(len(tracks[all_ticks[i]])):
|
||||
if j != 0:
|
||||
results.append((tracks[all_ticks[i]][j], 0))
|
||||
else:
|
||||
results.append(
|
||||
(tracks[all_ticks[i]][j], all_ticks[i] - all_ticks[i - 1])
|
||||
)
|
||||
else:
|
||||
for j in range(len(tracks[all_ticks[i]])):
|
||||
results.append((tracks[all_ticks[i]][j], all_ticks[i]))
|
||||
|
||||
return [results, max(all_ticks)]
|
||||
|
||||
def _toCmdList_withDelay_m2(
|
||||
self,
|
||||
MaxVolume: float = 1.0,
|
||||
speed: float = 1.0,
|
||||
player: str = "@a",
|
||||
) -> list:
|
||||
"""
|
||||
使用神羽和金羿的转换思路,将midi转换为我的世界命令列表,并输出每个音符之后的延迟
|
||||
:param MaxVolume: 最大播放音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||
:param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||
:param player: 玩家选择器,默认为`@a`
|
||||
:return: 全部指令列表[ ( str指令, int距离上一个指令的延迟 ),...]
|
||||
"""
|
||||
tracks = {}
|
||||
if speed == 0:
|
||||
raise ZeroSpeedError("播放速度仅可为正实数")
|
||||
|
||||
MaxVolume = 1 if MaxVolume > 1 else (0.001 if MaxVolume <= 0 else MaxVolume)
|
||||
InstID = -1
|
||||
self.to_music_channels()
|
||||
|
||||
results = []
|
||||
|
||||
for i in self.channels.keys():
|
||||
# 如果当前通道为空 则跳过
|
||||
if not self.channels[i]:
|
||||
continue
|
||||
|
||||
if i == 9:
|
||||
SpecialBits = True
|
||||
else:
|
||||
SpecialBits = False
|
||||
|
||||
for track_no, track in self.channels[i].items(): # type: ignore
|
||||
for msg in track:
|
||||
if msg[0] == "PgmC":
|
||||
InstID = msg[1]
|
||||
|
||||
elif msg[0] == "NoteS":
|
||||
soundID, _X = (
|
||||
inst_to_sould_with_deviation(
|
||||
msg[1], MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE
|
||||
)
|
||||
if SpecialBits
|
||||
else inst_to_sould_with_deviation(
|
||||
InstID, MM_CLASSIC_PITCHED_INSTRUMENT_TABLE
|
||||
)
|
||||
)
|
||||
score_now = round(msg[-1] / float(speed) / 50)
|
||||
|
||||
try:
|
||||
tracks[score_now].append(
|
||||
self.execute_cmd_head.format(player)
|
||||
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
||||
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
||||
)
|
||||
except KeyError:
|
||||
tracks[score_now] = [
|
||||
self.execute_cmd_head.format(player)
|
||||
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
||||
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
||||
]
|
||||
|
||||
all_ticks = list(tracks.keys())
|
||||
all_ticks.sort()
|
||||
|
||||
for i in range(len(all_ticks)):
|
||||
for j in range(len(tracks[all_ticks[i]])):
|
||||
results.append(
|
||||
(
|
||||
tracks[all_ticks[i]][j],
|
||||
(
|
||||
0
|
||||
if j != 0
|
||||
else (
|
||||
all_ticks[i] - all_ticks[i - 1]
|
||||
if i != 0
|
||||
else all_ticks[i]
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return [results, max(all_ticks)]
|
||||
@@ -1,401 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
存储许多非主要的相关类
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
from .types import Optional, Any, List, Mapping
|
||||
|
||||
from .constants import MC_PERCUSSION_INSTRUMENT_LIST
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class SingleNote:
|
||||
"""存储单个音符的类"""
|
||||
|
||||
instrument: int
|
||||
"""乐器编号"""
|
||||
|
||||
note: int
|
||||
"""音符编号"""
|
||||
|
||||
velocity: int
|
||||
"""力度/响度"""
|
||||
|
||||
start_time: int
|
||||
"""开始之时 ms"""
|
||||
|
||||
duration: int
|
||||
"""音符持续时间 ms"""
|
||||
|
||||
track_no: int
|
||||
"""音符所处的音轨"""
|
||||
|
||||
percussive: bool
|
||||
"""是否为打击乐器"""
|
||||
|
||||
extra_info: Any
|
||||
"""你觉得放什么好?"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
instrument: int,
|
||||
pitch: int,
|
||||
velocity: int,
|
||||
startime: int,
|
||||
lastime: int,
|
||||
track_number: int = 0,
|
||||
is_percussion: Optional[bool] = None,
|
||||
extra_information: Any = None,
|
||||
):
|
||||
"""用于存储单个音符的类
|
||||
:param instrument 乐器编号
|
||||
:param pitch 音符编号
|
||||
:param velocity 力度/响度
|
||||
:param startTime 开始之时(ms)
|
||||
注:此处的时间是用从乐曲开始到当前的毫秒数
|
||||
:param lastTime 音符延续时间(ms)"""
|
||||
self.instrument: int = instrument
|
||||
"""乐器编号"""
|
||||
self.note: int = pitch
|
||||
"""音符编号"""
|
||||
self.velocity: int = velocity
|
||||
"""力度/响度"""
|
||||
self.start_time: int = startime
|
||||
"""开始之时 ms"""
|
||||
self.duration: int = lastime
|
||||
"""音符持续时间 ms"""
|
||||
self.track_no: int = track_number
|
||||
"""音符所处的音轨"""
|
||||
|
||||
self.percussive = (
|
||||
(is_percussion in MC_PERCUSSION_INSTRUMENT_LIST)
|
||||
if (is_percussion is None)
|
||||
else is_percussion
|
||||
)
|
||||
"""是否为打击乐器"""
|
||||
|
||||
self.extra_info = extra_information
|
||||
|
||||
@property
|
||||
def inst(self) -> int:
|
||||
"""乐器编号"""
|
||||
return self.instrument
|
||||
|
||||
@inst.setter
|
||||
def inst(self, inst_: int):
|
||||
self.instrument = inst_
|
||||
|
||||
@property
|
||||
def pitch(self) -> int:
|
||||
"""音符编号"""
|
||||
return self.note
|
||||
|
||||
# @property
|
||||
# def get_mc_pitch(self,table: Dict[int, Tuple[str, int]]) -> float:
|
||||
# self.mc_sound_ID, _X = inst_to_sould_with_deviation(self.inst,table,"note.bd" if self.percussive else "note.flute",)
|
||||
# return -1 if self.percussive else 2 ** ((self.note - 60 - _X) / 12)
|
||||
|
||||
def set_info(self, sth: Any):
|
||||
"""设置附加信息"""
|
||||
self.extra_info = sth
|
||||
|
||||
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):
|
||||
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):
|
||||
return (
|
||||
{
|
||||
"Percussive": self.percussive,
|
||||
"Instrument": self.inst,
|
||||
"Velocity": self.velocity,
|
||||
"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:
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
return self.__str__() == other.__str__()
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class SingleCommand:
|
||||
"""存储单个指令的类"""
|
||||
|
||||
command_text: str
|
||||
"""指令文本"""
|
||||
|
||||
conditional: bool
|
||||
"""执行是否有条件"""
|
||||
|
||||
delay: int
|
||||
"""执行的延迟"""
|
||||
|
||||
annotation_text: str
|
||||
"""指令注释"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
command: str,
|
||||
condition: bool = False,
|
||||
tick_delay: int = 0,
|
||||
annotation: str = "",
|
||||
):
|
||||
"""
|
||||
存储单个指令的类
|
||||
|
||||
Parameters
|
||||
----------
|
||||
command: str
|
||||
指令
|
||||
condition: bool
|
||||
是否有条件
|
||||
tick_delay: int
|
||||
执行延时
|
||||
annotation: str
|
||||
注释
|
||||
"""
|
||||
self.command_text = command
|
||||
self.conditional = condition
|
||||
self.delay = tick_delay
|
||||
self.annotation_text = annotation
|
||||
|
||||
def copy(self):
|
||||
return SingleCommand(
|
||||
command=self.command_text,
|
||||
condition=self.conditional,
|
||||
tick_delay=self.delay,
|
||||
annotation=self.annotation_text,
|
||||
)
|
||||
|
||||
@property
|
||||
def cmd(self) -> str:
|
||||
"""
|
||||
我的世界函数字符串(包含注释)
|
||||
"""
|
||||
return self.__str__()
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""
|
||||
转为我的世界函数文件格式(包含注释)
|
||||
"""
|
||||
return "#[{cdt}]<{delay}> {ant}\n{cmd}".format(
|
||||
cdt="CDT" if self.conditional else "",
|
||||
delay=self.delay,
|
||||
ant=self.annotation_text,
|
||||
cmd=self.command_text,
|
||||
)
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
return self.__str__() == other.__str__()
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class SingleNoteBox:
|
||||
"""存储单个音符盒"""
|
||||
|
||||
instrument_block: str
|
||||
"""乐器方块"""
|
||||
|
||||
note_value: int
|
||||
"""音符盒音高"""
|
||||
|
||||
annotation_text: str
|
||||
"""音符注释"""
|
||||
|
||||
is_percussion: bool
|
||||
"""是否为打击乐器"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
instrument_block_: str,
|
||||
note_value_: int,
|
||||
percussion: Optional[bool] = None,
|
||||
annotation: str = "",
|
||||
):
|
||||
"""用于存储单个音符盒的类
|
||||
:param instrument_block_ 音符盒演奏所使用的乐器方块
|
||||
:param note_value_ 音符盒的演奏音高
|
||||
:param percussion 此音符盒乐器是否作为打击乐处理
|
||||
注:若为空,则自动识别是否为打击乐器
|
||||
:param annotation 音符注释"""
|
||||
self.instrument_block = instrument_block_
|
||||
"""乐器方块"""
|
||||
self.note_value = note_value_
|
||||
"""音符盒音高"""
|
||||
self.annotation_text = annotation
|
||||
"""音符注释"""
|
||||
if percussion is None:
|
||||
self.is_percussion = percussion in MC_PERCUSSION_INSTRUMENT_LIST
|
||||
else:
|
||||
self.is_percussion = percussion
|
||||
|
||||
@property
|
||||
def inst(self) -> str:
|
||||
"""获取音符盒下的乐器方块"""
|
||||
return self.instrument_block
|
||||
|
||||
@inst.setter
|
||||
def inst(self, inst_):
|
||||
self.instrument_block = inst_
|
||||
|
||||
@property
|
||||
def note(self) -> int:
|
||||
"""获取音符盒音调特殊值"""
|
||||
return self.note_value
|
||||
|
||||
@note.setter
|
||||
def note(self, note_):
|
||||
self.note_value = note_
|
||||
|
||||
@property
|
||||
def annotation(self) -> str:
|
||||
"""获取音符盒的备注"""
|
||||
return self.annotation_text
|
||||
|
||||
@annotation.setter
|
||||
def annotation(self, annotation_):
|
||||
self.annotation_text = annotation_
|
||||
|
||||
def copy(self):
|
||||
return SingleNoteBox(
|
||||
instrument_block_=self.instrument_block,
|
||||
note_value_=self.note_value,
|
||||
annotation=self.annotation_text,
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Note(inst = {self.inst}, note = {self.note}, )"
|
||||
|
||||
def __tuple__(self) -> tuple:
|
||||
return self.inst, self.note, self.annotation
|
||||
|
||||
def __dict__(self) -> dict:
|
||||
return {
|
||||
"inst": self.inst,
|
||||
"note": self.note,
|
||||
"annotation": self.annotation,
|
||||
}
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
return self.__str__() == other.__str__()
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class ProgressBarStyle:
|
||||
"""进度条样式类"""
|
||||
|
||||
base_style: str
|
||||
"""基础样式"""
|
||||
|
||||
to_play_style: str
|
||||
"""未播放之样式"""
|
||||
|
||||
played_style: str
|
||||
"""已播放之样式"""
|
||||
|
||||
def __init__(self, base_s: str, to_play_s: str, played_s: str):
|
||||
"""用于存储进度条样式的类
|
||||
:param base_s 基础样式,用以定义进度条整体
|
||||
:param to_play_s 进度条样式:尚未播放的样子
|
||||
:param played_s 已经播放的样子"""
|
||||
self.base_style = base_s
|
||||
self.to_play_style = to_play_s
|
||||
self.played_style = played_s
|
||||
|
||||
def set_base_style(self, value: str):
|
||||
"""设置基础样式"""
|
||||
self.base_style = value
|
||||
|
||||
def set_to_play_style(self, value: str):
|
||||
"""设置未播放之样式"""
|
||||
self.to_play_style = value
|
||||
|
||||
def set_played_style(self, value: str):
|
||||
"""设置已播放之样式"""
|
||||
self.played_style = value
|
||||
|
||||
def copy(self):
|
||||
dst = ProgressBarStyle(self.base_style, self.to_play_style, self.played_style)
|
||||
return dst
|
||||
|
||||
|
||||
DEFAULT_PROGRESSBAR_STYLE = ProgressBarStyle(
|
||||
r"▶ %%N [ %%s/%^s %%% __________ %%t|%^t ]",
|
||||
r"§e=§r",
|
||||
r"§7=§r",
|
||||
)
|
||||
"""
|
||||
默认的进度条样式
|
||||
"""
|
||||
|
||||
NoteChannelType = Mapping[
|
||||
int,
|
||||
List[SingleNote,],
|
||||
]
|
||||
"""
|
||||
频道信息类型
|
||||
|
||||
Dict[int,Dict[int,List[SingleNote,],],]
|
||||
"""
|
||||
@@ -1,64 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
存放数据类型的定义
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 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,
|
||||
Iterable,
|
||||
Sequence,
|
||||
Mapping,
|
||||
Callable,
|
||||
)
|
||||
|
||||
|
||||
MidiNoteNameTableType = Mapping[int, Tuple[str, ...]]
|
||||
"""
|
||||
Midi音符名称对照表类型
|
||||
"""
|
||||
|
||||
MidiInstrumentTableType = Mapping[int, Tuple[str, int]]
|
||||
"""
|
||||
Midi乐器对照表类型
|
||||
"""
|
||||
|
||||
FittingFunctionType = Callable[[float], float]
|
||||
|
||||
|
||||
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],]],],]
|
||||
"""
|
||||
@@ -1,225 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
存放主程序所必须的功能性内容
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
import math
|
||||
import random
|
||||
|
||||
from .constants import MM_INSTRUMENT_DEVIATION_TABLE, MC_INSTRUMENT_BLOCKS_TABLE
|
||||
from .subclass import SingleNote
|
||||
|
||||
from .types import (
|
||||
Any,
|
||||
Dict,
|
||||
Tuple,
|
||||
Optional,
|
||||
Callable,
|
||||
Literal,
|
||||
Union,
|
||||
MidiInstrumentTableType,
|
||||
)
|
||||
|
||||
|
||||
def mctick2timestr(mc_tick: int) -> str:
|
||||
"""
|
||||
将《我的世界》的游戏刻计转为表示时间的字符串
|
||||
"""
|
||||
return str(int(int(mc_tick / 20) / 60)) + ":" + str(int(int(mc_tick / 20) % 60))
|
||||
|
||||
|
||||
def empty_midi_channels(channel_count: int = 17, staff: Any = {}) -> Dict[int, Any]:
|
||||
"""
|
||||
空MIDI通道字典
|
||||
"""
|
||||
|
||||
return dict(
|
||||
(
|
||||
i,
|
||||
(staff.copy() if isinstance(staff, (dict, list)) else staff),
|
||||
) # 这告诉我们,你不能忽略任何一个复制的序列,因为它真的,我哭死,折磨我一整天,全在这个bug上了
|
||||
for i in range(channel_count)
|
||||
)
|
||||
|
||||
|
||||
def inst_to_sould_with_deviation(
|
||||
instrumentID: int,
|
||||
reference_table: MidiInstrumentTableType,
|
||||
default_instrument: str = "note.flute",
|
||||
default_deviation: Optional[int] = 5,
|
||||
) -> 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
|
||||
reference_table: Dict[int, Tuple[str, int]]
|
||||
转换乐器参照表
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple(str我的世界乐器名, int转换算法中的X)
|
||||
"""
|
||||
return reference_table.get(
|
||||
instrumentID,
|
||||
(
|
||||
default_instrument,
|
||||
(
|
||||
default_deviation
|
||||
if default_deviation
|
||||
else MM_INSTRUMENT_DEVIATION_TABLE.get(default_instrument, -1)
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
# 明明已经走了
|
||||
# 凭什么还要在我心里留下缠绵缱绻
|
||||
|
||||
|
||||
def natural_curve(
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
def straight_line(vol: float) -> float:
|
||||
"""
|
||||
midi力度值拟合成的距离函数
|
||||
|
||||
Parameters
|
||||
----------
|
||||
vol: int
|
||||
midi音符力度值
|
||||
|
||||
Returns
|
||||
-------
|
||||
float播放中心到玩家的距离
|
||||
"""
|
||||
return vol / -8 + 16
|
||||
|
||||
|
||||
def note_to_command_parameters(
|
||||
note_: SingleNote,
|
||||
reference_table: MidiInstrumentTableType,
|
||||
volume_percentage: float = 1,
|
||||
volume_processing_method: Callable[[float], float] = natural_curve,
|
||||
) -> Tuple[
|
||||
str,
|
||||
float,
|
||||
float,
|
||||
Union[float, Literal[None]],
|
||||
]:
|
||||
"""
|
||||
将音符转为播放的指令
|
||||
:param note_:int 音符对象
|
||||
:param reference_table:Dict[int, Tuple[str, int]] 转换对照表
|
||||
:param volume_percentage:int 音量占比(0,1]
|
||||
:param volume_proccessing_method:Callable[[float], float]: 音量处理函数
|
||||
|
||||
:return str[我的世界音符ID], float[播放距离], float[指令音量参数], float[指令音调参数]
|
||||
"""
|
||||
mc_sound_ID, deviation = inst_to_sould_with_deviation(
|
||||
note_.inst,
|
||||
reference_table,
|
||||
"note.bd" if note_.percussive else "note.flute",
|
||||
)
|
||||
|
||||
# delaytime_now = round(self.start_time / float(speed) / 50)
|
||||
mc_pitch = None if note_.percussive else 2 ** ((note_.note - 60 - deviation) / 12)
|
||||
|
||||
mc_distance_volume = volume_processing_method(note_.velocity * volume_percentage)
|
||||
|
||||
return mc_sound_ID, mc_distance_volume, volume_percentage, mc_pitch
|
||||
|
||||
|
||||
def from_single_note(
|
||||
note_: SingleNote, random_select: bool = False, default_block: str = "air"
|
||||
):
|
||||
"""
|
||||
将我的世界乐器名改作音符盒所需的对应方块名称
|
||||
|
||||
Parameters
|
||||
----------
|
||||
note_: SingleNote
|
||||
音符类
|
||||
random_select: bool
|
||||
是否随机选取对应方块
|
||||
default_block: str
|
||||
查表查不到怎么办?默认一个!
|
||||
|
||||
Returns
|
||||
-------
|
||||
str方块名称
|
||||
"""
|
||||
pass
|
||||
# return SingleNoteBox() # TO-DO
|
||||
|
||||
|
||||
@staticmethod
|
||||
def soundID_to_blockID(
|
||||
sound_id: str, random_select: bool = False, default_block: str = "air"
|
||||
) -> str:
|
||||
"""
|
||||
将我的世界乐器名改作音符盒所需的对应方块名称
|
||||
|
||||
Parameters
|
||||
----------
|
||||
sound_id: str
|
||||
将我的世界乐器名
|
||||
random_select: bool
|
||||
是否随机选取对应方块
|
||||
default_block: str
|
||||
查表查不到怎么办?默认一个!
|
||||
|
||||
Returns
|
||||
-------
|
||||
str方块名称
|
||||
"""
|
||||
if random_select:
|
||||
return random.choice(MC_INSTRUMENT_BLOCKS_TABLE.get(sound_id, (default_block,)))
|
||||
else:
|
||||
return MC_INSTRUMENT_BLOCKS_TABLE.get(sound_id, (default_block,))[0]
|
||||
@@ -1,70 +0,0 @@
|
||||
import Musicreater
|
||||
import Musicreater.experiment
|
||||
import Musicreater.previous
|
||||
|
||||
import Musicreater.plugin
|
||||
|
||||
from Musicreater.plugin.addonpack import (
|
||||
to_addon_pack_in_delay,
|
||||
to_addon_pack_in_repeater,
|
||||
to_addon_pack_in_score,
|
||||
)
|
||||
from Musicreater.plugin.mcstructfile import (
|
||||
to_mcstructure_file_in_delay,
|
||||
to_mcstructure_file_in_repeater,
|
||||
to_mcstructure_file_in_score,
|
||||
)
|
||||
|
||||
from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score
|
||||
|
||||
MSCT_MAIN = (
|
||||
Musicreater,
|
||||
Musicreater.experiment,
|
||||
Musicreater.previous,
|
||||
)
|
||||
|
||||
MSCT_PLUGIN = (Musicreater.plugin,)
|
||||
|
||||
MSCT_PLUGIN_FUNCTION = (
|
||||
to_addon_pack_in_delay,
|
||||
to_addon_pack_in_repeater,
|
||||
to_addon_pack_in_score,
|
||||
to_mcstructure_file_in_delay,
|
||||
to_mcstructure_file_in_repeater,
|
||||
to_mcstructure_file_in_score,
|
||||
to_BDX_file_in_delay,
|
||||
to_BDX_file_in_score,
|
||||
)
|
||||
|
||||
import hashlib
|
||||
|
||||
import dill
|
||||
import brotli
|
||||
|
||||
|
||||
def enpack_llc_pack(sth, to_dist: str):
|
||||
packing_bytes = brotli.compress(
|
||||
dill.dumps(
|
||||
sth,
|
||||
)
|
||||
)
|
||||
with open(
|
||||
to_dist,
|
||||
"wb",
|
||||
) as f:
|
||||
f.write(packing_bytes)
|
||||
|
||||
return hashlib.sha256(packing_bytes)
|
||||
|
||||
|
||||
with open("./Packer/checksum.txt", "w", encoding="utf-8") as f:
|
||||
f.write("MSCT_MAIN:\n")
|
||||
f.write(enpack_llc_pack(MSCT_MAIN, "./Packer/MSCT_MAIN.MPK").hexdigest())
|
||||
f.write("\nMSCT_PLUGIN:\n")
|
||||
f.write(enpack_llc_pack(MSCT_PLUGIN, "./Packer/MSCT_PLUGIN.MPK").hexdigest())
|
||||
f.write("\nMSCT_PLUGIN_FUNCTION:\n")
|
||||
f.write(
|
||||
enpack_llc_pack(
|
||||
MSCT_PLUGIN_FUNCTION, "./Packer/MSCT_PLUGIN_FUNCTION.MPK"
|
||||
).hexdigest()
|
||||
)
|
||||
532
PySide6-Pkg.ui
Normal file
532
PySide6-Pkg.ui
Normal file
@@ -0,0 +1,532 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QWidget" name="verticalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>951</width>
|
||||
<height>611</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabO">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Tab 1</string>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="verticalLayoutWidget_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>881</width>
|
||||
<height>591</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tableWidget"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Tab 2</string>
|
||||
</attribute>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>71</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>30</y>
|
||||
<width>281</width>
|
||||
<height>131</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QWidget" name="verticalLayoutWidget_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>0</y>
|
||||
<width>271</width>
|
||||
<height>131</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="widget_2" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>280</x>
|
||||
<y>30</y>
|
||||
<width>281</width>
|
||||
<height>131</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QWidget" name="verticalLayoutWidget_4">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>0</y>
|
||||
<width>271</width>
|
||||
<height>131</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="widget_3" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>160</y>
|
||||
<width>781</width>
|
||||
<height>191</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QWidget" name="verticalLayoutWidget_5">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>781</width>
|
||||
<height>191</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_14">
|
||||
<property name="text">
|
||||
<string>默认保存路径:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="toolButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="text">
|
||||
<string>速度倍率</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="horizontalSlider">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="text">
|
||||
<string>音量倍率</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="horizontalSlider_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_21">
|
||||
<property name="text">
|
||||
<string>最大指令生成高度</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_16">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_13">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_24">
|
||||
<property name="text">
|
||||
<string>选择器模式</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox_3"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>计分板名称</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEdit">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_20">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>玩家选择器</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEdit_2">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_19">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>bdx作者名称</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEdit">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_11">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_2">
|
||||
<property name="text">
|
||||
<string>是否启用进度条</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox">
|
||||
<property name="text">
|
||||
<string>是否自定义进度条</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_22">
|
||||
<property name="text">
|
||||
<string>自定义进度条内容:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEdit_3">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>22</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_18"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_23">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>选择命令格式版本</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox_2">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_14">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_25">
|
||||
<property name="text">
|
||||
<string>选择转换算法版本</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox_4"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_15">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_26">
|
||||
<property name="text">
|
||||
<string>选择导出模式</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox_5"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_3">
|
||||
<attribute name="title">
|
||||
<string>页</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_4">
|
||||
<attribute name="title">
|
||||
<string>页</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>26</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu">
|
||||
<property name="title">
|
||||
<string>文件</string>
|
||||
</property>
|
||||
<addaction name="action"/>
|
||||
<addaction name="action_Input"/>
|
||||
</widget>
|
||||
<addaction name="menu"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<action name="action">
|
||||
<property name="icon">
|
||||
<iconset theme="document-open">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>打开 Open</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Input">
|
||||
<property name="text">
|
||||
<string>导入 Input</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
126
README.md
126
README.md
@@ -1,127 +1,79 @@
|
||||
<h1 align="center">
|
||||
音·创 Musicreater
|
||||
</h1>
|
||||
<h1 align="center">音·创 Musicreater</h1>
|
||||
|
||||
<h2 align="center">库版 Package Version</h2>
|
||||
|
||||
<p align="center">
|
||||
<img width="128" height="128" src="https://gitee.com/TriM-Organization/Musicreater/raw/master/resources/msctIcon.ico">
|
||||
</img>
|
||||
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
|
||||
</p>
|
||||
|
||||
<h3 align="center">一款免费开源的《我的世界》数字音频转换库。</h3>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/BUILD%20WITH%20LOVE-FF3432?style=for-the-badge">
|
||||
<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>
|
||||
</a>
|
||||
<img src="https://forthebadge.com/images/badges/built-with-love.svg">
|
||||
<p>
|
||||
|
||||
[![][Bilibili: 金羿ELS]](https://space.bilibili.com/397369002/)
|
||||
[![][Bilibili: 诸葛亮与八卦阵]](https://space.bilibili.com/604072474)
|
||||
[![][Bilibili: 凌云金羿]](https://space.bilibili.com/397369002/)
|
||||
[![][Bilibili: 诸葛亮与八卦阵]](https://space.bilibili.com/604072474)
|
||||
[![CodeStyle: black]](https://github.com/psf/black)
|
||||
[![][python]](https://www.python.org/)
|
||||
[![][license]](LICENSE)
|
||||
[![][release]](../../releases)
|
||||
|
||||
[](https://gitee.com/TriM-Organization/Musicreater/stargazers)
|
||||
[](https://gitee.com/TriM-Organization/Musicreater/members)
|
||||
[](https://github.com/TriM-Organization/Musicreater/stargazers)
|
||||
[](https://github.com/TriM-Organization/Musicreater/forks)
|
||||
简体中文🇨🇳 | [English🇬🇧](README_EN.md)
|
||||
|
||||
简体中文 🇨🇳 | [English🇬🇧](README_EN.md)
|
||||
## **嘿伙计,你是不是来错地儿了,这里还在开发,注意选对版本!**
|
||||
|
||||
## 介绍 🚀
|
||||
## 介绍🚀
|
||||
|
||||
音·创 是一个免费开源的针对 **《我的世界》** 的 MIDI 音乐转换库
|
||||
音·创 Musicreater 是一款免费开源的 **《我的世界:基岩版》** 音乐制作软件
|
||||
|
||||
音·创 库版 (Musicreater Package Version) 是一款免费开源的针对 **《我的世界:基岩版》** 的midi音乐转换库
|
||||
|
||||
欢迎加群:[861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)
|
||||
|
||||
## 安装 🔳
|
||||
**此分支为音·创的包版本,即便于其他软件使用的可被import版本**
|
||||
|
||||
- 使用 pypi
|
||||
### **看这看这!看这看这!看这看这!教程:[教程链接](./docs/%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.md)**
|
||||
|
||||
```bash
|
||||
pip install --upgrade Musicreater
|
||||
```
|
||||
## 软件作者✒
|
||||
|
||||
- 如果无法更新最新,可以尝试:
|
||||
```bash
|
||||
pip install --upgrade -i https://pypi.python.org/simple Musicreater
|
||||
```
|
||||
金羿 Eilles:我的世界基岩版指令师,个人开发者,B站不知名UP主,江西在校高中生。
|
||||
|
||||
- 克隆仓库并安装(最新版本但**不推荐**)
|
||||
```bash
|
||||
git clone https://gitee.com/TriM-Organization/Musicreater.git
|
||||
cd Musicreater
|
||||
python setup.py install
|
||||
```
|
||||
诸葛亮与八卦阵 bgArray:我的世界基岩版玩家,喜欢编程和音乐,深圳初二学生。
|
||||
|
||||
以上命令中 `python`、`pip` 请依照各个环境不同灵活更换,可能为`python3`或`pip3`之类。
|
||||
|
||||
## 文档 📄
|
||||
|
||||
[生成文件的使用](./docs/%E7%94%9F%E6%88%90%E6%96%87%E4%BB%B6%E7%9A%84%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.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)
|
||||
- 感谢 **昀梦**\<QQ1515399885\> 找出指令生成错误bug并指正
|
||||
- 感谢由 **Charlie_Ping “查理平”** 带来的bdx文件转换参考,
|
||||
以及mid转我的世界乐器参考表格
|
||||
- 感谢由 **CMA_2401PT** 为我们的软件开发进行指导
|
||||
- 感谢由 **Dislink Sforza “断联·斯福尔扎”**\<QQ1600515314\>带来的midi音色解析以及转换指令的算法,我们将其改编并应用;同时,感谢他的[网页版转换器](https://dislink.github.io/midi2bdx/)给我们的开发与更新带来巨大的压力和动力,让我们在原本一骑绝尘的摸鱼道路上转向开发
|
||||
- 感谢 **Touch “偷吃”**\<QQ1793537164\>提供的测试支持,并对程序的改进提供了丰富的意见;同时也感谢他的不断尝试新的内容,使我们的排错更进一步
|
||||
- 感谢 **Mono**\<QQ738893087\>反馈安装时的问题
|
||||
- 感谢 **Ammelia** 敦促我们进行新的功能开发,并为新功能提出了非常优秀的大量建议
|
||||
|
||||
## 作者 ✒
|
||||
|
||||
**金羿 Eilles**:我的世界基岩版指令师,个人开发者,B 站不知名 UP 主,江西在校高中生。
|
||||
|
||||
**诸葛亮与八卦阵 bgArray**:我的世界基岩版玩家,喜欢编程和音乐,深圳初二学生。
|
||||
|
||||
**偷吃不是Touch Touch**:我的世界基岩版指令师,提供 BDX 导入测试支持
|
||||
|
||||
## 致谢 🙏
|
||||
|
||||
本致谢列表排名无顺序。
|
||||
|
||||
- 感谢 **昀梦**\<QQ1515399885\> 找出指令生成错误 bug 并指正
|
||||
- 感谢由 **Charlie_Ping “查理平”** 带来的 BDX 文件转换参考,以及 MIDI-我的世界对应乐器 参考表格
|
||||
- 感谢由 **[CMA_2401PT](https://github.com/CMA2401PT)** 为我们的软件开发的一些方面进行指导,同时我们参考了他的 BDXworkshop 作为 BDX 结构编辑的参考
|
||||
- 感谢由 **[Dislink Sforza](https://github.com/Dislink) “断联·斯福尔扎”**\<QQ1600515314\> 带来的 midi 音色解析以及转换指令的算法,我们将其改编并应用;同时,感谢他的[网页版转换器](https://dislink.github.io/midi2bdx/)给我们的开发与更新带来巨大的压力和动力,让我们在原本一骑绝尘的摸鱼道路上转向开发。
|
||||
- 感谢 **Mono**\<QQ738893087\> 反馈安装时的问题,辅助我们找到了视窗操作系统下的兼容性问题;感谢其反馈延迟播放器出现的重大问题,让我们得以修改全部延迟播放错误;尤其感谢他对于我们的软件的大力宣传
|
||||
- 感谢 **Ammelia “艾米利亚”**\<QQ2838334637\> 敦促我们进行新的功能开发,并为新功能提出了非常优秀的大量建议,以及提供的 BDX 导入测试支持,为我们的新结构生成算法提供了大量的实际理论支持
|
||||
- 感谢 **[神羽](https://gitee.com/snowykami) “[SnowyKami](https://github.com/snowyfirefly)”** 对我们项目的支持与宣传,非常感谢他为我们提供的服务器!
|
||||
- 感谢 **指令师\_苦力怕 playjuice123**\<QQ240667197\> 为我们的程序找出错误,并提醒我们修复一个一直存在的大 bug。
|
||||
- 感谢 **雷霆**\<QQ3555268519\> 用他那令所有开发者都大为光火的操作方法为我们的程序找出错误,并提醒修复 bug。
|
||||
- 感谢 **小埋**\<QQ2039310975\> 反馈附加包生成时缺少描述和标题的问题。
|
||||
- <table><tr><td>感谢 **油炸**\<QQ2836146704\> 激励我们不断开发新的内容。</td><td><img height="50" src="https://foruda.gitee.com/images/1695478907647543027/08ea9909_9911226.jpeg"></td></tr></table>
|
||||
|
||||
> 感谢广大群友为此库提供的测试和建议等
|
||||
> 感谢广大群友为此程序提供的测试等支持
|
||||
>
|
||||
> 若您对我们有所贡献但您的名字没有出现在此列表中,请联系我们!
|
||||
> 若您对我们有所贡献但您的名字没有显示在此列表中,请联系我!
|
||||
|
||||
## 联系 📞
|
||||
## 联系我们📞
|
||||
|
||||
若遇到库中的问题,欢迎在[此](https://gitee.com/TriM-Organization/Musicreater/issues/new)提出你的 issue。
|
||||
### 作者\<*金羿*\>(Eilles)联系方式
|
||||
|
||||
如果需要与开发组进行交流,欢迎加入我们的[开发闲聊 Q 群](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)。
|
||||
1. QQ 2647547478
|
||||
2. 电邮 EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com
|
||||
3. 微信 WYI_DoctorYI
|
||||
4. Telegram [@EillesWan](https://t.me/EillesWan)
|
||||
|
||||
亦可以联系我们[睿乐组织官方邮箱](mailto:TriM-Organization@hotmail.com),取得进一步联系!
|
||||
### 作者\<*诸葛亮与八卦阵*\>(bgArray)联系方式
|
||||
|
||||
---
|
||||
1. QQ 4740437765
|
||||
|
||||
此项目并非一个官方 《我的世界》(_Minecraft_)项目
|
||||
|
||||
此项目不隶属或关联于 Mojang Studios 或 微软
|
||||
|
||||
此项目亦不隶属或关联于 网易
|
||||
|
||||
“Minecraft”是 Mojang Synergies AB 的商标,此项目中所有对于“我的世界”、“Minecraft”等相关称呼均为引用性使用
|
||||
|
||||
- 上文提及的 网易 公司,指代的是在中国大陆运营《我的世界:中国版》的上海网之易网络科技发展有限公司
|
||||
|
||||
NOT AN OFFICIAL MINECRAFT PRODUCT.
|
||||
|
||||
NOT APPROVED BY OR ASSOCIATED WITH MOJANG OR MICROSOFT.
|
||||
|
||||
NOT APPROVED BY OR ASSOCIATED WITH NETEASE.
|
||||
|
||||
[Bilibili: 金羿ELS]: https://img.shields.io/badge/Bilibili-%E5%87%8C%E4%BA%91%E9%87%91%E7%BE%BF-00A1E7?style=for-the-badge
|
||||
[Bilibili: 凌云金羿]: https://img.shields.io/badge/Bilibili-%E5%87%8C%E4%BA%91%E9%87%91%E7%BE%BF-00A1E7?style=for-the-badge
|
||||
[Bilibili: 诸葛亮与八卦阵]: https://img.shields.io/badge/Bilibili-%E8%AF%B8%E8%91%9B%E4%BA%AE%E4%B8%8E%E5%85%AB%E5%8D%A6%E9%98%B5-00A1E7?style=for-the-badge
|
||||
[CodeStyle: black]: https://img.shields.io/badge/code%20style-black-121110.svg?style=for-the-badge
|
||||
[python]: https://img.shields.io/badge/python-3.8-AB70FF?style=for-the-badge
|
||||
[python]: https://img.shields.io/badge/python-3.6-AB70FF?style=for-the-badge
|
||||
[release]: https://img.shields.io/github/v/release/EillesWan/Musicreater?style=for-the-badge
|
||||
[license]: https://img.shields.io/badge/Licence-Apache-228B22?style=for-the-badge
|
||||
|
||||
128
README_EN.md
128
README_EN.md
@@ -1,130 +1,74 @@
|
||||
<h1 align="center">
|
||||
音·创 Musicreater
|
||||
</h1>
|
||||
<h1 align="center">音·创 Musicreater</h1>
|
||||
|
||||
<p align="center">
|
||||
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
|
||||
</img>
|
||||
<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 digital music files into <i>Minecraft</i> formats.</h3>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/BUILD%20WITH%20LOVE-FF3432?style=for-the-badge">
|
||||
</img>
|
||||
<img src="https://forthebadge.com/images/badges/built-with-love.svg">
|
||||
<p>
|
||||
|
||||
[![][Bilibili: Eilles]](https://space.bilibili.com/397369002/)
|
||||
[![][Bilibili: bgArray]](https://space.bilibili.com/604072474)
|
||||
[![][Bilibili: bgArray]](https://space.bilibili.com/604072474)
|
||||
[![CodeStyle: black]](https://github.com/psf/black)
|
||||
[![][python]](https://www.python.org/)
|
||||
![][python]
|
||||
[![][license]](LICENSE)
|
||||
[![][release]](../../releases)
|
||||
|
||||
[](https://gitee.com/TriM-Organization/Musicreater/stargazers)
|
||||
[](https://gitee.com/TriM-Organization/Musicreater/members)
|
||||
[](https://github.com/TriM-Organization/Musicreater/stargazers)
|
||||
[](https://github.com/TriM-Organization/Musicreater/forks)
|
||||
[简体中文🇨🇳](README.md) | English🇬🇧
|
||||
|
||||
[简体中文 🇨🇳](README.md) | English🇬🇧
|
||||
|
||||
**Notice that the localizations of documents may NOT be up-to-date.**
|
||||
**Notice that the language support of *README* may be a little SLOW.**
|
||||
|
||||
## Introduction🚀
|
||||
|
||||
Musicreater is a free open-source library used for converting digital music files into formats that could be read in _Minecraft_.
|
||||
Musicreater(音·创) is a free open source software which is used for making and also creating music in **Minecraft: Bedrock Edition**.
|
||||
|
||||
Musicreater pkgver(Package Version 音·创 库版) is a free open source library used for convert midi file into formats that is suitable for **Minecraft: Bedrock Edition**.
|
||||
|
||||
Welcome to join our QQ group: [861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)
|
||||
|
||||
## Installation 🔳
|
||||
### **SEEEEEEEEE Tutorials [HERE](./docs/Use%20of%20Funtions.md)**
|
||||
|
||||
- Via pypi
|
||||
### Authors✒
|
||||
|
||||
```bash
|
||||
pip install Musicreater --upgrade
|
||||
```
|
||||
Eilles (金羿):A high school student, individual developer, unfamous BilibiliUPer, which knows a little about commands in *Minecraft: Bedrock Edition*
|
||||
|
||||
- If above command cannot fetch latest version, try:
|
||||
```bash
|
||||
pip install -i https://pypi.python.org/simple Musicreater --upgrade
|
||||
```
|
||||
|
||||
- Clone repo and Install (Latest but **NOT RECOMMANDED**):
|
||||
```bash
|
||||
git clone https://github.com/TriM-Organization/Musicreater.git
|
||||
cd Musicreater
|
||||
python setup.py install
|
||||
```
|
||||
|
||||
Commands such as `python`、`pip` could be changed to some like `python3` or `pip3` according to the difference of platforms.
|
||||
bgArray "诸葛亮与八卦阵": Fix bugs, improve code aesthetics, add new functions, change data format, etc.
|
||||
|
||||
|
||||
## Documentation 📄
|
||||
## Thanks🙏
|
||||
|
||||
(Not in English yet)
|
||||
- Thank *昀梦*\<QQ1515399885\> for finding and correcting the bugs in the commands that *Musicreater* Created.
|
||||
- Thank *Charlie_Ping “查理平”* for bdx convert function, and
|
||||
the data label that's used to convert the mid's instruments into minecraft's instruments.
|
||||
- Thank *CMA_2401PT* for BDXWorkShop as the .bdx structure's operation guide.
|
||||
- Thank *Dislink Sforza* \<QQ1600515314\> for the algorithm brought to us, his midi analysis algorithm became one of us's best ones
|
||||
- Thank *Arthur Morgan*\<QQ312280061\> for his/her biggest support for the debugging of Musicreater
|
||||
- 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
|
||||
- Thanks for a lot of groupmates who support me and help me to test the program.
|
||||
- If you have give me some help but u haven't been in the list, please contact me.
|
||||
|
||||
[生成文件的使用](./docs/%E7%94%9F%E6%88%90%E6%96%87%E4%BB%B6%E7%9A%84%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.md)
|
||||
## Contact Information📞
|
||||
|
||||
[仓库 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)
|
||||
### Author *Eilles*(金羿)
|
||||
|
||||
### Authors ✒
|
||||
1. QQ 2647547478
|
||||
2. E-mail EillesWan2006@163.com
|
||||
W-YI_DoctorYI@outlook.com EillesWan@outlook.com
|
||||
3. WeChat WYI_DoctorYI
|
||||
4. Telegram [@EillesWan](https://t.me/EillesWan)
|
||||
|
||||
**Eilles (金羿)**:A senior high school student, individual developer, unfamous Bilibili UPer, which knows a little about commands in _Minecraft: Bedrock Edition_
|
||||
### Author *bgArray*(诸葛亮与八卦阵)
|
||||
|
||||
**bgArray (诸葛亮与八卦阵)**: A junior high school student, player of _Minecraft: Bedrock Edition_, which is a fan of music and programming.
|
||||
1. QQ 4740437765
|
||||
|
||||
**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
|
||||
|
||||
## Acknowledgements 🙏
|
||||
|
||||
This list is not in any order.
|
||||
|
||||
- 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 _[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.
|
||||
- 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 _[神羽](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 **雷霆**\<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=""It was once, a convert-QQ-bot is just in front my eyes" "Until lose, I finally know cannot chase back what I needs""></td><td><small>"It was once, a convert-QQ-bot is just in front my eyes"<br>"Until lose, I finally know cannot chase back what I needs"</small></td></tr></table>
|
||||
|
||||
> Thanks for the support and help of a lot of groupmates
|
||||
>
|
||||
> If you have given contributions but have not been in the list, please contact us!
|
||||
|
||||
## Contact Us 📞
|
||||
|
||||
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).
|
||||
|
||||
Or contact us via [TriM-Org Official Email](mailto:TriM-Organization@hotmail.com)!
|
||||
|
||||
---
|
||||
|
||||
NOT AN OFFICIAL MINECRAFT PRODUCT.
|
||||
|
||||
NOT APPROVED BY OR ASSOCIATED WITH MOJANG OR MICROSOFT.
|
||||
|
||||
NOT APPROVED BY OR ASSOCIATED WITH NETEASE.
|
||||
|
||||
此项目并非一个官方 《我的世界》(_Minecraft_)项目
|
||||
|
||||
此项目不隶属或关联于 Mojang Studios 或 微软
|
||||
|
||||
此项目亦不隶属或关联于 网易 相关
|
||||
|
||||
“Minecraft”是 Mojang Synergies AB 的商标,此项目中所有对于“我的世界”、“Minecraft”等相关称呼均为引用性使用
|
||||
|
||||
- 上文提及的 网易 公司,指代的是在中国大陆运营《我的世界:中国版》的上海网之易网络科技发展有限公司
|
||||
|
||||
|
||||
[Bilibili: Eilles]: https://img.shields.io/badge/Bilibili-%E5%87%8C%E4%BA%91%E9%87%91%E7%BE%BF-00A1E7?style=for-the-badge
|
||||
[Bilibili: bgArray]: https://img.shields.io/badge/Bilibili-%E8%AF%B8%E8%91%9B%E4%BA%AE%E4%B8%8E%E5%85%AB%E5%8D%A6%E9%98%B5-00A1E7?style=for-the-badge
|
||||
[CodeStyle: black]: https://img.shields.io/badge/code%20style-black-121110.svg?style=for-the-badge
|
||||
[python]: https://img.shields.io/badge/python-3.8-AB70FF?style=for-the-badge
|
||||
[python]: https://img.shields.io/badge/python-3.6-AB70FF?style=for-the-badge
|
||||
[release]: https://img.shields.io/github/v/release/EillesWan/Musicreater?style=for-the-badge
|
||||
[license]: https://img.shields.io/badge/Licence-Apache-228B22?style=for-the-badge
|
||||
[license]: https://img.shields.io/badge/Licence-Apache-228B22?style=for-the-badge
|
||||
@@ -1,8 +0,0 @@
|
||||
import Musicreater.experiment
|
||||
|
||||
|
||||
print(
|
||||
Musicreater.experiment.FutureMidiConvertRSNB.from_midi_file(
|
||||
input("midi路径:"), old_exe_format=False
|
||||
).to_note_list_in_delay()
|
||||
)
|
||||
@@ -1,22 +0,0 @@
|
||||
import shutil
|
||||
import os
|
||||
from rich.console import Console
|
||||
from rich.progress import track
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
def main():
|
||||
with console.status("Find the full path of .egg-info folder"):
|
||||
egg_info: list = []
|
||||
for file in os.listdir():
|
||||
if file.endswith(".egg-info"):
|
||||
egg_info.append(file)
|
||||
console.print(file)
|
||||
for file in track(["build", "dist", "logs", *egg_info], description="Deleting files"):
|
||||
if os.path.isdir(file) and os.access(file, os.W_OK):
|
||||
shutil.rmtree(file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
151
demo_convert.py
Normal file
151
demo_convert.py
Normal file
@@ -0,0 +1,151 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
# 音·创 开发交流群 861684859
|
||||
# Email EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com
|
||||
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 Lisence.md
|
||||
|
||||
|
||||
"""
|
||||
音·创 库版 MIDI转换展示程序
|
||||
Musicreater Package Version : Demo for Midi Conversion
|
||||
|
||||
Copyright 2023 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 ./Lisence.md
|
||||
Terms & Conditions: ./Lisence.md
|
||||
"""
|
||||
|
||||
from msctPkgver.main import *
|
||||
import os
|
||||
|
||||
convertion = midiConvert()
|
||||
|
||||
while True:
|
||||
midipath = input('请输入midi文件路径:')
|
||||
if os.path.exists(midipath):
|
||||
break
|
||||
else:
|
||||
print('文件不存在,请重新输入')
|
||||
|
||||
outpath = input('请输入输出路径:')
|
||||
|
||||
if not os.path.exists(outpath):
|
||||
os.makedirs(outpath)
|
||||
|
||||
while True:
|
||||
try:
|
||||
outFormat = int(input('请输入输出格式(0:mcpack|1:BDX结构):'))
|
||||
if outFormat == 0:
|
||||
isAutoReset = input('是否自动重置计分板(1|0):')
|
||||
if isAutoReset != '':
|
||||
isAutoReset = bool(int(isAutoReset))
|
||||
while True:
|
||||
isProgress = input('*进度条[本Demo不支持自定义]:')
|
||||
if isProgress != '':
|
||||
if isProgress in ('1', 'True'):
|
||||
isProgress = True
|
||||
elif isProgress in ('0', 'False'):
|
||||
isProgress = False
|
||||
else:
|
||||
isProgress = isProgress
|
||||
else:
|
||||
continue
|
||||
break
|
||||
sbname = input('请输入计分板名称:')
|
||||
volume = input('请输入音量(0-1):')
|
||||
if volume != '':
|
||||
volume = float(volume)
|
||||
speed = input('请输入速度倍率:')
|
||||
if speed != '':
|
||||
speed = float(speed)
|
||||
elif outFormat == 1:
|
||||
author = input('请输入作者:')
|
||||
while True:
|
||||
isProgress = input('*进度条[本Demo不支持自定义]:')
|
||||
if isProgress != '':
|
||||
if isProgress in ('1', 'True'):
|
||||
isProgress = True
|
||||
elif isProgress in ('0', 'False'):
|
||||
isProgress = False
|
||||
else:
|
||||
isProgress = isProgress
|
||||
else:
|
||||
continue
|
||||
break
|
||||
maxHeight = input('请输入指令结构最大生成高度:')
|
||||
if maxHeight != '':
|
||||
maxHeight = int(maxHeight)
|
||||
sbname = input('请输入计分板名称:')
|
||||
volume = input('请输入音量(0-1):')
|
||||
if volume != '':
|
||||
volume = float(volume)
|
||||
speed = input('请输入速度倍率:')
|
||||
if speed != '':
|
||||
speed = float(speed)
|
||||
isAutoReset = input('是否自动重置计分板(1|0):')
|
||||
if isAutoReset != '':
|
||||
isAutoReset = bool(int(isAutoReset))
|
||||
break
|
||||
except BaseException:
|
||||
print('输入错误,请重新输入')
|
||||
|
||||
|
||||
|
||||
|
||||
m = 1
|
||||
'''采用的算法编号'''
|
||||
|
||||
|
||||
if os.path.isdir(midipath):
|
||||
for i in os.listdir(midipath):
|
||||
if i.lower().endswith('.mid'):
|
||||
print(f'正在操作{i}')
|
||||
convertion.convert(midipath + '/' + i, outpath + '/' + i[:-4])
|
||||
if outFormat == 0:
|
||||
print(convertion.tomcpack(
|
||||
m,
|
||||
isAutoReset
|
||||
if isAutoReset != ''
|
||||
else bool(int(input('是否自动重置计分板(1|0):'))),
|
||||
isProgress,
|
||||
sbname if sbname != '' else input('请输入计分板名称:'),
|
||||
volume if volume != '' else float(input('请输入音量(0-1):')),
|
||||
speed if speed != '' else float(input('请输入速度倍率:')),
|
||||
))
|
||||
elif outFormat == 1:
|
||||
print(convertion.toBDXfile(
|
||||
m,
|
||||
author if author != '' else input('请输入作者:'),
|
||||
isProgress,
|
||||
maxHeight if maxHeight != '' else int(input('请输入指令结构最大生成高度:')),
|
||||
sbname if sbname != '' else input('请输入计分板名称:'),
|
||||
volume if volume != '' else float(input('请输入音量(0-1):')),
|
||||
speed if speed != '' else float(input('请输入速度倍率:')),
|
||||
isAutoReset
|
||||
if isAutoReset != ''
|
||||
else bool(int(input('是否自动重置计分板(1|0):'))),
|
||||
))
|
||||
else:
|
||||
convertion.convert(midipath, outpath)
|
||||
if outFormat == 0:
|
||||
print(convertion.tomcpack(
|
||||
m,
|
||||
isAutoReset if isAutoReset != '' else bool(int(input('是否自动重置计分板(1|0):'))),
|
||||
isProgress,
|
||||
sbname if sbname != '' else input('请输入计分板名称:'),
|
||||
volume if volume != '' else float(input('请输入音量(0-1):')),
|
||||
speed if speed != '' else float(input('请输入速度倍率:')),
|
||||
))
|
||||
elif outFormat == 1:
|
||||
print(convertion.toBDXfile(
|
||||
m,
|
||||
author if author != '' else input('请输入作者:'),
|
||||
isProgress,
|
||||
maxHeight if maxHeight != '' else int(input('请输入指令结构最大生成高度:')),
|
||||
sbname if sbname != '' else input('请输入计分板名称:'),
|
||||
volume if volume != '' else float(input('请输入音量(0-1):')),
|
||||
speed if speed != '' else float(input('请输入速度倍率:')),
|
||||
isAutoReset if isAutoReset != '' else bool(int(input('是否自动重置计分板(1|0):'))),
|
||||
))
|
||||
101
demo_convert_bdx_byDelay.py
Normal file
101
demo_convert_bdx_byDelay.py
Normal file
@@ -0,0 +1,101 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# THIS PROGRAM IS ONLY A TEST EXAMPLE
|
||||
|
||||
# 音·创 开发交流群 861684859
|
||||
# Email EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com
|
||||
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 Lisence.md
|
||||
|
||||
|
||||
"""
|
||||
音·创 库版 MIDI转换展示程序
|
||||
Musicreater Package Version : Demo for Midi Conversion
|
||||
|
||||
Copyright 2023 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 ./Lisence.md
|
||||
Terms & Conditions: ./Lisence.md
|
||||
"""
|
||||
|
||||
from msctPkgver.main import *
|
||||
|
||||
convertion = midiConvert()
|
||||
|
||||
while True:
|
||||
midipath = input('请输入midi文件路径:')
|
||||
if os.path.exists(midipath):
|
||||
break
|
||||
else:
|
||||
print('文件不存在,请重新输入')
|
||||
|
||||
outpath = input('请输入输出路径:')
|
||||
|
||||
if not os.path.exists(outpath):
|
||||
os.makedirs(outpath)
|
||||
|
||||
while True:
|
||||
try:
|
||||
authorname = input('请输入作者:')
|
||||
while True:
|
||||
isProgress = input('*进度条[本Demo不支持自定义]:')
|
||||
if isProgress != '':
|
||||
if isProgress in ('1', 'True'):
|
||||
isProgress = True
|
||||
elif isProgress in ('0', 'False'):
|
||||
isProgress = False
|
||||
else:
|
||||
isProgress = isProgress
|
||||
else:
|
||||
continue
|
||||
break
|
||||
|
||||
volume = input('请输入音量(0-1):')
|
||||
if volume != '':
|
||||
volume = float(volume)
|
||||
speed = input('请输入速度倍率:')
|
||||
if speed != '':
|
||||
speed = float(speed)
|
||||
player = input('请输入玩家选择器:')
|
||||
heightmax = input('请输入指令结构最大生成高度:')
|
||||
if heightmax != '':
|
||||
heightmax = int(heightmax)
|
||||
break
|
||||
|
||||
except BaseException:
|
||||
print('输入错误,请重新输入')
|
||||
|
||||
|
||||
def operation(
|
||||
i,
|
||||
):
|
||||
print(f'正在操作{i}')
|
||||
convertion.convert(midipath + '/' + i, outpath)
|
||||
convertion.toBDXfile_withDelay(
|
||||
1,
|
||||
authorname if authorname != '' else input('请输入作者:'),
|
||||
isProgress,
|
||||
heightmax if heightmax != '' else int(input('请输入指令结构最大生成高度:')),
|
||||
volume if volume != '' else float(input('请输入音量(0-1]:')),
|
||||
speed if speed != '' else float(input('请输入速度倍率:')),
|
||||
player if player != '' else input('请输入玩家选择器:'),
|
||||
)
|
||||
|
||||
|
||||
if os.path.isdir(midipath):
|
||||
import threading
|
||||
|
||||
for i in os.listdir(midipath):
|
||||
if i.lower().endswith('.mid'):
|
||||
threading.Thread(target=operation, args=(i,)).start()
|
||||
else:
|
||||
convertion.convert(midipath, outpath)
|
||||
convertion.toBDXfile_withDelay(
|
||||
1,
|
||||
authorname if authorname != '' else input('请输入作者:'),
|
||||
isProgress,
|
||||
heightmax if heightmax != '' else int(input('请输入指令结构最大生成高度:')),
|
||||
volume if volume != '' else float(input('请输入音量(0-1]:')),
|
||||
speed if speed != '' else float(input('请输入速度倍率:')),
|
||||
player if player != '' else input('请输入玩家选择器:'),
|
||||
)
|
||||
13
docs/API.md
13
docs/API.md
@@ -1,13 +0,0 @@
|
||||
<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
|
||||
262
docs/Use of Funtions.md
Normal file
262
docs/Use of Funtions.md
Normal file
@@ -0,0 +1,262 @@
|
||||
<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>
|
||||
|
||||
|
||||
# Instructions for Using Demo(s)
|
||||
|
||||
*This is a tutorial for using the demo(s) of this library, not the Development Guide. If you want to see so, please read Below*
|
||||
|
||||
## Under Windows
|
||||
|
||||
0. Install Python 3.6+
|
||||
|
||||
First of all, you need to install the runtime environment of this library, *Python*. And a Installation Pack maybe the best choice:
|
||||
|
||||
> [Downloading Python 64-bit](https://www.python.org/ftp/python/3.8.10/python-3.8.10-amd64.exe)
|
||||
> [Downloading Python 32-bit](https://www.python.org/ftp/python/3.8.10/python-3.8.10.exe)
|
||||
|
||||
While installing, it's better to check `Add Python 3.X to PATH`(just as the screenshot showed below), otherwise it needs to be set manually which may cause some time wasting
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1662736520757331846/e38efb81_9911226.png>
|
||||
|
||||
If you are new to Python or not very familiar to Computer Programming, and having the disk space in your PC's System Partition (usually C:) of 150 MB, you can directly choose *Install Now*.
|
||||
|
||||
However, if you want to do it like a pro, choosing *Customize Installation*, it's a necessary to be sure to check `pip` and `py launcher` will be installed on your computer(see screenshot below). The two options is required for the next step of installing the requirements.
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1662736621235871190/2ac3d98f_9911226.png>
|
||||
|
||||
After the installation, you can enter in your terminal(CMD/PowerShell/Bash/etc): "python" to ensure whether the installation was successful. If it was, your terminal will show things like below:
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1659972669907359295/cmd.png>
|
||||
|
||||
|
||||
1. Installing Requirements
|
||||
|
||||
It's better to open your terminal(CMD/PowerShell/Bash/etc) under Administrator Mode.
|
||||
|
||||
For example, if you want to use CMD in Administrator Mode, you can search `cmd` in the *Start Menu*, right click it and *Run as Administrator*
|
||||
|
||||
<img src="https://foruda.gitee.com/images/1662736878650993886/62487dd8_9911226.png">
|
||||
|
||||
Okay, after that, please enter in your terminal:
|
||||
|
||||
`pip install mido`
|
||||
|
||||
`pip install brotli`
|
||||
|
||||
If successful you will see something like below:
|
||||
|
||||
<img src="https://foruda.gitee.com/images/1662737676719454287/f61a70f7_9911226.png">
|
||||
|
||||
|
||||
|
||||
2. Download This Package and Demo(s)
|
||||
|
||||
- If you using Git, you can use the following commands to clone this lib:
|
||||
|
||||
`git clone -b pkgver https://gitee.com/EillesWan/Musicreater.git`
|
||||
|
||||
- If Git is not installed, you can download the zip package from the code page(from [GitHub](https://github.com/EillesWan/Musicreater.git) or [Gitee](https://gitee.com/EillesWan/Musicreater.git)). Or you are a Chinese fan having a QQ account, you can [Join the QQ Group 861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr) and get it from our GroupFiles.
|
||||
|
||||
<img src=" https://foruda.gitee.com/images/1659972440341216712/下载.png" >
|
||||
|
||||
|
||||
2. Start Using Demo(s)
|
||||
|
||||
Open your terminal in the directory of this, taking CMD, for example, just enter the directory and enter `cmd` in the path box:
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1659974437388532868/输入.png>
|
||||
<img src=https://foruda.gitee.com/images/1659974754378201859/输入c.png>
|
||||
|
||||
And enter one of the commands below by choosing what you need:
|
||||
|
||||
`python demo_convert.py`
|
||||
|
||||
`python demo_convert_bdx_byDelay.py`
|
||||
|
||||
|
||||
### Addition for Error(s) Using or Installing
|
||||
|
||||
1. Environment Error of Microsoft Visual C++ Redistributable
|
||||
|
||||
If you meet this condition as the screenshot shows:
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1659972789779764953/bug.jpeg>
|
||||
|
||||
Your MSVC Environment may be not installed, and you can download and install
|
||||
> [Here of 64-Bit VCREDIST](https://aka.ms/vs/17/release/vc_redist.x64.exe)
|
||||
> [Here of 32-Bit VCREDIST](https://aka.ms/vs/17/release/vc_redist.x86.exe)
|
||||
|
||||
Thank our groupmate *Mono* again for helping finding this problem.
|
||||
|
||||
|
||||
|
||||
## Under Linux OS
|
||||
|
||||
|
||||
### Install Runtime Environment
|
||||
|
||||
0. Install and Verify Python Runtime
|
||||
|
||||
Common Linux Releases do include a Python Runtime Environment, what we should do only is to check it is a satisfied version to our program. If the version ≥Python3.6, theoretically our program can be run.
|
||||
|
||||
We can type:
|
||||
|
||||
```bash
|
||||
python -V
|
||||
```
|
||||
|
||||
To check the Python version, as the follows
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1665120915821957090/429561fd_9911226.png>
|
||||
|
||||
- Not Necessary
|
||||
|
||||
If you want to change a Python version just as what I want to do, it is such a great fantastic action! Let do as the follows:
|
||||
|
||||
- pacman Package Manager(In Arch Linux Mostly)
|
||||
|
||||
Let's write python3 into the ingore list of updating. Via `vim` to edit `/etc/pacman.conf`, add `python3` after `IgnorePkg`.
|
||||
|
||||
```bash
|
||||
sudo vim /etc/pacman.conf
|
||||
```
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1665124611490335193/5e99ca26_9911226.png>
|
||||
|
||||
Then we can search for python releases in [Arch Achieve](https://archive.archlinux.org/packages/).(*HERE, under Arch, Python refers to Python3 defaultly, while some other Linux releases using Python2 as default. So dose Arch Achieve.*)What I find here is [Python3.8.6](https://archive.archlinux.org/packages/p/python/python-3.8.6-1-x86_64.pkg.tar.zst), so let's download she via `pacman`:
|
||||
|
||||
```bash
|
||||
sudo pacman -U https://archive.archlinux.org/packages/p/python/python-3.8.6-1-x86_64.pkg.tar.zst
|
||||
```
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1665126362769399903/ea4b9598_9911226.png>
|
||||
|
||||
Perfect!
|
||||
|
||||
1. Install and Verify pip Package Manager
|
||||
|
||||
Before installing, it is to be checked, wheather Python's pip is OK:
|
||||
|
||||
```bash
|
||||
python -m pip # To check is pip installed
|
||||
# If a long tip occured, it is OK
|
||||
|
||||
# If returned as this, then not.
|
||||
/usr/bin/python: No module named pip
|
||||
# We can install pip via:
|
||||
sudo pacman -S python-pip
|
||||
# Verfy, remember.
|
||||
python -m pip
|
||||
|
||||
|
||||
# If you did but failed, we should use other methods to install pip:
|
||||
wget https://bootstrap.pypa.io/get-pip.py
|
||||
sudo python get-pip.py
|
||||
# Verfy, must.
|
||||
python -m pip
|
||||
```
|
||||
|
||||
Over after checking, lets install the dependences.
|
||||
|
||||
```bash
|
||||
pip install mido -i https://mirrors.aliyun.com/pypi/simple/
|
||||
pip install brotli -i https://mirrors.aliyun.com/pypi/simple/
|
||||
```
|
||||
|
||||
See the tips below as successfully installed:
|
||||
|
||||
<img src="https://foruda.gitee.com/images/1662737676719454287/f61a70f7_9911226.png">
|
||||
|
||||
|
||||
### Download this sources pack and Using its demos.
|
||||
|
||||
1. 使用Git下载本库及其示例代码
|
||||
|
||||
```bash
|
||||
git clone -b pkgver https://gitee.com/EillesWan/Musicreater.git MSCTpkgver
|
||||
```
|
||||
|
||||
当上述命令执行成功,你会在执行此命令的所在位置发现一个名为 `MSCTpkgver` 的文件夹,其中包含的正是我们心心念念下载的本程序和示例代码。
|
||||
而我们要运行的也正是示例代码,因此,赶快进入下载到的文件夹:
|
||||
|
||||
```bash
|
||||
cd MSCTpkgver
|
||||
```
|
||||
|
||||
1. 开始使用
|
||||
|
||||
在目录下打开终端,执行以下命令:(选择你需要的)
|
||||
|
||||
```bash
|
||||
python demo_convert.py
|
||||
python demo_convert_bdx_byDelay.py
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2. More Info for Parameters of Our Demo Program(s)
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1659974810147043475/运行.png>
|
||||
|
||||
|Input Prompt|English Translation|Description|
|
||||
|----------------|----------------|-------|
|
||||
|请输入MIDI文件路径|Midi File Path|Path of a .mid file or a directory. While directory entered, our demo will convert all midi(s) in the directory|
|
||||
|请输入输出路径|Output Path|Where files converted in|
|
||||
|是否重置计分板|Whether Reset Scoreboard Automatically|Can only be 1 or 0(Recommanded 1)|
|
||||
|*进度条[注]|Progressbar|Whether to enable Progressbar and customize progressbar style. Type 0 or False to disable, 1 or True to use default style, or type using format taught in follow the Instructions below to customize one you like|
|
||||
|请输入计分板名称|Scoreboard Name|*Only not byDelay* The name of scoreboard that player using|
|
||||
|请输入音量|Volume|Only decimal in [0,1],(Recommanded 1)|
|
||||
|请输入速度倍率|Speed Multiplying Factor|Maybe you want to play it faster(>1) or slower(>0 <1)?|
|
||||
|请输入玩家选择器|Player Selector|Full Selector including `@x`. E.g: Play for players tagged `Holo`, enter `@a[tag=Holo]` on this parameter|
|
||||
|
||||
|
||||
|
||||
|
||||
# Instructions for **Customize Progress Bar**
|
||||
|
||||
We have supported the function of making progress bar in *Minecraft*'s music player. And also the method of customize them. So the following instructions are about the parameters of the Progress Bar Customizition.
|
||||
|
||||
A Progress Bar, of course, is composed of **changeless** parts and **changable** parts. And the changable parts include texts or *images*(these images are made up of texts, or we can say, character paintings 😁). That is, for *Minecraft*, a changable image in a progress bar is just the "bar" part(which is like a stripe).
|
||||
|
||||
We use a string to describe the style of progress bar you need, and it includes many **identifier**s to replace the changable parts.
|
||||
|
||||
There are the identifiers:
|
||||
|
||||
| Identifier | Changable Part |
|
||||
|--------------|------------------------------------------------------|
|
||||
| `%%N` | Music name(file name which is imported into program) |
|
||||
| `%%s` | Value of scoreboard of now |
|
||||
| `%^s` | Max value of scoreboard |
|
||||
| `%%t` | Current playback time |
|
||||
| `%^t` | Total music time |
|
||||
| `%%%` | Current playback progress |
|
||||
| `_` | To be replaced by the *Bar* part of the progress bar |
|
||||
|
||||
The `_` is a placeholder to identifying the *bar* part, yeah, just the changable image.
|
||||
|
||||
This is an example of **style description string**, and this is also the default style of *Musicreater*'s progress bar.
|
||||
|
||||
`▶ %%N [ %%s/%^s %%% __________ %%t|%^t]`
|
||||
|
||||
This is a progress bar with only one line, but it is possible if you want to give a multiline parameter into the style description string.
|
||||
|
||||
But the string above is only for style identification, but we also need to identifying the changable image's image(just what the bar's look).
|
||||
|
||||
A "bar", simply, included 2 parts: *Have Been Played* & *Not Been Played*. So we use a tuple to pass the parameter. It's under a simple format: `(str: played, str: not)`. For example, the default parameter is below:
|
||||
|
||||
`('§e=§r', '§7=§r')`
|
||||
|
||||
So it's time to combine what I said in one parameter now!
|
||||
|
||||
This is a default definder parameter:
|
||||
|
||||
`('▶ %%N [ %%s/%^s %%% __________ %%t|%^t]',('§e=§r', '§7=§r'))`
|
||||
|
||||
*Tip: To avoid errors, please not to use the identifiers as the other part of your style.*
|
||||
167
docs/download&atart/Android.md
Normal file
167
docs/download&atart/Android.md
Normal file
@@ -0,0 +1,167 @@
|
||||
## 使用前的准备工作
|
||||
|
||||
### 安装终端工具
|
||||
|
||||
这里我们选用 **Termux** 作为我们的终端工具来安装,这是一个强大的终端模拟器,旨在安卓环境下模拟Linux的软件包环境。
|
||||
|
||||
1. 下载
|
||||
|
||||
下载可以通过 [GitHub源](https://github.com/termux/termux-app/releases) 或者 [F-Droid源](https://f-droid.org/en/packages/com.termux/) ,个人建议选择 F-Droid 源,因为在国内可以访问得到,而 GitHub 源就看运气。
|
||||
|
||||
2. 安装
|
||||
|
||||
下载*APK*包后直接安装,安装后打开可以看到一个类似这样的界面:
|
||||
|
||||
<img height="512" src="https://foruda.gitee.com/images/1665933025120627254/a0479618_9911226.jpeg">
|
||||
|
||||
3. 完成
|
||||
|
||||
恭喜你,你已经获得了一个极客般流畅地操作你手机的终端工具。
|
||||
|
||||
### 安装运行环境
|
||||
|
||||
1. 换源
|
||||
|
||||
接下来,我们就要来准备安装一下 **Python** 运行环境了,这是运行 **Python** 源代码必要的。
|
||||
|
||||
首先,我估计你等不了多久,急得要死,所以我们要让下载速度稍微快一点,先来换个源。在 **Termux** 中,输入以下指令:
|
||||
|
||||
```bash
|
||||
echo "deb https://mirror.mwt.me/termux/main stable main" > /data/data/com.termux/files/usr/etc/apt/sources.list
|
||||
```
|
||||
|
||||
*感谢 天如<QQ 3291691454>为我们带来的简单换源方法。*
|
||||
|
||||
- *非必要步骤*:手动编辑换源
|
||||
|
||||
如果你闲着没事,非要要手动编辑个文档来换源,那用啥?用普通的编辑器肯定可以,于是我们就让他更普通一点,用**nano**吧!
|
||||
|
||||
在 **Termux** 中,输入以下指令:
|
||||
|
||||
```bash
|
||||
export EDITOR=nano
|
||||
apt edit-sources
|
||||
```
|
||||
|
||||
那么请把看到的如左下图的界面变为右下图吧:
|
||||
|
||||
<table><tr>
|
||||
<td><img src="https://foruda.gitee.com/images/1665933104313107707/41108f03_9911226.jpeg"> </td>
|
||||
<td><img src="https://foruda.gitee.com/images/1665933122534781330/3887a901_9911226.jpeg"></td>
|
||||
</tr></table>
|
||||
|
||||
- 图片中的文件,最后应该加入的两行为:
|
||||
|
||||
```bash
|
||||
deb https://mirrors.ustc.edu.cn/termux/apt/termux-main/ stable main
|
||||
deb https://mirrors.tuna.tsinghua.edu.cn/termux/apt/termux-main/ stable main
|
||||
```
|
||||
|
||||
然后键入 `Ctrl`+`S`,再键入 `Ctrl`+`X`,退出`nano`。
|
||||
|
||||
在换源之后,你可能会见到类似的提示:
|
||||
|
||||
```bash
|
||||
Your '/data/data/com.termux/files/usr/etc/apt/sources.list' file changed. Please run 'apt-get update'.
|
||||
```
|
||||
|
||||
那就遵循它的指引,输入:
|
||||
|
||||
```bash
|
||||
apt-get update
|
||||
```
|
||||
|
||||
Alright.
|
||||
|
||||
2. 安装 **Python**
|
||||
|
||||
```bash
|
||||
apt-get install python3
|
||||
```
|
||||
|
||||
如果遇到提示问是否继续,那就输入`Y`表示是,如左下图,安装成功后,图若右下。
|
||||
|
||||
<table><tr>
|
||||
<td><img src="https://foruda.gitee.com/images/1665933181440420034/7f0fb5fd_9911226.jpeg"></td>
|
||||
<td><img src="https://foruda.gitee.com/images/1665933238339972260/a9f06f4f_9911226.jpeg"></td>
|
||||
</tr></table>
|
||||
|
||||
接下来,我们来试一试 **Python** 是不是安装成了吧,输入
|
||||
|
||||
```bash
|
||||
python3 -V
|
||||
```
|
||||
|
||||
如果输出了形如 `Python 3.X.X` 的提示,则完成。
|
||||
|
||||
3. 安装依赖库
|
||||
|
||||
```bash
|
||||
# 首先换源
|
||||
pip config set global.index-url http://mirrors.aliyun.com/pypi/simple/
|
||||
# 然后安装
|
||||
pip install mido
|
||||
pip install brotli
|
||||
```
|
||||
|
||||
- 如果出现以下情况,真是死了鬼的,我们要来再搞个设置:
|
||||
|
||||
<img height="512" src="https://foruda.gitee.com/images/1665933289612919459/b87b7804_9911226.jpeg">
|
||||
|
||||
我们来修改收信任的源设置:
|
||||
|
||||
```bash
|
||||
pip config set global.trusted-host mirrors.aliyun.com/
|
||||
```
|
||||
|
||||
之后再来安装即可
|
||||
|
||||
```bash
|
||||
pip install mido
|
||||
pip install brotli
|
||||
```
|
||||
|
||||
安装成功后您可能会见到类似下图的提示:
|
||||
|
||||
<img src="https://foruda.gitee.com/images/1662737676719454287/f61a70f7_9911226.png">
|
||||
|
||||
### 安装下载工具
|
||||
|
||||
既然已经有了运行环境,那么我们就需要下载下我们的**音·创库版示例代码**工具,我非常推崇**Git**这种方便快捷好用还能下载仓库的代码管理器,这个世界上你也找不到第二个,所以我们来安装一下:
|
||||
|
||||
```bash
|
||||
pkg install git
|
||||
```
|
||||
|
||||
安装完成后记得测试一下:
|
||||
|
||||
<img height="512" src="https://foruda.gitee.com/images/1665933331269483373/9374c85d_9911226.jpeg">
|
||||
|
||||
## 本代码库的下载与演示程序的使用
|
||||
|
||||
1. 使用Git下载本库及其示例代码
|
||||
|
||||
```bash
|
||||
git clone -b pkgver https://gitee.com/EillesWan/Musicreater.git MSCTpkgver
|
||||
```
|
||||
|
||||
当上述命令执行成功,你会在执行此命令的所在位置发现一个名为 `MSCTpkgver` 的文件夹,其中包含的正是我们心心念念下载的本程序和示例代码。
|
||||
而我们要运行的也正是示例代码,因此,赶快进入下载到的文件夹:
|
||||
|
||||
```bash
|
||||
cd MSCTpkgver
|
||||
```
|
||||
|
||||
1. 开始使用演示程序
|
||||
|
||||
依照你的需要,执行以下命令之一:
|
||||
|
||||
```bash
|
||||
python demo_convert.py #计分板播放器,支持mcpack与BDX
|
||||
python demo_convert_bdx_byDelay.py #延迟播放器,仅支持BDX
|
||||
```
|
||||
|
||||
运行成功了,哦耶!
|
||||
|
||||
<img height="512" src="https://foruda.gitee.com/images/1665933366784631363/db9f80f6_9911226.jpeg">
|
||||
|
||||
104
docs/download&atart/Linux.md
Normal file
104
docs/download&atart/Linux.md
Normal file
@@ -0,0 +1,104 @@
|
||||
|
||||
## 运行环境安装
|
||||
|
||||
### 检验Python运行环境
|
||||
|
||||
0. 一般的Linux发行版都有安装Python环境,我们只需要保证其版本即可,理论上 ≥Python3.6 都可以运行我们的库
|
||||
|
||||
我们可以使用
|
||||
|
||||
```bash
|
||||
python -V
|
||||
```
|
||||
|
||||
来查看 Python 版本,如下
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1665120915821957090/429561fd_9911226.png>
|
||||
|
||||
1. *非必要环节*:回退版本
|
||||
|
||||
如果你跟作者一样,觉得 Python 3.10+ 太难用很烦人的话,那真是皆大欢喜,让我们一起来回退版本吧!
|
||||
|
||||
- pacman 包管理器(多用于Arch Linux上)
|
||||
|
||||
1. 让我们先来把 python3 加入忽略升级的列表中,使用`vim`修改`/etc/pacman.conf`,在`IgnorePkg`后加上`python3`
|
||||
|
||||
```bash
|
||||
sudo vim /etc/pacman.conf
|
||||
```
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1665124611490335193/5e99ca26_9911226.png>
|
||||
|
||||
2. 然后我们开始从[Arch Achieve](https://archive.archlinux.org/packages/)上找Python的版本列表。(*这里说明一下,在Arch中,Python默认指的是Python3,而与其他某些Linux发行版中Python默认指代Python2不同,所以在Arch Achieve中也是如此。*)我这里找到的是[Python3.8.6](https://archive.archlinux.org/packages/p/python/python-3.8.6-1-x86_64.pkg.tar.zst),于是我们用`pacman`把她下载下来并安装:
|
||||
|
||||
```bash
|
||||
sudo pacman -U https://archive.archlinux.org/packages/p/python/python-3.8.6-1-x86_64.pkg.tar.zst
|
||||
```
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1665126362769399903/ea4b9598_9911226.png>
|
||||
|
||||
3. 完美!
|
||||
|
||||
- 其他包管理器
|
||||
|
||||
暂无
|
||||
|
||||
### 检查并安装pip包管理器依赖
|
||||
|
||||
1. 我们在安装依赖库之前,应该确认一下,Python自带的包管理器pip是否安装到位:
|
||||
|
||||
```bash
|
||||
python -m pip # 确认pip是否安装
|
||||
# 当这个命令输入后有长段提示出现则为已经安装
|
||||
|
||||
# 如果返回如下,那么则pip尚未安装
|
||||
/usr/bin/python: No module named pip
|
||||
# 可以使用如下命令来安装pip
|
||||
sudo pacman -S python-pip
|
||||
# 安装完成后记得验证
|
||||
python -m pip
|
||||
|
||||
|
||||
# 如果还是失败,那么就需要用其他工具安装pip:
|
||||
wget https://bootstrap.pypa.io/get-pip.py
|
||||
sudo python get-pip.py
|
||||
# 安装完成后一定要验证!!!
|
||||
python -m pip
|
||||
```
|
||||
|
||||
2. 确认完成之后,我们来安装一下依赖库:
|
||||
|
||||
```bash
|
||||
pip install mido -i https://mirrors.aliyun.com/pypi/simple/
|
||||
pip install brotli -i https://mirrors.aliyun.com/pypi/simple/
|
||||
```
|
||||
|
||||
3. 安装成功后可能会见到类似下图的提示:
|
||||
|
||||
<img src="https://foruda.gitee.com/images/1662737676719454287/f61a70f7_9911226.png">
|
||||
|
||||
|
||||
## 本代码库的下载与使用
|
||||
|
||||
1. 使用Git下载本库及其示例代码
|
||||
|
||||
```bash
|
||||
git clone -b pkgver https://gitee.com/EillesWan/Musicreater.git MSCTpkgver
|
||||
```
|
||||
|
||||
当上述命令执行成功,你会在执行此命令的所在位置发现一个名为 `MSCTpkgver` 的文件夹,其中包含的正是我们心心念念下载的本程序和示例代码。
|
||||
而我们要运行的也正是示例代码,因此,赶快进入下载到的文件夹:
|
||||
|
||||
```bash
|
||||
cd MSCTpkgver
|
||||
```
|
||||
|
||||
1. 开始使用
|
||||
|
||||
在目录下打开终端,执行以下命令:(选择你需要的)
|
||||
|
||||
```bash
|
||||
python demo_convert.py #计分板播放器,支持mcpack与BDX
|
||||
python demo_convert_bdx_byDelay.py #延迟播放器,仅支持BDX
|
||||
```
|
||||
|
||||
89
docs/download&atart/Windows.md
Normal file
89
docs/download&atart/Windows.md
Normal file
@@ -0,0 +1,89 @@
|
||||
## 一、运行环境安装
|
||||
|
||||
### (一)安装 Python3.6+
|
||||
|
||||
1. 首先需要下载Python的安装包,最好是 *Python3.8*,因为作者就用的是这个版本
|
||||
|
||||
> [下载64位Python安装包](https://www.python.org/ftp/python/3.8.10/python-3.8.10-amd64.exe)
|
||||
> [下载32位Python安装包](https://www.python.org/ftp/python/3.8.10/python-3.8.10.exe)
|
||||
|
||||
2. 在安装时,最好需要勾选 `Add Python 3.X to PATH`,如下图所示,当然,如果您对自己非常自信,您也可以手动设置此项目:
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1662736520757331846/e38efb81_9911226.png>
|
||||
|
||||
- 若您对Python一知半解或者不怎么了解、并对自己的系统盘(通常是C盘)有大约150*兆字节*(MB)的信心的话,您可以在安装时直接选择*快速安装*(Install Now)
|
||||
|
||||
3. 若您选择了*自定义安装*(Customize Installation),请务必勾选 `pip` 和 `py launcher` 便于后续安装依赖,如下图:
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1662736621235871190/2ac3d98f_9911226.png>
|
||||
|
||||
4. 安装结束之后可以在*终端*(命令行/PowerShell/Bash/etc)中输入:python 试试是否安装成功,成功安装之后,在终端中输入python会显示诸如如下图片的提示:
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1659972669907359295/cmd.png>
|
||||
|
||||
|
||||
### (二)安装依赖
|
||||
|
||||
1. 请以管理员模式打开您的*终端*(命令行/PowerShell/Bash/etc)
|
||||
|
||||
例如,命令行,可以如此打开:在*视窗开始菜单*(Windows开始)中搜索 `cmd`, 并以管理员身份运行
|
||||
|
||||
<img src="https://foruda.gitee.com/images/1662736878650993886/62487dd8_9911226.png">
|
||||
|
||||
2. 打开了终端之后,请在终端中输入以下指令
|
||||
|
||||
```bash
|
||||
pip install mido -i https://mirrors.aliyun.com/pypi/simple/
|
||||
pip install brotli -i https://mirrors.aliyun.com/pypi/simple/
|
||||
```
|
||||
|
||||
3. 安装成功后您可能会见到类似下图的提示:
|
||||
|
||||
<img src="https://foruda.gitee.com/images/1662737676719454287/f61a70f7_9911226.png">
|
||||
|
||||
## 二、本工具的下载与使用
|
||||
|
||||
0. 下载本代码库以及演示程序
|
||||
|
||||
- 若您使用git,请直接克隆本仓库:
|
||||
|
||||
```bash
|
||||
git clone -b pkgver https://gitee.com/EillesWan/Musicreater.git
|
||||
```
|
||||
|
||||
- 若您不使用git,可以在[*码云*(Gitee)](https://gitee.com/EillesWan/Musicreater.git)或[*GitHub*](https://github.com/EillesWan/Musicreater.git)下载zip包,或者[加入QQ群聊861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr),在群文件中获取。
|
||||
|
||||
<img src="https://foruda.gitee.com/images/1669540786443169766/fabf0acd_9911226.png" >
|
||||
|
||||
值得注意的是,这张图上有1、2两个数字,虽然是手写的,但确实是数字,表示着要进行的步骤。希望眼尖得能找出我的口头禅的你们能够发现这两个用鼠标手写的数字并在安装时认真地执行。我认为这并不算十分的难,移动鼠标并单击大约消耗不了多少卡路里,不过我没有进行精确的计算,我也不是十分的清楚这个活动对于一个常人来讲有多难,但我怀疑它不难。
|
||||
|
||||
|
||||
1. 开始使用
|
||||
|
||||
在目录下打开终端。
|
||||
|
||||
例如:打开命令行:请进入到目录下,在文件资源管理器的地址框内输入`cmd`:
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1659974437388532868/输入.png>
|
||||
<img src=https://foruda.gitee.com/images/1659974754378201859/输入c.png>
|
||||
|
||||
执行以下命令:(选择你需要的)
|
||||
|
||||
```bash
|
||||
python demo_convert.py #计分板播放器,支持mcpack与BDX
|
||||
python demo_convert_bdx_byDelay.py #延迟播放器,仅支持BDX
|
||||
```
|
||||
|
||||
## 三、安装时错误的补充说明
|
||||
|
||||
1. Microsoft Visual C++ Redistributable 环境出错
|
||||
|
||||
如果你遇到了类似以下这种情况:
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1659972789779764953/bug.jpeg>
|
||||
|
||||
请下载最新的VCREDIST安装包,可以参照[这个网页](https://docs.microsoft.com/zh-CN/cpp/windows/latest-supported-vc-redist)的说明,也可以在这直接选择你需要的安装包下载:
|
||||
> [下载64位VCREDIST安装包](https://aka.ms/vs/17/release/vc_redist.x64.exe)
|
||||
> [下载32位VCREDIST安装包](https://aka.ms/vs/17/release/vc_redist.x86.exe)
|
||||
|
||||
感谢群友Mono帮我们发现这个问题。
|
||||
105
docs/功能使用说明.md
Normal file
105
docs/功能使用说明.md
Normal file
@@ -0,0 +1,105 @@
|
||||
<h1 align="center">音·创 Musicreater</h1>
|
||||
|
||||
<h2 align="center">库版 Package Version</h2>
|
||||
|
||||
<p align="center">
|
||||
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
|
||||
</p>
|
||||
|
||||
# 演示程序使用教程
|
||||
|
||||
*由于先前的 **读我文件**(README.md) 过于冗杂,现另辟蹊径来给大家全方位的教程。*
|
||||
|
||||
*这是演示程序(demo)的使用教程,将在这里提供演示程序的相应的使用教程*
|
||||
|
||||
## 下载与启动教程
|
||||
|
||||
### [视窗(Windows)操作系统](./download%26atart/Windows.md)
|
||||
### [里纽克斯(Linux)操作系统](./download%26atart/Linux.md)
|
||||
### [安卓(Android)与安卓衍生操作系统](./download%26atart/Android.md)
|
||||
|
||||
## 演示程序使用教程
|
||||
|
||||
1. 参数说明
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1659974810147043475/运行.png>
|
||||
|
||||
- midi路径:含有mid文件路径、文件名、后缀的完整文件路径,或者一个目录(demo可接受批量转换)。可以使用相对或绝对路径皆可
|
||||
|
||||
- 输出路径:输出文件夹的路径,不需要指示文件名
|
||||
|
||||
- 是否重置计分板:`1`或`0`(歌曲放完是否重置,推荐`1`)
|
||||
|
||||
- 进度条:是否启用进度条,以及自定义进度条样式。输入`0`或`False`表示不启用进度条,输入`1`或`True`表示使用默认进度条,其余的输入均表示使用输入的格式作为自定义的进度条样式
|
||||
|
||||
若不支持自定义,则仅能输入`0`或`1`表示“关闭”或“开启”
|
||||
|
||||
- 计分板名称:游戏内的计分板名称
|
||||
|
||||
- 音量:0-1之间的小数(含0,1)正常来说推荐`1`
|
||||
|
||||
- 速度倍率:小数数据,其值不可为0,一般写`1`
|
||||
|
||||
- 玩家选择器:包括 `@x` 在内的全部选择器,即若要选择全部标签为`Holo`的玩家,则需要如此输入:`@a[tag=Holo]`
|
||||
|
||||
- 没有报错且在输出路径下找到mcpack或bdx即为生成成功:
|
||||
|
||||
<img src=https://foruda.gitee.com/images/1659973655881460036/输出.png>
|
||||
|
||||
|
||||
# 其他说明
|
||||
|
||||
## 对于 延迟播放器 的说明
|
||||
|
||||
问1:什么是延迟播放器?
|
||||
|
||||
答1:播放音乐,不同的音符之间存在间隔,可以用不同的方式实现音符间空白的时间。其一是计分板,通过给计分板加分,对应分数播放对应的音符就可以间隔到,还有就是命令方块自带的延迟,这样也可以实现音符间空白。延迟播放器即后者,但是因为只能对命令方块写入这样的延迟数据,所以仅可以使用包含NBT的结构导出或者直接输出至世界。
|
||||
|
||||
问2:可不可以不要积分板?
|
||||
|
||||
答2:详见问1,切换你所需要的播放器即可。
|
||||
|
||||
## 对于 进度条自定义 功能的说明
|
||||
|
||||
因为我们提供了可以自动转换进度条的功能,因此在这里给出进度条自定义参数的详细解释。
|
||||
|
||||
请注意,并非所有的演示样例程序都支持自定义进度条。
|
||||
|
||||
一个进度条,明显地,有**固定部分**和**可变部分**来构成。而可变部分又包括了文字和图形两种(当然,《我的世界》里头的进度条,可变的图形也就是那个“条”了)。这一点你需要了解,因为后文中包含了很多这方面的概念需要你了解。
|
||||
|
||||
进度条的自定义功能使用一个字符串来定义自己的样式,其中包含众多**标识符**来表示可变部分。
|
||||
|
||||
标识符如下(注意大小写):
|
||||
|
||||
| 标识符 | 指定的可变量 |
|
||||
|---------|----------------|
|
||||
| `%%N` | 乐曲名(即传入的文件名)|
|
||||
| `%%s` | 当前计分板值 |
|
||||
| `%^s` | 计分板最大值 |
|
||||
| `%%t` | 当前播放时间 |
|
||||
| `%^t` | 曲目总时长 |
|
||||
| `%%%` | 当前进度比率 |
|
||||
| `_` | 用以表示进度条占位|
|
||||
|
||||
表示进度条占位的 `_` 是用来标识你的进度条的。也就是可变部分的唯一的图形部分。
|
||||
|
||||
**样式定义字符串**的样例如下,这也是默认的进度条的样式:
|
||||
|
||||
`▶ %%N [ %%s/%^s %%% __________ %%t|%^t]`
|
||||
|
||||
这是单独一行的进度条,当然你也可以制作多行的,如果是一行的,输出时所使用的指令便是 `title`,而如果是多行的话,输出就会用 `titleraw` 作为进度条字幕。
|
||||
|
||||
哦对了,上面的只不过是样式定义,同时还需要定义的是可变图形的部分,也就是进度条上那个真正的“条”。
|
||||
|
||||
对于这个我们就采用了固定参数的方法,对于一个进度条,无非就是“已经播放过的”和“没播放过的”两种形态,所以,使用一个元组来传入这两个参数就是最简单的了。元组的格式也很简单:`(str: 播放过的部分长啥样, str: 没播放过的部分长啥样)` 。例如,我们默认的进度“条”的定义是这样的:
|
||||
|
||||
`('§e=§r', '§7=§r')`
|
||||
|
||||
综合起来,把这些参数传给函数需要一个参数整合,你猜用的啥?啊对对对,我用的还是元组!
|
||||
|
||||
我们的默认定义参数如下:
|
||||
|
||||
`(r'▶ %%N [ %%s/%^s %%% __________ %%t|%^t]',('§e=§r', '§7=§r'))`
|
||||
|
||||
*对了!为了避免生成错误,请尽量避免使用标识符作为定义样式字符串的其他部分*
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
<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>
|
||||
|
||||
**此为开发相关文档,内容包括:库的简单调用、所生成文件结构的详细说明、特殊参数的详细解释**
|
||||
|
||||
# 库的简单调用
|
||||
|
||||
参见[example.py的相关部分](../example.py),使用此库进行MIDI转换非常简单。
|
||||
|
||||
- 在导入转换库后,使用 MidiConvert 类建立转换对象(读取Midi文件)
|
||||
|
||||
音·创库支持新旧两种execute语法,需要在对象实例化时指定
|
||||
```python
|
||||
# 导入音·创库
|
||||
import Musicreater
|
||||
|
||||
# 指定是否使用旧的execute指令语法(即1.18及以前的《我的世界:基岩版》语法)
|
||||
old_execute_format = False
|
||||
|
||||
# 可以通过文件地址自动读取
|
||||
cvt_mid = Musicreater.MidiConvert.from_midi_file(
|
||||
"Midi文件地址",
|
||||
old_exe_format=old_execute_format
|
||||
)
|
||||
|
||||
# 也可以导入Mido对象
|
||||
cvt_mid = Musicreater.MidiConvert(
|
||||
mido.MidiFile("Midi文件地址"),
|
||||
"音乐名称",
|
||||
old_exe_format=old_execute_format
|
||||
)
|
||||
```
|
||||
|
||||
- 获取 Midi 音乐经转换后的播放指令
|
||||
|
||||
```python
|
||||
# 通过函数 to_command_list_in_score, to_command_list_in_delay
|
||||
# 分别可以得到
|
||||
# 以计分板作为播放器的指令对象列表、以延迟作为播放器的指令对象列表
|
||||
# 数据不仅存储在对象本身内,也会以返回值的形式返回,详见代码内文档
|
||||
|
||||
# 使用 to_command_list_in_score 函数进行转换之后,返回值有三个
|
||||
# 值得注意的是第一个返回值返回的是依照midi频道存储的指令对象列表
|
||||
# 也就是列表套列表
|
||||
# 但是,在对象内部所存储的数据却不会如此嵌套
|
||||
command_channel_list, command_count, max_score = cvt_mid.to_command_list_in_score(
|
||||
"计分板名称",
|
||||
1.0, # 音量比率
|
||||
1.0, # 速度倍率
|
||||
)
|
||||
|
||||
# 使用 to_command_list_in_delay 转换后的返回值只有两个
|
||||
# 但是第一个返回值没有列表套列表
|
||||
command_list, max_delay = cvt_mid.to_command_list_in_delay(
|
||||
1.0, # 音量比率
|
||||
1.0, # 速度倍率
|
||||
"@a", # 玩家选择器
|
||||
)
|
||||
|
||||
# 运行之后,指令和总延迟会存储至对象内
|
||||
print(
|
||||
"音乐长度:{}/游戏刻".format(
|
||||
cvt_mid.music_tick_num
|
||||
)
|
||||
)
|
||||
print(
|
||||
"指令如下:\n{}".format(
|
||||
cvt_mid.music_command_list
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
- 除了获取播放指令外,还可以获取进度条指令
|
||||
|
||||
```python
|
||||
# 通过函数 form_progress_bar 可以获得
|
||||
# 以计分板为载体所生成的进度条的指令对象列表
|
||||
# 数据不仅存储在对象本身内,也会以返回值的形式返回,详见代码内文档
|
||||
|
||||
# 使用 form_progress_bar 函数进行转换之后,返回值有三个
|
||||
# 值得注意的是第一个返回值返回的是依照midi频道存储的指令对象列表
|
||||
# 也就是列表套列表
|
||||
cvt_mid.form_progress_bar(
|
||||
max_score, # 音乐时长游戏刻
|
||||
scoreboard_name, # 进度条使用的计分板名称
|
||||
progressbar_style, # 进度条样式组(详见下方)
|
||||
)
|
||||
|
||||
# 同上面生成播放指令的理,进度条指令也会存储至对象内
|
||||
print(
|
||||
"进度条指令如下:\n{}".format(
|
||||
cvt_mid.progress_bar_command
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
在上面的代码中,进度条样式是可以自定义的,详见[下方说明](%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` 对象转换为相应格式。
|
||||
|
||||
```python
|
||||
# 导入 Musicreater
|
||||
import Musicreater
|
||||
# 导入附加组件功能
|
||||
import Musicreater.plugin
|
||||
|
||||
|
||||
# 导入相应的文件格式转换功能
|
||||
|
||||
# 转换为函数附加包
|
||||
import Musicreater.plugin.funpack
|
||||
# 转换为 BDX 结构文件
|
||||
import Musicreater.plugin.bdxfile
|
||||
# 转换为 mcstructure 结构文件
|
||||
import Musicreater.plugin.mcstructfile
|
||||
# 转换为结构附加包
|
||||
import Musicreater.plugin.mcstructpack
|
||||
# 直接通过 websocket 功能播放(正在开发)
|
||||
import Musicreater.plugin.websocket
|
||||
|
||||
|
||||
# 定义转换参数
|
||||
cvt_cfg = Musicreater.plugin.ConvertConfig(
|
||||
output_path,
|
||||
volumn, # 音量大小参数
|
||||
speed, # 速度倍率
|
||||
progressbar, # 进度条样式组(详见下方)
|
||||
)
|
||||
|
||||
# 使用附加组件转换,其调用的函数应为:
|
||||
# Musicreater.plugin.输出格式.播放器格式
|
||||
# 值得注意的是,并非所有输出格式都支持所有播放器格式
|
||||
# 调用的时候还请注意甄别
|
||||
# 例如,以下函数是将 MidiConvert 对象 cvt_mid
|
||||
# 以 cvt_cfg 指定的参数
|
||||
# 以延迟播放器转换为 mcstructure 文件
|
||||
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay(
|
||||
cvt_mid,
|
||||
cvt_cfg,
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
# 生成文件结构
|
||||
|
||||
## 名词解释
|
||||
|
||||
|名词|解释|备注|
|
||||
|--------|-----------|----------|
|
||||
|指令区|一个用于放置指令系统的区域,通常是常加载区。|常见于服务器指令系统、好友联机房间中|
|
||||
|指令链(链)|与链式指令方块不同,一个指令链通常指代的是一串由某种非链式指令方块作为开头,后面连着一串链式指令方块的结构。|通常的链都应用于需要“单次激活而多指令”的简单功能|
|
||||
|起始块|链最初的那个非链式指令方块。|此方块为脉冲方块或重复方块皆可|
|
||||
|指令系统(系统)|指令系统通常指的是,由一个或多个指令链以及相关红石机构相互配合、一同组成的,为达到某种特定的功能而构建的整体结构。|通常的系统都应用于需要“综合调配指令”的复杂功能。可由多个实现不同功能的模块构成,不同系统之间可以相互调用各自的模块。|
|
||||
|游戏刻(刻)|游戏的一刻是指《我的世界》的游戏进程循环运行一次所占用的时间。([详见《我的世界》中文维基](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}$ 秒左右的延迟。但是,同理于游戏刻,一秒对应的红石刻是不定的。|
|
||||
|
||||
## 播放器
|
||||
|
||||
**音·创**生成的文件可以采用多种方式播放,一类播放方式,我们称其为**播放器**,例如**延迟播放器**和**计分板播放器**等等,以后推出的新的播放器,届时也会在此处更新。
|
||||
|
||||
为什么要设计这么多播放器?是为了适应不同的播放环境需要。通常情况下,一个音乐中含有多个音符,音符与音符之间存在间隔,这里就产生了不一样的,实现音符间时间间隔的方式。而不同的应用环境下,又会产生不一样的要求。接下来将对不同的播放器进行详细介绍。
|
||||
|
||||
### 参数释义
|
||||
|
||||
|参数|说明|备注|
|
||||
|--------|-----------|----------|
|
||||
|`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://zh.minecraft.wiki/wiki/%E9%9F%B3%E7%AC%A6%E7%9B%92#%E4%B9%90%E5%99%A8),我们通过音调偏移来进行调整。|
|
||||
|
||||
### 播放器内容
|
||||
|
||||
1. 计分板播放器
|
||||
|
||||
计分板播放器是一种传统的《我的世界》音乐播放方式。通过对于计分板加分来实现播放不同的音符。一个很简单的原理,就是**用不同的计分板分值对应不同的音符**,再通过加分,来达到那个分值,即播放出来。
|
||||
|
||||
在**音·创**中,用来达到这种效果的指令是这样的:
|
||||
|
||||
```mcfunction
|
||||
execute @a[scores={ScBd=x}] ~ ~ ~ playsound InstID @s ^ ^ ^Ht Vlct Ptc
|
||||
```
|
||||
|
||||
|
||||
|
||||
后四个参数决定了这个音的性质,而前两个参数仅仅是为了决定音播放的时间。
|
||||
|
||||
2. 延迟播放器
|
||||
|
||||
延迟播放器是通过《我的世界》游戏中,指令方块的设置项“延迟刻数”来达到定位音符的效果。**将所有的音符依照其播放时距离乐曲开始时的时间(毫秒),放在一个序列内,再计算音符两两之间对应的时间差值,转换为《我的世界》内对应的游戏刻数之后填入指令方块的设置中。**
|
||||
|
||||
在**音·创**中,由于此方式播放的音乐不需要用计分板,所以播放指令是这样的:
|
||||
|
||||
```mcfunction
|
||||
execute Tg ~ ~ ~ playsound InstID @s ^ ^ ^Ht Vlct Ptc
|
||||
```
|
||||
|
||||
|
||||
其中后四个参数决定了这个音的性质。
|
||||
|
||||
由于这样的延迟数据是依赖于指令方块的设置项,所以使用这种播放器所转换出的结果仅可以存储在包含方块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$。如此往复,直至指令链堆叠完成。
|
||||
|
||||
|
||||
# 进度条自定义
|
||||
|
||||
因为我们提供了可以自动转换进度条的功能,因此在这里给出进度条自定义参数的详细解释。
|
||||
|
||||
一个进度条,明显地,有**固定部分**和**可变部分**来构成。而可变部分又包括了文字和图形两种(当然,《我的世界》里头的进度条,可变的图形也就是那个“条”了)。这一点你需要了解,因为后文中包含了很多这方面的概念需要你了解。
|
||||
|
||||
进度条的自定义功能使用一个字符串来定义自己的样式,其中包含众多**标识符**来表示可变部分。
|
||||
|
||||
标识符如下(注意大小写):
|
||||
|
||||
| 标识符 | 指定的可变量 |
|
||||
|---------|----------------|
|
||||
| `%%N` | 乐曲名(即传入的文件名)|
|
||||
| `%%s` | 当前计分板值 |
|
||||
| `%^s` | 计分板最大值 |
|
||||
| `%%t` | 当前播放时间 |
|
||||
| `%^t` | 曲目总时长 |
|
||||
| `%%%` | 当前进度比率 |
|
||||
| `_` | 用以表示进度条占位|
|
||||
|
||||
表示进度条占位的 `_` 是用来标识你的进度条的。也就是可变部分的唯一的图形部分。
|
||||
|
||||
**样式定义字符串(基础样式)**的样例如下,这也是默认进度条的基础样式:
|
||||
|
||||
```▶ %%N [ %%s/%^s %%% __________ %%t|%^t]```
|
||||
|
||||
这是单独一行的进度条,当然你也可以制作多行的,如果是一行的,输出时所使用的指令便是 `title`,而如果是多行的话,输出就会用 `titleraw` 作为进度条字幕。
|
||||
|
||||
哦对了,上面的只不过是样式定义,同时还需要定义的是可变图形的部分,也就是进度条上那个真正的“条”。
|
||||
|
||||
对于这个我们就采用了固定参数的方法,对于一个进度条,无非就是“已经播放过的”和“没播放过的”两种形态,例如,我们默认的进度“条”(**可变样式**)的定义是这样的:
|
||||
|
||||
**可变样式甲(已播放样式)**:`'§e=§r'`
|
||||
|
||||
**可变样式乙(未播放样式)**:`'§7=§r'`
|
||||
|
||||
综合起来,把这些参数传给函数需要一个参数整合,使用位于 `Musicreater/subclass.py` 下的 `ProgressBarStyle` 类进行定义:
|
||||
|
||||
我们的默认定义参数如下:
|
||||
|
||||
```python
|
||||
DEFAULT_PROGRESSBAR_STYLE = ProgressBarStyle(
|
||||
r"▶ %%N [ %%s/%^s %%% __________ %%t|%^t ]",
|
||||
r"§e=§r",
|
||||
r"§7=§r",
|
||||
)
|
||||
```
|
||||
|
||||
*为了避免生成错误,请尽量避免使用标识符作为定义样式字符串的其他部分*
|
||||
|
||||
180
docs/新手答疑指南.md
Normal file
180
docs/新手答疑指南.md
Normal file
@@ -0,0 +1,180 @@
|
||||
<h1 align="center">音·创 Musicreater</h1>
|
||||
|
||||
<h2 align="center">库版 Package Version</h2>
|
||||
|
||||
<p align="center">
|
||||
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
|
||||
</p>
|
||||
|
||||
# 新手答疑指南
|
||||
|
||||
**考虑到某些用户电脑技术不是特别先进,且对这个项目充满了好奇心,但是又了解的不是很充分,
|
||||
为此,我特别在这里写一份文档,解答各位的好奇心。放心,本文件全程中文,英文版本随后更新!**
|
||||
|
||||
## 第一部分 关于音·创的作用
|
||||
|
||||
### 1.1 音·创简介
|
||||
|
||||
音·创 Musicreater 是一款免费开源的 **《我的世界:基岩版》** 音乐制作软件
|
||||
|
||||
音·创 库版 (Musicreater Package Version) 是一款免费开源的针对 **《我的世界:基岩版》** 的midi音乐转换库
|
||||
|
||||
音·创 采用的是**带有特殊条款的Apache2.0**开源协议,详情请见[这里](https://gitee.com/EillesWan/Musicreater/blob/master/LICENSE.md)。
|
||||
|
||||
音·创
|
||||
目前已经具备较为完善的**教程**,如果你还不知道音·创有教程,请点击
|
||||
[
|
||||
这里](https://gitee.com/EillesWan/Musicreater/blob/pkgver/docs/%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.md)。
|
||||
|
||||
### 1.2 音·创到底目前有什么功能?
|
||||
* - [x] 支持导入.mid文件
|
||||
* - [x] 支持写入.mcpack文件
|
||||
* - [x] 支持写入.bdx文件
|
||||
|
||||
|
||||
* 不支持导入.mp3 .mp4 .wav等音频流文件
|
||||
|
||||
## 第二部分 关于格式的问题
|
||||
|
||||
### 2.1 关于.mid格式
|
||||
其实对于经常玩音乐电子制作的朋友们来说,mid格式并不陌生,这里我简单介绍一下。
|
||||
|
||||
> mid格式是由MIDI继承而来。MID文件并不是一段录制好的声音,而是记录声音的信息,然后再告诉声卡如何再现音乐的一组指令。
|
||||
> 这样一个MIDI文件每存1分钟的音乐只用大约5~10KB。今天,MID文件主要用于原始乐器作品、流行歌曲的业余表演、游戏音轨以及电子贺卡等。
|
||||
>
|
||||
> ——百度百科
|
||||
>
|
||||
> MIDI 音乐,保存在电脑上,就是 *.mid 类型的文件。和一般意义上的“音乐”不同,mid 类型的音乐通常并不是可直接发行供大众欣赏的音乐,
|
||||
> 它更像是一种音乐创作者使用的工程文件。它没有歌词,不含有人类语言。如果说五线谱是给人类阅读的乐谱,
|
||||
> MIDI音乐文件便是给计算机或其他电子设备使用的乐谱。对于“音乐”这个世界来说,它微不足道,却又是现代数字音乐的基石。虽不为大众所知,
|
||||
> 但在 MIDI 音乐爱好者眼中,它却是非常有意思的东西。有了 MIDI 音乐技术和一台电脑,基本就等于拥有了一个乐队,加上相关的音乐知识,
|
||||
> 就可以自己指挥这个“乐队”,创作音乐作品了!
|
||||
> 同时,MIDI 技术由于数据结构简单,又与音乐有关,也成为了部分技术极客的爱好。有人使用MIDI技术+Arduino开源硬件制作了架子鼓,
|
||||
> 有人通过分析大量MIDI乐曲结构,设计了人工智能作曲软件,还有更酷的人使用MIDI音乐制作了随节奏变幻的特斯拉线圈!
|
||||
> 在MIDI 音乐的世界里,汇聚了一批喜欢音乐又热爱技术的人。我们有理由相信,玩MIDI音乐的你,定是多才多艺的。
|
||||
>
|
||||
> ——midishow.com
|
||||
|
||||
**所以,mid文件的本质是电子乐谱,而不是记录声音波形文件的.mp3/.wav/.m4a等等文件!**
|
||||
|
||||
### 2.2 关于.mid格式的获取
|
||||
|
||||
通过刚刚的介绍,你可能已经发现,*.mid文件因为受众并不多而并没有被广泛熟知,因而你之前可能完全没见过这个文件,更何况要弄一首自己心仪的歌了。
|
||||
|
||||
这里介绍几个获取.mid格式的方法:
|
||||
|
||||
1. 最简单的方法就是成为一个作编曲的人,当然,很多人做不到这一点,但是有必要提到这一点,当你在下面的渠道中都无法获得想要的文件时,
|
||||
你应该考虑是不是需要自己创作内容,或者,请别人帮你创作内容。
|
||||
2. 当你没有这样的文件时,问问别人或者从群里下在公告的免费的也是个思路,不过这个办法通常使用次数有限。
|
||||
3. 自己找mid:现在,我将给你提供一个完整的找mid的方法,请认真学习:
|
||||
**1.** 首先你需要在www.midishow.com中注册一个账号,并不复杂,你可能只需要一个QQ号。
|
||||
**2.** 登录你的账号,主界面应该如下图所示:
|
||||
<img src="https://foruda.gitee.com/images/1672644995747340749/36c9c143_10062986.png">
|
||||
**3.** 在显眼的搜索框中,输入你想要的歌曲名称,接下来的教程,我以`夜空中最亮的星`这首歌及它的mid来演示下载流程。点击搜索框,完成这一步。
|
||||
<img src="https://foruda.gitee.com/images/1672645340943813483/44d0db69_10062986.png">
|
||||
**4.** 接下来看到搜索结果如下,你可以点进去一个听听,决定你是否要下载;也有可能你什么都没搜到,那你就得换首歌或者换个途径了。
|
||||
<img src="https://foruda.gitee.com/images/1672645574402099694/6b7e18e5_10062986.png">
|
||||
**5.** 点击播放按钮试听,网站上的效果可能一般,但是会听音乐的人应该很快就能决定你是否需要下载这个音乐。如果需要下载,你可以进行到下一步,
|
||||
如果不行,那你可以退回刚刚的页面换一个。
|
||||
<img src="https://foruda.gitee.com/images/1672645752246438160/47cead78_10062986.png">
|
||||
**6.** 翻到最下面,如图,你可以对这首歌进行打分,打分可以使积分+1,建议打5分;你可以对这首歌进行评论,一次有意义的评论可以+3分。
|
||||
下载一首歌需要-3积分,如果你先完成了这两项操作再下载,那你还会再赚1积分。所以推荐这样做!
|
||||
<img src="https://foruda.gitee.com/images/1672646433045265565/26ea29b2_10062986.png">
|
||||
**完成后会像这样:**
|
||||
<img src="https://foruda.gitee.com/images/1672646242419857480/5e8809b8_10062986.png">
|
||||
**7.** 接下来下载,往上翻网页,找到这个标识,然后单击。
|
||||
<img src="https://foruda.gitee.com/images/1672646275215822535/63b402db_10062986.png">
|
||||
**8.** 确认完毕后点击下载。
|
||||
<img src="https://foruda.gitee.com/images/1672646309787744490/f459adac_10062986.png">
|
||||
**9.** 完成下载,如图:
|
||||
<img src="https://foruda.gitee.com/images/1672646335942429563/2449ed0b_10062986.png">
|
||||
4. 使用mp3转换为mid的工具:不得不说,科技的进步带来了许多便利,让这样困难的事情也变得简单。如果上述方法仍然没有解决你的问题,
|
||||
那么你可以在下一章节中找到有关这个途径的说明。
|
||||
|
||||
### 2.3 .mp3格式转换为.mid格式
|
||||
|
||||
**注意,使用这个技术有一定难度,酌情使用!**
|
||||
|
||||
概述:mp3转mid需要把**模拟的波形频谱信号转为数字乐谱信号**,这个过程**一定有损失**。
|
||||
|
||||
请各位以后不要在答疑群中问类似于我这个转换效果为什么不好这样的问题,谢谢。
|
||||
|
||||
如果直接用**widi**的话相当于对声音进行了**傅里叶变换**,你确实可以分离出其中的音高,但是效果在不同情况下差异较大,
|
||||
尤其是有架子鼓啊啥的玩也的时候,比较**差**。
|
||||
([widi工具链接](https://pan.baidu.com/s/1j_Jui0piQ6rMOXM0GJtqTg) 提取码:b0lw )
|
||||
|
||||
**如果只有钢琴的谱子需要分离,有一个完美的选择**:[这里](https://zhuanlan.zhihu.com/p/270999354)
|
||||
|
||||
如果不然有其他的乐器也需要分离,那么可能就有点困难,像cakewalk就确实也可以吧音频变成mid,但是可能要
|
||||
**先把原MP3分离成人声音轨、贝斯音轨等再傅里叶变换。**
|
||||
|
||||
当然我也可能了解的不全,没准现在已经有传新的技术可以分离的更好了,那就太棒了!(〃'▽'〃)
|
||||
|
||||
另外,我们开发者也象征性的找了一些mp3转mid的网站,我们没使用过,不担保效果。
|
||||
[https://www.serocs.cn/transcription/](https://www.serocs.cn/transcription/)
|
||||
|
||||
同时,midishow论坛里有一些大佬也提供了解决方案,看看他们的发言也是一个不错的选择。
|
||||
|
||||
我就只给个论坛链接了,经常有人问这种问题,你可以去那里随便找到一篇帖子看看。
|
||||
[论坛](https://www.midishow.com/question)
|
||||
|
||||
### 2.4 输出格式的问题
|
||||
|
||||
在前面说到,输出主要有`.mcpack`和`.bdx`文件的输出,接下来我会围绕这两个文件进行简单的说明。
|
||||
|
||||
#### 2.4.1 .mcpack是什么
|
||||
|
||||
输出.mcpack文件其实对于一个**资深基岩版我的世界玩家**应该并不陌生。
|
||||
|
||||
.mcpack文件其实就是我的世界基岩版的附加包,里面一般分为行为包和材质包
|
||||
|
||||
我们生成的命令组属于**行为包** (下图就是一个例子)
|
||||
|
||||
<img src="https://foruda.gitee.com/images/1672647779755793187/884abaa9_10062986.png">
|
||||
|
||||
如果你是国际电脑版基岩玩家就像我一样,是可以直接打开这个文件,并将这个包导入我的世界的。
|
||||
|
||||
关于怎么使用,请看[**这里**](https://gitee.com/EillesWan/Musicreater/blob/pkgver/docs/%E7%94%9F%E6%88%90%E6%96%87%E4%BB%B6%E7%9A%84%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.md)。
|
||||
|
||||
#### 2.4.2 .bdx是什么
|
||||
|
||||
.bdx 文件其实是FastBuilder这款软件的结构文件,可以导入到我的世界。
|
||||
|
||||
由于这不属于官方文件,且我并不是很熟悉这个结构,所以请对这个格式有问题的好兄弟加一下QQ群,问问群里负责导入的大佬。
|
||||
|
||||
## 第三部分 关于其他问题
|
||||
|
||||
**由于开发者们都很忙,群友们的耐心也都有限,所以恳请大家提出有意义的问题。这里附一个关于提问方法的链接,大家可以看看。**
|
||||
|
||||
[提问的智慧](https://github.com/tvvocold/How-To-Ask-Questions-The-Smart-Way)
|
||||
|
||||
希望不要我把这个文件在聊天中甩给你,那说明我可能已经没有耐心了。
|
||||
|
||||
看完这份说明,如果你打算开始使用我们的工具的话,请移步至[这里](https://gitee.com/EillesWan/Musicreater/blob/pkgver/docs/%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.md)。
|
||||
|
||||
## 结尾
|
||||
|
||||
### 作者\<*金羿*\>(Eilles)联系方式
|
||||
|
||||
1. QQ 2647547478
|
||||
2. 电邮 EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com
|
||||
3. 微信 WYI_DoctorYI
|
||||
4. Telegram [@EillesWan](https://t.me/EillesWan)
|
||||
|
||||
### 作者\<*诸葛亮与八卦阵*\>(bgArray)联系方式
|
||||
|
||||
1. QQ 4740437765
|
||||
|
||||
**欢迎加群:[861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)**
|
||||
|
||||
**欢迎加群:[861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)**
|
||||
|
||||
**欢迎加群:[861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)**
|
||||
|
||||
(重要的事情说三遍)
|
||||
|
||||
**(有能力的同志麻烦给仓库star一下)**
|
||||
|
||||
**(有能力的同志麻烦给仓库star一下)**
|
||||
|
||||
**(有能力的同志麻烦给仓库star一下)**
|
||||
@@ -1,56 +1,32 @@
|
||||
<h1 align="center">音·创 Musicreater</h1>
|
||||
|
||||
<h2 align="center">库版 Package Version</h2>
|
||||
|
||||
<p align="center">
|
||||
<img width="128" height="128" src="https://s1.ax1x.com/2022/05/06/Ouhghj.md.png" >
|
||||
</p>
|
||||
|
||||
# 生成文件的使用
|
||||
# 生成文件的使用说明
|
||||
|
||||
*这是本库所生成文件的使用声明,不是使用本库的教程,若要查看**本库的文档**,可点击[此处](./%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)*
|
||||
*由于先前的 **读我文件**(README.md) 过于冗杂,现另辟蹊径来给大家全方位的教程。*
|
||||
|
||||
## 附加包格式
|
||||
*这是本库生成后文件的相关说明,不是使用本库的教程,若要查看**本库的演示程序**使用教程,可点击[此处](%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.md)*
|
||||
|
||||
支持的文件后缀:`.MCPACK`
|
||||
## 文件格式
|
||||
|
||||
- 计分板播放器
|
||||
1. 附加包格式(`.mcpack`)
|
||||
|
||||
1. 导入附加包
|
||||
2. 在一个循环方块中输入指令 `function index`
|
||||
3. 将需要聆听音乐的实体的播放所用计分板设置为 `1`
|
||||
4. 激活循环方块
|
||||
5. 若想要暂停播放,可以停止循环指令方块的激活状态
|
||||
6. 若想要重置某实体的播放,可以将其播放用的计分板重置
|
||||
7. 若要终止全部玩家的播放,在聊天框输入指令 `function stop`
|
||||
使用附加包格式导出音乐,则音乐会以指令函数文件(`.mcfunction`)存储于附加包内。在附加包中,函数文件的存储结构应为:
|
||||
|
||||
> 其中 步骤三 和 步骤四 的顺序可以调换。
|
||||
|
||||
- 延迟播放器
|
||||
|
||||
1. 导入附加包
|
||||
2. 在聊天框输入指令 `function index`
|
||||
3. 同时激活所生成的循环和脉冲指令方块
|
||||
4. 若要终止播放,在聊天框输入指令 `function stop` 试试看,不确保有用
|
||||
|
||||
> 需要注意的是,循环指令方块需要一直激活直到音乐结束
|
||||
|
||||
## 结构格式
|
||||
|
||||
支持的文件后缀:`.MCSTRUCTURE`、`.BDX`
|
||||
|
||||
1. 将结构导入世界
|
||||
|
||||
- 延迟播放器
|
||||
|
||||
2. 将结构生成的第一个指令方块之模式更改为**脉冲**
|
||||
3. 激活脉冲方块
|
||||
4. 若欲重置播放,可以停止对此链的激活,例如停止区块加载
|
||||
5. 此播放器不支持暂停
|
||||
|
||||
- 计分板播放器
|
||||
|
||||
2. 在所生成的第一个指令方块前,放置一个循环指令方块,其朝向应当对着所生成的第一个方块
|
||||
3. 在循环指令方块中输入使播放对象的播放用计分板加分的指令,延迟为 `0`,每次循环增加 `1` 分
|
||||
4. 激活循环方块
|
||||
5. 若想要暂停播放,可以停止循环指令方块的激活状态
|
||||
6. 若想要重置某实体的播放,可以将其播放用的计分板重置
|
||||
- `functions\`
|
||||
- `index.mcfunction`
|
||||
- `mscply\`
|
||||
- `progressShow.mcfunction`
|
||||
- `track1.mcfunction`
|
||||
- `track2.mcfunction`
|
||||
- ...
|
||||
- `trackN.mcfunction`
|
||||
|
||||
如图,其中,`index.mcfunction`文件和`mscply`文件夹存在于函数目录的根下;在`mscply`目录中,包含音乐导出的众多音轨播放文件(`trackX.mcfunction`),同时,若生成此包时选择了带有进度条的选项,则会包含`progressShow.mcfunction`文件。
|
||||
|
||||
`index.mcfunction`用于开始播放,其中包含打开各个音轨对应函数的指令,以及加分指令,这里的加分,是将**播放计分板的值大于等于`1`**的所有**玩家**的播放计分板分数增加`1`。同时,若生成此包时选择了自动重置计分板的选项,则会包含一条重置计分板的指令。
|
||||
|
||||
217
docs/转换乐器对照表.md
217
docs/转换乐器对照表.md
@@ -1,217 +0,0 @@
|
||||
<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 中规定开源_**
|
||||
|
||||
原表格请见[constant.py](../Musicreater/constants.py#176)
|
||||
|
||||
**_使用时请遵循协议规定_**
|
||||
|
||||
- 版权所有 © 2024 音·创 开发者
|
||||
- Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
* 开源相关声明请见 仓库根目录下的 License.md
|
||||
* Terms & Conditions: License.md in the root directory
|
||||
|
||||
音·创 开发交流群 861684859\
|
||||
Email TriM-Organization@hotmail.com\
|
||||
若需转载或借鉴 许可声明请查看仓库根目录下的 License.md
|
||||
|
||||
### 名词解释
|
||||
|
||||
| 名词 | 说明 |
|
||||
| ------------ | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 音符名称 | 我的世界游戏内用于播放音乐的 `playsound` 指令所规定的 `Sound ID` |
|
||||
| 音调偏移参数 | 在《我的世界》中,不同乐器的音域不同,对应的 `pitch` 值也不尽相同,该参数的解释请参考[文档说明](库的生成与功能文档.md#参数释义) |
|
||||
|
||||
# 乐音乐器
|
||||
|
||||
对照表版本:2023 0527
|
||||
|
||||
| Midi 乐器值 | 音符名称 | 音调偏移参数 |
|
||||
| ----------- | ------------------- | ------------ |
|
||||
| 0 | note.harp | 6 |
|
||||
| 1 | note.harp | 6 |
|
||||
| 2 | note.pling | 6 |
|
||||
| 3 | note.harp | 6 |
|
||||
| 4 | note.pling | 6 |
|
||||
| 5 | note.pling | 6 |
|
||||
| 6 | note.harp | 6 |
|
||||
| 7 | note.harp | 6 |
|
||||
| 8 | note.share | 7 |
|
||||
| 9 | note.harp | 6 |
|
||||
| 10 | note.didgeridoo | 8 |
|
||||
| 11 | note.harp | 6 |
|
||||
| 12 | note.xylophone | 4 |
|
||||
| 13 | note.chime | 4 |
|
||||
| 14 | note.harp | 6 |
|
||||
| 15 | note.harp | 6 |
|
||||
| 16 | note.bass | 8 |
|
||||
| 17 | note.harp | 6 |
|
||||
| 18 | note.harp | 6 |
|
||||
| 19 | note.harp | 6 |
|
||||
| 20 | note.harp | 6 |
|
||||
| 21 | note.harp | 6 |
|
||||
| 22 | note.harp | 6 |
|
||||
| 23 | note.guitar | 7 |
|
||||
| 24 | note.guitar | 7 |
|
||||
| 25 | note.guitar | 7 |
|
||||
| 26 | note.guitar | 7 |
|
||||
| 27 | note.guitar | 7 |
|
||||
| 28 | note.guitar | 7 |
|
||||
| 29 | note.guitar | 7 |
|
||||
| 30 | note.guitar | 7 |
|
||||
| 31 | note.bass | 8 |
|
||||
| 32 | note.bass | 8 |
|
||||
| 33 | note.bass | 8 |
|
||||
| 34 | note.bass | 8 |
|
||||
| 35 | note.bass | 8 |
|
||||
| 36 | note.bass | 8 |
|
||||
| 37 | note.bass | 8 |
|
||||
| 38 | note.bass | 8 |
|
||||
| 39 | note.bass | 8 |
|
||||
| 40 | note.harp | 6 |
|
||||
| 41 | note.harp | 6 |
|
||||
| 42 | note.harp | 6 |
|
||||
| 43 | note.harp | 6 |
|
||||
| 44 | note.iron_xylophone | 6 |
|
||||
| 45 | note.guitar | 7 |
|
||||
| 46 | note.harp | 6 |
|
||||
| 47 | note.harp | 6 |
|
||||
| 48 | note.guitar | 7 |
|
||||
| 49 | note.guitar | 7 |
|
||||
| 50 | note.bit | 6 |
|
||||
| 51 | note.bit | 6 |
|
||||
| 52 | note.harp | 6 |
|
||||
| 53 | note.harp | 6 |
|
||||
| 54 | note.bit | 6 |
|
||||
| 55 | note.flute | 5 |
|
||||
| 56 | note.flute | 5 |
|
||||
| 57 | note.flute | 5 |
|
||||
| 58 | note.flute | 5 |
|
||||
| 59 | note.flute | 5 |
|
||||
| 60 | note.flute | 5 |
|
||||
| 61 | note.flute | 5 |
|
||||
| 62 | note.flute | 5 |
|
||||
| 63 | note.flute | 5 |
|
||||
| 64 | note.bit | 6 |
|
||||
| 65 | note.bit | 6 |
|
||||
| 66 | note.bit | 6 |
|
||||
| 67 | note.bit | 6 |
|
||||
| 68 | note.flute | 5 |
|
||||
| 69 | note.harp | 6 |
|
||||
| 70 | note.harp | 6 |
|
||||
| 71 | note.flute | 5 |
|
||||
| 72 | note.flute | 5 |
|
||||
| 73 | note.flute | 5 |
|
||||
| 74 | note.harp | 6 |
|
||||
| 75 | note.flute | 5 |
|
||||
| 76 | note.harp | 6 |
|
||||
| 77 | note.harp | 6 |
|
||||
| 78 | note.harp | 6 |
|
||||
| 79 | note.harp | 6 |
|
||||
| 80 | note.bit | 6 |
|
||||
| 81 | note.bit | 6 |
|
||||
| 82 | note.bit | 6 |
|
||||
| 83 | note.bit | 6 |
|
||||
| 84 | note.bit | 6 |
|
||||
| 85 | note.bit | 6 |
|
||||
| 86 | note.bit | 6 |
|
||||
| 87 | note.bit | 6 |
|
||||
| 88 | note.bit | 6 |
|
||||
| 89 | note.bit | 6 |
|
||||
| 90 | note.bit | 6 |
|
||||
| 91 | note.bit | 6 |
|
||||
| 92 | note.bit | 6 |
|
||||
| 93 | note.bit | 6 |
|
||||
| 94 | note.bit | 6 |
|
||||
| 95 | note.bit | 6 |
|
||||
| 96 | note.bit | 6 |
|
||||
| 97 | note.bit | 6 |
|
||||
| 98 | note.bit | 6 |
|
||||
| 99 | note.bit | 6 |
|
||||
| 100 | note.bit | 6 |
|
||||
| 101 | note.bit | 6 |
|
||||
| 102 | note.bit | 6 |
|
||||
| 103 | note.bit | 6 |
|
||||
| 104 | note.harp | 6 |
|
||||
| 105 | note.banjo | 6 |
|
||||
| 106 | note.harp | 6 |
|
||||
| 107 | note.harp | 6 |
|
||||
| 108 | note.harp | 6 |
|
||||
| 109 | note.harp | 6 |
|
||||
| 110 | note.harp | 6 |
|
||||
| 111 | note.guitar | 7 |
|
||||
| 112 | note.harp | 6 |
|
||||
| 113 | note.bell | 4 |
|
||||
| 114 | note.harp | 6 |
|
||||
| 115 | note.cow_bell | 5 |
|
||||
| 116 | note.bd | 7 |
|
||||
| 117 | note.bass | 8 |
|
||||
| 118 | note.bit | 6 |
|
||||
| 119 | note.bd | 7 |
|
||||
| 120 | note.guitar | 7 |
|
||||
| 121 | note.harp | 6 |
|
||||
| 122 | note.harp | 6 |
|
||||
| 123 | note.harp | 6 |
|
||||
| 124 | note.harp | 6 |
|
||||
| 125 | note.hat | 7 |
|
||||
| 126 | note.bd | 7 |
|
||||
| 127 | note.snare | 7 |
|
||||
|
||||
# 打击乐器
|
||||
|
||||
| Midi 打击乐器值 | 音符名称 | 音调偏移参数 |
|
||||
| --------------- | ------------------- | ------------ |
|
||||
| 34 | note.bd | 7 |
|
||||
| 35 | note.bd | 7 |
|
||||
| 36 | note.hat | 7 |
|
||||
| 37 | note.snare | 7 |
|
||||
| 38 | note.snare | 7 |
|
||||
| 39 | note.snare | 7 |
|
||||
| 40 | note.hat | 7 |
|
||||
| 41 | note.snare | 7 |
|
||||
| 42 | note.hat | 7 |
|
||||
| 43 | note.snare | 7 |
|
||||
| 44 | note.snare | 7 |
|
||||
| 45 | note.bell | 4 |
|
||||
| 46 | note.snare | 7 |
|
||||
| 47 | note.snare | 7 |
|
||||
| 48 | note.bell | 4 |
|
||||
| 49 | note.hat | 7 |
|
||||
| 50 | note.bell | 4 |
|
||||
| 51 | note.bell | 4 |
|
||||
| 52 | note.bell | 4 |
|
||||
| 53 | note.bell | 4 |
|
||||
| 54 | note.bell | 4 |
|
||||
| 55 | note.bell | 4 |
|
||||
| 56 | note.snare | 7 |
|
||||
| 57 | note.hat | 7 |
|
||||
| 58 | note.chime | 4 |
|
||||
| 59 | note.iron_xylophone | 6 |
|
||||
| 60 | note.bd | 7 |
|
||||
| 61 | note.bd | 7 |
|
||||
| 62 | note.xylophone | 4 |
|
||||
| 63 | note.xylophone | 4 |
|
||||
| 64 | note.xylophone | 4 |
|
||||
| 65 | note.hat | 7 |
|
||||
| 66 | note.bell | 4 |
|
||||
| 67 | note.bell | 4 |
|
||||
| 68 | note.hat | 7 |
|
||||
| 69 | note.hat | 7 |
|
||||
| 70 | note.flute | 5 |
|
||||
| 71 | note.flute | 5 |
|
||||
| 72 | note.hat | 7 |
|
||||
| 73 | note.hat | 7 |
|
||||
| 74 | note.xylophone | 4 |
|
||||
| 75 | note.hat | 7 |
|
||||
| 76 | note.hat | 7 |
|
||||
| 77 | note.xylophone | 4 |
|
||||
| 78 | note.xylophone | 4 |
|
||||
| 79 | note.bell | 4 |
|
||||
| 80 | note.bell | 4 |
|
||||
210
example.py
210
example.py
@@ -1,210 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 伶伦 开发交流群 861684859
|
||||
|
||||
|
||||
"""
|
||||
音·创 (Musicreater) 演示程序
|
||||
是一款免费开源的针对《我的世界》的midi音乐转换库
|
||||
Musicreater (音·创)
|
||||
A free open source library used for convert midi file into formats that is suitable for **Minecraft**.
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 ./License.md
|
||||
Terms & Conditions: ./License.md
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import Musicreater
|
||||
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.mcstructfile import (
|
||||
to_mcstructure_file_in_delay,
|
||||
to_mcstructure_file_in_repeater,
|
||||
to_mcstructure_file_in_score,
|
||||
)
|
||||
|
||||
from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score
|
||||
|
||||
# 获取midi列表
|
||||
midi_path = input(f"请输入MIDI路径:")
|
||||
|
||||
|
||||
# 获取输出地址
|
||||
out_path = input(f"请输入输出路径:")
|
||||
|
||||
|
||||
# 选择输出格式
|
||||
fileFormat = int(
|
||||
input(f"请输入输出格式[MCSTRUCTURE(2) 或 BDX(1) 或 MCPACK(0)]:").lower()
|
||||
)
|
||||
playerFormat = int(input(f"请选择播放方式[红石(2) 或 计分板(1) 或 延迟(0)]:").lower())
|
||||
|
||||
|
||||
# 真假字符串判断
|
||||
def bool_str(sth: str):
|
||||
try:
|
||||
return bool(float(sth))
|
||||
except:
|
||||
if str(sth).lower() in ("true", "真", "是", "y", "t"):
|
||||
return True
|
||||
elif str(sth).lower() in ("false", "假", "否", "f", "n"):
|
||||
return False
|
||||
else:
|
||||
raise ValueError("非法逻辑字串")
|
||||
|
||||
|
||||
def isin(sth: str, range_list: dict):
|
||||
sth = sth.lower()
|
||||
for bool_value, res_list in range_list.items():
|
||||
if sth in res_list:
|
||||
return bool_value
|
||||
raise ValueError(
|
||||
"不在可选范围内:{}".format([j for i in range_list.values() for j in i])
|
||||
)
|
||||
|
||||
|
||||
if os.path.exists("./demo_config.json"):
|
||||
import json
|
||||
|
||||
prompts = json.load(open("./demo_config.json", "r", encoding="utf-8"))
|
||||
else:
|
||||
prompts = []
|
||||
# 提示语 检测函数 错误提示语
|
||||
for args in [
|
||||
(
|
||||
f"输入音量:",
|
||||
float,
|
||||
),
|
||||
(
|
||||
f"输入播放速度:",
|
||||
float,
|
||||
),
|
||||
(
|
||||
f"是否启用进度条:",
|
||||
bool_str,
|
||||
),
|
||||
(
|
||||
(
|
||||
f"计分板名称:",
|
||||
str,
|
||||
)
|
||||
if playerFormat == 1
|
||||
else (
|
||||
f"玩家选择器:",
|
||||
str,
|
||||
)
|
||||
),
|
||||
(
|
||||
(
|
||||
f"是否自动重置计分板:",
|
||||
bool_str,
|
||||
)
|
||||
if playerFormat == 1
|
||||
else ()
|
||||
),
|
||||
(
|
||||
(
|
||||
f"BDX作者署名:",
|
||||
str,
|
||||
)
|
||||
if fileFormat == 1
|
||||
else (
|
||||
(
|
||||
(
|
||||
"结构延展方向:",
|
||||
lambda a: isin(
|
||||
a,
|
||||
{
|
||||
"z+": ["z+", "Z+"],
|
||||
"x+": ["X+", "x+"],
|
||||
"z-": ["Z-", "z-"],
|
||||
"x-": ["x-", "X-"],
|
||||
},
|
||||
),
|
||||
)
|
||||
if (playerFormat == 2 and fileFormat == 2)
|
||||
else ()
|
||||
),
|
||||
)
|
||||
),
|
||||
(
|
||||
()
|
||||
if playerFormat == 1
|
||||
else (
|
||||
(
|
||||
"基础空白方块:",
|
||||
str,
|
||||
)
|
||||
if (playerFormat == 2 and fileFormat == 2)
|
||||
else (
|
||||
f"最大结构高度:",
|
||||
int,
|
||||
)
|
||||
)
|
||||
),
|
||||
]:
|
||||
if args:
|
||||
prompts.append(args[1](input(args[0])))
|
||||
|
||||
|
||||
print(f"正在处理 {midi_path} :")
|
||||
cvt_mid = Musicreater.MidiConvert.from_midi_file(midi_path, old_exe_format=False)
|
||||
cvt_cfg = ConvertConfig(out_path, *prompts[:3])
|
||||
|
||||
if fileFormat == 0:
|
||||
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
|
||||
elif fileFormat == 2:
|
||||
if playerFormat == 1:
|
||||
cvt_method = to_mcstructure_file_in_score
|
||||
elif playerFormat == 0:
|
||||
cvt_method = to_mcstructure_file_in_delay
|
||||
elif playerFormat == 2:
|
||||
cvt_method = to_mcstructure_file_in_repeater
|
||||
|
||||
|
||||
print(
|
||||
" 指令总长:{},最高延迟:{}".format(
|
||||
*(cvt_method(cvt_mid, cvt_cfg, *prompts[3:])) # type: ignore
|
||||
)
|
||||
if fileFormat == 0
|
||||
else (
|
||||
" 指令总长:{},最高延迟:{},结构大小{},终点坐标{}".format(
|
||||
*(
|
||||
to_BDX_file_in_score(cvt_mid, cvt_cfg, *prompts[3:])
|
||||
if playerFormat == 1
|
||||
else to_BDX_file_in_delay(cvt_mid, cvt_cfg, *prompts[3:])
|
||||
)
|
||||
)
|
||||
if fileFormat == 1
|
||||
else (" 结构大小:{},延迟总数:{},指令数量:{}".format(
|
||||
*(cvt_method(cvt_mid, cvt_cfg, *prompts[3:])) # type: ignore
|
||||
) if playerFormat == 2 else
|
||||
" 结构大小:{},延迟总数:{}".format(
|
||||
*(cvt_method(cvt_mid, cvt_cfg, *prompts[3:])) # type: ignore
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
exitSth = input("回车退出").lower()
|
||||
if exitSth == "record":
|
||||
import json
|
||||
|
||||
with open("./demo_config.json", "w", encoding="utf-8") as f:
|
||||
json.dump(prompts, f)
|
||||
elif exitSth == "delrec":
|
||||
os.remove("./demo_config.json")
|
||||
18
example_convert_bdx.py
Normal file
18
example_convert_bdx.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# THIS PROGRAM IS ONLY A TEST EXAMPLE
|
||||
|
||||
|
||||
from msctPkgver.main import *
|
||||
|
||||
convertion = midiConvert()
|
||||
convertion.convert(input('请输入midi文件路径:'), input('请输入输出路径:'))
|
||||
for i in convertion.toBDXfile(
|
||||
1,
|
||||
input('请输入作者:'),
|
||||
bool(int(input('是否开启进度条(1|0):'))),
|
||||
int(input('请输入指令结构最大生成高度:')),
|
||||
input('请输入计分板名称:'),
|
||||
float(input('请输入音量(0-1]:')),
|
||||
float(input('请输入速度倍率:')),
|
||||
bool(int(input('是否自动重置计分板(1|0):'))),
|
||||
):
|
||||
print(i)
|
||||
24
example_convert_linear_mcpack.py
Normal file
24
example_convert_linear_mcpack.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# THIS PROGRAM IS ONLY A TEST EXAMPLE
|
||||
|
||||
|
||||
from msctPkgver.main import *
|
||||
|
||||
convertion = midiConvert()
|
||||
convertion.convert(input("请输入midi文件路径:"), input("请输入输出路径:"))
|
||||
print(
|
||||
convertion.tomcpack(
|
||||
3,
|
||||
bool(int(input("是否自动重置计分板(1|0):"))),
|
||||
bool(int(input("是否开启进度条(1|0):"))),
|
||||
input("请输入计分板名称:"),
|
||||
float(input("请输入音量(0-1):")),
|
||||
float(input("请输入速度倍率:")),
|
||||
)
|
||||
)
|
||||
|
||||
# for the test
|
||||
# if __name__ == '__main__':
|
||||
# convertion = midiConvert()
|
||||
# convertion.convert(r"C:\Users\lc\Documents\MuseScore3\乐谱\乐谱\victory.mid", ".")
|
||||
# convertion.tomcpack(
|
||||
# 1, True, True, "scb", 1, 1)
|
||||
22
example_convert_mcpack.py
Normal file
22
example_convert_mcpack.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# THIS PROGRAM IS ONLY A TEST EXAMPLE
|
||||
|
||||
|
||||
from msctPkgver.main import *
|
||||
|
||||
convertion = midiConvert()
|
||||
convertion.convert(input('请输入midi文件路径:'), input('请输入输出路径:'))
|
||||
convertion.tomcpack(
|
||||
1,
|
||||
bool(int(input('是否自动重置计分板(1|0):'))),
|
||||
bool(int(input('是否开启进度条(1|0):'))),
|
||||
input('请输入计分板名称:'),
|
||||
float(input('请输入音量(0-1):')),
|
||||
float(input('请输入速度倍率:')),
|
||||
)
|
||||
|
||||
# for the test
|
||||
# if __name__ == '__main__':
|
||||
# convertion = midiConvert()
|
||||
# convertion.convert(r"C:\Users\lc\Documents\MuseScore3\乐谱\乐谱\victory.mid", ".")
|
||||
# convertion.tomcpack(
|
||||
# 1, True, True, "scb", 1, 1)
|
||||
@@ -1,13 +0,0 @@
|
||||
import Musicreater.experiment
|
||||
import Musicreater.plugin
|
||||
import Musicreater.plugin.mcstructfile
|
||||
|
||||
print(
|
||||
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay(
|
||||
Musicreater.experiment.FutureMidiConvertM4.from_midi_file(
|
||||
input("midi路径:"), old_exe_format=False
|
||||
),
|
||||
Musicreater.plugin.ConvertConfig(input("输出路径:"), volume=1),
|
||||
max_height=32,
|
||||
)
|
||||
)
|
||||
BIN
logo_done_c_Finish_C_Done_CCC_1024px.ico
Normal file
BIN
logo_done_c_Finish_C_Done_CCC_1024px.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
343
magicDemo.py
Normal file
343
magicDemo.py
Normal file
@@ -0,0 +1,343 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
# 音·创 开发交流群 861684859
|
||||
# Email EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com
|
||||
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 Lisence.md
|
||||
|
||||
|
||||
"""
|
||||
音·创 库版 MIDI转换示例程序
|
||||
Musicreater Package Version : Demo for Midi Conversion
|
||||
|
||||
Copyright 2023 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 ./Lisence.md
|
||||
Terms & Conditions: ./Lisence.md
|
||||
"""
|
||||
|
||||
languages = {
|
||||
"ZH_CN": {
|
||||
"MSCT": "音·创",
|
||||
"ChooseLang": "选择语言",
|
||||
"LangChd": "当前语言已经切换为",
|
||||
"ZH_CN": "简体中文",
|
||||
"ZH_TW": "繁体中文(台湾)",
|
||||
"EN_GB": "英语(英国)",
|
||||
"EN_US": "英语(美国)",
|
||||
":": ":",
|
||||
",": ",",
|
||||
".": "。",
|
||||
"ChooseFileFormat": "请输入输出格式[BDX(1)或MCPACK(0)]",
|
||||
"ChoosePlayer": "请选择播放方式[计分板(1) 或 延迟(0)]",
|
||||
"ChoosePath": "请输入MIDI路径或所在文件夹",
|
||||
"WhetherArgEntering": "是否为文件夹内文件的转换统一参数[是(1) 或 否(0)]",
|
||||
"EnterArgs": "请输入转换参数",
|
||||
"noteofArgs": "注:文件夹内的全部midi将统一以此参数转换",
|
||||
"ChooseSbReset": "是否自动重置计分板[是(1) 或 否(0)]",
|
||||
"WhetherCstmProgressBar": "是否自定义进度条[是(1) 或 否(0)]",
|
||||
"EnterProgressBarStyle": "请输入进度条样式",
|
||||
"EnterSbName": "请输入计分板名称",
|
||||
"EnterVolume": "请输入音量大小(0~1)",
|
||||
"EnterSpeed": "请输入速度倍率",
|
||||
"EnterAuthor": "请输入作者",
|
||||
"EnterMaxHeight": "请输入指令结构最大生成高度",
|
||||
"ErrEnter": "输入错误",
|
||||
"Re-Enter": "请重新输入",
|
||||
"Dealing": "正在处理",
|
||||
"FileNotFound": "文件(夹)不存在",
|
||||
"ChooseOutPath": "请输入结果输出路径",
|
||||
"EnterSelecter": "请输入播放者选择器",
|
||||
"Saying": "言·论",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
if sys.argv.__len__() > 0:
|
||||
currentLang = sys.argv[0]
|
||||
if not currentLang in languages.keys():
|
||||
currentLang = "ZH_CN"
|
||||
else:
|
||||
currentLang = "ZH_CN"
|
||||
|
||||
|
||||
def _(__):
|
||||
'''
|
||||
`languages`
|
||||
'''
|
||||
return languages[currentLang][__]
|
||||
|
||||
|
||||
import os
|
||||
import random
|
||||
import datetime
|
||||
|
||||
from msctPkgver.main import *
|
||||
|
||||
try:
|
||||
from rich.console import Console
|
||||
except ModuleNotFoundError as E:
|
||||
if input("您需要安装 Rich 模块才能使用这个样例\n请问是否安装?(y/n)").lower() in ('y', '1'):
|
||||
os.system("pip install Rich -i https://mirrors.aliyun.com/pypi/")
|
||||
from rich.console import Console
|
||||
else:
|
||||
raise E
|
||||
|
||||
try:
|
||||
import zhdate
|
||||
except ModuleNotFoundError as E:
|
||||
if input("您需要安装 zhdate 模块才能使用这个样例\n请问是否安装?(y/n)").lower() in ('y', '1'):
|
||||
os.system("pip install zhdate -i https://mirrors.aliyun.com/pypi/")
|
||||
import zhdate
|
||||
else:
|
||||
raise E
|
||||
|
||||
|
||||
try:
|
||||
import requests
|
||||
except ModuleNotFoundError as E:
|
||||
if input("您需要安装 requests 模块才能使用这个样例\n请问是否安装?(y/n)").lower() in ('y', '1'):
|
||||
os.system("pip install requests -i https://mirrors.aliyun.com/pypi/")
|
||||
import requests
|
||||
else:
|
||||
raise E
|
||||
|
||||
|
||||
MainConsole = Console()
|
||||
|
||||
MainConsole.print(
|
||||
"[#121110 on #F0F2F4] ",
|
||||
style="#121110 on #F0F2F4",
|
||||
justify="center",
|
||||
)
|
||||
|
||||
|
||||
# 显示大标题
|
||||
MainConsole.rule(title="[bold #AB70FF]欢迎使用音·创独立转换器", characters="=", style="#26E2FF")
|
||||
MainConsole.rule(
|
||||
title="[bold #AB70FF]Welcome to Independent Musicreater Convernter", characters="-"
|
||||
)
|
||||
|
||||
|
||||
# 显示箴言部分
|
||||
MainConsole.print(
|
||||
"[#121110 on #F0F2F4]"
|
||||
+ random.choice(
|
||||
requests.get(
|
||||
"https://gitee.com/EillesWan/Musicreater/raw/master/resources/myWords.txt"
|
||||
)
|
||||
.text.strip("\r\n")
|
||||
.split("\r\n")
|
||||
),
|
||||
style="#121110 on #F0F2F4",
|
||||
justify="center",
|
||||
)
|
||||
|
||||
|
||||
from typing import Any, Literal, Optional, TextIO
|
||||
|
||||
JustifyMethod = Literal["default", "left", "center", "right", "full"]
|
||||
OverflowMethod = Literal["fold", "crop", "ellipsis", "ignore"]
|
||||
|
||||
# 高级的打印函数
|
||||
def prt(
|
||||
*objects: Any,
|
||||
sep: str = " ",
|
||||
end: str = "\n",
|
||||
justify: Optional[JustifyMethod] = None,
|
||||
overflow: Optional[OverflowMethod] = None,
|
||||
no_wrap: Optional[bool] = None,
|
||||
emoji: Optional[bool] = None,
|
||||
markup: Optional[bool] = None,
|
||||
highlight: Optional[bool] = None,
|
||||
width: Optional[int] = None,
|
||||
height: Optional[int] = None,
|
||||
crop: bool = True,
|
||||
soft_wrap: Optional[bool] = None,
|
||||
new_line_start: bool = False,
|
||||
) -> None:
|
||||
"""打印到控制台。
|
||||
|
||||
Args:
|
||||
objects (位置性的args): 要记录到终端的对象。
|
||||
sep (str, 可选): 要在打印数据之间写入的字符串。默认为""。
|
||||
end (str, optio可选nal): 在打印数据结束时写入的字符串。默认值为"\\\\n"。
|
||||
style (Union[str, Style], 可选): 应用于输出的样式。默认为`None`。
|
||||
justify (str, 可选): 校正位置,可为"default", "left", "right", "center" 或 "full". 默认为`None`。
|
||||
overflow (str, 可选): 控制溢出:"ignore"忽略, "crop"裁剪, "fold"折叠, "ellipsis"省略号。默认为`None`。
|
||||
no_wrap (Optional[bool], 可选): 禁用文字包装。默认为`None`。
|
||||
emoji (Optional[bool], 可选): 启用表情符号代码,或使用控制台默认的`None`。默认为`None`。
|
||||
markup (Optional[bool], 可选): 启用标记,或`None`使用控制台默认值。默认为`None`。
|
||||
highlight (Optional[bool], 可选): 启用自动高亮,或`None`使用控制台默认值。默认为`None`。
|
||||
width (Optional[int], 可选): 输出的宽度,或`None`自动检测。默认为`None`。
|
||||
crop (Optional[bool], 可选): 裁剪输出到终端的宽度。默认为`True`。
|
||||
soft_wrap (bool, 可选): 启用软包装模式,禁止文字包装和裁剪,或`None``用于 控制台默认值。默认为`None`。
|
||||
new_line_start (bool, False): 如果输出包含多行,在开始时插入一个新行。默认值为`False`。
|
||||
"""
|
||||
MainConsole.print(
|
||||
*objects,
|
||||
sep=sep,
|
||||
end=end,
|
||||
style="#F0F2F4 on #121110",
|
||||
justify=justify,
|
||||
overflow=overflow,
|
||||
no_wrap=no_wrap,
|
||||
emoji=emoji,
|
||||
markup=markup,
|
||||
highlight=highlight,
|
||||
width=width,
|
||||
height=height,
|
||||
crop=crop,
|
||||
soft_wrap=soft_wrap,
|
||||
new_line_start=new_line_start,
|
||||
)
|
||||
|
||||
|
||||
prt(f"{_('LangChd')}{_(':')}{_(currentLang)}")
|
||||
|
||||
# 高级的输入函数
|
||||
def ipt(
|
||||
*objects: Any,
|
||||
sep: str = " ",
|
||||
justify: Optional[JustifyMethod] = None,
|
||||
overflow: Optional[OverflowMethod] = None,
|
||||
no_wrap: Optional[bool] = None,
|
||||
emoji: Optional[bool] = None,
|
||||
markup: Optional[bool] = None,
|
||||
highlight: Optional[bool] = None,
|
||||
width: Optional[int] = None,
|
||||
height: Optional[int] = None,
|
||||
crop: bool = True,
|
||||
soft_wrap: Optional[bool] = None,
|
||||
new_line_start: bool = False,
|
||||
password: bool = False,
|
||||
stream: Optional[TextIO] = None,
|
||||
) -> str:
|
||||
"""显示一个提示并等待用户的输入。
|
||||
|
||||
它的工作方式与Python内建的 :func:`input` 函数相同,如果Python内建的 :mod:`readline` 模块先前已经加载,则提供详细的行编辑和历史功能。
|
||||
|
||||
Args:
|
||||
objects (位置性的args): 要记录到终端的对象。
|
||||
sep (str, 可选): 要在打印数据之间写入的字符串。默认为""。
|
||||
end (str, optio可选nal): 在打印数据结束时写入的字符串。默认值为"\\\\n"。
|
||||
style (Union[str, Style], 可选): 应用于输出的样式。默认为`None`。
|
||||
justify (str, 可选): 校正位置,可为"default", "left", "right", "center" 或 "full". 默认为`None`。
|
||||
overflow (str, 可选): 控制溢出:"ignore"忽略, "crop"裁剪, "fold"折叠, "ellipsis"省略号。默认为`None`。
|
||||
no_wrap (Optional[bool], 可选): 禁用文字包装。默认为`None`。
|
||||
emoji (Optional[bool], 可选): 启用表情符号代码,或使用控制台默认的`None`。默认为`None`。
|
||||
markup (Optional[bool], 可选): 启用标记,或`None`使用控制台默认值。默认为`None`。
|
||||
highlight (Optional[bool], 可选): 启用自动高亮,或`None`使用控制台默认值。默认为`None`。
|
||||
width (Optional[int], 可选): 输出的宽度,或`None`自动检测。默认为`None`。
|
||||
crop (Optional[bool], 可选): 裁剪输出到终端的宽度。默认为`True`。
|
||||
soft_wrap (bool, 可选): 启用软包装模式,禁止文字包装和裁剪,或`None``用于 控制台默认值。默认为`None`。
|
||||
new_line_start (bool, False): 如果输出包含多行,在开始时插入一个新行。默认值为`False`。
|
||||
password (bool, 可选): 隐藏已经输入的文案,默认值为`False`。
|
||||
stream (TextIO, 可选): 可选从文件中读取(而非控制台),默认为 `None`。
|
||||
|
||||
Returns:
|
||||
str: 从stdin读取的字符串
|
||||
"""
|
||||
MainConsole.print(
|
||||
*objects,
|
||||
sep=sep,
|
||||
end="",
|
||||
style="#F0F2F4 on #121110",
|
||||
justify=justify,
|
||||
overflow=overflow,
|
||||
no_wrap=no_wrap,
|
||||
emoji=emoji,
|
||||
markup=markup,
|
||||
highlight=highlight,
|
||||
width=width,
|
||||
height=height,
|
||||
crop=crop,
|
||||
soft_wrap=soft_wrap,
|
||||
new_line_start=new_line_start,
|
||||
)
|
||||
|
||||
return MainConsole.input("", password=password, stream=stream)
|
||||
|
||||
|
||||
def formatipt(notice: str, fun, errnote: str = "", *extraArg):
|
||||
'''循环输入,以某种格式
|
||||
notice: 输入时的提示
|
||||
fun: 格式函数
|
||||
errnote: 输入不符格式时的提示
|
||||
*extraArg: 对于函数的其他参数'''
|
||||
while True:
|
||||
result = ipt(notice)
|
||||
try:
|
||||
funresult = fun(result, *extraArg)
|
||||
break
|
||||
except:
|
||||
prt(errnote)
|
||||
continue
|
||||
return result, funresult
|
||||
|
||||
|
||||
# 获取midi列表
|
||||
while True:
|
||||
midipath = ipt(f"{_('ChoosePath')}{_(':')}").lower()
|
||||
if os.path.exists(midipath):
|
||||
if os.path.isfile(midipath):
|
||||
midis = (midipath,)
|
||||
elif os.path.isdir(midipath):
|
||||
midis = tuple(
|
||||
(
|
||||
os.path.join(midipath, i)
|
||||
for i in os.listdir(midipath)
|
||||
if i.lower().endswith('.mid') or i.lower().endswith('.midi')
|
||||
)
|
||||
)
|
||||
else:
|
||||
prt(f"{_('ErrEnter')}{_(',')}{_('Re-Enter')}{_('.')}")
|
||||
continue
|
||||
else:
|
||||
prt(f"{_('FileNotFound')}{_(',')}{_('Re-Enter')}{_('.')}")
|
||||
continue
|
||||
break
|
||||
|
||||
# 获取输出地址
|
||||
outpath = formatipt(
|
||||
f"{_('ChooseOutPath')}{_(':')}",
|
||||
os.path.exists,
|
||||
f"{_('FileNotFound')}{_(',')}{_('Re-Enter')}{_('.')}",
|
||||
).lower()
|
||||
|
||||
|
||||
# 选择输出格式
|
||||
while True:
|
||||
fileFormat = ipt(f"{_('ChooseFileFormat')}{_(':')}").lower()
|
||||
if fileFormat in ('0', 'mcpack'):
|
||||
fileFormat = 0
|
||||
prt(_("EnterArgs"))
|
||||
if len(midis) > 1:
|
||||
prt(_("noteofArgs"))
|
||||
|
||||
elif fileFormat in ('1', 'bdx'):
|
||||
fileFormat = 1
|
||||
while True:
|
||||
playerFormat = ipt(f"{_('ChoosePlayer')}{_(':')}").lower()
|
||||
if playerFormat in ('0', '延迟'):
|
||||
playerFormat = 0
|
||||
elif playerFormat in ('1', '计分板'):
|
||||
playerFormat = 1
|
||||
else:
|
||||
prt(f"{_('ErrEnter')}{_(',')}{_('Re-Enter')}{_('.')}")
|
||||
continue
|
||||
break
|
||||
else:
|
||||
prt(f"{_('ErrEnter')}{_(',')}{_('Re-Enter')}{_('.')}")
|
||||
continue
|
||||
break
|
||||
|
||||
|
||||
if fileFormat == 0:
|
||||
pass
|
||||
|
||||
|
||||
MainConsole.input()
|
||||
67
magicFun.py
Normal file
67
magicFun.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
# 音·创 开发交流群 861684859
|
||||
# Email EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com
|
||||
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||
# 若需转载或借鉴 请依照 Apache 2.0 许可证进行许可
|
||||
|
||||
|
||||
"""
|
||||
音·创 库版 MIDI转换示例程序
|
||||
Musicreater Package Version : Demo for Midi Conversion
|
||||
|
||||
Copyright 2022 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.
|
||||
"""
|
||||
|
||||
from rich.console import Console
|
||||
|
||||
|
||||
MainConsole = Console()
|
||||
|
||||
import requests
|
||||
import random
|
||||
|
||||
|
||||
# 欸嘿!
|
||||
while True:
|
||||
|
||||
MainConsole.print(
|
||||
"[#121110 on #F0F2F4] ",
|
||||
style="#121110 on #F0F2F4",
|
||||
justify="center",
|
||||
)
|
||||
|
||||
|
||||
MainConsole.rule(title="[bold #AB70FF]欢迎使用音·创独立转换器", characters="=", style="#26E2FF")
|
||||
MainConsole.rule(
|
||||
title="[bold #AB70FF]Welcome to Independent Musicreater Convernter", characters="-"
|
||||
)
|
||||
|
||||
|
||||
MainConsole.print(
|
||||
"[#121110 on #F0F2F4]"
|
||||
+ random.choice(
|
||||
requests.get(
|
||||
"https://gitee.com/EillesWan/Musicreater/raw/master/resources/myWords.txt"
|
||||
)
|
||||
.text.strip("\r\n")
|
||||
.split("\r\n")
|
||||
),
|
||||
style="#121110 on #F0F2F4",
|
||||
justify="center",
|
||||
)
|
||||
|
||||
MainConsole.print()
|
||||
153
mid_analyse.py
Normal file
153
mid_analyse.py
Normal file
@@ -0,0 +1,153 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# Form generated from reading UI file 'mid_analyse.ui'
|
||||
##
|
||||
# Created by: Qt User Interface Compiler version 6.4.1
|
||||
##
|
||||
# WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
##########################################################################
|
||||
|
||||
# from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||
# QMetaObject, QObject, QPoint, QRect,
|
||||
# QSize, QTime, QUrl, Qt)
|
||||
# from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||
# QFont, QFontDatabase, QGradient, QIcon,
|
||||
# QImage, QKeySequence, QLinearGradient, QPainter,
|
||||
# QPalette, QPixmap, QRadialGradient, QTransform)
|
||||
# from PySide6.QtWidgets import (QApplication, QGroupBox, QHBoxLayout, QLabel,
|
||||
# QLineEdit, QPushButton, QSizePolicy, QWidget)
|
||||
|
||||
import sys
|
||||
from PySide6.QtWidgets import QApplication, QWidget, QGroupBox, QPushButton, \
|
||||
QLabel, QLineEdit, QHBoxLayout, QFileDialog
|
||||
from PySide6.QtCore import QRect, QMetaObject, Slot
|
||||
|
||||
|
||||
class Ui_Form(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.groupBox = None
|
||||
self.horizontalLayout = None
|
||||
self.horizontalLayoutWidget = None
|
||||
self.close_button = None
|
||||
self.fileChoseButton = None
|
||||
self.input_button = None
|
||||
self.note_count_label = None
|
||||
self.note_count_shower = None
|
||||
self.output_button = None
|
||||
|
||||
def setupUi(self, Form):
|
||||
if not Form.objectName():
|
||||
Form.setObjectName(u"Form")
|
||||
Form.resize(582, 355)
|
||||
self.groupBox = QGroupBox(Form)
|
||||
self.groupBox.setObjectName(u"groupBox")
|
||||
self.groupBox.setGeometry(QRect(10, 0, 561, 311))
|
||||
self.fileChoseButton = QPushButton(self.groupBox)
|
||||
self.fileChoseButton.setObjectName(u"file_chose_button")
|
||||
self.fileChoseButton.setGeometry(QRect(10, 20, 75, 24))
|
||||
self.note_count_label = QLabel(self.groupBox)
|
||||
self.note_count_label.setObjectName(u"note_count_label")
|
||||
self.note_count_label.setGeometry(QRect(20, 50, 54, 16))
|
||||
self.note_count_shower = QLineEdit(self.groupBox)
|
||||
self.note_count_shower.setObjectName(u"note_count_shower")
|
||||
self.note_count_shower.setGeometry(QRect(70, 50, 113, 20))
|
||||
self.horizontalLayoutWidget = QWidget(Form)
|
||||
self.horizontalLayoutWidget.setObjectName(u"horizontalLayoutWidget")
|
||||
self.horizontalLayoutWidget.setGeometry(QRect(10, 310, 561, 41))
|
||||
self.horizontalLayout = QHBoxLayout(self.horizontalLayoutWidget)
|
||||
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.input_button = QPushButton(self.horizontalLayoutWidget)
|
||||
self.input_button.setObjectName(u"input_button")
|
||||
|
||||
self.horizontalLayout.addWidget(self.input_button)
|
||||
|
||||
self.output_button = QPushButton(self.horizontalLayoutWidget)
|
||||
self.output_button.setObjectName(u"output_button")
|
||||
|
||||
self.horizontalLayout.addWidget(self.output_button)
|
||||
|
||||
self.close_button = QPushButton(self.horizontalLayoutWidget)
|
||||
self.close_button.setObjectName(u"close_button")
|
||||
|
||||
self.horizontalLayout.addWidget(self.close_button)
|
||||
|
||||
Form.setWindowTitle("Mid解析器")
|
||||
|
||||
self.groupBox.setTitle("mid信息")
|
||||
self.fileChoseButton.setText("选择文件")
|
||||
self.note_count_label.setText("音符数")
|
||||
self.input_button.setText("导入")
|
||||
self.output_button.setText("导出分析")
|
||||
self.close_button.setText("关闭")
|
||||
|
||||
QMetaObject.connectSlotsByName(Form)
|
||||
|
||||
# self.ui.btnCalculate.clicked.connect(self.fileLoading)
|
||||
|
||||
def fileLoading(self):
|
||||
filePath, _ = QFileDialog.getOpenFileName(
|
||||
self.groupBox, # 父窗口对象
|
||||
"选择文件", # 标题
|
||||
r"./", # 起始目录
|
||||
"mid类型 (*.mid *.midi)" # 选择类型过滤项,过滤内容在括号中
|
||||
)
|
||||
print(filePath)
|
||||
|
||||
@Slot()
|
||||
def on_fileChoseButton_clicked(self):
|
||||
filePath, _ = QFileDialog.getOpenFileName(
|
||||
self.groupBox, # 父窗口对象
|
||||
"选择文件", # 标题
|
||||
r"./", # 起始目录
|
||||
"mid类型 (*.mid *.midi)" # 选择类型过滤项,过滤内容在括号中
|
||||
)
|
||||
print(filePath)
|
||||
|
||||
|
||||
class MidAnalyseGui(QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.ui = Ui_Form()
|
||||
self.ui.setupUi(self)
|
||||
self.ui.fileChoseButton.clicked.connect(self.fileLoading)
|
||||
|
||||
def fileLoading(self):
|
||||
filePath, _ = QFileDialog.getOpenFileName(
|
||||
self.ui.groupBox, # 父窗口对象
|
||||
"选择文件", # 标题
|
||||
r"./", # 起始目录
|
||||
"mid类型 (*.mid *.midi)" # 选择类型过滤项,过滤内容在括号中
|
||||
)
|
||||
print(filePath)
|
||||
|
||||
@Slot()
|
||||
def on_fileChoseButton_clicked(self):
|
||||
filePath, _ = QFileDialog.getOpenFileName(
|
||||
self.ui.groupBox, # 父窗口对象
|
||||
"选择文件", # 标题
|
||||
r"./", # 起始目录
|
||||
"mid类型 (*.mid *.midi)" # 选择类型过滤项,过滤内容在括号中
|
||||
)
|
||||
print(filePath)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv) # 创建一个QApplication,也就是你要开发的软件app
|
||||
# MainWindow = QMainWindow() # 创建一个QMainWindow,用来装载你需要的各种组件、控件
|
||||
# MainWindow = QWidget() # 创建一个QMainWindow,用来装载你需要的各种组件、控件
|
||||
ui = MidAnalyseGui() # ui是你创建的ui类的实例化对象
|
||||
# ui.setupUi(MainWindow) # 执行类中的setupUi方法,方法的参数是第二步中创建的QMainWindow
|
||||
ui.show() # 执行QMainWindow的show()方法,显示这个QMainWindow
|
||||
sys.exit(app.exec()) # 使用exit()或者点击关闭按钮退出QApplication
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
MainWindow = QWidget()
|
||||
ui = Ui_Form()
|
||||
ui.setupUi(MainWindow)
|
||||
ui.show()
|
||||
sys.exit(app.exec())
|
||||
101
mid_analyse.ui
Normal file
101
mid_analyse.ui
Normal file
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>582</width>
|
||||
<height>355</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>0</y>
|
||||
<width>561</width>
|
||||
<height>311</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>mid信息</string>
|
||||
</property>
|
||||
<widget class="QPushButton" name="file_chose_button">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>20</y>
|
||||
<width>75</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>选择文件</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="note_count_label">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>50</y>
|
||||
<width>54</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>音符数</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="note_count_shower">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>70</x>
|
||||
<y>50</y>
|
||||
<width>113</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="horizontalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>310</y>
|
||||
<width>561</width>
|
||||
<height>41</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="input_button">
|
||||
<property name="text">
|
||||
<string>导入</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="output_button">
|
||||
<property name="text">
|
||||
<string>导出分析</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="close_button">
|
||||
<property name="text">
|
||||
<string>关闭</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
34
msctPkgver/__init__.py
Normal file
34
msctPkgver/__init__.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""一个简单的基于音·创的我的世界音频操作工具"""
|
||||
|
||||
# 音·创 开发交流群 861684859
|
||||
# Email EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com
|
||||
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 Lisence.md
|
||||
|
||||
|
||||
__version__ = '0.1.0'
|
||||
__all__ = []
|
||||
__author__ = (('金羿', 'Eilles Wan'), ('诸葛亮与八卦阵', 'bgArray'), ('鸣凤鸽子', 'MingFengPigeon'))
|
||||
|
||||
"""
|
||||
音·创 库版 (Musicreater Package Version)
|
||||
是一款免费开源的针对《我的世界:基岩版》的midi音乐转换库
|
||||
Musicreater pkgver (Package Version 音·创 库版)
|
||||
A free open source library used for convert midi file into formats that is suitable for **Minecraft: Bedrock Edition**.
|
||||
|
||||
Copyright 2023 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 ../Lisence.md
|
||||
Terms & Conditions: ../Lisence.md
|
||||
"""
|
||||
|
||||
|
||||
|
||||
from .main import *
|
||||
|
||||
print('此Midi转换功能由音·创开发者开发,版权归参与开发的人员共同所有。')
|
||||
print('Copyright © 2022 all the developers of Musicreater')
|
||||
print("小贴:不妨试试Mid-BDX转换网页:在线的多功能Midi转换器")
|
||||
print("https://dislink.github.io/midi2bdx/")
|
||||
|
||||
60
msctPkgver/exceptions.py
Normal file
60
msctPkgver/exceptions.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
# 音·创 开发交流群 861684859
|
||||
# Email EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com
|
||||
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 Lisence.md
|
||||
|
||||
|
||||
"""
|
||||
音·创 库版 (Musicreater Package Version)
|
||||
是一款免费开源的针对《我的世界:基岩版》的midi音乐转换库
|
||||
Musicreater pkgver (Package Version 音·创 库版)
|
||||
A free open source library used for convert midi file into formats that is suitable for **Minecraft: Bedrock Edition**.
|
||||
|
||||
Copyright 2023 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 ../Lisence.md
|
||||
Terms & Conditions: ../Lisence.md
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
class MSCTBaseException(Exception):
|
||||
"""音·创库版本的所有错误均继承于此"""
|
||||
|
||||
def __init__(self, *args):
|
||||
super().__init__(*args)
|
||||
|
||||
def 喵(self,):
|
||||
for i in self.args:
|
||||
print(i+"喵!")
|
||||
|
||||
def crash_it(self):
|
||||
raise self
|
||||
|
||||
|
||||
class CrossNoteError(MSCTBaseException):
|
||||
'''同通道下同音符交叉出现所产生的错误'''
|
||||
pass
|
||||
|
||||
|
||||
class NotDefineTempoError(MSCTBaseException):
|
||||
'''没有Tempo设定导致时间无法计算的错误'''
|
||||
pass
|
||||
|
||||
|
||||
class MidiDestroyedError(MSCTBaseException):
|
||||
'''Midi文件损坏'''
|
||||
pass
|
||||
|
||||
class ChannelOverFlowError(MSCTBaseException):
|
||||
'''一个midi中含有过多的通道(数量应≤16)'''
|
||||
pass
|
||||
|
||||
class NotDefineProgramError(MSCTBaseException):
|
||||
'''没有Program设定导致没有乐器可以选择的错误'''
|
||||
pass
|
||||
|
||||
217
msctPkgver/magicmain.py
Normal file
217
msctPkgver/magicmain.py
Normal file
@@ -0,0 +1,217 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
# 音·创 开发交流群 861684859
|
||||
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||
# 若需使用或借鉴 请依照 Apache 2.0 许可证进行许可
|
||||
|
||||
|
||||
"""
|
||||
音·创 库版 (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
|
||||
|
||||
Copyright 2022 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.
|
||||
"""
|
||||
|
||||
|
||||
def _toCmdList_m1(
|
||||
self,
|
||||
scoreboardname: str = "mscplay",
|
||||
volume: float = 1.0,
|
||||
speed: float = 1.0) -> list:
|
||||
"""
|
||||
使用Dislink Sforza的转换思路,将midi转换为我的世界命令列表
|
||||
:param scoreboardname: 我的世界的计分板名称
|
||||
:param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||
:param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||
:return: tuple(命令列表, 命令个数, 计分板最大值)
|
||||
"""
|
||||
tracks = []
|
||||
if volume > 1:
|
||||
volume = 1
|
||||
if volume <= 0:
|
||||
volume = 0.001
|
||||
|
||||
commands = 0
|
||||
maxscore = 0
|
||||
|
||||
for i, track in enumerate(self.midi.tracks):
|
||||
|
||||
ticks = 0
|
||||
instrumentID = 0
|
||||
singleTrack = []
|
||||
|
||||
for msg in track:
|
||||
ticks += msg.time
|
||||
# print(msg)
|
||||
if msg.is_meta:
|
||||
if msg.type == "set_tempo":
|
||||
tempo = msg.tempo
|
||||
else:
|
||||
if msg.type == "program_change":
|
||||
# print("TT")
|
||||
instrumentID = msg.program
|
||||
if msg.type == "note_on" and msg.velocity != 0:
|
||||
nowscore = round(
|
||||
(ticks * tempo)
|
||||
/ ((self.midi.ticks_per_beat * float(speed)) * 50000)
|
||||
)
|
||||
maxscore = max(maxscore, nowscore)
|
||||
soundID, _X = self.__Inst2soundIDwithX(instrumentID)
|
||||
singleTrack.append(
|
||||
"execute @a[scores={" +
|
||||
str(scoreboardname) +
|
||||
"=" +
|
||||
str(nowscore) +
|
||||
"}" +
|
||||
f"] ~ ~ ~ playsound {soundID} @s ~ ~{1 / volume - 1} ~ {msg.velocity * (0.7 if msg.channel == 0 else 0.9)} {2 ** ((msg.note - 60 - _X) / 12)}")
|
||||
commands += 1
|
||||
if len(singleTrack) != 0:
|
||||
tracks.append(singleTrack)
|
||||
|
||||
return [tracks, commands, maxscore]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ============================
|
||||
|
||||
|
||||
|
||||
|
||||
import mido
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class NoteMessage:
|
||||
def __init__(self, channel, pitch, velocity, startT, lastT, midi, now_bpm, change_bpm=None):
|
||||
self.channel = channel
|
||||
self.note = pitch
|
||||
self.velocity = velocity
|
||||
self.startTime = startT
|
||||
self.lastTime = lastT
|
||||
self.tempo = now_bpm # 这里要程序实现获取bpm可以参考我的程序
|
||||
|
||||
def mt2gt(mt, tpb_a, bpm_a):
|
||||
return mt / tpb_a / bpm_a * 60
|
||||
self.startTrueTime = mt2gt(self.startTime, midi.ticks_per_beat, self.tempo) # / 20
|
||||
# delete_extra_zero(round_up())
|
||||
if change_bpm is not None:
|
||||
self.lastTrueTime = mt2gt(self.lastTime, midi.ticks_per_beat, change_bpm) # / 20
|
||||
else:
|
||||
self.lastTrueTime = mt2gt(self.lastTime, midi.ticks_per_beat, self.tempo) # / 20
|
||||
# delete_extra_zero(round_up())
|
||||
print((self.startTime * self.tempo) / (midi.ticks_per_beat * 50000))
|
||||
|
||||
def __str__(self):
|
||||
return "noteMessage channel=" + str(self.channel) + " note=" + str(self.note) + " velocity=" + \
|
||||
str(self.velocity) + " startTime=" + str(self.startTime) + " lastTime=" + str(self.lastTime) + \
|
||||
" startTrueTime=" + str(self.startTrueTime) + " lastTrueTime=" + str(self.lastTrueTime)
|
||||
|
||||
|
||||
def load(mid: mido.MidiFile):
|
||||
|
||||
type_ = [False, False, False] # note_off / note_on+0 / mixed
|
||||
|
||||
is_tempo = False
|
||||
|
||||
# 预检
|
||||
for i, track in enumerate(mid.tracks):
|
||||
for msg in track:
|
||||
# print(msg)
|
||||
if msg.is_meta is not True:
|
||||
if msg.type == 'note_on' and msg.velocity == 0:
|
||||
type_[1] = True
|
||||
elif msg.type == "note_off":
|
||||
type_[0] = True
|
||||
if msg.is_meta is True and msg.type == "set_tempo":
|
||||
is_tempo = True
|
||||
|
||||
if is_tempo is not True:
|
||||
raise Exception("这个mid没有可供计算时间的tempo事件")
|
||||
|
||||
if type_[0] is True and type_[1] is True:
|
||||
type_[2] = True
|
||||
type_[1] = False
|
||||
type_[0] = False
|
||||
print(type_)
|
||||
|
||||
bpm = 0
|
||||
recent_change_bpm = 0
|
||||
is_change_bpm = False
|
||||
# 实检
|
||||
for i, track in enumerate(mid.tracks):
|
||||
noteOn = []
|
||||
trackS = []
|
||||
ticks = 0
|
||||
for msg in track:
|
||||
print(msg)
|
||||
ticks += msg.time
|
||||
print(ticks)
|
||||
if msg.is_meta is True and msg.type == "set_tempo":
|
||||
recent_change_bpm = bpm
|
||||
bpm = 60000000 / msg.tempo
|
||||
is_change_bpm = True
|
||||
|
||||
if msg.type == 'note_on' and msg.velocity != 0:
|
||||
noteOn.append([msg, msg.note, ticks])
|
||||
if type_[1] is True:
|
||||
if msg.type == 'note_on' and msg.velocity == 0:
|
||||
for u in noteOn:
|
||||
index = 0
|
||||
if u[1] == msg.note:
|
||||
lastMessage = u[0]
|
||||
lastTick = u[2]
|
||||
break
|
||||
index += 1
|
||||
print(lastTick)
|
||||
if is_change_bpm and recent_change_bpm != 0:
|
||||
trackS.append(NoteMessage(msg.channel, msg.note, lastMessage.velocity, lastTick, ticks - lastTick,
|
||||
mid, recent_change_bpm, bpm))
|
||||
is_change_bpm = False
|
||||
else:
|
||||
trackS.append(
|
||||
NoteMessage(msg.channel, msg.note, lastMessage.velocity, lastTick, ticks - lastTick,
|
||||
mid, bpm))
|
||||
# print(noteOn)
|
||||
# print(index)
|
||||
try:
|
||||
noteOn.pop(index)
|
||||
except IndexError:
|
||||
noteOn.pop(index - 1)
|
||||
print(trackS)
|
||||
for j in trackS:
|
||||
print(j)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
load(mido.MidiFile("test.mid"))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1547
msctPkgver/main.py
Normal file
1547
msctPkgver/main.py
Normal file
File diff suppressed because it is too large
Load Diff
580
pp.py
Normal file
580
pp.py
Normal file
@@ -0,0 +1,580 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# Form generated from reading UI file 'PySide6-Pkg.ui'
|
||||
##
|
||||
# Created by: Qt User Interface Compiler version 6.4.1
|
||||
##
|
||||
# WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
##########################################################################
|
||||
|
||||
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||
QMetaObject, QObject, QPoint, QRect,
|
||||
QSize, QTime, QUrl, Qt)
|
||||
from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient,
|
||||
QCursor, QFont, QFontDatabase, QGradient,
|
||||
QIcon, QImage, QKeySequence, QLinearGradient,
|
||||
QPainter, QPalette, QPixmap, QRadialGradient,
|
||||
QTransform)
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
QCheckBox,
|
||||
QComboBox,
|
||||
QHBoxLayout,
|
||||
QHeaderView,
|
||||
QLabel,
|
||||
QMainWindow,
|
||||
QMenu,
|
||||
QMenuBar,
|
||||
QSizePolicy,
|
||||
QSlider,
|
||||
QSpinBox,
|
||||
QStatusBar,
|
||||
QTabWidget,
|
||||
QTableWidget,
|
||||
QTableWidgetItem,
|
||||
QTextEdit,
|
||||
QToolButton,
|
||||
QVBoxLayout,
|
||||
QWidget)
|
||||
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, MainWindow):
|
||||
if not MainWindow.objectName():
|
||||
MainWindow.setObjectName(u"MainWindow")
|
||||
MainWindow.resize(800, 600)
|
||||
self.action = QAction(MainWindow)
|
||||
self.action.setObjectName(u"action")
|
||||
icon = QIcon()
|
||||
iconThemeName = u"document-open"
|
||||
if QIcon.hasThemeIcon(iconThemeName):
|
||||
icon = QIcon.fromTheme(iconThemeName)
|
||||
else:
|
||||
icon.addFile(u".", QSize(), QIcon.Normal, QIcon.Off)
|
||||
|
||||
self.action.setIcon(icon)
|
||||
self.action_Input = QAction(MainWindow)
|
||||
self.action_Input.setObjectName(u"action_Input")
|
||||
self.centralwidget = QWidget(MainWindow)
|
||||
self.centralwidget.setObjectName(u"centralwidget")
|
||||
self.verticalLayoutWidget = QWidget(self.centralwidget)
|
||||
self.verticalLayoutWidget.setObjectName(u"verticalLayoutWidget")
|
||||
self.verticalLayoutWidget.setGeometry(QRect(0, 0, 951, 611))
|
||||
self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget)
|
||||
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.tabO = QTabWidget(self.verticalLayoutWidget)
|
||||
self.tabO.setObjectName(u"tabO")
|
||||
self.tab = QWidget()
|
||||
self.tab.setObjectName(u"tab")
|
||||
self.verticalLayoutWidget_2 = QWidget(self.tab)
|
||||
self.verticalLayoutWidget_2.setObjectName(u"verticalLayoutWidget_2")
|
||||
self.verticalLayoutWidget_2.setGeometry(QRect(0, 0, 881, 591))
|
||||
self.verticalLayout_2 = QVBoxLayout(self.verticalLayoutWidget_2)
|
||||
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
||||
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||
self.tableWidget = QTableWidget(self.verticalLayoutWidget_2)
|
||||
self.tableWidget.setObjectName(u"tableWidget")
|
||||
|
||||
self.verticalLayout_2.addWidget(self.tableWidget)
|
||||
|
||||
self.tabO.addTab(self.tab, "")
|
||||
self.tab_2 = QWidget()
|
||||
self.tab_2.setObjectName(u"tab_2")
|
||||
self.label = QLabel(self.tab_2)
|
||||
self.label.setObjectName(u"label")
|
||||
self.label.setGeometry(QRect(10, 10, 71, 16))
|
||||
self.widget = QWidget(self.tab_2)
|
||||
self.widget.setObjectName(u"widget")
|
||||
self.widget.setGeometry(QRect(0, 30, 281, 131))
|
||||
self.verticalLayoutWidget_3 = QWidget(self.widget)
|
||||
self.verticalLayoutWidget_3.setObjectName(u"verticalLayoutWidget_3")
|
||||
self.verticalLayoutWidget_3.setGeometry(QRect(10, 0, 271, 131))
|
||||
self.verticalLayout_3 = QVBoxLayout(self.verticalLayoutWidget_3)
|
||||
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
|
||||
self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
|
||||
self.label_2 = QLabel(self.verticalLayoutWidget_3)
|
||||
self.label_2.setObjectName(u"label_2")
|
||||
|
||||
self.verticalLayout_3.addWidget(self.label_2)
|
||||
|
||||
self.label_4 = QLabel(self.verticalLayoutWidget_3)
|
||||
self.label_4.setObjectName(u"label_4")
|
||||
|
||||
self.verticalLayout_3.addWidget(self.label_4)
|
||||
|
||||
self.label_5 = QLabel(self.verticalLayoutWidget_3)
|
||||
self.label_5.setObjectName(u"label_5")
|
||||
|
||||
self.verticalLayout_3.addWidget(self.label_5)
|
||||
|
||||
self.label_7 = QLabel(self.verticalLayoutWidget_3)
|
||||
self.label_7.setObjectName(u"label_7")
|
||||
|
||||
self.verticalLayout_3.addWidget(self.label_7)
|
||||
|
||||
self.label_6 = QLabel(self.verticalLayoutWidget_3)
|
||||
self.label_6.setObjectName(u"label_6")
|
||||
|
||||
self.verticalLayout_3.addWidget(self.label_6)
|
||||
|
||||
self.label_3 = QLabel(self.verticalLayoutWidget_3)
|
||||
self.label_3.setObjectName(u"label_3")
|
||||
|
||||
self.verticalLayout_3.addWidget(self.label_3)
|
||||
|
||||
self.widget_2 = QWidget(self.tab_2)
|
||||
self.widget_2.setObjectName(u"widget_2")
|
||||
self.widget_2.setGeometry(QRect(280, 30, 281, 131))
|
||||
self.verticalLayoutWidget_4 = QWidget(self.widget_2)
|
||||
self.verticalLayoutWidget_4.setObjectName(u"verticalLayoutWidget_4")
|
||||
self.verticalLayoutWidget_4.setGeometry(QRect(10, 0, 271, 131))
|
||||
self.verticalLayout_4 = QVBoxLayout(self.verticalLayoutWidget_4)
|
||||
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
|
||||
self.verticalLayout_4.setContentsMargins(0, 0, 0, 0)
|
||||
self.label_9 = QLabel(self.verticalLayoutWidget_4)
|
||||
self.label_9.setObjectName(u"label_9")
|
||||
|
||||
self.verticalLayout_4.addWidget(self.label_9)
|
||||
|
||||
self.label_8 = QLabel(self.verticalLayoutWidget_4)
|
||||
self.label_8.setObjectName(u"label_8")
|
||||
|
||||
self.verticalLayout_4.addWidget(self.label_8)
|
||||
|
||||
self.label_10 = QLabel(self.verticalLayoutWidget_4)
|
||||
self.label_10.setObjectName(u"label_10")
|
||||
|
||||
self.verticalLayout_4.addWidget(self.label_10)
|
||||
|
||||
self.label_12 = QLabel(self.verticalLayoutWidget_4)
|
||||
self.label_12.setObjectName(u"label_12")
|
||||
|
||||
self.verticalLayout_4.addWidget(self.label_12)
|
||||
|
||||
self.label_13 = QLabel(self.verticalLayoutWidget_4)
|
||||
self.label_13.setObjectName(u"label_13")
|
||||
|
||||
self.verticalLayout_4.addWidget(self.label_13)
|
||||
|
||||
self.label_11 = QLabel(self.verticalLayoutWidget_4)
|
||||
self.label_11.setObjectName(u"label_11")
|
||||
|
||||
self.verticalLayout_4.addWidget(self.label_11)
|
||||
|
||||
self.widget_3 = QWidget(self.tab_2)
|
||||
self.widget_3.setObjectName(u"widget_3")
|
||||
self.widget_3.setGeometry(QRect(10, 160, 781, 191))
|
||||
self.verticalLayoutWidget_5 = QWidget(self.widget_3)
|
||||
self.verticalLayoutWidget_5.setObjectName(u"verticalLayoutWidget_5")
|
||||
self.verticalLayoutWidget_5.setGeometry(QRect(0, 0, 781, 191))
|
||||
self.verticalLayout_5 = QVBoxLayout(self.verticalLayoutWidget_5)
|
||||
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
|
||||
self.verticalLayout_5.setContentsMargins(0, 0, 0, 0)
|
||||
self.horizontalLayout = QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||
self.label_14 = QLabel(self.verticalLayoutWidget_5)
|
||||
self.label_14.setObjectName(u"label_14")
|
||||
|
||||
self.horizontalLayout.addWidget(self.label_14)
|
||||
|
||||
self.comboBox = QComboBox(self.verticalLayoutWidget_5)
|
||||
self.comboBox.setObjectName(u"comboBox")
|
||||
|
||||
self.horizontalLayout.addWidget(self.comboBox)
|
||||
|
||||
self.toolButton = QToolButton(self.verticalLayoutWidget_5)
|
||||
self.toolButton.setObjectName(u"toolButton")
|
||||
|
||||
self.horizontalLayout.addWidget(self.toolButton)
|
||||
|
||||
self.verticalLayout_5.addLayout(self.horizontalLayout)
|
||||
|
||||
self.horizontalLayout_5 = QHBoxLayout()
|
||||
self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
|
||||
self.horizontalLayout_4 = QHBoxLayout()
|
||||
self.horizontalLayout_4.setObjectName(u"horizontalLayout_4")
|
||||
self.horizontalLayout_2 = QHBoxLayout()
|
||||
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
|
||||
self.label_15 = QLabel(self.verticalLayoutWidget_5)
|
||||
self.label_15.setObjectName(u"label_15")
|
||||
|
||||
self.horizontalLayout_2.addWidget(self.label_15)
|
||||
|
||||
self.horizontalSlider = QSlider(self.verticalLayoutWidget_5)
|
||||
self.horizontalSlider.setObjectName(u"horizontalSlider")
|
||||
self.horizontalSlider.setOrientation(Qt.Horizontal)
|
||||
|
||||
self.horizontalLayout_2.addWidget(self.horizontalSlider)
|
||||
|
||||
self.horizontalLayout_4.addLayout(self.horizontalLayout_2)
|
||||
|
||||
self.label_17 = QLabel(self.verticalLayoutWidget_5)
|
||||
self.label_17.setObjectName(u"label_17")
|
||||
|
||||
self.horizontalLayout_4.addWidget(self.label_17)
|
||||
|
||||
self.horizontalSlider_3 = QSlider(self.verticalLayoutWidget_5)
|
||||
self.horizontalSlider_3.setObjectName(u"horizontalSlider_3")
|
||||
self.horizontalSlider_3.setOrientation(Qt.Horizontal)
|
||||
|
||||
self.horizontalLayout_4.addWidget(self.horizontalSlider_3)
|
||||
|
||||
self.horizontalLayout_5.addLayout(self.horizontalLayout_4)
|
||||
|
||||
self.horizontalLayout_9 = QHBoxLayout()
|
||||
self.horizontalLayout_9.setObjectName(u"horizontalLayout_9")
|
||||
self.label_21 = QLabel(self.verticalLayoutWidget_5)
|
||||
self.label_21.setObjectName(u"label_21")
|
||||
|
||||
self.horizontalLayout_9.addWidget(self.label_21)
|
||||
|
||||
self.spinBox = QSpinBox(self.verticalLayoutWidget_5)
|
||||
self.spinBox.setObjectName(u"spinBox")
|
||||
|
||||
self.horizontalLayout_9.addWidget(self.spinBox)
|
||||
|
||||
self.horizontalLayout_5.addLayout(self.horizontalLayout_9)
|
||||
|
||||
self.verticalLayout_5.addLayout(self.horizontalLayout_5)
|
||||
|
||||
self.horizontalLayout_16 = QHBoxLayout()
|
||||
self.horizontalLayout_16.setObjectName(u"horizontalLayout_16")
|
||||
self.horizontalLayout_13 = QHBoxLayout()
|
||||
self.horizontalLayout_13.setObjectName(u"horizontalLayout_13")
|
||||
self.label_24 = QLabel(self.verticalLayoutWidget_5)
|
||||
self.label_24.setObjectName(u"label_24")
|
||||
|
||||
self.horizontalLayout_13.addWidget(self.label_24)
|
||||
|
||||
self.comboBox_3 = QComboBox(self.verticalLayoutWidget_5)
|
||||
self.comboBox_3.setObjectName(u"comboBox_3")
|
||||
|
||||
self.horizontalLayout_13.addWidget(self.comboBox_3)
|
||||
|
||||
self.horizontalLayout_6 = QHBoxLayout()
|
||||
self.horizontalLayout_6.setObjectName(u"horizontalLayout_6")
|
||||
self.label_18 = QLabel(self.verticalLayoutWidget_5)
|
||||
self.label_18.setObjectName(u"label_18")
|
||||
|
||||
self.horizontalLayout_6.addWidget(self.label_18)
|
||||
|
||||
self.textEdit = QTextEdit(self.verticalLayoutWidget_5)
|
||||
self.textEdit.setObjectName(u"textEdit")
|
||||
self.textEdit.setMaximumSize(QSize(16777215, 22))
|
||||
|
||||
self.horizontalLayout_6.addWidget(self.textEdit)
|
||||
|
||||
self.horizontalLayout_8 = QHBoxLayout()
|
||||
self.horizontalLayout_8.setObjectName(u"horizontalLayout_8")
|
||||
self.label_20 = QLabel(self.verticalLayoutWidget_5)
|
||||
self.label_20.setObjectName(u"label_20")
|
||||
self.label_20.setMaximumSize(QSize(16777215, 22))
|
||||
|
||||
self.horizontalLayout_8.addWidget(self.label_20)
|
||||
|
||||
self.textEdit_2 = QTextEdit(self.verticalLayoutWidget_5)
|
||||
self.textEdit_2.setObjectName(u"textEdit_2")
|
||||
self.textEdit_2.setMaximumSize(QSize(16777215, 22))
|
||||
|
||||
self.horizontalLayout_8.addWidget(self.textEdit_2)
|
||||
|
||||
self.horizontalLayout_6.addLayout(self.horizontalLayout_8)
|
||||
|
||||
self.horizontalLayout_13.addLayout(self.horizontalLayout_6)
|
||||
|
||||
self.horizontalLayout_16.addLayout(self.horizontalLayout_13)
|
||||
|
||||
self.verticalLayout_5.addLayout(self.horizontalLayout_16)
|
||||
|
||||
self.horizontalLayout_7 = QHBoxLayout()
|
||||
self.horizontalLayout_7.setObjectName(u"horizontalLayout_7")
|
||||
self.label_19 = QLabel(self.verticalLayoutWidget_5)
|
||||
self.label_19.setObjectName(u"label_19")
|
||||
self.label_19.setMaximumSize(QSize(16777215, 22))
|
||||
|
||||
self.horizontalLayout_7.addWidget(self.label_19)
|
||||
|
||||
self.textEdit1 = QTextEdit(self.verticalLayoutWidget_5)
|
||||
self.textEdit1.setObjectName(u"textEdit1")
|
||||
self.textEdit1.setMaximumSize(QSize(16777215, 22))
|
||||
|
||||
self.horizontalLayout_7.addWidget(self.textEdit1)
|
||||
|
||||
self.horizontalLayout_11 = QHBoxLayout()
|
||||
self.horizontalLayout_11.setObjectName(u"horizontalLayout_11")
|
||||
self.checkBox_2 = QCheckBox(self.verticalLayoutWidget_5)
|
||||
self.checkBox_2.setObjectName(u"checkBox_2")
|
||||
|
||||
self.horizontalLayout_11.addWidget(self.checkBox_2)
|
||||
|
||||
self.checkBox = QCheckBox(self.verticalLayoutWidget_5)
|
||||
self.checkBox.setObjectName(u"checkBox")
|
||||
|
||||
self.horizontalLayout_11.addWidget(self.checkBox)
|
||||
|
||||
self.label_22 = QLabel(self.verticalLayoutWidget_5)
|
||||
self.label_22.setObjectName(u"label_22")
|
||||
|
||||
self.horizontalLayout_11.addWidget(self.label_22)
|
||||
|
||||
self.textEdit_3 = QTextEdit(self.verticalLayoutWidget_5)
|
||||
self.textEdit_3.setObjectName(u"textEdit_3")
|
||||
self.textEdit_3.setMaximumSize(QSize(16777215, 22))
|
||||
|
||||
self.horizontalLayout_11.addWidget(self.textEdit_3)
|
||||
|
||||
self.horizontalLayout_7.addLayout(self.horizontalLayout_11)
|
||||
|
||||
self.verticalLayout_5.addLayout(self.horizontalLayout_7)
|
||||
|
||||
self.horizontalLayout_18 = QHBoxLayout()
|
||||
self.horizontalLayout_18.setObjectName(u"horizontalLayout_18")
|
||||
|
||||
self.verticalLayout_5.addLayout(self.horizontalLayout_18)
|
||||
|
||||
self.horizontalLayout_12 = QHBoxLayout()
|
||||
self.horizontalLayout_12.setObjectName(u"horizontalLayout_12")
|
||||
self.label_23 = QLabel(self.verticalLayoutWidget_5)
|
||||
self.label_23.setObjectName(u"label_23")
|
||||
self.label_23.setMaximumSize(QSize(120, 16777215))
|
||||
|
||||
self.horizontalLayout_12.addWidget(self.label_23)
|
||||
|
||||
self.comboBox_2 = QComboBox(self.verticalLayoutWidget_5)
|
||||
self.comboBox_2.setObjectName(u"comboBox_2")
|
||||
self.comboBox_2.setMaximumSize(QSize(200, 16777215))
|
||||
|
||||
self.horizontalLayout_12.addWidget(self.comboBox_2)
|
||||
|
||||
self.horizontalLayout_14 = QHBoxLayout()
|
||||
self.horizontalLayout_14.setObjectName(u"horizontalLayout_14")
|
||||
self.label_25 = QLabel(self.verticalLayoutWidget_5)
|
||||
self.label_25.setObjectName(u"label_25")
|
||||
|
||||
self.horizontalLayout_14.addWidget(self.label_25)
|
||||
|
||||
self.comboBox_4 = QComboBox(self.verticalLayoutWidget_5)
|
||||
self.comboBox_4.setObjectName(u"comboBox_4")
|
||||
|
||||
self.horizontalLayout_14.addWidget(self.comboBox_4)
|
||||
|
||||
self.horizontalLayout_12.addLayout(self.horizontalLayout_14)
|
||||
|
||||
self.verticalLayout_5.addLayout(self.horizontalLayout_12)
|
||||
|
||||
self.horizontalLayout_15 = QHBoxLayout()
|
||||
self.horizontalLayout_15.setObjectName(u"horizontalLayout_15")
|
||||
self.label_26 = QLabel(self.verticalLayoutWidget_5)
|
||||
self.label_26.setObjectName(u"label_26")
|
||||
|
||||
self.horizontalLayout_15.addWidget(self.label_26)
|
||||
|
||||
self.comboBox_5 = QComboBox(self.verticalLayoutWidget_5)
|
||||
self.comboBox_5.setObjectName(u"comboBox_5")
|
||||
|
||||
self.horizontalLayout_15.addWidget(self.comboBox_5)
|
||||
|
||||
self.verticalLayout_5.addLayout(self.horizontalLayout_15)
|
||||
|
||||
self.tabO.addTab(self.tab_2, "")
|
||||
self.tab_3 = QWidget()
|
||||
self.tab_3.setObjectName(u"tab_3")
|
||||
self.tabO.addTab(self.tab_3, "")
|
||||
self.tab_4 = QWidget()
|
||||
self.tab_4.setObjectName(u"tab_4")
|
||||
self.tabO.addTab(self.tab_4, "")
|
||||
|
||||
self.verticalLayout.addWidget(self.tabO)
|
||||
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
self.menubar = QMenuBar(MainWindow)
|
||||
self.menubar.setObjectName(u"menubar")
|
||||
self.menubar.setGeometry(QRect(0, 0, 800, 26))
|
||||
self.menu = QMenu(self.menubar)
|
||||
self.menu.setObjectName(u"menu")
|
||||
MainWindow.setMenuBar(self.menubar)
|
||||
self.statusbar = QStatusBar(MainWindow)
|
||||
self.statusbar.setObjectName(u"statusbar")
|
||||
MainWindow.setStatusBar(self.statusbar)
|
||||
|
||||
self.menubar.addAction(self.menu.menuAction())
|
||||
self.menu.addAction(self.action)
|
||||
self.menu.addAction(self.action_Input)
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
|
||||
self.tabO.setCurrentIndex(1)
|
||||
|
||||
QMetaObject.connectSlotsByName(MainWindow)
|
||||
|
||||
# setupUi
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
MainWindow.setWindowTitle(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"MainWindow", None))
|
||||
self.action.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u6253\u5f00 Open",
|
||||
None))
|
||||
self.action_Input.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u5bfc\u5165 Input",
|
||||
None))
|
||||
self.tabO.setTabText(
|
||||
self.tabO.indexOf(
|
||||
self.tab), QCoreApplication.translate(
|
||||
"MainWindow", u"Tab 1", None))
|
||||
self.label.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"TextLabel", None))
|
||||
self.label_2.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"TextLabel", None))
|
||||
self.label_4.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"TextLabel", None))
|
||||
self.label_5.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"TextLabel", None))
|
||||
self.label_7.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"TextLabel", None))
|
||||
self.label_6.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"TextLabel", None))
|
||||
self.label_3.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"TextLabel", None))
|
||||
self.label_9.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"TextLabel", None))
|
||||
self.label_8.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"TextLabel", None))
|
||||
self.label_10.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"TextLabel", None))
|
||||
self.label_12.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"TextLabel", None))
|
||||
self.label_13.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"TextLabel", None))
|
||||
self.label_11.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"TextLabel", None))
|
||||
self.label_14.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u9ed8\u8ba4\u4fdd\u5b58\u8def\u5f84\uff1a",
|
||||
None))
|
||||
self.toolButton.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow", u"...", None))
|
||||
self.label_15.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u901f\u5ea6\u500d\u7387",
|
||||
None))
|
||||
self.label_17.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u97f3\u91cf\u500d\u7387",
|
||||
None))
|
||||
self.label_21.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u6700\u5927\u6307\u4ee4\u751f\u6210\u9ad8\u5ea6",
|
||||
None))
|
||||
self.label_24.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u9009\u62e9\u5668\u6a21\u5f0f",
|
||||
None))
|
||||
self.label_18.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u8ba1\u5206\u677f\u540d\u79f0",
|
||||
None))
|
||||
self.label_20.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u73a9\u5bb6\u9009\u62e9\u5668",
|
||||
None))
|
||||
self.label_19.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"bdx\u4f5c\u8005\u540d\u79f0",
|
||||
None))
|
||||
self.checkBox_2.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u662f\u5426\u542f\u7528\u8fdb\u5ea6\u6761",
|
||||
None))
|
||||
self.checkBox.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u662f\u5426\u81ea\u5b9a\u4e49\u8fdb\u5ea6\u6761",
|
||||
None))
|
||||
self.label_22.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u81ea\u5b9a\u4e49\u8fdb\u5ea6\u6761\u5185\u5bb9\uff1a",
|
||||
None))
|
||||
self.label_23.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u9009\u62e9\u547d\u4ee4\u683c\u5f0f\u7248\u672c",
|
||||
None))
|
||||
self.label_25.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u9009\u62e9\u8f6c\u6362\u7b97\u6cd5\u7248\u672c",
|
||||
None))
|
||||
self.label_26.setText(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u9009\u62e9\u5bfc\u51fa\u6a21\u5f0f",
|
||||
None))
|
||||
self.tabO.setTabText(
|
||||
self.tabO.indexOf(
|
||||
self.tab_2), QCoreApplication.translate(
|
||||
"MainWindow", u"Tab 2", None))
|
||||
self.tabO.setTabText(
|
||||
self.tabO.indexOf(
|
||||
self.tab_3), QCoreApplication.translate(
|
||||
"MainWindow", u"\u9875", None))
|
||||
self.tabO.setTabText(
|
||||
self.tabO.indexOf(
|
||||
self.tab_4), QCoreApplication.translate(
|
||||
"MainWindow", u"\u9875", None))
|
||||
self.menu.setTitle(
|
||||
QCoreApplication.translate(
|
||||
"MainWindow",
|
||||
u"\u6587\u4ef6",
|
||||
None))
|
||||
# retranslateUi
|
||||
|
||||
|
||||
# Error: PySide6-Pkg.ui: Warning: The name 'textEdit' (QTextEdit) is already in use, defaulting to 'textEdit1'.
|
||||
#
|
||||
#
|
||||
# while executing
|
||||
# 'C:\Users\lc\AppData\Roaming\Python\Python39\site-packages\PySide6\uic
|
||||
# -g python PySide6-Pkg.ui'
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
from PySide6 import QtWidgets
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv) # 创建一个QApplication,也就是你要开发的软件app
|
||||
MainWindow = QtWidgets.QMainWindow() # 创建一个QMainWindow,用来装载你需要的各种组件、控件
|
||||
# MainWindow = QtWidgets.QWidget() # 创建一个QMainWindow,用来装载你需要的各种组件、控件
|
||||
ui = Ui_MainWindow() # ui是你创建的ui类的实例化对象
|
||||
ui.setupUi(MainWindow) # 执行类中的setupUi方法,方法的参数是第二步中创建的QMainWindow
|
||||
MainWindow.show() # 执行QMainWindow的show()方法,显示这个QMainWindow
|
||||
sys.exit(app.exec_()) # 使用exit()或者点击关闭按钮退出QApplication
|
||||
47
pp_main_write.py
Normal file
47
pp_main_write.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import sys
|
||||
from PySide6.QtWidgets import QApplication, QWidget
|
||||
from PySide6.QtWidgets import QLabel, QPushButton
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QPixmap
|
||||
|
||||
|
||||
class MusicreaterGUI:
|
||||
def __init__(self):
|
||||
self.button, self.label = None, None
|
||||
|
||||
def setupUi(self, window):
|
||||
window.setWindowTitle("音·创") # 窗口标题
|
||||
window.resize(300, 150) # 重置大小
|
||||
|
||||
self.label = QLabel(window) # 在窗口上创建实例化label
|
||||
string = "welcome to musicreater"
|
||||
self.label.setText(string)
|
||||
self.label.setGeometry(80, 50, 150, 20)
|
||||
|
||||
self.button = QPushButton(window)
|
||||
self.button.setText("close")
|
||||
self.button.setGeometry(120, 100, 50, 20)
|
||||
self.button.clicked.connect(window.close)
|
||||
|
||||
|
||||
class MusicreaterWidget(QWidget, MusicreaterGUI): # 很棒的继承,请 体会
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setupUi(self)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv) # 创建应用程序实例对象
|
||||
app.setApplicationDisplayName("Musicreater-pp")
|
||||
app.setApplicationVersion("v0.0.1")
|
||||
app.setEffectEnabled(Qt.UI_AnimateCombo)
|
||||
app.setWindowIcon(QPixmap(r"logo_done_c_Finish_C_Done_CCC_1024px.ico"))
|
||||
|
||||
window_ = MusicreaterWidget() # 窗口实例化
|
||||
window_.show()
|
||||
n = app.exec()
|
||||
print(n)
|
||||
try:
|
||||
sys.exit(n)
|
||||
except SystemExit:
|
||||
print("hi, error")
|
||||
@@ -1 +0,0 @@
|
||||
mido>=1.3
|
||||
@@ -1,21 +0,0 @@
|
||||
# 注意,这里是作者署名文件,文件格式开头为单子启
|
||||
# 紧跟其后,不加空格留下常用名,常用名即常用网名
|
||||
# 而在其后是各个语言下的名字。用 井字符 开头表示
|
||||
# 注释,请注意,注释符号必须在一行之首否则无作用
|
||||
# 每进行一次分段表示一个新的开发者,换行表示一个
|
||||
# 新的语言。请全体开发者就此署名,谢谢!
|
||||
启金羿
|
||||
zh-CN 金羿
|
||||
zh-TW 金羿
|
||||
zh-ME 金羿羿喵
|
||||
zh-HK 金 羿
|
||||
en-GB Eilles Wan
|
||||
en-US EillesWan
|
||||
|
||||
启诸葛亮与八卦阵
|
||||
zh-CN 诸葛亮与八卦阵
|
||||
zh-TW 諸葛亮與八卦陣
|
||||
zh-ME 诸葛八卦喵
|
||||
zh-HK 諸葛亮與八卦陣
|
||||
en-GB Bagua Array
|
||||
en-US bgArray
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.6 MiB |
@@ -1,42 +0,0 @@
|
||||
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()
|
||||
@@ -1,36 +0,0 @@
|
||||
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()
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,14 +0,0 @@
|
||||
音乐谱面文件
|
||||
|
||||
测试片段.mid、测试片段.mscz
|
||||
|
||||
皆系 音·创 库所用之连带软件
|
||||
禁止在 音·创 库以外传播
|
||||
不允许独立发行、独立副本
|
||||
不允许未经允许的二次创作
|
||||
不允许脱离于 音·创 独立存在
|
||||
不得随任何其他非 音·创 软件连带发布
|
||||
不得用于商业用途
|
||||
若 音·创 库被用于商业用途,应当将其剔除
|
||||
|
||||
版权所有 © 2024 诸葛亮与八卦阵
|
||||
38
setup.py
38
setup.py
@@ -1,38 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import setuptools
|
||||
import Musicreater
|
||||
|
||||
with open("requirements.txt", "r", encoding="utf-8") as fh:
|
||||
dependences = fh.read().strip().split("\n")
|
||||
|
||||
with open("README_EN.md", "r", encoding="utf-8") as fh:
|
||||
long_description = fh.read().replace(
|
||||
"./docs/", "https://github.com/TriM-Organization/Musicreater/blob/master/docs/"
|
||||
)
|
||||
|
||||
setuptools.setup(
|
||||
name="Musicreater",
|
||||
version=Musicreater.__version__,
|
||||
author="Eilles Wan, bgArray",
|
||||
author_email="TriM-Organization@hotmail.com",
|
||||
description="一款免费开源的 《我的世界》 mid音乐转换库。\n"
|
||||
"A free open-source python library used to convert midi into Minecraft.",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
url="https://github.com/TriM-Organization/Musicreater",
|
||||
packages=setuptools.find_packages(),
|
||||
classifiers=[
|
||||
"Intended Audience :: Developers",
|
||||
"Natural Language :: Chinese (Simplified)",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Operating System :: OS Independent",
|
||||
"Topic :: Software Development :: Libraries",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
],
|
||||
# 需要安装的依赖
|
||||
install_requires=dependences,
|
||||
)
|
||||
1
trans_mid_analyse.bat
Normal file
1
trans_mid_analyse.bat
Normal file
@@ -0,0 +1 @@
|
||||
pyside6-uic mid_analyse.ui -o mid_analyse.py
|
||||
15
update.sh
Normal file
15
update.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
rm -rf ./MusictraterPkgver
|
||||
|
||||
git clone https://gitee.com/EillesWan/Musicreater.git -b pkgver MusictraterPkgver
|
||||
|
||||
cd ./MusictraterPkgver
|
||||
|
||||
python3 -O -m compileall -b .
|
||||
|
||||
find . -name "*.py"|xargs rm -rf
|
||||
|
||||
find . -name "__pycache__" |xargs rm -rf
|
||||
|
||||
rm -rf ./.git
|
||||
|
||||
rm ./.gitignore
|
||||
@@ -1,3 +0,0 @@
|
||||
python setup.py sdist bdist_wheel
|
||||
python -m twine upload dist/*
|
||||
python clean_update.py
|
||||
Reference in New Issue
Block a user