Bass drum uses one channel almost similarly to regular 2-op channel, high hat and snare drum share one channel but have pseudorandom noise in their phase generator, and tom tom and top cymbal share a channel.
Basically there are two problems, to figure out how pseudorandom noise generator affects the operators, and how the pseudorandom noise generator itself works.
The noise generator output could be fed into Berlekamp-Massey algorithm to figure out the noise feedback algorithm, but since there is knowledge about both how noise is generated in MAME and how noise affects operators producing sound, so I just decided to verify if the OPL3 output matches the MAME emulator implementation.
First I read MAME code to figure out how to configure the chip so only the noise affects the sound output. Then 125 seconds of carefully configured snare drum noise was recorded from real OPL3, resulting in about 6.21M samples of audio data. Knowing that the first sample was invalid because it started mid-stream, that sample was skipped and next 64 samples were taken as a string of random numbers into a 64-bit variable. Then the MAME implementation of the linear feedback shift register was used to generate as many random bits as required until its output matches the output samples of the real chip. First there was no match at all, but interpretation of one and zero bits of the original OPL3 stream was then swapped. Then it took about 7.43 Msamples to generate the same string of random bits into another 64-bit variable, so a match was found. Then both the OPL3 output data and linear feedback shift register data was compared if they are in sync or not, and they were in sync for the rest of the data, only the first 65 bits of the 6.21M samples were used to sync the generator to OPL3 stream.
So based on that fact, both OPL2 and OPL3 have identical random number generator and it is implemented correctly in MAME. Syncing the random generator phases with brute force approach and comparing the 6.21M samples took only 2.1 seconds.
- Code: Select all
uint32_t noise=0x1UL; // initialize to nonzero or it won't advance, real value at chip reset unknown.
uint32_t advance_noise(void)
{
if (noise&1) noise=noise^0x800302;
noise=noise>>1;
return (noise&1);
}