-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
improve apu pulse channel nonlinear mixing precision #89
improve apu pulse channel nonlinear mixing precision #89
Conversation
The nonlinear mixing formula is not a precise formula, it's just the closest fit Blargg could find at the time to what they measured. I tried myself to measure several systems and improve it, but with the amount of variation other inherent noise, Blargg's seemed "good enough" and it felt difficult to pursue anything that could be considered definitively better. Anyway, the point is that increasing the precision of this computation does not increase the accuracy of emulation at all. The formula itself is just an approximation, and the precision is already well beyond the errors of approximation and other un-emulated factors. So... in theory, it is still okay to improve the precision of the code, though I feel it is probably not worth the effort required since it's not an emulation accuracy improvement (and all of this code will be obsoleted by NSFPlay 3). However, if you wish to proceed, I have questions about the code changes:
This third question is the one that requires the most effort to verify. |
Thank you for your reply and time. Sorry for not being explicit. This PR is a minor fix, the error fixed is not a big deal. Perhaps confusingly there are two types of mixing involving this PR: 1. the APU non-linear mixing and 2. stereo mixing, and both are done in the same My fix targets specifically a minor error in the default mono mix for the pulse channels stereo output (i.e. the mono mix actual hardware produces). Prior to the fix the default pan/volume mono setting produced slightly different output from the To correct this error I had to change To illustrate the prior error, here's a table of differences between old mono mix (column marked 'stereo' in the table) vs the old lookup (marked 'mono') -- i.e. the errors. Note, the table contains only the incorrect error cases, not the correct ones, is generated with the python snippet in my previous comment, and is therefore not a functional test of this pull request C++ code. Column marked test 2: old mono vs old stereo (diffs)
My fix corrects the above differences (error).
You are mistaken. See the line
Thanks, I'll certainly rather go with
The additional 4 bits precision and the |
Just to reiterate, this is not an emulation accuracy issue, it's just improving the precision of a formula that is already much more inaccurate than its precision. The difference will not be more accurate emulation, nor will it be audible. So, if you really want to proceed, I will continue, but to me it doesn't seem worth the time for either of us, doubly so because the code will be made obsolete by NSFPlay 3.
|
Yes, that's a reasonable perspective. As background, I'm emulating mixing implementations of various emulators (mesen, fceumm, nestopia) in my music creation tool. Being able to quickly switch between different emulator mixing helps in song mastering. This was motivated mainly by the vast differences in emulator expansion audio mixing. nsfplay is one emulator I looked at. I also use nsf2wav for functional testing. During my research I noticed the minor issue which this PR solves.
Yes. The lines were redundant.
I'll explain it in detail... The new First, assume computations don't reach 32 bits range so that accumulating carry from addition and subtraction don't need to be accounted. Hence only multiplications and divisions are relevant:
32 bits is not reached so all is good. And, as assumed, accounting for the accumulating carry was irrelevant.
Sorry but there is no off by one. The new I will explain using an example decimal value of 1.997. Decimal is more intuitive than binary, but the principle is the same. Think this example as if The lines |
a9f4c13
to
245f03a
Compare
BTW Considering performance, unsigned integers produce in some cases faster code than signed. So it might be beneficial in the future to change |
1 Thank you for the confirmation. 3 Thank you for showing the resulting range of output is not compromised. 4 I don't really understand your response here. What is it supposed to mean that integer and floating-point are in bold? I assure you that I would know their meaning when written plainly. I am glad that you want to help with this project, but everything you wrote here reads in a tone that is very strange to me, and I don't know what useful purpose it could have. I will return to this at the end. I'll try to explain my question again. You sent above a table with ~100 results in it. As far as I could tell, every result in this table "old mono" differs by one from "old stereo". However, as I am trying not to belabour, NSFPlay 2 is on life support, and I don't want to spend the time carefully looking at 100 rows which all might say the exact same thing, or even counting how many there are. My request was that you summarize what information that table was meant to convey so that I can more easily understand the difference you're trying to explain to me. Were there any rows in that table that were not "old mono" = "old stereo" + 1? That was my question. Rereading your original post, I see that I made a mistake in that I assumed the two columns were actually the result with the old table compared to the new table. I see now that you are comparing the existing mix code ("old stereo") with the square table itself ("old mono")? What I guess you were saying is the current stereo mix has lost half a bit of precision due to its truncation, which wasn't actually about the rounding of the table itself. It was unclear to me on first reading that this is what you were trying to express. It was unintuitive to me that you were comparing the existing mix to a hypothetical "old mono" mix, so I had just assumed it was a point about the new mix. My expectation was that you were showing me a table of values from the old mix versus your new mix. This is, after all, what I think is most important to evaluate. What difference does the change ultimately produce?
I don't think this would be reasonable. Even though the NES APU output is only positive, that is not at all true of most of the rest of the pipeline, all the way down to the end result which must be signed output, so this seems like quite a large change that would touch the entire rendering code across the whole program. Secondly, "some cases" is extremely specific, and switching them is not any kind of guaranteed speedup. Offhand, I think dealing with the needed bias for an unsigned audio pipeline would probably be a net slow-down here. The canonical speedup example that I know of regarding unsigned integers is in loop counters, and we don't do this with audio. Is there an example in this code which you'd like to justify this with a generated assembly example? If you've found something specific and with practical performance benefit, I'd be happy to look at it and learn from it, but I don't see how this suggestion could be considered from a rule-of-thumb perspective. summaryYou've demonstrated that your changes are sound. I would be happy to accept with PR, though there are two more things I'd like to see.
As a secondary matter... I really am very happy that you want to contribute to NSFPlay, and I don't wish to alienate you. I'd rather you stick around if you're interested in helping. This is why I did not simply close the pull request after explaining that this code is in end-of-life maintenance, and that increasing precision to this formula does not reflect emulation accuracy. My questions were an attempt to relieve my burden of work in accepting this PR. I need to know that the issues I would consider were considered (e.g. calculation overhead), I need to verify that there is a reason for changes that were made without comment about them, I need to know that you've built and tested this code and listened to the result. In this respect, I have failed at relieving this burden, but open source software is as much a social problem as it is a technical one, and if you're willing to do the work I want to honour that by scrutinizing it and then accepting it. Finally, please don't condescend to me like you just did. That response really crossed a line, and the burden of trying to find an appropriate response is a lot more time wasting than I would like. I don't know too much about you, as your github profile is fairly private, but if you'd like some context my profile has links to a lot of information about my career. I don't need an explanation of what a floating point is, and I don't know what led you down the path of thinking that was the root nature of my question, but if you find yourself on this point again in the future, please consider whether there's a more polite way to verify whether the issue is understanding of a basic common concept like this. |
No.
Yes.
Yes, some precision is lost in the rounding but the main thing is the added 4 bits in
The error was around 1/8192 measured from the output of the
I don't have. Not probably reasonable for just NES audio, although for some more complex audio emulation it may be worth it.
Integer literal to floating-point literal cast is implicit (assuming C++17).
My change is trivial and I find this unacceptable. I won't do it. Sorry.
Thanks. You're welcome. If the code is close end of life, then it shouldn't be that much of relevance. I've given enough proof in my opinion. The changes are trivial and can be verified by reading.
Sorry to rub you in the wrong way. It wasn't my intention. PS. Have you considered functional tests? Perhaps have a bunch of reference .flac audio files to test against in Git LFS. They'd be generated by previous validated build output. PRs would be then tested by GitHub actions against these to catch differences of output. I've got some python code using two techniques to test match audio files. For some tests, like waveform generation, one would disable some parts of the pipeline, ex. lowpass filter or stereo mixing. Anyway, potential issue is that audio comparison is a bit computationally expensive, so it's possible free GitHub hosts might take a long time to run the tests. But I don't know. |
So, wait, are you saying the result of the whole change never makes more than a difference of +1 in any of the outputs?
NSFPlay 2 is not C++17, it is currently using MSVC v141's dialect of C++, though the majority of the code is more than 20 years old. However the suggestion was my personal preference for the explicit version, not a lack of understanding of C++ promotion. Honestly I do not care much between the 3, just it should not be 2 things multiplied together.
Well, the question I'm asking is what is the impact of this change on the output. This is something you haven't given a clear answer for. Your change adds 4 extra bits of precision on square_table, but what you said above suggests there was only a gain of 0.5 bits of precision in the output? If you want to make a functional change to end-of-line code that has been stable for 13 years, I want the functional impact to be measured. You already wanted to make a table to show me some impact of the code in your OP, I assumed it would not be very much trouble to modify your python script to show what I was suggesting instead, so I'm surprised at the refusal, but okay, this is not the only way. I would just like you to answer the question as best you can in whatever method you think is reasonable.
If your answer is "verify it yourself", then I'm sorry, and I wish this had not persisted past my first response explaining that I believe this change is not worth the risk or time investment. Obviously it is much more trivial to you than it is to me, but if it's not worth your time either, then please let's not proceed any further with this.
For NSFPlay 3 I do want to do some sort of unit testing, but I won't work on deciding what's a worthwhile test until I get much further along on it. In general I probably don't want to do much audio verification like that, but choosing tests is always a difficult matter of finding a tradeoff between the cost of maintaining the test, and the cost of the expected code errors it should check against. |
I apologize for allowing it to go this far, but since the code change does not produce a significant functional difference in the output, it should remain as-is. |
Please don't close this PR. Please reconsider.
The change in this PR is valid and functional. The change is simple enough to be verified by reading, and for additional demonstration and proof, I provided you explanations, data and a python snippet in the PR message. So there is some burden in proof I've taken, but that doesn't seem to be enough...? How much do you need? I additionally did the changes you requested. I did what you wanted. Although the improvement from this change is minor, it's still an improvement. PS. Now you're aware of this issue, so naturally |
Again, I'm sorry. I misunderstood the nature of this PR from the start. I should have realized right away and rejected it, and I apologize for letting it carry on like this. To clarify, the closing is ultimately not about work you did or did not do. The code itself looks OK, but once I understood that the magnitude of change to the output, I realized it is not a PR I can accept. I can accept PRs that improve the function of the program for a user, but I categorically don't wish to take PRs that only cosmetically alter the code. This code is more than 20 years old, and it is in its end-of-life phase. There are an endless number of cosmetic issues with the code, and they are simply not worth the cost to address. It is more important to have the code remain stable and the way it is, than introduce a lateral change to its history. |
I see... I respect your conclusion here but I don't like how my effort was wasted. I have two suggestions to avoid this mistake happening again. Make the above information official and explicit, that you're not looking for active involvement in the old I have two suggestions to present this information based from my point of view (I may not fully understand, but I hope this is helpful):
I think the Archive option is better if you want to completely quit maintaining the old |
This is not an archive. I did just accept a PR from you. I am just not willing to accept PRs of this one's kind, and allowing it to proceed was my mistake, for which I apologize. I misunderstood the information you presented. There are no planned future releases for NSFPlay 2, but if there was some unexpected fix or functionality upgrade that warranted one I would do another release. |
Apology accepted and thanks for your time. |
Minor improvements to pulse channels non-linear mixing precision by increasing decimal bits in the lookup. Stereo mix on default (mono) setting now produces same output as the lookup. This fixes a 1/8192 error on output on some pulse 1 and 2 level combinations and removes some redundant left bit shifting.
Here is a python script used for testing: