Bandlimited waveforms algorithm?

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

User avatar
Zepper
Formerly Fx3
Posts: 3192
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Bandlimited waveforms algorithm?

Post by Zepper » Sun Nov 28, 2004 7:00 pm

I was on hold... but i decided to ask anyways...
Yes, I know the Blargg's page, but not much, only graphics and basic information. FCEUltra and VirtuaNES have their own way to generate an outstanding pAPU output.

Is there any algorithm or a more detailed information available for bandlimited synthesis? ;) Thanks in advance.

Guest

Post by Guest » Mon Nov 29, 2004 3:57 am

A slow, but generally accurate, way would be to generate one sample per NES CPU cycle(effective rate would be about 1.78977272Mhz with NTSC), and resample it to 44.1KHz or whatever the output rate is.

If I were to reimplement the resampling code in FCE Ultra from scratch, I'd look into using an MMX or SSE based FIR filtering routine to resample the stream by an integral divisor(32 would be good) of the original rate, and then use libsamplerate http://www.mega-nerd.com/SRC/api.html to resample it to the output rate.

So, you would have:

1.789772 MHz MMX/SSE FIR filter-> 55,930.4 Hz libsamplerate-> 44,100Hz

Guest

Post by Guest » Mon Nov 29, 2004 3:59 am

Oh, I forgot one thing. I'd use gmeteor to create the filter coefficients. Be warned that it can run very slowly.

http://gmeteor.sourceforge.net/

User avatar
Zepper
Formerly Fx3
Posts: 3192
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Post by Zepper » Mon Nov 29, 2004 6:39 am

Thank you for the information... Quite interesting. :)
Last edited by Zepper on Sat Feb 27, 2010 6:57 pm, edited 1 time in total.

User avatar
Disch
Posts: 1849
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch » Mon Nov 29, 2004 11:52 am

The easiest way I've found to downsample is simple:

Accumulate the output of each channel every cycle and when you output a sample, divide that value by the number of cycles passed. It's not dreadfully slow, and produces a mighty-fine quality sound (although there are better methods)

