Page 1 of 1

Rhythm mode

PostPosted: Sat Mar 24, 2012 12:23 am
by opl3
The rhythm mode converts last three of the first nine 2-operator channels into five rhythm channels.

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;
    return (noise&1);

Re: Rhythm mode

PostPosted: Wed Jul 18, 2012 7:51 am
by carbon14
I have started work on what I call phase 3 of this project.
Phase 1 was the the phase generators and the non-varying or cyclic attenuations
Phase 2 was the envelope generators and various options there
Phase 3 is the rhythm section
Phase 4 will be the remaining parts of the chip, including the timers / interrupts

I started by identifying which slots affect which of the 5 rhythm sounds, although the process is not complete I've started. There are 6 slots assigned to the rhythm section, from 13 to 18, and 5 of these are associated one each with the rhythm sounds. A second slot, slot 13 is also associated with the bass drum as stated above, but I've not yet seen the effect of that because I've so far only worked on the bass drum with 0Hz waveforms.

I have also seen the noise that opl3 was measuring. It's nice and clear in the High hat output. I took a small sample of the output, extracted the noise signal from it and I did feed it through the Berlekamp-Massey algorithm

103 samples yielded a characteristic polynomial of x^23 + x^22 + x^15 + x^14 + 1 which is consistent with the noise generator opl3 used. This is no surprise of course because he has already proven that such an lfsr generates noise that matches the chip. But I wanted to close the circle.

the noise generator in the example posted above is a Galois configured XOR lfsr. This is almost certainly the most efficient configuration that can be written in C. But I wonder if an XNOR configured lfsr might be simpler to acheive in the silicon. The issue with an XOR generator is the need to ensure that the ring doesn't boot up with all the bits reset. This would be degenerate and would generate no noise at all. You can get round the problem by ensuring that at least one bit (or all of them) boots up set. But I believe that you could simply allow the ring to boot up with the bits reset, but add an extra inverter. Given that the reset pin sets pretty much everything else to 0, it would probably be consistent to reset the noise ring at the same time and this arrangement would prevent degeneration of the lfsr. The resultant output is an inversion of the output of the XOR configured lfsr, but given that the output is not used as a number, but as a switch between two code pathways, its actual binary sense would seem to be irrelevant.

This is all probably moot. I'm sure that the XNOR configuration is slower when written in C, and would not be efficient in machine code either unless your processor has a shift instruction that sets the incoming bit.

footnote: I referred to a noise ring above; that's because in the traditional fibonacci configuration an lfsr does resemble a ring. Most such devices are built with maximum length cycles (m-sequences) and all such sequences apparently include the first bit feeding back (along with others) into the last bit. This is the conceptual ring. The Galois configuration still looks like a ring if built in logic gates, but it's much less obviously ring-like when written as a bitmask in C.

Re: Rhythm mode

PostPosted: Thu Jul 19, 2012 12:02 pm
by carbon14

just realised that you can implement the xnor lfsr efficiently in machine code. If you initialise the noise loop with a 1 in the msb position, then use a arithmetic shift right instruction (available on most microprocessors), this wil populate the new msb with a 1 as well. Because this is a 23 bit shift register, sitting in a 32 bit register in the processor, the actual logic of the lfsr never goes near the uppermost bits, so your set bit 32 is protected and continues to pump new set bits into the top of the shift register.

Re: Rhythm mode

PostPosted: Thu Jul 26, 2012 11:10 am
by carbon14
Playing with the high hat:

The output of the high hat seems to be a combination of a pulse form for the phase, and the output of the noise ring.

At the frequency which I'm currently using, I'm getting out 4 samples which are taken from either the 52nd or the 208th entry in the waveform, followed by 12 samples which are taken from the 564th or 720th entry. This pattern of 4 and 12 is repeated, but for each sample, whether it's the first or the second of the two possible values is down to the noise output.

I determined which entries these were, by looking at the output attenuation, and finding the matching output values in the waveform. For waveform 0, the simple sine wave, then there are two (or more) samples with any given value, but by cross comparing with the other waveforms it was possible to narrow these down to just sample points, which give consistent and unique results across all 8 possible waveforms.

