From 4788d8949b3cc49dcf7a502d2730c8002b34701f Mon Sep 17 00:00:00 2001 From: bgArray <474037765@qq.com> Date: Tue, 12 Apr 2022 14:15:33 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __init__.py | 16 ++--- __pycache__/main.cpython-39.pyc | Bin 0 -> 6332 bytes example_convert.py | 12 ++-- main.py | 103 ++++++++++++++++++-------------- 4 files changed, 72 insertions(+), 59 deletions(-) create mode 100644 __pycache__/main.cpython-39.pyc diff --git a/__init__.py b/__init__.py index 0c080fe..e0fe887 100644 --- a/__init__.py +++ b/__init__.py @@ -1,13 +1,15 @@ -#-*- coding:utf-8 -*- -'''一个简单的基于音·创的我的世界音频操作工具''' - +# -*- coding:utf-8 -*- +"""一个简单的基于音·创的我的世界音频操作工具""" +import main +if main.importDebug(): + print("Debug finished correctly") +else: + print("Debug finished with errors") __version__ = '0.0.1' __all__ = [] -__author__ = (('金羿','Eilles Wan'),('诸葛亮与八卦阵','bgArray'),('鸣凤鸽子','MingFengPigeon')) - - +__author__ = (('金羿', 'Eilles Wan'), ('诸葛亮与八卦阵', 'bgArray'), ('鸣凤鸽子', 'MingFengPigeon')) """ Copyright © 2022 Team-Ryoun @@ -27,5 +29,3 @@ __author__ = (('金羿','Eilles Wan'),('诸葛亮与八卦阵','bgArray'),('鸣 print('此工具由凌天之云创新团队开发,版权归本团队参与开发的人员共同所有。') print('This tool is developed by Team-Ryoun, copyright belongs to the team members who developed the tool.') - -from main import * diff --git a/__pycache__/main.cpython-39.pyc b/__pycache__/main.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90e270c5a12ec3f500fbfbd849ed570e4154b65d GIT binary patch literal 6332 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 literal 0 HcmV?d00001 diff --git a/example_convert.py b/example_convert.py index 07d9d99..f1ddb31 100644 --- a/example_convert.py +++ b/example_convert.py @@ -1,9 +1,7 @@ - - - # THIS PROGRAM IS ONLY A TEST EXAMPLE - -from main import * - -midiConvert(input('请输入midi文件路径:'), input('请输入输出路径:')).tomcpack(1,input('请输入计分板名称:'),float(input('请输入音量(0-1):')),float(input('请输入速度倍率:'))) \ No newline at end of file +if __name__ == '__main__': + from main import * + one = 1 + midiConvert(input('请输入midi文件路径:'), input('请输入输出路径:')).tomcpack(one, input('请输入计分板名称:'), float(input('请输入音量(0-1):')), + float(input('请输入速度倍率:'))) diff --git a/main.py b/main.py index 9a6783d..3a92e48 100644 --- a/main.py +++ b/main.py @@ -4,12 +4,10 @@ import mido import os import json import uuid -import zipfile import shutil import zipfile - def makeZip(sourceDir, outFilename, compression=8, exceptFile=None): """使用compression指定的算法打包目录为zip文件\n 默认算法为DEFLATED(8),可用算法如下:\n @@ -30,10 +28,9 @@ def makeZip(sourceDir, outFilename, compression=8, exceptFile=None): zipf.close() - class midiConvert: def __init__(self, midiFile: str, outputPath: str): - '''简单的midi转换类,将midi文件转换为我的世界结构或者包''' + """简单的midi转换类,将midi文件转换为我的世界结构或者包""" self.midiFile = midiFile '''midi文件路径''' self.midi = mido.MidiFile(self.midiFile) @@ -44,14 +41,16 @@ class midiConvert: self.midFileName = os.path.splitext(os.path.basename(self.midiFile))[0] '''文件名,不含路径且不含后缀''' + self.staticDebug = True - - - def __Inst2SoundID(self,instrumentID, default='note.harp'): - '''返回midi的乐器ID对应的我的世界乐器名 + def __Inst2SoundID(self, instrumentID, default='note.harp'): + """返回midi的乐器ID对应的我的世界乐器名 :param instrumentID: midi的乐器ID :param default: 如果instrumentID不在范围内,返回的默认我的世界乐器名称 - :return: 我的世界乐器名 str''' + :return: 我的世界乐器名 str""" + if self.staticDebug: + pass + if instrumentID == 105: return 'note.banjo' if instrumentID in range(32, 40): @@ -84,54 +83,58 @@ class midiConvert: return 'note.xylophone' return default - - def _toCmdList_m1(self,scoreboardname : str = 'mscplay',volume:float = 1.0, speed:float = 1.0) -> list: - '''使用Dislink Sforza的转换算法,将midi转换为我的世界命令列表 + 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: 我的世界命令列表''' + :return: 我的世界命令列表""" tracks = [] if volume > 1: volume = 1 if volume <= 0: volume = 0.001 - + for i, track in enumerate(self.midi.tracks): - ticks=0 - commands=0 - instrumentID=0 + ticks = 0 + commands = 0 + instrumentID = 0 singleTrack = [] for msg in track: if msg.is_meta: if msg.type == 'set_tempo': - tempo=msg.tempo + tempo = msg.tempo if msg.type == 'program_change': - instrumentID=msg.program + instrumentID = msg.program else: - ticks+=msg.time + ticks += msg.time if msg.type == 'note_on' and msg.velocity != 0: - singleTrack.append('execute @a[scores={'+scoreboardname+'='+str(round((ticks*tempo)/((self.midi.ticks_per_beat*float(speed))*50000)))+'}'+f'] ~~~ playsound {self.__Inst2SoundID(instrumentID)} @s ~~{1/volume-1}~ {msg.velocity*(0.7 if msg.channel == 0 else 0.9)} {2**((msg.note-66)/12)}') - commands+=1 + singleTrack.append('execute @a[scores={' + scoreboardname + '=' + + str(round((ticks * tempo)((self.midi.ticks_per_beat * float(speed)) * + 50000))) + '}' + + f'] ~~~ playsound {self.__Inst2SoundID(instrumentID)} ' + f'@s ~~{1 / volume - 1}~ {msg.velocity * (0.7 if msg.channel == 0 else 0.9)}' + f' {2 ** ((msg.note - 66) / 12)}') + commands += 1 tracks.append(singleTrack) - + return tracks - - def tomcpack(self,method:int = 1,scoreboardname : str = 'mscplay',volume:float = 1.0, speed:float = 1.0) -> bool: - '''使用method指定的转换算法,将midi转换为我的世界mcpack格式的包 + + def tomcpack(self, method: int = 1, scoreboardname: str = 'mscplay', volume: float = 1.0, + speed: float = 1.0) -> tuple: + """使用method指定的转换算法,将midi转换为我的世界mcpack格式的包 :param method: 转换算法 :param scoreboardname: 我的世界的计分板名称 :param volume: 音量,注意:这里的音量范围为(0,1],其原理为在距离玩家 (1 / volume -1) 的地方播放音频 :param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed - :return 成功与否,成功返回(True,True),失败返回(False,str失败原因)''' + :return 成功与否,成功返回(True,True),失败返回(False,str失败原因)""" if method == 1: - cmdlist = self._toCmdList_m1(scoreboardname,volume,speed) + cmdlist = self._toCmdList_m1(scoreboardname, volume, speed) else: - return (False,f'无法找到算法ID{method}对应的转换算法') - + return False, f'无法找到算法ID{method}对应的转换算法' # 当文件f夹{self.outputPath}/temp/functions存在时清空其下所有项目,若其不存在则创建 if os.path.exists(f'{self.outputPath}/temp/functions/'): @@ -141,29 +144,41 @@ class midiConvert: # 写入manifest.json if not os.path.exists(f'{self.outputPath}/temp/manifest.json'): with open(f"{self.outputPath}/temp/manifest.json", "w") as f: - f.write("{\n \"format_version\": 1,\n \"header\": {\n \"description\": \"" + self.midFileName + " Pack : behavior pack\",\n \"version\": [ 0, 0, 1 ],\n \"name\": \"" + self.midFileName + "Pack\",\n \"uuid\": \"" + str(uuid.uuid4()) + "\"\n },\n \"modules\": [\n {\n \"description\": \"" + f"the Player of the Music {self.midFileName}" + "\",\n \"type\": \"data\",\n \"version\": [ 0, 0, 1 ],\n \"uuid\": \"" + str(uuid.uuid4()) + "\"\n }\n ]\n}") + f.write( + "{\n \"format_version\": 1,\n \"header\": {\n \"description\": \"" + self.midFileName + + " Pack : behavior pack\",\n \"version\": [ 0, 0, 1 ],\n \"name\": \"" + self.midFileName + + "Pack\",\n \"uuid\": \"" + str( + uuid.uuid4()) + "\"\n },\n \"modules\": [\n {\n \"description\": \"" + + f"the Player of the Music {self.midFileName}" + "\",\n \"type\": \"data\",\n " + "\"version\": [ 0, 0, 1 ],\n \"uuid\": " + "\"" + str( + uuid.uuid4()) + "\"\n }\n ]\n}") else: with open(f'{self.outputPath}/temp/manifest.json', 'r') as manifest: - data=json.loads(manifest.read()) - data['header']['description']=f"the Player of the Music {self.midFileName}" - data['header']['name']=self.midFileName - data['header']['uuid']=str(uuid.uuid4()) - data['modules'][0]['description']='None' - data['modules'][0]['uuid']=str(uuid.uuid4()) - manifest.close() - open(f'{self.outputPath}/temp/manifest.json','w').write(json.dumps(data)) + data = json.loads(manifest.read()) + data['header']['description'] = f"the Player of the Music {self.midFileName}" + data['header']['name'] = self.midFileName + data['header']['uuid'] = str(uuid.uuid4()) + data['modules'][0]['description'] = 'None' + data['modules'][0]['uuid'] = str(uuid.uuid4()) + manifest.close() + open(f'{self.outputPath}/temp/manifest.json', 'w').write(json.dumps(data)) # 将命令列表写入文件 indexfile = open(f'{self.outputPath}/temp/functions/index.mcfunction', 'w', encoding='utf-8') for track in cmdlist: - indexfile.write('function mscplay/track'+str(cmdlist.index(track)+1)+'\n') - with open(f'{self.outputPath}/temp/functions/mscplay/track{cmdlist.index(track)+1}.mcfunction','w',encoding='utf-8') as f: + indexfile.write('function mscplay/track' + str(cmdlist.index(track) + 1) + '\n') + with open(f'{self.outputPath}/temp/functions/mscplay/track{cmdlist.index(track) + 1}.mcfunction', 'w', + encoding='utf-8') as f: f.write('\n'.join(track)) - indexfile.write('scoreboard players add @a[scores={'+scoreboardname+'=1..}] '+scoreboardname+' 1\n') + indexfile.write('scoreboard players add @a[scores={' + scoreboardname + '=1..}] ' + scoreboardname + ' 1\n') indexfile.close() - makeZip(f'{self.outputPath}/temp/',self.outputPath+f'/{self.midFileName}.mcpack') + makeZip(f'{self.outputPath}/temp/', self.outputPath + f'/{self.midFileName}.mcpack') shutil.rmtree(f'{self.outputPath}/temp/') - \ No newline at end of file + +def importDebug(): + """调试用的函数,可以在这里写一些调试代码""" + return None