VOGONS


(Dual) OPL2 vs OPL3?

Topic actions

First post, by superfury

User metadata
Rank l33t++
Rank
l33t++

Is it easy to add OPL3 support to accurate OPL2 emulation?

From what I can see it's just some (compared to dual OPL2) cross-chip AM/FM synthesis (4-OP mode for up to 6 lower dual-chip channels), stereo field select (left on/off, right on/off) and some extra 4 waveforms (perhaps created based on binary logic and the existing OPL2 waveforms?).

For 4-OP mode, simply bridge the OPL2 left chip(port 220h) to the right chip (port 222h) instead of left chip directly to output on the backend for AM or FM synthesis? In this case, that's operator 2 to 3 in the diagrams (counting 1 through 4)?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 1 of 38, by mkarcher

User metadata
Rank l33t
Rank
l33t

As far as I know, there is a comparison between the digital sound output of the OPL2 and the OPL3, and they found out that the OPL3 does not generate bitwise identical sample data compared to the OPL2. Alas, I don't remember whether this was some blog post or a VOGONs post.

Reply 2 of 38, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've right now simply started implementing the OPL3 based on my OPL2 emulation for now.

The only thing I'm currently stumbling on is the logarithmic sawtooth (waveform #7).
Is it like a normal sine at double speed, but inverted in that case? Since it should be OPL2 compatible with it's lookup tables (probably the same method as OPL2?), it should probably be derived of the 256-entry lookup table too (of a quarter sine wave)?

Any idea how the logarithmic sawtooth is derived from the LogSin tables?

Last edited by superfury on 2025-06-14, 16:04. Edited 1 time in total.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 3 of 38, by mkarcher

User metadata
Rank l33t
Rank
l33t
superfury wrote on 2025-06-14, 14:25:

The only thing I'm currently stumbling on is the logarithmic sawtooth (waveform #7).

I assume the logarithmic sawtooth does not use a lookup table, but directly sends the phase accumulator value to the DAC, which uses a kind of floating-point/logarithmic encoding.

Reply 4 of 38, by superfury

User metadata
Rank l33t++
Rank
l33t++
mkarcher wrote on 2025-06-14, 14:29:
superfury wrote on 2025-06-14, 14:25:

The only thing I'm currently stumbling on is the logarithmic sawtooth (waveform #7).

I assume the logarithmic sawtooth does not use a lookup table, but directly sends the phase accumulator value to the DAC, which uses a kind of floating-point/logarithmic encoding.

But if it didn't use the lookup tables, it would have to resort to very slow floating point algorithmic, wouldn't it? I'd assume it uses some kind of trick like it does with the other waveforms (using the first quarter of the sinus) and perhaps the sign bit too?

It's only got one LogSin lookup table after all. And seeing as it doesn't perform the Exponential table lookup until later in the process, it probably does something to the LogSin table lookup input (index) or the table's output to archieve the 8th wave generation?

Edit: Or supplying a direct mantissa/exponent value set depending on the lookup entry perhaps?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 5 of 38, by mkarcher

User metadata
Rank l33t
Rank
l33t
superfury wrote on 2025-06-14, 16:06:
mkarcher wrote on 2025-06-14, 14:29:

I assume the logarithmic sawtooth does not use a lookup table, but directly sends the phase accumulator value to the DAC, which uses a kind of floating-point/logarithmic encoding.

But if it didn't use the lookup tables, it would have to resort to very slow floating point algorithmic, wouldn't it?

No, that's not what I meant. You get a phase angle as integer, which you use as index into the LogSin table to obtain sine-like waveforms. As you already understand correctly, the lookup into this table is modified to obtain different shapes of sine-derived waveforms. The cool thing about the output of that table being logarithmic is that all multiplications to shape the volume (like the tremolo effect and the envelope generator) are implemented as additions, making both the computation easy and allowing a high dynamic range with a limited number of bits.

Going through the "exp" table is the last step of synthesis for each operator, converting the logarithmic value that can easily be multiplied into a linear value that can easily be mixed by adding the voices or used as linear phase delta for FM.

