通过 Python 在 Midi 的特定时间写笔记

Posted

技术标签:

【中文标题】通过 Python 在 Midi 的特定时间写笔记【英文标题】:Write Note at specific time in Midi via Python 【发布时间】:2020-03-28 22:44:51 【问题描述】:

我想将一系列音符(由时间、持续时间、音高定义)转换为音频文件。为此,我认为首先创建一个 midi 然后将其编译为 wav 是要走的路。

我对音频处理和 MIDI 文件还很陌生,所以即使我阅读了几个教程,也可能我没有明白这一点。

编辑:我发现了问题,请参阅下面的解决方案。

有什么问题

通过 python 的MIDIUtil 在特定时间以特定持续时间编写笔记无法按预期工作。事实上,放置音符的时间(以秒为单位)很大程度上取决于音轨的 bpm,尽管我认为我在将音符时间转换为 MIDI 的四分音符时间度量时考虑了 bpm。

我的尝试

我正在使用给定的 bpm 创建一个 MIDI 轨道。 然后我通过转换笔记的事件时间 t_quarter = t_seconds * bpm/60

示例

我正在用下面的代码写两个音符,最后一个在 t=5 秒,持续时间为 1 秒;即我期待一个持续 6 秒的 midi 文件。但是在 bpm=600 时,文件的长度为 14 秒。在 bpm=100 时,几乎是预期的 6 秒。

这是我的代码


from midiutil import MIDIFile

def convert_seconds_to_quarter(time_in_sec, bpm):
    quarter_per_second = (bpm/60)
    time_in_quarter = time_in_sec * quarter_per_second
    return time_in_quarter

def write_test_midi():
    bpm = 600

    MyMIDI = MIDIFile(1)
    MyMIDI.addTrackName(track=0, time=0, trackName="Sample Track")
    MyMIDI.addTempo(track=0, time=0, tempo=bpm)

    MyMIDI.addNote(track=0, channel=0, pitch=60,
                   time=convert_seconds_to_quarter(1, bpm),
                   duration=convert_seconds_to_quarter(1, bpm), volume=100)
    MyMIDI.addNote(track=0, channel=0, pitch=60,
                   time=convert_seconds_to_quarter(5, bpm),
                   duration=convert_seconds_to_quarter(1, bpm), volume=100)

    with open("/tmp/output.mid", 'wb') as binfile:
        MyMIDI.writeFile(binfile)

其他信息

带有bpm=100的文件的十六进制内容:

ADDRESS        00 01 02 03   04 05 06 07   08 09 0a 0b   0c 0d 0e 0f       ASCII
00000010       4d 54 68 64   00 00 00 06   00 01 00 02   03 c0 4d 54       MThd..........MT
00000020       72 6b 00 00   00 0b 00 ff   51 03 09 27   c0 00 ff 2f       rk......Q..'.../
00000030       00 4d 54 72   6b 00 00 00   28 00 ff 03   0c 53 61 6d       .MTrk...(....Sam
00000040       70 6c 65 20   54 72 61 63   6b 8c 40 90   3c 64 8c 40       ple.Track.@.<d.@
00000050       80 3c 64 a5   40 90 3c 64   8c 40 80 3c   64 00 ff 2f       .<d.@.<d.@.<d../
00000060       00 00 00 00                                                 .

文件内容用bpm=600:

ADDRESS        00 01 02 03   04 05 06 07   08 09 0a 0b   0c 0d 0e 0f       ASCII
00000010       4d 54 68 64   00 00 00 06   00 01 00 02   03 c0 4d 54       MThd..........MT
00000020       72 6b 00 00   00 0b 00 ff   51 03 01 86   a0 00 ff 2f       rk......Q....../
00000030       00 4d 54 72   6b 00 00 00   29 00 ff 03   0c 53 61 6d       .MTrk...)....Sam
00000040       70 6c 65 20   54 72 61 63   6b cb 00 90   3c 64 cb 00       ple.Track...<d..
00000050       80 3c 64 81   e1 00 90 3c   64 cb 00 80   3c 64 00 ff       .<d....<d...<d..
00000060       2f 00 00 00                                                 /.

解决方案

我贴的代码、转换函数和文件都是正确的。问题是我以前听 MIDI 的 VLC 播放器。

【问题讨论】:

您用“at a bpm=600”写了两句话。这是正确的吗? 感谢您的提示!这是一个错字,我改正了! ((我想说“在 bpm=100 时,这几乎是预期的 6 秒”。 更改bpm 不应更改事件的绝对时间。你如何测量 MIDI 文件的长度?你能显示实际的文件内容吗? 您的意思是,更改bpm 不应该改变以秒为单位的绝对时间或以节拍/节拍/四分音符为单位的绝对时间吗?当一切保持不变时,以秒为单位的绝对时间应该会改变。就我而言,我试图弄清楚,我需要如何使用 bpm 更改节拍/节拍/季度的绝对时间,以保持以秒为单位的绝对时间不变。关于 MIDI 文件内容。我用十六进制查看器阅读它,我希望这是,你问我什么?谢谢! 【参考方案1】:

第一个文件:

增量消息绝对时间 时间滴答秒 ... 03 c0 ... 每四分音符 960 个刻度 ... ff 51 03 09 27 c0 0 0 速度:每四分音符 600000 微秒 8c 40 90 3c 64 1600 1 注意 8c 40 80 3c 64 3200 2 音符 a5 40 90 3c 64 8000 5 注意 8c 40 80 3c 64 9600 6 音符 00 ff 2f 00 9600 6 曲目结束

第二个文件:

... 03 c0 ... 每四分音符 960 个刻度 ... ff 51 03 01 86 a0 0 0 速度:每四分音符 100000 微秒 cb 00 90 3c 64 9600 1 注意 cb 00 80 3c 64 19200 2 注意 81 e1 00 90 3c 64 48000 5 注意 cb 00 80 3c 64 57600 6 注意 00 ff 2f 00 57600 6 轨道结束

这两个文件的长度正好是六秒。您的代码和文件是正确的。

问题在于您使用什么工具来处理文件。

【讨论】:

非常感谢您帮助我并解析十六进制!事实上,我的 VLC 播放器听 MIDI 是有问题的。我现在切换到timidity 将midi 转换为wav,现在我也看到文件是正确的!顺便说一句,为了能够自己验证我的 midis 的有效性:您是“手动”解析 midi hex 还是使用哪个工具从 hex 中提取 midi 事件和时间? 这几个字节可以手动解析。 (对于编写了多个 MIDI 解析器的人来说,这当然更容易。)

以上是关于通过 Python 在 Midi 的特定时间写笔记的主要内容,如果未能解决你的问题,请参考以下文章

Python通过比较倒数第二个值来加/减计数器

通过 MacOS 上的端口将 MIDI 数据发送到数码钢琴

Python:midi 到音频流

笔记本电脑退出休眠后 Java MIDI 音频延迟

在 python 中播放 MIDI 文件?

在 python 中播放 MIDI 文件?