NSFPlay 2.4

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
NSFPlay 2.4
by on (#236794)
NSFPlay 2.4 is released: https://github.com/bbbradsmith/nsfplay/releases

This one took me a while, but I think I'm back on track now. ;)

For anyone knew to it, it's an NSF player and Winamp plugin for Windows. (Hope to expand to other platforms when I get to version 3.) Next up for 2.5 is a rewrite of 5B and VRC7 emulation.

Code:
NSFPlay 2.4 - 3/30/2019
- Fixed incorrect bytes per second in stereo WAV output.
- Fixed NSFe track title display when using playlist.
- Fixed crash issue with 1MB NSF files. (Bank counting was incorrect.)
- Default NTSC speed changed from 16640 to 16639. (Slightly more accurate to hardware.)
- More bits of precision on fade-out, creates smoother fade.
- 48000Hz is the new default samplerate.
- Fixed keyboard view wave string bias on N163/FDS.
- Fixed frequency rounding for triangle/noise/DMC in keyboard display.
- Fixed intermittent crash with Winamp starting up with NSF in playlist.
- Fixed FDS mod table bit-mask and wavetable read address. (Minor.)
- Fixed broken playback rate for rates < 28 Hz.
- NSFe RATE chunk implemented.
- NSFe regn chunk implemented, including full Dendy support.
- NSFe mixe chunk implemented.
- NSFe taut, psfx chunk implemented.
- Drag and drop will now acknowledge a failed load with a pop-up alert.
- Migrate to VS2017.
- IRQ support.
- Play now runs at next opportunity rather than always at the start of frame, similar to poll based players like PowerPak.
- Other expansions with FDS automatically disables FDS RAM writes, and FDS banking for $6000-7FFF.
- Full NSF2 implementation. (non-returning INIT, suppress play, IRQ, NSFe metadata.)
- Removed non-functional filters, compressors, etc. from the audio chain.
- Removed per-channel quality settings, created a single master quality setting.
- DPCM byte read now correctly takes 2 cycles.
- Improved NES CPU vs APU/audio synchronization (controlled by quality setting).
- Better error messages for files that can't be loaded.
- Fixed broken seek, and restored the "fask seek" option. (Seeks as if quality=1, normally OK.)
- If in_yansf.ini can't be found or created next to in_yansf.dll, will try to save settings to %AppData%\NSFPlay\ instead.
- N163 compatbility options for phase write protect and limited wavelength. (Supports old NSFs that are not hardware accurate.)
- Option to randomize starting triangle phase.
- NSFe VRC7 chunk implemented, provisional support for YM2413 variant.
- VRC7 patch set dump by Nuke.YKT.
Re: NSFPlay 2.4
by on (#236804)
Two questions:

1. plugins\in_yansf.ini is no longer included with the releases. Is this an effect of change "If in_yansf.ini can't be found or created next to in_yansf.dll, will try to save settings to %AppData%\NSFPlay\ instead"?

2. Binaries across the board are now substantially larger; was this 2.4 release a "optimised release" build, or is this an effect of change "Migrate to VS2017"?

Code:
-rwxr--r--     232448 Jul 19  2013 nsfplay23/nsfplay.exe
-rwxr--r--         52 Jul  7  2013 nsfplay23/nsfplay.ini
-rwxr--r--      13591 Oct  5  2014 nsfplay23/nsfplay.txt
-rwxr--r--       5520 Jul 22  2013 nsfplay23/plugins/in_yansf.ini
-rwxr--r--     308224 Jul 19  2013 nsfplay23/plugins/in_yansf.dll
-rwxr--r--     581632 Jul 19  2013 nsfplay23/plugins/nsfplug_ui.dll
-rwxr--r--      14057 Oct  5  2014 nsfplay23/nsfplay_Deutsch.txt

-rwxr--r--    1834496 Mar 30 19:05 nsfplay24/nsfplay.exe
-rwxr--r--         52 Mar 30 19:07 nsfplay24/nsfplay.ini
-rwxr--r--      15896 Mar 30 18:56 nsfplay24/nsfplay.txt
-rwxr--r--     592384 Mar 30 19:04 nsfplay24/plugins/in_yansf.dll
-rwxr--r--    2239488 Mar 30 19:05 nsfplay24/plugins/nsfplug_ui.dll
Re: NSFPlay 2.4
by on (#236805)
1. in_yansf.ini shouldn't ever have been included, really. It was just full of the default settings (manually updated, potentially out of date if forgotten). Much better to leave it out, and the program will just create it on first use.

Adding the fallback to appdata wasn't the reason for deleting it, but that fallback does require that you don't stick a non-writable in_yansf.ini in your winamp plugins folder (which would block it from trying the fallback).

2. Yes, the binary size just instantly increased with the switch to VS2017. It's not really affected by the optimization settings as far as I found, but I decided the extra 1MB download wasn't worth going on a many hours adventure hunt to figure out. I looked at it for about as long as I thought it was worth looking at, then gave up.

I also thought it was worth staying with VS2017 and not going back to VS2005, for a bunch of reasons.

So... if you have an answer feel free to submit a compiler settings patch, and it'll go into 2.5. Otherwise, I don't care enough. Any work toward fixing this problem (if fixable at all) will be wasted when I rewrite for version 3 anyway.
Re: NSFPlay 2.4
by on (#236875)
For people who use NSFPlay Synthesia, HertzDevil has been keeping their repository up to date to at least 2.4b8: https://github.com/HertzDevil/nsfplay/releases
Re: NSFPlay 2.4
by on (#236893)
I have a feeling the increased executable size might just be due to VS2017's MFC libraries being bigger? I'm not certain about this. If that's the case though, definitely not going to do anything about it... it'll just be bigger until 3.0 when I throw away MFC altogether (and probably use some even more bloated UI library, hue hue hue...)

Unrelated, I did find a dead code elimination switch that wasn't enabled on in_yansf.dll that got it back to 333k. It hadn't bloated nearly as much as the others though, and it's also the only executable of the four not using MFC.

On a separate note, I set up appveyor. (Tried travis.ci first but got hella confused hella fast.) So, outside of the times I actually want to beta test, anyone can still get unstable builds from there now:
https://ci.appveyor.com/project/bbbradsmith/nsfplay/branch/master/artifacts

Or I guess emuCR has been doing this unofficially for years. :P
Re: NSFPlay 2.4
by on (#237109)
I am having a problem in NSFPlug 2.4 with the N163 volumes, especially together with the mixe chunk.

Please find here a test NSF and recordings (32 MiB because of flac recordings). The test NSF is an NSF version of the program that I used, with the hot-plugging technique, to measure the relative N163 volume of all my N163 cartridges. It first plays an APU square, then an N163 square using one channel, then an N163 square using eight channels, in all volume levels. I tested NSFPlay 2.4 both with default settings and using my custom settings, which involve not using non-linear mixing but without me messing with the volume sliders; the custom settings' .INI file are included in the archive.

First of all, the one-channel N163 square should have the same RMS volume as the eight-channel N163 square, as heard in the hardware recording from Megami Tensei II. In NSFPlay at both default and custom settings, the eight-channel N163 square is slightly louder than the one-channel N163 square (2.2 dB difference). This means that default settings, only the one-channel N163 square is +13 dB louder than the APU square, as the mixe chunk specifies; the eight-channel one is too loud relative to the APU square.

With custom settings, the eight-channel N163 is still 2.2 dB louder than the one-channel one, but the one-channel N163 is 15.1 dB louder than the APU square instead of the desired 13 dB. I assume this is the result of not using nonlinear mixing.

I believe that the mixe setting should apply similarly regardless of how many N163 channels are used, and whether the APU uses linear or non-linear mixing. Please tell me if you believe that NSFPlay does everything right and that only my settings are screwy, or my assumptions are wrong.
Re: NSFPlay 2.4
by on (#237121)
Okay, so in your SMT recording, there seems to be a ~0.27 dB difference between the 8 and 1 channel tests. On NSFPlay with the more accurate serial mutiplex mixing, it's showing ~0.1. With the default less accuract mixing it's 2.21.

With your custom settings, the 1 channel version has a difference of ~15dB instead, but this is due to turning off the more accuracte nonlinear mix of the APU square. That option is not the default, and I had not been testing it for reference to NSFe mixe. (I should have, sorry.) I don't remember what the correlation between APU nonlinear and linear mixes is, offhand, but probably APU volume 15 should match between the two and it should be linear from 0 to that endpoint... maybe it was using 2 x square 15 as the endpoint instead? Will take a look at it later, but using this option does unfortunately invalidate the reference mixes (until I revise that). Better modelling of the nonlinear mix of the APU is on my to-do list for 2.5 anyway, so it's an appropriate time to review this anyway.

The NSFe mixe chunk seems to be doing the correct thing, though. You specified 13dB and it delivers that for the reference test (1 channel vs square). So that part is fine.


Back to the N163 issue, for this particular test there is some very audible distortion with the 8-channel tone, and looking at the shape of the wave there seems to be a difference between this and how it's emulated in NSFPlay, so I think from the start the comparison is going to be thrown off by this. Despite this difference, the more accurate serial mutiplex mixing seems to be reasonably close (0.15 dB?) to your recorded version, so I feel I should consider it valid for the time being, at least until the emulation difference is resolved and I can make a finer comparison.

The less accurate (but default) non-multiplexed mixing does seem to be a problem here. 2 dB is significant. I don't know if it's overemphasizing that emulation difference, but yes there's a clear disparity that I will look into. It's a little bit difficult to make up a non-accurate form of mixing like this but also keep its relationship to the more accurate version simple and transparent.


Could you share source code for your NSF? I'd like to see how you're setting the phase of the 8 tones. It might be possible to create a more "fair" test that doesn't have as much distortion by writing 0 to all of their phases quickly, or doing something else to synchronize them more closely?

There's a bunch of very fine/nitty details about the N163 internal update that haven't been documented yet, e.g. what the registers read at various cycles in the sequence. I know there's at least some difference between this and NSFPlay's own internal update, but I haven't gone diving quite that deep into figuring out these timings (this level of detail is approaching a need for a decap IMO, though there is definitely more I can determine through hotswap tests still).
Re: NSFPlay 2.4
by on (#237123)
rainwarrior wrote:
Could you share source code for your NSF
Sure. I guess in the meantime, I will just use serial multiplex mixing and the nonlinear APU mixer.
Attachment:
N163NSF.ASM [5.3 KiB]
Downloaded 164 times
Re: NSFPlay 2.4
by on (#237829)
rainwarrior wrote:
The less accurate (but default) non-multiplexed mixing does seem to be a problem here. 2 dB is significant. I don't know if it's overemphasizing that emulation difference, but yes there's a clear disparity that I will look into. It's a little bit difficult to make up a non-accurate form of mixing like this but also keep its relationship to the more accurate version simple and transparent.
The reason I don't like the nonlinear mixer is that in addition to modifying the volumes, it also distorts the waveforms, which results in a somewhat dirty sound with very loud games, and also some aliasing on games with very high notes, such as Deep Dungeon 1. This aliasing sounds somewhat similar to what one gets without proper band-limited synthesis of the square waves, but in this case is caused by the non-linear mixer and is heard on the real hardware as well.

I have found that I can get what I want --- clean waveforms but correct volumes --- by doing the proper nonlinear mixing on Triangle/Noise/DPCM, but doing a linear mix on the two squares and scaling by the combined volume setting rather than the instantaneous output:
Code:
const static double squareSumFactor[32] ={ 1.000000, 1.352455, 1.336216, 1.320361, 1.304878, 1.289755, 1.274978, 1.260535, 1.246416, 1.232610, 1.219107, 1.205896, 1.192968, 1.180314, 1.167927, 1.155796, 1.143915, 1.132276, 1.120871, 1.109693, 1.098737, 1.087994, 1.077460, 1.067127, 1.056991, 1.047046, 1.037286, 1.027706, 1.018302, 1.009068, 1.000000 };
      if (Settings::NonlinearMixing) {
         Pulse =(square0.Pos |square1.Pos)? 95.88 /( (8128.0 /((square0.Pos + square1.Pos))) +100.0): 0.0;
         TND   =(triangle.Pos  |noise.Pos  |dpcm->Pos)? 159.79 /(1.0/(triangle.Pos  /8227.0 +noise.Pos /12241.0 +dpcm->Pos/22638.0) +100.0): 0.0;
      } else {
         Pulse =(square0.Pos +square1.Pos)/30.0 *squareSumFactor[square0.Vol +square1.Vol] *0.258483;
         TND   =(triangle.Pos  |noise.Pos  |dpcm->Pos)? 159.79 /(1.0/(triangle.Pos  /8227.0 +noise.Pos /12241.0 +dpcm->Pos/22638.0) +100.0): 0.0;
      }
      Output =Pulse +TND;

With "Pos" being the current waveform position, and Square0.Vol" being "Vol =envelope? volume: Envelope;". That gets me the best of both worlds: clean, distortion-free square waves, but still at correct volumes, correct enough for the "square.nes" part of blargg's apu_mixer.

The squareSumFactor array was obtained via:
Code:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main (int arc, char **argv) {
   float factorArray[32];
   
   for (int i =0; i <16; i++) {
      for (int j =0; j <16; j++) {
         if (j) printf(", ");
         float pulse =(i |j)? 95.88 /( (8128.0 /((i + j))) +100.0): 0.0;
         float normalized = pulse/0.258483;
         float linear =(i+j)/(15.0+15.0);
         float factor =i|j? normalized /linear: 1.0;
         factorArray[i+j] =factor;
      }
   }
   
   printf("const static double squareSumFactor[32] ={");
   for (int i =0; i <31; i++) printf(" %f%s", factorArray[i], i ==30? "": ",");
   printf("};");
   
   return EXIT_SUCCESS;
}
With 0.258483 just being a normalization factor, so that the last entry is at 1.0000.
Re: NSFPlay 2.4
by on (#237838)
NewRisingSun wrote:
and also some aliasing on games with very high notes, such as Deep Dungeon 1.
Here is an example of what I mean, from the intro of Majou Densetsu II: Daimashikyou Galious. The first time rendered with the above code but with Settings::NonlinearMixing=false, the second time with Settings::NonlinearMixing=true, which matches the output of real hardware and nonlinear-mixing emulators. You might need headphones to hear the difference.