My suggestion for the log waveform was not entirely correct, I didn't think about the exp table, so talking about "value to the DAC" was nonsense, but the main idea is that you most easily implement a waveform like that within the OPL3 architecture by using the top bit of the accumulator as sign bit. If it is set, you flip all the other bits, and then just treat those bits as a logarithmic value. Putting the triangle-shaped input the exp table (later) will create the "logarithmic sawtooth".

superfury wrote on 2025-06-14, 16:06:

Edit: Or supplying a direct mantissa/exponent value set depending on the lookup entry perhaps?

No lookup. The phase angle directly is interpreted as mantissa/exponent!

Reply 6 of 38, by superfury

User metadata
Rank l33t++
Rank
l33t++

I did some experimenting. I simply adjusted the logsin lookup for that case with (according to the format of the floating point using mantissa/exponent).

Since the format of the mantissa/exponent/lookup lookup table is lower 8 bits, upper 3 bits, and a sign bit (implemented as bit 15, which is way out of range for ease of lookup).
Thus the lowest 8 bits, higher 3 bits and top bit(sign bit) are given to the exp lookup table.
Simply shift left the 8-bit index (range of 0-255) left by 3 to move the top 3 bits into the exponent input, remaining lower 5 bits into the mantissa input and the sign input used directly). In my emulator's case, the sign/mantissa/exponent lookup is done using a simple lookup table (64K entries). The bottom 8 bits are the mantissa, bits 8-10 are the exponent and bit 15 is the sign).

So the LogSin sine wave lookup function simply checks for the specific case of the derived square wave, then if that's detected perform:
- output from the table is instead the table location shifted left by 3 bits.
- If the lookup is supposed to be performed for the second/third quarters (it keeps a 2-bit value which is the PI portion of the wave being requested, thus it's a simple case of the two bits of that not being equal (thus #1 or #2 (the quarter being 0-based, so it's first quarter, second quarter, third quarter, fourth quarter when read in decimal, or 00b, 01b, 10b, 11b))).
The final part that's applied to the normal sign determination (using bit 1 of the quarter/half sine indicator mentioned above) is determined as usual.

I seem to get a normal inverted-ish sine wave-ish result now (or something that at least looks correct)? Only of course it's exponential-based instead, as the input to the exponential table is simply linear in nature (with the mantissa/exponent generating the curve directly). The lowest 3 bits of the 'angle' in the mantiassa part are cleared of course.

Below file is simply generated by performing the sinus lookup for 1 sinus per second, then passing it's output through the exponential table and converting the result to a valid 16-bit PCM format range for mono output.

The attachment adlibwave.7z is no longer available

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 7 of 38, by mkarcher

User metadata
Rank l33t
Rank
l33t
superfury wrote on 2025-06-14, 17:02:

Simply shift left the 8-bit index (range of 0-255) left by 3 to move the top 3 bits into the exponent input, remaining lower 5 bits into the mantissa input and the sign input used directly).

Yeah, that's what I meant to say.

superfury wrote on 2025-06-14, 17:02:

Below file is simply generated by performing the sinus lookup for 1 sinus per second, then passing it's output through the exponential table and converting the result to a valid 16-bit PCM format range for mono output.

The attachment adlibwave.7z is no longer available

Looks very similar to https://doomwiki.org/wiki/OPL_emulation , so I think you got it correct. Switching audacity to "dB" display (which generates a logarithmic scale) shows the expected sawtooth for wave shape number 8. On the other hand, I notice that you get no points more quiet than around -51dB, which indicates a resolution of just 9 bits + sign, i.e. 10 bits total. Is the exp table in the OPL2 that coarse, or did you hit some resolution limit in your emulator?

Reply 8 of 38, by superfury

User metadata
Rank l33t++
Rank
l33t++

Hmmm.. Assuming sign bit isn't included in the 'floating' point decimal numbers used, how many bits are remaining in the mantissa/exponent part that's the low bits? Or do I simply need to ignore those bits?
I mean the bits that get input into the Exponential table formula.

	//Reverse the range given! Input 0=Maximum volume, Input max=No output.
