lowpass with transition-based BL synth? (blargg?)

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
lowpass with transition-based BL synth? (blargg?)
by on (#6205)
Currently I have a big table with several 'sets', each consisting of 12 steps. Each of the 12 steps is output everytime audio output changes (transition). Those familiar with blargg's BL synth docs/methods might have the general idea of what I'm talking about.

I vaguely recall blargg saying something about a simple lowpass filter being easily implimented with such a setup -- as you wouldn't have to do any additional code during sample generation -- you could just modify your lookup table so that the filter takes effect for every transition before any of them are output.

However, I scanned blargg's site for info on the subject and didn't come up with a thing. I tried fiddling around on my own but my general lack of audio filter knowledge just led to distorted/ugly sound (I even seemed to decrease the signal:noise ratio rather than increase =P)

Anyway if anyone (*cough*blargg*cough*) can give me some info/advice on implimenting a lowpass filter with such a setup, I'd be very appreciative.

Thanks

by on (#6214)
To recap, as I understand it Disch is essentially generating a master square wave and then sampling one of its band-limited transitions at several offsets, then reusing these to generate band-limited transitions. To implement low-pass with this scheme you just reduce the amplitude of the higher sine wave harmonics to whatever profile you want as you add them when generating the master square wave. A simple algorithm is to scale each harmonic by an exponential "rolloff" factor, where the first harmonic is at normal amplitude, the second at 0.98 the usual, the third at 0.98*0.98, the fourth at 0.98*0.98*0.98, etc. This is in addition to the normal scaling of square wave harmonics, of course, where the first is at 1.0, the second at 1/3, the third at 1/5, fourth at 1/7, etc.

Code:
double period = 100;
double scale = 1.0;
double rolloff = 0.98;
for ( double harmonic = 1; harmonic <= period / 2; harmonic = harmonic + 2 )
{
    add_sine( period / harmonic, scale / harmonic );
    scale = scale * rolloff;
}


Here are a few square waves with progressively lower rolloff factors:

Image

If you still have trouble with this, I'll finish working on the demo code tutorial I've been wanting to complete for a while.

by on (#6216)
Excellent, that seems simple enough. So then the 'rolloff' factor would be the "strength" of the lowpass filter? The further down from 1.0 you get, the more the filter applies?

Up until now I've been using a pre-calculated const transition lookup table -- but now it looks like I'd be better off building one at runtime.

Many thanks, blargg -- I'll start implimenting this right away.

by on (#6217)
Yeah, the rolloff is as simple as that. It needs to be fairly close to 1.0 or else it will really roll off (since it's multiplied by itself many times). Building the square wave from individual harmonics makes this really simple to understand, almost like a graphic equalizer. I've tried other schemes where none of the lower harmonics are affected at all and found that the simple rolloff sounds best.

Seriously optimizing generation of the master transitions is quite complex and messy, so I definitely recommend sticking to building the square from sine waves. You can always make a sine lookup table that has the same number of entries as your base period is, times how ever many sub-sample offsets you're taking. All harmonics are at integer multiples of the base frequency, so you just step through this table at an increment of 1, then 3, then 5, then 7, etc. I'll probably go ahead and write the simple demo tomorrow, since I need a break from my emulator. :)

by on (#6238)
I finished the demo code. It shows how to generate the master square wave using a sine table (for speed) and then sample each band-limited step at each phase, then add them into a buffer. I tried to make it fairly readable, but didn't go to great lengths for that.

band_limited_step.zip

by on (#6251)
And here's the super-simple version (with added high-pass filtering) rewritten for clarity, at only 53 lines of code.

band_limited_step2.zip

by on (#6253)
Would it be possible to get some recorded examples with that method? I tried my own method but it didn't work out very well -- I was probably mucking something up.

by on (#6256)
If you want its output, just run the demo and examine the wave sound file. If you set the high-pass to 0.0, you can see the raw first difference of the sampled steps. I'm not sure what help this would be, since it'll look pretty much like the output with no high-pass filtering.

The main intermediate is the "master" band-limited step:

Image

This is just a square made from sines, with the angle from -pi/2 to pi/2 (one half of a full cycle). You'll sample it at the same interval as shown by the vertical lines, at several phases (shift the vertical lines to the right very slightly each time until you would get back to the original ones):

Image

Each time I've implemented this method I've had to write wave file of the intermediate waveforms in order to debug each one. It's tricky to get each one right.