Reputation: 556
I am working on a script in Python to parse MIDI files (yes I know MIDI parsing libraries exist for Python but for my use case it's easiest if i make it from scratch).
The one thing I'm having a problem with is the time division. the last two bytes of the header specifies the time division, but I'm having trouble determining if a file's time division is noted in ticks per beat or frames per second. After doing some reading, it seems that the top bit of the top byte indicates which of the two the time division is noted in. What I am confused about is if the top bit of a byte is the first bit of a byte or the last bit of a byte, as well as how to read the MIDI time division entirely.
EDIT: for example, a header of a MIDI file I have is the following:
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.
Edit 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")
Upvotes: 3
Views: 1348
Reputation: 2576
Maybe you don't know that the official MIDI specifications are available and you can download the document for free. (You need to register as site user first). It includes the detailed SMF format.
Here is the description of the header chunk.
The header chunk at the beginning of the file specifies some basic information about the data in the file. Here's the syntax of the complete chunk:
<Header Chunk> = <chunk type> <length> <format> <ntrks> <division>
As described above, <chunk type>
is the four ASCII characters 'MThd'; <length>
is a 32-bit representation of the number 6 (high byte first).
The data section contains three 16-bit words, stored most-significant byte first.
The first word, <format>
, specifies the overall organization of the file. Only three values of <format>
are specified:
0=the file contains a single multi-channel track
1=the file contains one or more simultaneous tracks (or MIDI outputs) of a sequence
2=the file contains one or more sequentially independent single-track patterns More information about these formats is provided below.
The next word, <ntrks>
, is the number of track chunks in the file. It will always be 1 for a format 0 file.
The third word, <division>
, specifies the meaning of the delta-times. It has two formats, one for metrical time, and one for time-code-based time:
|bits |
|15|14 ... 8|7 ... 0 |
|--|-----------------------|-----------------|
| 0| ticks per quarter-note |
| 1| negative SMPTE format | ticks per frame |
If bit 15 of <division>
is a zero, the bits 14 thru 0 represent the number of delta-time "ticks" which make up a quarter-note. For instance, if <division>
is 96, then a time interval of an eighth-note between two events in the file would be 48. If bit 15 of <division>
is a one, delta-times in a file correspond to subdivisions of a second, in a way consistent with SMPTE and MIDI time code. Bits 14 thru 8 contain one of the four values -24, -25, -29, or -30, corresponding to the four standard SMPTE and MIDI time code formats (-29 corresponds to 30 drop frame), and represents the number of frames per second. These negative numbers are stored in two's complement form. The second byte (stored positive) is the resolution within a frame: typical values may be 4 (MIDI time code resolution),
8, 10, 80 (bit resolution), or 100. This system allows exact specification of time-code-based tracks, but also allows millisecond-based tracks by specifying 25 frames/sec and a resolution of 40 units per frame. If the events in a file are stored with bit resolution of thirty-frame time code, the division word would be E250 hex.
In your example, your third word (hex 0078) means that the <division>
is 120 ticks per quarter-note.
Delta time is given in ticks for the events in the file. Time signature is another totally different thing. It is an indication of the rhythm, and is a meta-event type. (See page 10 of the specification).
Upvotes: 5