mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2025-02-07 19:36:46 +08:00
修复打击乐器播放时的错误,即将步入红石音乐生成阶段
This commit is contained in:
parent
3da472052e
commit
556ce74cfb
@ -16,8 +16,9 @@ Terms & Conditions: License.md in the root directory
|
|||||||
# Email TriM-Organization@hotmail.com
|
# Email TriM-Organization@hotmail.com
|
||||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
from .main import *
|
|
||||||
|
|
||||||
__version__ = "1.0.0"
|
__version__ = "1.1.0"
|
||||||
__all__ = []
|
__all__ = []
|
||||||
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"), ("鸣凤鸽子", "MingFengPigeon"))
|
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"), ("鸣凤鸽子", "MingFengPigeon"))
|
||||||
|
|
||||||
|
from .main import *
|
||||||
|
@ -204,162 +204,3 @@ def _toCmdList_m4(
|
|||||||
|
|
||||||
return [tracks, cmdAmount, maxScore]
|
return [tracks, cmdAmount, maxScore]
|
||||||
|
|
||||||
|
|
||||||
def to_note_list(
|
|
||||||
self,
|
|
||||||
speed: float = 1.0,
|
|
||||||
) -> list:
|
|
||||||
"""
|
|
||||||
使用金羿的转换思路,将midi转换为我的世界音符盒所用的音高列表,并输出每个音符之后的延迟
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
speed: float
|
|
||||||
速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
tuple( list[tuple(str指令, int距离上一个指令的延迟 ),...], int音乐时长游戏刻 )
|
|
||||||
"""
|
|
||||||
|
|
||||||
if speed == 0:
|
|
||||||
if self.debug_mode:
|
|
||||||
raise ZeroSpeedError("播放速度仅可为正实数")
|
|
||||||
speed = 1
|
|
||||||
|
|
||||||
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
|
||||||
channels = empty_midi_channels()
|
|
||||||
|
|
||||||
# 我们来用通道统计音乐信息
|
|
||||||
# 但是是用分轨的思路的
|
|
||||||
for track_no, track in enumerate(self.midi.tracks):
|
|
||||||
microseconds = 0
|
|
||||||
|
|
||||||
for msg in track:
|
|
||||||
if msg.time != 0:
|
|
||||||
try:
|
|
||||||
microseconds += msg.time * tempo / self.midi.ticks_per_beat / 1000
|
|
||||||
# print(microseconds)
|
|
||||||
except NameError:
|
|
||||||
if self.debug_mode:
|
|
||||||
raise NotDefineTempoError("计算当前分数时出错 未定义参量 Tempo")
|
|
||||||
else:
|
|
||||||
microseconds += (
|
|
||||||
msg.time
|
|
||||||
* mido.midifiles.midifiles.DEFAULT_TEMPO
|
|
||||||
/ self.midi.ticks_per_beat
|
|
||||||
) / 1000
|
|
||||||
|
|
||||||
if msg.is_meta:
|
|
||||||
if msg.type == "set_tempo":
|
|
||||||
tempo = msg.tempo
|
|
||||||
if self.debug_mode:
|
|
||||||
self.prt(f"TEMPO更改:{tempo}(毫秒每拍)")
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
if msg.channel > 15 and self.debug_mode:
|
|
||||||
raise ChannelOverFlowError(f"当前消息 {msg} 的通道超限(≤15)")
|
|
||||||
if not track_no in channels[msg.channel].keys():
|
|
||||||
channels[msg.channel][track_no] = []
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if msg.type == "program_change":
|
|
||||||
channels[msg.channel][track_no].append(
|
|
||||||
("PgmC", msg.program, microseconds)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif msg.type == "note_on" and msg.velocity != 0:
|
|
||||||
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"
|
|
||||||
):
|
|
||||||
channels[msg.channel][track_no].append(
|
|
||||||
("NoteE", msg.note, microseconds)
|
|
||||||
)
|
|
||||||
|
|
||||||
"""整合后的音乐通道格式
|
|
||||||
每个通道包括若干消息元素其中逃不过这三种:
|
|
||||||
|
|
||||||
1 切换乐器消息
|
|
||||||
("PgmC", 切换后的乐器ID: int, 距离演奏开始的毫秒)
|
|
||||||
|
|
||||||
2 音符开始消息
|
|
||||||
("NoteS", 开始的音符ID, 力度(响度), 距离演奏开始的毫秒)
|
|
||||||
|
|
||||||
3 音符结束消息
|
|
||||||
("NoteS", 结束的音符ID, 距离演奏开始的毫秒)"""
|
|
||||||
|
|
||||||
tracks = {}
|
|
||||||
|
|
||||||
# 此处 我们把通道视为音轨
|
|
||||||
for i in channels.keys():
|
|
||||||
# 如果当前通道为空 则跳过
|
|
||||||
if not channels[i]:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 第十通道是打击乐通道
|
|
||||||
SpecialBits = True if i == 9 else False
|
|
||||||
|
|
||||||
# nowChannel = []
|
|
||||||
|
|
||||||
for track_no, track in channels[i].items():
|
|
||||||
for msg in track:
|
|
||||||
if msg[0] == "PgmC":
|
|
||||||
InstID = msg[1]
|
|
||||||
|
|
||||||
elif msg[0] == "NoteS":
|
|
||||||
try:
|
|
||||||
soundID, _X = (
|
|
||||||
self.perc_inst_to_soundID_withX(InstID)
|
|
||||||
if SpecialBits
|
|
||||||
else self.inst_to_souldID_withX(InstID)
|
|
||||||
)
|
|
||||||
except UnboundLocalError as E:
|
|
||||||
if self.debug_mode:
|
|
||||||
raise NotDefineProgramError(f"未定义乐器便提前演奏。\n{E}")
|
|
||||||
else:
|
|
||||||
soundID, _X = (
|
|
||||||
self.perc_inst_to_soundID_withX(-1)
|
|
||||||
if SpecialBits
|
|
||||||
else self.inst_to_souldID_withX(-1)
|
|
||||||
)
|
|
||||||
score_now = round(msg[-1] / float(speed) / 50)
|
|
||||||
# print(score_now)
|
|
||||||
|
|
||||||
try:
|
|
||||||
tracks[score_now].append(
|
|
||||||
self.execute_cmd_head.format(player)
|
|
||||||
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
|
||||||
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
|
||||||
)
|
|
||||||
except KeyError:
|
|
||||||
tracks[score_now] = [
|
|
||||||
self.execute_cmd_head.format(player)
|
|
||||||
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
|
||||||
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
|
||||||
]
|
|
||||||
|
|
||||||
all_ticks = list(tracks.keys())
|
|
||||||
all_ticks.sort()
|
|
||||||
results = []
|
|
||||||
|
|
||||||
for i in range(len(all_ticks)):
|
|
||||||
for j in range(len(tracks[all_ticks[i]])):
|
|
||||||
results.append(
|
|
||||||
(
|
|
||||||
tracks[all_ticks[i]][j],
|
|
||||||
(
|
|
||||||
0
|
|
||||||
if j != 0
|
|
||||||
else (
|
|
||||||
all_ticks[i] - all_ticks[i - 1] if i != 0 else all_ticks[i]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return [results, max(all_ticks)]
|
|
||||||
|
@ -21,7 +21,7 @@ Terms & Conditions: License.md in the root directory
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import math
|
import math
|
||||||
from typing import Tuple, List
|
from typing import Tuple, List, Union
|
||||||
|
|
||||||
import mido
|
import mido
|
||||||
|
|
||||||
@ -87,6 +87,9 @@ class MidiConvert:
|
|||||||
execute_cmd_head: str
|
execute_cmd_head: str
|
||||||
"""execute指令头部"""
|
"""execute指令头部"""
|
||||||
|
|
||||||
|
channels: Dict[int, Dict[int, List[Tuple[str, int, int, Union[None, int]]]]]
|
||||||
|
"""频道信息字典"""
|
||||||
|
|
||||||
music_command_list: List[SingleCommand]
|
music_command_list: List[SingleCommand]
|
||||||
"""音乐指令列表"""
|
"""音乐指令列表"""
|
||||||
|
|
||||||
@ -128,6 +131,7 @@ class MidiConvert:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.progress_bar_command = self.music_command_list = []
|
self.progress_bar_command = self.music_command_list = []
|
||||||
|
self.channels = {}
|
||||||
self.music_tick_num = 0
|
self.music_tick_num = 0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -147,7 +151,7 @@ class MidiConvert:
|
|||||||
是否启用旧版(≤1.19)指令格式,默认为否
|
是否启用旧版(≤1.19)指令格式,默认为否
|
||||||
"""
|
"""
|
||||||
|
|
||||||
midi_music_name = os.path.splitext(os.path.basename(midi_file_path))[0]
|
midi_music_name = os.path.splitext(os.path.basename(midi_file_path))[0].replace(' ','_')
|
||||||
"""文件名,不含路径且不含后缀"""
|
"""文件名,不含路径且不含后缀"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -485,35 +489,19 @@ class MidiConvert:
|
|||||||
self.progress_bar_command = result
|
self.progress_bar_command = result
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def to_command_list_in_score(
|
def to_music_channels(
|
||||||
self,
|
self,
|
||||||
scoreboard_name: str = "mscplay",
|
) -> Dict[int, Dict[int, List[Tuple[str, int, int, Union[None, int]]]]]:
|
||||||
max_volume: float = 1.0,
|
|
||||||
speed: float = 1.0,
|
|
||||||
) -> Tuple[List[List[SingleCommand]], int, int]:
|
|
||||||
"""
|
"""
|
||||||
使用金羿的转换思路,将midi转换为我的世界命令列表
|
使用金羿的转换思路,将midi解析并转换为频道信息
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
scoreboard_name: str
|
|
||||||
我的世界的计分板名称
|
|
||||||
max_volume: float
|
|
||||||
最大播放音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放
|
|
||||||
speed: float
|
|
||||||
速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
tuple( list[list[SingleCommand指令,... ],... ], int指令数量, int音乐时长游戏刻 )
|
Dict[int, Dict[int, List[Tuple[str,int,int,Union[None,int]]]]]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if speed == 0:
|
|
||||||
raise ZeroSpeedError("播放速度仅可为正实数")
|
|
||||||
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
|
|
||||||
|
|
||||||
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
||||||
channels = empty_midi_channels()
|
midi_channels = empty_midi_channels()
|
||||||
|
|
||||||
# 我们来用通道统计音乐信息
|
# 我们来用通道统计音乐信息
|
||||||
# 但是是用分轨的思路的
|
# 但是是用分轨的思路的
|
||||||
@ -546,22 +534,22 @@ class MidiConvert:
|
|||||||
# except AttributeError:
|
# except AttributeError:
|
||||||
# pass
|
# pass
|
||||||
|
|
||||||
if not track_no in channels[msg.channel].keys():
|
if not track_no in midi_channels[msg.channel].keys():
|
||||||
channels[msg.channel][track_no] = []
|
midi_channels[msg.channel][track_no] = []
|
||||||
if msg.type == "program_change":
|
if msg.type == "program_change":
|
||||||
channels[msg.channel][track_no].append(
|
midi_channels[msg.channel][track_no].append(
|
||||||
("PgmC", msg.program, microseconds)
|
("PgmC", msg.program, microseconds)
|
||||||
)
|
)
|
||||||
|
|
||||||
elif msg.type == "note_on" and msg.velocity != 0:
|
elif msg.type == "note_on" and msg.velocity != 0:
|
||||||
channels[msg.channel][track_no].append(
|
midi_channels[msg.channel][track_no].append(
|
||||||
("NoteS", msg.note, msg.velocity, microseconds)
|
("NoteS", msg.note, msg.velocity, microseconds)
|
||||||
)
|
)
|
||||||
|
|
||||||
elif (msg.type == "note_on" and msg.velocity == 0) or (
|
elif (msg.type == "note_on" and msg.velocity == 0) or (
|
||||||
msg.type == "note_off"
|
msg.type == "note_off"
|
||||||
):
|
):
|
||||||
channels[msg.channel][track_no].append(
|
midi_channels[msg.channel][track_no].append(
|
||||||
("NoteE", msg.note, microseconds)
|
("NoteE", msg.note, microseconds)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -577,14 +565,46 @@ class MidiConvert:
|
|||||||
3 音符结束消息
|
3 音符结束消息
|
||||||
("NoteS", 结束的音符ID, 距离演奏开始的毫秒)"""
|
("NoteS", 结束的音符ID, 距离演奏开始的毫秒)"""
|
||||||
|
|
||||||
|
self.channels = midi_channels
|
||||||
|
return self.channels
|
||||||
|
|
||||||
|
def to_command_list_in_score(
|
||||||
|
self,
|
||||||
|
scoreboard_name: str = "mscplay",
|
||||||
|
max_volume: float = 1.0,
|
||||||
|
speed: float = 1.0,
|
||||||
|
) -> Tuple[List[List[SingleCommand]], int, int]:
|
||||||
|
"""
|
||||||
|
使用金羿的转换思路,将midi转换为我的世界命令列表
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
scoreboard_name: str
|
||||||
|
我的世界的计分板名称
|
||||||
|
max_volume: float
|
||||||
|
最大播放音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放
|
||||||
|
speed: float
|
||||||
|
速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
tuple( list[list[SingleCommand指令,... ],... ], int指令数量, int音乐时长游戏刻 )
|
||||||
|
"""
|
||||||
|
|
||||||
|
if speed == 0:
|
||||||
|
raise ZeroSpeedError("播放速度仅可为正实数")
|
||||||
|
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
|
||||||
|
|
||||||
tracks = []
|
tracks = []
|
||||||
cmdAmount = 0
|
cmdAmount = 0
|
||||||
maxScore = 0
|
maxScore = 0
|
||||||
|
|
||||||
|
self.to_music_channels()
|
||||||
|
|
||||||
# 此处 我们把通道视为音轨
|
# 此处 我们把通道视为音轨
|
||||||
for i in channels.keys():
|
for i in self.channels.keys():
|
||||||
# 如果当前通道为空 则跳过
|
# 如果当前通道为空 则跳过
|
||||||
if not channels[i]:
|
if not self.channels[i]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 第十通道是打击乐通道
|
# 第十通道是打击乐通道
|
||||||
@ -592,7 +612,7 @@ class MidiConvert:
|
|||||||
|
|
||||||
# nowChannel = []
|
# nowChannel = []
|
||||||
|
|
||||||
for track_no, track in channels[i].items():
|
for track_no, track in self.channels[i].items():
|
||||||
nowTrack = []
|
nowTrack = []
|
||||||
|
|
||||||
for msg in track:
|
for msg in track:
|
||||||
@ -669,77 +689,14 @@ class MidiConvert:
|
|||||||
raise ZeroSpeedError("播放速度仅可为正实数")
|
raise ZeroSpeedError("播放速度仅可为正实数")
|
||||||
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
|
max_volume = 1 if max_volume > 1 else (0.001 if max_volume <= 0 else max_volume)
|
||||||
|
|
||||||
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
self.to_music_channels()
|
||||||
channels = empty_midi_channels()
|
|
||||||
|
|
||||||
# 我们来用通道统计音乐信息
|
|
||||||
# 但是是用分轨的思路的
|
|
||||||
for track_no, track in enumerate(self.midi.tracks):
|
|
||||||
microseconds = 0
|
|
||||||
|
|
||||||
for msg in track:
|
|
||||||
if msg.time != 0:
|
|
||||||
try:
|
|
||||||
microseconds += (
|
|
||||||
msg.time * tempo / self.midi.ticks_per_beat / 1000
|
|
||||||
)
|
|
||||||
# print(microseconds)
|
|
||||||
except NameError:
|
|
||||||
# raise NotDefineTempoError("计算当前分数时出错 未定义参量 Tempo")
|
|
||||||
microseconds += (
|
|
||||||
msg.time
|
|
||||||
* mido.midifiles.midifiles.DEFAULT_TEMPO
|
|
||||||
/ self.midi.ticks_per_beat
|
|
||||||
) / 1000
|
|
||||||
|
|
||||||
if msg.is_meta:
|
|
||||||
if msg.type == "set_tempo":
|
|
||||||
tempo = msg.tempo
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
# 曾用于调试模式
|
|
||||||
# if msg.channel > 15:
|
|
||||||
# raise ChannelOverFlowError(f"当前消息 {msg} 的通道超限(≤15)")
|
|
||||||
if not track_no in channels[msg.channel].keys():
|
|
||||||
channels[msg.channel][track_no] = []
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if msg.type == "program_change":
|
|
||||||
channels[msg.channel][track_no].append(
|
|
||||||
("PgmC", msg.program, microseconds)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif msg.type == "note_on" and msg.velocity != 0:
|
|
||||||
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"
|
|
||||||
):
|
|
||||||
channels[msg.channel][track_no].append(
|
|
||||||
("NoteE", msg.note, microseconds)
|
|
||||||
)
|
|
||||||
|
|
||||||
"""整合后的音乐通道格式
|
|
||||||
每个通道包括若干消息元素其中逃不过这三种:
|
|
||||||
|
|
||||||
1 切换乐器消息
|
|
||||||
("PgmC", 切换后的乐器ID: int, 距离演奏开始的毫秒)
|
|
||||||
|
|
||||||
2 音符开始消息
|
|
||||||
("NoteS", 开始的音符ID, 力度(响度), 距离演奏开始的毫秒)
|
|
||||||
|
|
||||||
3 音符结束消息
|
|
||||||
("NoteS", 结束的音符ID, 距离演奏开始的毫秒)"""
|
|
||||||
|
|
||||||
tracks = {}
|
tracks = {}
|
||||||
|
|
||||||
# 此处 我们把通道视为音轨
|
# 此处 我们把通道视为音轨
|
||||||
for i in channels.keys():
|
for i in self.channels.keys():
|
||||||
# 如果当前通道为空 则跳过
|
# 如果当前通道为空 则跳过
|
||||||
if not channels[i]:
|
if not self.channels[i]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 第十通道是打击乐通道
|
# 第十通道是打击乐通道
|
||||||
@ -747,7 +704,7 @@ class MidiConvert:
|
|||||||
|
|
||||||
# nowChannel = []
|
# nowChannel = []
|
||||||
|
|
||||||
for track_no, track in channels[i].items():
|
for track_no, track in self.channels[i].items():
|
||||||
for msg in track:
|
for msg in track:
|
||||||
if msg[0] == "PgmC":
|
if msg[0] == "PgmC":
|
||||||
InstID = msg[1]
|
InstID = msg[1]
|
||||||
@ -755,7 +712,7 @@ class MidiConvert:
|
|||||||
elif msg[0] == "NoteS":
|
elif msg[0] == "NoteS":
|
||||||
try:
|
try:
|
||||||
soundID, _X = (
|
soundID, _X = (
|
||||||
self.perc_inst_to_soundID_withX(InstID)
|
self.perc_inst_to_soundID_withX(msg[1])
|
||||||
if SpecialBits
|
if SpecialBits
|
||||||
else self.inst_to_souldID_withX(InstID)
|
else self.inst_to_souldID_withX(InstID)
|
||||||
)
|
)
|
||||||
@ -772,14 +729,22 @@ class MidiConvert:
|
|||||||
try:
|
try:
|
||||||
tracks[score_now].append(
|
tracks[score_now].append(
|
||||||
self.execute_cmd_head.format(player_selector)
|
self.execute_cmd_head.format(player_selector)
|
||||||
+ f"playsound {soundID} @s ^ ^ ^{1 / max_volume - 1} {msg[2] / 128} "
|
+ f"playsound {soundID} @s ^ ^ ^{128 / max_volume / msg[2] - 1} {msg[2] / 128} "
|
||||||
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
+ (
|
||||||
|
""
|
||||||
|
if SpecialBits
|
||||||
|
else f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
tracks[score_now] = [
|
tracks[score_now] = [
|
||||||
self.execute_cmd_head.format(player_selector)
|
self.execute_cmd_head.format(player_selector)
|
||||||
+ f"playsound {soundID} @s ^ ^ ^{1 / max_volume - 1} {msg[2] / 128} "
|
+ f"playsound {soundID} @s ^ ^ ^{128 / max_volume / msg[2] - 1} {msg[2] / 128} "
|
||||||
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
+ (
|
||||||
|
""
|
||||||
|
if SpecialBits
|
||||||
|
else f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
all_ticks = list(tracks.keys())
|
all_ticks = list(tracks.keys())
|
||||||
@ -819,7 +784,6 @@ class MidiConvert:
|
|||||||
:return: dict()
|
:return: dict()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
# 一个midi中仅有16个通道 我们通过通道来识别而不是音轨
|
||||||
channels = empty_midi_channels()
|
channels = empty_midi_channels()
|
||||||
|
|
||||||
@ -850,7 +814,7 @@ class MidiConvert:
|
|||||||
try:
|
try:
|
||||||
# 曾用于调试模式
|
# 曾用于调试模式
|
||||||
# if msg.channel > 15:
|
# if msg.channel > 15:
|
||||||
# raise ChannelOverFlowError(f"当前消息 {msg} 的通道超限(≤15)")
|
# raise ChannelOverFlowError(f"当前消息 {msg} 的通道超限(≤15)")
|
||||||
if not track_no in channels[msg.channel].keys():
|
if not track_no in channels[msg.channel].keys():
|
||||||
channels[msg.channel][track_no] = []
|
channels[msg.channel][track_no] = []
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -887,7 +851,6 @@ class MidiConvert:
|
|||||||
|
|
||||||
return channels
|
return channels
|
||||||
|
|
||||||
|
|
||||||
def copy_important(self):
|
def copy_important(self):
|
||||||
dst = MidiConvert(
|
dst = MidiConvert(
|
||||||
midi_obj=None,
|
midi_obj=None,
|
||||||
@ -898,4 +861,3 @@ class MidiConvert:
|
|||||||
dst.progress_bar_command = [i.copy() for i in self.progress_bar_command]
|
dst.progress_bar_command = [i.copy() for i in self.progress_bar_command]
|
||||||
dst.music_tick_num = self.music_tick_num
|
dst.music_tick_num = self.music_tick_num
|
||||||
return dst
|
return dst
|
||||||
|
|
||||||
|
@ -67,3 +67,9 @@ def to_mcstructure_file_in_delay(
|
|||||||
struct.dump(f)
|
struct.dump(f)
|
||||||
|
|
||||||
return size, max_delay
|
return size, max_delay
|
||||||
|
|
||||||
|
|
||||||
|
def to_mcstructure_file_in_redstone(
|
||||||
|
midi_cvt: MidiConvert,
|
||||||
|
data_cfg: ConvertConfig,):
|
||||||
|
pass
|
116
Musicreater/plugin/noteblock.py
Normal file
116
Musicreater/plugin/noteblock.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
存放有关红石音乐生成操作的内容
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
版权所有 © 2023 音·创 开发者
|
||||||
|
Copyright © 2023 all the developers of Musicreater
|
||||||
|
|
||||||
|
开源相关声明请见 仓库根目录下的 License.md
|
||||||
|
Terms & Conditions: License.md in the root directory
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 睿穆组织 开发交流群 861684859
|
||||||
|
# Email TriM-Organization@hotmail.com
|
||||||
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
|
||||||
|
from ..exceptions import NotDefineProgramError, ZeroSpeedError
|
||||||
|
from ..main import MidiConvert
|
||||||
|
from ..subclass import SingleCommand
|
||||||
|
|
||||||
|
# 你以为写完了吗?其实并没有
|
||||||
|
|
||||||
|
|
||||||
|
def to_note_list(
|
||||||
|
midi_cvt: MidiConvert,
|
||||||
|
speed: float = 1.0,
|
||||||
|
) -> list:
|
||||||
|
"""
|
||||||
|
使用金羿的转换思路,将midi转换为我的世界音符盒所用的音高列表,并输出每个音符之后的延迟
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
speed: float
|
||||||
|
速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
tuple( list[tuple(str指令, int距离上一个指令的延迟 ),...], int音乐时长游戏刻 )
|
||||||
|
"""
|
||||||
|
|
||||||
|
if speed == 0:
|
||||||
|
raise ZeroSpeedError("播放速度仅可为正实数")
|
||||||
|
|
||||||
|
midi_channels = (
|
||||||
|
midi_cvt.to_music_channels() if not midi_cvt.channels else midi_cvt.channels
|
||||||
|
)
|
||||||
|
|
||||||
|
tracks = {}
|
||||||
|
|
||||||
|
# 此处 我们把通道视为音轨
|
||||||
|
for i in midi_channels.keys():
|
||||||
|
# 如果当前通道为空 则跳过
|
||||||
|
if not midi_channels[i]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 第十通道是打击乐通道
|
||||||
|
SpecialBits = True if i == 9 else False
|
||||||
|
|
||||||
|
# nowChannel = []
|
||||||
|
|
||||||
|
for track_no, track in midi_channels[i].items():
|
||||||
|
for msg in track:
|
||||||
|
if msg[0] == "PgmC":
|
||||||
|
InstID = msg[1]
|
||||||
|
|
||||||
|
elif msg[0] == "NoteS":
|
||||||
|
try:
|
||||||
|
soundID, _X = (
|
||||||
|
midi_cvt.perc_inst_to_soundID_withX(InstID)
|
||||||
|
if SpecialBits
|
||||||
|
else midi_cvt.inst_to_souldID_withX(InstID)
|
||||||
|
)
|
||||||
|
except UnboundLocalError as E:
|
||||||
|
soundID, _X = (
|
||||||
|
midi_cvt.perc_inst_to_soundID_withX(-1)
|
||||||
|
if SpecialBits
|
||||||
|
else midi_cvt.inst_to_souldID_withX(-1)
|
||||||
|
)
|
||||||
|
score_now = round(msg[-1] / float(speed) / 50)
|
||||||
|
# print(score_now)
|
||||||
|
|
||||||
|
try:
|
||||||
|
tracks[score_now].append(
|
||||||
|
self.execute_cmd_head.format(player)
|
||||||
|
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
||||||
|
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
||||||
|
)
|
||||||
|
except KeyError:
|
||||||
|
tracks[score_now] = [
|
||||||
|
self.execute_cmd_head.format(player)
|
||||||
|
+ f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2] / 128} "
|
||||||
|
f"{2 ** ((msg[1] - 60 - _X) / 12)}"
|
||||||
|
]
|
||||||
|
|
||||||
|
all_ticks = list(tracks.keys())
|
||||||
|
all_ticks.sort()
|
||||||
|
results = []
|
||||||
|
|
||||||
|
for i in range(len(all_ticks)):
|
||||||
|
for j in range(len(tracks[all_ticks[i]])):
|
||||||
|
results.append(
|
||||||
|
(
|
||||||
|
tracks[all_ticks[i]][j],
|
||||||
|
(
|
||||||
|
0
|
||||||
|
if j != 0
|
||||||
|
else (
|
||||||
|
all_ticks[i] - all_ticks[i - 1] if i != 0 else all_ticks[i]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return [results, max(all_ticks)]
|
41
Musicreater/plugin/websocket.py
Normal file
41
Musicreater/plugin/websocket.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
存放有关WebSocket服务器操作的内容
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
版权所有 © 2023 音·创 开发者
|
||||||
|
Copyright © 2023 all the developers of Musicreater
|
||||||
|
|
||||||
|
开源相关声明请见 仓库根目录下的 License.md
|
||||||
|
Terms & Conditions: License.md in the root directory
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 睿穆组织 开发交流群 861684859
|
||||||
|
# Email TriM-Organization@hotmail.com
|
||||||
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import 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))
|
||||||
|
|
21
Musicreater/plugin/websocket/__init__.py
Normal file
21
Musicreater/plugin/websocket/__init__.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
用以启动WebSocket服务器播放的附加功能
|
||||||
|
|
||||||
|
版权所有 © 2023 音·创 开发者
|
||||||
|
Copyright © 2023 all the developers of Musicreater
|
||||||
|
|
||||||
|
开源相关声明请见 仓库根目录下的 License.md
|
||||||
|
Terms & Conditions: License.md in the root directory
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 睿穆组织 开发交流群 861684859
|
||||||
|
# Email TriM-Organization@hotmail.com
|
||||||
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = []
|
||||||
|
__author__ = (("金羿", "Eilles Wan"),)
|
||||||
|
|
||||||
|
from .main import *
|
||||||
|
|
29
Musicreater/plugin/websocket/main.py
Normal file
29
Musicreater/plugin/websocket/main.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
版权所有 © 2023 音·创 开发者
|
||||||
|
Copyright © 2023 all the developers of Musicreater
|
||||||
|
|
||||||
|
开源相关声明请见 仓库根目录下的 License.md
|
||||||
|
Terms & Conditions: License.md in the root directory
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 睿穆组织 开发交流群 861684859
|
||||||
|
# Email TriM-Organization@hotmail.com
|
||||||
|
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||||
|
|
||||||
|
import fcwslib
|
||||||
|
|
||||||
|
from ...main import MidiConvert
|
||||||
|
|
||||||
|
from ..main import ConvertConfig
|
||||||
|
from ...subclass import SingleCommand
|
||||||
|
|
||||||
|
|
||||||
|
def open_websocket_server(
|
||||||
|
midi_cvt: MidiConvert,
|
||||||
|
data_cfg: ConvertConfig,
|
||||||
|
player: str = "@a",
|
||||||
|
server_dist: str = "localhost",
|
||||||
|
server_port: int = 8000,
|
||||||
|
):
|
||||||
|
wssever = fcwslib.Server(server=server_dist,port=server_port,debug_mode=False)
|
@ -54,7 +54,6 @@ if os.path.exists("./demo_config.json"):
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
prompts = json.load(open("./demo_config.json", "r", encoding="utf-8"))
|
prompts = json.load(open("./demo_config.json", "r", encoding="utf-8"))
|
||||||
prompts = prompts[:-1]
|
|
||||||
else:
|
else:
|
||||||
prompts = []
|
prompts = []
|
||||||
# 提示语 检测函数 错误提示语
|
# 提示语 检测函数 错误提示语
|
||||||
@ -115,8 +114,8 @@ print(
|
|||||||
else to_mcstructure_addon_in_delay(cvt_mid, cvt_cfg, *prompts[3:])
|
else to_mcstructure_addon_in_delay(cvt_mid, cvt_cfg, *prompts[3:])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
) if fileFormat == 0 else print(
|
if fileFormat == 0
|
||||||
" 指令总长:{},最高延迟:{},结构大小{},终点坐标{}".format(
|
else " 指令总长:{},最高延迟:{},结构大小{},终点坐标{}".format(
|
||||||
*(
|
*(
|
||||||
to_BDX_file_in_score(cvt_mid, cvt_cfg, *prompts[3:])
|
to_BDX_file_in_score(cvt_mid, cvt_cfg, *prompts[3:])
|
||||||
if playerFormat == 1
|
if playerFormat == 1
|
||||||
|
@ -7,6 +7,7 @@ print(
|
|||||||
Musicreater.MidiConvert.from_midi_file(input("midi路径:"), old_exe_format=False),
|
Musicreater.MidiConvert.from_midi_file(input("midi路径:"), old_exe_format=False),
|
||||||
Musicreater.plugin.ConvertConfig(
|
Musicreater.plugin.ConvertConfig(
|
||||||
input("输出路径:"),
|
input("输出路径:"),
|
||||||
|
volume=1
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user