-
Notifications
You must be signed in to change notification settings - Fork 48
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
Output 32-bit float samples to preserve dynamic range and prevent clipping #59
base: master
Are you sure you want to change the base?
Conversation
…pping Added '-af' option to output 32-bit float samples in order to preserve dynamic range and prevent clipping at the same time.
src/mcu.cpp
Outdated
else if (sample_buffer_float) | ||
{ | ||
const float divRec = 1 / 32768.0f; | ||
sample[0] >>= 14; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be no need for bit shift if you divide for 2147483648.0
instead, and you could also keep more precision
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HI,
If you inspect sample[0] and sample[1] at that place you will notice that if you right shift with 13 you will get samples that has a granularity of 2 if you right shift with 12 you will get a granularity of 4 and so on. So currently with right shift 14 you get the maximum resolution. So you will not get more resolution with fewer right shifts.
Moreover the float resolution is not infinite. By dividing with 2^31 you will get less precise results than with exact right shifts and then dividing with 2^15 since 1 / 2^15 and its multiplicands can be exactly represented in 32-bit float.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense, thanks for the explanation!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi,
Actually you were right in the sense that the 2 shifts can be skipped and the result is the same. So no more precision, but somewhat better performance. You do not have to divide by 2147483648.0 but only 536870912.0.
So overall this produces the same numbers as the current code:
const float divRec = 1 / 536870912.0f;
sample_buffer[sample_write_ptr + 0] = sample[0] * divRec;
sample_buffer[sample_write_ptr + 1] = sample[1] * divRec;
sample_write_ptr = (sample_write_ptr + 2) % audio_buffer_size;
} | ||
|
||
if (audioFormat == AudioFormat::FLOAT32 && !isBufferSet) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this flag would be not needed if you move the pageSize,pageNum set to inside the else if (!strcmp(argv[i], "-af"))
if case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case the argument order would be mandatory otherwise at
(!strcmp(argv[i], "-af")) you cannot know if -ab is set before, not set at all or only will be set after -af.
The if (audioFormat == AudioFormat::FLOAT32 && !isBufferSet) check is added to make sure that only change the dafault if -ab is not set manually.
But correct me if I'm wrong, maybe I overlook something.
It significantly improves the consistency of audio output (less pauses, crackles) when the emulator is used together with other programs. Tested with Win10 x64, Win7 x64, WinXP x86.
Sample conversion implementation by @Falcosoft Upstream PR: nukeykt#59
Sample conversion implementation by @Falcosoft Upstream PR: nukeykt#59
Sample conversion implementation by @Falcosoft Upstream PR: nukeykt#59
Sample conversion implementation by @Falcosoft Upstream PR: nukeykt#59
Added '-af' option to output 32-bit float samples in order to preserve dynamic range and prevent clipping at the same time.