mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2026-01-08 12:52:11 +00:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf173a9f81 | ||
|
|
6bea46977f | ||
|
|
3b8532af61 | ||
|
|
9f35e9e16a | ||
|
|
65358b12d0 | ||
|
|
046c2af27c | ||
|
|
fa6cfb4873 | ||
|
|
3d6d0e17a2 | ||
|
|
f1bea1d57e | ||
|
|
63b59442a4 | ||
|
|
7f74b29569 | ||
|
|
885dc085ea | ||
|
|
24742a140c | ||
|
|
6f94f477f2 | ||
|
|
c056eb1fcb | ||
|
|
b796a363d8 | ||
|
|
774f78359c | ||
|
|
8df63df26c | ||
|
|
9764019f08 | ||
|
|
250857c78f | ||
|
|
7ab6b77cff | ||
|
|
321f80540c | ||
|
|
50373b05d0 | ||
|
|
b65483b32d | ||
|
|
45e9adbbd1 | ||
|
|
7a1ca86132 | ||
|
|
64048a5e31 | ||
|
|
3739138059 | ||
|
|
048b631bd6 | ||
|
|
23bf69619b | ||
|
|
b4e16353ec | ||
|
|
c14489f3a7 | ||
|
|
889f8f9641 | ||
|
|
2df8d6a270 | ||
|
|
78eabf2df6 | ||
|
|
b3ddfc7ade | ||
|
|
bd031ef547 | ||
|
|
e4304dc3d1 | ||
|
|
794ee6d080 | ||
|
|
b67554c7c9 | ||
|
|
6f391567ba | ||
|
|
932b9a34e0 | ||
|
|
f6b884e38f | ||
|
|
bf1b7b99d8 | ||
|
|
fe7c11636b | ||
|
|
382b0e1601 | ||
|
|
c17ff0e53a | ||
|
|
f1ab2373b5 | ||
|
|
77351d767b | ||
|
|
69624df711 | ||
|
|
d4925e4d75 | ||
|
|
95c0ff1b47 | ||
|
|
3831c41b9a | ||
|
|
a4565b8529 |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,14 +1,17 @@
|
||||
# sth. can't open
|
||||
/msctPkgver/secrets/*.py
|
||||
/msctPkgver/secrets/*.c
|
||||
|
||||
/fool/
|
||||
|
||||
# mystuff
|
||||
/*.zip
|
||||
/.vscode
|
||||
/*.mid
|
||||
/*.midi
|
||||
/*.mcpack
|
||||
/*.bdx
|
||||
/*.msq
|
||||
/*.fsq
|
||||
/*.json
|
||||
/*.mcstructure
|
||||
.mscbackup
|
||||
@@ -21,6 +24,9 @@ RES.txt
|
||||
/MSCT_Packer.py
|
||||
/Packer/*.MPK
|
||||
/Packer/checksum.txt
|
||||
/bgArrayLib
|
||||
/fcwslib
|
||||
test_lyric-mido.py
|
||||
|
||||
# Byte-compiled / optimized
|
||||
__pycache__/
|
||||
@@ -46,6 +52,7 @@ share/python-wheels/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
.pdm-build/
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
|
||||
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.10
|
||||
266
LICENSE.md
266
LICENSE.md
@@ -1,219 +1,123 @@
|
||||
**注意,以下条款或版权声明应当且必须是高于此项目中任何其他声明的**
|
||||
# 汉钰律许可协议,第一版
|
||||
|
||||
1. 音·创的全部开发者享有其完整版权,其开发者可以在任一时刻终止以后音·创源代码开放,若经由其开发者授予特殊权利,则授权对象可以将源代码进行特定的被特殊授权的操作
|
||||
2. 音·创或(及)其代码允许在 Apache2.0 协议的条款与说明下进行非商业使用
|
||||
3. 除部分代码特殊声明外,音·创允许对其或(及)其代码进行商业化使用,但是需要经过音·创主要开发者(诸葛亮与八卦阵、金羿)的一致授权,同时,授权对象在商业化授权的使用过程中必须依照 Apache2.0 协议的条款与说明
|
||||
4. 若存在对于音·创包含的部分代码的特殊开源声明,则此部分代码依照其特定的开源方式授权,但若此部分代码经由此部分代码的主要开发者一致特殊授权后商用,则授权对象在商用时依照此部分的开发者所准许的方式(或条款)进行商用
|
||||
5. Apache2.0 协议的英文原文副本可见下文
|
||||
**总第一版 第二次修订 · 二〇二四年七月七日编 二〇二五年四月二十六日修订**
|
||||
|
||||
> The English Translation of the TERMS AND CONDITIONS above is listed below
|
||||
>
|
||||
> This translated version is for reference only and has no legal effect.
|
||||
>
|
||||
> The version with legal effect is the Chinese version above.
|
||||
## 一、重要须知
|
||||
|
||||
**Note, The TERMS AND CONDITIONS below should and must be above all others in this project**
|
||||
1. 为保护采用本协议的作品在开源过程中,其著作权人所应有的权益,根据**中华人民共和国著作权法和相关法律法规**,制定本协议。
|
||||
|
||||
1. *Musicreater* is fully copyrighted by all its developers, the developers have the right to make *Musicreater* close sourced at any time. Operations are permitted under specific terms instructed by its developer(s).
|
||||
2. Non-commercial use of *Musicreater* and(or) its source code is permitted under Apache License 2.0.
|
||||
3. Commercial use of *Musicreater* is permitted under Apache License 2.0 with the unanimous permission of the steering developers of *Musicreater* (*bgArray*诸葛亮与八卦阵 and *Eilles*金羿).
|
||||
4. *Musicreater* is open sourced under priority given:
|
||||
1. License granted by the core developer(s) of a section after negotiation.
|
||||
2. Explicitly stated license.
|
||||
3. Apache 2.0 License.
|
||||
5. A copy of the original Apache Lisence 2.0 can be found below.
|
||||
2. 本协议履行过程中,请注意本协议中**免除或限制**民事主体**责任或权利**的条款、法律适用和争议解决条款(尤其是加有特殊标记的条款),这些条款应在中国法律所允许的范围内最大程度地适用。
|
||||
|
||||
3. 若本协议所涉及的自然人**未满 18 周岁**,该自然人应在监护人的陪同下阅读本协议及有关本协议的条款内容,并在取得其监护人同意后开始或继续应用本协议所授权的行为。
|
||||
|
||||
```text
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
4. 由于互联网服务、互联网内容的特殊性,若本协议以电子协议形式分发并签订,其依然有效。您一旦开始对本协议所授权之作品进行本协议所授权的行为,即视为您已经阅读、理解并同意并已经接受本协议的全部条款。
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
5. 本协议的订立、履行、解释及争议的解决均**适用中华人民共和国法律并排除其他一切冲突法的适用**。_本协议订立于许可证最初的颁发者的地址。若颁发者为自然人,则订立于该自然人户籍所在地;若为法人或非法人组织,则订立于其注册地_。本协议的订立各方应友好协商解决于协议所规定之行为的履行相关的争议;如协商不成,任何一方均可向合同签订地有管辖权的人民法院提起诉讼。
|
||||
|
||||
1. Definitions.
|
||||
6. 本协议的原本仅为现代汉语,书写于简体中文。若存在其他语言的翻译或其他同等语言但非简体中文文本的版本,应当无法律效力。
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
## 二、术语定义
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
1. “**许可证**”、“**协议**”(后文称“本协议”)是指根据本文档中所列举的全部术语、定义、条款、限制等文本,是本合同的简称称谓。本合同全称是 **“汉钰律许可协议,第一版”**。
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
2. “**协议颁发者**”(后文称“颁发者”)是将条款或协议应用于其拥有著作财产权的作品的民事主体,或由其指定从而拥有颁发者身份的民事主体。
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
3. “**源**”形式是指对包括但不限于 软件、硬件、文档、配置项 等种类的作品进行修改、编辑的首选形式;若不存在首选形式,则初次编辑该作品所需的形式即为源形式。
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
4. “**目标**”形式是指对源形式进行机械转换、翻译、打印、制造、加工等同类型活动后形成的结果形式,包括但不限于源代码编译后的目标软件、生成的文件、转换出的媒体、制造出的机械、打印出的实体文本、加工后的零件。
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
5. “**采用本协议的作品**”(后文称“此作品”)是指经颁发者授权而使用本协议进行授权的任何作品,该作品应在自然人可见处明确附加一个自然人可读的版权通知(可以参考文末附录中提供的示例);若在一个可分割的作品中,部分地采用本协议进行授权,则该部分应当视为一个独立的采用本协议的作品,该作品应当在自然人可见处明确附加一个自然人可读的范围限定和版权通知(同样可以参考文末附录中提供的示例)。
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
6. “**贡献**”是指对作品进行的,意在提交给此作品颁发者以让著作权人包含在其作品中的任何修订或补充,该修订或补充同样属于一种作品。依据此定义,**“提交”**一词表示经由此作品颁发者所指定的形式,将其所进行的修改发送给此作品颁发者。该形式应当包括在此作品颁发者指定的平台内发送易于编辑的修改信息、在此作品颁发者指定的电子邮箱中发送易于编辑的修改信息、在此作品颁发者指定的源码控制系统或发布跟踪系统上提交的易于编辑的修改信息,但由著作权人以明显标注或指定为“非贡献”的活动除外。颁发者自己对作品进行的修改同样视作对作品的贡献。
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
7. “**贡献者**”是指此作品颁发者接受的贡献的提交者,或包含在作品的贡献清单中的民事主体。贡献者在提交贡献并经此作品颁发者通过且该贡献已经被应用于此作品中后,该贡献者应当视为此作品的著作权人之一,但不应视为此作品非其贡献的部分的著作权人。一个作品的颁发者同样属于其贡献者。**请注意**,针对贡献者提交的贡献,该贡献者应被视为该贡献的协议颁发者,但不应视作本作品的颁发者。
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
8. “**用户**”、“**使用者**”是指行使本协议所授权之行为的民事主体。据此,贡献者亦属于用户。
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
9. “**商业性使用**”、“**商用**”是指任何以谋取利益为目的的使用,包括但不限于以贩卖、出租的形式对作品进行使用;但若将该获取利益之活动明确指示为“捐赠”,且在获利者在进行本协议所授权的活动时不以捐赠数额为标准而区别之,则此种的获取利益的“捐赠”行为不属于商业性使用。
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
## 三、权利授予
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
1. 任何由颁发者所进行的特殊声明、特别注意等此类内容,应当在法律效力上高于本协议的条款或声明;这些声明若与本协议冲突,本协议的该冲突部分无效;本协议与这些声明共同构成颁发者与用户之间的合同。
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
2. 此作品的贡献者享有其贡献的完整著作权。
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
3. 此作品的贡献者将自己的贡献的全部著作财产权,免费、公开、不可撤销、无限期、非专有地授予此作品的全部著作权人,并准许其在全世界范围内使用上述权利;若无明确的标识,贡献者允许此作品的颁发者对其贡献进行免费、公开、不可撤销、无限期、非专有、世界范围内的商业性使用。
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
4. 此作品的著作权人及贡献者授予用户**免费、公开、不可撤销、非专有、非商用**地以任意形式**复制、发行、展览、表演、放映、广播、信息网络传播、摄制、改编、翻译、汇编、二次授权**的权利,准许其在此作品颁发者所指定的区域与时间内行使上述权利;若此作品颁发者未特别指定的,则视作在全世界范围内无限期地授权;若此作品颁发者特别指定在特定情况下可以商用,则应当按照其所指定的条件进行商业性使用,商用的过程中,应当明确标识此作品的著作权人。
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
5. 一旦此作品有任意由非贡献形式而产生的更改,更改的部分将不视为此作品的一部分,除非该部分不可离开此作品单独存在;若该部分必须依赖此作品而不可与此作品分离从而单独存在,则更改后的作品不视作此作品,在这种情况下,除非此更改后的作品已获得此作品颁发者的特殊许可、或更改者即为此作品颁发者本人,否则对该作品进行的任何活动都应当遵守本协议。
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
6. 经贡献而产生的对此作品的更改,属于此作品的一部分;在此情况下,更改后的作品,依旧视作此作品。
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
7. 依据本款的第 4 条,若用户在本协议的授权下,将此作品授予他人进行任何形式的活动(即“二次授权”、“二次分发”),则应确保其使用的协议或授权内容,与本协议的条款不冲突;当存在与本协议条款的冲突时,则该冲突内容无效,被授权的第三方应依照本协议的条款进行活动;除非该用户获得了此作品颁发者的特殊许可、或该用户即为此作品颁发者本人。
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
8. 依据本款的第 5 条,若由非贡献形式而产生更改的部分是可分割而不需依赖此作品即可单独存在的,若该部分明确注明不使用本协议进行授权或明确声明了其他授权条款,则该部分不视作采用本协议;但未更改的部分仍应视作原此作品的一部分,需要采用本协议进行授权,除非此更改后的作品已获得此作品颁发者的特殊许可、或更改者即为此作品颁发者本人。
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
9. 若此作品或所提交的贡献包含其著作权人的专利,则该专利所有人即此作品的著作权人应准许此作品全体著作权人**免费、公开、不可撤销、非专有、无版权费的专利许可**,以便贡献者对作品进行本协议所授权进行的活动。
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
10. 上述专利许可的授予,仅适用于在所提交的贡献中,可由专利所有者授予的,且在对此作品进行本协议所授权的活动中,必须使用的专利。
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
11. 如果用户对任何民事主体,因其在进行本协议所授权进行的活动中侵犯该用户的专利而提起诉讼,那么根据本协议授予该用户的所有关于此作品的任何其他专利许可将在提起上述诉讼之日起终止。
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
12. 如果本作品作为用户的其他作品的不可分割的一部分进行任何民事活动,本协议依旧对本作品(即该用户的其他作品的一部分)生效;若本作品完全融入该用户的其他作品之中而不可独立存在,则该用户需要保证其作品存在与本协议冲突的条款;除非该作品已获得此作品颁发者的特殊许可、或该用户即为此作品颁发者本人。
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
## 四、使用条件
|
||||
|
||||
在对此作品进行本协议所授权的民事活动中,应当同时满足以下条款:
|
||||
|
||||
Copyright 2023 TriM-Organization 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & all the developers of Musicreater
|
||||
1. 用户必须为此作品的任何其他接收者提供本协议的副本,在不得已无法提供副本的情况下,也应明确指示其他接收者可查阅本协议的位置。
|
||||
|
||||
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
|
||||
2. 用户必须在修改后的作品中附带明显的通知,声明用户已更改文件,并注明更改位置。
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
3. 若用户二次分发此作品,可以选择向此作品的接收者提供无偿或有偿的担保维修、支持服务或其他责任、义务。但是,该用户只可以其自己的名义提供上述内容,不得以任何其他贡献者的名义。且该用户必须明确表明任何此类责任或义务是由其个人独立提供,且其同意并应当承担赔偿此作品的全体贡献者因其个人承担上述责任义务而产生的任何赔偿责任。
|
||||
|
||||
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.
|
||||
4. 用户不得删除或更改此作品中包含的任何许可声明(包括版权声明,专利声明,免责声明,或赔偿责任限制),除非该更改是对已知事实错误的修补、或其已获得此作品颁发者的特殊许可、或更改者即为此作品颁发者本人。
|
||||
|
||||
5. 若此作品将权益的声明通知作为一部分,那么由用户分发的任何版本的作品中须至少在下列三处之一包含该声明通知的自然人可读副本:
|
||||
|
||||
- 该作品的权益声明通知中
|
||||
- 在源形式的文件中(当且仅当该作品开放源代码)
|
||||
- 在惯例中作为第三方通知出现之处(当且仅当该作品会产生画面,且该画面可被自然人详细观察)
|
||||
|
||||
该通知的内容仅供信息提供,不应对许可证进行任何文字上的修改。用户可在其分发的作品中,在不构成修改本协议的前提下,在作品自身的声明通知或属性描述后或作为附录添加。
|
||||
|
||||
6. 依据本款第3条,若用户二次分发此作品时,选择向作品的接收者提供收费的担保服务,则必须明确告知该接收者本协议全部内容与此作品原出处,并确保其知悉上述内容;但若用户在二次分发此作品时,不选择提供任何服务,则该用户不允许向作品的接收者收取任何费用,除非该用户获得了此作品颁发者的特殊许可、或该用户即为此作品颁发者本人。
|
||||
|
||||
## 五、提交贡献
|
||||
|
||||
除非贡献者明确声明,在本作品中由该贡献者向颁发者的提供的提交,必须符合本协议的条款,并与本协议的条款不存在冲突;除非此贡献中与本协议冲突的附加条款已获得颁发者的特殊许可、或贡献者即为此作品颁发者本人。
|
||||
|
||||
## 六、商标相关
|
||||
|
||||
本协议并未授予用户,将颁发者的商标、专属标记或特定产品名称,用于合理的或惯例性的描述或此类声明之外其他任何位置的权利。
|
||||
|
||||
## 七、免责声明
|
||||
|
||||
1. 若非因法律要求或经过了特殊准许,此作品在根据本协议“原样”提供的基础上,**不予提供任何形式的担保、任何明示、任何暗示或类似承诺**,此类包括但不限于担保此作品毫无缺陷、担保此作品适于贩卖、担保此作品适于特定目的、担保使用此作品绝不侵权。用户将自行承担因此作品的质量或性能问题而产生的全部风险。若此作品在任何方面欠妥,将由用户(而非任何贡献者、而非任何颁发者)承担所有必要的服务、维修或除错的任何成本。本免责声明是本许可的重要组成部分。当且仅当遵守本免责声明时,本协议的其他条款中对本作品的使用授权方可生效。
|
||||
|
||||
2. 无论是因何种原因,如果不是在法律规定的特殊情况(如,确为贡献者的故意或重大过失)下或者经过了特殊准许,即使贡献者事先已知发生损害的可能,在使用本作品时,用户产生的任何直接、间接、特殊、偶然或必然造成的损失(包括但不限于商誉损失、工作延误、计算机系统故障等),**均不由任一贡献者承担**。
|
||||
|
||||
**以上是本许可协议的全部条款**
|
||||
|
||||
---
|
||||
|
||||
附录
|
||||
|
||||
**如何在自己的作品中应用 汉钰律许可协议**
|
||||
|
||||
若要在自己源形式的作品应用本协议,请在其中附加下面的通知模板,并将六角括号“〔〕”中的字段替换成自身的实际信息来替换(不包括括号本身)。这些文本必须以对应文件格式适当的注释句法包含在其中,可以是实体的纸质文档、也可以是网络公告或者计算机文件;或者脱离该源之外,另起一个新的文件,使之指向要应用本协议的那个作品。同时也建议将作品名或类别名以及目的说明之类的声明囊括在同一个可被打印的页面上作为版权通知的整体,这样更加容易的区分出第三方内容。
|
||||
|
||||
若需要在自己以目标形式存在的作品中应用本协议,同样需要附加下面的通知模板并更改六角括号中的字样。但是,这些文本可以是位于作品的标签上、位于作品的用户可见且能被自然人详细观察的画面之中、或者按照惯例中许可协议应该出现的位置;同时,这些文本的所处位置应当能够明确指示到本协议应用的那个作品。另外,建议将作品名或类别名以及目的说明之类的声明囊括在同一个可被打印的位置上作为版权通知的整体,这样更加容易的区分出第三方内容。
|
||||
|
||||
**通知模板**
|
||||
|
||||
```
|
||||
版权所有 © 〔年份〕 〔著作权人〕
|
||||
〔或者:版权所有 (C) 〔年份〕 〔著作权人〕〕
|
||||
|
||||
〔该作品〕根据 汉钰律许可协议,第一版(“本协议”)授权。
|
||||
任何人皆可从以下地址获得本协议副本:〔本协议副本所在地址〕。
|
||||
若非因法律要求或经过了特殊准许,此作品在根据本协议“原样”提供的基础上,不予提供任何形式的担保、任何明示、任何暗示或类似承诺。也就是说,用户将自行承担因此作品的质量或性能问题而产生的全部风险。
|
||||
详细的准许和限制条款请见原协议文本。
|
||||
```
|
||||
@@ -1,15 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""一个简单的我的世界音频转换库
|
||||
音·创 (Musicreater)
|
||||
是一款免费开源的针对《我的世界》的midi音乐转换库
|
||||
是一款免费开源的《我的世界》数字音频支持库。
|
||||
Musicreater(音·创)
|
||||
A free open source library used for convert midi file into formats that is suitable for **Minecraft**.
|
||||
A free open source library used for dealing with **Minecraft** digital musics.
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
音·创(“本项目”)的协议颁发者为 金羿、诸葛亮与八卦阵
|
||||
The Licensor of Musicreater("this project") is Eilles, bgArray.
|
||||
|
||||
本项目根据 第一版 汉钰律许可协议(“本协议”)授权。
|
||||
任何人皆可从以下地址获得本协议副本:https://gitee.com/EillesWan/YulvLicenses。
|
||||
若非因法律要求或经过了特殊准许,此作品在根据本协议“原样”提供的基础上,不予提供任何形式的担保、任何明示、任何暗示或类似承诺。也就是说,用户将自行承担因此作品的质量或性能问题而产生的全部风险。
|
||||
详细的准许和限制条款请见原协议文本。
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
@@ -17,29 +22,123 @@ Terms & Conditions: License.md in the root directory
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
__version__ = "1.7.3"
|
||||
__vername__ = "功能结构化"
|
||||
__version__ = "2.4.2.3"
|
||||
__vername__ = "音符附加信息升级"
|
||||
__author__ = (
|
||||
("金羿", "Eilles Wan"),
|
||||
("金羿", "Eilles"),
|
||||
("诸葛亮与八卦阵", "bgArray"),
|
||||
("鱼旧梦", "ElapsingDreams"),
|
||||
("偷吃不是Touch", "Touch"),
|
||||
("鸣凤鸽子", "MingFengPigeon"),
|
||||
)
|
||||
__all__ = [
|
||||
# 主要类
|
||||
"MusicSequence",
|
||||
"MidiConvert",
|
||||
# 附加类
|
||||
"SingleNote",
|
||||
"SingleCommand",
|
||||
# "SingleNote",
|
||||
"MineNote",
|
||||
"MineCommand",
|
||||
"SingleNoteBox",
|
||||
"ProgressBarStyle",
|
||||
# "TimeStamp", 未来功能
|
||||
# 字典键
|
||||
"MIDI_PROGRAM",
|
||||
"MIDI_VOLUME",
|
||||
"MIDI_PAN",
|
||||
# 默认值
|
||||
"MIDI_DEFAULT_PROGRAM_VALUE",
|
||||
"MIDI_DEFAULT_VOLUME_VALUE",
|
||||
"DEFAULT_PROGRESSBAR_STYLE",
|
||||
# Midi 自己的对照表
|
||||
"MIDI_PITCH_NAME_TABLE",
|
||||
"MIDI_PITCHED_NOTE_NAME_GROUP",
|
||||
"MIDI_PITCHED_NOTE_NAME_TABLE",
|
||||
"MIDI_PERCUSSION_NOTE_NAME_TABLE",
|
||||
# Minecraft 自己的对照表
|
||||
"MC_PERCUSSION_INSTRUMENT_LIST",
|
||||
"MC_PITCHED_INSTRUMENT_LIST",
|
||||
"MC_INSTRUMENT_BLOCKS_TABLE",
|
||||
"MC_EILLES_RTJE12_INSTRUMENT_REPLACE_TABLE",
|
||||
"MC_EILLES_RTBETA_INSTRUMENT_REPLACE_TABLE",
|
||||
# Midi 与 游戏 的对照表
|
||||
"MM_INSTRUMENT_RANGE_TABLE",
|
||||
"MM_INSTRUMENT_DEVIATION_TABLE",
|
||||
"MM_CLASSIC_PITCHED_INSTRUMENT_TABLE",
|
||||
"MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE",
|
||||
"MM_TOUCH_PITCHED_INSTRUMENT_TABLE",
|
||||
"MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE",
|
||||
"MM_DISLINK_PITCHED_INSTRUMENT_TABLE",
|
||||
"MM_DISLINK_PERCUSSION_INSTRUMENT_TABLE",
|
||||
"MM_NBS_PITCHED_INSTRUMENT_TABLE",
|
||||
"MM_NBS_PERCUSSION_INSTRUMENT_TABLE",
|
||||
# 操作性函数
|
||||
"velocity_2_distance_natural",
|
||||
"velocity_2_distance_straight",
|
||||
"panning_2_rotation_linear",
|
||||
"panning_2_rotation_trigonometric",
|
||||
# 工具函数
|
||||
"load_decode_musicsequence_metainfo",
|
||||
"load_decode_msq_flush_release",
|
||||
"load_decode_fsq_flush_release",
|
||||
"guess_deviation",
|
||||
"mctick2timestr",
|
||||
"midi_inst_to_mc_sound",
|
||||
]
|
||||
|
||||
from .main import *
|
||||
from .main import MusicSequence, MidiConvert
|
||||
|
||||
from .subclass import (
|
||||
MineNote,
|
||||
MineCommand,
|
||||
SingleNoteBox,
|
||||
ProgressBarStyle,
|
||||
mctick2timestr,
|
||||
DEFAULT_PROGRESSBAR_STYLE,
|
||||
)
|
||||
|
||||
from .utils import (
|
||||
# 兼容性函数
|
||||
load_decode_musicsequence_metainfo,
|
||||
load_decode_msq_flush_release,
|
||||
load_decode_fsq_flush_release,
|
||||
# 工具函数
|
||||
guess_deviation,
|
||||
midi_inst_to_mc_sound,
|
||||
# 处理用函数
|
||||
velocity_2_distance_natural,
|
||||
velocity_2_distance_straight,
|
||||
panning_2_rotation_linear,
|
||||
panning_2_rotation_trigonometric,
|
||||
)
|
||||
|
||||
from .constants import (
|
||||
# 字典键
|
||||
MIDI_PROGRAM,
|
||||
MIDI_PAN,
|
||||
MIDI_VOLUME,
|
||||
# 默认值
|
||||
MIDI_DEFAULT_PROGRAM_VALUE,
|
||||
MIDI_DEFAULT_VOLUME_VALUE,
|
||||
# MIDI 表
|
||||
MIDI_PITCH_NAME_TABLE,
|
||||
MIDI_PITCHED_NOTE_NAME_GROUP,
|
||||
MIDI_PITCHED_NOTE_NAME_TABLE,
|
||||
MIDI_PERCUSSION_NOTE_NAME_TABLE,
|
||||
# 我的世界 表
|
||||
MC_EILLES_RTBETA_INSTRUMENT_REPLACE_TABLE,
|
||||
MC_EILLES_RTJE12_INSTRUMENT_REPLACE_TABLE,
|
||||
MC_INSTRUMENT_BLOCKS_TABLE,
|
||||
MC_PERCUSSION_INSTRUMENT_LIST,
|
||||
MC_PITCHED_INSTRUMENT_LIST,
|
||||
# MIDI 到 我的世界 表
|
||||
MM_INSTRUMENT_RANGE_TABLE,
|
||||
MM_INSTRUMENT_DEVIATION_TABLE,
|
||||
MM_CLASSIC_PITCHED_INSTRUMENT_TABLE,
|
||||
MM_CLASSIC_PERCUSSION_INSTRUMENT_TABLE,
|
||||
MM_TOUCH_PITCHED_INSTRUMENT_TABLE,
|
||||
MM_TOUCH_PERCUSSION_INSTRUMENT_TABLE,
|
||||
MM_DISLINK_PITCHED_INSTRUMENT_TABLE,
|
||||
MM_DISLINK_PERCUSSION_INSTRUMENT_TABLE,
|
||||
MM_NBS_PITCHED_INSTRUMENT_TABLE,
|
||||
MM_NBS_PERCUSSION_INSTRUMENT_TABLE,
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
421
Musicreater/data.py
Normal file
421
Musicreater/data.py
Normal file
@@ -0,0 +1,421 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
存储音·创新数据存储类
|
||||
"""
|
||||
|
||||
# WARNING 本文件中使用之功能尚未启用
|
||||
|
||||
"""
|
||||
版权所有 © 2025 金羿
|
||||
Copyright © 2025 Eilles
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
from math import sin, cos, asin, radians, degrees, sqrt, atan, inf
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, Any, List, Tuple, Union, Dict, Sequence, Callable
|
||||
import bisect
|
||||
|
||||
from .types import FittingFunctionType
|
||||
from .constants import MC_PITCHED_INSTRUMENT_LIST
|
||||
|
||||
|
||||
class ArgumentCurve:
|
||||
|
||||
base_line: float = 0
|
||||
"""基线/默认值"""
|
||||
|
||||
default_curve: Callable[[float], float]
|
||||
"""默认曲线"""
|
||||
|
||||
defined_curves: Dict[float, "ArgumentCurve"] = {}
|
||||
"""调整后的曲线集合"""
|
||||
|
||||
left_border: float = 0
|
||||
"""定义域左边界"""
|
||||
|
||||
right_border: float = inf
|
||||
"""定义域右边界"""
|
||||
|
||||
def __init__(self, baseline: float = 0, default_function: Callable[[float], float] = lambda x: 0, function_set: Dict = {}) -> None:
|
||||
pass
|
||||
|
||||
def __call__(self, *args: Any, **kwds: Any) -> Any:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class SoundAtmos:
|
||||
|
||||
sound_distance: float
|
||||
"""声源距离 方块"""
|
||||
|
||||
sound_azimuth: Tuple[float, float]
|
||||
"""声源方位 角度"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
distance: Optional[float] = None,
|
||||
azimuth: Optional[Tuple[float, float]] = None,
|
||||
) -> None:
|
||||
|
||||
self.sound_azimuth = (azimuth[0] % 360, azimuth[1] % 360) if azimuth else (0, 0)
|
||||
"""声源方位"""
|
||||
|
||||
# 如果指定为零,那么为零,但如果不指定或者指定为负数,则为 0.01 的距离
|
||||
self.sound_distance = (
|
||||
(16 if distance > 16 else (distance if distance >= 0 else 0.01))
|
||||
if distance is not None
|
||||
else 0.01
|
||||
)
|
||||
"""声源距离"""
|
||||
|
||||
@classmethod
|
||||
def from_displacement(
|
||||
cls,
|
||||
displacement: Optional[Tuple[float, float, float]] = None,
|
||||
) -> "SoundAtmos":
|
||||
|
||||
if displacement is None:
|
||||
# displacement = (0, 0, 0)
|
||||
return cls()
|
||||
else:
|
||||
r = sqrt(displacement[0] ** 2 + displacement[1] ** 2 + displacement[2] ** 2)
|
||||
if r == 0:
|
||||
return cls(distance=0, azimuth=(0, 0))
|
||||
else:
|
||||
beta_h = round(degrees(asin(displacement[1] / r)), 8)
|
||||
if displacement[2] == 0:
|
||||
alpha_v = -90 if displacement[0] > 0 else 90
|
||||
else:
|
||||
alpha_v = round(
|
||||
degrees(atan(-displacement[0] / displacement[2])), 8
|
||||
)
|
||||
return cls(distance=r, azimuth=(alpha_v, beta_h))
|
||||
|
||||
@property
|
||||
def position_displacement(self) -> Tuple[float, float, float]:
|
||||
"""声像位移"""
|
||||
dk1 = self.sound_distance * round(cos(radians(self.sound_azimuth[1])), 8)
|
||||
return (
|
||||
-dk1 * round(sin(radians(self.sound_azimuth[0])), 8),
|
||||
self.sound_distance * round(sin(radians(self.sound_azimuth[1])), 8),
|
||||
dk1 * round(cos(radians(self.sound_azimuth[0])), 8),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class SingleNote:
|
||||
"""存储单个音符的类"""
|
||||
|
||||
note_pitch: int
|
||||
"""midi音高"""
|
||||
|
||||
velocity: int
|
||||
"""力度"""
|
||||
|
||||
start_tick: int
|
||||
"""开始之时 命令刻"""
|
||||
|
||||
duration: int
|
||||
"""音符持续时间 命令刻"""
|
||||
|
||||
high_precision_time: int
|
||||
"""高精度开始时间偏量 1/1250 秒"""
|
||||
|
||||
extra_info: Dict[str, Any]
|
||||
"""你觉得放什么好?"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
midi_pitch: Optional[int],
|
||||
midi_velocity: int,
|
||||
start_time: int,
|
||||
last_time: int,
|
||||
mass_precision_time: int = 0,
|
||||
extra_information: Dict[str, Any] = {},
|
||||
):
|
||||
"""
|
||||
用于存储单个音符的类
|
||||
|
||||
Parameters
|
||||
------------
|
||||
midi_pitch: int
|
||||
midi音高
|
||||
midi_velocity: int
|
||||
midi响度(力度)
|
||||
start_time: int
|
||||
开始之时(命令刻)
|
||||
注:此处的时间是用从乐曲开始到当前的刻数
|
||||
last_time: int
|
||||
音符延续时间(命令刻)
|
||||
mass_precision_time: int
|
||||
高精度的开始时间偏移量(1/1250秒)
|
||||
is_percussion: bool
|
||||
是否作为打击乐器
|
||||
distance: float
|
||||
发声源距离玩家的距离(半径 `r`)
|
||||
注:距离越近,音量越高,默认为 0。此参数可以与音量成某种函数关系。
|
||||
azimuth: tuple[float, float]
|
||||
声源方位
|
||||
注:此参数为tuple,包含两个元素,分别表示:
|
||||
`rV` 发声源在竖直(上下)轴上,从玩家视角正前方开始,向顺时针旋转的角度
|
||||
`rH` 发声源在水平(左右)轴上,从玩家视角正前方开始,向上(到达玩家正上方顶点后变为向下,以此类推的旋转)旋转的角度
|
||||
extra_information: Dict[str, Any]
|
||||
附加信息,尽量存储为字典
|
||||
|
||||
Returns
|
||||
---------
|
||||
MineNote 类
|
||||
"""
|
||||
|
||||
self.note_pitch: int = 66 if midi_pitch is None else midi_pitch
|
||||
"""midi音高"""
|
||||
self.velocity: int = midi_velocity
|
||||
"""响度(力度)"""
|
||||
self.start_tick: int = start_time
|
||||
"""开始之时 命令刻"""
|
||||
self.duration: int = last_time
|
||||
"""音符持续时间 命令刻"""
|
||||
self.high_precision_time: int = mass_precision_time
|
||||
"""高精度开始时间偏量 0.4 毫秒"""
|
||||
|
||||
self.extra_info = extra_information if extra_information else {}
|
||||
|
||||
@classmethod
|
||||
def decode(cls, code_buffer: bytes, is_high_time_precision: bool = True):
|
||||
"""自字节码析出 SingleNote 类"""
|
||||
duration_ = (
|
||||
group_1 := int.from_bytes(code_buffer[:6], "big")
|
||||
) & 0b11111111111111111
|
||||
start_tick_ = (group_1 := group_1 >> 17) & 0b11111111111111111
|
||||
note_velocity_ = (group_1 := group_1 >> 17) & 0b1111111
|
||||
note_pitch_ = (group_1 := group_1 >> 7) & 0b1111111
|
||||
|
||||
try:
|
||||
return cls(
|
||||
midi_pitch=note_pitch_,
|
||||
midi_velocity=note_velocity_,
|
||||
start_time=start_tick_,
|
||||
last_time=duration_,
|
||||
mass_precision_time=code_buffer[6] if is_high_time_precision else 0,
|
||||
)
|
||||
except:
|
||||
print(
|
||||
"[Error] 单音符解析错误,字节码`{}`,{}启用高精度时间偏移\n".format(
|
||||
code_buffer, "已" if is_high_time_precision else "未"
|
||||
)
|
||||
)
|
||||
raise
|
||||
|
||||
def encode(self, is_high_time_precision: bool = True) -> bytes:
|
||||
"""
|
||||
将数据打包为字节码
|
||||
|
||||
Parameters
|
||||
------------
|
||||
is_high_time_precision: bool
|
||||
是否启用高精度,默认为**是**
|
||||
|
||||
Returns
|
||||
---------
|
||||
bytes
|
||||
打包好的字节码
|
||||
"""
|
||||
|
||||
# SingleNote 的字节码
|
||||
|
||||
# note_pitch 7 位 支持到 127
|
||||
# velocity 长度 7 位 支持到 127
|
||||
# start_tick 17 位 支持到 131071 即 109.22583 分钟 合 1.8204305 小时
|
||||
# duration 17 位 支持到 131071 即 109.22583 分钟 合 1.8204305 小时
|
||||
# 共 48 位 合 6 字节
|
||||
|
||||
# high_time_precision(可选)长度 8 位 支持到 255 合 1 字节 支持 1/1250 秒]
|
||||
|
||||
return (
|
||||
(
|
||||
(
|
||||
((((self.note_pitch << 7) + self.velocity) << 17) + self.start_tick)
|
||||
<< 17
|
||||
)
|
||||
+ self.duration
|
||||
).to_bytes(6, "big")
|
||||
# + self.track_no.to_bytes(1, "big")
|
||||
+ (
|
||||
self.high_precision_time.to_bytes(1, "big")
|
||||
if is_high_time_precision
|
||||
else b""
|
||||
)
|
||||
)
|
||||
|
||||
def set_info(self, key: Union[str, Sequence[str]], value: Any):
|
||||
"""设置附加信息"""
|
||||
if isinstance(key, str):
|
||||
self.extra_info[key] = value
|
||||
elif (
|
||||
isinstance(key, Sequence)
|
||||
and isinstance(value, Sequence)
|
||||
and (k := len(key)) == len(value)
|
||||
):
|
||||
for i in range(k):
|
||||
self.extra_info[key[i]] = value[i]
|
||||
else:
|
||||
# 提供简单报错就行了,如果放一堆 if 语句,降低处理速度
|
||||
raise TypeError("参数类型错误;键:`{}` 值:`{}`".format(key, value))
|
||||
|
||||
def get_info(self, key: str, default: Any = None) -> Any:
|
||||
"""获取附加信息"""
|
||||
return self.extra_info.get(key, default)
|
||||
|
||||
def stringize(self, include_extra_data: bool = False) -> str:
|
||||
return "TrackedNote(Pitch = {}, Velocity = {}, StartTick = {}, Duration = {}, TimeOffset = {}".format(
|
||||
self.note_pitch,
|
||||
self.velocity,
|
||||
self.start_tick,
|
||||
self.duration,
|
||||
self.high_precision_time,
|
||||
) + (
|
||||
", ExtraData = {})".format(self.extra_info) if include_extra_data else ")"
|
||||
)
|
||||
|
||||
# def __list__(self) -> List[int]:
|
||||
# 我不认为这个类应当被作为列表使用
|
||||
|
||||
def __tuple__(
|
||||
self,
|
||||
) -> Tuple[int, int, int, int, int]:
|
||||
return (
|
||||
self.note_pitch,
|
||||
self.velocity,
|
||||
self.start_tick,
|
||||
self.duration,
|
||||
self.high_precision_time,
|
||||
)
|
||||
|
||||
def __dict__(self):
|
||||
return {
|
||||
"Pitch": self.note_pitch,
|
||||
"Velocity": self.velocity,
|
||||
"StartTick": self.start_tick,
|
||||
"Duration": self.duration,
|
||||
"TimeOffset": self.high_precision_time,
|
||||
"ExtraData": self.extra_info,
|
||||
}
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
"""比较两个音符是否具有相同的属性,不计附加信息"""
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
return self.__tuple__() == other.__tuple__()
|
||||
|
||||
def __lt__(self, other) -> bool:
|
||||
"""比较自己是否在开始时间上早于另一个音符"""
|
||||
if self.start_tick == other.start_tick:
|
||||
return self.high_precision_time < other.high_precision_time
|
||||
else:
|
||||
return self.start_tick < other.start_tick
|
||||
|
||||
def __gt__(self, other) -> bool:
|
||||
"""比较自己是否在开始时间上晚于另一个音符"""
|
||||
if self.start_tick == other.start_tick:
|
||||
return self.high_precision_time > other.high_precision_time
|
||||
else:
|
||||
return self.start_tick > other.start_tick
|
||||
|
||||
|
||||
class SingleTrack(list):
|
||||
"""存储单个轨道的类"""
|
||||
|
||||
track_name: str
|
||||
"""轨道之名称"""
|
||||
|
||||
track_instrument: str
|
||||
"""乐器ID"""
|
||||
|
||||
track_volume: float
|
||||
"""该音轨的音量"""
|
||||
|
||||
is_high_time_precision: bool
|
||||
"""该音轨是否使用高精度时间"""
|
||||
|
||||
is_percussive: bool
|
||||
"""该音轨是否标记为打击乐器轨道"""
|
||||
|
||||
sound_position: SoundAtmos
|
||||
"""声像方位"""
|
||||
|
||||
argument_curves: Dict[str, FittingFunctionType]
|
||||
"""参数曲线"""
|
||||
|
||||
extra_info: Dict[str, Any]
|
||||
"""你觉得放什么好?"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "未命名音轨",
|
||||
instrument: str = "",
|
||||
volume: float = 0,
|
||||
precise_time: bool = True,
|
||||
percussion: bool = False,
|
||||
sound_direction: SoundAtmos = SoundAtmos(),
|
||||
extra_information: Dict[str, Any] = {},
|
||||
*args: SingleNote,
|
||||
):
|
||||
self.track_name = name
|
||||
"""音轨名称"""
|
||||
|
||||
self.track_instrument = instrument
|
||||
"""乐器ID"""
|
||||
|
||||
self.track_volume = volume
|
||||
"""音量"""
|
||||
|
||||
self.is_high_time_precision = precise_time
|
||||
"""是否使用高精度时间"""
|
||||
|
||||
self.is_percussive = percussion
|
||||
"""是否为打击乐器"""
|
||||
|
||||
self.sound_position = sound_direction
|
||||
"""声像方位"""
|
||||
|
||||
self.extra_info = extra_information if extra_information else {}
|
||||
|
||||
super().__init__(*args)
|
||||
|
||||
@property
|
||||
def note_amount(self) -> int:
|
||||
"""音符数"""
|
||||
return len(self)
|
||||
|
||||
def set_info(self, key: Union[str, Sequence[str]], value: Any):
|
||||
"""设置附加信息"""
|
||||
if isinstance(key, str):
|
||||
self.extra_info[key] = value
|
||||
elif (
|
||||
isinstance(key, Sequence)
|
||||
and isinstance(value, Sequence)
|
||||
and (k := len(key)) == len(value)
|
||||
):
|
||||
for i in range(k):
|
||||
self.extra_info[key[i]] = value[i]
|
||||
else:
|
||||
# 提供简单报错就行了,如果放一堆 if 语句,降低处理速度
|
||||
raise TypeError("参数类型错误;键:`{}` 值:`{}`".format(key, value))
|
||||
|
||||
def get_info(self, key: str, default: Any = None) -> Any:
|
||||
"""获取附加信息"""
|
||||
return self.extra_info.get(key, default)
|
||||
|
||||
|
||||
class SingleMusic:
|
||||
pass
|
||||
@@ -1,12 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
存放一堆报错类型
|
||||
存放一些报错类型
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -18,11 +18,11 @@ Terms & Conditions: License.md in the root directory
|
||||
|
||||
|
||||
class MSCTBaseException(Exception):
|
||||
"""音·创库版本的所有错误均继承于此"""
|
||||
"""音·创 的所有错误均继承于此"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""音·创库版本的所有错误均继承于此"""
|
||||
super().__init__(*args)
|
||||
"""音·创 的所有错误均继承于此"""
|
||||
super().__init__("音·创", *args)
|
||||
|
||||
def meow(
|
||||
self,
|
||||
@@ -35,11 +35,11 @@ class MSCTBaseException(Exception):
|
||||
|
||||
|
||||
class MidiFormatException(MSCTBaseException):
|
||||
"""音·创库版本的所有MIDI格式错误均继承于此"""
|
||||
"""音·创 的所有MIDI格式错误均继承于此"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""音·创库版本的所有MIDI格式错误均继承于此"""
|
||||
super().__init__("MIDI格式错误", *args)
|
||||
"""音·创 的所有MIDI格式错误均继承于此"""
|
||||
super().__init__("MIDI 格式错误", *args)
|
||||
|
||||
|
||||
class MidiDestroyedError(MSCTBaseException):
|
||||
@@ -47,18 +47,19 @@ class MidiDestroyedError(MSCTBaseException):
|
||||
|
||||
def __init__(self, *args):
|
||||
"""Midi文件损坏"""
|
||||
super().__init__("MIDI文件损坏:无法读取MIDI文件", *args)
|
||||
super().__init__("MIDI文件损坏:无法读取 MIDI 文件", *args)
|
||||
|
||||
|
||||
class MidiUnboundError(MSCTBaseException):
|
||||
"""未定义Midi对象"""
|
||||
# class MidiUnboundError(MSCTBaseException):
|
||||
# """未定义Midi对象(无用)"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""未绑定Midi对象"""
|
||||
super().__init__("未定义MidiFile对象:你甚至没有对象就想要生孩子?", *args)
|
||||
# def __init__(self, *args):
|
||||
# """未绑定Midi对象"""
|
||||
# super().__init__("未定义MidiFile对象:你甚至没有对象就想要生孩子?", *args)
|
||||
# 此错误在本版本内已经不再使用
|
||||
|
||||
|
||||
class CommandFormatError(RuntimeError):
|
||||
class CommandFormatError(MSCTBaseException, RuntimeError):
|
||||
"""指令格式与目标格式不匹配而引起的错误"""
|
||||
|
||||
def __init__(self, *args):
|
||||
@@ -75,6 +76,10 @@ class CommandFormatError(RuntimeError):
|
||||
# 这TM是什么错误?
|
||||
# 我什么时候写的这玩意?
|
||||
# 我哪知道这说的是啥?
|
||||
# !!!
|
||||
# 我知道这是什么了 —— 金羿 2025 0401
|
||||
# 两个其他属性相同的音符在同一个通道,出现连续两个开音信息和连续两个停止信息
|
||||
# 那么这两个音符的音长无法判断。这是个好问题,但是不是我现在能解决的,也不是我们现在想解决的问题
|
||||
|
||||
|
||||
class NotDefineTempoError(MidiFormatException):
|
||||
@@ -82,7 +87,7 @@ class NotDefineTempoError(MidiFormatException):
|
||||
|
||||
def __init__(self, *args):
|
||||
"""没有Tempo设定导致时间无法计算的错误"""
|
||||
super().__init__("在曲目开始时没有声明Tempo(未指定拍长)", *args)
|
||||
super().__init__("在曲目开始时没有声明 Tempo(未指定拍长)", *args)
|
||||
|
||||
|
||||
class ChannelOverFlowError(MidiFormatException):
|
||||
@@ -109,9 +114,49 @@ class NoteOnOffMismatchError(MidiFormatException):
|
||||
super().__init__("音符不匹配", *args)
|
||||
|
||||
|
||||
class ZeroSpeedError(ZeroDivisionError):
|
||||
class LyricMismatchError(MSCTBaseException):
|
||||
"""歌词匹配解析错误"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""有可能产生了错误的歌词解析"""
|
||||
super().__init__("歌词解析错误", *args)
|
||||
|
||||
|
||||
class ZeroSpeedError(MSCTBaseException, ZeroDivisionError):
|
||||
"""以0作为播放速度的错误"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""以0作为播放速度的错误"""
|
||||
super().__init__("播放速度为0", *args)
|
||||
super().__init__("播放速度为零", *args)
|
||||
|
||||
|
||||
class IllegalMinimumVolumeError(MSCTBaseException, ValueError):
|
||||
"""最小播放音量有误的错误"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""最小播放音量错误"""
|
||||
super().__init__("最小播放音量超出范围", *args)
|
||||
|
||||
|
||||
class MusicSequenceDecodeError(MSCTBaseException):
|
||||
"""音乐序列解码错误"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""音乐序列无法正确解码的错误"""
|
||||
super().__init__("解码音符序列文件时出现问题", *args)
|
||||
|
||||
|
||||
class MusicSequenceTypeError(MSCTBaseException):
|
||||
"""音乐序列类型错误"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""无法识别音符序列字节码的类型"""
|
||||
super().__init__("错误的音符序列字节类型", *args)
|
||||
|
||||
|
||||
class MusicSequenceVerificationFailed(MusicSequenceDecodeError):
|
||||
"""音乐序列校验失败"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""音符序列文件与其校验值不一致"""
|
||||
super().__init__("音符序列文件校验失败", *args)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@
|
||||
|
||||
# 音·创 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||
# 版权所有 金羿("Eilles") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
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
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 ../License.md
|
||||
Terms & Conditions: ../License.md
|
||||
@@ -183,6 +183,7 @@ if __name__ == "__main__":
|
||||
|
||||
# ============================
|
||||
from typing import Literal
|
||||
|
||||
from ..constants import x, y, z
|
||||
|
||||
|
||||
@@ -198,7 +199,7 @@ def delay_to_note_blocks(
|
||||
:return 是否生成成功
|
||||
"""
|
||||
|
||||
from TrimMCStruct import Structure, Block
|
||||
from TrimMCStruct import Block, Structure
|
||||
|
||||
struct = Structure(
|
||||
(_sideLength, max_height, _sideLength), # 声明结构大小
|
||||
|
||||
1665
Musicreater/main.py
1665
Musicreater/main.py
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
存放非音·创本体的附加内容(插件?)
|
||||
存放非音·创本体的附加功能件
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -16,7 +16,7 @@ Terms & Conditions: License.md in the root directory
|
||||
|
||||
__all__ = [
|
||||
# 通用
|
||||
"ConvertConfig",
|
||||
# "ConvertConfig",
|
||||
"bottem_side_length_of_smallest_square_bottom_box",
|
||||
# 打包
|
||||
"compress_zipfile",
|
||||
@@ -34,6 +34,7 @@ __all__ = [
|
||||
"AXIS_PARTICULAR_VALUE",
|
||||
"COMPABILITY_VERSION_117",
|
||||
"COMPABILITY_VERSION_119",
|
||||
"COMPABILITY_VERSION_121",
|
||||
# BDX 函数
|
||||
"bdx_move",
|
||||
"form_command_block_in_BDX_bytes",
|
||||
@@ -41,9 +42,9 @@ __all__ = [
|
||||
# BDX 常量
|
||||
"BDX_MOVE_KEY",
|
||||
]
|
||||
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"))
|
||||
__author__ = (("金羿", "Eilles"), ("诸葛亮与八卦阵", "bgArray"))
|
||||
|
||||
from .main import ConvertConfig
|
||||
# from .main import ConvertConfig
|
||||
|
||||
from .archive import compress_zipfile, behavior_mcpack_manifest
|
||||
|
||||
@@ -62,6 +63,7 @@ from .mcstructure import (
|
||||
AXIS_PARTICULAR_VALUE,
|
||||
COMPABILITY_VERSION_119,
|
||||
COMPABILITY_VERSION_117,
|
||||
COMPABILITY_VERSION_121,
|
||||
command_statevalue,
|
||||
form_note_block_in_NBT_struct,
|
||||
form_repeater_in_NBT_struct,
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"""
|
||||
用以生成附加包的附加功能
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -18,11 +18,13 @@ __all__ = [
|
||||
"to_addon_pack_in_delay",
|
||||
"to_addon_pack_in_score",
|
||||
"to_addon_pack_in_repeater",
|
||||
"to_addon_pack_in_repeater_divided_by_instrument",
|
||||
]
|
||||
__author__ = (("金羿", "Eilles Wan"),)
|
||||
__author__ = (("金羿", "Eilles"),)
|
||||
|
||||
from .main import (
|
||||
to_addon_pack_in_delay,
|
||||
to_addon_pack_in_repeater,
|
||||
to_addon_pack_in_score,
|
||||
to_addon_pack_in_repeater_divided_by_instrument,
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -14,16 +14,15 @@ Terms & Conditions: License.md in the root directory
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
from typing import Tuple
|
||||
|
||||
from TrimMCStruct import Structure
|
||||
from typing import Literal, Optional, Tuple
|
||||
|
||||
from ...main import MidiConvert
|
||||
from ...subclass import ProgressBarStyle
|
||||
from ..archive import behavior_mcpack_manifest, compress_zipfile
|
||||
from ..main import ConvertConfig
|
||||
from ..mcstructure import (
|
||||
COMPABILITY_VERSION_117,
|
||||
COMPABILITY_VERSION_119,
|
||||
Structure,
|
||||
commands_to_redstone_delay_structure,
|
||||
commands_to_structure,
|
||||
form_command_block_in_NBT_struct,
|
||||
@@ -32,7 +31,8 @@ from ..mcstructure import (
|
||||
|
||||
def to_addon_pack_in_score(
|
||||
midi_cvt: MidiConvert,
|
||||
data_cfg: ConvertConfig,
|
||||
dist_path: str,
|
||||
progressbar_style: Optional[ProgressBarStyle],
|
||||
scoreboard_name: str = "mscplay",
|
||||
auto_reset: bool = False,
|
||||
) -> Tuple[int, int]:
|
||||
@@ -43,8 +43,10 @@ def to_addon_pack_in_score(
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
data_cfg: ConvertConfig 对象
|
||||
部分转换通用参数
|
||||
dist_path: str
|
||||
转换结果输出的目标路径
|
||||
progressbar_style: ProgressBarStyle 对象
|
||||
进度条对象
|
||||
scoreboard_name: str
|
||||
我的世界的计分板名称
|
||||
auto_reset: bool
|
||||
@@ -56,21 +58,25 @@ def to_addon_pack_in_score(
|
||||
"""
|
||||
|
||||
cmdlist, maxlen, maxscore = midi_cvt.to_command_list_in_score(
|
||||
scoreboard_name, data_cfg.volume_ratio, data_cfg.speed_multiplier
|
||||
scoreboard_name=scoreboard_name,
|
||||
)
|
||||
|
||||
# 当文件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")
|
||||
if os.path.exists(f"{dist_path}/temp/functions/"):
|
||||
shutil.rmtree(f"{dist_path}/temp/functions/")
|
||||
os.makedirs(f"{dist_path}/temp/functions/mscplay")
|
||||
|
||||
# 写入manifest.json
|
||||
with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f:
|
||||
with open(f"{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 + "播放",
|
||||
pack_description=f"{midi_cvt.music_name} 音乐播放包,MCFUNCTION(MCPACK) 计分播放器 - 由 音·创 生成",
|
||||
pack_name=midi_cvt.music_name + "播放",
|
||||
modules_description=f"无 - 由 音·创 生成",
|
||||
format_version=1 if midi_cvt.enable_old_exe_format else 2,
|
||||
pack_engine_version=(
|
||||
None if midi_cvt.enable_old_exe_format else [1, 19, 50]
|
||||
),
|
||||
),
|
||||
fp=f,
|
||||
indent=4,
|
||||
@@ -78,18 +84,18 @@ def to_addon_pack_in_score(
|
||||
|
||||
# 写入stop.mcfunction
|
||||
with open(
|
||||
f"{data_cfg.dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8"
|
||||
f"{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"
|
||||
f"{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",
|
||||
f"{dist_path}/temp/functions/mscplay/track{i + 1}.mcfunction",
|
||||
"w",
|
||||
encoding="utf-8",
|
||||
) as f:
|
||||
@@ -113,13 +119,13 @@ def to_addon_pack_in_score(
|
||||
if auto_reset
|
||||
else ""
|
||||
),
|
||||
f"function mscplay/progressShow\n" if data_cfg.progressbar_style else "",
|
||||
f"function mscplay/progressShow\n" if progressbar_style else "",
|
||||
)
|
||||
)
|
||||
|
||||
if data_cfg.progressbar_style:
|
||||
if progressbar_style:
|
||||
with open(
|
||||
f"{data_cfg.dist_path}/temp/functions/mscplay/progressShow.mcfunction",
|
||||
f"{dist_path}/temp/functions/mscplay/progressShow.mcfunction",
|
||||
"w",
|
||||
encoding="utf-8",
|
||||
) as f:
|
||||
@@ -128,7 +134,7 @@ def to_addon_pack_in_score(
|
||||
[
|
||||
single_cmd.cmd
|
||||
for single_cmd in midi_cvt.form_progress_bar(
|
||||
maxscore, scoreboard_name, data_cfg.progressbar_style
|
||||
maxscore, scoreboard_name, progressbar_style
|
||||
)
|
||||
]
|
||||
)
|
||||
@@ -136,21 +142,22 @@ def to_addon_pack_in_score(
|
||||
|
||||
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")
|
||||
if os.path.exists(f"{dist_path}/{midi_cvt.music_name}.mcpack"):
|
||||
os.remove(f"{dist_path}/{midi_cvt.music_name}.mcpack")
|
||||
compress_zipfile(
|
||||
f"{data_cfg.dist_path}/temp/",
|
||||
f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack",
|
||||
f"{dist_path}/temp/",
|
||||
f"{dist_path}/{midi_cvt.music_name}[score].mcpack",
|
||||
)
|
||||
|
||||
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
|
||||
shutil.rmtree(f"{dist_path}/temp/")
|
||||
|
||||
return maxlen, maxscore
|
||||
|
||||
|
||||
def to_addon_pack_in_delay(
|
||||
midi_cvt: MidiConvert,
|
||||
data_cfg: ConvertConfig,
|
||||
dist_path: str,
|
||||
progressbar_style: Optional[ProgressBarStyle],
|
||||
player: str = "@a",
|
||||
max_height: int = 64,
|
||||
) -> Tuple[int, int]:
|
||||
@@ -161,8 +168,10 @@ def to_addon_pack_in_delay(
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
data_cfg: ConvertConfig 对象
|
||||
部分转换通用参数
|
||||
dist_path: str
|
||||
转换结果输出的目标路径
|
||||
progressbar_style: ProgressBarStyle 对象
|
||||
进度条对象
|
||||
player: str
|
||||
玩家选择器,默认为`@a`
|
||||
max_height: int
|
||||
@@ -180,35 +189,38 @@ def to_addon_pack_in_delay(
|
||||
)
|
||||
|
||||
command_list, max_delay = midi_cvt.to_command_list_in_delay(
|
||||
data_cfg.volume_ratio,
|
||||
data_cfg.speed_multiplier,
|
||||
player,
|
||||
player_selector=player,
|
||||
)[:2]
|
||||
|
||||
if not os.path.exists(data_cfg.dist_path):
|
||||
os.makedirs(data_cfg.dist_path)
|
||||
if not os.path.exists(dist_path):
|
||||
os.makedirs(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/")
|
||||
if os.path.exists(f"{dist_path}/temp/"):
|
||||
shutil.rmtree(f"{dist_path}/temp/")
|
||||
os.makedirs(f"{dist_path}/temp/functions/")
|
||||
os.makedirs(f"{dist_path}/temp/structures/")
|
||||
|
||||
# 写入manifest.json
|
||||
with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f:
|
||||
with open(f"{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 + "播放",
|
||||
pack_description=f"{midi_cvt.music_name} 音乐播放包,MCSTRUCTURE(MCPACK) 延迟播放器 - 由 音·创 生成",
|
||||
pack_name=midi_cvt.music_name + "播放",
|
||||
modules_description=f"无 - 由 音·创 生成",
|
||||
format_version=1 if midi_cvt.enable_old_exe_format else 2,
|
||||
pack_engine_version=(
|
||||
None if midi_cvt.enable_old_exe_format else [1, 19, 50]
|
||||
),
|
||||
),
|
||||
fp=f,
|
||||
indent=4,
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
# 写入stop.mcfunction
|
||||
with open(
|
||||
f"{data_cfg.dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8"
|
||||
f"{dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8"
|
||||
) as f:
|
||||
f.write(
|
||||
"gamerule commandblocksenabled false\ngamerule commandblocksenabled true"
|
||||
@@ -216,7 +228,7 @@ def to_addon_pack_in_delay(
|
||||
|
||||
# 将命令列表写入文件
|
||||
index_file = open(
|
||||
f"{data_cfg.dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8"
|
||||
f"{dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8"
|
||||
)
|
||||
|
||||
struct, size, end_pos = commands_to_structure(
|
||||
@@ -227,9 +239,9 @@ def to_addon_pack_in_delay(
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(
|
||||
data_cfg.dist_path,
|
||||
dist_path,
|
||||
"temp/structures/",
|
||||
f"{midi_cvt.midi_music_name}_main.mcstructure",
|
||||
f"{midi_cvt.music_name}_main.mcstructure",
|
||||
)
|
||||
),
|
||||
"wb+",
|
||||
@@ -238,8 +250,8 @@ def to_addon_pack_in_delay(
|
||||
|
||||
del struct
|
||||
|
||||
if data_cfg.progressbar_style:
|
||||
scb_name = midi_cvt.midi_music_name[:3] + "Pgb"
|
||||
if progressbar_style:
|
||||
scb_name = midi_cvt.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)
|
||||
@@ -259,19 +271,19 @@ def to_addon_pack_in_delay(
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(
|
||||
data_cfg.dist_path,
|
||||
dist_path,
|
||||
"temp/structures/",
|
||||
f"{midi_cvt.midi_music_name}_start.mcstructure",
|
||||
f"{midi_cvt.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")
|
||||
index_file.write(f"structure load {midi_cvt.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),
|
||||
midi_cvt.form_progress_bar(max_delay, scb_name, progressbar_style),
|
||||
max_height - 1,
|
||||
compability_version_=compability_ver,
|
||||
)
|
||||
@@ -279,16 +291,16 @@ def to_addon_pack_in_delay(
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(
|
||||
data_cfg.dist_path,
|
||||
dist_path,
|
||||
"temp/structures/",
|
||||
f"{midi_cvt.midi_music_name}_pgb.mcstructure",
|
||||
f"{midi_cvt.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")
|
||||
index_file.write(f"structure load {midi_cvt.music_name}_pgb ~ ~1 ~1\n")
|
||||
|
||||
struct_a = Structure(
|
||||
(1, 1, 1),
|
||||
@@ -309,9 +321,9 @@ def to_addon_pack_in_delay(
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(
|
||||
data_cfg.dist_path,
|
||||
dist_path,
|
||||
"temp/structures/",
|
||||
f"{midi_cvt.midi_music_name}_reset.mcstructure",
|
||||
f"{midi_cvt.music_name}_reset.mcstructure",
|
||||
)
|
||||
),
|
||||
"wb+",
|
||||
@@ -321,34 +333,37 @@ def to_addon_pack_in_delay(
|
||||
del struct_a, pgb_struct
|
||||
|
||||
index_file.write(
|
||||
f"structure load {midi_cvt.midi_music_name}_reset ~{pgbSize[0] + 2} ~ ~1\n"
|
||||
f"structure load {midi_cvt.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"
|
||||
f"structure load {midi_cvt.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.write(f"structure load {midi_cvt.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")
|
||||
if os.path.exists(f"{dist_path}/{midi_cvt.music_name}.mcpack"):
|
||||
os.remove(f"{dist_path}/{midi_cvt.music_name}.mcpack")
|
||||
compress_zipfile(
|
||||
f"{data_cfg.dist_path}/temp/",
|
||||
f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack",
|
||||
f"{dist_path}/temp/",
|
||||
f"{dist_path}/{midi_cvt.music_name}[delay].mcpack",
|
||||
)
|
||||
|
||||
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
|
||||
shutil.rmtree(f"{dist_path}/temp/")
|
||||
|
||||
return len(command_list), max_delay
|
||||
|
||||
|
||||
def to_addon_pack_in_repeater(
|
||||
midi_cvt: MidiConvert,
|
||||
data_cfg: ConvertConfig,
|
||||
dist_path: str,
|
||||
progressbar_style: Optional[ProgressBarStyle],
|
||||
player: str = "@a",
|
||||
axis_side: Literal["z+", "z-", "Z+", "Z-", "x+", "x-", "X+", "X-"] = "z+",
|
||||
basement_block: str = "concrete",
|
||||
max_height: int = 65,
|
||||
) -> Tuple[int, int]:
|
||||
"""
|
||||
@@ -358,8 +373,10 @@ def to_addon_pack_in_repeater(
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
data_cfg: ConvertConfig 对象
|
||||
部分转换通用参数
|
||||
dist_path: str
|
||||
转换结果输出的目标路径
|
||||
progressbar_style: ProgressBarStyle 对象
|
||||
进度条对象
|
||||
player: str
|
||||
玩家选择器,默认为`@a`
|
||||
max_height: int
|
||||
@@ -377,27 +394,29 @@ def to_addon_pack_in_repeater(
|
||||
)
|
||||
|
||||
command_list, max_delay, max_together = midi_cvt.to_command_list_in_delay(
|
||||
data_cfg.volume_ratio,
|
||||
data_cfg.speed_multiplier,
|
||||
player,
|
||||
player_selector=player,
|
||||
)
|
||||
|
||||
if not os.path.exists(data_cfg.dist_path):
|
||||
os.makedirs(data_cfg.dist_path)
|
||||
if not os.path.exists(dist_path):
|
||||
os.makedirs(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/")
|
||||
if os.path.exists(f"{dist_path}/temp/"):
|
||||
shutil.rmtree(f"{dist_path}/temp/")
|
||||
os.makedirs(f"{dist_path}/temp/functions/")
|
||||
os.makedirs(f"{dist_path}/temp/structures/")
|
||||
|
||||
# 写入manifest.json
|
||||
with open(f"{data_cfg.dist_path}/temp/manifest.json", "w", encoding="utf-8") as f:
|
||||
with open(f"{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 + "播放",
|
||||
pack_description=f"{midi_cvt.music_name} 音乐播放包,MCSTRUCTURE(MCPACK) 中继器播放器 - 由 音·创 生成",
|
||||
pack_name=midi_cvt.music_name + "播放",
|
||||
modules_description=f"无 - 由 音·创 生成",
|
||||
format_version=1 if midi_cvt.enable_old_exe_format else 2,
|
||||
pack_engine_version=(
|
||||
None if midi_cvt.enable_old_exe_format else [1, 19, 50]
|
||||
),
|
||||
),
|
||||
fp=f,
|
||||
indent=4,
|
||||
@@ -405,7 +424,7 @@ def to_addon_pack_in_repeater(
|
||||
|
||||
# 写入stop.mcfunction
|
||||
with open(
|
||||
f"{data_cfg.dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8"
|
||||
f"{dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8"
|
||||
) as f:
|
||||
f.write(
|
||||
"gamerule commandblocksenabled false\ngamerule commandblocksenabled true"
|
||||
@@ -413,21 +432,23 @@ def to_addon_pack_in_repeater(
|
||||
|
||||
# 将命令列表写入文件
|
||||
index_file = open(
|
||||
f"{data_cfg.dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8"
|
||||
f"{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,
|
||||
commands=command_list,
|
||||
delay_length=max_delay,
|
||||
max_multicmd_length=max_together,
|
||||
base_block=basement_block,
|
||||
axis_=axis_side,
|
||||
compability_version_=compability_ver,
|
||||
)
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(
|
||||
data_cfg.dist_path,
|
||||
dist_path,
|
||||
"temp/structures/",
|
||||
f"{midi_cvt.midi_music_name}_main.mcstructure",
|
||||
f"{midi_cvt.music_name}_main.mcstructure",
|
||||
)
|
||||
),
|
||||
"wb+",
|
||||
@@ -436,8 +457,8 @@ def to_addon_pack_in_repeater(
|
||||
|
||||
del struct
|
||||
|
||||
if data_cfg.progressbar_style:
|
||||
scb_name = midi_cvt.midi_music_name[:3] + "Pgb"
|
||||
if progressbar_style:
|
||||
scb_name = midi_cvt.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)
|
||||
@@ -457,19 +478,19 @@ def to_addon_pack_in_repeater(
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(
|
||||
data_cfg.dist_path,
|
||||
dist_path,
|
||||
"temp/structures/",
|
||||
f"{midi_cvt.midi_music_name}_start.mcstructure",
|
||||
f"{midi_cvt.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")
|
||||
index_file.write(f"structure load {midi_cvt.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),
|
||||
midi_cvt.form_progress_bar(max_delay, scb_name, progressbar_style),
|
||||
max_height - 1,
|
||||
compability_version_=compability_ver,
|
||||
)
|
||||
@@ -477,16 +498,16 @@ def to_addon_pack_in_repeater(
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(
|
||||
data_cfg.dist_path,
|
||||
dist_path,
|
||||
"temp/structures/",
|
||||
f"{midi_cvt.midi_music_name}_pgb.mcstructure",
|
||||
f"{midi_cvt.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")
|
||||
index_file.write(f"structure load {midi_cvt.music_name}_pgb ~ ~1 ~1\n")
|
||||
|
||||
struct_a = Structure(
|
||||
(1, 1, 1),
|
||||
@@ -507,9 +528,9 @@ def to_addon_pack_in_repeater(
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(
|
||||
data_cfg.dist_path,
|
||||
dist_path,
|
||||
"temp/structures/",
|
||||
f"{midi_cvt.midi_music_name}_reset.mcstructure",
|
||||
f"{midi_cvt.music_name}_reset.mcstructure",
|
||||
)
|
||||
),
|
||||
"wb+",
|
||||
@@ -519,25 +540,145 @@ def to_addon_pack_in_repeater(
|
||||
del struct_a, pgb_struct
|
||||
|
||||
index_file.write(
|
||||
f"structure load {midi_cvt.midi_music_name}_reset ~{pgbSize[0] + 2} ~ ~1\n"
|
||||
f"structure load {midi_cvt.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"
|
||||
f"structure load {midi_cvt.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.write(f"structure load {midi_cvt.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")
|
||||
if os.path.exists(f"{dist_path}/{midi_cvt.music_name}.mcpack"):
|
||||
os.remove(f"{dist_path}/{midi_cvt.music_name}.mcpack")
|
||||
compress_zipfile(
|
||||
f"{data_cfg.dist_path}/temp/",
|
||||
f"{data_cfg.dist_path}/{midi_cvt.midi_music_name}.mcpack",
|
||||
f"{dist_path}/temp/",
|
||||
f"{dist_path}/{midi_cvt.music_name}[repeater].mcpack",
|
||||
)
|
||||
|
||||
shutil.rmtree(f"{data_cfg.dist_path}/temp/")
|
||||
shutil.rmtree(f"{dist_path}/temp/")
|
||||
|
||||
return len(command_list), max_delay
|
||||
|
||||
|
||||
def to_addon_pack_in_repeater_divided_by_instrument(
|
||||
midi_cvt: MidiConvert,
|
||||
dist_path: str,
|
||||
player: str = "@a",
|
||||
max_height: int = 65,
|
||||
base_block: str = "concrete",
|
||||
axis_side: Literal["z+", "z-", "Z+", "Z-", "x+", "x-", "X+", "X-"] = "z+",
|
||||
) -> Tuple[int, int]:
|
||||
"""
|
||||
将midi以中继器播放器形式转换为mcstructure结构文件后打包成附加包,并在附加包中生成相应地导入函数
|
||||
|
||||
Parameters
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
dist_path: str
|
||||
转换结果输出的目标路径
|
||||
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
|
||||
)
|
||||
|
||||
if not os.path.exists(dist_path):
|
||||
os.makedirs(dist_path)
|
||||
|
||||
# 当文件f夹{self.outputPath}/temp/存在时清空其下所有项目,然后创建
|
||||
if os.path.exists(f"{dist_path}/temp/"):
|
||||
shutil.rmtree(f"{dist_path}/temp/")
|
||||
os.makedirs(f"{dist_path}/temp/functions/")
|
||||
os.makedirs(f"{dist_path}/temp/structures/")
|
||||
|
||||
# 写入manifest.json
|
||||
with open(f"{dist_path}/temp/manifest.json", "w", encoding="utf-8") as f:
|
||||
json.dump(
|
||||
behavior_mcpack_manifest(
|
||||
pack_description=f"{midi_cvt.music_name} 音乐播放包,MCSTRUCTURE(MCPACK) 中继器播放器(拆分) - 由 音·创 生成",
|
||||
pack_name=midi_cvt.music_name + "播放",
|
||||
modules_description=f"无 - 由 音·创 生成",
|
||||
format_version=1 if midi_cvt.enable_old_exe_format else 2,
|
||||
pack_engine_version=(
|
||||
None if midi_cvt.enable_old_exe_format else [1, 19, 50]
|
||||
),
|
||||
),
|
||||
fp=f,
|
||||
indent=4,
|
||||
)
|
||||
|
||||
# 写入stop.mcfunction
|
||||
with open(
|
||||
f"{dist_path}/temp/functions/stop.mcfunction", "w", encoding="utf-8"
|
||||
) as f:
|
||||
f.write(
|
||||
"gamerule commandblocksenabled false\ngamerule commandblocksenabled true"
|
||||
)
|
||||
|
||||
# 将命令列表写入文件
|
||||
index_file = open(
|
||||
f"{dist_path}/temp/functions/index.mcfunction", "w", encoding="utf-8"
|
||||
)
|
||||
|
||||
cmd_dict, max_delay, max_multiple_cmd_count = (
|
||||
midi_cvt.to_command_list_in_delay_devided_by_instrument(
|
||||
player_selector=player,
|
||||
)
|
||||
)
|
||||
|
||||
base_height = 0
|
||||
|
||||
for inst, cmd_list in cmd_dict.items():
|
||||
struct, size, end_pos = commands_to_redstone_delay_structure(
|
||||
cmd_list,
|
||||
max_delay,
|
||||
max_multiple_cmd_count[inst],
|
||||
base_block,
|
||||
axis_=axis_side,
|
||||
compability_version_=compability_ver,
|
||||
)
|
||||
|
||||
bkn = "{}_{}".format(midi_cvt.music_name, inst.replace(".", "-"))
|
||||
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(
|
||||
dist_path,
|
||||
"temp/structures/",
|
||||
"{}_main.mcstructure".format(bkn),
|
||||
)
|
||||
),
|
||||
"wb+",
|
||||
) as f:
|
||||
struct.dump(f)
|
||||
|
||||
index_file.write("structure load {}_main ~ ~{} ~3\n".format(bkn, base_height))
|
||||
base_height += 2 + size[1]
|
||||
|
||||
index_file.close()
|
||||
|
||||
if os.path.exists(f"{dist_path}/{midi_cvt.music_name}.mcpack"):
|
||||
os.remove(f"{dist_path}/{midi_cvt.music_name}.mcpack")
|
||||
compress_zipfile(
|
||||
f"{dist_path}/temp/",
|
||||
f"{dist_path}/{midi_cvt.music_name}[repeater-div].mcpack",
|
||||
)
|
||||
|
||||
shutil.rmtree(f"{dist_path}/temp/")
|
||||
|
||||
return midi_cvt.total_note_count, max_delay
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -25,12 +25,28 @@ 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
|
||||
"""
|
||||
使用指定的压缩算法将目录打包为zip文件
|
||||
|
||||
Parameters
|
||||
------------
|
||||
sourceDir: str
|
||||
要压缩的源目录路径
|
||||
outFilename: str
|
||||
输出的zip文件路径
|
||||
compression: int, 可选
|
||||
压缩算法,默认为8 (DEFLATED)
|
||||
可用算法:
|
||||
STORED = 0
|
||||
DEFLATED = 8 (默认)
|
||||
BZIP2 = 12
|
||||
LZMA = 14
|
||||
exceptFile: list[str], 可选
|
||||
需要排除在压缩包外的文件名称列表(可选)
|
||||
|
||||
Returns
|
||||
---------
|
||||
None
|
||||
"""
|
||||
|
||||
zipf = zipfile.ZipFile(outFilename, "w", compression)
|
||||
@@ -46,10 +62,12 @@ def compress_zipfile(sourceDir, outFilename, compression=8, exceptFile=None):
|
||||
|
||||
|
||||
def behavior_mcpack_manifest(
|
||||
format_version: Union[Literal[1], Literal[2]] = 1,
|
||||
pack_description: str = "",
|
||||
pack_version: Union[List[int], Literal[None]] = None,
|
||||
pack_name: str = "",
|
||||
pack_uuid: Union[str, Literal[None]] = None,
|
||||
pack_engine_version: Union[List[int], None] = None,
|
||||
modules_description: str = "",
|
||||
modules_version: List[int] = [0, 0, 1],
|
||||
modules_uuid: Union[str, Literal[None]] = None,
|
||||
@@ -64,8 +82,8 @@ def behavior_mcpack_manifest(
|
||||
now_date.month * 100 + now_date.day,
|
||||
now_date.hour * 100 + now_date.minute,
|
||||
]
|
||||
return {
|
||||
"format_version": 1,
|
||||
result = {
|
||||
"format_version": format_version,
|
||||
"header": {
|
||||
"description": pack_description,
|
||||
"version": pack_version,
|
||||
@@ -81,3 +99,6 @@ def behavior_mcpack_manifest(
|
||||
}
|
||||
],
|
||||
}
|
||||
if pack_engine_version:
|
||||
result["header"]["min_engine_version"] = pack_engine_version
|
||||
return result
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -18,7 +18,7 @@ Terms & Conditions: License.md in the root directory
|
||||
from typing import List
|
||||
|
||||
from ..constants import x, y, z
|
||||
from ..subclass import SingleCommand
|
||||
from ..subclass import MineCommand
|
||||
from .common import bottem_side_length_of_smallest_square_bottom_box
|
||||
|
||||
BDX_MOVE_KEY = {
|
||||
@@ -60,50 +60,55 @@ def form_command_block_in_BDX_bytes(
|
||||
customName: str = "",
|
||||
executeOnFirstTick: bool = False,
|
||||
trackOutput: bool = True,
|
||||
):
|
||||
) -> bytes:
|
||||
"""
|
||||
使用指定项目返回指定的指令方块放置指令项
|
||||
:param command: `str`
|
||||
使用指定参数生成指定的指令方块放置指令项
|
||||
|
||||
Parameters
|
||||
------------
|
||||
command: str
|
||||
指令
|
||||
:param particularValue:
|
||||
particularValue: int
|
||||
方块特殊值,即朝向
|
||||
:0 下 无条件
|
||||
:1 上 无条件
|
||||
:2 z轴负方向 无条件
|
||||
:3 z轴正方向 无条件
|
||||
:4 x轴负方向 无条件
|
||||
:5 x轴正方向 无条件
|
||||
:6 下 无条件
|
||||
:7 下 无条件
|
||||
|
||||
:8 下 有条件
|
||||
:9 上 有条件
|
||||
:10 z轴负方向 有条件
|
||||
:11 z轴正方向 有条件
|
||||
:12 x轴负方向 有条件
|
||||
:13 x轴正方向 有条件
|
||||
:14 下 有条件
|
||||
:14 下 有条件
|
||||
: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`
|
||||
impluse: int (0|1|2)
|
||||
方块类型
|
||||
0脉冲 1循环 2连锁
|
||||
:param condition: `bool`
|
||||
0脉冲 1循环 2连锁
|
||||
condition: bool
|
||||
是否有条件
|
||||
:param needRedstone: `bool`
|
||||
needRedstone: bool
|
||||
是否需要红石
|
||||
:param tickDelay: `int`
|
||||
tickDelay: int
|
||||
执行延时
|
||||
:param customName: `str`
|
||||
customName: str
|
||||
悬浮字
|
||||
lastOutput: `str`
|
||||
上次输出字符串,注意此处需要留空
|
||||
:param executeOnFirstTick: `bool`
|
||||
首刻执行(循环指令方块是否激活后立即执行,若为False,则从激活时起延迟后第一次执行)
|
||||
:param trackOutput: `bool`
|
||||
是否输出
|
||||
lastOutput: str
|
||||
命令方块的上次输出字符串,注意此处需要留空
|
||||
executeOnFirstTick: bool
|
||||
是否启用首刻执行(循环指令方块是否激活后立即执行,若为False,则从激活时起延迟后第一次执行)
|
||||
trackOutput: bool
|
||||
是否启用命令方块输出
|
||||
|
||||
:return:str
|
||||
Returns
|
||||
---------
|
||||
bytes
|
||||
用以生成 bdx 结构的字节码
|
||||
"""
|
||||
block = b"\x24" + particularValue.to_bytes(2, byteorder="big", signed=False)
|
||||
|
||||
@@ -123,13 +128,23 @@ def form_command_block_in_BDX_bytes(
|
||||
|
||||
|
||||
def commands_to_BDX_bytes(
|
||||
commands_list: List[SingleCommand],
|
||||
commands_list: List[MineCommand],
|
||||
max_height: int = 64,
|
||||
):
|
||||
"""
|
||||
:param commands: 指令列表(指令, 延迟)
|
||||
:param max_height: 生成结构最大高度
|
||||
:return 成功与否,成功返回(True,未经过压缩的源,结构占用大小),失败返回(False,str失败原因)
|
||||
指令列表转换为用以生成 bdx 结构的字节码
|
||||
|
||||
Parameters
|
||||
------------
|
||||
commands: list[tuple[str, int]]
|
||||
指令列表,每个元素为 (指令, 延迟)
|
||||
max_height: int
|
||||
生成结构最大高度
|
||||
|
||||
Returns
|
||||
---------
|
||||
tuple[bool, bytes, int] or tuple[bool, str]
|
||||
成功与否,成功返回 (True, 未经过压缩的源, 结构占用大小),失败返回 (False, str失败原因)
|
||||
"""
|
||||
|
||||
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"""
|
||||
用以生成BDX结构文件的附加功能
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -15,6 +15,6 @@ Terms & Conditions: License.md in the root directory
|
||||
|
||||
|
||||
__all__ = ["to_BDX_file_in_score", "to_BDX_file_in_delay"]
|
||||
__author__ = (("金羿", "Eilles Wan"),)
|
||||
__author__ = (("金羿", "Eilles"),)
|
||||
|
||||
from .main import to_BDX_file_in_delay, to_BDX_file_in_score
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -13,11 +13,12 @@ Terms & Conditions: License.md in the root directory
|
||||
|
||||
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
import brotli
|
||||
|
||||
from ...main import MidiConvert
|
||||
from ...subclass import SingleCommand
|
||||
from ...subclass import MineCommand, ProgressBarStyle
|
||||
from ..bdx import (
|
||||
bdx_move,
|
||||
commands_to_BDX_bytes,
|
||||
@@ -26,12 +27,12 @@ from ..bdx import (
|
||||
y,
|
||||
z,
|
||||
)
|
||||
from ..main import ConvertConfig
|
||||
|
||||
|
||||
def to_BDX_file_in_score(
|
||||
midi_cvt: MidiConvert,
|
||||
data_cfg: ConvertConfig,
|
||||
dist_path: str,
|
||||
progressbar_style: Optional[ProgressBarStyle],
|
||||
scoreboard_name: str = "mscplay",
|
||||
auto_reset: bool = False,
|
||||
author: str = "Eilles",
|
||||
@@ -44,8 +45,10 @@ def to_BDX_file_in_score(
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
data_cfg: ConvertConfig 对象
|
||||
部分转换通用参数
|
||||
dist_path: str
|
||||
转换结果输出的目标路径
|
||||
progressbar_style: ProgressBarStyle 对象
|
||||
进度条对象
|
||||
scoreboard_name: str
|
||||
我的世界的计分板名称
|
||||
auto_reset: bool
|
||||
@@ -61,16 +64,14 @@ def to_BDX_file_in_score(
|
||||
"""
|
||||
|
||||
cmdlist, command_count, max_score = midi_cvt.to_command_list_in_score(
|
||||
scoreboard_name, data_cfg.volume_ratio, data_cfg.speed_multiplier
|
||||
scoreboard_name=scoreboard_name,
|
||||
)
|
||||
|
||||
if not os.path.exists(data_cfg.dist_path):
|
||||
os.makedirs(data_cfg.dist_path)
|
||||
if not os.path.exists(dist_path):
|
||||
os.makedirs(dist_path)
|
||||
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx")
|
||||
),
|
||||
os.path.abspath(os.path.join(dist_path, f"{midi_cvt.music_name}[score].bdx")),
|
||||
"w+",
|
||||
) as f:
|
||||
f.write("BD@")
|
||||
@@ -83,7 +84,7 @@ def to_BDX_file_in_score(
|
||||
midi_cvt.music_command_list
|
||||
+ (
|
||||
[
|
||||
SingleCommand(
|
||||
MineCommand(
|
||||
command="scoreboard players reset @a[scores={"
|
||||
+ scoreboard_name
|
||||
+ "="
|
||||
@@ -99,11 +100,9 @@ def to_BDX_file_in_score(
|
||||
max_height - 1,
|
||||
)
|
||||
|
||||
if data_cfg.progressbar_style:
|
||||
if progressbar_style:
|
||||
pgbBytes, pgbSize, pgbNowPos = commands_to_BDX_bytes(
|
||||
midi_cvt.form_progress_bar(
|
||||
max_score, scoreboard_name, data_cfg.progressbar_style
|
||||
),
|
||||
midi_cvt.form_progress_bar(max_score, scoreboard_name, progressbar_style),
|
||||
max_height - 1,
|
||||
)
|
||||
_bytes += pgbBytes
|
||||
@@ -118,9 +117,7 @@ def to_BDX_file_in_score(
|
||||
_bytes += cmdBytes
|
||||
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx")
|
||||
),
|
||||
os.path.abspath(os.path.join(dist_path, f"{midi_cvt.music_name}[score].bdx")),
|
||||
"ab+",
|
||||
) as f:
|
||||
f.write(brotli.compress(_bytes + b"XE"))
|
||||
@@ -130,7 +127,8 @@ def to_BDX_file_in_score(
|
||||
|
||||
def to_BDX_file_in_delay(
|
||||
midi_cvt: MidiConvert,
|
||||
data_cfg: ConvertConfig,
|
||||
dist_path: str,
|
||||
progressbar_style: Optional[ProgressBarStyle],
|
||||
player: str = "@a",
|
||||
author: str = "Eilles",
|
||||
max_height: int = 64,
|
||||
@@ -142,8 +140,10 @@ def to_BDX_file_in_delay(
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
data_cfg: ConvertConfig 对象
|
||||
部分转换通用参数
|
||||
dist_path: str
|
||||
转换结果输出的目标路径
|
||||
progressbar_style: ProgressBarStyle 对象
|
||||
进度条对象
|
||||
player: str
|
||||
玩家选择器,默认为`@a`
|
||||
author: str
|
||||
@@ -157,18 +157,14 @@ def to_BDX_file_in_delay(
|
||||
"""
|
||||
|
||||
cmdlist, max_delay = midi_cvt.to_command_list_in_delay(
|
||||
data_cfg.volume_ratio,
|
||||
data_cfg.speed_multiplier,
|
||||
player,
|
||||
player_selector=player,
|
||||
)[:2]
|
||||
|
||||
if not os.path.exists(data_cfg.dist_path):
|
||||
os.makedirs(data_cfg.dist_path)
|
||||
if not os.path.exists(dist_path):
|
||||
os.makedirs(dist_path)
|
||||
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx")
|
||||
),
|
||||
os.path.abspath(os.path.join(dist_path, f"{midi_cvt.music_name}[delay].bdx")),
|
||||
"w+",
|
||||
) as f:
|
||||
f.write("BD@")
|
||||
@@ -179,8 +175,8 @@ def to_BDX_file_in_delay(
|
||||
|
||||
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"
|
||||
if progressbar_style:
|
||||
scb_name = midi_cvt.music_name[:3] + "Pgb"
|
||||
_bytes += form_command_block_in_BDX_bytes(
|
||||
r"scoreboard objectives add {} dummy {}计".replace(r"{}", scb_name),
|
||||
1,
|
||||
@@ -195,7 +191,7 @@ def to_BDX_file_in_delay(
|
||||
)
|
||||
_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),
|
||||
midi_cvt.form_progress_bar(max_delay, scb_name, progressbar_style),
|
||||
max_height - 1,
|
||||
)
|
||||
_bytes += pgbBytes
|
||||
@@ -216,9 +212,7 @@ def to_BDX_file_in_delay(
|
||||
_bytes += cmdBytes
|
||||
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.bdx")
|
||||
),
|
||||
os.path.abspath(os.path.join(dist_path, f"{midi_cvt.music_name}[delay].bdx")),
|
||||
"ab+",
|
||||
) as f:
|
||||
f.write(brotli.compress(_bytes + b"XE"))
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -19,9 +19,22 @@ Terms & Conditions: License.md in the root directory
|
||||
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)))
|
||||
def bottem_side_length_of_smallest_square_bottom_box(
|
||||
_total_block_count: int, _max_height: int
|
||||
):
|
||||
"""
|
||||
给定结构的总方块数量和规定的最大高度,返回该结构应当构成的图形,在底面的外切正方形之边长
|
||||
|
||||
Parameters
|
||||
------------
|
||||
_total_block_count: int
|
||||
总方块数量
|
||||
_max_height: int
|
||||
规定的结构最大高度
|
||||
|
||||
Returns
|
||||
---------
|
||||
int
|
||||
外切正方形的边长
|
||||
"""
|
||||
return math.ceil(math.sqrt(math.ceil(_total_block_count / _max_height)))
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -16,91 +16,77 @@ Terms & Conditions: License.md in the root directory
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal, Tuple, Union
|
||||
# from dataclasses import dataclass
|
||||
# from typing import Literal, Tuple, Union
|
||||
|
||||
from ..subclass import DEFAULT_PROGRESSBAR_STYLE, ProgressBarStyle
|
||||
# from ..subclass import DEFAULT_PROGRESSBAR_STYLE, ProgressBarStyle
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class ConvertConfig:
|
||||
"""
|
||||
转换通用设置存储类
|
||||
"""
|
||||
# @dataclass(init=False)
|
||||
# class ConvertConfig: # 必定要改
|
||||
# """
|
||||
# 转换通用设置存储类
|
||||
# """
|
||||
|
||||
volume_ratio: float
|
||||
"""音量比例"""
|
||||
# progressbar_style: Union[ProgressBarStyle, None]
|
||||
# """进度条样式"""
|
||||
|
||||
speed_multiplier: float
|
||||
"""速度倍率"""
|
||||
# dist_path: str
|
||||
# """输出目录"""
|
||||
|
||||
progressbar_style: Union[ProgressBarStyle, None]
|
||||
"""进度条样式"""
|
||||
# def __init__(
|
||||
# self,
|
||||
# output_path: str,
|
||||
# progressbar: Union[bool, Tuple[str, Tuple[str, str]], ProgressBarStyle] = True,
|
||||
# ignore_progressbar_param_error: bool = False,
|
||||
# ):
|
||||
# """
|
||||
# 将已经转换好的数据内容指令载入MC可读格式
|
||||
|
||||
dist_path: str
|
||||
"""输出目录"""
|
||||
# Parameters
|
||||
# ----------
|
||||
# output_path: str
|
||||
# 生成内容的输出目录
|
||||
# volume: float
|
||||
# 音量比率,范围为(0,1],其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||
# speed: float
|
||||
# 速度倍率,注意:这里的速度指的是播放速度倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||
# progressbar: bool|tuple[str, Tuple[str,]]
|
||||
# 进度条,当此参数为 `True` 时使用默认进度条,为其他的**值为真**的参数时识别为进度条自定义参数,为其他**值为假**的时候不生成进度条
|
||||
|
||||
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
|
||||
# """输出目录"""
|
||||
|
||||
"""
|
||||
# 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.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
|
||||
"""进度条样式组"""
|
||||
# self.progressbar_style = None
|
||||
# """进度条样式组"""
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"""
|
||||
用以生成单个mcstructure文件的附加功能
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -18,7 +18,13 @@ __all__ = [
|
||||
"to_mcstructure_file_in_delay",
|
||||
"to_mcstructure_file_in_repeater",
|
||||
"to_mcstructure_file_in_score",
|
||||
"to_mcstructure_files_in_repeater_divided_by_instruments",
|
||||
]
|
||||
__author__ = (("金羿", "Eilles Wan"),)
|
||||
__author__ = (("金羿", "Eilles"),)
|
||||
|
||||
from .main import to_mcstructure_file_in_delay, to_mcstructure_file_in_repeater, to_mcstructure_file_in_score
|
||||
from .main import (
|
||||
to_mcstructure_file_in_delay,
|
||||
to_mcstructure_file_in_repeater,
|
||||
to_mcstructure_file_in_score,
|
||||
to_mcstructure_files_in_repeater_divided_by_instruments,
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -14,10 +14,8 @@ Terms & Conditions: License.md in the root directory
|
||||
import os
|
||||
from typing import Literal
|
||||
|
||||
# from ...exceptions import CommandFormatError
|
||||
from ...main import MidiConvert
|
||||
from ..main import ConvertConfig
|
||||
from ...subclass import SingleCommand
|
||||
from ...subclass import MineCommand
|
||||
from ..mcstructure import (
|
||||
COMPABILITY_VERSION_117,
|
||||
COMPABILITY_VERSION_119,
|
||||
@@ -28,7 +26,7 @@ from ..mcstructure import (
|
||||
|
||||
def to_mcstructure_file_in_delay(
|
||||
midi_cvt: MidiConvert,
|
||||
data_cfg: ConvertConfig,
|
||||
dist_path: str,
|
||||
player: str = "@a",
|
||||
max_height: int = 64,
|
||||
):
|
||||
@@ -39,8 +37,8 @@ def to_mcstructure_file_in_delay(
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
data_cfg: ConvertConfig 对象
|
||||
部分转换通用参数
|
||||
dist_path: str
|
||||
转换结果输出的目标路径
|
||||
player: str
|
||||
玩家选择器,默认为`@a`
|
||||
max_height: int
|
||||
@@ -58,22 +56,18 @@ def to_mcstructure_file_in_delay(
|
||||
)
|
||||
|
||||
cmd_list, max_delay = midi_cvt.to_command_list_in_delay(
|
||||
data_cfg.volume_ratio,
|
||||
data_cfg.speed_multiplier,
|
||||
player,
|
||||
player_selector=player,
|
||||
)[:2]
|
||||
|
||||
if not os.path.exists(data_cfg.dist_path):
|
||||
os.makedirs(data_cfg.dist_path)
|
||||
if not os.path.exists(dist_path):
|
||||
os.makedirs(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")
|
||||
),
|
||||
os.path.abspath(os.path.join(dist_path, f"{midi_cvt.music_name}[delay].mcstructure")),
|
||||
"wb+",
|
||||
) as f:
|
||||
struct.dump(f)
|
||||
@@ -83,7 +77,7 @@ def to_mcstructure_file_in_delay(
|
||||
|
||||
def to_mcstructure_file_in_score(
|
||||
midi_cvt: MidiConvert,
|
||||
data_cfg: ConvertConfig,
|
||||
dist_path: str,
|
||||
scoreboard_name: str = "mscplay",
|
||||
auto_reset: bool = False,
|
||||
max_height: int = 64,
|
||||
@@ -95,8 +89,8 @@ def to_mcstructure_file_in_score(
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
data_cfg: ConvertConfig 对象
|
||||
部分转换通用参数
|
||||
dist_path: str
|
||||
转换结果输出的目标路径
|
||||
scoreboard_name: str
|
||||
我的世界的计分板名称
|
||||
auto_reset: bool
|
||||
@@ -116,18 +110,17 @@ def to_mcstructure_file_in_score(
|
||||
)
|
||||
|
||||
cmd_list, cmd_count, max_delay = midi_cvt.to_command_list_in_score(
|
||||
scoreboard_name,
|
||||
data_cfg.volume_ratio,
|
||||
data_cfg.speed_multiplier,
|
||||
scoreboard_name=scoreboard_name,
|
||||
)
|
||||
|
||||
if not os.path.exists(data_cfg.dist_path):
|
||||
os.makedirs(data_cfg.dist_path)
|
||||
if not os.path.exists(dist_path):
|
||||
os.makedirs(dist_path)
|
||||
|
||||
struct, size, end_pos = commands_to_structure(
|
||||
midi_cvt.music_command_list+(
|
||||
midi_cvt.music_command_list
|
||||
+ (
|
||||
[
|
||||
SingleCommand(
|
||||
MineCommand(
|
||||
command="scoreboard players reset @a[scores={"
|
||||
+ scoreboard_name
|
||||
+ "="
|
||||
@@ -139,13 +132,13 @@ def to_mcstructure_file_in_score(
|
||||
]
|
||||
if auto_reset
|
||||
else []
|
||||
), max_height - 1, compability_version_=compability_ver
|
||||
),
|
||||
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")
|
||||
),
|
||||
os.path.abspath(os.path.join(dist_path, f"{midi_cvt.music_name}[score].mcstructure")),
|
||||
"wb+",
|
||||
) as f:
|
||||
struct.dump(f)
|
||||
@@ -155,7 +148,7 @@ def to_mcstructure_file_in_score(
|
||||
|
||||
def to_mcstructure_file_in_repeater(
|
||||
midi_cvt: MidiConvert,
|
||||
data_cfg: ConvertConfig,
|
||||
dist_path: str,
|
||||
player: str = "@a",
|
||||
axis_side: Literal["z+", "z-", "Z+", "Z-", "x+", "x-", "X+", "X-"] = "z+",
|
||||
basement_block: str = "concrete",
|
||||
@@ -167,8 +160,8 @@ def to_mcstructure_file_in_repeater(
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
data_cfg: ConvertConfig 对象
|
||||
部分转换通用参数
|
||||
dist_path: str
|
||||
转换结果输出的目标路径
|
||||
player: str
|
||||
玩家选择器,默认为`@a`
|
||||
axis_side: Literal["z+","z-","Z+","Z-","x+","x-","X+","X-"]
|
||||
@@ -188,13 +181,11 @@ def to_mcstructure_file_in_repeater(
|
||||
)
|
||||
|
||||
cmd_list, max_delay, max_multiple_cmd = midi_cvt.to_command_list_in_delay(
|
||||
data_cfg.volume_ratio,
|
||||
data_cfg.speed_multiplier,
|
||||
player,
|
||||
player_selector=player,
|
||||
)
|
||||
|
||||
if not os.path.exists(data_cfg.dist_path):
|
||||
os.makedirs(data_cfg.dist_path)
|
||||
if not os.path.exists(dist_path):
|
||||
os.makedirs(dist_path)
|
||||
|
||||
struct, size, end_pos = commands_to_redstone_delay_structure(
|
||||
cmd_list,
|
||||
@@ -206,11 +197,102 @@ def to_mcstructure_file_in_repeater(
|
||||
)
|
||||
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(data_cfg.dist_path, f"{midi_cvt.midi_music_name}.mcstructure")
|
||||
),
|
||||
os.path.abspath(os.path.join(dist_path, f"{midi_cvt.music_name}[repeater].mcstructure")),
|
||||
"wb+",
|
||||
) as f:
|
||||
struct.dump(f)
|
||||
|
||||
return size, max_delay
|
||||
|
||||
|
||||
def to_mcstructure_files_in_repeater_divided_by_instruments(
|
||||
midi_cvt: MidiConvert,
|
||||
dist_path: str,
|
||||
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对象
|
||||
dist_path: str
|
||||
转换结果输出的目标路径
|
||||
player: str
|
||||
玩家选择器,默认为`@a`
|
||||
axis_side: Literal["z+","z-","Z+","Z-","x+","x-","X+","X-"]
|
||||
生成结构的延展方向
|
||||
basement_block: str
|
||||
结构的基底方块
|
||||
|
||||
Returns
|
||||
-------
|
||||
int音乐总延迟
|
||||
"""
|
||||
|
||||
compability_ver = (
|
||||
COMPABILITY_VERSION_117
|
||||
if midi_cvt.enable_old_exe_format
|
||||
else COMPABILITY_VERSION_119
|
||||
)
|
||||
|
||||
cmd_dict, max_delay, max_multiple_cmd_count = (
|
||||
midi_cvt.to_command_list_in_delay_devided_by_instrument(
|
||||
player_selector=player,
|
||||
)
|
||||
)
|
||||
|
||||
if not os.path.exists(dist_path):
|
||||
os.makedirs(dist_path)
|
||||
|
||||
for inst, cmd_list in cmd_dict.items():
|
||||
struct, size, end_pos = commands_to_redstone_delay_structure(
|
||||
cmd_list,
|
||||
max_delay,
|
||||
max_multiple_cmd_count[inst],
|
||||
basement_block,
|
||||
axis_side,
|
||||
compability_version_=compability_ver,
|
||||
)
|
||||
|
||||
with open(
|
||||
os.path.abspath(
|
||||
os.path.join(
|
||||
dist_path,
|
||||
"{}[repeater-div]_{}.mcstructure".format(
|
||||
midi_cvt.music_name, inst.replace(".", "-")
|
||||
),
|
||||
)
|
||||
),
|
||||
"wb+",
|
||||
) as f:
|
||||
struct.dump(f)
|
||||
|
||||
return max_delay
|
||||
|
||||
|
||||
def to_mcstructure_file_in_blocks(
|
||||
midi_cvt: MidiConvert,
|
||||
dist_path: str,
|
||||
player: str = "@a",
|
||||
):
|
||||
"""
|
||||
将midi以方块形式转换为mcstructure结构文件
|
||||
|
||||
Parameters
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
dist_path: str
|
||||
转换结果输出的目标路径
|
||||
player: str
|
||||
玩家选择器,默认为`@a`
|
||||
|
||||
Returns
|
||||
-------
|
||||
int音乐总延迟
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -21,7 +21,7 @@ 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 ..subclass import MineCommand
|
||||
from .common import bottem_side_length_of_smallest_square_bottom_box
|
||||
|
||||
|
||||
@@ -58,6 +58,10 @@ COMPABILITY_VERSION_117: int = 17879555
|
||||
"""
|
||||
Minecraft 1.17 兼容版本号
|
||||
"""
|
||||
COMPABILITY_VERSION_121: int = 18168865
|
||||
"""
|
||||
Minecraft 1.21 兼容版本号
|
||||
"""
|
||||
|
||||
|
||||
def command_statevalue(axis_: Literal["x", "y", "z", "X", "Y", "Z"], forward_: bool):
|
||||
@@ -70,17 +74,25 @@ def form_note_block_in_NBT_struct(
|
||||
instrument: str = "note.harp",
|
||||
powered: bool = False,
|
||||
compability_version_number: int = COMPABILITY_VERSION_119,
|
||||
):
|
||||
"""生成音符盒方块
|
||||
:param note: `int`(0~24)
|
||||
) -> Block:
|
||||
"""
|
||||
生成音符盒方块
|
||||
|
||||
Parameters
|
||||
------------
|
||||
note: int (0~24)
|
||||
音符的音高
|
||||
:param coordinate: `tuple[int,int,int]`
|
||||
coordinate: tuple[int, int, int]
|
||||
此方块所在之相对坐标
|
||||
:param instrument: `str`
|
||||
instrument: str
|
||||
音符盒的乐器
|
||||
:param powered: `bool`
|
||||
powered: bool
|
||||
是否已被激活
|
||||
:return Block
|
||||
|
||||
Returns
|
||||
-------
|
||||
Block
|
||||
生成的方块对象
|
||||
"""
|
||||
|
||||
return Block(
|
||||
@@ -108,15 +120,26 @@ def form_repeater_in_NBT_struct(
|
||||
delay: int,
|
||||
facing: int,
|
||||
compability_version_number: int = COMPABILITY_VERSION_119,
|
||||
):
|
||||
"""生成中继器方块
|
||||
:param facing: 朝向:
|
||||
) -> Block:
|
||||
"""
|
||||
生成中继器方块
|
||||
|
||||
Parameters
|
||||
----------
|
||||
facing: int (0~3)
|
||||
朝向:
|
||||
Z- 北 0
|
||||
X- 东 1
|
||||
Z+ 南 2
|
||||
X+ 西 3
|
||||
:param delay: 0~3
|
||||
:return Block()"""
|
||||
delay: int (0~3)
|
||||
信号延迟
|
||||
|
||||
Returns
|
||||
-------
|
||||
Block
|
||||
生成的方块对象
|
||||
"""
|
||||
|
||||
return Block(
|
||||
"minecraft",
|
||||
@@ -141,50 +164,58 @@ def form_command_block_in_NBT_struct(
|
||||
executeOnFirstTick: bool = False,
|
||||
trackOutput: bool = True,
|
||||
compability_version_number: int = COMPABILITY_VERSION_119,
|
||||
):
|
||||
) -> Block:
|
||||
"""
|
||||
使用指定项目返回指定的指令方块结构
|
||||
:param command: `str`
|
||||
使用指定参数生成指令方块
|
||||
|
||||
|
||||
Parameters
|
||||
----------
|
||||
command: str
|
||||
指令
|
||||
:param coordinate: `tuple[int,int,int]`
|
||||
coordinate: tuple[int,int,int]
|
||||
此方块所在之相对坐标
|
||||
:param particularValue:
|
||||
particularValue: int
|
||||
方块特殊值,即朝向
|
||||
:0 下 无条件
|
||||
:1 上 无条件
|
||||
:2 z轴负方向 无条件
|
||||
:3 z轴正方向 无条件
|
||||
:4 x轴负方向 无条件
|
||||
:5 x轴正方向 无条件
|
||||
:6 下 无条件
|
||||
:7 下 无条件
|
||||
|
||||
:8 下 有条件
|
||||
:9 上 有条件
|
||||
:10 z轴负方向 有条件
|
||||
:11 z轴正方向 有条件
|
||||
:12 x轴负方向 有条件
|
||||
:13 x轴正方向 有条件
|
||||
:14 下 有条件
|
||||
:14 下 有条件
|
||||
: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`
|
||||
impluse: int (0|1|2)
|
||||
方块类型
|
||||
0脉冲 1循环 2连锁
|
||||
:param condition: `bool`
|
||||
0脉冲 1循环 2连锁
|
||||
condition: bool
|
||||
是否有条件
|
||||
:param alwaysRun: `bool`
|
||||
alwaysRun: bool
|
||||
是否始终执行
|
||||
:param tickDelay: `int`
|
||||
tickDelay: int
|
||||
执行延时
|
||||
:param customName: `str`
|
||||
customName: str
|
||||
悬浮字
|
||||
:param executeOnFirstTick: `bool`
|
||||
首刻执行(循环指令方块是否激活后立即执行,若为False,则从激活时起延迟后第一次执行)
|
||||
:param trackOutput: `bool`
|
||||
是否输出
|
||||
executeOnFirstTick: bool
|
||||
是否启用首刻执行(循环指令方块是否激活后立即执行,若为False,则从激活时起延迟后第一次执行)
|
||||
trackOutput: bool
|
||||
是否启用命令方块输出
|
||||
compability_version_number: int
|
||||
版本兼容代号
|
||||
|
||||
:return:str
|
||||
Returns
|
||||
-------
|
||||
Block
|
||||
生成的方块对象
|
||||
"""
|
||||
|
||||
return Block(
|
||||
@@ -209,7 +240,9 @@ def form_command_block_in_NBT_struct(
|
||||
"SuccessCount": 0,
|
||||
"TickDelay": tickDelay,
|
||||
"TrackOutput": trackOutput,
|
||||
"Version": 25,
|
||||
"Version": (
|
||||
25 if compability_version_number <= COMPABILITY_VERSION_119 else 43
|
||||
),
|
||||
"auto": alwaysRun,
|
||||
"conditionMet": False, # 是否已经满足条件
|
||||
"conditionalMode": condition,
|
||||
@@ -226,14 +259,24 @@ def form_command_block_in_NBT_struct(
|
||||
|
||||
|
||||
def commands_to_structure(
|
||||
commands: List[SingleCommand],
|
||||
commands: List[MineCommand],
|
||||
max_height: int = 64,
|
||||
compability_version_: int = COMPABILITY_VERSION_119,
|
||||
):
|
||||
"""
|
||||
:param commands: 指令列表
|
||||
:param max_height: 生成结构最大高度
|
||||
:return 结构类,结构占用大小,终点坐标
|
||||
由指令列表生成(纯指令方块)结构
|
||||
|
||||
Parameters
|
||||
------------
|
||||
commands: list
|
||||
指令列表
|
||||
max_height: int
|
||||
生成结构最大高度
|
||||
|
||||
Returns
|
||||
---------
|
||||
Structure, tuple[int, int, int], tuple[int, int, int]
|
||||
结构类, 结构占用大小, 终点坐标
|
||||
"""
|
||||
|
||||
_sideLength = bottem_side_length_of_smallest_square_bottom_box(
|
||||
@@ -313,7 +356,7 @@ def commands_to_structure(
|
||||
|
||||
|
||||
def commands_to_redstone_delay_structure(
|
||||
commands: List[SingleCommand],
|
||||
commands: List[MineCommand],
|
||||
delay_length: int,
|
||||
max_multicmd_length: int,
|
||||
base_block: str = "concrete",
|
||||
@@ -321,12 +364,25 @@ def commands_to_redstone_delay_structure(
|
||||
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 结构类,结构占用大小,终点坐标
|
||||
由指令列表生成由红石中继器延迟的结构
|
||||
|
||||
Parameters
|
||||
------------
|
||||
commands: list
|
||||
指令列表
|
||||
delay_length: int
|
||||
延时总长
|
||||
max_multicmd_length: int
|
||||
最大同时播放的音符数量
|
||||
base_block: Block
|
||||
生成结构的基底方块
|
||||
axis_: str
|
||||
生成结构的延展方向
|
||||
|
||||
Returns
|
||||
---------
|
||||
Structure, tuple[int, int, int], tuple[int, int, int]
|
||||
结构类, 结构占用大小, 终点坐标
|
||||
"""
|
||||
if axis_ in ["z+", "Z+"]:
|
||||
extensioon_direction = z
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -18,7 +18,7 @@ Terms & Conditions: License.md in the root directory
|
||||
|
||||
from ..exceptions import NotDefineProgramError, ZeroSpeedError
|
||||
from ..main import MidiConvert
|
||||
from ..subclass import SingleCommand
|
||||
from ..subclass import MineCommand
|
||||
from ..utils import inst_to_sould_with_deviation, perc_inst_to_soundID_withX
|
||||
|
||||
# 你以为写完了吗?其实并没有
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -15,4 +15,4 @@ Terms & Conditions: License.md in the root directory
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
import nbtschematic
|
||||
# import nbtschematic
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"""
|
||||
用以生成Schematic结构的附加功能
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -16,7 +16,7 @@ Terms & Conditions: License.md in the root directory
|
||||
|
||||
__all__ = [
|
||||
]
|
||||
__author__ = (("金羿", "Eilles Wan"),)
|
||||
__author__ = (("金羿", "Eilles"),)
|
||||
|
||||
from .main import *
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
|
||||
@@ -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))
|
||||
@@ -2,8 +2,8 @@
|
||||
"""
|
||||
用以启动WebSocket服务器播放的附加功能
|
||||
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -15,7 +15,7 @@ Terms & Conditions: License.md in the root directory
|
||||
|
||||
|
||||
__all__ = []
|
||||
__author__ = (("金羿", "Eilles Wan"),)
|
||||
__author__ = (("金羿", "Eilles"),)
|
||||
|
||||
from .main import *
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -11,19 +11,132 @@ Terms & Conditions: License.md in the root directory
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
import asyncio
|
||||
import time
|
||||
import uuid
|
||||
from typing import List, Literal, Optional, Tuple
|
||||
|
||||
import fcwslib
|
||||
|
||||
from ...main import MidiConvert
|
||||
|
||||
from ..main import ConvertConfig
|
||||
from ...subclass import SingleCommand
|
||||
from ...subclass import MineCommand, ProgressBarStyle
|
||||
|
||||
|
||||
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)
|
||||
def to_websocket_server(
|
||||
midi_cvt_lst: List[MidiConvert],
|
||||
server_dist: str,
|
||||
server_port: int,
|
||||
progressbar_style: Optional[ProgressBarStyle],
|
||||
) -> None:
|
||||
"""
|
||||
将midi以延迟播放器形式转换为mcstructure结构文件后打包成附加包,并在附加包中生成相应地导入函数
|
||||
|
||||
Parameters
|
||||
----------
|
||||
midi_cvt: List[MidiConvert]
|
||||
一组用于转换的MidiConvert对象
|
||||
server_dist: str
|
||||
WebSocket播放服务器开启地址
|
||||
server_port: str
|
||||
WebSocket播放服务器开启端口
|
||||
progressbar_style: ProgressBarStyle 对象
|
||||
进度条对象
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
"""
|
||||
|
||||
replacement = str(uuid.uuid4())
|
||||
|
||||
musics = dict(
|
||||
[
|
||||
(k.music_name, k.to_command_list_in_delay(replacement)[:2])
|
||||
for k in midi_cvt_lst
|
||||
]
|
||||
)
|
||||
|
||||
class Plugin(fcwslib.Plugin):
|
||||
async def on_connect(self) -> None:
|
||||
print("已成功获连接")
|
||||
await self.send_command("list", callback=self.cmd_feedback)
|
||||
await self.subscribe("PlayerMessage", callback=self.player_message)
|
||||
|
||||
async def on_disconnect(self) -> None:
|
||||
print("连接已然终止")
|
||||
await self.disconnect()
|
||||
|
||||
async def on_receive(self, response) -> None:
|
||||
print("已收取非已知列回复 {}".format(response))
|
||||
|
||||
async def cmd_feedback(self, response) -> None:
|
||||
print("已收取指令执行回复 {}".format(response))
|
||||
|
||||
async def player_message(self, response) -> None:
|
||||
print("已收取玩家事件回复 {}".format(response))
|
||||
if response["body"]["message"].startswith(("。播放", ".play")):
|
||||
whom_to_play: str = response["body"]["sender"]
|
||||
music_to_play: str = (
|
||||
response["body"]["message"]
|
||||
.replace("。播放", "")
|
||||
.replace(".play", "")
|
||||
.strip()
|
||||
)
|
||||
if music_to_play in musics.keys():
|
||||
self.check_play = True
|
||||
delay_of_now = 0
|
||||
now_played_cmd = 0
|
||||
_time = time.time()
|
||||
for i in range(musics[music_to_play][1]):
|
||||
if not self.check_play:
|
||||
break
|
||||
await asyncio.sleep((0.05 - (time.time() - _time)) % 0.05)
|
||||
_time = time.time()
|
||||
if progressbar_style:
|
||||
await self.send_command(
|
||||
"title {} actionbar {}".format(
|
||||
whom_to_play,
|
||||
progressbar_style.play_output(
|
||||
played_delays=i,
|
||||
total_delays=musics[music_to_play][1],
|
||||
music_name=music_to_play,
|
||||
),
|
||||
),
|
||||
callback=self.cmd_feedback,
|
||||
)
|
||||
delay_of_now += 1
|
||||
if (
|
||||
delay_of_now
|
||||
>= (cmd := musics[music_to_play][0][now_played_cmd]).delay
|
||||
):
|
||||
await self.send_command(
|
||||
cmd.command_text.replace(replacement, whom_to_play),
|
||||
callback=self.cmd_feedback,
|
||||
)
|
||||
now_played_cmd += 1
|
||||
delay_of_now = 0
|
||||
|
||||
else:
|
||||
await self.send_command(
|
||||
"tellraw {} {}{}{}".format(
|
||||
whom_to_play,
|
||||
r'{"rawtext":[{"text":"§c§l所选歌曲',
|
||||
music_to_play,
|
||||
'无法播放:播放列表不存在之"}]}',
|
||||
),
|
||||
callback=self.cmd_feedback,
|
||||
)
|
||||
elif response["body"]["message"].startswith(
|
||||
("。停止播放", ".stopplay", ".stoplay")
|
||||
):
|
||||
self.check_play = False
|
||||
|
||||
elif response["body"]["message"].startswith(
|
||||
("。终止连接", ".terminate", ".endconnection")
|
||||
):
|
||||
await self.disconnect()
|
||||
|
||||
server = fcwslib.Server(server=server_dist, port=server_port, debug_mode=True)
|
||||
server.add_plugin(Plugin)
|
||||
asyncio.run(server.run_forever())
|
||||
|
||||
@@ -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,12 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
存储许多非主要的相关类
|
||||
存储音·创附属子类
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -16,134 +16,480 @@ Terms & Conditions: License.md in the root directory
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
from math import sin, cos, asin, radians, degrees, sqrt, atan
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
from .types import Optional, Any, List, Mapping
|
||||
from typing import Optional, Any, List, Tuple, Union, Dict, Sequence
|
||||
|
||||
from .constants import MC_PERCUSSION_INSTRUMENT_LIST
|
||||
from .constants import MC_PITCHED_INSTRUMENT_LIST
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class SingleNote:
|
||||
class MineNote:
|
||||
"""存储单个音符的类"""
|
||||
|
||||
instrument: int
|
||||
"""乐器编号"""
|
||||
sound_name: str
|
||||
"""乐器ID"""
|
||||
|
||||
note: int
|
||||
"""音符编号"""
|
||||
note_pitch: int
|
||||
"""midi音高"""
|
||||
|
||||
velocity: int
|
||||
"""力度/响度"""
|
||||
"""力度"""
|
||||
|
||||
start_time: int
|
||||
"""开始之时 ms"""
|
||||
start_tick: int
|
||||
"""开始之时 命令刻"""
|
||||
|
||||
duration: int
|
||||
"""音符持续时间 ms"""
|
||||
"""音符持续时间 命令刻"""
|
||||
|
||||
track_no: int
|
||||
"""音符所处的音轨"""
|
||||
high_precision_time: int
|
||||
"""高精度开始时间偏量 1/1250 秒"""
|
||||
|
||||
percussive: bool
|
||||
"""是否为打击乐器"""
|
||||
"""是否作为打击乐器启用"""
|
||||
|
||||
extra_info: Any
|
||||
sound_distance: float
|
||||
"""声源距离 方块"""
|
||||
|
||||
sound_azimuth: Tuple[float, float]
|
||||
"""声源方位 角度"""
|
||||
|
||||
extra_info: Dict[str, Any]
|
||||
"""你觉得放什么好?"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
instrument: int,
|
||||
pitch: int,
|
||||
velocity: int,
|
||||
startime: int,
|
||||
lastime: int,
|
||||
track_number: int = 0,
|
||||
mc_sound_name: str,
|
||||
midi_pitch: Optional[int],
|
||||
midi_velocity: int,
|
||||
start_time: int,
|
||||
last_time: int,
|
||||
mass_precision_time: int = 0,
|
||||
is_percussion: Optional[bool] = None,
|
||||
extra_information: Any = None,
|
||||
distance: Optional[float] = None,
|
||||
azimuth: Optional[Tuple[float, float]] = None,
|
||||
extra_information: Dict[str, Any] = {},
|
||||
):
|
||||
"""用于存储单个音符的类
|
||||
: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
|
||||
"""音符所处的音轨"""
|
||||
"""
|
||||
用于存储单个音符的类
|
||||
|
||||
Parameters
|
||||
------------
|
||||
mc_sound_name: str
|
||||
《我的世界》声音ID
|
||||
midi_pitch: int
|
||||
midi音高
|
||||
midi_velocity: int
|
||||
midi响度(力度)
|
||||
start_time: int
|
||||
开始之时(命令刻)
|
||||
注:此处的时间是用从乐曲开始到当前的刻数
|
||||
last_time: int
|
||||
音符延续时间(命令刻)
|
||||
mass_precision_time: int
|
||||
高精度的开始时间偏移量(1/1250秒)
|
||||
is_percussion: bool
|
||||
是否作为打击乐器
|
||||
distance: float
|
||||
发声源距离玩家的距离(半径 `r`)
|
||||
注:距离越近,音量越高,默认为 0。此参数可以与音量成某种函数关系。
|
||||
azimuth: tuple[float, float]
|
||||
声源方位
|
||||
注:此参数为tuple,包含两个元素,分别表示:
|
||||
`rV` 发声源在竖直(上下)轴上,从玩家视角正前方开始,向顺时针旋转的角度
|
||||
`rH` 发声源在水平(左右)轴上,从玩家视角正前方开始,向上(到达玩家正上方顶点后变为向下,以此类推的旋转)旋转的角度
|
||||
extra_information: Dict[str, Any]
|
||||
附加信息,尽量存储为字典
|
||||
|
||||
Returns
|
||||
---------
|
||||
MineNote 类
|
||||
"""
|
||||
self.sound_name: str = mc_sound_name
|
||||
"""乐器ID"""
|
||||
self.note_pitch: int = 66 if midi_pitch is None else midi_pitch
|
||||
"""midi音高"""
|
||||
self.velocity: int = midi_velocity
|
||||
"""响度(力度)"""
|
||||
self.start_tick: int = start_time
|
||||
"""开始之时 命令刻"""
|
||||
self.duration: int = last_time
|
||||
"""音符持续时间 命令刻"""
|
||||
self.high_precision_time: int = mass_precision_time
|
||||
"""高精度开始时间偏量 0.4 毫秒"""
|
||||
|
||||
self.percussive = (
|
||||
(is_percussion in MC_PERCUSSION_INSTRUMENT_LIST)
|
||||
(mc_sound_name not in MC_PITCHED_INSTRUMENT_LIST)
|
||||
if (is_percussion is None)
|
||||
else is_percussion
|
||||
)
|
||||
"""是否为打击乐器"""
|
||||
|
||||
self.extra_info = extra_information
|
||||
self.sound_azimuth = (azimuth[0] % 360, azimuth[1] % 360) if azimuth else (0, 0)
|
||||
"""声源方位"""
|
||||
|
||||
@property
|
||||
def inst(self) -> int:
|
||||
"""乐器编号"""
|
||||
return self.instrument
|
||||
# 如果指定为零,那么为零,但如果不指定或者指定为负数,则为 0.01 的距离
|
||||
self.sound_distance = (
|
||||
(16 if distance > 16 else (distance if distance >= 0 else 0.01))
|
||||
if distance is not None
|
||||
else 0.01
|
||||
)
|
||||
"""声源距离"""
|
||||
|
||||
@inst.setter
|
||||
def inst(self, inst_: int):
|
||||
self.instrument = inst_
|
||||
self.extra_info = extra_information if extra_information else {}
|
||||
|
||||
@property
|
||||
def pitch(self) -> int:
|
||||
"""音符编号"""
|
||||
return self.note
|
||||
@classmethod
|
||||
def from_traditional(
|
||||
cls,
|
||||
mc_sound_name: str,
|
||||
midi_pitch: Optional[int],
|
||||
midi_velocity: int,
|
||||
start_time: int,
|
||||
last_time: int,
|
||||
mass_precision_time: int = 0,
|
||||
is_percussion: Optional[bool] = None,
|
||||
displacement: Optional[Tuple[float, float, float]] = None,
|
||||
extra_information: Optional[Any] = None,
|
||||
):
|
||||
"""
|
||||
从传统音像位移格式传参,写入用于存储单个音符的类
|
||||
|
||||
# @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)
|
||||
Parameters
|
||||
------------
|
||||
mc_sound_name: str
|
||||
《我的世界》声音ID
|
||||
midi_pitch: int
|
||||
midi音高
|
||||
midi_velocity: int
|
||||
midi响度(力度)
|
||||
start_time: int
|
||||
开始之时(命令刻)
|
||||
注:此处的时间是用从乐曲开始到当前的刻数
|
||||
last_time: int
|
||||
音符延续时间(命令刻)
|
||||
mass_precision_time: int
|
||||
高精度的开始时间偏移量(1/1250秒)
|
||||
is_percussion: bool
|
||||
是否作为打击乐器
|
||||
displacement: tuple[float, float, float]
|
||||
声像位移
|
||||
extra_information: Any
|
||||
附加信息,尽量为字典。
|
||||
|
||||
def set_info(self, sth: Any):
|
||||
"""设置附加信息"""
|
||||
self.extra_info = sth
|
||||
Returns
|
||||
---------
|
||||
MineNote 类
|
||||
"""
|
||||
|
||||
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 "",
|
||||
if displacement is None:
|
||||
displacement = (0, 0, 0)
|
||||
r = 0
|
||||
alpha_v = 0
|
||||
beta_h = 0
|
||||
else:
|
||||
r = sqrt(displacement[0] ** 2 + displacement[1] ** 2 + displacement[2] ** 2)
|
||||
if r == 0:
|
||||
alpha_v = 0
|
||||
beta_h = 0
|
||||
else:
|
||||
beta_h = round(degrees(asin(displacement[1] / r)), 8)
|
||||
if displacement[2] == 0:
|
||||
alpha_v = -90 if displacement[0] > 0 else 90
|
||||
else:
|
||||
alpha_v = round(
|
||||
degrees(atan(-displacement[0] / displacement[2])), 8
|
||||
)
|
||||
|
||||
return cls(
|
||||
mc_sound_name=mc_sound_name,
|
||||
midi_pitch=midi_pitch,
|
||||
midi_velocity=midi_velocity,
|
||||
start_time=start_time,
|
||||
last_time=last_time,
|
||||
mass_precision_time=mass_precision_time,
|
||||
is_percussion=is_percussion,
|
||||
distance=r,
|
||||
azimuth=(alpha_v, beta_h),
|
||||
extra_information=(
|
||||
(
|
||||
extra_information
|
||||
if isinstance(extra_information, dict)
|
||||
else {"EXTRA_INFO": extra_information}
|
||||
)
|
||||
if extra_information
|
||||
else {}
|
||||
),
|
||||
)
|
||||
|
||||
def __tuple__(self):
|
||||
@property
|
||||
def position_displacement(self) -> Tuple[float, float, float]:
|
||||
"""声像位移"""
|
||||
dk1 = self.sound_distance * round(cos(radians(self.sound_azimuth[1])), 8)
|
||||
return (
|
||||
-dk1 * round(sin(radians(self.sound_azimuth[0])), 8),
|
||||
self.sound_distance * round(sin(radians(self.sound_azimuth[1])), 8),
|
||||
dk1 * round(cos(radians(self.sound_azimuth[0])), 8),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def decode(cls, code_buffer: bytes, is_high_time_precision: bool = True):
|
||||
"""自字节码析出 MineNote 类"""
|
||||
group_1 = int.from_bytes(code_buffer[:6], "big")
|
||||
percussive_ = bool(group_1 & 0b1)
|
||||
duration_ = (group_1 := group_1 >> 1) & 0b11111111111111111
|
||||
start_tick_ = (group_1 := group_1 >> 17) & 0b11111111111111111
|
||||
note_pitch_ = (group_1 := group_1 >> 17) & 0b1111111
|
||||
sound_name_length = group_1 >> 7
|
||||
|
||||
if code_buffer[6] & 0b1:
|
||||
distance_ = (
|
||||
code_buffer[8 + sound_name_length]
|
||||
if is_high_time_precision
|
||||
else code_buffer[7 + sound_name_length]
|
||||
) / 15
|
||||
|
||||
group_2 = int.from_bytes(
|
||||
(
|
||||
code_buffer[9 + sound_name_length : 14 + sound_name_length]
|
||||
if is_high_time_precision
|
||||
else code_buffer[8 + sound_name_length : 13 + sound_name_length]
|
||||
),
|
||||
"big",
|
||||
)
|
||||
azimuth_ = ((group_2 >> 20) / 2912, (group_2 & 0xFFFFF) / 2912)
|
||||
|
||||
else:
|
||||
distance_ = 0
|
||||
azimuth_ = (0, 0)
|
||||
|
||||
try:
|
||||
return cls(
|
||||
mc_sound_name=(
|
||||
o := (
|
||||
code_buffer[8 : 8 + sound_name_length]
|
||||
if is_high_time_precision
|
||||
else code_buffer[7 : 7 + sound_name_length]
|
||||
)
|
||||
).decode(encoding="GB18030"),
|
||||
midi_pitch=note_pitch_,
|
||||
midi_velocity=code_buffer[6] >> 1,
|
||||
start_time=start_tick_,
|
||||
last_time=duration_,
|
||||
mass_precision_time=code_buffer[7] if is_high_time_precision else 0,
|
||||
is_percussion=percussive_,
|
||||
distance=distance_,
|
||||
azimuth=azimuth_,
|
||||
)
|
||||
except:
|
||||
print(code_buffer, "\n", o)
|
||||
raise
|
||||
|
||||
def encode(
|
||||
self, is_displacement_included: bool = True, is_high_time_precision: bool = True
|
||||
) -> bytes:
|
||||
"""
|
||||
将数据打包为字节码
|
||||
|
||||
Parameters
|
||||
------------
|
||||
is_displacement_included: bool
|
||||
是否包含声像偏移数据,默认为**是**
|
||||
is_high_time_precision: bool
|
||||
是否启用高精度,默认为**是**
|
||||
|
||||
Returns
|
||||
---------
|
||||
bytes
|
||||
打包好的字节码
|
||||
"""
|
||||
|
||||
# MineNote 的字节码共有三个顺次版本分别如下
|
||||
|
||||
# 字符串长度 6 位 支持到 63
|
||||
# note_pitch 7 位 支持到 127
|
||||
# start_tick 17 位 支持到 131071 即 109.22583 分钟 合 1.8204305 小时
|
||||
# duration 17 位 支持到 131071 即 109.22583 分钟 合 1.8204305 小时
|
||||
# percussive 长度 1 位 支持到 1
|
||||
# 共 48 位 合 6 字节
|
||||
# +++
|
||||
# velocity 长度 7 位 支持到 127
|
||||
# is_displacement_included 长度 1 位 支持到 1
|
||||
# 共 8 位 合 1 字节
|
||||
# +++
|
||||
# (在第二版中已舍弃)
|
||||
# track_no 长度 8 位 支持到 255 合 1 字节
|
||||
# (在第二版中新增)
|
||||
# high_time_precision(可选)长度 8 位 支持到 255 合 1 字节 支持 1/1250 秒
|
||||
# +++
|
||||
# sound_name 长度最多 63 支持到 31 个中文字符 或 63 个西文字符
|
||||
# 第一版编码: UTF-8
|
||||
# 第二版编码: GB18030
|
||||
# +++
|
||||
# (在第三版中已废弃)
|
||||
# position_displacement 每个元素长 16 位 合 2 字节
|
||||
# 共 48 位 合 6 字节 支持存储三位小数和两位整数,其值必须在 [0, 65.535] 之间
|
||||
# (在第三版中新增)
|
||||
# sound_distance 8 位 支持到 255 即 16 格 合 1 字节(按值放大 15 倍存储,精度可达 1 / 15)
|
||||
# sound_azimuth 每个元素长 20 位 共 40 位 合 5 字节。每个值放大 2912 倍存储,即支持到 360.08756868131866 度,精度同理
|
||||
|
||||
return (
|
||||
(
|
||||
(
|
||||
(
|
||||
(
|
||||
(
|
||||
(
|
||||
(
|
||||
(
|
||||
len(
|
||||
r := self.sound_name.encode(
|
||||
encoding="GB18030"
|
||||
)
|
||||
)
|
||||
<< 7
|
||||
)
|
||||
+ self.note_pitch
|
||||
)
|
||||
<< 17
|
||||
)
|
||||
+ self.start_tick
|
||||
)
|
||||
<< 17
|
||||
)
|
||||
+ self.duration
|
||||
)
|
||||
<< 1
|
||||
)
|
||||
+ self.percussive
|
||||
).to_bytes(6, "big")
|
||||
+ ((self.velocity << 1) + is_displacement_included).to_bytes(1, "big")
|
||||
# + self.track_no.to_bytes(1, "big")
|
||||
+ (
|
||||
self.high_precision_time.to_bytes(1, "big")
|
||||
if is_high_time_precision
|
||||
else b""
|
||||
)
|
||||
+ r
|
||||
+ (
|
||||
(
|
||||
round(self.sound_distance * 15).to_bytes(1, "big")
|
||||
+ (
|
||||
(round(self.sound_azimuth[0] * 2912) << 20)
|
||||
+ round(self.sound_azimuth[1] * 2912)
|
||||
).to_bytes(5, "big")
|
||||
)
|
||||
if is_displacement_included
|
||||
else b""
|
||||
)
|
||||
)
|
||||
|
||||
def set_info(self, key: Union[str, Sequence[str]], value: Any):
|
||||
"""设置附加信息"""
|
||||
if isinstance(key, str):
|
||||
self.extra_info[key] = value
|
||||
elif (
|
||||
isinstance(key, Sequence)
|
||||
and isinstance(value, Sequence)
|
||||
and (k := len(key)) == len(value)
|
||||
):
|
||||
for i in range(k):
|
||||
self.extra_info[key[i]] = value[i]
|
||||
else:
|
||||
# 提供简单报错就行了,如果放一堆 if 语句,降低处理速度
|
||||
raise TypeError("参数类型错误;键:`{}` 值:`{}`".format(key, value))
|
||||
|
||||
def get_info(self, key: str) -> Any:
|
||||
"""获取附加信息"""
|
||||
if key in self.extra_info:
|
||||
return self.extra_info[key]
|
||||
elif "EXTRA_INFO" in self.extra_info:
|
||||
if (
|
||||
isinstance(self.extra_info["EXTRA_INFO"], dict)
|
||||
and key in self.extra_info["EXTRA_INFO"]
|
||||
):
|
||||
return self.extra_info["EXTRA_INFO"].get(key)
|
||||
else:
|
||||
return self.extra_info["EXTRA_INFO"]
|
||||
else:
|
||||
return None
|
||||
|
||||
def stringize(
|
||||
self, include_displacement: bool = False, include_extra_data: bool = False
|
||||
) -> str:
|
||||
return (
|
||||
"{}Note(Instrument = {}, {}Velocity = {}, StartTick = {}, Duration = {}{}".format(
|
||||
"Percussive" if self.percussive else "",
|
||||
self.sound_name,
|
||||
"" if self.percussive else "NotePitch = {}, ".format(self.note_pitch),
|
||||
self.velocity,
|
||||
self.start_tick,
|
||||
self.duration,
|
||||
)
|
||||
+ (
|
||||
", SoundDistance = `r`{}, SoundAzimuth = (`αV`{}, `βH`{})".format(
|
||||
self.sound_distance, *self.sound_azimuth
|
||||
)
|
||||
if include_displacement
|
||||
else ""
|
||||
)
|
||||
+ (", ExtraData = {}".format(self.extra_info) if include_extra_data else "")
|
||||
+ ")"
|
||||
)
|
||||
|
||||
def tuplize(self, is_displacement: bool = False):
|
||||
tuplized = self.__tuple__()
|
||||
return tuplized[:-2] + (tuplized[-2:] if is_displacement else ())
|
||||
|
||||
def __list__(self) -> List:
|
||||
return (
|
||||
[
|
||||
self.percussive,
|
||||
self.sound_name,
|
||||
self.velocity,
|
||||
self.start_tick,
|
||||
self.duration,
|
||||
self.sound_distance,
|
||||
self.sound_azimuth,
|
||||
]
|
||||
if self.percussive
|
||||
else [
|
||||
self.percussive,
|
||||
self.sound_name,
|
||||
self.note_pitch,
|
||||
self.velocity,
|
||||
self.start_tick,
|
||||
self.duration,
|
||||
self.sound_distance,
|
||||
self.sound_azimuth,
|
||||
]
|
||||
)
|
||||
|
||||
def __tuple__(
|
||||
self,
|
||||
) -> Union[
|
||||
Tuple[bool, str, int, int, int, int, float, Tuple[float, float]],
|
||||
Tuple[bool, str, int, int, int, float, Tuple[float, float]],
|
||||
]:
|
||||
return (
|
||||
(
|
||||
self.percussive,
|
||||
self.inst,
|
||||
self.sound_name,
|
||||
self.velocity,
|
||||
self.start_time,
|
||||
self.start_tick,
|
||||
self.duration,
|
||||
self.track_no,
|
||||
self.sound_distance,
|
||||
self.sound_azimuth,
|
||||
)
|
||||
if self.percussive
|
||||
else (
|
||||
self.percussive,
|
||||
self.inst,
|
||||
self.note,
|
||||
self.sound_name,
|
||||
self.note_pitch,
|
||||
self.velocity,
|
||||
self.start_time,
|
||||
self.start_tick,
|
||||
self.duration,
|
||||
self.track_no,
|
||||
self.sound_distance,
|
||||
self.sound_azimuth,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -151,32 +497,36 @@ class SingleNote:
|
||||
return (
|
||||
{
|
||||
"Percussive": self.percussive,
|
||||
"Instrument": self.inst,
|
||||
"Instrument": self.sound_name,
|
||||
"Velocity": self.velocity,
|
||||
"StartTime": self.start_time,
|
||||
"StartTick": self.start_tick,
|
||||
"Duration": self.duration,
|
||||
"Track": self.track_no,
|
||||
"SoundDistance": self.sound_distance,
|
||||
"SoundAzimuth": self.sound_azimuth,
|
||||
"ExtraData": self.extra_info,
|
||||
}
|
||||
if self.percussive
|
||||
else {
|
||||
"Percussive": self.percussive,
|
||||
"Instrument": self.inst,
|
||||
"Pitch": self.note,
|
||||
"Instrument": self.sound_name,
|
||||
"Pitch": self.note_pitch,
|
||||
"Velocity": self.velocity,
|
||||
"StartTime": self.start_time,
|
||||
"StartTick": self.start_tick,
|
||||
"Duration": self.duration,
|
||||
"Track": self.track_no,
|
||||
"SoundDistance": self.sound_distance,
|
||||
"SoundAzimuth": self.sound_azimuth,
|
||||
"ExtraData": self.extra_info,
|
||||
}
|
||||
)
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
if not isinstance(other, self.__class__):
|
||||
return False
|
||||
return self.__str__() == other.__str__()
|
||||
return self.tuplize() == other.tuplize()
|
||||
|
||||
|
||||
@dataclass(init=False)
|
||||
class SingleCommand:
|
||||
class MineCommand:
|
||||
"""存储单个指令的类"""
|
||||
|
||||
command_text: str
|
||||
@@ -218,7 +568,7 @@ class SingleCommand:
|
||||
self.annotation_text = annotation
|
||||
|
||||
def copy(self):
|
||||
return SingleCommand(
|
||||
return MineCommand(
|
||||
command=self.command_text,
|
||||
condition=self.conditional,
|
||||
tick_delay=self.delay,
|
||||
@@ -236,8 +586,8 @@ class SingleCommand:
|
||||
"""
|
||||
转为我的世界函数文件格式(包含注释)
|
||||
"""
|
||||
return "#[{cdt}]<{delay}> {ant}\n{cmd}".format(
|
||||
cdt="CDT" if self.conditional else "",
|
||||
return "# {cdt}<{delay}> {ant}\n{cmd}".format(
|
||||
cdt="[CDT]" if self.conditional else "",
|
||||
delay=self.delay,
|
||||
ant=self.annotation_text,
|
||||
cmd=self.command_text,
|
||||
@@ -272,12 +622,26 @@ class SingleNoteBox:
|
||||
percussion: Optional[bool] = None,
|
||||
annotation: str = "",
|
||||
):
|
||||
"""用于存储单个音符盒的类
|
||||
:param instrument_block_ 音符盒演奏所使用的乐器方块
|
||||
:param note_value_ 音符盒的演奏音高
|
||||
:param percussion 此音符盒乐器是否作为打击乐处理
|
||||
"""
|
||||
用于存储单个音符盒的类
|
||||
|
||||
Parameters
|
||||
------------
|
||||
instrument_block_: str
|
||||
音符盒演奏所使用的乐器方块
|
||||
note_value_: int
|
||||
音符盒的演奏音高
|
||||
percussion: bool
|
||||
此音符盒乐器是否作为打击乐处理
|
||||
注:若为空,则自动识别是否为打击乐器
|
||||
:param annotation 音符注释"""
|
||||
annotation: Any
|
||||
音符注释
|
||||
|
||||
Returns
|
||||
---------
|
||||
SingleNoteBox 类
|
||||
"""
|
||||
|
||||
self.instrument_block = instrument_block_
|
||||
"""乐器方块"""
|
||||
self.note_value = note_value_
|
||||
@@ -285,7 +649,7 @@ class SingleNoteBox:
|
||||
self.annotation_text = annotation
|
||||
"""音符注释"""
|
||||
if percussion is None:
|
||||
self.is_percussion = percussion in MC_PERCUSSION_INSTRUMENT_LIST
|
||||
self.is_percussion = percussion not in MC_PITCHED_INSTRUMENT_LIST
|
||||
else:
|
||||
self.is_percussion = percussion
|
||||
|
||||
@@ -355,14 +719,71 @@ class ProgressBarStyle:
|
||||
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 __init__(
|
||||
self,
|
||||
base_s: Optional[str] = None,
|
||||
to_play_s: Optional[str] = None,
|
||||
played_s: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
用于存储进度条样式的类
|
||||
|
||||
| 标识符 | 指定的可变量 |
|
||||
|---------|----------------|
|
||||
| `%%N` | 乐曲名(即传入的文件名)|
|
||||
| `%%s` | 当前计分板值 |
|
||||
| `%^s` | 计分板最大值 |
|
||||
| `%%t` | 当前播放时间 |
|
||||
| `%^t` | 曲目总时长 |
|
||||
| `%%%` | 当前进度比率 |
|
||||
| `_` | 用以表示进度条占位|
|
||||
|
||||
Parameters
|
||||
------------
|
||||
base_s: str
|
||||
基础样式,用以定义进度条整体
|
||||
to_play_s: str
|
||||
进度条样式:尚未播放的样子
|
||||
played_s: str
|
||||
已经播放的样子
|
||||
|
||||
Returns
|
||||
---------
|
||||
ProgressBarStyle 类
|
||||
"""
|
||||
|
||||
self.base_style = (
|
||||
base_s if base_s else r"▶ %%N [ %%s/%^s %%% §e__________§r %%t|%^t ]"
|
||||
)
|
||||
self.to_play_style = to_play_s if to_play_s else r"§7="
|
||||
self.played_style = played_s if played_s else r"="
|
||||
|
||||
@classmethod
|
||||
def from_tuple(cls, tuplized_style: Optional[Tuple[str, Tuple[str, str]]]):
|
||||
"""自旧版进度条元组表示法读入数据(已不建议使用)"""
|
||||
|
||||
if tuplized_style is None:
|
||||
return cls(
|
||||
r"▶ %%N [ %%s/%^s %%% §e__________§r %%t|%^t ]",
|
||||
r"§7=",
|
||||
r"=",
|
||||
)
|
||||
|
||||
if isinstance(tuplized_style, tuple):
|
||||
if isinstance(tuplized_style[0], str) and isinstance(
|
||||
tuplized_style[1], tuple
|
||||
):
|
||||
if isinstance(tuplized_style[1][0], str) and isinstance(
|
||||
tuplized_style[1][1], str
|
||||
):
|
||||
return cls(
|
||||
tuplized_style[0], tuplized_style[1][0], tuplized_style[1][1]
|
||||
)
|
||||
raise ValueError(
|
||||
"元组表示的进度条样式组 {} 格式错误,已不建议使用此功能,请尽快更换。".format(
|
||||
tuplized_style
|
||||
)
|
||||
)
|
||||
|
||||
def set_base_style(self, value: str):
|
||||
"""设置基础样式"""
|
||||
@@ -380,22 +801,61 @@ class ProgressBarStyle:
|
||||
dst = ProgressBarStyle(self.base_style, self.to_play_style, self.played_style)
|
||||
return dst
|
||||
|
||||
def play_output(
|
||||
self,
|
||||
played_delays: int,
|
||||
total_delays: int,
|
||||
music_name: str = "无题",
|
||||
) -> str:
|
||||
"""
|
||||
直接依照此格式输出一个进度条
|
||||
|
||||
Parameters
|
||||
------------
|
||||
played_delays: int
|
||||
当前播放进度积分值
|
||||
total_delays: int
|
||||
乐器总延迟数(计分板值)
|
||||
music_name: str
|
||||
曲名
|
||||
|
||||
Returns
|
||||
---------
|
||||
str
|
||||
进度条字符串
|
||||
"""
|
||||
|
||||
return (
|
||||
self.base_style.replace(r"%%N", music_name)
|
||||
.replace(r"%%s", str(played_delays))
|
||||
.replace(r"%^s", str(total_delays))
|
||||
.replace(r"%%t", mctick2timestr(played_delays))
|
||||
.replace(r"%^t", mctick2timestr(total_delays))
|
||||
.replace(
|
||||
r"%%%",
|
||||
"{:0>5.2f}%".format(int(10000 * played_delays / total_delays) / 100),
|
||||
)
|
||||
.replace(
|
||||
"_",
|
||||
self.played_style,
|
||||
(played_delays * self.base_style.count("_") // total_delays) + 1,
|
||||
)
|
||||
.replace("_", self.to_play_style)
|
||||
)
|
||||
|
||||
|
||||
def mctick2timestr(mc_tick: int) -> str:
|
||||
"""
|
||||
将《我的世界》的游戏刻计转为表示时间的字符串
|
||||
"""
|
||||
return "{:0>2d}:{:0>2d}".format(mc_tick // 1200, (mc_tick // 20) % 60)
|
||||
|
||||
|
||||
DEFAULT_PROGRESSBAR_STYLE = ProgressBarStyle(
|
||||
r"▶ %%N [ %%s/%^s %%% __________ %%t|%^t ]",
|
||||
r"§e=§r",
|
||||
r"§7=§r",
|
||||
r"▶ %%N [ %%s/%^s %%% §e__________§r %%t|%^t ]",
|
||||
r"§7=",
|
||||
r"=",
|
||||
)
|
||||
"""
|
||||
默认的进度条样式
|
||||
"""
|
||||
|
||||
NoteChannelType = Mapping[
|
||||
int,
|
||||
List[SingleNote,],
|
||||
]
|
||||
"""
|
||||
频道信息类型
|
||||
|
||||
Dict[int,Dict[int,List[SingleNote,],],]
|
||||
"""
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2024 音·创 开发者
|
||||
Copyright © 2024 all the developers of Musicreater
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -16,33 +16,24 @@ Terms & Conditions: License.md in the root directory
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
from typing import (
|
||||
Any,
|
||||
Dict,
|
||||
List,
|
||||
Literal,
|
||||
Optional,
|
||||
Tuple,
|
||||
Union,
|
||||
Iterable,
|
||||
Sequence,
|
||||
Mapping,
|
||||
Callable,
|
||||
)
|
||||
from typing import Callable, Dict, List, Literal, Mapping, Tuple, Union
|
||||
|
||||
from .subclass import MineNote
|
||||
|
||||
MidiNoteNameTableType = Mapping[int, Tuple[str, ...]]
|
||||
"""
|
||||
Midi音符名称对照表类型
|
||||
"""
|
||||
|
||||
MidiInstrumentTableType = Mapping[int, Tuple[str, int]]
|
||||
MidiInstrumentTableType = Mapping[int, str]
|
||||
"""
|
||||
Midi乐器对照表类型
|
||||
"""
|
||||
|
||||
FittingFunctionType = Callable[[float], float]
|
||||
|
||||
"""
|
||||
拟合函数类型
|
||||
"""
|
||||
|
||||
ChannelType = Dict[
|
||||
int,
|
||||
@@ -58,7 +49,25 @@ ChannelType = Dict[
|
||||
],
|
||||
]
|
||||
"""
|
||||
以字典所标记的频道信息类型(已弃用)
|
||||
以字典所标记的通道信息类型(已弃用)
|
||||
|
||||
Dict[int,Dict[int,List[Union[Tuple[Literal["PgmC"], int, int],Tuple[Literal["NoteS"], int, int, int],Tuple[Literal["NoteE"], int, int],]],],]
|
||||
"""
|
||||
|
||||
|
||||
MineNoteChannelType = Mapping[
|
||||
int,
|
||||
List[MineNote,],
|
||||
]
|
||||
"""
|
||||
我的世界通道信息类型
|
||||
|
||||
Dict[int,Dict[int,List[MineNote,],],]
|
||||
"""
|
||||
|
||||
MineNoteTrackType = Mapping[
|
||||
int,
|
||||
List[MineNote,],
|
||||
]
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,24 @@
|
||||
import Musicreater
|
||||
import Musicreater.experiment
|
||||
import Musicreater.previous
|
||||
|
||||
import Musicreater.plugin
|
||||
|
||||
# import Musicreater.previous
|
||||
from Musicreater.plugin.addonpack import (
|
||||
to_addon_pack_in_delay,
|
||||
to_addon_pack_in_repeater,
|
||||
to_addon_pack_in_score,
|
||||
)
|
||||
from Musicreater.plugin.bdxfile import to_BDX_file_in_delay, to_BDX_file_in_score
|
||||
from Musicreater.plugin.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,
|
||||
# Musicreater.previous,
|
||||
)
|
||||
|
||||
MSCT_PLUGIN = (Musicreater.plugin,)
|
||||
@@ -38,11 +36,11 @@ MSCT_PLUGIN_FUNCTION = (
|
||||
|
||||
import hashlib
|
||||
|
||||
import dill
|
||||
import brotli
|
||||
import dill
|
||||
|
||||
|
||||
def enpack_llc_pack(sth, to_dist: str):
|
||||
def enpack_msct_pack(sth, to_dist: str):
|
||||
packing_bytes = brotli.compress(
|
||||
dill.dumps(
|
||||
sth,
|
||||
@@ -59,12 +57,12 @@ def enpack_llc_pack(sth, to_dist: str):
|
||||
|
||||
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(enpack_msct_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(enpack_msct_pack(MSCT_PLUGIN, "./Packer/MSCT_PLUGIN.MPK").hexdigest())
|
||||
f.write("\nMSCT_PLUGIN_FUNCTION:\n")
|
||||
f.write(
|
||||
enpack_llc_pack(
|
||||
enpack_msct_pack(
|
||||
MSCT_PLUGIN_FUNCTION, "./Packer/MSCT_PLUGIN_FUNCTION.MPK"
|
||||
).hexdigest()
|
||||
)
|
||||
|
||||
99
README.md
99
README.md
@@ -1,13 +1,18 @@
|
||||
<h1 align="center">
|
||||
音·创 Musicreater
|
||||
</h1>
|
||||
[Bilibili: 金羿ELS]: https://img.shields.io/badge/Bilibili-%E9%87%91%E7%BE%BFELS-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
|
||||
[release]: https://img.shields.io/github/v/release/EillesWan/Musicreater?style=for-the-badge
|
||||
[license]: https://img.shields.io/badge/Licence-%E6%B1%89%E9%92%B0%E5%BE%8B%E8%AE%B8%E5%8F%AF%E5%8D%8F%E8%AE%AE-228B22?style=for-the-badge
|
||||
|
||||
<h1 align="center">音·创 Musicreater </h1>
|
||||
|
||||
<p align="center">
|
||||
<img width="128" height="128" src="https://gitee.com/TriM-Organization/Musicreater/raw/master/resources/msctIcon.ico">
|
||||
<img width="128" height="128" src="https://gitee.com/TriM-Organization/Musicreater/raw/master/resources/msctIcon.png">
|
||||
</img>
|
||||
</p>
|
||||
|
||||
<h3 align="center">一款免费开源的《我的世界》数字音频转换库。</h3>
|
||||
<h3 align="center">一款免费开源的《我的世界》数字音频支持库。</h3>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/BUILD%20WITH%20LOVE-FF3432?style=for-the-badge">
|
||||
@@ -29,33 +34,42 @@
|
||||
[](https://github.com/TriM-Organization/Musicreater/stargazers)
|
||||
[](https://github.com/TriM-Organization/Musicreater/forks)
|
||||
|
||||
简体中文 🇨🇳 | [English🇬🇧](README_EN.md)
|
||||
简体中文🇨🇳 | [English🇬🇧](README_EN.md)
|
||||
|
||||
## 介绍 🚀
|
||||
|
||||
音·创 是一个免费开源的针对 **《我的世界》** 的 MIDI 音乐转换库
|
||||
音·创 是一款免费开源的针对 **《我的世界》** 音乐的支持库
|
||||
|
||||
欢迎加群:[861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)
|
||||
|
||||
> **注意** 本仓库内的项目仅仅是支持库,其用户为基岩版音乐相关软件的开发者
|
||||
>
|
||||
> 面向常规用户的 **基岩版音乐转换工具** 请参阅:[伶伦转换器](../../../Linglun-Converter)
|
||||
>
|
||||
> 我们也正在开发面向高级用户的 **基岩版音乐编辑工具**(数字音频工作站):[伶伦](../../../LinglunStudio)
|
||||
|
||||
|
||||
## 安装 🔳
|
||||
|
||||
- 使用 pypi
|
||||
- 使用 pypi
|
||||
|
||||
```bash
|
||||
pip install --upgrade Musicreater
|
||||
```
|
||||
```bash
|
||||
pip install --upgrade Musicreater
|
||||
```
|
||||
|
||||
- 如果无法更新最新,可以尝试:
|
||||
```bash
|
||||
pip install --upgrade -i https://pypi.python.org/simple Musicreater
|
||||
```
|
||||
- 如果无法更新最新,可以尝试:
|
||||
|
||||
- 克隆仓库并安装(最新版本但**不推荐**)
|
||||
```bash
|
||||
git clone https://gitee.com/TriM-Organization/Musicreater.git
|
||||
cd Musicreater
|
||||
python setup.py install
|
||||
```
|
||||
```bash
|
||||
pip install --upgrade -i https://pypi.python.org/simple Musicreater
|
||||
```
|
||||
|
||||
- 克隆仓库并安装(最新版本但**不推荐**)
|
||||
|
||||
```bash
|
||||
git clone https://gitee.com/TriM-Organization/Musicreater.git
|
||||
cd Musicreater
|
||||
python setup.py install
|
||||
```
|
||||
|
||||
以上命令中 `python`、`pip` 请依照各个环境不同灵活更换,可能为`python3`或`pip3`之类。
|
||||
|
||||
@@ -67,30 +81,32 @@
|
||||
|
||||
## 作者 ✒
|
||||
|
||||
**金羿 Eilles**:我的世界基岩版指令师,个人开发者,B 站不知名 UP 主,江西在校高中生。
|
||||
**金羿 Eilles**:我的世界基岩版指令作者,个人开发者,B 站不知名 UP 主。
|
||||
|
||||
**诸葛亮与八卦阵 bgArray**:我的世界基岩版玩家,喜欢编程和音乐,深圳初二学生。
|
||||
**诸葛亮与八卦阵 bgArray**:我的世界基岩版玩家,喜欢编程和音乐,深圳学生。
|
||||
|
||||
**偷吃不是Touch Touch**:我的世界基岩版指令师,提供 BDX 导入测试支持
|
||||
**偷吃不是Touch Touch**:我的世界基岩版指令制作者,提供测试支持
|
||||
|
||||
## 致谢 🙏
|
||||
|
||||
本致谢列表排名无顺序。
|
||||
|
||||
- 感谢 **昀梦**\<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>
|
||||
- 感谢 **昀梦**\<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>
|
||||
- 感谢 **雨**\<QQ237667809\> 反馈在新版本的指令格式下,计分板播放器的附加包无法播放的问题。
|
||||
- 感谢 **梦幻duang**\<QQ13753593\> 为我们提供 Java 1.12.2 版本命令格式参考。
|
||||
- 感谢 [_Open Note Block Studio_](https://github.com/OpenNBS/NoteBlockStudio) 项目的开发为我们提供持续的追赶动力。
|
||||
|
||||
> 感谢广大群友为此库提供的测试和建议等
|
||||
>
|
||||
> 若您对我们有所贡献但您的名字没有出现在此列表中,请联系我们!
|
||||
|
||||
## 联系 📞
|
||||
@@ -109,19 +125,12 @@
|
||||
|
||||
此项目亦不隶属或关联于 网易
|
||||
|
||||
“Minecraft”是 Mojang Synergies AB 的商标,此项目中所有对于“我的世界”、“Minecraft”等相关称呼均为引用性使用
|
||||
“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-%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
|
||||
[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
|
||||
|
||||
96
README_EN.md
96
README_EN.md
@@ -1,3 +1,10 @@
|
||||
[Bilibili: Eilles]: https://img.shields.io/badge/Bilibili-%E9%87%91%E7%BE%BFELS-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
|
||||
[release]: https://img.shields.io/github/v/release/EillesWan/Musicreater?style=for-the-badge
|
||||
[license]: https://img.shields.io/badge/Licence-%E6%B1%89%E9%92%B0%E5%BE%8B%E8%AE%B8%E5%8F%AF%E5%8D%8F%E8%AE%AE-228B22?style=for-the-badge
|
||||
|
||||
<h1 align="center">
|
||||
音·创 Musicreater
|
||||
</h1>
|
||||
@@ -7,7 +14,7 @@
|
||||
</img>
|
||||
</p>
|
||||
|
||||
<h3 align="center">A free open-source library of converting digital music files into <i>Minecraft</i> formats.</h3>
|
||||
<h3 align="center">A free open-source library of <i>Minecraft</i> digital music.</h3>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/BUILD%20WITH%20LOVE-FF3432?style=for-the-badge">
|
||||
@@ -32,33 +39,40 @@
|
||||
|
||||
## 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 library used for digital music that being played in _Minecraft_.
|
||||
|
||||
Welcome to join our QQ group: [861684859](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)
|
||||
|
||||
> **NOTICE** The project inside this repository is only the support library, which is mainly for the developers of music related software for _Minecraft: Bedrock Edition_.
|
||||
>
|
||||
> The _Bedrock Edition music convertor_ which is for common users is in [Linglun Converter](../../../Linglun-Converter).
|
||||
>
|
||||
> We are also developing a _BE music editor_ (digital audio workstation): [Linglun](../../../LinglunStudio)
|
||||
|
||||
|
||||
## Installation 🔳
|
||||
|
||||
- Via pypi
|
||||
- Via pypi
|
||||
|
||||
```bash
|
||||
pip install Musicreater --upgrade
|
||||
```
|
||||
```bash
|
||||
pip install Musicreater --upgrade
|
||||
```
|
||||
|
||||
- If above command cannot fetch latest version, try:
|
||||
```bash
|
||||
pip install -i https://pypi.python.org/simple Musicreater --upgrade
|
||||
```
|
||||
- If above command cannot fetch latest version, try:
|
||||
|
||||
- Clone repo and Install (Latest but **NOT RECOMMANDED**):
|
||||
```bash
|
||||
git clone https://github.com/TriM-Organization/Musicreater.git
|
||||
cd Musicreater
|
||||
python setup.py install
|
||||
```
|
||||
```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.
|
||||
|
||||
|
||||
## Documentation 📄
|
||||
|
||||
(Not in English yet)
|
||||
@@ -69,35 +83,37 @@ Commands such as `python`、`pip` could be changed to some like `python3` or `pi
|
||||
|
||||
### Authors ✒
|
||||
|
||||
**Eilles (金羿)**:A senior high school student, individual developer, unfamous Bilibili UPer, which knows a little about commands in _Minecraft: Bedrock Edition_
|
||||
**Eilles (金羿)**:A student, individual developer, unfamous Bilibili UPer, which knows a little about commands in _Minecraft: Bedrock Edition_
|
||||
|
||||
**bgArray (诸葛亮与八卦阵)**: A junior high school student, player of _Minecraft: Bedrock Edition_, which is a fan of music and programming.
|
||||
**bgArray (诸葛亮与八卦阵)**: A student, player of _Minecraft: Bedrock Edition_, which is a fan of music and programming.
|
||||
|
||||
**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
|
||||
**Touch (偷吃不是 Touch)**: A man who is good at using 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>
|
||||
- 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 <i>油炸</i> <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>
|
||||
- Thank _雨_\<QQ237667809\> for give us report that under the new `execute` command format that the scoreboard player's add-on packs cannot play correctly.
|
||||
- Thank _梦幻duang_\<QQ13753593\> for providing us with his knowlodeg of the command format in Minecraft: Java Edition Version 1.12.2.
|
||||
- Thank [_Open Note Block Studio_](https://github.com/OpenNBS/NoteBlockStudio)'s Project for giving us the power and energy of continual developing.
|
||||
|
||||
> Thanks for the support and help of a lot of groupmates
|
||||
>
|
||||
> 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)!
|
||||
Meet problems? Welcome to give out your issue [here](../../issues/new)!
|
||||
|
||||
Want to get in contact of developers? Welcome to join our [Chat QQ group](https://jq.qq.com/?_wv=1027&k=hpeRxrYr).
|
||||
|
||||
@@ -117,14 +133,6 @@ NOT APPROVED BY OR ASSOCIATED WITH NETEASE.
|
||||
|
||||
此项目亦不隶属或关联于 网易 相关
|
||||
|
||||
“Minecraft”是 Mojang Synergies AB 的商标,此项目中所有对于“我的世界”、“Minecraft”等相关称呼均为引用性使用
|
||||
“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
|
||||
[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
|
||||
- 上文提及的 网易 公司,指代的是在中国大陆运营《我的世界:中国版》的上海网之易璀璨网络科技有限公司
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
@@ -7,13 +7,13 @@ console = Console()
|
||||
|
||||
|
||||
def main():
|
||||
with console.status("Find the full path of .egg-info folder"):
|
||||
with console.status("寻众迹于 .egg-info 内"):
|
||||
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"):
|
||||
for file in track(["build", "dist", "logs", *egg_info], description="正删档"):
|
||||
if os.path.isdir(file) and os.access(file, os.W_OK):
|
||||
shutil.rmtree(file)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<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 width="128" height="128" src="https://gitee.com/TriM-Organization/Musicreater/raw/master/resources/msctIcon.png" >
|
||||
</p>
|
||||
|
||||
**此为开发相关文档,内容包括:库的简单调用、所生成文件结构的详细说明、特殊参数的详细解释**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<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 width="128" height="128" src="https://gitee.com/TriM-Organization/Musicreater/raw/master/resources/msctIcon.png" >
|
||||
</p>
|
||||
|
||||
**此为开发相关文档,内容包括:库的简单调用、所生成文件结构的详细说明、特殊参数的详细解释**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<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 width="128" height="128" src="https://gitee.com/TriM-Organization/Musicreater/raw/master/resources/msctIcon.png" >
|
||||
</p>
|
||||
|
||||
# 生成文件的使用
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<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 width="128" height="128" src="https://gitee.com/TriM-Organization/Musicreater/raw/master/resources/msctIcon.png" >
|
||||
</p>
|
||||
|
||||
# 转换乐器对照表
|
||||
@@ -12,11 +12,16 @@
|
||||
|
||||
**_使用时请遵循协议规定_**
|
||||
|
||||
- 版权所有 © 2024 音·创 开发者
|
||||
- Copyright © 2024 all the developers of Musicreater
|
||||
- 版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
- Copyright © 2025 Eilles & bgArray
|
||||
|
||||
* 开源相关声明请见 仓库根目录下的 License.md
|
||||
* Terms & Conditions: License.md in the root directory
|
||||
* 音·创(“本项目”)的协议颁发者为 金羿、诸葛亮与八卦阵
|
||||
* The Licensor of Musicreater("this project") is Eilles, bgArray.
|
||||
|
||||
本项目根据 第一版 汉钰律许可协议(“本协议”)授权。
|
||||
任何人皆可从以下地址获得本协议副本:https://gitee.com/EillesWan/YulvLicenses。
|
||||
若非因法律要求或经过了特殊准许,此作品在根据本协议“原样”提供的基础上,不予提供任何形式的担保、任何明示、任何暗示或类似承诺。也就是说,用户将自行承担因此作品的质量或性能问题而产生的全部风险。
|
||||
详细的准许和限制条款请见原协议文本。
|
||||
|
||||
音·创 开发交流群 861684859\
|
||||
Email TriM-Organization@hotmail.com\
|
||||
|
||||
149
docs/音乐序列文件格式.md
Normal file
149
docs/音乐序列文件格式.md
Normal file
@@ -0,0 +1,149 @@
|
||||
# 音乐序列文件格式
|
||||
|
||||
音·创 库的音符序列文件格式包含两种,一种是常规的音乐序列存储采用的 MSQ 格式,另一种是为了流式读取音符而采用的 FSQ 格式。
|
||||
|
||||
## MSQ 数据格式
|
||||
|
||||
MSQ 格式是 音·创 库存储音符序列的一种字节码格式,取自 **M**usic**S**e**Q**uence 类之名。
|
||||
|
||||
现在 音·创 库及其上游软件使用的是在 第二版 的基础上增设校验功能的 MSQ 第三版。
|
||||
|
||||
### MSQ 第三版
|
||||
|
||||
第三版 MSQ 格式的码头是 `MSQ!` ,这一版中,所有的**字符串**皆以 _**GB18030**_ 编码进行编解码,**数值**皆是以 _**大端字节序**_ 存储的无符号整数。
|
||||
|
||||
码头是字节码前四个字节的内容,这一部分内容是可读的 ASCII 字串。因此,第三版的字节码中前四个字节的内容必为 `MSQ!`。
|
||||
|
||||
第二版 MSQ 取 `MSQ@` 作为码头是因为美式键盘上 @ 是 Shift+2 键 按下取得的,故代表 MSQ 第二版。
|
||||
|
||||
你猜为什么第三版是 `MSQ!`。
|
||||
|
||||
#### 元信息
|
||||
|
||||
| 信息名称 | 西文代号 | 位长(多少个 0 或 1) | 支持说明 |
|
||||
| ------------------------------ | -------------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **码头** | _无_ | 32 位 | 值为 `MSQ!` |
|
||||
| **音乐名称长度** | music_name_length | 6 位 | 支持数值 0~63 |
|
||||
| **最小音量** | minimum_volume | 10 位 | 支持数值 0~1023,注意,这里每个 1 代表最小音量的 0.001 个单位,即取值是此处表示数字的千分倍 |
|
||||
| **是否启用高精度音符时间控制** | enable_high_precision_time | 1 位 | 1 是启用,反之同理 |
|
||||
| **总音调偏移** | music_deviation | 15 位 | 支持数值 -16383~16383,这里也是表示三位小数的,和最小音量一样。这里 15 位中的第一位(从左往右)是正负标记,若为 1 则为负数,反之为正数,后面的 14 位是数值 |
|
||||
| **音乐名称** | music_name | 依据先前定义 | 最多可支持 31 个中文字符 或 63 个西文字符,其长度取决于先前获知的 “音乐名称长度” 的定义 |
|
||||
|
||||
在这一元信息中,**音乐名称长度**和**最小音量**合计共 2 字节;**高精度音符时间控制启用**和**总音调偏移**合计共 2 字节;因此,除**音乐名称**为任意长度,前四字节内容均为固定。
|
||||
|
||||
#### 音符序列
|
||||
|
||||
每个序列前 4 字节为一个用以表示当前通道中音符数量的值,也就是**通道音符数**(notes_count)。也即是说,一个通道内的音符可以是 0~4294967295 个。
|
||||
|
||||
在这之后,就是这些数量的音符了,其中每个音符的信息存储方式如下
|
||||
|
||||
| 信息名称 | 西文代号 | 位长 | 支持说明 |
|
||||
| ---------------------------- | ------------------------ | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **乐器名称长度** | name_length | 6 位 | 支持数值 0~63 |
|
||||
| **Midi 音高** | note_pitch | 7 位 | 支持数值 0~127 |
|
||||
| **开始时刻** | start_tick | 17 位 | 单位 二十分之一秒,即约为 1 命令刻;支持数值 0~131071 即 109.22583 分钟 合 1.8204305 小时 |
|
||||
| **音符持续刻数** | duration | 17 位 | 同上 |
|
||||
| **是否作为打击乐器** | percussive | 1 位 | 1 是启用,反之同理 |
|
||||
| **响度(力度)** | velocity | 7 位 | 支持数值 0~127 |
|
||||
| **是否启用声像位移** | is_displacement_included | 1 位 | 1 是启用,反之同理 |
|
||||
| **时间精度提升值**(非必含) | high_time_precision | 8 位 | 支持数值 0~255,若在 元信息 中启用**高精度音符时间控制**,则此值启用,代表音符时间控制精度偏移,此值每增加 1,则音符开始时刻向后增加 1/1250 秒 |
|
||||
| **乐器名称** | sound_name | 依据先前定义 | 最多可支持 31 个中文字符 或 63 个西文字符,其长度取决于先前获知的 “乐器名称长度” 的定义 |
|
||||
| **声像位移**(非必含) | position_displacement | 共三个值,每个值 16 位 共 48 位 | 若前述**是否启用声像位移**已启用,则此值启用;三个值分别代表 x、y、z 轴上的偏移,每个值支持数值 0~65535,注意,这里每个 1 代表最小音量的 0.001 个单位,即取值是此处表示数字的千分倍 |
|
||||
|
||||
#### 序列校验
|
||||
|
||||
_第三版新增_
|
||||
|
||||
在每个音符序列结尾包含一个 128 位的校验值,用以标识该序列结束的同时,验证该序列的完整性。
|
||||
在这 128 位里,前 64 位是该通道音符数的 XXHASH64 校验值,以 3 作为种子值。
|
||||
后 64 位是整个通道全部字节串的 XXHASH64 校验值(包括通道开头的音符数),以 该通道音符数 作为种子值。
|
||||
|
||||
#### 总体校验
|
||||
|
||||
_第三版新增_
|
||||
|
||||
在所有有效数据之后,包含一个 128 位的校验值,用以标识整个字节串结束的同时,验证整个字节码数据的完整性。
|
||||
|
||||
该 128 位的校验值是 包括码头在内的元信息的 XXHASH64 校验值(种子值是全曲音符数) 对于前述所有校验值彼此异或的异或 所得值之 XXHASH128 校验值,以 全曲音符总数 作为种子值。
|
||||
|
||||
请注意,是前述每个 XXHASH64 校验值的异或(每次取 XXHASH64 都计一遍),也就并非是每个序列结尾,那个已经合并了的 128 位校验值再彼此异或。对于这个异或值,再取其种子是 全曲音符数 的 XXHASH128 校验字节码。
|
||||
|
||||
听起来很复杂?我来举个例子。以下是该算法的伪代码。我们设:
|
||||
|
||||
- `meta_info` : `bytes` 为 元信息字节串
|
||||
- `note_seq_1` : `bytes` 为 第一个音符序列的编码字节串
|
||||
- `note_seq_2` : `bytes` 为 第二个音符序列的编码字节串
|
||||
- `XXH64(bytes, seed)` : `bytes` 为 XXHASH64 校验函数
|
||||
- `XXH128(bytes, seed)` : `bytes` 为 XXHASH128 校验函数
|
||||
- `XOR(bytesLike, bytesLike)` : `bytes` 为 异或 函数
|
||||
- `note_count` : `int` 为 全曲音符数
|
||||
- `seq_1_note_count` : `int` 为 第一个音符序列的音符数
|
||||
- `seq_2_note_count` : `int` 为 第二个音符序列的音符数
|
||||
|
||||
为了简化,我们假设只有两个序列,实际上每个通道都是一个序列(最多 16 个序列)
|
||||
|
||||
那么,一个完整的 MSQ 文件应当如下排列其字节串:
|
||||
|
||||
```assembly
|
||||
ADD meta_info
|
||||
ADD note_seq_1
|
||||
ADD XXH64(seq_1_note_count, 3)
|
||||
ADD XXH64(note_seq_1, seq_1_note_count)
|
||||
ADD note_seq_2
|
||||
ADD XXH64(seq_2_note_count, 3)
|
||||
ADD XXH64(note_seq_2, seq_2_note_count)
|
||||
ADD XXH128(
|
||||
XOR(
|
||||
XOR(
|
||||
XXH64(meta_info, note_count),
|
||||
XOR(
|
||||
XXH64(seq_1_note_count, 3),
|
||||
XXH64(note_seq_1, seq_1_note_count)
|
||||
),
|
||||
),
|
||||
XOR(
|
||||
XXH64(seq_2_note_count, 3),
|
||||
XXH64(note_seq_2, seq_2_note_count),
|
||||
)
|
||||
),
|
||||
note_count
|
||||
)
|
||||
```
|
||||
|
||||
## FSQ 数据格式
|
||||
|
||||
FSQ 格式是 音·创 库存储音符序列的一种字节码格式,取自 **F**lowing Music **S**e**q**uence 之名。
|
||||
|
||||
现在 音·创 库及其上游软件使用的是在 MSQ 第三版 的基础上进行流式的兼容性变动。
|
||||
|
||||
### FSQ 第一版
|
||||
|
||||
第一版的码头是 `FSQ!` ,这一版中,所有的**字符串**皆以 _**GB18030**_ 编码进行编解码,**数值**皆是以 _**大端字节序**_ 存储的无符号整数。
|
||||
|
||||
码头是字节串前四个字节的内容,这一部分内容是可读的 ASCII 字串。因此,这一版的文件前四个字节的内容必为 `FSQ!`。
|
||||
|
||||
因为这一版本是在 MSQ 第三版的基础上演变而来的,因此取自 MSQ 码头的 `MSQ!` 而改作 `FSQ!`。
|
||||
|
||||
#### 元信息
|
||||
|
||||
FSQ 第一版的元信息是在 MSQ 第三版的元信息的基础上增加了一个占 5 字节的全曲音符总数。
|
||||
|
||||
也就是说,与 MSQ 第三版一致的,在这一组信息中,**音乐名称长度**和**最小音量**合计共 2 字节;**高精度音符时间控制启用**和**总音调偏移**合计共 2 字节;因此,除**音乐名称**为任意长度,前四字节内容均为固定。而最后增加 5 字节作为全曲音符总数。
|
||||
|
||||
#### 音符序列
|
||||
|
||||
FSQ 格式不包含音符的通道信息,在读取处理时默认将同一乐器的音符视为同一通道。也就是说,仅存在一个序列。其中每个音符的信息存储方式与 MSQ 第三版一致。
|
||||
|
||||
音符序列的存储顺序是按照音符的**开始时间**进行排序的。
|
||||
|
||||
但是注意!有可能一个较长的音符的开始到结束时间内还包含有音符,此时如有要适配的读取器,还请继续读取直到下一个音符的开始时间大于此较长音符的结束时间。
|
||||
|
||||
在每 100 个音符后,插入一段 32 位的 XXHASH32 校验码,其所校验的内容为这一百个音符中每个音符的第 6 个字节彼此异或之结果,种子值为这一百个音符中每个音符的第 2 个字节的彼此异或结果。
|
||||
|
||||
若最后不满足 100 个音符,则不插入上述校验码。
|
||||
|
||||
#### 总体校验
|
||||
|
||||
在所有有效数据之后,包含一个 128 位的校验值,用以标识整个字节串结束的同时,验证整个字节码数据的完整性。
|
||||
|
||||
该 128 位的校验值是 包括码头在内的元信息的 XXHASH64 校验值(种子值是全曲音符数) 对于前述所有 XXHASH32 校验值彼此异或的异或 所得值之 XXHASH128 校验值,以 全曲音符总数 作为种子值。
|
||||
100
example.py
100
example.py
@@ -9,8 +9,8 @@
|
||||
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
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 ./License.md
|
||||
Terms & Conditions: ./License.md
|
||||
@@ -19,7 +19,6 @@ 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,
|
||||
@@ -80,11 +79,11 @@ else:
|
||||
# 提示语 检测函数 错误提示语
|
||||
for args in [
|
||||
(
|
||||
f"输入音量:",
|
||||
f"最小播放音量:",
|
||||
float,
|
||||
),
|
||||
(
|
||||
f"输入播放速度:",
|
||||
f"播放速度:",
|
||||
float,
|
||||
),
|
||||
(
|
||||
@@ -118,21 +117,19 @@ else:
|
||||
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 ()
|
||||
),
|
||||
"结构延展方向:",
|
||||
lambda a: isin(
|
||||
a,
|
||||
{
|
||||
"z+": ["z+", "Z+"],
|
||||
"x+": ["X+", "x+"],
|
||||
"z-": ["Z-", "z-"],
|
||||
"x-": ["x-", "X-"],
|
||||
},
|
||||
),
|
||||
)
|
||||
if (playerFormat == 2 and fileFormat == 2)
|
||||
else ()
|
||||
)
|
||||
),
|
||||
(
|
||||
@@ -152,12 +149,17 @@ else:
|
||||
),
|
||||
]:
|
||||
if args:
|
||||
prompts.append(args[1](input(args[0])))
|
||||
try:
|
||||
prompts.append(args[1](input(args[0])))
|
||||
except Exception:
|
||||
print(args)
|
||||
|
||||
|
||||
print(f"正在处理 {midi_path} :")
|
||||
cvt_mid = Musicreater.MidiConvert.from_midi_file(midi_path, old_exe_format=False)
|
||||
cvt_cfg = ConvertConfig(out_path, *prompts[:3])
|
||||
cvt_mid = Musicreater.MidiConvert.from_midi_file(
|
||||
midi_path, old_exe_format=False, min_volume=prompts[0], play_speed=prompts[1]
|
||||
)
|
||||
|
||||
|
||||
if fileFormat == 0:
|
||||
if playerFormat == 1:
|
||||
@@ -174,26 +176,62 @@ elif fileFormat == 2:
|
||||
elif playerFormat == 2:
|
||||
cvt_method = to_mcstructure_file_in_repeater
|
||||
|
||||
# 测试
|
||||
|
||||
# print(cvt_mid)
|
||||
|
||||
|
||||
print(
|
||||
" 指令总长:{},最高延迟:{}".format(
|
||||
*(cvt_method(cvt_mid, cvt_cfg, *prompts[3:])) # type: ignore
|
||||
*(
|
||||
cvt_method(
|
||||
cvt_mid,
|
||||
out_path,
|
||||
Musicreater.DEFAULT_PROGRESSBAR_STYLE if prompts[2] else None, # type: ignore
|
||||
*prompts[3:],
|
||||
)
|
||||
)
|
||||
)
|
||||
if fileFormat == 0
|
||||
else (
|
||||
" 指令总长:{},最高延迟:{},结构大小{},终点坐标{}".format(
|
||||
*(
|
||||
to_BDX_file_in_score(cvt_mid, cvt_cfg, *prompts[3:])
|
||||
to_BDX_file_in_score(
|
||||
cvt_mid,
|
||||
out_path,
|
||||
Musicreater.DEFAULT_PROGRESSBAR_STYLE if prompts[2] else None,
|
||||
*prompts[3:],
|
||||
)
|
||||
if playerFormat == 1
|
||||
else to_BDX_file_in_delay(cvt_mid, cvt_cfg, *prompts[3:])
|
||||
else to_BDX_file_in_delay(
|
||||
cvt_mid,
|
||||
out_path,
|
||||
Musicreater.DEFAULT_PROGRESSBAR_STYLE if prompts[2] else None,
|
||||
*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
|
||||
else (
|
||||
" 结构大小:{},延迟总数:{},指令数量:{}".format(
|
||||
*(
|
||||
cvt_method(
|
||||
cvt_mid,
|
||||
out_path,
|
||||
*prompts[3:],
|
||||
)
|
||||
)
|
||||
)
|
||||
if playerFormat == 1
|
||||
else " 结构大小:{},延迟总数:{}".format(
|
||||
*(
|
||||
cvt_method(
|
||||
cvt_mid,
|
||||
out_path,
|
||||
# Musicreater.DEFAULT_PROGRESSBAR_STYLE if prompts[2] else None, # type: ignore
|
||||
*prompts[3:],
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -7,7 +7,8 @@ print(
|
||||
Musicreater.experiment.FutureMidiConvertM4.from_midi_file(
|
||||
input("midi路径:"), old_exe_format=False
|
||||
),
|
||||
Musicreater.plugin.ConvertConfig(input("输出路径:"), volume=1),
|
||||
input("输出路径:"),
|
||||
# Musicreater.plugin.ConvertConfig(input("输出路径:"),),
|
||||
max_height=32,
|
||||
)
|
||||
)
|
||||
|
||||
16
example_singleConvert.py
Normal file
16
example_singleConvert.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import Musicreater
|
||||
import Musicreater.plugin
|
||||
import Musicreater.plugin.mcstructfile
|
||||
|
||||
print(
|
||||
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay(
|
||||
Musicreater.MidiConvert.from_midi_file(
|
||||
input("midi路径:"),
|
||||
old_exe_format=False,
|
||||
# note_table_replacement={"note.harp": "note.flute"},
|
||||
),
|
||||
input("输出路径:"),
|
||||
# Musicreater.plugin.ConvertConfig(input("输出路径:"),),
|
||||
# max_height=32,
|
||||
)
|
||||
)
|
||||
24
example_websocket.py
Normal file
24
example_websocket.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import Musicreater
|
||||
import Musicreater.plugin
|
||||
import Musicreater.plugin.websocket
|
||||
|
||||
import os
|
||||
|
||||
dire = input("midi目录:")
|
||||
|
||||
print(
|
||||
Musicreater.plugin.websocket.to_websocket_server(
|
||||
[
|
||||
Musicreater.MidiConvert.from_midi_file(
|
||||
os.path.join(dire, names), old_exe_format=False
|
||||
)
|
||||
for names in os.listdir(
|
||||
dire,
|
||||
)
|
||||
if names.endswith((".mid", ".midi"))
|
||||
],
|
||||
input("服务器地址:"),
|
||||
int(input("服务器端口:")),
|
||||
Musicreater.DEFAULT_PROGRESSBAR_STYLE,
|
||||
)
|
||||
)
|
||||
163
let_future_java.py
Normal file
163
let_future_java.py
Normal file
@@ -0,0 +1,163 @@
|
||||
"""
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from typing import Optional, Tuple
|
||||
|
||||
import Musicreater.experiment
|
||||
from Musicreater.plugin.archive import compress_zipfile
|
||||
from Musicreater.utils import guess_deviation, is_in_diapason
|
||||
|
||||
|
||||
def to_zip_pack_in_score(
|
||||
midi_cvt: Musicreater.experiment.FutureMidiConvertJavaE,
|
||||
dist_path: str,
|
||||
progressbar_style: Optional[Musicreater.experiment.ProgressBarStyle],
|
||||
scoreboard_name: str = "mscplay",
|
||||
sound_source: str = "ambient",
|
||||
auto_reset: bool = False,
|
||||
) -> Tuple[int, int]:
|
||||
"""
|
||||
将midi以计分播放器形式转换为我的世界函数附加包
|
||||
|
||||
Parameters
|
||||
----------
|
||||
midi_cvt: MidiConvert 对象
|
||||
用于转换的MidiConvert对象
|
||||
dist_path: str
|
||||
转换结果输出的目标路径
|
||||
progressbar_style: ProgressBarStyle 对象
|
||||
进度条对象
|
||||
scoreboard_name: str
|
||||
我的世界的计分板名称
|
||||
auto_reset: bool
|
||||
是否自动重置计分板
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple[int指令数量, int音乐总延迟]
|
||||
"""
|
||||
|
||||
cmdlist, maxlen, maxscore = midi_cvt.to_command_list_in_java_score(
|
||||
scoreboard_name=scoreboard_name,
|
||||
source_of_sound=sound_source,
|
||||
)
|
||||
|
||||
# 当文件f夹{self.outputPath}/temp/mscplyfuncs存在时清空其下所有项目,然后创建
|
||||
if os.path.exists(f"{dist_path}/temp/mscplyfuncs/"):
|
||||
shutil.rmtree(f"{dist_path}/temp/mscplyfuncs/")
|
||||
os.makedirs(f"{dist_path}/temp/mscplyfuncs/mscplay")
|
||||
|
||||
# 写入stop.mcfunction
|
||||
with open(
|
||||
f"{dist_path}/temp/mscplyfuncs/stop.mcfunction", "w", encoding="utf-8"
|
||||
) as f:
|
||||
f.write("scoreboard players reset @a {}".format(scoreboard_name))
|
||||
|
||||
# 将命令列表写入文件
|
||||
index_file = open(
|
||||
f"{dist_path}/temp/mscplyfuncs/index.mcfunction", "w", encoding="utf-8"
|
||||
)
|
||||
for i in range(len(cmdlist)):
|
||||
index_file.write(f"function mscplyfuncs:mscplay/track{i + 1}\n")
|
||||
with open(
|
||||
f"{dist_path}/temp/mscplyfuncs/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[score_{0}_min=1] {0} 1\n".format(
|
||||
scoreboard_name
|
||||
),
|
||||
(
|
||||
"scoreboard players reset @a[score_{0}_min={1}] {0}\n".format(
|
||||
scoreboard_name, maxscore + 20
|
||||
)
|
||||
if auto_reset
|
||||
else ""
|
||||
),
|
||||
f"function mscplyfuncs:mscplay/progressShow\n" if progressbar_style else "",
|
||||
)
|
||||
)
|
||||
|
||||
if progressbar_style:
|
||||
with open(
|
||||
f"{dist_path}/temp/mscplyfuncs/mscplay/progressShow.mcfunction",
|
||||
"w",
|
||||
encoding="utf-8",
|
||||
) as f:
|
||||
f.writelines(
|
||||
"\n".join(
|
||||
[
|
||||
single_cmd.cmd
|
||||
for single_cmd in midi_cvt.form_java_progress_bar(
|
||||
maxscore, scoreboard_name, progressbar_style
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
index_file.close()
|
||||
|
||||
if os.path.exists(f"{dist_path}/{midi_cvt.music_name}.zip"):
|
||||
os.remove(f"{dist_path}/{midi_cvt.music_name}.zip")
|
||||
compress_zipfile(
|
||||
f"{dist_path}/temp/",
|
||||
f"{dist_path}/{midi_cvt.music_name}[JEscore].zip",
|
||||
)
|
||||
|
||||
shutil.rmtree(f"{dist_path}/temp/")
|
||||
|
||||
return maxlen, maxscore
|
||||
|
||||
|
||||
msc_cvt = Musicreater.experiment.FutureMidiConvertJavaE.from_midi_file(
|
||||
input("midi路径:"),
|
||||
play_speed=float(input("播放速度:")),
|
||||
old_exe_format=True,
|
||||
note_table_replacement=Musicreater.MC_EILLES_RTJE12_INSTRUMENT_REPLACE_TABLE,
|
||||
# pitched_note_table=Musicreater.MM_NBS_PITCHED_INSTRUMENT_TABLE,
|
||||
)
|
||||
|
||||
msc_cvt.set_deviation(
|
||||
guess_deviation(
|
||||
msc_cvt.total_note_count,
|
||||
len(msc_cvt.note_count_per_instrument),
|
||||
msc_cvt.note_count_per_instrument,
|
||||
music_channels=msc_cvt.channels,
|
||||
)
|
||||
)
|
||||
|
||||
in_diapason_count = 0
|
||||
for this_note in [k for j in msc_cvt.channels.values() for k in j]:
|
||||
if is_in_diapason(
|
||||
this_note.note_pitch + msc_cvt.music_deviation, this_note.sound_name
|
||||
):
|
||||
in_diapason_count += 1
|
||||
|
||||
|
||||
zip_res = to_zip_pack_in_score(
|
||||
msc_cvt,
|
||||
input("输出路径:"),
|
||||
Musicreater.experiment.ProgressBarStyle(),
|
||||
# Musicreater.plugin.ConvertConfig(input("输出路径:"),),
|
||||
scoreboard_name=input("计分板名称:"),
|
||||
sound_source=input("发音源:"),
|
||||
auto_reset=True,
|
||||
)
|
||||
print(
|
||||
"符合音符播放音高的音符数量:{}/{}({:.2f}%)".format(
|
||||
in_diapason_count,
|
||||
msc_cvt.total_note_count,
|
||||
in_diapason_count * 100 / msc_cvt.total_note_count,
|
||||
),
|
||||
"\n指令数量:{};音乐总延迟:{}".format(*zip_res),
|
||||
)
|
||||
98
pyproject.toml
Normal file
98
pyproject.toml
Normal file
@@ -0,0 +1,98 @@
|
||||
[project]
|
||||
name = "Musicreater"
|
||||
dynamic = ["version"]
|
||||
requires-python = ">= 3.8, < 4.0"
|
||||
dependencies = [
|
||||
"mido >= 1.3",
|
||||
"xxhash >= 3",
|
||||
]
|
||||
|
||||
authors = [
|
||||
{ name = "金羿Eilles" },
|
||||
{ name = "玉衡Alioth" },
|
||||
{ name = "鱼旧梦ElapsingDreams" },
|
||||
{ name = "睿乐组织 TriMO", email = "TriM-Organization@hotmail.com" },
|
||||
]
|
||||
maintainers = [
|
||||
{ name = "金羿Eilles", email = "EillesWan@outlook.com" },
|
||||
]
|
||||
|
||||
description = "A free open source library used for dealing with **Minecraft** digital musics."
|
||||
readme = "README_EN.md"
|
||||
license = { file = "LICENSE.md" }
|
||||
|
||||
keywords = ["midi", "minecraft", "minecraft: bedrock edition"]
|
||||
classifiers = [
|
||||
"Intended Audience :: Developers",
|
||||
"Natural Language :: Chinese (Simplified)",
|
||||
"Operating System :: OS Independent",
|
||||
"Topic :: Software Development :: Libraries",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Multimedia",
|
||||
"Topic :: Multimedia :: Sound/Audio :: MIDI",
|
||||
]
|
||||
|
||||
|
||||
[project.urls]
|
||||
# Homepage = "https://example.com"
|
||||
# Documentation = "https://readthedocs.org"
|
||||
Repository = "https://gitee.com/TriM-Organization/Musicreater"
|
||||
Issues = "https://gitee.com/TriM-Organization/Musicreater/issues"
|
||||
Mirror-Repository = "https://github.com/TriM-Organization/Musicreater"
|
||||
Mirror-Issues = "https://github.com/TriM-Organization/Musicreater/issues"
|
||||
|
||||
|
||||
[project.optional-dependencies]
|
||||
full = [
|
||||
"TrimMCStruct <= 0.0.5.9",
|
||||
"brotli >= 1.0.0",
|
||||
]
|
||||
dev = [
|
||||
"TrimMCStruct <= 0.0.5.9",
|
||||
"brotli >= 1.0.0",
|
||||
"dill",
|
||||
"rich",
|
||||
"pyinstaller",
|
||||
"twine",
|
||||
]
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["pdm-backend"]
|
||||
build-backend = "pdm.backend"
|
||||
|
||||
# https://backend.pdm-project.org/build_config/#build-configurations
|
||||
[tool.pdm.build]
|
||||
# includes = [
|
||||
# # "README_EN.md",
|
||||
# # "README.md",
|
||||
# # "LICENSE.md",
|
||||
# # "Musicreater/",
|
||||
# # "docs/",
|
||||
# ]
|
||||
source-includes = [
|
||||
"README_EN.md",
|
||||
"README.md",
|
||||
"LICENSE.md",
|
||||
]
|
||||
excludes = [
|
||||
"fcwslib/",
|
||||
"bgArrayLib/",
|
||||
"Packer/",
|
||||
"resources/",
|
||||
"./*.mid",
|
||||
"./*.msq",
|
||||
"./*.fsq",
|
||||
"./MSCT_Packer.py",
|
||||
"resources/poem.md",
|
||||
]
|
||||
|
||||
|
||||
[tool.pdm.version]
|
||||
source = "file"
|
||||
path = "Musicreater/__init__.py"
|
||||
|
||||
|
||||
[tool.pyright]
|
||||
typeCheckingMode = "basic"
|
||||
@@ -1 +0,0 @@
|
||||
mido>=1.3
|
||||
@@ -9,7 +9,7 @@ zh-CN 金羿
|
||||
zh-TW 金羿
|
||||
zh-ME 金羿羿喵
|
||||
zh-HK 金 羿
|
||||
en-GB Eilles Wan
|
||||
en-GB Eilles
|
||||
en-US EillesWan
|
||||
|
||||
启诸葛亮与八卦阵
|
||||
|
||||
@@ -43,5 +43,5 @@
|
||||
>
|
||||
> Just let the wonderful music of peace surround the world
|
||||
>
|
||||
> ---- Eilles Wan
|
||||
> ---- Eilles
|
||||
> 7/5 2022
|
||||
260
resources/test/enfasted_list_merging_test.py
Normal file
260
resources/test/enfasted_list_merging_test.py
Normal file
@@ -0,0 +1,260 @@
|
||||
import random
|
||||
import time
|
||||
from itertools import chain
|
||||
from multiprocessing import Pool, Process, freeze_support
|
||||
|
||||
from rich.console import Console
|
||||
from rich.progress import Progress
|
||||
from rich.table import Table
|
||||
|
||||
console = Console()
|
||||
|
||||
# gening_stst = {"NOWIDX": 0, "DATA": {}}
|
||||
|
||||
|
||||
# 生成单个字典的函数(用于多进程)
|
||||
def generate_single_dict(args):
|
||||
dict_id, dict_size = args
|
||||
# if dict_id:
|
||||
# console.print(
|
||||
# f"字典 {dict_id + 1} 大小 {dict_size} 生成中...",
|
||||
# )
|
||||
# else:
|
||||
# console.print(
|
||||
# f"\n字典 {dict_id + 1} 大小 {dict_size} 生成中...",
|
||||
# )
|
||||
# final_d = {}
|
||||
# gening_stst["DATA"][dict_id] = 0
|
||||
# for i in range(dict_size):
|
||||
# final_d[i] = [random.randint(0, 1000) for _ in range(random.randint(10000, 99999))]
|
||||
# gening_stst["DATA"][dict_id] += 1
|
||||
return dict_id, {
|
||||
i: [random.randint(0, 1000) for _ in range(random.randint(10000, 90000))]
|
||||
for i in range(dict_size)
|
||||
}
|
||||
# return dict_id, final_d
|
||||
|
||||
|
||||
# 合并函数定义
|
||||
def chain_merging(dict_info: dict):
|
||||
return sorted(chain(*dict_info.values()))
|
||||
|
||||
|
||||
def seq_merging(dict_info: dict):
|
||||
return sorted([i for sub in dict_info.values() for i in sub])
|
||||
|
||||
|
||||
def summing(*_):
|
||||
k = []
|
||||
for i in _:
|
||||
k += i
|
||||
return k
|
||||
|
||||
|
||||
def plus_merging(dict_info: dict):
|
||||
return sorted(summing(*dict_info.values()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
freeze_support() # Windows系统需要这个调用
|
||||
|
||||
# 测试配置
|
||||
dict_size = 50 # 每个字典的键值对数量
|
||||
num_tests = 50 # 测试次数
|
||||
|
||||
function_list = [chain_merging, seq_merging, plus_merging]
|
||||
# dict_list = []
|
||||
results = {func.__name__: [] for func in function_list}
|
||||
|
||||
# 多进程生成多个字典
|
||||
with Progress() as progress:
|
||||
task = progress.add_task("[green]进行速度测试...", total=num_tests)
|
||||
# gen_task = progress.add_task("[cyan] - 生成测试数据...", total=num_tests)
|
||||
with Pool() as pool:
|
||||
args_list = [
|
||||
(
|
||||
i,
|
||||
dict_size,
|
||||
)
|
||||
for i in range(num_tests)
|
||||
]
|
||||
|
||||
# def disp_work():
|
||||
# while gening_stst["NOWIDX"] < num_tests:
|
||||
# progress.update(
|
||||
# gen_task,
|
||||
# advance=1,
|
||||
# description=f"[cyan]正在生成 {gening_stst['DATA']['NOWIDX']}/{dict_size -1}",
|
||||
# # description="正在生成..."+console._render_buffer(
|
||||
# # console.render(table,),
|
||||
# # ),
|
||||
# )
|
||||
|
||||
# Process(target=disp_work).start()
|
||||
|
||||
for result in pool.imap_unordered(generate_single_dict, args_list):
|
||||
# dict_list.append(result)
|
||||
progress.update(
|
||||
task,
|
||||
advance=1,
|
||||
description=f"[cyan]正在测试 {result[0] + 1}/{num_tests}",
|
||||
# description="正在生成..."+console._render_buffer(
|
||||
# console.render(table,),
|
||||
# ),
|
||||
# refresh=True,
|
||||
)
|
||||
|
||||
# gening_stst["NOWIDX"] += 1
|
||||
|
||||
# for _ in range(num_tests):
|
||||
# 随机选择字典和打乱函数顺序
|
||||
# current_dict = generate_single_dict((_, dict_size))
|
||||
# progress.update(
|
||||
# test_task,
|
||||
# advance=1,
|
||||
# # description=f"[cyan]正在测试 {_}/{num_tests -1}",
|
||||
# # description="正在测试..."+console._render_buffer(
|
||||
# # console.render(table,progress.console.options),
|
||||
# # ),
|
||||
# # refresh=True,
|
||||
# )
|
||||
|
||||
# rangen_task = progress.add_task(
|
||||
# "[green]正在生成测试数据...",
|
||||
# total=dict_size,
|
||||
# )
|
||||
# current_dict = {}
|
||||
# desc = "正在生成序列 {}/{}".format("{}",dict_size-1)
|
||||
|
||||
# for i in range(dict_size):
|
||||
# # print("正在生成第", i, "个序列",end="\r",flush=True)
|
||||
# progress.update(rangen_task, advance=1, description=desc.format(i))
|
||||
# current_dict[i] = [random.randint(0, 1000) for _ in range(random.randint(10000, 99999))]
|
||||
|
||||
shuffled_funcs = random.sample(function_list, len(function_list))
|
||||
# table.rows
|
||||
# table.columns = fine_column
|
||||
# progress.live
|
||||
# progress.console._buffer.extend(progress.console.render(table))
|
||||
# for j in progress.console.render(table,progress.console.options):
|
||||
# progress.console._buffer.insert(0,j)
|
||||
|
||||
for i, func in enumerate(shuffled_funcs):
|
||||
|
||||
start = time.perf_counter()
|
||||
func(result[1])
|
||||
elapsed = time.perf_counter() - start
|
||||
results[func.__name__].append(elapsed)
|
||||
# gening_stst["NOWIDX"] = num_tests
|
||||
|
||||
# fine_column = table.columns.copy()
|
||||
|
||||
# for func in function_list:
|
||||
# name = func.__name__
|
||||
|
||||
# table.add_row(
|
||||
# name,
|
||||
# f"-",
|
||||
# f"-",
|
||||
# f"-",
|
||||
# f"-",
|
||||
# )
|
||||
|
||||
# # proc_pool = []
|
||||
|
||||
# 测试执行部分(保持顺序执行)
|
||||
# with Progress() as progress:
|
||||
# # progress.live.update(table, refresh=True)
|
||||
# # progress.live.process_renderables([table],)
|
||||
# # print([console._render_buffer(
|
||||
# # console.render(table,),
|
||||
# # )])
|
||||
# # progress.console._buffer.extend(progress.console.render(table))
|
||||
# test_task = progress.add_task("[cyan]进行速度测试...", total=num_tests)
|
||||
|
||||
# for _ in range(num_tests):
|
||||
# # 随机选择字典和打乱函数顺序
|
||||
# # current_dict = generate_single_dict((_, dict_size))
|
||||
# progress.update(
|
||||
# test_task,
|
||||
# advance=1,
|
||||
# description=f"[cyan]正在测试 {_}/{num_tests -1}",
|
||||
# # description="正在测试..."+console._render_buffer(
|
||||
# # console.render(table,progress.console.options),
|
||||
# # ),
|
||||
# # refresh=True,
|
||||
# )
|
||||
|
||||
# rangen_task = progress.add_task(
|
||||
# "[green]正在生成测试数据...",
|
||||
# total=dict_size,
|
||||
# )
|
||||
# current_dict = {}
|
||||
# desc = "正在生成序列 {}/{}".format("{}",dict_size-1)
|
||||
|
||||
# for i in range(dict_size):
|
||||
# # print("正在生成第", i, "个序列",end="\r",flush=True)
|
||||
# progress.update(rangen_task, advance=1, description=desc.format(i))
|
||||
# current_dict[i] = [random.randint(0, 1000) for _ in range(random.randint(10000, 99999))]
|
||||
|
||||
# shuffled_funcs = random.sample(function_list, len(function_list))
|
||||
# # table.rows
|
||||
# # table.columns = fine_column
|
||||
# # progress.live
|
||||
# # progress.console._buffer.extend(progress.console.render(table))
|
||||
# # for j in progress.console.render(table,progress.console.options):
|
||||
# # progress.console._buffer.insert(0,j)
|
||||
|
||||
# for i, func in enumerate(shuffled_funcs):
|
||||
|
||||
# start = time.perf_counter()
|
||||
# func(current_dict)
|
||||
# elapsed = time.perf_counter() - start
|
||||
# results[func.__name__].append(elapsed)
|
||||
|
||||
# times = results[func.__name__]
|
||||
# avg_time = sum(times) / len(times)
|
||||
# min_time = min(times)
|
||||
# max_time = max(times)
|
||||
|
||||
# table.columns[0]
|
||||
|
||||
# table.columns[0]._cells[i] = func.__name__
|
||||
# table.columns[1]._cells[i] = f"{avg_time:.5f}"
|
||||
# table.columns[2]._cells[i] = f"{min_time:.5f}"
|
||||
# table.columns[3]._cells[i] = f"{max_time:.5f}"
|
||||
# table.columns[4]._cells[i] = str(len(times))
|
||||
|
||||
# progress.update(test_task, advance=0.5)
|
||||
|
||||
# 结果展示部分
|
||||
|
||||
# 结果表格
|
||||
table = Table(title="\n[cyan]性能测试结果", show_header=True, header_style="bold")
|
||||
table.add_column("函数名称", style="dim", width=15)
|
||||
table.add_column("平均耗时 (秒)", justify="right")
|
||||
table.add_column("最小耗时 (秒)", justify="right")
|
||||
table.add_column("最大耗时 (秒)", justify="right")
|
||||
table.add_column("测试次数", justify="right")
|
||||
|
||||
for i, func in enumerate(function_list):
|
||||
name = func.__name__
|
||||
times = results[name]
|
||||
avg_time = sum(times) / len(times)
|
||||
min_time = min(times)
|
||||
max_time = max(times)
|
||||
|
||||
table.add_row(
|
||||
name,
|
||||
f"{avg_time:.5f}",
|
||||
f"{min_time:.5f}",
|
||||
f"{max_time:.5f}",
|
||||
str(len(times)),
|
||||
)
|
||||
# table.columns[0]._cells[i] = name
|
||||
# table.columns[1]._cells[i] = f"{avg_time:.5f}"
|
||||
# table.columns[2]._cells[i] = f"{min_time:.5f}"
|
||||
# table.columns[3]._cells[i] = f"{max_time:.5f}"
|
||||
# table.columns[4]._cells[i] = str(len(times))
|
||||
|
||||
console.print(table)
|
||||
39
resources/test/list_merging_text.py
Normal file
39
resources/test/list_merging_text.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import random
|
||||
import time
|
||||
from itertools import chain
|
||||
|
||||
print("生成序列中")
|
||||
|
||||
fine_dict = {}
|
||||
|
||||
for i in range(50):
|
||||
print("正在生成第", i, "个序列",end="\r",flush=True)
|
||||
fine_dict[i] = [random.randint(0, 1000) for _ in range(random.randint(10000, 99999))]
|
||||
|
||||
print("序列生成完成")
|
||||
|
||||
def chain_merging(dict_info: dict):
|
||||
return sorted(chain(*dict_info.values()))
|
||||
|
||||
def seq_merging(dict_info: dict):
|
||||
return sorted([i for sub in dict_info.values() for i in sub])
|
||||
|
||||
def summing(*_):
|
||||
k = []
|
||||
for i in _:
|
||||
k += i
|
||||
return k
|
||||
|
||||
def plus_merging(dict_info: dict):
|
||||
return sorted(summing(*dict_info.values()))
|
||||
|
||||
function_list = [chain_merging, seq_merging, plus_merging]
|
||||
|
||||
|
||||
for func in function_list:
|
||||
print("正在使用",func.__name__,"函数",)
|
||||
start = time.time()
|
||||
func(fine_dict)
|
||||
print("耗时",time.time() - start)
|
||||
|
||||
print("结束")
|
||||
@@ -1,6 +1,6 @@
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from scipy.optimize import curve_fit
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
def q_function1(x, a, a2, c1,):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
不得用于商业用途
|
||||
若 音·创 库被用于商业用途,应当将其剔除
|
||||
|
||||
版权所有 © 2024 诸葛亮与八卦阵
|
||||
版权所有 © 2025 诸葛亮与八卦阵
|
||||
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,
|
||||
)
|
||||
39
test_fsq_opera.py
Normal file
39
test_fsq_opera.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from rich.pretty import pprint
|
||||
|
||||
import Musicreater
|
||||
from Musicreater.utils import (
|
||||
load_decode_fsq_flush_release,
|
||||
load_decode_musicsequence_metainfo,
|
||||
)
|
||||
|
||||
msc_seq = Musicreater.MusicSequence.from_mido(
|
||||
Musicreater.mido.MidiFile(
|
||||
"./resources/测试片段.mid",
|
||||
),
|
||||
"TEST-测试片段",
|
||||
)
|
||||
|
||||
pprint("音乐源取入成功:")
|
||||
pprint(msc_seq)
|
||||
|
||||
with open("test.fsq", "wb") as f:
|
||||
f.write(fsq_bytes := msc_seq.encode_dump(flowing_codec_support=True))
|
||||
|
||||
with open("test.fsq", "rb") as f:
|
||||
msc_seq_r = Musicreater.MusicSequence.load_decode(f.read(), verify=True)
|
||||
|
||||
pprint("FSQ 传入类成功:")
|
||||
pprint(msc_seq_r)
|
||||
|
||||
|
||||
with open("test.fsq", "rb") as f:
|
||||
pprint("流式 FSQ 元数据:")
|
||||
pprint(metas := load_decode_musicsequence_metainfo(f))
|
||||
pprint("流式 FSQ 音符序列:")
|
||||
cnt = 0
|
||||
for i in load_decode_fsq_flush_release(f, metas[-2], metas[-3], metas[-1]):
|
||||
pprint(
|
||||
i,
|
||||
)
|
||||
cnt += 1
|
||||
pprint(f"共 {cnt} 个音符")
|
||||
33
test_future_kamires.py
Normal file
33
test_future_kamires.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import Musicreater.experiment
|
||||
import Musicreater.plugin
|
||||
import Musicreater.plugin.mcstructfile
|
||||
|
||||
msct = Musicreater.experiment.FutureMidiConvertKamiRES.from_midi_file(
|
||||
input("midi路径:"), old_exe_format=False
|
||||
)
|
||||
|
||||
opt = input("输出路径:")
|
||||
|
||||
print(
|
||||
"乐器使用情况",
|
||||
)
|
||||
|
||||
for name in sorted(
|
||||
set(
|
||||
[
|
||||
n.split(".")[0].replace("c", "").replace("d", "")
|
||||
for n in msct.note_count_per_instrument.keys()
|
||||
]
|
||||
)
|
||||
):
|
||||
print("\t", name, flush=True)
|
||||
|
||||
print(
|
||||
"\n输出:",
|
||||
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay(
|
||||
msct,
|
||||
opt,
|
||||
# Musicreater.plugin.ConvertConfig(input("输出路径:"),),
|
||||
max_height=32,
|
||||
),
|
||||
)
|
||||
33
test_future_lyric.py
Normal file
33
test_future_lyric.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import Musicreater.experiment
|
||||
import Musicreater.plugin
|
||||
import Musicreater.plugin.mcstructfile
|
||||
|
||||
msct = Musicreater.experiment.FutureMidiConvertLyricSupport.from_midi_file(
|
||||
input("midi路径:"), old_exe_format=False
|
||||
)
|
||||
|
||||
opt = input("输出路径:")
|
||||
|
||||
# print(
|
||||
# "乐器使用情况",
|
||||
# )
|
||||
|
||||
# for name in sorted(
|
||||
# set(
|
||||
# [
|
||||
# n.split(".")[0].replace("c", "").replace("d", "")
|
||||
# for n in msct.note_count_per_instrument.keys()
|
||||
# ]
|
||||
# )
|
||||
# ):
|
||||
# print("\t", name, flush=True)
|
||||
|
||||
print(
|
||||
"\n输出:",
|
||||
Musicreater.plugin.mcstructfile.to_mcstructure_file_in_delay(
|
||||
msct,
|
||||
opt,
|
||||
# Musicreater.plugin.ConvertConfig(input("输出路径:"),),
|
||||
max_height=32,
|
||||
),
|
||||
)
|
||||
34
test_msq_opera.py
Normal file
34
test_msq_opera.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from rich.pretty import pprint
|
||||
|
||||
import Musicreater
|
||||
from Musicreater.utils import (
|
||||
load_decode_msq_flush_release,
|
||||
load_decode_musicsequence_metainfo,
|
||||
)
|
||||
|
||||
msc_seq = Musicreater.MusicSequence.from_mido(
|
||||
Musicreater.mido.MidiFile(
|
||||
"./resources/测试片段.mid",
|
||||
),
|
||||
"TEST-测试片段",
|
||||
)
|
||||
|
||||
pprint("音乐源取入成功:")
|
||||
pprint(msc_seq)
|
||||
|
||||
with open("test.msq", "wb") as f:
|
||||
f.write(msq_bytes := msc_seq.encode_dump())
|
||||
|
||||
with open("test.msq", "rb") as f:
|
||||
msc_seq_r = Musicreater.MusicSequence.load_decode(f.read())
|
||||
|
||||
pprint("常规 MSQ 读取成功:")
|
||||
pprint(msc_seq_r)
|
||||
|
||||
|
||||
with open("test.msq", "rb") as f:
|
||||
pprint("流式 MSQ 元数据:")
|
||||
pprint(metas := load_decode_musicsequence_metainfo(f))
|
||||
pprint("流式 MSQ 音符序列:")
|
||||
for i in load_decode_msq_flush_release(f, metas[-2], metas[-3], metas[-1]):
|
||||
pprint(i)
|
||||
@@ -1,3 +1,7 @@
|
||||
python setup.py sdist bdist_wheel
|
||||
python -m twine upload dist/*
|
||||
uv build
|
||||
python -m twine check dist/*
|
||||
pause
|
||||
uv publish
|
||||
pause
|
||||
python clean_update.py
|
||||
pause
|
||||
Reference in New Issue
Block a user