My initial tests with the high hat, carried out with 0Hz frequency showed no -ve output, so presumably the frequency of the high hat can be controlled by F-NUM although I haven't got as far as testing.

This experiment also highlighted what appears to be a bug in my originally posted waveform lookup code. I've posted corrected code in the 'Code' topic.

Re: Rhythm mode

PostPosted: Wed Aug 01, 2012 9:15 am
by carbon14
More on the high hat. The basic frequency of the high had seems to be 64 times higher than that of the melody.

I ran a sound through as a melody, and then immediately afterwards I used the same operators to generate a high hat sound, I would suggest that the phase generator operates as normal, and two bits are used to determine which part of the wave table to use.

Talking in terms of a 10-bit phase generator:

If the phase is xxxxxx01xx and the noise bit is 0, use sample 0x34 from the wave table
if the phase is xxxxxx01xx and the noise bit is 1, use sample 0xd0 from the wave table
if the phase is not xxxxxx01xx and the noise bit is 0, use sample 0x2d0 from the wave table
if the phase is not xxxxxx01xx and the noise bit is 1, use sample 0x234 from the wave table

Starting with the melodic sound, I was able to identify the 10-bit phase sample by sample.
Then when I ran the rhythmic sound, the phase generator should continue as before, and I can match up the 0x34 and 0xd0 samples to specific phases.

As to the contribution of the noise generator:-

I started with a 0Hz noise, which has a constant phase contribution, this yielded 0x34 and 0xd0 samples. Assuming that 0xd0 comes from a noise bit value of 1, I fed this into the Berlekamp-Massey algorithm and got the characteristic polynomial previously described.
If we assume that 0x34 comes from a noise bit value of 1, we get a less than optimal lfsr instead. Occam's razor suggests that the first assumption is correct.

Then adding in a phase contribution, I can test the hypotheses that 0x234 results from a noise value of 1, or that 0x2d0 results from a noise value of 1.

Only one of these hypotheses yields the same characteristic polynomial, the other yields an absurd polynomial with a span of 34 bits, and I suspect I didn't feed it sufficient data points, so it may well be larger still.

Given that the 0Hz signal yielded positive data points, and that my latest findings suggest that this must result from a non-zero phase, there is a discrepancy there that I need to investigate further. It's entirely possible that my previous tests were carried out on an operator which had been running at a non-zero frequency and was therefore at a non-zero phase when I lowered the frequency to 0.

Re: Rhythm mode

PostPosted: Thu Aug 02, 2012 6:11 am
by opl3
carbon14 wrote:Talking in terms of a 10-bit phase generator:

If the phase is xxxxxx01xx and the noise bit is 0, use sample 0x34 from the wave table
if the phase is xxxxxx01xx and the noise bit is 1, use sample 0xd0 from the wave table
if the phase is not xxxxxx01xx and the noise bit is 0, use sample 0x2d0 from the wave table
if the phase is not xxxxxx01xx and the noise bit is 1, use sample 0x234 from the wave table

Comparing your findings to another emulator (I have not verified myself), you should have the four output phases (0x34,0xd0,0x234,0x2d0) correct. In fact, it is also funny that 0xd0>>2==0x34.

And the sound does depend on bits 2 and 3 of one phase generator. But it should depend on more than two bits from that phase generator, and also some bits from another phase generator. Also your explanation what output phases are caused by noise only and what output phases are caused by input phase only seem differ from the emulator comments, but of course the emulator can have an error.

So maybe you do have one or two phase generators running, or stopped to non-zero phase. That could explain the discrepancies you are seeing. However, the High Hat is by far the most complex waveform to solve, so it will only get easier after this.

Re: Rhythm mode

PostPosted: Fri Aug 03, 2012 11:56 am
by carbon14
That's great.

And a good point. I have been working with a single phase generator at this time. Obviously I should widen the scope of my experiments here.