diff --git a/README.md b/README.md index 260e6ca..5837545 100644 --- a/README.md +++ b/README.md @@ -33,130 +33,21 @@ 诸葛亮与八卦阵 bgArray:我的世界基岩版玩家,喜欢编程和音乐,深圳初一学生。 -## 软件架构🏢 -这是一个简单的Python包 - -## 使用教程📕 - -> 0. 安装python3.6+ -> -> 在安装时,一定要勾选Add Python 3.X to PATH,不然就要手动设置!! -> -> 同时,装完之后记得在cmd中输入:python 试试是否安装成功, -> python的安装可以去网上随便找一下。 -> 成功安装之后,在cmd中输入python会显示: -> -> 之类的东西。 -> 1. 安装(下载本程序)git的话,可以使用以下命令: -> -> `git clone -b pkgver https://gitee.com/EillesWan/Musicreater.git` -> -> 没有安装git的话,可以下载zip包,解压后进入目录即可。 -> -> 2. 运行(进入目录) -> 在目录下打开cmd,进入到目录下,执行以下命令: -> -> -> 回车一下,然后: -> -> 输入下面的三个指令即可! -> -> `pip install mido` -> -> `pip install brotli` -> -> `pip install openpyxl` -> -> 3. 开始使用! -> 在目录下打开cmd(步骤与上面的图片一致,只是执行的代码换了),进入到目录下,执行以下命令:(选择你需要的) -> -> `python example_convert_bdx.py` -> -> `python example_convert_mcpack.py` -> -> 4. 错误补充说明 -> 如果你遇到了以下这种情况 -> -> 那么,请按照这篇文章指引做: -> https://blog.csdn.net/qq_41179280/article/details/123804948 -> -> 感谢Mono帮我们发现这个问题 -> -> 5. 使用补充说明 -> -> midi路径:含有mid文件路径、文件名、后缀的完整绝对路径 -> -> 输出路径:输出文件夹的路径,就写一个英文.(句号)可以表示生成到当前目录下 -> (意思就是支持相对路径) -> -> 是否重置计分板:1或0(歌曲放完是否重置,推荐1) -> -> 是否启用进度条:1或0(看个人需要) -> -> 计分板名称:游戏内的计分板名称 -> -> 音量:0-1之间的小数(含0,1)正常来说推荐1 -> -> 变速:float数据,一般写1 -> -> 没有报错且在输出路径下找到mcpack或bdx即为生成成功: -> - - -### 对于 进度条自定义 功能的说明 - -因为我们提供了可以自动转换进度条的功能,因此在这里给出进度条自定义参数的详细解释。 - -一个进度条,明显地,有**固定部分**和**可变部分**来构成。而可变部分又包括了文字和图形两种(当然,《我的世界》里头的进度条,可变的图形也就是那个“条”了)。这一点你需要了解,因为后文中包含了很多这方面的概念需要你了解。 - -进度条的自定义功能使用一个字符串来定义自己的样式,其中包含众多**标识符**来表示可变部分。 - -标识符如下(注意大小写): - -| 标识符 | 指定的可变量 | -|---------|----------------| -| `%%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'))` - -*对了!为了避免生成错误,请尽量避免使用标识符作为定义样式字符串的其他部分* ## 致谢🙏 -- 感谢 昀梦\ 找出指令生成错误bug并指正 -- 感谢由 Charlie_Ping “查理平” 带来的bdx文件转换参考, +- 感谢 **昀梦**\ 找出指令生成错误bug并指正 +- 感谢由 **Charlie_Ping “查理平”** 带来的bdx文件转换参考, 以及mid转我的世界乐器参考表格 -- 感谢由 CMA_2401PT 为我们的软件开发进行指导 -- 感谢由 Dislink Sforza \带来的midi音色解析以及转换指令的算法,我们将其加入了我们众多算法之一 -- 感谢 Touch \提供的测试支持 -- 感谢 Mono\反馈安装时的问题 -- 感谢广大群友为此程序提供的测试等支持 -- 若您对我们有所贡献但您的名字没有显示在此列表中,请联系我! +- 感谢由 **CMA_2401PT** 为我们的软件开发进行指导 +- 感谢由 **Dislink Sforza**\带来的midi音色解析以及转换指令的算法,我们将其改编并应用 +- 感谢 **Touch “偷吃”**\提供的测试支持,并对程序的改进提供了丰富的意见 +- 感谢 **Mono**\反馈安装时的问题 + +> 感谢广大群友为此程序提供的测试等支持 +> +> 若您对我们有所贡献但您的名字没有显示在此列表中,请联系我! ## 联系我们📞 @@ -166,7 +57,7 @@ 2. 电邮 EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com 3. 微信 WYI_DoctorYI -### 作者\<*诸葛亮与八卦阵*\>(bgArray) 联系方式 +### 作者\<*诸葛亮与八卦阵*\>(bgArray)联系方式 1. QQ 4740437765 diff --git a/README_EN.md b/README_EN.md index 3617f60..8efc7c8 100644 --- a/README_EN.md +++ b/README_EN.md @@ -34,103 +34,17 @@ Eilles (金羿):A high school student, individual developer, unfamous Bilibili bgArray "诸葛亮与八卦阵": Fix bugs, improve code aesthetics, add new functions, change data format, etc. -### Framework🏢 - -A simple Python package. - -## Instructions📕 - -> 0. Install Python 3.6+ -> -> During installation, be sure to check "add Python 3.X to path", otherwise it needs to be set manually -> -> At the same time, after the installation, remember to enter in CMD: "python" to try -> -> whether the installation is successful. -> -> Python installation tutorial can be found on the Internet easily. -> -> 1. Install (download this program) -> Git, you can use the following commands: -> -> `git clone -b pkgver https://gitee.com/EillesWan/Musicreater.git` -> -> If Git is not installed, you can download the zip package from the website. Then decompress it -> and enter the directory. -> -> 2. Run (enter directory) -> -> Open CMD in the directory, enter the directory, and execute the following commands: -> -> `pip install mido` -> -> `pip install brotli` -> -> `pip install openpyxl` -> -> 3. Start using! -> -> Open CMD in the directory, enter the directory, and execute the following commands: -> -> (Choose what you need) -> -> `python example_convert_bdx.py` -> -> `python example_convert_mcpack.py` - -### Instructions for **Customize Progress Bar** - -We have supported the function of making progress bar in *Minecraft*'s music player. And also the method of customize them. So the following instructions are about the parameters of the Progress Bar Customizition. - -A Progress Bar, of course, is composed of **changeless** parts and **changable** parts. And the changable parts include texts or *images*(these images are made up of texts, or we can say, character paintings 😁). That is, for *Minecraft*, a changable image in a progress bar is just the "bar" part(which is like a stripe). - -We use a string to describe the style of progress bar you need, and it includes many **identifier**s to replace the changable parts. - -There are the identifiers: - -| Identifier | Changable Part | -|--------------|------------------------------------------------------| -| `%%N` | Music name(file name which is imported into program) | -| `%%s` | Value of scoreboard of now | -| `%^s` | Max value of scoreboard | -| `%%t` | Current playback time | -| `%^t` | Total music time | -| `%%%` | Current playback progress | -| `_` | To be replaced by the *Bar* part of the progress bar | - -The `_` is a placeholder to identifying the *bar* part, yeah, just the changable image. - -This is an example of **style description string**, and this is also the default style of *Musicreater*'s progress bar. - -`▶ %%N [ %%s/%^s %%% __________ %%t|%^t]` - -This is a progress bar with only one line, but it is possible if you want to give a multiline parameter into the style description string. - -But the string above is only for style identification, but we also need to identifying the changable image's image(just what the bar's look). - -A "bar", simply, included 2 parts: *Have Been Played* & *Not Been Played*. So we use a tuple to pass the parameter. It's under a simple format: `(str: played, str: not)`. For example, the default parameter is below: - -`('§e=§r', '§7=§r')` - -So it's time to combine what I said in one parameter now! - -This is a default definder parameter: - -`('▶ %%N [ %%s/%^s %%% __________ %%t|%^t]',('§e=§r', '§7=§r'))` - -*Tip: To avoid errors, please not to use the identifiers as the other part of your style.* ## Thanks🙏 -- Thank [Fuckcraft](https://github.com/fuckcraft) *(“鸣凤鸽子” ,etc)* for the function of Creating the Websocket Server for Minecraft: Bedrock Edition. - - *!! They have given me the rights to directly copy the lib into Musicreater* - Thank *昀梦*\ for finding and correcting the bugs in the commands that *Musicreater* Created. - Thank *Charlie_Ping “查理平”* for bdx convert function, and the data label that's used to convert the mid's instruments into minecraft's instruments. - Thank *CMA_2401PT* for BDXWorkShop as the .bdx structure's operation guide. -- Thank *Miracle Plume “神羽”* \ for the Miracle Plume Bedrock Edition Audio Resource Pack -- 感谢由 Dislink Sforza \带来的midi转换算法,我们将其加入了我们众多算法之一 -- Thank *Arthur Morgan* for his/her biggest support for the debugging of Musicreater +- Thank *Dislink Sforza* \ for the algorithm brought to us, his midi analysis algorithm became one of us's best ones +- Thank *Arthur Morgan*\ for his/her biggest support for the debugging of Musicreater +- Thank *Touch “偷吃”*\ for support of debugging and testing program and algorithm, as well his/her suggestions to the improvement of our project +- Thank *Mono*\ for reporting problems while installing - Thanks for a lot of groupmates who support me and help me to test the program. - If you have give me some help but u haven't been in the list, please contact me. @@ -139,8 +53,10 @@ the data label that's used to convert the mid's instruments into minecraft's ins ### Author *Eilles*(金羿) 1. QQ 2647547478 -2. E-mail EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com +2. E-mail EillesWan2006@163.com + W-YI_DoctorYI@outlook.com EillesWan@outlook.com 3. WeChat WYI_DoctorYI +4. Telegram [@EillesWan](https://t.me/EillesWan) ### Author *bgArray*(诸葛亮与八卦阵) diff --git a/demo_convert.py b/demo_convert.py index cd4039f..3c4ac42 100644 --- a/demo_convert.py +++ b/demo_convert.py @@ -86,7 +86,7 @@ while True: if os.path.isdir(midipath): for i in os.listdir(midipath): - if i.endswith('.mid'): + if i.lower().endswith('.mid'): print(f'正在操作{i}') convertion.convert(midipath + '/' + i, outpath + '/' + i[:-4] ) if outFormat == 0: diff --git a/demo_convert_bdx_byDelay.py b/demo_convert_bdx_byDelay.py index d9a209e..a3e1ee1 100644 --- a/demo_convert_bdx_byDelay.py +++ b/demo_convert_bdx_byDelay.py @@ -39,21 +39,25 @@ while True: print('输入错误,请重新输入') -if os.path.isdir(midipath): - for i in os.listdir(midipath): - if i.endswith('.mid'): - print(f'正在操作{i}') - convertion.convert(midipath + '/' + i, outpath) - convertion.toBDXfile_withDelay( - 1, - authorname if authorname != '' else input('请输入作者:'), - isProgress if isProgress != '' else bool(int(input('是否开启进度条(1|0):'))), - heightmax if heightmax != '' else int(input('请输入指令结构最大生成高度:')), - volume if volume != '' else float(input('请输入音量(0-1]:')), - speed if speed != '' else float(input('请输入速度倍率:')), - player if player != '' else input('请输入玩家选择器:'), - ) +def operation(i,): + print(f'正在操作{i}') + convertion.convert(midipath + '/' + i, outpath) + convertion.toBDXfile_withDelay( + 1, + authorname if authorname != '' else input('请输入作者:'), + isProgress if isProgress != '' else bool(int(input('是否开启进度条(1|0):'))), + heightmax if heightmax != '' else int(input('请输入指令结构最大生成高度:')), + volume if volume != '' else float(input('请输入音量(0-1]:')), + speed if speed != '' else float(input('请输入速度倍率:')), + player if player != '' else input('请输入玩家选择器:'), + ) + +if os.path.isdir(midipath): + import threading + for i in os.listdir(midipath): + if i.lower().endswith('.mid'): + threading.Thread(target=operation,args=(i,)).start() else: convertion.convert(midipath, outpath) convertion.toBDXfile_withDelay( diff --git a/docs/Use of Funtions.md b/docs/Use of Funtions.md new file mode 100644 index 0000000..ded6dfc --- /dev/null +++ b/docs/Use of Funtions.md @@ -0,0 +1,88 @@ +

