It is currently Sun Oct 22, 2017 5:09 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 21 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: sound resample methods?
PostPosted: Tue Jan 07, 2014 1:52 pm 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 3064
Location: Brazil
Hi. In my emulator, the APU generates 1 sample at each odd CPU cycle. The resampling method I use, in short words, is calculated as the arithmetical average (sum all the samples, divided by the number of samples).

Is there another method, not so much complicated?
Thank you.


Top
 Profile  
 
PostPosted: Tue Jan 07, 2014 2:41 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 6294
Location: Seattle
Right now, what you're doing is using a "boxcar" filter as a lowpass. Its frequency rejection is rather lousy, but it is better than the only thing that's even simpler that what you're doing:

The only simpler thing I can think of is to calculate the current state of the APU for each output sample, but that has no lowpass properties at all and will cause audible aliasing.

There are techniques to generate exactly as many samples as needed at the sample rate, such as blargg's blip_buf, but I couldn't honestly call them "simpler", just "better for CPU load while still completely accurate"


Top
 Profile  
 
PostPosted: Tue Jan 07, 2014 4:22 pm 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
Yeah, averaging each source sample is about as simple as it gets, short of skipping source samples (which is pretty lousy quality). You might be able to specify a higher output rate, like 894886Hz, and let the OS/sound output deal with resampling. I've heard of people using a combination of skipping samples (to reduce it to say 160kHz) and then letting the OS do the rest. There might also be wrappers for whatever sound output you're using (Allegro?) that have a resampler built in.


Top
 Profile  
 
PostPosted: Tue Jan 07, 2014 5:43 pm 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 3064
Location: Brazil
Problem: the sound output volume is quite low. If I take the result and << 1, it becomes too high. So, I use a crap decay, volume -= volume >> 1, every time a sample (from resampling) is generated. It works, but it's still cracking depending of the number of active channels.


Top
 Profile  
 
PostPosted: Tue Jan 07, 2014 6:04 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 6294
Location: Seattle
Tentatively, I think you've got something else funny going on here. Each time you multiply by 2 you only increase the volume by 6dB, so if it clips when you <<1 and doesn't when you don't, you're already extremely close to the loudest your soundcard can get. Maybe data type issues?

The other thing you're doing now is called companding, and that basically can't be done in a way that guarantees it will never clip without either looking ahead in time (1/10th of a second or more) or accepting the volume being non-continuous.


Top
 Profile  
 
PostPosted: Tue Jan 07, 2014 6:48 pm 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
Unless there's a high-pass filter, the output will be positive only so will be another 6dB quieter than necessary, even when integer overflow starts to occur. A hacky fix is to subtract half the sample range (e.g. 32768 for a 16-bit sample) just before outputting to the OS, though then your OS's mixer will hate you because on quiet parts you'll be outputting -32768 continuously.


Top
 Profile  
 
PostPosted: Tue Jan 07, 2014 7:34 pm 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 3064
Location: Brazil
Attachment:
Image

Default output setting, without the volume -= volume >> 1 thing.


Top
 Profile  
 
PostPosted: Tue Jan 07, 2014 10:16 pm 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
So you have some kind of high-pass filter. That's good. Any reason you're using bit shifting for volume adjustments? Just multiply by say 192 then right shift by 7 and you have a 1.5 adjustment. Or *205>>7 for 1.6 adjustment. You have to do some kind of clamping, because that sounds much better than overflow which wraps around.
Code:
 // s = 16-bit sample, vol = 128 for 1.0, 192 for 1.5, etc.
int gain_clamp( int s, int vol )
{
    s = (s * vol) >> 7;
    if ( s < -32768 )
        s = -32768;
    if ( s > 32767 )
        s = 32767;
    return s;
}


Top
 Profile  
 
PostPosted: Wed Jan 08, 2014 9:28 am 
Offline
Formerly ~J-@D!~
User avatar

Joined: Sun Mar 12, 2006 12:36 am
Posts: 445
Location: Rive nord de Montréal
blargg wrote:
Code:
 // s = 16-bit sample, vol = 128 for 1.0, 192 for 1.5, etc.
int gain_clamp( int s, int vol )
{
    s = (s * vol) >> 7;
    if ( s < -32768 )
        s = -32768;
    if ( s > 32767 )
        s = 32767;
    return s;
}