For an example... every cycle the channel is clocked (this can be optimized by mutliplying Channel_Output by a value... I'll show sample code later):

OutputBuffer += Channel_Output;


When you output a sample:

SampleOut = OutputBuffer / CyclesPastSinceLastSample;
fTicksUntilNextSample += fTicksPerSample;

fTicksPerSample should be floating point and set to CPU_CLOCK_RATE / SAMPLE_RATE. On an NTSC system outputting at 44KHz, this will be roughly 40.58 (don't round too much). This means CyclesPastSinceLastSample will alternate between 40 and 41 as samples are generated.

This is the method I use in NotSo Fatso and I'd say it works quite well.

In case it's still unclear... here's some demo code to further clarify:

void DoSquareTicks(int ticks)
{
int mn;

while(ticks)
{
mn = min(nFreqCount, ticks);

ticks -= mn;
nFreqCount -= mn;

if(nDutyCount < nDutyCycle)
nSquareOutputBuffer += nVolume * mn; /*output volume if duty cycle is in positive section*/

if(nFreqCount <= 0)
{
nFreqCount = nFreqTimer.W + 1;
nDutyCount = (nDutyCount + 1) & 0x0F;
}
}
}


void RunAPU(int tick)
{
int mn;

while(tick)
{
mn = min( tick, ceil(fTicksUntilNextSample) );

fTicksUntilNextSample -= mn;
tick -= mn;

DoSquareTicks(mn);

nCyclesPassed += mn;

if(fTicksUntilNextSample <= 0)
{
fTicksUntilNextSample += fTicksPerSample;
OutputSample( nSquareOutputBuffer / nCyclesPassed );
nCyclesPassed = 0;
}
}
}



That code wont' work... but it'll give the general idea.

The method Blargg discusses in his doc (the one where you put transitions in a buffer, then run through the buffer later to generate samples) is likely faster and higher quality. It's kind of hard to understand though. I have yet to actually finish a player which impliments it.

User avatar
baisoku
Posts: 121
Joined: Thu Nov 11, 2004 5:30 am
Location: San Francisco, CA
Contact:

Post by baisoku » Mon Nov 29, 2004 1:28 pm

Disch wrote:The easiest way I've found to downsample is simple:

Accumulate the output of each channel every cycle and when you output a sample, divide that value by the number of cycles passed. It's not dreadfully slow, and produces a mighty-fine quality sound (although there are better methods)
[snip]
while the naive linear interpolation method you describe does produce generally acceptable results (it's what i used in my shitty nsf plugin way back in the day), it's important to note that it is exactly why shay presented his bandlimited technique - lerp creates aliasing, especially in the upper range of the nes frequencies.
...patience...

User avatar
laughy
Posts: 41
Joined: Wed Nov 17, 2004 12:34 pm
Contact:

:(

Post by laughy » Mon Nov 29, 2004 3:43 pm

My head hurts :(

I did it the "naive" way

Hey Disch, if you ever actually implement Blargg's buffering thing make sure you tell us if it was worth it :)

User avatar
Quietust
Posts: 1551
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Post by Quietust » Mon Nov 29, 2004 3:53 pm

My emulator also does the same 'naive' downsampling, mainly because a proper FIR filter would be trying to consume CPU time that simply isn't available.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

User avatar
Zepper
Formerly Fx3
Posts: 3192
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Post by Zepper » Mon Nov 29, 2004 4:50 pm

WOOHOO!!! :lol:
What a nice day... hehehe ;)
It worked nicely. Thanks for the help!

User avatar
baisoku
Posts: 121
Joined: Thu Nov 11, 2004 5:30 am
Location: San Francisco, CA
Contact:

Post by baisoku » Mon Nov 29, 2004 5:38 pm

Quietust wrote:My emulator also does the same 'naive' downsampling, mainly because a proper FIR filter would be trying to consume CPU time that simply isn't available.
as does mine. and i assume that's what most are doing. but that's why on all of them, you can hear the aliasing effects in the kid icarus intro and the solstice music. :)
...patience...

User avatar
Disch
Posts: 1849
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch » Mon Nov 29, 2004 9:05 pm

baisoku wrote:but that's why on all of them, you can hear the aliasing effects in the kid icarus intro and the solstice music. :)
The reasoning the aliasing appears for Kid Icarus in NotSo is because I emulate the non-linear interdependency square output as layed out in blargg's doc (and he tells me that he noticed the same aliasing appearing from the real thing.. though I haven't personally tested this). When I have both channels outputting independent, linear output... I can't notice any audible aliasing.

Perhaps I could record a quick wav or something for a demo and see if you guys notice any?

But yes... I've gotten Blargg's methods working and the results were definatly worth it. Less CPU intensive since you don't have to run each channel seperately for each sample (you can run them all at once for a big chunk the size of your output buffer). A crisper sound, and allows for more nifty sound tricks and easier filter application.

It's definatly worth it. It's just somewhat weird to understand. I didn't understand it fully myself until I exchanged several e-mails with Blargg.

User avatar
laughy
Posts: 41
Joined: Wed Nov 17, 2004 12:34 pm
Contact:

:)

Post by laughy » Tue Nov 30, 2004 12:10 pm

Would you mind posting those emails somewhere Disch for those who may run into the same problems you did?

User avatar
Zepper
Formerly Fx3
Posts: 3192
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Re: :)

Post by Zepper » Tue Nov 30, 2004 3:01 pm

laughy wrote:Would you mind posting those emails somewhere Disch for those who may run into the same problems you did?
Yay better, assemble a document. I'm sure it'll be much welcome.

User avatar
laughy
Posts: 41
Joined: Wed Nov 17, 2004 12:34 pm
Contact:

Re: :)

Post by laughy » Tue Nov 30, 2004 7:18 pm

Fx3 wrote:
laughy wrote:Would you mind posting those emails somewhere Disch for those who may run into the same problems you did?
Yay better, assemble a document. I'm sure it'll be much welcome.
Hey you did a good job on RockNes - however my facepic is still better.

TimW
Posts: 6
Joined: Mon Dec 20, 2004 4:51 am

Post by TimW » Mon Dec 20, 2004 5:11 am

sllightly better then linear interpolation with out the over head of fir would be a weighed guassian with it's center at the center of the sample period I can't remember the coefficents, but there are some with power of two coefficents which of course eliminates the divide, if anyone cares, I'll post em. example

Post Reply