Musicreater/docs/库的生成与功能文档.md

308 lines
17 KiB
Python
Raw Normal View History

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