tildearrow / furnace

a multi-system chiptune tracker compatible with DefleMask modules
GNU General Public License v2.0
2.24k stars 199 forks source link

Wavetable Synth has broken PM #481

Closed Toonlink8101 closed 2 years ago

Toonlink8101 commented 2 years ago

The Phase Modulation in the wavetable synthesizer produces different sounds on different sound chips. For example, the same settings on a Konami SCC sound much brighter than on a Namco WSG.

This is using the current dev build.

Here is an example module: wavetable synth broken.zip

tildearrow commented 2 years ago

This is maybe because of differing wavetable height....

Toonlink8101 commented 2 years ago

@tildearrow While differing wavetable height is worth investigating, it seems that the same issue can be found between the Konami SCC and the PC Engine. Afaik, these two have the same wavetable height.

To further specify the issue, it seems that the Konami SCC implementations differ from (most of) the other wavetable sound chips. I can confirm that behavior between the PCE, Gameboy, Konami Bubble, and Namco WSG are all identical. Konami SCC and SCC+ are the only ones that I can confirm are different.

Here's an updated module: wavetable synth broken 2.zip

Toonlink8101 commented 2 years ago

Just when through each wavetable chip. Here is a list for group of chips:

Harsh:

Soft:

freq-mod commented 2 years ago

Afaik, these two have the same wavetable height.

Then you know wrong - PCE has 5-bit waves, SCC 8-bit.

Toonlink8101 commented 2 years ago

You both are right. I admit my mistake: PCE is 32 by 32 and SCC/+ is 32 by 128. I think I'd already heard about that too. I must've confused the width and height somehow...

Something else I noticed is that the issue might go deeper than just the resulting sound. I think the synthesizer preview was also at fault, always showing the harsh waveform(s) instead of the softer ones. This could be a separate issue, but I thought I'd mention it here in case they are related.

In any case, I still need to verify that this related issue is present in the newest version.

Toonlink8101 commented 2 years ago

I feel the need to say this. While I am familiar with "classically supported" chips, being those found in Famitracker or Deflemask, I'm not nearly as familiar with the different limitations of "newly supported" chips, such as the Amiga, Seta, and SCC.

Please pardon my lack of experience when I speak about them.

Honestly, it is the inclusion of the wavetable synthesizer that brought me to these chips in the first place. Otherwise, I'd be more focused on the FM chips.

Anyway, here are some findings.

Using the most recent version, this one, I was able to determine that the resulting timbre of the Phase Modulation directly correlates to the wavetable height of the respective chip.

Keeping this in mind I can group the list by height to roughly indicate harshness:

Harshest (height of 256):

Partly Harsh (height of 128):

In the Middle (height of 64):

Partly Soft (height of 32):

Softest (height of 16):

Toonlink8101 commented 2 years ago

It seems that when determining the modulation amount, the end result is BSR (bit-shifted right) by eight, which divides by 256. This seems to be the correct behavior for SCC, but this division causes the "shorter" wavetable chips to have a fraction of the modulation intensity.

If the desire is for the wavetable synth to be consistent across each sound chip, this could be achieved by having the BSR be proportional to each chip's wavetable height. For example, the Gameboy would BSR by 4, the PC Engine would BSR by 5, and so on.

Effectively, instead of the BSR value being a constant of 8, it would use the following C++ expression: bsrValue = log2(wavetableHeight);

The only issue I can foresee is an error for wavetable heights that aren't a power of two. I can't think of an example of this, but if such a chip were to be added, then the code would need to be redone.

Hopefully, this is helpful in finding a solution.

Toonlink8101 commented 2 years ago

Just to be clear, this is the edit I'm suggesting.

I would take this line, and change it to this: int mod=(wave2[pos]*(state.param2-stage))>>(log2(height));

Or, for better readability: int bsrValue = log2(height); int mod=(wave2[pos]*(state.param2-stage))>>bsrValue;

Or, maybe even: int mod = wave2[pos]; mod *= state.param2 - stage; mod >>= log2(height);

In the end, though, this is just how I would go about things.

tildearrow commented 2 years ago

Issue fixed! Phase modulation is consistent across systems now.