if (v > MaximumExponential) v = MaximumExponential; //Limit to the maximum value available!
v = MaximumExponential - v; //Reverse our range to get the correct value!
#ifdef IS_LONGDOUBLE
return sign * (DOUBLE)(OPL2_ExpTable[v & 0xFF] + 1024) * pow(2.0L, (DOUBLE)(v >> 8)); //Lookup normally with the specified sign, mantissa(8 bits translated to 10 bits) and exponent(3 bits taken from the high part of the input)!
#else
return sign * (DOUBLE)(OPL2_ExpTable[v & 0xFF] + 1024) * pow(2.0, (DOUBLE)(v >> 8)); //Lookup normally with the specified sign, mantissa(8 bits translated to 10 bits) and exponent(3 bits taken from the high part of the input)!

In this case that's the MaximumExponential variable.
The last builds have it set to "(0x3F<<5)+(0x1FF(being silence)<<3)+logsintable[0]".

It effectively determines the range of the ExpTable formula inputs (low 8 bits being mantissa and top bits being exponent).
As can be seen, it's used by my OPL2 implementation to invert the range to get maximum range of inputs.
Edit: Tried adjusting it to 1FFFh instead (8 bits mantissa, 5 bits exponent, being the "v" variable range), seeing as that's at least what the implementation needs (including volume envelopes etc)?
Even the lookup table itself far exceeds 11 bits itself? At least 13 bits including the volume envelope's silence value (1FFh)?

The new output with OPL3 defines enabled to enable the unfinished OPL3 emulation (forced on).

The attachment adlibwave.7z is no longer available

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 9 of 38, by mkarcher

User metadata
Rank l33t
Rank
l33t
superfury wrote on 2025-06-14, 21:44:
[…]
Show full quote
	//Reverse the range given! Input 0=Maximum volume, Input max=No output.
if (v > MaximumExponential) v = MaximumExponential; //Limit to the maximum value available!
v = MaximumExponential - v; //Reverse our range to get the correct value!
#ifdef IS_LONGDOUBLE
return sign * (DOUBLE)(OPL2_ExpTable[v & 0xFF] + 1024) * pow(2.0L, (DOUBLE)(v >> 8)); //Lookup normally with the specified sign, mantissa(8 bits translated to 10 bits) and exponent(3 bits taken from the high part of the input)!
#else
return sign * (DOUBLE)(OPL2_ExpTable[v & 0xFF] + 1024) * pow(2.0, (DOUBLE)(v >> 8)); //Lookup normally with the specified sign, mantissa(8 bits translated to 10 bits) and exponent(3 bits taken from the high part of the input)!

That code looks sensible. Looking at your wave file, I find that all the positive samples are 10 variable bits with five one bits concatenated to them (so the are xx1F, xx3F, xx5F, xx7F, xx9F, xxBF, xxDF or xxFF), and the negative samples are looking likewise. While the 8-bit logarithmic "mantissa" is supposed to be expanded to a 10-bit linear mantissa, having a sign bit, 10 variable bits and the remaining bits "padding" makes sense, but as the output of the logsin is between 0 and 2137, I would expect v>>8 to vary between 0 and 8. Assuming we process the values of the LogSin table directly, we would have MaximumExponental at 2137. The first three LogSin samples after negation are 0, 406 and 594. That is "exponent zero, mantissa zero", "exponent one, mantissa 150" and "exponent two, mantissa 82". Translating that using this lookup formula yields 1024, 2*(1024+513) = 3074 and 5116. The maximum value, 2137, is exponent 8, mantissa 89, which yields 333568. If you normalize 333568 to 32767, the lowest three values translate to 101, 302 and 503. Yet, your wave file has 95, 287 and 479, each of them being a multiple of 32 minus 1.

It seems somewhere in the processing chain used to generate the wave file you attached, some resolution got lost. It's likely not the OPL emulation code, though.

The log-sawtooth waveform seems to be impacted by this more than it should. Are you cutting the 2nd and 3rd quarter to plain zero? I'd expect that shifting the index just by 2 bits instead of 3 bits, and using a 9-bit index instead is more plausible. But if you want to be sure, you should check what NukedOPL does.

Reply 10 of 38, by superfury

