Ultrasonic pulse behaviors for 2a0x and MMC5.

Discuss NSF files, FamiTracker, MML tools, or anything else related to NES music.

Moderator: Moderators

Rahsennor
Posts: 475
Joined: Thu Aug 20, 2015 3:09 am

Re: Ultrasonic pulse behaviors for 2a0x and MMC5.

Post by Rahsennor » Mon Jul 23, 2018 12:57 am

rainwarrior wrote:It's also oversampling for the output device.
No. Oversampling is when you sample a signal at a higher rate than the signal requires, not whatever your desired output is. To quote Wikipedia:
In signal processing, oversampling is the process of sampling a signal with a sampling frequency significantly higher than the Nyquist rate. [...] The Nyquist rate is defined as twice the highest frequency component in the signal.
The Nyquist rate of the NES's audio is also, surprise surprise, its sample rate. The fact that it contains frequencies all the way up to half that rate is exactly why a resampler is needed.

But you're right, arguing semantics isn't helpful. I just don't want people to hear "oversampled" and think it's either a) something unnecessary that the NES itself doesn't do, or b) some kind of enhancement that makes your implementation better than other emulators (or actual hardware) that don't "oversample".
rainwarrior wrote:Actually, to give you an idea why this affects performance so much for NSFPlay, how often it has to jump back and forth between CPU emulation and generating samples really adds a lot of overhead, and as a side effect negatively affects code caching / branch prediction / etc. at the same time. One of my planned ideas for performance improvement will be to institute the concept of CPU<->audio sync points so it can operate on longer buffers at a time, but that's going to be a big overhaul of the how original code worked. I suspect eventually, the idea of needing to undersample the NES might even be able to disappear, but for now it's deeply rooted in how it works. (If you look at other old NSF players, like NotSoFatso, this was pretty commonly done.)
Yep, repeat does that too. One cycle at a time. One word: ouch.

That's why I stopped working on it, in favour of a VGM player that uses logs and batching to run hundreds or thousands of cycles of each channel before moving onto the next. Once I get around to re-implementing the NES audio chips, I'll rip out my NES CPU emulator, make it spit out a write log, and let the audio emulation run in batches as large as it likes. But that's a ways off yet.
rainwarrior wrote:As for integer vs float... yeah I mean we have good vector hardware for floats these days. I'm not sure if NSFPlay will ever make it to float, but it's something that could be done in theory at least. If this were a starting-from-scratch situation it would be a lot easier to write something from the beginning that would be easier to switch to one or the other with a #define. (...maybe for NSFPlay 3.)
Would it be helpful to anyone if I put my resampler code up somewhere? It needs a good cleanup, but I feel obliged to put my money (or rather code) where my mouth is, so to speak.

tepples
Posts: 22054
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Ultrasonic pulse behaviors for 2a0x and MMC5.

Post by tepples » Mon Jul 23, 2018 7:56 am

Rahsennor wrote:Once I get around to re-implementing the NES audio chips, I'll rip out my NES CPU emulator, make it spit out a write log, and let the audio emulation run in batches as large as it likes.
That's the paradigm I've been recommending for quite a while: one process emulates the CPU, outputting a stream of writes to another process that emulates the APU signal generation. I concede that parts of the APU would need to be emulated on the CPU side of the pipe as well, such as length counters if the program reads $4015.

ap9
Posts: 39
Joined: Sat Jun 01, 2013 11:55 am
Location: Maine, U.S.A.
Contact:

Re: Ultrasonic pulse behaviors for 2a0x and MMC5.

Post by ap9 » Thu Aug 02, 2018 3:20 am

Accounting for all cycles and, when resampling, an accurate lowpass filter are needed for accurate results. Some box car interpolation can speed up the process, but the larger the ratio, the more inconsistent the attenuation. In my own tests, however, I've found that FIR alone can deliver messy results. So I've found a balance between the two.

Attached is 96 KHz, a box car of 4:1, followed by FIR (Blackman-Nuttall, 160 taps). The results are pretty clean, although the mixed channel portions differ from the hardware render (due to the nature of my MMC5 emulation).

Edit: corrected spelling of Nuttall. Also, the "messy results" I mentioned for FIR alone are probably because large fixed-point coefficient windows don't seem to respond well to raw rectangular pulses.

Update: it turns out the messy results I was getting was due to the limited precision used in interpolating the polyphase coefficients. The results are now cleaner. Now down to 139 taps using a Kaiser window.
Attachments
ultrasonic_tests-dc-kaiser9.7z
No DC filter; Kaiser, beta=9.3
(363.92 KiB) Downloaded 250 times
ultrasonic_tests-no dcf.7z
No DC filter; Blackman-Nuttall
(311.69 KiB) Downloaded 283 times
Last edited by ap9 on Thu Sep 13, 2018 4:52 am, edited 4 times in total.

ap9
Posts: 39
Joined: Sat Jun 01, 2013 11:55 am
Location: Maine, U.S.A.
Contact:

Re: Ultrasonic pulse behaviors for 2a0x and MMC5.

Post by ap9 » Sun Aug 19, 2018 12:13 pm

This is why you can't use boxcar alone (see attachment), as some emulators may do. While 2:1 is mathematically sound, no ratio provides 1:1 frequency response, and a pattern emerges the higher the ratio.
Attachments
box car frequency response.png

lidnariq
Posts: 9689
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: Ultrasonic pulse behaviors for 2a0x and MMC5.

Post by lidnariq » Sun Aug 19, 2018 1:36 pm

A boxcar filter in time is a sinc in frequency, and vice versa. (Your graphs are "just" of abs(sinc(x)) on a semilog plot)

The only robust way to use a boxcar filter is to use it as a close-but-not-quite 1st order lowpass.

ap9
Posts: 39
Joined: Sat Jun 01, 2013 11:55 am
Location: Maine, U.S.A.
Contact:

Re: Ultrasonic pulse behaviors for 2a0x and MMC5.

Post by ap9 » Mon Aug 20, 2018 1:38 am

Yes, boxcar is rectangular interpolation that it lines up with a rectangular sinc window. And likewise it has no side lobe suppression nor any flattening of the top in terms of frequency response. But it can make a good soft lowpass at 2:1 or an okay first order filter. Or in the case of use in my emulator, a reduction of computation time and memory requirements for storing samples (since the first stage uses the "WAVE[cycle>>boxcarshift]+=out" approach found in FCE Ultra).

Post Reply