如何使用python从头开始编写Midi文件

2024-06-26 02:37:41 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在研究Midi文件规范,现在我正在测试它,如果由Timidity播放,效果很好,但是它在车库乐队、OSX(输出不播放)和合成器中都损坏了

head = '4d 54 68 64' 
chunklen = '00 00 00 06'
mformat = '00 01' 
ntracks = '00 02' 
tickdiv = '00 60'
trackid = '4d 54 72 6b'
eot = '00 ff 2f 00'

makeheader = lambda : " ".join([head,chunklen,mformat,ntracks,tickdiv])

def chunklencalc(notes):
    chlen = format(len(notes)*4, 'x')
    return " ".join([x for x in re.compile('(.{2})').split("00000000"[len(chlen):] + chlen) if x != ''])

maketrack = lambda notes : " ".join([trackid, chunklencalc(notes)] + notes + [eot])

makestandardquarter = lambda root : f"00 90 {root} 64 60 80 {root} 64"

def createMidi(filename,bytelist):
    with open(filename, 'wb') as f:
        for e in bytelist.split(" "):
            f.write(bytes.fromhex(e))


filename = 'firsttest.mid'
head = makeheader()
notes1 =[
    makestandardquarter('3c'),
    makestandardquarter('3c'),
    makestandardquarter('3c'),
    makestandardquarter('3c'),
    makestandardquarter('3c'),
    makestandardquarter('3c'),
    makestandardquarter('3c'),
    makestandardquarter('3c'),
]
notes2 =[
    makestandardquarter('40'),
    makestandardquarter('40'),
    makestandardquarter('40'),
    makestandardquarter('40'),
    makestandardquarter('40'),
    makestandardquarter('40'),
    makestandardquarter('40'),
    makestandardquarter('40'),
]
track1 = maketrack(notes1)
track2 = maketrack(notes2)

createMidi(filename, " ".join([head, track1,track2]))

我期望在两条赛道上有一系列的四分之一,在一条赛道上只有前四分之一


Tags: lambdarootfilenameheadnotesjoineottrackid
1条回答
网友
1楼 · 发布于 2024-06-26 02:37:41

在使用hexdump进行深入查看并查看定义的块长度后: 第一个区块声明为0x20(32)字节长,从位置0x17(23)开始,到0x5b(91)结束,这意味着您的区块长度计算减少了34字节

00000000  4d 54 68 64 00 00 00 06  00 01 00 02 00 60 4d 54  |MThd.........`MT|
00000010  72 6b 00 00 00 20 00 90  3c 64 60 80 3c 64 00 90  |rk... ..<d`.<d..|
00000020  3c 64 60 80 3c 64 00 90  3c 64 60 80 3c 64 00 90  |<d`.<d..<d`.<d..|
*
00000050  3c 64 60 80 3c 64 00 ff  2f 00 4d 54 72 6b 00 00  |<d`.<d../.MTrk..|
00000060  00 20 00 90 40 64 60 80  40 64 00 90 40 64 60 80  |. ..@d`.@d..@d`.|
00000070  40 64 00 90 40 64 60 80  40 64 00 90 40 64 60 80  |@d..@d`.@d..@d`.|
*
000000a0  40 64 00 ff 2f 00                                 |@d../.|
000000a6

我使用struct编写了自己的版本:

import struct

HEAD_ID = b"\x4d\x54\x68\x64"
TRACK_ID = b"\x4d\x54\x72\x6b"

class HeaderChunk:
    def __init__(self, format, ntrack, tickdiv):
        self.format = format
        self.ntrack = ntrack
        self.tickdiv = tickdiv

    def dump(self):
        payload = struct.pack(">HH2s", self.format, self.ntrack, self.tickdiv)
        header = HEAD_ID + struct.pack(">I", len(payload))
        return header + payload


class TrackChunk:
    """Represents a track"""
    def __init__(self):
        self.data = b""
    def quarter(self, note):
        self.data += b"\x00\x90" + note + b"\x64\x60\x80" + note + b"\x64"

    def dump(self):
        header = TRACK_ID + struct.pack(">I", len(self.data))
        return header + self.data


header = HeaderChunk(1, 2, b"\x00\x60")

first_track = TrackChunk()
for _ in range(8):
    first_track.quarter(b"\x3c")

second_track = TrackChunk()
for _ in range(8):
    second_track.quarter(b"\x40")


with open("joac-example.mid", "wb") as output:
    output.write(header.dump())
    output.write(first_track.dump())
    output.write(second_track.dump())

它正确地装载在车库带上

相关问题 更多 >