学校的更改

This commit is contained in:
EillesWan 2022-10-16 21:19:14 +08:00
parent 451d1d8e80
commit 6cde168bf3
2 changed files with 92 additions and 99 deletions

View File

@ -4,14 +4,16 @@
from msctPkgver.main import * from msctPkgver.main import *
convertion = midiConvert() convertion = midiConvert()
convertion.convert(input('请输入midi文件路径'), input('请输入输出路径:')) convertion.convert(input("请输入midi文件路径"), input("请输入输出路径:"))
convertion.tomcpack( print(
1, convertion.tomcpack(
bool(int(input('是否自动重置计分板(1|0)'))), 3,
bool(int(input('是否开启进度条(1|0)'))), bool(int(input("是否自动重置计分板(1|0)"))),
input('请输入计分板名称:'), bool(int(input("是否开启进度条(1|0)"))),
float(input('请输入音量0-1')), input("请输入计分板名称:"),
float(input('请输入速度倍率:')), float(input("请输入音量0-1")),
float(input("请输入速度倍率:")),
)
) )
# for the test # for the test

View File

@ -64,38 +64,27 @@ def makeZip(sourceDir, outFilename, compression=8, exceptFile=None):
class SingleNote: class SingleNote:
def __init__(self, inst: int, pitch: int, velocity, startTime, lastTime): def __init__(self, instrument: int, pitch: int, velocity, startTime, lastTime):
'''用于存储单个音符的类 """用于存储单个音符的类
:param inst 乐器编号 :param inst 乐器编号
:param pitch 音符编号 :param pitch 音符编号
:param velocity 力度/响度 :param velocity 力度/响度
:param startTime 开始之时(ms) :param startTime 开始之时(ms)
此处的时间是用从乐曲开始到当前的毫秒数 此处的时间是用从乐曲开始到当前的毫秒数
:param lastTime 音符延续时间(ms)''' :param lastTime 音符延续时间(ms)"""
self.inst = inst self.instrument = instrument
self.note = pitch self.note = pitch
self.velocity = velocity self.velocity = velocity
self.startTime = startTime self.startTime = startTime
self.lastTime = lastTime self.lastTime = lastTime
@property
def inst(self):
return self.inst
@property
def note(self):
return self.note
@property
def velocity(self):
return self.velocity
@property @property
def startTime(self): def inst(self):
return self.startTime return self.instrument
@property @property
def lastTime(self): def pitch(self):
return self.lastTime return self.note
def __str__(self): def __str__(self):
return f"Note(inst = {self.inst}, pitch = {self.note}, velocity = {self.velocity}, startTime = {self.startTime}, lastTime = {self.lastTime}, )" return f"Note(inst = {self.inst}, pitch = {self.note}, velocity = {self.velocity}, startTime = {self.startTime}, lastTime = {self.lastTime}, )"
@ -113,7 +102,7 @@ class SingleNote:
} }
''' """
学习笔记 学习笔记
tempo: microseconds per quarter note 毫秒每四分音符换句话说就是一拍占多少毫秒 tempo: microseconds per quarter note 毫秒每四分音符换句话说就是一拍占多少毫秒
tick: midi帧 tick: midi帧
@ -129,7 +118,7 @@ tempo * amount_of_beats => 毫秒数
tempo * tick / ticks_per_beat => 毫秒数 tempo * tick / ticks_per_beat => 毫秒数
''' """
class midiConvert: class midiConvert:
@ -523,7 +512,7 @@ class midiConvert:
/ ((self.midi.ticks_per_beat * float(speed)) * 50000) / ((self.midi.ticks_per_beat * float(speed)) * 50000)
) )
except NameError: except NameError:
raise NotDefineTempoError('计算当前分数时出错 未定义参量 Tempo') raise NotDefineTempoError("计算当前分数时出错 未定义参量 Tempo")
maxscore = max(maxscore, nowscore) maxscore = max(maxscore, nowscore)
soundID, _X = self.__Inst2soundIDwithX(instrumentID) soundID, _X = self.__Inst2soundIDwithX(instrumentID)
singleTrack.append( singleTrack.append(
@ -563,7 +552,6 @@ class midiConvert:
# 一个midi中仅有16通道 我们通过通道来识别而不是音轨 # 一个midi中仅有16通道 我们通过通道来识别而不是音轨
channels = [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []] channels = [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]
# 我们来用通道统计音乐信息 # 我们来用通道统计音乐信息
for i, track in enumerate(self.midi.tracks): for i, track in enumerate(self.midi.tracks):
@ -575,8 +563,7 @@ class midiConvert:
try: try:
microseconds += msg.time * tempo / self.midi.ticks_per_beat microseconds += msg.time * tempo / self.midi.ticks_per_beat
except NameError: except NameError:
raise NotDefineTempoError('计算当前分数时出错 未定义参量 Tempo') raise NotDefineTempoError("计算当前分数时出错 未定义参量 Tempo")
if msg.is_meta: if msg.is_meta:
if msg.type == "set_tempo": if msg.type == "set_tempo":
@ -597,20 +584,17 @@ class midiConvert:
("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].append( channels[msg.channel].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].append( channels[msg.channel].append(("NoteE", msg.note, microseconds))
("NoteE", msg.note, microseconds)
)
"""整合后的音乐通道格式
'''整合后的音乐通道格式
每个通道包括若干消息元素其中逃不过这三种 每个通道包括若干消息元素其中逃不过这三种
1 切换乐器消息 1 切换乐器消息
@ -623,7 +607,7 @@ class midiConvert:
3 音符结束消息 3 音符结束消息
("NoteS", 结束的音符ID, 距离演奏开始的毫秒)''' ("NoteS", 结束的音符ID, 距离演奏开始的毫秒)"""
tracks = [] tracks = []
cmdAmount = 0 cmdAmount = 0
@ -635,7 +619,6 @@ class midiConvert:
# 如果当前通道为空 则跳过 # 如果当前通道为空 则跳过
if not track: if not track:
continue continue
if channels.index(track) == 0: if channels.index(track) == 0:
CheckFirstChannel = True CheckFirstChannel = True
@ -643,18 +626,17 @@ class midiConvert:
CheckFirstChannel = False CheckFirstChannel = False
nowTrack = [] nowTrack = []
for msg in track:
for msg in track:
if msg[0] == "PgmC": if msg[0] == "PgmC":
InstID = msg[1] InstID = msg[1]
elif msg[0] == "NoteS": elif msg[0] == "NoteS":
soundID, _X = self.__Inst2soundIDwithX(InstID) soundID, _X = self.__Inst2soundIDwithX(InstID)
score_now = round(msg[-1]/ float(speed) / 50000) score_now = round(msg[-1] / float(speed) / 50000)
maxScore = max(maxScore,score_now) maxScore = max(maxScore, score_now)
nowTrack.append( nowTrack.append(
"execute @a[scores={" "execute @a[scores={"
@ -666,12 +648,16 @@ class midiConvert:
) )
cmdAmount += 1 cmdAmount += 1
tracks.append(nowTrack) tracks.append(nowTrack)
return [tracks, cmdAmount, maxScore] return [tracks, cmdAmount, maxScore]
# 这才是加了线性插值的算法 # 这才是加了线性插值的算法
# 此算法有巨大问题
# 暂时无法使用
# 已经经过验证 需要再次修改
# 等一段时间后我来通知
def _toCmdList_m3( def _toCmdList_m3(
self, self,
scoreboardname: str = "mscplay", scoreboardname: str = "mscplay",
@ -679,7 +665,7 @@ class midiConvert:
speed: float = 1.0, speed: float = 1.0,
) -> list: ) -> list:
""" """
使用金羿的转换思路将midi转换为我的世界命令列表并使用线性插值算法优化音量此处的插值是固定斜率的方法 使用金羿的转换思路将midi转换为我的世界命令列表并使用线性插值算法优化音量
:param scoreboardname: 我的世界的计分板名称 :param scoreboardname: 我的世界的计分板名称
:param MaxVolume: 音量注意这里的音量范围为(0,1]如果超出将被处理为正确值其原理为在距离玩家 (1 / volume -1) 的地方播放音频 :param MaxVolume: 音量注意这里的音量范围为(0,1]如果超出将被处理为正确值其原理为在距离玩家 (1 / volume -1) 的地方播放音频
:param speed: 速度注意这里的速度指的是播放倍率其原理为在播放音频的时候每个音符的播放时间除以 speed :param speed: 速度注意这里的速度指的是播放倍率其原理为在播放音频的时候每个音符的播放时间除以 speed
@ -694,7 +680,6 @@ class midiConvert:
# 一个midi中仅有16通道 我们通过通道来识别而不是音轨 # 一个midi中仅有16通道 我们通过通道来识别而不是音轨
channels = [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []] channels = [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]
# 我们来用通道统计音乐信息 # 我们来用通道统计音乐信息
for i, track in enumerate(self.midi.tracks): for i, track in enumerate(self.midi.tracks):
@ -706,8 +691,7 @@ class midiConvert:
try: try:
microseconds += msg.time * tempo / self.midi.ticks_per_beat microseconds += msg.time * tempo / self.midi.ticks_per_beat
except NameError: except NameError:
raise NotDefineTempoError('计算当前分数时出错 未定义参量 Tempo') raise NotDefineTempoError("计算当前分数时出错 未定义参量 Tempo")
if msg.is_meta: if msg.is_meta:
if msg.type == "set_tempo": if msg.type == "set_tempo":
@ -728,20 +712,17 @@ class midiConvert:
("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].append( channels[msg.channel].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].append( channels[msg.channel].append(("NoteE", msg.note, microseconds))
("NoteE", msg.note, microseconds)
)
"""整合后的音乐通道格式
'''整合后的音乐通道格式
每个通道包括若干消息元素其中逃不过这三种 每个通道包括若干消息元素其中逃不过这三种
1 切换乐器消息 1 切换乐器消息
@ -754,8 +735,7 @@ class midiConvert:
3 音符结束消息 3 音符结束消息
("NoteS", 结束的音符ID, 距离演奏开始的毫秒)''' ("NoteS", 结束的音符ID, 距离演奏开始的毫秒)"""
note_channels = [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []] note_channels = [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]
@ -764,27 +744,34 @@ class midiConvert:
# 如果当前通道为空 则跳过 # 如果当前通道为空 则跳过
noteMsgs = [] noteMsgs = []
for msg in channels[i]: for msg in channels[i]:
if msg[0] == "PgmC": if msg[0] == "PgmC":
InstID = msg[1] InstID = msg[1]
elif msg[0] == "NoteS": elif msg[0] == "NoteS":
noteMsgs.append(msg[1:]) noteMsgs.append(msg[1:])
elif msg[0] == "NoteE": elif msg[0] == "NoteE":
record_pop_ids = [] record_pop_ids = []
for j in range(len(noteMsgs)): for j in range(len(noteMsgs)):
if noteMsgs[j][0] == msg[1]: if noteMsgs[j][0] == msg[1]:
note_channels[i].append(SingleNote(InstID, msg[1],noteMsgs[j][1],noteMsgs[j][2],msg[-1]-noteMsgs[j][2])) note_channels[i].append(
SingleNote(
InstID,
msg[1],
noteMsgs[j][1],
noteMsgs[j][2],
msg[-1] - noteMsgs[j][2],
)
)
record_pop_ids.append(j) record_pop_ids.append(j)
for j in record_pop_ids: for j in record_pop_ids:
noteMsgs.pop(j) noteMsgs.pop(j)
tracks = [] tracks = []
cmdAmount = 0 cmdAmount = 0
maxScore = 0 maxScore = 0
CheckFirstChannel = False CheckFirstChannel = False
@ -794,36 +781,37 @@ class midiConvert:
# 如果当前通道为空 则跳过 # 如果当前通道为空 则跳过
if not track: if not track:
continue continue
if channels.index(track) == 0: if note_channels.index(track) == 0:
CheckFirstChannel = True CheckFirstChannel = True
else: else:
CheckFirstChannel = False CheckFirstChannel = False
nowTrack = [] nowTrack = []
for note in track: for note in track:
for i in range(note.lastTime / 500): for i in range(int(note.lastTime / 500)):
# 实在没时间写了
# 用半秒做一个时间单位
# 然后计算即可
# 我找时间写完
soundID, _X = self.__Inst2soundIDwithX(InstID) # 应该是计算的时候出了点小问题
# 我们应该用一个MC帧作为时间单位而不是半秒
score_now = round(msg.startTime+i*(msg.lastTime / 500)/ float(speed) / 50000)
maxScore = max(maxScore,score_now) soundID, _X = self.__Inst2soundIDwithX(InstID)
score_now = round(
note.startTime
+ i * (note.lastTime / 500) / float(speed) / 50000
)
maxScore = max(maxScore, score_now)
nowTrack.append( nowTrack.append(
"execute @a[scores={" "execute @a[scores={"
+ str(scoreboardname) + str(scoreboardname)
+ "=" + "="
+ str(score_now) + str(score_now)
+ "}" + "}"
+ f"] ~ ~ ~ playsound {soundID} @s ~ ~{1 / (MaxVolume)*(1-i*500/msg.lastTime) - 1} ~ {msg[2] * (0.7 if CheckFirstChannel else 0.9)} {2 ** ((msg[1] - 60 - _X) / 12)}" + f"] ~ ~ ~ playsound {soundID} @s ~ ~{1 / (MaxVolume)*(1-i*500/note.lastTime) - 1} ~ {note.velocity * (0.7 if CheckFirstChannel else 0.9)} {2 ** ((note.note - 60 - _X) / 12)}"
) )
cmdAmount += 1 cmdAmount += 1
@ -831,8 +819,6 @@ class midiConvert:
return [tracks, cmdAmount, maxScore] return [tracks, cmdAmount, maxScore]
def _toCmdList_withDelay_m1( def _toCmdList_withDelay_m1(
self, self,
volume: float = 1.0, volume: float = 1.0,
@ -1029,11 +1015,12 @@ class midiConvert:
:param speed: 速度注意这里的速度指的是播放倍率其原理为在播放音频的时候每个音符的播放时间除以 speed :param speed: 速度注意这里的速度指的是播放倍率其原理为在播放音频的时候每个音符的播放时间除以 speed
:return 成功与否成功返回(True,True)失败返回(False,str失败原因) :return 成功与否成功返回(True,True)失败返回(False,str失败原因)
""" """
try: # try:
cmdlist, _a, maxscore = self.methods[method-1](scoreboardname, volume, speed) cmdlist, maxlen, maxscore = self.methods[method - 1](
except: scoreboardname, volume, speed
return (False, f"无法找到算法ID{method}对应的转换算法") )
del _a # except:
# return (False, f"无法找到算法ID{method}对应的转换算法")
# 当文件f夹{self.outputPath}/temp/functions存在时清空其下所有项目若其不存在则创建 # 当文件f夹{self.outputPath}/temp/functions存在时清空其下所有项目若其不存在则创建
if os.path.exists(f"{self.outputPath}/temp/functions/"): if os.path.exists(f"{self.outputPath}/temp/functions/"):
@ -1142,6 +1129,8 @@ class midiConvert:
shutil.rmtree(f"{self.outputPath}/temp/") shutil.rmtree(f"{self.outputPath}/temp/")
return (True, f"转换完成,总长度{maxlen}")
def toBDXfile( def toBDXfile(
self, self,
method: int = 1, method: int = 1,
@ -1166,12 +1155,12 @@ class midiConvert:
:return 成功与否成功返回(True,未经过压缩的源,结构占用大小)失败返回(False,str失败原因) :return 成功与否成功返回(True,未经过压缩的源,结构占用大小)失败返回(False,str失败原因)
""" """
try: try:
cmdlist, totalcount, maxScore = self.methods[method-1](scoreboardname, volume, speed) cmdlist, totalcount, maxScore = self.methods[method - 1](
scoreboardname, volume, speed
)
except: except:
return (False, f"无法找到算法ID{method}对应的转换算法") return (False, f"无法找到算法ID{method}对应的转换算法")
if not os.path.exists(self.outputPath): if not os.path.exists(self.outputPath):
os.makedirs(self.outputPath) os.makedirs(self.outputPath)
@ -1302,7 +1291,9 @@ class midiConvert:
""" """
try: try:
cmdlist = self.methods_byDelay[method-1](volume, speed, player, progressbar) cmdlist = self.methods_byDelay[method - 1](
volume, speed, player, progressbar
)
except: except:
return (False, f"无法找到算法ID{method}对应的转换算法") return (False, f"无法找到算法ID{method}对应的转换算法")