音·创 Musicreater

+ +

+ +

+ + +## Instructions📕 + +> 0. Install Python 3.6+ +> +> While installing, be sure to check "add Python 3.X to path", otherwise it needs to be set manually +> +> After the installation, remember to enter in CMD: "python" to try +> +> whether the installation is successful. +> +> Python installation tutorial can be found on the Internet easily. +> +> 1. Install (download this program) +> Git, you can use the following commands: +> +> `git clone -b pkgver https://gitee.com/EillesWan/Musicreater.git` +> +> If Git is not installed, you can download the zip package from the website. Then decompress it +> and enter the directory. +> +> 2. Run (enter directory) +> +> Open CMD in the directory, enter the directory, and execute the following commands: +> +> `pip install mido` +> +> `pip install brotli` +> +> `pip install openpyxl` +> +> 3. Start using! +> +> Open CMD in the directory, enter the directory, and execute the following commands: +> +> (Choose what you need) +> +> `python example_convert_bdx.py` +> +> `python example_convert_mcpack.py` + +### Instructions for **Customize Progress Bar** + +We have supported the function of making progress bar in *Minecraft*'s music player. And also the method of customize them. So the following instructions are about the parameters of the Progress Bar Customizition. + +A Progress Bar, of course, is composed of **changeless** parts and **changable** parts. And the changable parts include texts or *images*(these images are made up of texts, or we can say, character paintings 😁). That is, for *Minecraft*, a changable image in a progress bar is just the "bar" part(which is like a stripe). + +We use a string to describe the style of progress bar you need, and it includes many **identifier**s to replace the changable parts. + +There are the identifiers: + +| Identifier | Changable Part | +|--------------|------------------------------------------------------| +| `%%N` | Music name(file name which is imported into program) | +| `%%s` | Value of scoreboard of now | +| `%^s` | Max value of scoreboard | +| `%%t` | Current playback time | +| `%^t` | Total music time | +| `%%%` | Current playback progress | +| `_` | To be replaced by the *Bar* part of the progress bar | + +The `_` is a placeholder to identifying the *bar* part, yeah, just the changable image. + +This is an example of **style description string**, and this is also the default style of *Musicreater*'s progress bar. + +`▶ %%N [ %%s/%^s %%% __________ %%t|%^t]` + +This is a progress bar with only one line, but it is possible if you want to give a multiline parameter into the style description string. + +But the string above is only for style identification, but we also need to identifying the changable image's image(just what the bar's look). + +A "bar", simply, included 2 parts: *Have Been Played* & *Not Been Played*. So we use a tuple to pass the parameter. It's under a simple format: `(str: played, str: not)`. For example, the default parameter is below: + +`('§e=§r', '§7=§r')` + +So it's time to combine what I said in one parameter now! + +This is a default definder parameter: + +`('▶ %%N [ %%s/%^s %%% __________ %%t|%^t]',('§e=§r', '§7=§r'))` + +*Tip: To avoid errors, please not to use the identifiers as the other part of your style.* diff --git a/docs/使用教程.md b/docs/使用教程.md new file mode 100644 index 0000000..c5c3c69 --- /dev/null +++ b/docs/使用教程.md @@ -0,0 +1,117 @@ +

