aboutsummaryrefslogtreecommitdiff
path: root/src/SPU.cpp
diff options
context:
space:
mode:
authorArisotura <thetotalworm@gmail.com>2021-08-08 14:27:57 +0200
committerGitHub <noreply@github.com>2021-08-08 14:27:57 +0200
commit2df6b4fdc3439ea42b043d38f859efb5d4bd4466 (patch)
tree300323e14a13447039cb216dc8a097fe9f4bffd1 /src/SPU.cpp
parentb28a9e4d24b42a59de5e333c99aec6dcc30435f0 (diff)
Audio interpolation (#1176)
add audio interpolation (emulation improvement)
Diffstat (limited to 'src/SPU.cpp')
-rw-r--r--src/SPU.cpp83
1 files changed, 81 insertions, 2 deletions
diff --git a/src/SPU.cpp b/src/SPU.cpp
index 56f83f6..e42bff8 100644
--- a/src/SPU.cpp
+++ b/src/SPU.cpp
@@ -18,6 +18,7 @@
#include <stdio.h>
#include <string.h>
+#include <cmath>
#include "Platform.h"
#include "NDS.h"
#include "DSi.h"
@@ -27,7 +28,6 @@
// SPU TODO
// * capture addition modes, overflow bugs
// * channel hold
-// * 'length less than 4' glitch
namespace SPU
{
@@ -62,6 +62,12 @@ const s16 PSGTable[8][8] =
{-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF}
};
+// audio interpolation is an improvement upon the original hardware
+// (which performs no interpolation)
+int InterpType;
+s16 InterpCos[0x100];
+s16 InterpCubic[0x100][4];
+
const u32 OutputBufferSize = 2*2048;
s16 OutputBackbuffer[2 * OutputBufferSize];
u32 OutputBackbufferWritePosition;
@@ -90,6 +96,32 @@ bool Init()
AudioLock = Platform::Mutex_Create();
+ InterpType = 0;
+
+ // generate interpolation tables
+ // values are 1:1:14 fixed-point
+
+ float m_pi = std::acos(-1.0f);
+ for (int i = 0; i < 0x100; i++)
+ {
+ float ratio = (i * m_pi) / 255.0f;
+ ratio = 1.0f - std::cos(ratio);
+
+ InterpCos[i] = (s16)(ratio * 0x2000);
+ }
+
+ for (int i = 0; i < 0x100; i++)
+ {
+ s32 i1 = i << 6;
+ s32 i2 = (i * i) >> 2;
+ s32 i3 = (i * i * i) >> 10;
+
+ InterpCubic[i][0] = -i3 + 2*i2 - i1;
+ InterpCubic[i][1] = i3 - 2*i2 + 0x4000;
+ InterpCubic[i][2] = -i3 + i2 + i1;
+ InterpCubic[i][3] = i3 - i2;
+ }
+
return true;
}
@@ -148,6 +180,11 @@ void DoSavestate(Savestate* file)
}
+void SetInterpolation(int type)
+{
+ InterpType = type;
+}
+
void SetBias(u16 bias)
{
Bias = bias;
@@ -202,6 +239,7 @@ void Channel::DoSavestate(Savestate* file)
file->Var8((u8*)&KeyOn);
file->Var32(&Timer);
file->Var32((u32*)&Pos);
+ file->VarArray(PrevSample, sizeof(PrevSample));
file->Var16((u16*)&CurSample);
file->Var16(&NoiseVal);
@@ -215,7 +253,7 @@ void Channel::DoSavestate(Savestate* file)
file->Var32(&FIFOWritePos);
file->Var32(&FIFOReadOffset);
file->Var32(&FIFOLevel);
- file->VarArray(FIFO, 8*4);
+ file->VarArray(FIFO, sizeof(FIFO));
}
void Channel::FIFO_BufferData()
@@ -269,6 +307,9 @@ void Channel::Start()
Pos = -3;
NoiseVal = 0x7FFF;
+ PrevSample[0] = 0;
+ PrevSample[1] = 0;
+ PrevSample[2] = 0;
CurSample = 0;
FIFOReadPos = 0;
@@ -444,6 +485,16 @@ s32 Channel::Run()
{
Timer = TimerReload + (Timer - 0x10000);
+ // for optional interpolation: save previous samples
+ // the interpolated audio will be delayed by a couple samples,
+ // but it's easier to deal with this way
+ if ((type < 3) && (InterpType != 0))
+ {
+ PrevSample[2] = PrevSample[1];
+ PrevSample[1] = PrevSample[0];
+ PrevSample[0] = CurSample;
+ }
+
switch (type)
{
case 0: NextSample_PCM8(); break;
@@ -455,6 +506,34 @@ s32 Channel::Run()
}
s32 val = (s32)CurSample;
+
+ // interpolation (emulation improvement, not a hardware feature)
+ if ((type < 3) && (InterpType != 0))
+ {
+ s32 samplepos = ((Timer - TimerReload) * 0x100) / (0x10000 - TimerReload);
+ if (samplepos > 0xFF) samplepos = 0xFF;
+
+ switch (InterpType)
+ {
+ case 1: // linear
+ val = ((val * samplepos) +
+ (PrevSample[0] * (0xFF-samplepos))) >> 8;
+ break;
+
+ case 2: // cosine
+ val = ((val * InterpCos[samplepos]) +
+ (PrevSample[0] * InterpCos[0xFF-samplepos])) >> 14;
+ break;
+
+ case 3: // cubic
+ val = ((PrevSample[2] * InterpCubic[samplepos][0]) +
+ (PrevSample[1] * InterpCubic[samplepos][1]) +
+ (PrevSample[0] * InterpCubic[samplepos][2]) +
+ (val * InterpCubic[samplepos][3])) >> 14;
+ break;
+ }
+ }
+
val <<= VolumeShift;
val *= Volume;
return val;