Merge pull request #773 from diizy/master

NES: fix dc offset and distortion
This commit is contained in:
Vesa V
2014-05-26 18:04:13 +03:00
2 changed files with 54 additions and 44 deletions

View File

@@ -84,9 +84,6 @@ NesObject::NesObject( NesInstrument * nes, const sample_rate_t samplerate, NoteP
m_12Last = 0.0f;
m_34Last = 0.0f;
m_itm = 0.0f;
m_otm = 0.0f;
m_lastNoteFreq = 0.0f;
m_maxWlen = wavelength( MIN_FREQ );
@@ -143,11 +140,18 @@ void NesObject::renderOutput( sampleFrame * buf, fpp_t frames )
int ch4EnvLen = wavelength( floorf( 240.0 / ( m_parent->m_ch4EnvLen.value() + 1 ) ) );
bool ch4EnvLoop = m_parent->m_ch4EnvLooped.value();
// processing variables for operators
int ch1;
int ch2;
int ch3;
int ch4;
// levels for generators (used for dc offset compensation)
int ch1Level;
int ch2Level;
int ch3Level;
int ch4Level;
int ch1SweepRate = wavelength( floorf( 120.0 / ( m_parent->m_ch1SweepRate.value() + 1 ) ) );
int ch2SweepRate = wavelength( floorf( 120.0 / ( m_parent->m_ch2SweepRate.value() + 1 ) ) );
@@ -187,13 +191,14 @@ void NesObject::renderOutput( sampleFrame * buf, fpp_t frames )
// render pulse wave
if( m_wlen1 <= m_maxWlen && m_wlen1 >= MIN_WLEN && ch1Enabled )
{
ch1Level = m_parent->m_ch1EnvEnabled.value()
? static_cast<int>( ( m_parent->m_ch1Volume.value() * m_ch1EnvValue ) / 15.0 )
: static_cast<int>( m_parent->m_ch1Volume.value() );
ch1 = m_ch1Counter > m_wlen1 * ch1DutyCycle
? 0
: m_parent->m_ch1EnvEnabled.value()
? static_cast<int>( ( m_parent->m_ch1Volume.value() * m_ch1EnvValue ) / 15.0 )
: static_cast<int>( m_parent->m_ch1Volume.value() );
: ch1Level;
}
else ch1 = 0;
else ch1 = ch1Level = 0;
// update sweep
m_ch1SweepCounter++;
@@ -240,13 +245,14 @@ void NesObject::renderOutput( sampleFrame * buf, fpp_t frames )
// render pulse wave
if( m_wlen2 <= m_maxWlen && m_wlen2 >= MIN_WLEN && ch2Enabled )
{
ch2Level = m_parent->m_ch2EnvEnabled.value()
? static_cast<int>( ( m_parent->m_ch2Volume.value() * m_ch2EnvValue ) / 15.0 )
: static_cast<int>( m_parent->m_ch2Volume.value() );
ch2 = m_ch2Counter > m_wlen2 * ch2DutyCycle
? 0
: m_parent->m_ch2EnvEnabled.value()
? static_cast<int>( ( m_parent->m_ch2Volume.value() * m_ch2EnvValue ) / 15.0 )
: static_cast<int>( m_parent->m_ch2Volume.value() );
: ch2Level;
}
else ch2 = 0;
else ch2 = ch2Level = 0;
// update sweep
m_ch2SweepCounter++;
@@ -297,10 +303,11 @@ void NesObject::renderOutput( sampleFrame * buf, fpp_t frames )
// render triangle wave
if( m_wlen3 <= m_maxWlen && ch3Enabled )
{
ch3Level = static_cast<int>( m_parent->m_ch3Volume.value() );
ch3 = TRIANGLE_WAVETABLE[ ( m_ch3Counter * 32 ) / m_wlen3 ];
ch3 = ( ch3 * static_cast<int>( m_parent->m_ch3Volume.value() ) ) / 15;
ch3 = ( ch3 * ch3Level ) / 15;
}
else ch3 = 0;
else ch3 = ch3Level = 0;
m_ch3Counter++;
@@ -314,13 +321,14 @@ void NesObject::renderOutput( sampleFrame * buf, fpp_t frames )
// render pseudo noise
if( ch4Enabled )
{
ch4Level = m_parent->m_ch4EnvEnabled.value()
? ( static_cast<int>( m_parent->m_ch4Volume.value() ) * m_ch4EnvValue ) / 15
: static_cast<int>( m_parent->m_ch4Volume.value() );
ch4 = LFSR()
? ( m_parent->m_ch4EnvEnabled.value()
? ( static_cast<int>( m_parent->m_ch4Volume.value() ) * m_ch4EnvValue ) / 15
: static_cast<int>( m_parent->m_ch4Volume.value() ) )
? ch4Level
: 0;
}
else ch4 = 0;
else ch4 = ch4Level = 0;
// update framecounters
m_ch4Counter++;
@@ -349,42 +357,47 @@ void NesObject::renderOutput( sampleFrame * buf, fpp_t frames )
// //
////////////////////////////////
float ch12 = static_cast<float>( ch1 + ch2 );
float pin1 = static_cast<float>( ch1 + ch2 );
// add dithering noise
ch12 *= 1.0 + ( Oscillator::noiseSample( 0.0f ) * DITHER_AMP );
ch12 = ch12 / 15.0f - 1.0f;
pin1 *= 1.0 + ( Oscillator::noiseSample( 0.0f ) * DITHER_AMP );
pin1 = pin1 / 30.0f;
ch12 = signedPow( ch12, NES_DIST );
pin1 = signedPow( pin1, NES_DIST );
pin1 = pin1 * 2.0f - 1.0f;
// simple first order iir filter, to simulate the frequency response falloff in nes analog audio output
ch12 = linearInterpolate( ch12, m_12Last, m_nsf );
m_12Last = ch12;
pin1 = linearInterpolate( pin1, m_12Last, m_nsf );
m_12Last = pin1;
// compensate DC offset
pin1 += 1.0f - signedPow( static_cast<float>( ch1Level + ch2Level ) / 30.0f, NES_DIST );
pin1 *= NES_MIXING_12;
ch12 *= NES_MIXING_12;
float ch34 = static_cast<float>( ch3 + ch4 );
float pin2 = static_cast<float>( ch3 + ch4 );
// add dithering noise
ch34 *= 1.0 + ( Oscillator::noiseSample( 0.0f ) * DITHER_AMP );
ch34 = ch34 / 15.0f - 1.0f;
pin2 *= 1.0 + ( Oscillator::noiseSample( 0.0f ) * DITHER_AMP );
pin2 = pin2 / 30.0f;
ch34 = signedPow( ch34, NES_DIST );
pin2 = signedPow( pin2, NES_DIST );
pin2 = pin2 * 2.0f - 1.0f;
// simple first order iir filter, to simulate the frequency response falloff in nes analog audio output
ch34 = linearInterpolate( ch34, m_34Last, m_nsf );
m_34Last = ch34;
pin2 = linearInterpolate( pin2, m_34Last, m_nsf );
m_34Last = pin2;
// compensate DC offset
pin2 += 1.0f - signedPow( static_cast<float>( ch3Level + ch4Level ) / 30.0f, NES_DIST );
ch34 *= NES_MIXING_34;
pin2 *= NES_MIXING_34;
float mixdown = ( ch12 + ch34 ) * NES_MIXING_ALL * m_parent->m_masterVol.value();
// dc offset removal
m_otm = 0.999f * m_otm + mixdown - m_itm;
m_itm = mixdown;
buf[f][0] = m_otm;
buf[f][1] = m_otm;
const float mixdown = ( pin1 + pin2 ) * NES_MIXING_ALL * m_parent->m_masterVol.value();
buf[f][0] = mixdown;
buf[f][1] = mixdown;
} // end framebuffer loop

View File

@@ -73,7 +73,7 @@ const float NES_MIXING_12 = 1.0 / 20.0;
const float NES_MIXING_34 = 1.0 / 12.0;
const float NES_MIXING_ALL = 1.0 / ( NES_MIXING_12 + NES_MIXING_34 ); // constants to simulate the hardwired mixing values for nes channels
const int MIN_WLEN = 8;
const int MIN_WLEN = 4;
class NesInstrument;
@@ -156,9 +156,6 @@ private:
float m_12Last;
float m_34Last;
float m_itm;
float m_otm;
float m_lastNoteFreq;