[英]Determining time division of a MIDI file
我正在用 Python 编写一个脚本来解析 MIDI 文件(是的,我知道 Python 存在 MIDI 解析库,但对于我的用例,如果我从头开始制作它是最简单的)。
我遇到的一件事是时间划分。 标题的最后两个字节指定了时分,但我无法确定文件的时分是以每节拍的滴答声还是每秒帧数来表示的。 经过一些阅读后,似乎顶部字节的顶部位表示时分记在两个中的哪一个。 我感到困惑的是字节的顶部位是字节的第一位还是最后一位一个字节的位,以及如何完全读取 MIDI 时分。
编辑:例如,我拥有的 MIDI 文件的标题如下:
4d54 6864 0000 0006 0000 0001 0078
0078 are the two bytes that denote the time sig, but I am confused as how to interpret it.
编辑2:
def openmidi(file):
tmbr = []
f = open(file, "rb")#opening the midi in binary mode
loopfile = True
while loopfile == True:
cb = f.read(1)
if cb != b'':#checking if there are still bytes left to read
tmbr.append(cb)
else:
loopfile = False
return tmbr
def byteread(num):#will read and return the specified number of bytes
global bytecounter
bytehold = b''
for i in range(0, num):#reads specified number of bytes
bytehold+=midibytearray[i+bytecounter]#number of increment plus the read position
bytecounter+=num#after reading is done read position is incremented by the number of bytes read.
return bytehold#after looping is done the specified bytes are returned.
def timetype(deltatimebytes):#used to determine if the time division is in ticks per beat or frames per second.
if str(deltatimebytes).replace("b'","").replace("'","")[0:2] == "00":
return True#if true the time division is in ticks per beat.
else:
return False#the time division is in frames per second.
global bytecounter
bytecounter = 0 #keeps track of what position in the file is being read.
midibytearray = openmidi("C:\\Users\\gabep\\Desktop\\Electrorchestrion\\Midis\\BONEY M.Rasputin K.mid") #array that the bytes will be stored in.
header = byteread(4)
chunklength = byteread(4)
formattype = byteread(2)
numofmtrkchunks = byteread(2)
deltatime = byteread(2)#if no tempo is assigned, 120bpm is assumed.
print(deltatime)
print("Header: "+str(header.decode("utf-8")))
print("MThd chunk length: "+str(int(chunklength.hex(), 16)))
print("Midi Format Type: "+str(int(formattype.hex(), 16)))
print("Number of MTrk chunks (number of tracks): "+str(int(numofmtrkchunks.hex(), 16)))
print("Delta time: "+str(int(deltatime.hex(), 16)))
if timetype(deltatime.hex()) == True:
print("Time signature is in ticks per beat")
else:
print("Time signature is in frames per second")
可能你不知道官方的MIDI规范是可用的,你可以免费下载文档。 (您需要先注册为站点用户)。 它包括详细的 SMF 格式。
这是头块的描述。
文件开头的头块指定了文件中数据的一些基本信息。 这是完整块的语法:
<Header Chunk> = <chunk type> <length> <format> <ntrks> <division>
如上所述, <chunk type>
是四个ASCII字符'MThd'; <length>
是数字 6 的 32 位表示(高字节在前)。 数据部分包含三个 16 位字,首先存储最高有效字节。 第一个词<format>
指定文件的整体组织。 仅指定了<format>
三个值:
0=文件包含单个多通道轨道
1=文件包含一个或多个同步音轨(或 MIDI 输出)的序列
2=该文件包含一个或多个顺序独立的单轨模式 下面提供了有关这些格式的更多信息。
下一个词<ntrks>
是文件中轨道块的数量。 对于格式 0 文件,它将始终为 1。
第三个词<division>
指定了增量时间的含义。 它有两种格式,一种用于公制时间,一种用于基于时间码的时间:
|bits |
|15|14 ... 8|7 ... 0 |
|--|-----------------------|-----------------|
| 0| ticks per quarter-note |
| 1| negative SMPTE format | ticks per frame |
如果<division>
15 位为零,则第 14 到 0 位表示构成四分音符的增量时间“滴答声”的数量。 例如,如果<division>
为 96,则文件中两个事件之间的八分音符的时间间隔将为 48。如果<division>
15 位为 1,则文件中的 delta-times 对应于一秒钟,以与 SMPTE 和 MIDI 时间码一致的方式。 位 14 到 8 包含四个值之一 -24、-25、-29 或 -30,对应于四种标准 SMPTE 和 MIDI 时间码格式(-29 对应于 30 个丢帧),并表示帧数每秒。 这些负数以二进制补码形式存储。 第二个字节(存储的正数)是帧内的分辨率:典型值可能是 4(MIDI 时间码分辨率)、8、10、80(位分辨率)或 100。该系统允许基于时间码的精确规范跟踪,但也允许通过指定 25 帧/秒和每帧 40 个单位的分辨率来实现基于毫秒的跟踪。 如果文件中的事件以 30 帧时间码的位分辨率存储,则划分字将为 E250 十六进制。
在您的示例中,您的第三个单词(十六进制 0078)表示<division>
是每个四分音符 120 个刻度。
Delta 时间以文件中事件的滴答为单位给出。 时间签名是另一件完全不同的事情。 它是节奏的指示,并且是元事件类型。 (参见规范的第 10 页)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.