fbpx
Skip to main content

MIDI Forum

Help with track len...
 
Notifications
Clear all

Help with track length code

9 Posts
3 Users
0 Reactions
5,604 Views
Jason
Posts: 441
Honorable Member
Topic starter
 

My track length code seems to have a bug, though I currently do not see what the issue is. Am I not using correct values somewhere? The code is GML (Game Maker Language), which is C/C++ like.

old_len4 through old_len1 are the 4 bytes of the track length as read from the midi file left to right (bytes are read individually).
old_len is the total of those values as a single combined sum.

I read the 4 individual bytes, combine them, add a certain number of bytes to the track length (new_length_add), and then write the new bytes back to the file.

It's worked so far, but I have just come across a file where my final values are off, thus corrupting the file.
My final track length should be
00 01 0C 05
but instead I end up with
00 02 0B 05
for a difference of
FF 00

This code has been presumably run many many times before the file is saved as I add in SYSEX resets and bank selects. I haven't had an issue thus far because this file must have a track length value that ends up right on the threshold between working and breaking, where all other files up to this point were far enough away from whatever the issue is that they did not trigger it. I had very lazy track length code prior to December that also worked for a very long time before it finally caused a problem, and this updated code was meant to fix my laziness, which it has up until now.

[code type=markup]
old_len4 = buffer_peek(new_buffer, buffer_tell(new_buffer), buffer_u8) * 0x1000000;
old_len3 = buffer_peek(new_buffer, buffer_tell(new_buffer) + 1, buffer_u8) * 0x10000;
old_len2 = buffer_peek(new_buffer, buffer_tell(new_buffer) + 2, buffer_u8) * 0x100;
old_len1 = buffer_peek(new_buffer, buffer_tell(new_buffer) + 3, buffer_u8);
old_len = old_len4 + old_len3 + old_len2 + old_len1;
old_len += global.new_length_add;
if (old_len > 0xFFFFFF)
{
buffer_write(new_buffer, buffer_u8, old_len / 0x1000000);
old_len -= 0x1000000;
}
else
buffer_seek(new_buffer, buffer_seek_relative, 1);
if (old_len > 0xFFFF)
{
buffer_write(new_buffer, buffer_u8, old_len / 0x10000);
old_len -= 0x10000;
}
else
buffer_seek(new_buffer, buffer_seek_relative, 1);
if (old_len > 0xFF)
{
buffer_write(new_buffer, buffer_u8, old_len / 0x100);
old_len -= 0x100;
}
else
buffer_seek(new_buffer, buffer_seek_relative, 1);
buffer_write(new_buffer, buffer_u8, old_len);
[/code]

 
Posted : 08/02/2023 2:53 pm
Bavi_H
Posts: 267
Reputable Member
 

1. I think the lines like this:

old_len -= 0x1000000;
old_len -= 0x10000;
old_len -= 0x100;

will only work right when the bytes in the first three positions are exactly 1:

01 01 01 xx

2. Perhaps you might consider using bitwise operators, some people might find it easier to follow:

old_len = ( buffer_peek(new_buffer, buffer_tell(new_buffer)    , buffer_u8) << 24 ) +
( buffer_peek(new_buffer, buffer_tell(new_buffer) + 1, buffer_u8) << 16 ) +
( buffer_peek(new_buffer, buffer_tell(new_buffer) + 2, buffer_u8) << 8 ) +
( buffer_peek(new_buffer, buffer_tell(new_buffer) + 3, buffer_u8) ) ;

old_len += global.new_length_add;

buffer_write(new_buffer, buffer_u8, (old_len & 0xFF000000) >> 24);
buffer_write(new_buffer, buffer_u8, (old_len & 0x00FF0000) >> 16);
buffer_write(new_buffer, buffer_u8, (old_len & 0x0000FF00) >> 8);
buffer_write(new_buffer, buffer_u8, (old_len & 0x000000FF) );

3. Minor nitpick: You might consider changing the name of "old_len" to just "length" or something similar. (Once you add in "global.new_length_add", it's no longer the "old_len", it's the "new_len" right?)

 
Posted : 08/02/2023 4:45 pm
Jason
Posts: 441
Honorable Member
Topic starter
 

[quotePost id=17558]
will only work right when the bytes in the first three positions are exactly 1:
[/quotePost]
I thought so too. I tried to compensate by multiplying by the digit provided by the division in the previous line, but it ended up neutralizing all changes to the track length.

2. Perhaps you might consider using bitwise operators, some people might find it easier to follow:

My brain can not properly process bitwise operators 😉 Though that is probably the ideal way to do it. 😀

3. Minor nitpick: You might consider changing the name of "old_len" to just "length" or something similar. (Once you add in "global.new_length_add", it's no longer the "old_len", it's the "new_len" right?)

I used to have a separate value from my previous lazy math version, but when I updated it, I ended up just using the single value, so now everything old is new again?

