Malvineous / libgamemusic

Library providing a standard mechanism for playing and converting music used in DOS games.
http://www.shikadi.net/camoto/
GNU General Public License v3.0
23 stars 4 forks source link

[cdfm] New format discoveries to implement #9

Open sagamusix opened 6 years ago

sagamusix commented 6 years ago

Note: These observations are based on the Amnesia and Zone 66 S3M conversions which I assume went through this tool, as well as reading through mus-cdfm-zone66.cpp.

Volume

Composer 670 / CDFM uses linear volume for both PCM and OPL instruments. S3M only uses linear volume for samples and uses the OPL's logarithmic volume for OPL instruments. Hence CDFM volume needs to be re-mapped for OPL instruments when converting to S3M.

The following table is from the CDFM source code (fmvoltbl in DFMPM.ASM), but the values are upside down to compensate for the fact OPL volume 0 equals S3M volume 64:

static const uint8 fmVolume[16] =
{
    0x08, 0x10, 0x18, 0x20, 0x28, 0x2C, 0x30, 0x34,
    0x36, 0x38, 0x3A, 0x3C, 0x3D, 0x3E, 0x3F, 0x40,
};

Note that there is a slightly different variant of the table in DFMPMA.ASM so it is not entirely clear which of the two tables has been used. Maybe an uncompressed disassembly of Amnesia/Zone66 would give some insight here.

PCM volume should probably be translated using the formula 4 * volume + 4 (yes, volume 0 is not completely silent).

More than 16 instruments

CDFM can address 32 PCM instruments and 32 OPL instruments. The high bit of the instrument number is stored in the highest bit of the note/octave byte. This can be observed in DARE.C67 which has more than 16 samples.

Pattern breaks

The last issue can be observed in the first pattern of AMNESIA2.S3M: If pattern command 0x60 is found before the last row of the pattern, a pattern break should be inserted in the previous row.

sagamusix commented 6 years ago

Here's another find that cannot really be fixed in the S3M export, but maybe when playing/rendering CDFM files. The Zone 66 soundtrack heavily relies on this hack. After calculating the fnum of a note, the playback routines add the current OPL channel's number modulo 4 to the fnum. So when playing the same note on two channels, they are slightly detuned against each other! In practice, this creates a neat phaser effect.

Malvineous commented 6 years ago

Very interesting finds, many thanks for letting me know! I haven't had a chance to work on libgamemusic for a while but I hope to address these when I next get back to it. Let me know if you find anything else!