Page 1 of 1

Music

Posted: 02 Apr 2007 20:43
by The Straw Guy
I have the DOS version of Transport Tycoon Deluxe and have just found out about OpenTTD, it's good but how do I get music? I can only see instructions for the windows version (I'm running XP)

Cheers All

Posted: 02 Apr 2007 21:10
by glx
You can't get the music from DOS TTD.

Posted: 07 Apr 2007 18:46
by cirdan
Hi everyone,

Sorry to jump in with my first post, but I think this is worth mentioning.
You can't get the music from DOS TTD.
Well, you can, I've done it. :)

As I've read in the documentation, the "standard" way to get the music working in OpenTTD is to copy the MIDI files into the data/gm directory, and then the game will play the files from there. If I'm not mistaken, the windows version of TTD has all of the required files in the clear, so one only has to copy them into that directory.

The DOS version is different. It has the music as well (it plays during the game, after all), but the data is stuffed within a single file (gm.cat) in a format very similar to MIDI although with some significant differences. Years ago, long before I knew of OpenTTD (in fact, long before the OpenTTD project was started), I happened to be fiddling with this file and I managed to make sense of it, so I wrote a program to decode the music data out of it and into standard MIDI files, because I wanted to be able to play the music outside the game. When I later installed OpenTTD, I found that those files did the trick to get music working---I just had to guess the right names for them, gm_ttNN.gm.

After seeing this thread I decided to check if I still had that program at hand, and I do. I can post it if there is any interest in it.

Posted: 07 Apr 2007 19:14
by Lordmwa
I have done this and founs it easy. I had no idea that it supports MIDI does this mean that you can stick any random midis int he folder

Posted: 07 Apr 2007 20:51
by Jezulkim
cirdan wrote:After seeing this thread I decided to check if I still had that program at hand, and I do. I can post it if there is any interest in it.
Please do, I have a program that can extract wav files from Microprose cat files. It'd be nice to be able to do same for the cat files with midi inside.

Posted: 08 Apr 2007 10:16
by cirdan
Here it is. I've taken my old program (dating back to 2002) and polished it a bit, so it compiles cleanly (with gcc) on my box (linux and glibc), but it only uses standard C and libc functions so compilation on other platforms should be straightforward.

Once compiled, you have to run it on the same directory as the 'gm.cat' file, and it will extract the songs it finds in there, naming the output files it produces along the way. Alternatively, the full path to a file can be given on the command line. The ouput files will be standard multi-track MIDI files, with the song title in a text chunk. To use the files with OpenTTD you should rename the XX.mid files to gm_ttXX.gm and place them in the data/gm or equivalent directory, although you can use the extractor on gm.cat files from other games, too.

Some considerations: I've been able to extract the MIDI files from the DOS TTD gm.cat file, and from other gm.cat files, but I cannot guarantee that it will always work. Also, there are some basic sanity checks in the program to test for the expected format of the file, but most probably they are not fool-proof, so the program may crash if the format is different; you have been warned. Lastly, the program is designed to run on a little-endian machine, and relies on the fact that the gm.cat format is little-endian, so it won't work on big-endian machines.

Suggestions and bug reports are welcome.

Posted: 08 Apr 2007 11:39
by Maedhros
It would be really great if you could document the file format used by the gm.cat file, as I'd like to help to add a native decoder to OpenTTD so it could play the gm.cat file natively. :)

Posted: 08 Apr 2007 14:38
by cirdan
It would be really great if you could document the file format used by the gm.cat file
The problem with documenting the file format is that I understood it and wrote the program five years ago, then extracted the songs and then forgot about it. Anyway, this is what I can recall from the code.

The file starts with an index array that allows to seek to a particular song within the file. Each element in the array is a pair of little-endian uint32's: The first of them is the offset within the file of a particular song, while the second is the length in bytes of the song. (This is best followed with a hex dump of the file in front of you.) Normally the sum of the offset and the length matches the offset of the next song. Also, there is no way to know in advance how many songs there are (the program doing the decoding is supposed to know that), but the songs start the first byte after the index array, so in fact the first offset gives the number of songs times 8 (the size of two uint32's).

The rest of the file contains the songs, as indexed in the header. Each song has three parts: the title, the subsequences and the tracks. They are packed together one after another.

The title is easy: It has a length byte and then that many characters that make up the name of the song.

The subsequence part starts again with a single byte that is the number of subsequences, and then has that many subsequences. Each subsequence starts with a little-endian uint32 representing the subsequence size (including the uint32) and then has the subsequence data, in standard MIDI format: first the delta-time, then the command, then the command data.

The track part is similar. It also has a first byte stating the number of tracks, and then that many tracks, only that tracks, as opposed to subsequences, have a leading channel byte. So a track is one channel byte (the MIDI channel to which output for that track will be sent), then a little-endian uint32 with the size of the track (including the uint32 itself but excluding the leading channel byte), and then the track data, again in standard MIDI format.

In the program I posted, the function gmext_song is responsible for loading the song data; everything from the start to the comment that reads 'write output file' is all that is needed to read a particular song into memory.

This is all about the file format. However, to properly decode the track data (as a MIDI sequencer would have to do), there are some caveats about the MIDI commands; all this is taken from the source code of the program I posted:

* Commands 0x80/0x90 have their second data byte (the volume) modified.

* Command 0xC0 treats some values of its data byte specially.

* Command 0xB0 also treats some data values specially.

* Commands 0xFD, 0xFE, 0xFF have a special meaning in the decoding.

Command 0xFE is the 'insert subsequence' command. It has a single data byte which is the number of the subsequence to be inserted replacing the 0xFE command. This way, subsequences are parts of the song which are factored out of the track data (possibly to reduce storage size) and get inserted at runtime.

I do not remember what commands 0xFD and 0xFF do. Telling from the program, since they return control from the decoding function, they must signal termination of the current subsequence or track, so they can be used to append padding delta-time at the end of a subsequence. I vaguely remember that they did occur as the last command in a sequence (I should double-check this), and it is possible that one of them meant normal ending while the other meant restart (used in the theme song to get automatic repetition).
I'd like to help to add a native decoder to OpenTTD so it could play the gm.cat file natively.
If you are interested in a native decoder, I can write it for you, if you just tell me in which format you want the data to be stored in memory.

Posted: 08 Apr 2007 16:03
by Jezulkim
Sorry, but could it be possible to have it compiled, as I can't compile.

Posted: 08 Apr 2007 16:45
by DaleStan
Yes, you can. Even if you're on Windows. Cygwin is a beautiful thing.

Or, if you're getting error messages, you need to report them.

Posted: 08 Apr 2007 19:20
by Jezulkim
Thanks, it worked. And the biggest thankyou to you, cirdan :D