A small problem: varying amplitudes

Now that I have a simple tune playing, I'm ready to move on to the next step - except there's a small issue. I noticed that in the simple scale it was playing, the volume was slightly lower on one of the last notes. To confirm my suspicion, I put some output statements to see what the maximum and minimum amplitudes were. Here's the output:

Sinewave amplitude range by note

Sawtooth amplitude range by note

Triangle amplitude range by note

Remember the range of a signed short is from 32767 to -32768. Only one of the sinewave notes reaches the expected amplitude, none of the sawtooth wave notes, and one of the triangle wave notes. I didn't include the square wave since it's always guaranteed to reach the specified amplitude due to the way that oscillator works.

Some of differences are pretty large. The B4 note in particular seems to be having issues, for the sawtooth and triangle waves it misses the max amplitude by over 1000 units.

So, why is this happening? My guess is that since these are periodic functions, the x value needed to reach the maximum amplitude of the period is often between integer values. Since our iteration is only by integers, the value it produces is usually not the peak value.

This normally would not be a problem if the maximum values were consistent between notes, but as you can see, they aren't. And this results in notes being at slightly different volumes from each other.

This is a drawback of doing this with a digital synthesizer - an analog synthesizer would probably not have this problem.

So, what's the fix for this? I can think of a couple possible ideas:

Solution 1: Adjust the x-values to get a proper curve

This would involve converting the x-value provided into a an x-value that will produce the proper expected y-value. So, if our period is 103, we would need to do some math to produce the "correct" range and then do our calculation based on that value. Essentially, we want to get a sin(2pi x) that results in a value of 1 for the max period, and a sin(2pi x) that results in a value of -1 for the min period. So, we would need to cast every x value to a new value to get a better curve.

Solution 2: Produce all the samples for a single period and then resample

Another option could be to have an oscillator produce a properly curved sample set independently of the specified frequency, and then apply a linear sample rate conversion function to get the frequency we actually want. I'm less enthusiastic about this idea, since an oscillator shouldn't need to resample. I'm going to need to learn to resample anyway if I'm going to be able to write a Protracker mod player, but I don't think this is the right application.

So, I'm going to go with solution 1. I'll post again if I'm successful.

Comments