Remove high frequency artifacts from Lanczos resampling.

Run the TIA signal through a high pass with 10Hz cutoff.
This commit is contained in:
Christian Speckner 2018-08-08 23:09:46 +02:00
parent 8298ad4d26
commit a4d923cbe6
5 changed files with 97 additions and 4 deletions

View File

@ -0,0 +1,41 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#include <cmath>
#ifndef M_PI
#define M_PI 3.14159265358979323846f
#endif
#include "HighPass.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HighPass::HighPass(float cutOffFrequency, float frequency)
: myLastValueIn(0),
myLastValueOut(0),
myAlpha(1.f / (1.f + 2.f*M_PI*cutOffFrequency/frequency))
{}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
float HighPass::apply(float valueIn)
{
float valueOut = myAlpha * (myLastValueOut + valueIn - myLastValueIn);
myLastValueIn = valueIn;
myLastValueOut = valueOut;
return valueOut;
}

View File

@ -0,0 +1,42 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#ifndef HIGH_PASS_HXX
#define HIGH_PASS_HXX
class HighPass
{
public:
HighPass(float cutOffFrequency, float frequency);
float apply(float value);
private:
float myLastValueIn;
float myLastValueOut;
float myAlpha;
private:
HighPass() = delete;
};
#endif // HIGH_PASS_HXX

View File

@ -25,6 +25,7 @@
namespace { namespace {
constexpr float CLIPPING_FACTOR = 0.75; constexpr float CLIPPING_FACTOR = 0.75;
constexpr float HIGH_PASS_CUT_OFF = 10;
uInt32 reducedDenominator(uInt32 n, uInt32 d) uInt32 reducedDenominator(uInt32 n, uInt32 d)
{ {
@ -78,6 +79,9 @@ LanczosResampler::LanczosResampler(
myCurrentFragment(nullptr), myCurrentFragment(nullptr),
myFragmentIndex(0), myFragmentIndex(0),
myIsUnderrun(true), myIsUnderrun(true),
myHighPassL(HIGH_PASS_CUT_OFF, formatFrom.sampleRate),
myHighPassR(HIGH_PASS_CUT_OFF, formatFrom.sampleRate),
myHighPass(HIGH_PASS_CUT_OFF, formatFrom.sampleRate),
myTimeIndex(0) myTimeIndex(0)
{ {
myPrecomputedKernels = make_unique<float[]>(myPrecomputedKernelCount * myKernelSize); myPrecomputedKernels = make_unique<float[]>(myPrecomputedKernelCount * myKernelSize);
@ -184,11 +188,11 @@ inline void LanczosResampler::shiftSamples(uInt32 samplesToShift)
{ {
while (samplesToShift-- > 0) { while (samplesToShift-- > 0) {
if (myFormatFrom.stereo) { if (myFormatFrom.stereo) {
myBufferL->shift(myCurrentFragment[2*myFragmentIndex] / static_cast<float>(0x7fff)); myBufferL->shift(myHighPassL.apply(myCurrentFragment[2*myFragmentIndex] / static_cast<float>(0x7fff)));
myBufferR->shift(myCurrentFragment[2*myFragmentIndex + 1] / static_cast<float>(0x7fff)); myBufferR->shift(myHighPassR.apply(myCurrentFragment[2*myFragmentIndex + 1] / static_cast<float>(0x7fff)));
} }
else else
myBuffer->shift(myCurrentFragment[myFragmentIndex] / static_cast<float>(0x7fff)); myBuffer->shift(myHighPass.apply(myCurrentFragment[myFragmentIndex] / static_cast<float>(0x7fff)));
myFragmentIndex++; myFragmentIndex++;

View File

@ -21,6 +21,7 @@
#include "bspf.hxx" #include "bspf.hxx"
#include "Resampler.hxx" #include "Resampler.hxx"
#include "ConvolutionBuffer.hxx" #include "ConvolutionBuffer.hxx"
#include "HighPass.hxx"
class LanczosResampler : public Resampler class LanczosResampler : public Resampler
{ {
@ -59,6 +60,10 @@ class LanczosResampler : public Resampler
uInt32 myFragmentIndex; uInt32 myFragmentIndex;
bool myIsUnderrun; bool myIsUnderrun;
HighPass myHighPassL;
HighPass myHighPassR;
HighPass myHighPass;
uInt32 myTimeIndex; uInt32 myTimeIndex;
}; };

View File

@ -3,7 +3,8 @@ MODULE := src/common/audio
MODULE_OBJS := \ MODULE_OBJS := \
src/common/audio/SimpleResampler.o \ src/common/audio/SimpleResampler.o \
src/common/audio/ConvolutionBuffer.o \ src/common/audio/ConvolutionBuffer.o \
src/common/audio/LanczosResampler.o src/common/audio/LanczosResampler.o \
src/common/audio/HighPass.o
MODULE_DIRS += \ MODULE_DIRS += \
src/emucore/tia src/emucore/tia