I've been making audio tests of my FDS over the last few days, and I've spent a lot of time trying to verify the currently used formula for modulation strength outlined in disch's old document (and currently on the wiki FDS audio page), which always struck me as a very odd formula, especially since disch himself had a disclaimer that he'd never peresonally tested the real hardware. I found it to be mostly correct, but requiring a few tweaks to get it to match the empirical data I gathered from my test recordings.
The notable changes to disch's formula are:
So... at least the wrapping step got simpler (and common-sensified), even though there are two new rounding complications. I have no idea why it only adjusts if bit 7 is clear. The -1/+2 thing is also strange to me, but that was already there. I can't explain why it's this way, but it definitely matches my tests now.
I will add this to the wiki in a few days, as I am still doing another round of tests at the moment. In the meantime, is there anybody here who can independently verify this? I can detail the methods I used to determine this if anyone's interested, but I would appreciate verification by a different experiment if possible.
Code:
// pitch = $4082/4083 (12-bit unsigned pitch value)
// bias = $4085 (7-bit signed bias)
// gain = $4084 (6-bit unsigned gain)
// 1. multiply bias by gain, lose lowest 4 bits of result but "round" in a strange way
temp = bias * gain;
remainder = temp & 0xF;
temp >>= 4;
if ((remainder > 0) && ((temp & 0x80) == 0))
{
if (bias < 0) temp -= 1;
else temp += 2;
}
// 2. wrap if a certain range is exceeded
if (temp >= 192) temp -= 256;
else if (temp < -64) temp += 256;
// 3. multiply result by pitch, then round to nearest while dropping 6 bits
temp = pitch * temp;
remainder = temp & 0x3F;
temp >>= 6;
if (remainder >= 32) temp += 1;
// final mod result is in temp
// bias = $4085 (7-bit signed bias)
// gain = $4084 (6-bit unsigned gain)
// 1. multiply bias by gain, lose lowest 4 bits of result but "round" in a strange way
temp = bias * gain;
remainder = temp & 0xF;
temp >>= 4;
if ((remainder > 0) && ((temp & 0x80) == 0))
{
if (bias < 0) temp -= 1;
else temp += 2;
}
// 2. wrap if a certain range is exceeded
if (temp >= 192) temp -= 256;
else if (temp < -64) temp += 256;
// 3. multiply result by pitch, then round to nearest while dropping 6 bits
temp = pitch * temp;
remainder = temp & 0x3F;
temp >>= 6;
if (remainder >= 32) temp += 1;
// final mod result is in temp
The notable changes to disch's formula are:
- the weird early "rounding" step is only applied if bit 7 of the result is clear
- the wrapping step no longer uneven, the fix to the rounding makes this wrap really just an 8-bit result biased by -64
- the final result appears to be rounded to nearest, rather than simply truncating
So... at least the wrapping step got simpler (and common-sensified), even though there are two new rounding complications. I have no idea why it only adjusts if bit 7 is clear. The -1/+2 thing is also strange to me, but that was already there. I can't explain why it's this way, but it definitely matches my tests now.
I will add this to the wiki in a few days, as I am still doing another round of tests at the moment. In the meantime, is there anybody here who can independently verify this? I can detail the methods I used to determine this if anyone's interested, but I would appreciate verification by a different experiment if possible.