确定MIDI文件的时间分割

3

我正在使用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")

你能添加你目前已经有的代码吗? - Sanjit Sarda
当然,我会将其作为编辑添加进去。 - 3ddavies
我发布的代码是我认为正确的用于确定时间分割类型的,但我不知道如何实际计算时间分割本身。 - 3ddavies
嗯,我不确定,已经有一段时间没有使用过中间件了。 - Sanjit Sarda
这个问题可能是 https://stackoverflow.com/questions/22428271/midi-division-in-the-header-chunk 的重复。 - Former contributor
1个回答

5
也许你还不知道官方的 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>,指定delta-time的含义。它有两种格式,一种是用于测量时间,另一种是基于时间代码的时间:

  |bits                                        |
  |15|14        ...         8|7     ...      0 |
  |--|-----------------------|-----------------|
  | 0|         ticks per quarter-note          |
  | 1| negative SMPTE format | ticks per frame |

如果<division>的第15位是0,则14到0位表示组成四分音符的delta-time“ticks”数。例如,如果<division>是96,则在文件中两个事件之间的8分音符时间间隔为48。如果<division>的第15位是1,则文件中的delta-time对应于SMPTE和MIDI时间码的细分。14到8位包含四个值之一:-24、-25、-29或-30,对应于四种标准SMPTE和MIDI时间码格式(-29对应于30 drop frame),并表示每秒的帧数。这些负数以二进制补码形式存储。第二个字节(存储为正数)是帧内分辨率:典型值可以是4(MIDI时间码分辨率)、8、10、80(位分辨率)或100。这个系统允许精确指定基于时间码的曲目,同时还可以通过指定每秒25帧和每帧40个单位的分辨率来指定基于毫秒的曲目。如果文件中的事件以30帧时间码的位分辨率存储,则division word将为E250十六进制。
在您的示例中,第三个单词(十六进制0078)表示<division>为每个四分音符120个ticks。
文件中的事件的delta time以ticks表示。时间签名是另一种完全不同的东西。它是一种节奏指示,是一种元事件类型。(请参见规范书第10页)。

第15位是从左到右的第一位还是最后一位? - 3ddavies

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接