音·创 Musicreater

+ +

+ +

+ + +## 使用教程📕 + +> 0. 安装python3.6+ +> +> 在安装时,一定要勾选Add Python 3.X to PATH,不然就要手动设置!! +> +> 同时,装完之后记得在cmd中输入:python 试试是否安装成功, +> python的安装可以去网上随便找一下。 +> 成功安装之后,在cmd中输入python会显示: +> +> 之类的东西。 +> 1. 下载本程序 +> +> git的话,可以使用以下命令: +> +> `git clone -b pkgver https://gitee.com/EillesWan/Musicreater.git` +> +> 没有安装git的话,可以下载zip包,解压后进入目录即可。 +> +> 2. 运行(进入目录) +> 在目录下打开cmd,进入到目录下,执行以下命令: +> +> +> 回车一下,然后: +> +> 输入下面的三个指令即可! +> +> `pip install mido` +> +> `pip install brotli` +> +> `pip install openpyxl` +> +> 3. 开始使用! +> 在目录下打开cmd(步骤与上面的图片一致,只是执行的代码换了),进入到目录下,执行以下命令:(选择你需要的) +> +> `python example_convert_bdx.py` +> +> `python example_convert_mcpack.py` +> +> 4. 错误补充说明 +> 如果你遇到了以下这种情况 +> +> 那么,请按照这篇文章指引做: +> https://blog.csdn.net/qq_41179280/article/details/123804948 +> +> 感谢Mono帮我们发现这个问题 +> +> 5. 使用补充说明 +> +> midi路径:含有mid文件路径、文件名、后缀的完整绝对路径 +> +> 输出路径:输出文件夹的路径,就写一个英文.(句号)可以表示生成到当前目录下 +> (意思就是支持相对路径) +> +> 是否重置计分板:1或0(歌曲放完是否重置,推荐1) +> +> 是否启用进度条:1或0(看个人需要) +> +> 计分板名称:游戏内的计分板名称 +> +> 音量:0-1之间的小数(含0,1)正常来说推荐1 +> +> 变速:float数据,一般写1 +> +> 没有报错且在输出路径下找到mcpack或bdx即为生成成功: +> + + +### 对于 进度条自定义 功能的说明 + +因为我们提供了可以自动转换进度条的功能,因此在这里给出进度条自定义参数的详细解释。 + +一个进度条,明显地,有**固定部分**和**可变部分**来构成。而可变部分又包括了文字和图形两种(当然,《我的世界》里头的进度条,可变的图形也就是那个“条”了)。这一点你需要了解,因为后文中包含了很多这方面的概念需要你了解。 + +进度条的自定义功能使用一个字符串来定义自己的样式,其中包含众多**标识符**来表示可变部分。 + +标识符如下(注意大小写): + +| 标识符 | 指定的可变量 | +|---------|----------------| +| `%%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'))` + +*对了!为了避免生成错误,请尽量避免使用标识符作为定义样式字符串的其他部分* diff --git a/msctPkgver/__init__.py b/msctPkgver/__init__.py index 4bc583d..a2bef6c 100644 --- a/msctPkgver/__init__.py +++ b/msctPkgver/__init__.py @@ -38,5 +38,5 @@ Note! Except for this source file, all the files in this repository and this pro from .main import * -print('此转换功能由音·创开发者开发,版权归参与开发的人员共同所有。') +print('此Midi转换功能由音·创开发者开发,版权归参与开发的人员共同所有。') print('Copyright © 2022 all the developers of Musicreater') diff --git a/msctPkgver/main.py b/msctPkgver/main.py index 3c5fe8f..21098d0 100644 --- a/msctPkgver/main.py +++ b/msctPkgver/main.py @@ -463,7 +463,7 @@ class midiConvert: self, scoreboardname: str = "mscplay", volume: float = 1.0, speed: float = 1.0 ) -> list: """ - 使用金羿的转换思路,将midi转换为我的世界命令列表 + 使用金羿的转换思路,将midi转换为我的世界命令列表,使用线性方法调整音量 :param scoreboardname: 我的世界的计分板名称 :param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频 :param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed @@ -483,6 +483,7 @@ class midiConvert: ticks = 0 instrumentID = 0 singleTrack = [] + noteOn = [] for msg in track: ticks += msg.time @@ -494,6 +495,33 @@ class midiConvert: if msg.type == "program_change": # print("TT") instrumentID = msg.program + if msg.type == 'note_on' and msg.velocity != 0: + noteOn.append([msg, msg.note, ticks]) + elif type_[1] is True: + if msg.type == 'note_on' and msg.velocity == 0: + for u in noteOn: + index = 0 + if u[1] == msg.note: + lastMessage = u[0] + lastTick = u[2] + break + index += 1 + trackS.append( + NoteMessage( + msg.channel, + msg.note, + lastMessage.velocity, + lastTick, + ticks - lastTick, + mid, + ) + ) + # print(noteOn) + # print(index) + try: + noteOn.pop(index) + except IndexError: + noteOn.pop(index - 1) if msg.type == "note_on" and msg.velocity != 0: nowscore = round( (ticks * tempo) diff --git a/noteTmain.py b/noteTmain.py new file mode 100644 index 0000000..7978304 --- /dev/null +++ b/noteTmain.py @@ -0,0 +1,144 @@ +import mido + + +def delete_extra_zero(n: float) -> int or float: + """ + 删除多余的0 + ———————————————— + 版权声明:本文为CSDN博主「XerCis」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 + 原文链接:https://blog.csdn.net/lly1122334/article/details/108770141 + 删除小数点后多余的0 + :param n: input + :return: output + """ + n = '{:g}'.format(n) + n = float(n) if '.' in n else int(n) # 含小数点转float否则int + return n + + +def bpm_by_MetaMessage_Set_tempo(tmp: int) -> int or float: + """ + midi文件tempo事件bpm算法。 + A function that's used to compute the bpm of a midiFile, + which algorithm is made up of midiFile's tempo meta message. + :param tmp:输入mid的metaMessage中速度tempo值 + input the tempo value which is in the tempo meta message. + :return:bpm + + This algorithm is made by ©bgArray. + 算法版权归©诸葛亮与八卦阵所有。 + """ + second = tmp / 1000000 + bpm = delete_extra_zero(60 / second) + # debug.dp(bpm) + return bpm + + +class NoteMessage: + def __init__(self, channel, pitch, velocity, startT, lastT, midi, now_bpm, change_bpm=None): + self.channel = channel + self.note = pitch + self.velocity = velocity + self.startTime = startT + self.lastTime = lastT + self.tempo = now_bpm # 这里要程序实现获取bpm可以参考我的程序 + + def mt2gt(mt, tpb_a, bpm_a): + return mt / tpb_a / bpm_a * 60 + self.startTrueTime = mt2gt(self.startTime, midi.ticks_per_beat, self.tempo) # / 20 + # delete_extra_zero(round_up()) + if change_bpm is not None: + self.lastTrueTime = mt2gt(self.lastTime, midi.ticks_per_beat, change_bpm) # / 20 + else: + self.lastTrueTime = mt2gt(self.lastTime, midi.ticks_per_beat, self.tempo) # / 20 + # delete_extra_zero(round_up()) + print((self.startTime * self.tempo) / (midi.ticks_per_beat * 50000)) + + def __str__(self): + return "noteMessage channel=" + str(self.channel) + " note=" + str(self.note) + " velocity=" + \ + str(self.velocity) + " startTime=" + str(self.startTime) + " lastTime=" + str(self.lastTime) + \ + " startTrueTime=" + str(self.startTrueTime) + " lastTrueTime=" + str(self.lastTrueTime) + + +def load(mid: mido.MidiFile): + + type_ = [False, False, False] # note_off / note_on+0 / mixed + + is_tempo = False + + # 预检 + for i, track in enumerate(mid.tracks): + for msg in track: + # print(msg) + if msg.is_meta is not True: + if msg.type == 'note_on' and msg.velocity == 0: + type_[1] = True + elif msg.type == "note_off": + type_[0] = True + if msg.is_meta is True and msg.type == "set_tempo": + is_tempo = True + + if is_tempo is not True: + raise Exception("这个mid没有可供计算时间的tempo事件") + + if type_[0] is True and type_[1] is True: + type_[2] = True + type_[1] = False + type_[0] = False + print(type_) + + bpm = 0 + recent_change_bpm = 0 + is_change_bpm = False + # 实检 + for i, track in enumerate(mid.tracks): + noteOn = [] + trackS = [] + ticks = 0 + for msg in track: + print(msg) + ticks += msg.time + print(ticks) + if msg.is_meta is True and msg.type == "set_tempo": + recent_change_bpm = bpm + bpm = bpm_by_MetaMessage_Set_tempo(msg.tempo) + is_change_bpm = True + # print((ticks * 92) / (mid.ticks_per_beat * 50000)) + # MC_tick = delete_extra_zero(round_up( + # (ticks * 92) / (mid.ticks_per_beat * 50000) + # )) + # print(MC_tick) + # print(ticks / mid.ticks_per_beat / 92 * 60) + if msg.type == 'note_on' and msg.velocity != 0: + noteOn.append([msg, msg.note, ticks]) + if type_[1] is True: + if msg.type == 'note_on' and msg.velocity == 0: + for u in noteOn: + index = 0 + if u[1] == msg.note: + lastMessage = u[0] + lastTick = u[2] + break + index += 1 + print(lastTick) + if is_change_bpm and recent_change_bpm != 0: + trackS.append(NoteMessage(msg.channel, msg.note, lastMessage.velocity, lastTick, ticks - lastTick, + mid, recent_change_bpm, bpm)) + is_change_bpm = False + else: + trackS.append( + NoteMessage(msg.channel, msg.note, lastMessage.velocity, lastTick, ticks - lastTick, + mid, bpm)) + # print(noteOn) + # print(index) + try: + noteOn.pop(index) + except IndexError: + noteOn.pop(index - 1) + print(trackS) + for j in trackS: + print(j) + + +if __name__ == '__main__': + load(mido.MidiFile("test.mid"))