View Single Post
Old 09-07-2015, 03:39 PM   #46
planetnine
Human being with feelings
 
planetnine's Avatar
 
Join Date: Oct 2007
Location: Lincoln, UK
Posts: 7,926
Default

Those hex values are strings representing the value of each byte in the .mid binary file. You can probably convert them to whatever value you need from that, or it's probably better to amend the script you have to return numerics into an array or table in Lua. It looks like the file is read into a table (t?) and stored as bytes in lines, so you're going to have to learn the syntax of that so you can read those bytes into the format you need. I'll investigate as I have time, but I've not used my Lua guide for a while

I've taken my info mostly from this web page: (.mid) Standard MIDI File Format. Unfortunately if you are going to read .mid files, you need to write your script to deal with any and all of the MIDI event commands or metacommands that are likely to be encountered, even if they are not actualy needed for your required dataset (where they could be dumped into dummy variables). This web page seems to cover them all.

I hope this is of some use to you, maybe in a week or two I'll have a bit of time to follow up on the Lua side of this problem (and catch up on long-overdue debugging my own EEL scripts)

Here goes...



The bytes in the file are read differently depending on where you are and what you've just read-in. For headers, the byte values are converted directly into ASCII characters (header titles); for some values, four bytes are used to represent a straight-up 32-bit integer; for others, two bytes are used as a 16-bit integer; for some 1-byte is used for an 8-bit integer. For one value, because it has such a wide range of values in large (long) MIDI files, it uses 7-bits out of each of a variable number of bytes, the first bit used for indication whether there is another byte to read-in or you've reaced the end of the integer.

The file is separated into chunks. There is a header chunk and one or more track chunks. The track chunks exist for each MIDI track in the file and contain as many MIDI commands as are needed to describe the MIDI items in each track (your example has only one track).


The header chunk always looks like: 4D 54 68 64 00 00 00 06 ff ff nn nn dd dd

The first four bytes are converted to directly to ASCII and represent the header tag or title: 4D "M", 54 "T", 68 "h", 64 "d"

The next four bytes represent the remaining bytes in the header. 00 00 00 06 is a 32-bit integer represented as 0x00000006, ie 6 in decimal. Apparently it always reads 6 as the structure of the header is actually fixed.

the remaining six bytes are represented here by ff ff nn nn dd dd. They are three 2-byte integers which store the parameters:
ff ff file format. This can be 0 (single track), 1 (multiple tracks, synchronous) or 3 (multiple tracks, asynchronous).
nn nn number of MIDI tracks in the file.
dd dd number of ticks per quarter.

Taking the hex bytes from your example, it translates as:
4D 54 68 64 Header tag "MThd"
00 00 00 06 0x00000006, dec6, 6 bytes to follow in header.
00 00 00 01 03 C0 0x0000, dec0, single track MIDI file; 0x0001, decimal1, num of tracks =1; 0x03C0, dec960, ticks per qtr =960.


Then follow the track chunks. In your example there is just one.

4D 54 72 6B Header tag "MTrk"
00 00 00 1C 0x0000001C, dec28, 28 bytes to follow in this track chunk.



After this, the track chunk follows the structure for each MIDI item:
Δt (delta ticks, change in ticks since last command)
MIDI event command
Command parameters.
...with a command to end that track chunk.

Δt
Because there is a huge variance in the size of Δt, to save padding out small values to accommodate the possible very much larger values in the file, a variable-size byte system is used. The binary value is stored as seven bits in as many 8-bit bytes as are needed, the most significant bit in each byte being used to indicate if it is the last byte for that value of Δt. If the first bit is a "1", then more byte for that Δt follow, if it is a "0", then it is the last byte.

It sounds a bit complicated, but it's actually quite elegant. Let's take a Δt value of 115200 (one minute at 120bpm, here), which is 00000000 00000001 11000010 00000000 using four 8-bit bytes. If this is rearraged into 7-bit bytes: 0000111 0000100 0000000, and padded to 8-bits with a marker bit (msb): 10000111 10000100 00000000, the reading application knows when to stop as the msb is zero.

It does mean you need to test for that bit as you read in the bytes, but a 1 means the byte >= dec128 (0x80), so you can just test for it and if true (1)subtract 128, (2)add it to the previous (multiplied) bit, (3)multiply the result by 128 to shift it 7 bits ...until you reach the zero msb byte, the last byte of the parameter, and just add it.


MIDI event commands
One byte long, separated into two 4-bit integers (nibbes)
The first 4 bits represent the command ID, the second, the MIDI channel (0 to 15=chns 1 to 16). Metacommands don't include the MIDI channel number, they have the format: byte "FF" followed by the ID byte (and parameters).

Command parameters
The number of parameters and the number of bytes needed for these are dependent on the particular MIDI event command. For standard commnds, it is one or two 1-byte values. For MIDI metacommands, the structure is dependent on the particular metacommand, but some byte lengths are variable and that length is contained in the first parameter byte.



To continue with your example, here are the remaining 28-bytes translated:

00 Δt, dec0. First event, so ticks=0 +Δt =0+0 =0.
90 bin1001 0000, nibbles dec9 and dec0 represent "note on" and chan1.
3C 60 for "note on", nn and vv, note=dec60 and velocity=dec96.

81 70 Δt, dec129 and dec112, bin 10000001 and 01110000
7-bit values, dec1 and dec112, combined dec240 (11110000)
ticks=ticks +Δt =0+240 =240
80 bin1000 0000, nibbles dec8 and dec0 represent "note off" and chan1.
3C 00 for "note off", nn and vv, note=dec60 and velocity=dec0.

85 50 Δt, dec133 and dec80, bin 10000101 and 01010000
7-bit values, dec5 and dec80, combined dec720 (0010 11010000)
ticks=ticks +Δt =240+720 =960
90 bin1001 0000, nibbles dec9 and dec0 represent "note on" and chan1.
3C 60 for "note on", nn and vv, note=dec60 and velocity=dec96.

81 70 Δt, dec129 and dec112, bin 10000001 and 01110000
7-bit values, dec1 and dec112, combined dec240 (11110000)
ticks=ticks +Δt =960+240 =1200
80 bin1000 0000, nibbles dec8 and dec0 represent "note off" and chan1.
3C 00 for "note off", nn and vv, note=dec60 and velocity=dec0.

86 30 Δt, dec134 and dec48, bin 10000110 and 00110000
7-bit values, dec6 and dec48, combined dec816 (0011 00110000)
ticks=ticks +Δt =1200+816 =2016
B0 bin1011 0000, nibbles dec11 and dec0 represent "control change" and chan1.
7B 00 for "control change", cc and vv, controller num=dec123 and new value=dec0.

00 Δt, dec0, bin 00110000
ticks=ticks +Δt =2016+0 =2016
FF 2F 00 FF denotes metacommand, 2F is "end of track", parameter dec0.



This seems to correspond to your MIDI text:
Code:
File: 11.MID
MFile 0 1 960
MTrk
0 On ch=1 n=60 v=96
240 Off ch=1 n=60 v=0
960 On ch=1 n=60 v=96
1200 Off ch=1 n=60 v=0
2016 Par ch=1 c=123 v=0
2016 Meta TrkEnd
TrkEnd

Hope this helps, I look forward to seeing what you do with it




>
__________________
Nathan, Lincoln, UK. | Item Marker Tool. (happily retired) | Source Time Position Tool. | CD Track Marker Tool. | Timer Recording Tool. | dB marks on MCP faders FR.
planetnine is online now   Reply With Quote