Watch out when using this code, because it relies on implementation-defined behavior (the signed right shift when using negative integers). Almost all of the compilers I've used propagated the sign bit but if you want to be 100% portable then don't use this code.

Basically, what blargg suggests you is using fixed-point arithmetic. I don't know if there is significant speed gains on PCs...


Top
 Profile  
 
PostPosted: Wed Jan 08, 2014 9:51 am 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 3064
Location: Brazil
It would be possible for each sound channel, instead of applying it in the final mixed sample.

Code:
output = (value * 192) >> 7;
output_dac = (duty_sign)? output: -output;

value is the unsigned sample generated in a channel. Usually, it would be:
Code:
output_dac = (duty_sign)? : output: 0;


Last edited by Zepper on Wed Jan 08, 2014 12:23 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Wed Jan 08, 2014 10:50 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19115
Location: NE Indiana, USA (NTSC)
Jarhmander wrote:
Watch out when using this code, because it relies on implementation-defined behavior (the signed right shift when using negative integers).

It's fine to rely on implementation-defined behavior so long as you use a compile-time assertion. Undefined behavior is much trickier to test for, especially in compilers that optimize out your tests using the "as if" rule.

Quote:
Basically, what blargg suggests you is using fixed-point arithmetic. I don't know if there is significant speed gains on PCs...

Even if speed gains of fixed-point arithmetic are minimal, stability gains can be significant in some cases, as rounding introduces the same amount of noise throughout the output range.


Top
 Profile  
 
PostPosted: Wed Jan 08, 2014 11:02 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2963
Location: Tampere, Finland
Jarhmander wrote:
if you want to be 100% portable then don't use this code.

Or replace the shift with an actual divide by 128. The compiler will optimize it when it can.

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: kkfos.aspekt.fi


Top
 Profile  
 
PostPosted: Wed Jan 08, 2014 11:32 am 
Offline
User avatar

Joined: Sat Jan 22, 2005 8:51 am
Posts: 427
Location: Chicago, IL
Jarhmander wrote:
I don't know if there is significant speed gains on PCs...

I just put together a quick test to compare an int multiplication and shift vs. a conversion to a double, multiplication, and conversion back to an int. Test box is a Core i7-3770. Code was compiled without optimizations.

The int version is 2.1x faster than the x87 version but only 1.1x faster than the SSE2 version. YMMV.

_________________
get nemulator
http://nemulator.com


Top
 Profile  
 
PostPosted: Wed Jan 08, 2014 12:27 pm 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
Since the OP was stuck on not even using multiplies, just shifts, I assumed that there were non-technical reasons for avoiding more flexible approaches, and stuck to one that was only slightly more "daring".

On the topic of compiler optimization, since unfortunately C generally (and I believe always in C99) uses round-towards-zero for divide, rather than round-towards-negative-infinity (Euclidian division), a compiler can't simply optimize just a signed divide-by-2^n to a right shift; when the operand is negative, it must adjust the result as well.

tepples wrote:
It's fine to rely on implementation-defined behavior so long as you use a compile-time assertion.

And so long as most implementations have this behavior, otherwise you make your code break on things that don't, leaving the user to find an alternate implementation and be sure they catch every dependence on it.

Zepper wrote:
Code:
output = (value * 192) >> 7;
output_dac = (duty_sign)? -output: output;

That would work, and you could also do the upscaling from the NES 4-bit DAC to your 16-bit sample range. You wouldn't even need a right shift anymore, just a multiplier, e.g. multiplier=gain*0.1*32767/15 and then output = dac * multiplier.

The NES DACs are not signed, BTW. They actually do output_dac = duty_sign ? 0 : output. And it does make an audible difference; notes have less punch with signed.


Top
 Profile  
 
PostPosted: Wed Jan 08, 2014 12:58 pm 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 3064
Location: Brazil
blargg wrote:
The NES DACs are not signed, BTW. They actually do output_dac = duty_sign ? 0 : output. And it does make an audible difference; notes have less punch with signed.


From the wiki:
Code:
0 1 1 0 0 0 0 0 (25%)


I though it would generate a wave like _--_ _ _ _ _
Isn't that, but -_ _----- ???


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 21 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group