User metadata
Rank l33t++
Rank
l33t++

For the 2nd and 3rd quarter of the log-sawtooth waveform, it should just return the first entry #0 (entry #0) of the LogSin table, thus giving silence (that's based on the detection of the first,second,third,fourth quarter of the waveform directly)? From what I see in Audacity, it looks like the 3rd quarter position has that correctly, but somehow the 2nd quarter of the wave doesn't somehow (slightly above 0 perhaps)?

The inputs to that logsin-to-exponentional function are simply the direct outputs of the sinus lookup. The normal rendering also adds other things, like volume envelope attenuation (calculated value (range of 0 through 1FFh)<<3), channel volume attenuation (setting<<5, but 0 when ignored for a channel) and ksl ROM also adds attenuation (based on Dosbox's code from what I remember, eventually shifted left by 3) as does tremolo (0 through 26).

I also improved my OPL2/dual OPL2 player (VGM/DRO files) to support OPL3. If it detects OPL3 being emulated with dual OPL2 files, it will simply enable OPL3 mode (register 5 bit 0 at base+3) manually and set the channel registers to left and right channels for dual OPL2 compatibility. It doesn't enforce any registers to be read-only on those bits though, so if a song currently overwrites them, it'll change the left/right panning of the instruments.
Edit: Somehow, after playing a file once, the OPL2 sounds get messed up somehow? I only adjusted the DRO player a bit to clear all registers properly before starting to play any DRO file (and set register 05h on the second chip to 00h for OPL2 compatiblity mode).
Edit: I think I managed to fix the issue. There was some initialization and post-playback clearing issue with OPL2/OPL3 registers and compatibility modes.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 11 of 38, by superfury

User metadata
Rank l33t++
Rank
l33t++

Hmmm... Seeing as almost all of OPL3 is implemented right now (except the 4-OP mode, which is just a wrapping of inputs to outputs and skipping of some channels for the 2nd set of 3 channels (channel 4-6 on a Dual OPL2 chip configuration perspective, for each of the two chips) when it's enabled for one of the first 3 channels (channel 0 skipping channel 3, channel 1 skipping channel 4, channel 2 skipping channel 5),

How is the 4-OP mode implemented? Does each set of 2 channels (0/3, 1 /4, 2/5) have it's own frequency setting for example? Of course disabling feedback on the higher channel from what I understand?
So can you configure the first 2 operators frequency independently from the latter 2 operators? Them just being linked by the modulation (FM or AM)?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 12 of 38, by superfury

User metadata
Rank l33t++
Rank
l33t++

Anyone knows how exactly 4-OP mode differs from 2-OP modes? Is it just the normal 2-OP modes bolted together in additive or FM synthesis mode? Do the first and second set of 2 operators still have their own multipliers, frequencies (for a set of 2 operators for each of the two parts of the 4-OP combination (so operator 1&2 and 3&4)) etc.? Or does the first or second overrule the other?

About the dB range, I looked at some other OPL2 synthesizers, but they seem to shift by 3 too (or multiply by 8 ).
Edit: Simply made it work like normal 2-OP channels. Just the links between the channels (FM performing phase modulation to next operators, AM steps effectively summing to output (additive synthesis)). Thus the four modes are:
FM-FM mode: FM,FM,FM,AM
AM-FM mode: AM,FM,FM,AM
FM-AM mode: FM,AM,FM,AM
AM-AM mode: AM,FM,AM,AM
That being implemented based on https://moddingwiki.shikadi.net/wiki/OPL_chip … ation_Synthesis

Now all that's left is to implement the note-on/note-off specifics of the mode, which seems to simply perform note on on both chips instead of just the first pair when a channel is turned on?

Although, what would happen if you turned on a 4-OP note (using the first chip), then turning off 4-OP mode, then write the first or second chip's note on/off? Does it simply map the first chip's note on to the second chip's (at base+3) note on, keeping them in sync? Or does it do something else internally?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 13 of 38, by superfury

User metadata
Rank l33t++
Rank
l33t++

OK. For now, I've done the following:
- Note on/off for the primary 4-OP channels (channels 0/1/2) handle the primary channels normally.
- Note on/off and 4-OP mode changes for a channel handle the second channel too. The operators affected are detected at the start of the note on/off function, where:
-- The 4-OP extended (upper) operators are notified of their old key-on state (for change detection at a later point in time, further down the function (it only keeps the lower operators original state)).
-- The 4-OP extended (upper) operators get their data source (for frequency number, block number sources and key-on (of modulator or carrier) state of their assigned channel) updated with either the 4-OP channel or their original channel (both precalculated, as this is fixed).
-- Flags are checked for 4-OP mode changes to force update fnum-related data and other upper channels properly.

Then, when processing the key-on and key-off, it will check (for the 4-OP upper channels) it's new source and update:
- Frequency number / octave / increment as it does usually, but depending on the new 4-OP or 2-OP source channel settings.
- Key-on and key-off are handled in the same way as normal key-on or key-off, but for an upper source instead (if the source gets changed, the (non) changing modulator/carrier key-on state of the new channel (or same channel if not changed) will trigger a key-on or key-off to match the new channel being selected.

So if you turn an OPL3 4-OP note on and then disable 4-OP mode, the then split 2-OP modulator/carrier note on bits will affect the modulator/carrier accordingly. Frequency/octave/increment(which is based on the frequency/block and operator frequency multiplier) are always reloaded in this case (as happens with any note on/off event).

Edit: Found a bug with feedback when enabled being forced, even though it shouldn't. Although that doesn't affect anything other than normal modulators, which have the feedback setting used anyways.
Does the OPL2 drum channels force feedback in some ways? Or is that a myth?

Edit: For now, implemented the basic (pretty much same) Sound Blaster Pro II DSP (which is effectively the same as the Pro I from what I can see, other than the version number reported and OPL3).
The sound card will of course enable the new OPL3 chip for emulation.

Although that doesn't mean that the new modes I implemented are working properly, they just work in theory so far. Until a bug is found (like in the new waveforms or 4-OP mode), at which point that'll need fixing.
The 0xE3 DSP command is implemented and recognised, but it will just give nothing as a result (according to Dosbox), thus acting as a NOP command of sorts.

The OPL3 chip is actually activated in it's full glory in this Sound Blaster version, so it can be tested.
All the backend if in theory implemented, just not sure yet if the 4-OP mode is properly working (and the final (8th) waveform is actually correct). Waveforms #4, #5 and #6 (#7 being the 8th waveform) should be correct, although maybe the 5th and 6th (#4/5) waveforms might be a bit squashed? That could be due to putting a 1024-entry waveform in half it's actual timespace, thus distorting it because you're fitting the 1024-entry full sinus waveform inside 512 entries (skipping every other entry). But that's probably also happening on a real OPL3 I'd think?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 14 of 38, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've also improved the VGM/DRO players to, when writing OPL3 registers (for initialization, termination and playback) to patch the OPL3-specific registers in (forcing no 4-OP mode, stereo channels fixed (left/right), adlib mode (if an adlib OPL2 single-channel song) and compatiblity mode enforced (for OPL2 single-chip songs only), and fixing the OPL3 mode emulating OPL2 to only use 4 waveworms (by masking the top bit off)).

OPL3 songs on the other hand get full access to all OPL3 registers without any patching (as they're made for OPL3 anyways).

If it's detecting a (dual) OPL2 chip, it will not patch any registers and block OPL3 songs from loading (as they can't be reliably played).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 15 of 38, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've just been thinking a bit.... What is the difference between FM-AM mode and 2-OP mode?

Edit: Found an issue in the 18-channel configuration of the OPL3: the maximum volume was applied for 9 channels, which should be 18 channels before the summing to the output of the chip itself (actually this is the DAC processing, which is directly after the exponential table output, but it's essentially the same behaviour).

Said DAC behaviour is simply to sum all values from the exponential tables of all channels and divide it evenly over the 16-bit left/right space.
In this case, both left and right channels each get 1/18th of the output to keep their volume equal.

One odd thing that I do notice is that 2-OP mode and 4-OP mode have vastly different ranges of outputs? While 2-OP mode in FM simply gets the direct exponential table inputs, in additive synthesis mode, it's theoretically doubled if two of the same signals are used?
And with the 4-OP modes, this gets even more ridiculous: AM-AM mode has 3 outputs, not 2 or 1, thus each channel component is even louder!?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 16 of 38, by superfury

User metadata
Rank l33t++
Rank
l33t++

How many bits should my exponent table input with non-sign integer be? This is important, because the exponential lookup itself reverses the range of the input (so 0 becomes maximum value and maximum value becomes 0).

The result of such an input is:
- LogSin lookup (or simply 0-1023 for the final wave).
- operator Volume SHL 5
- volume envelope (0 (full volume) to 1FF(silence))
- KSL adjustment (8 times KSLadd, based on Dosbox's code (see below))
- tremolo (0-26 range)

KSL adjustment adjusted from Dosbox:

void EnvelopeGenerator_setAttennuation(ADLIBOP *operator)
{
if( operator->m_ksl == 0 ) {
operator->m_kslAdd = 0;
return;
}

if (!operator->blockfnumkeyonchannel) return; //Invalid channel?
// 1.5 dB att. for base 2 of oct. 7
// created by: round(8*log2( 10^(dbMax[msb]/10) ))+8;
// verified from real chip ROM
static const int kslRom[16] = {
0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64
};
// 7 negated is, by one's complement, effectively -8. To compensate this,
// the ROM's values have an offset of 8.
int tmp = kslRom[operator->blockfnumkeyonchannel->m_fnum >> 6] + 8 * ( operator->blockfnumkeyonchannel->m_block - 8 );
if( tmp <= 0 ) {
operator->m_kslAdd = 0;
return;
}
operator->m_kslAdd = tmp;
switch( operator->m_ksl ) {
case 1:
// 3 db
operator->m_kslAdd <<= 1;
break;
case 2:
// no change, 1.5 dB
break;
case 3:
// 6 dB
operator->m_kslAdd <<= 2;
break;
default:
break;
}
}

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 17 of 38, by superfury

User metadata
Rank l33t++
Rank
l33t++

Took a look at OPL3emu:
https://github.com/datajake1999/OPL3EMU/blob/ … pl3lib/opl3.cpp

Looking at OPL3_EnvelopeCalcSin0, I see the whole handling my emulator does (from the logsin table output being reached at line 210) pretty much match my emulator's.
In my emulator's case, it's the result from calcOPL2Signal until OPL2_Exponential essentially doing the same as OPL3_EnvelopeCalcExp.

The only thing my emulator does after that is divide the output by 1024 to convert the range to output, as the exponential lookup is normalized in my emulator to provide for easier convertion. It's essentially done like this:
- Logsin table output
- Gain added.
- Exponential table used. The output is converted to -1024-1024 range (used for modulation).
- When rendering an output sample, it's converted to -1.0-1.0 range before summing the channels together.
That's at https://bitbucket.org/superfury/unipcemu/src/ … ib.c#lines-1181 in my source code.

After summing all channels together, SHRT_MAX divided by (3000 times 9 or 18) is used to convert the channels' summed value into 16-bit integer range for the rendered 16-bit sample.

Checked the exponential table lookup limits. After some checking, it seems to require 14 bits (for storing the maximum calculated value), excluding the sign bit?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 18 of 38, by superfury

User metadata
Rank l33t++
Rank
l33t++

I'm wondering a bit. How many channels can the final output of the channels hold? Right now I have downscaled the maximum output times 18 (or 9 in the case of the Adlib) to 16-bit format (so each channel gets 1/9th or 1/18th of that).
What is it like on a real chip?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 19 of 38, by superfury

User metadata
Rank l33t++
Rank
l33t++

Are the primary (lower) and secondary (upper) 4-OP channels handled using the primary chanel's frequency number and block number? From what I gather, the primary's block number, frequency number and key-on is effective only? So the secondary channels (OP3&4) only have multipliers to work with for their frequency? Or is it both (0 for never attack, 15 for 0 cycle attack)?

Last edited by superfury on 2025-07-03, 09:15. Edited 1 time in total.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io