Just before you replied, I changed all of the "/" to "div" to force integers only, but I think Game Maker automatically does that when using hex values anyway. I also realized that if the value I am checking is less than the value I'm comparing to (ie, old_len > 0xFFFF) I was moving to the next byte, ASSUMING that the byte already there by default would already be 0, which I have found not to be the case. I added in writing a 0 byte instead of skipping, and at least in this particular broken file, it now works properly.

[code type=markup]
if (old_len > 0xFFFFFF)
{
buffer_write(new_buffer, buffer_u8, old_len div 0x1000000);
old_len -= 0x1000000;
}
else
buffer_write(new_buffer, buffer_u8, 0);
// buffer_seek(new_buffer, buffer_seek_relative, 1);

[/code]

 
Posted : 08/02/2023 5:26 pm
Carlos
Posts: 86
Estimable Member
 

As Bavi_H says, his problem is that he only subtracts 0x1000000

If I understand the problem correctly it should be like this without bit operators:

[code type=markup]//This operation looks ridiculous written like this but don't forget that you are losing the decimal places in the process. 😀
old_len -= (old_len / 0x1000000) * 0x1000000;
old_len -= (old_len / 0x10000) * 0x10000;
old_len -= (old_len / 0x100) * 0x100;[/code]

I don't know GML but why write each byte separately and not directly write old_len?

 
Posted : 24/02/2023 6:32 pm
Bavi_H
Posts: 267
Reputable Member
 

[quotePost id=17767]why write each byte separately and not directly write old_len?[/quotePost]
Endianness.

 
Posted : 24/02/2023 6:57 pm
Jason
Posts: 441
Honorable Member
Topic starter
 

[quotePost id=17767]As Bavi_H says, his problem is that he only subtracts 0x1000000

If I understand the problem correctly it should be like this without bit operators:

[code type=markup]//This operation looks ridiculous written like this but don't forget that you are losing the decimal places in the process. 😀
old_len -= (old_len / 0x1000000) * 0x1000000;
old_len -= (old_len / 0x10000) * 0x10000;
old_len -= (old_len / 0x100) * 0x100;[/code]

I don't know GML but why write each byte separately and not directly write old_len?[/quotePost]
Haha, actually, I probably can... I've been doing everything with unsigned 8 bit data (1 byte) because most midi data is byte specific. Apparently it supports many more read/write sizes, including 32 bit unsigned, which is just the right length.

https://manual.yoyogames.com/GameMaker_Language/GML_Reference/Buffers/buffer_write.htm

Guess I have something to test out.

 
Posted : 24/02/2023 10:16 pm
Bavi_H
Posts: 267
Reputable Member
 

[quotePost id=17767]why write each byte separately and not directly write old_len?[/quotePost][quotePost id=17768]Endianness.[/quotePost]__________[quotePost id=17767]why write each byte separately and not directly write old_len?[/quotePost][quotePost id=17769]Haha, actually, I probably can... I've been doing everything with unsigned 8 bit data (1 byte) because most midi data is byte specific. Apparently it supports many more read/write sizes, including 32 bit unsigned, which is just the right length.[/quotePost]
To expand on my previous post about endianness: Most modern computers use little endian ordering, but the chunk size values in a MIDI file use big endian ordering. To ensure the value is read and written correctly, you should read and write the bytes individually.

 
Posted : 25/02/2023 4:53 pm
Jason
Posts: 441
Honorable Member
Topic starter
 

[quotePost id=17779]To expand on my previous post about endianness: Most modern computers use little endian ordering, but the chunk size values in a MIDI file use big endian ordering. To ensure the value is read and written correctly, you should read and write the bytes individually.[/quotePost]
I was aware of endianness but did not think it would ever be an issue for anything I was doing. However, I just checked some Game Maker information, and you are correct. This is an example that was provided:

var buffer = buffer_create(4, buffer_fixed, 1);
buffer_write(buffer, buffer_u32, 0x01020304);

buffer_seek(buffer, buffer_seek_start, 0);
repeat (4) {
show_debug_message(buffer_read(buffer, buffer_u8)); // prints 4, 3, 2 and then 1
}
bufffer_delete(buffer);

As you expected (but not me), the bytes for a 32 bit buffer value would be written in "reverse" order, causing chaos for me. I only half-finished changing my code to use a 32 bit write, so I guess I'll put it back and correct my 8 bit buffer write code.

 
Posted : 25/02/2023 8:49 pm
Jason
Posts: 441
Honorable Member
Topic starter
 

[quotePost id=17767][code type=markup]//This operation looks ridiculous written like this but don't forget that you are losing the decimal places in the process. 😀
old_len -= (old_len / 0x1000000) * 0x1000000;
old_len -= (old_len / 0x10000) * 0x10000;
old_len -= (old_len / 0x100) * 0x100;[/code]
[/quotePost]
Because of how the math is handled in GameMaker (no type casting or inherent distinction between decimal and hex during calculation), I had to change the "/" (which keeps remainders) to "div" (which discards remainders) to get the correct track lengths, otherwise the files were getting corrupted with incorrect track lengths. So far, so good!

 
Posted : 26/02/2023 12:08 pm
Share: