diff --git a/src/SPU.cpp b/src/SPU.cpp index 3a994f03..e42bff8a 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -18,6 +18,7 @@ #include #include +#include #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,7 +307,9 @@ void Channel::Start() Pos = -3; NoiseVal = 0x7FFF; - PrevSample = 0; + PrevSample[0] = 0; + PrevSample[1] = 0; + PrevSample[2] = 0; CurSample = 0; FIFOReadPos = 0; @@ -445,7 +485,15 @@ s32 Channel::Run() { Timer = TimerReload + (Timer - 0x10000); - PrevSample = CurSample; + // 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) { @@ -459,15 +507,31 @@ s32 Channel::Run() s32 val = (s32)CurSample; - // interpolation - // now you get to enjoy Electric Angel in high quality I guess. - // TODO MAKE CONDITIONAL!!! - if (true) + // interpolation (emulation improvement, not a hardware feature) + if ((type < 3) && (InterpType != 0)) { - s32 samplepos = ((Timer - TimerReload) * 0x1000) / (0x10000 - TimerReload); -//printf("CHANNEL %d POS=%d SAMPLEPOS=%08X (%04X/%04X)\n", Num, Pos, samplepos, Timer, TimerReload); - val = ((val * samplepos) + (PrevSample * (0x1000-samplepos))) >> 12; - //printf("ASSDERP = %08X/%08X => %08X\n", CurSample, PrevSample, val); + 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; diff --git a/src/SPU.h b/src/SPU.h index 12da1686..055a7660 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -31,6 +31,9 @@ void Stop(); void DoSavestate(Savestate* file); +// 0=none 1=linear 2=cosine 3=cubic +void SetInterpolation(int type); + void SetBias(u16 bias); void Mix(u32 dummy); @@ -73,7 +76,7 @@ public: bool KeyOn; u32 Timer; s32 Pos; - s16 PrevSample; + s16 PrevSample[3]; s16 CurSample; u16 NoiseVal; diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp index f47a62a2..5ac178ab 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp +++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp @@ -38,8 +38,15 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui( ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); + oldInterp = Config::AudioInterp; oldVolume = Config::AudioVolume; + ui->cbInterpolation->addItem("None"); + ui->cbInterpolation->addItem("Linear"); + ui->cbInterpolation->addItem("Cosine"); + ui->cbInterpolation->addItem("Cubic"); + ui->cbInterpolation->setCurrentIndex(Config::AudioInterp); + ui->slVolume->setValue(Config::AudioVolume); grpMicMode = new QButtonGroup(this); @@ -73,11 +80,22 @@ void AudioSettingsDialog::on_AudioSettingsDialog_accepted() void AudioSettingsDialog::on_AudioSettingsDialog_rejected() { + Config::AudioInterp = oldInterp; Config::AudioVolume = oldVolume; closeDlg(); } +void AudioSettingsDialog::on_cbInterpolation_currentIndexChanged(int idx) +{ + // prevent a spurious change + if (ui->cbInterpolation->count() < 4) return; + + Config::AudioInterp = ui->cbInterpolation->currentIndex(); + + emit updateAudioSettings(); +} + void AudioSettingsDialog::on_slVolume_valueChanged(int val) { Config::AudioVolume = val; diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.h b/src/frontend/qt_sdl/AudioSettingsDialog.h index 5d98540f..51399758 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.h +++ b/src/frontend/qt_sdl/AudioSettingsDialog.h @@ -51,10 +51,14 @@ public: currentDlg = nullptr; } +signals: + void updateAudioSettings(); + private slots: void on_AudioSettingsDialog_accepted(); void on_AudioSettingsDialog_rejected(); + void on_cbInterpolation_currentIndexChanged(int idx); void on_slVolume_valueChanged(int val); void onChangeMicMode(int mode); void on_btnMicWavBrowse_clicked(); @@ -62,6 +66,7 @@ private slots: private: Ui::AudioSettingsDialog* ui; + int oldInterp; int oldVolume; QButtonGroup* grpMicMode; }; diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.ui b/src/frontend/qt_sdl/AudioSettingsDialog.ui index e57b6c52..e3295238 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.ui +++ b/src/frontend/qt_sdl/AudioSettingsDialog.ui @@ -7,7 +7,7 @@ 0 0 482 - 230 + 256 @@ -29,14 +29,14 @@ Audio output - + Volume: - + <html><head/><body><p>Controls the volume of the audio output.</p></body></html> @@ -52,6 +52,20 @@ + + + + Interpolation: + + + + + + + <html><head/><body><p>Applies interpolation to audio samples for better quality. Option &quot;None&quot; is accurate to DS hardware.</p></body></html> + + + diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.ui b/src/frontend/qt_sdl/EmuSettingsDialog.ui index 42915397..e670c88d 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.ui +++ b/src/frontend/qt_sdl/EmuSettingsDialog.ui @@ -354,21 +354,21 @@ - Branch Optimisations + Branch optimisations - Literal Optimisations + Literal optimisations - Fast Memory + Fast memory diff --git a/src/frontend/qt_sdl/PlatformConfig.cpp b/src/frontend/qt_sdl/PlatformConfig.cpp index d98c5d3c..4692e7ab 100644 --- a/src/frontend/qt_sdl/PlatformConfig.cpp +++ b/src/frontend/qt_sdl/PlatformConfig.cpp @@ -69,6 +69,7 @@ int DirectLAN; int SavestateRelocSRAM; +int AudioInterp; int AudioVolume; int MicInputType; char MicWavPath[1024]; @@ -177,6 +178,7 @@ ConfigEntry PlatformConfigFile[] = {"SavStaRelocSRAM", 0, &SavestateRelocSRAM, 0, NULL, 0}, + {"AudioInterp", 0, &AudioInterp, 0, NULL, 0}, {"AudioVolume", 0, &AudioVolume, 256, NULL, 0}, {"MicInputType", 0, &MicInputType, 1, NULL, 0}, {"MicWavPath", 1, MicWavPath, 0, "", 1023}, @@ -199,7 +201,7 @@ ConfigEntry PlatformConfigFile[] = {"MouseHide", 0, &MouseHide, 0, NULL, 0}, {"MouseHideSeconds", 0, &MouseHideSeconds, 5, NULL, 0}, {"PauseLostFocus", 0, &PauseLostFocus, 0, NULL, 0}, - + {"", -1, NULL, 0, NULL, 0} }; diff --git a/src/frontend/qt_sdl/PlatformConfig.h b/src/frontend/qt_sdl/PlatformConfig.h index 43ac139d..202e36ae 100644 --- a/src/frontend/qt_sdl/PlatformConfig.h +++ b/src/frontend/qt_sdl/PlatformConfig.h @@ -85,6 +85,7 @@ extern int DirectLAN; extern int SavestateRelocSRAM; +extern int AudioInterp; extern int AudioVolume; extern int MicInputType; extern char MicWavPath[1024]; diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.cpp b/src/frontend/qt_sdl/WifiSettingsDialog.cpp index 538a35b4..061d914e 100644 --- a/src/frontend/qt_sdl/WifiSettingsDialog.cpp +++ b/src/frontend/qt_sdl/WifiSettingsDialog.cpp @@ -144,12 +144,12 @@ void WifiSettingsDialog::on_cbxDirectAdapter_currentIndexChanged(int sel) LAN_PCap::AdapterData* adapter = &LAN_PCap::Adapters[sel]; char tmp[64]; - sprintf(tmp, "MAC: %02X:%02X:%02X:%02X:%02X:%02X", + sprintf(tmp, "%02X:%02X:%02X:%02X:%02X:%02X", adapter->MAC[0], adapter->MAC[1], adapter->MAC[2], adapter->MAC[3], adapter->MAC[4], adapter->MAC[5]); ui->lblAdapterMAC->setText(QString(tmp)); - sprintf(tmp, "IP: %d.%d.%d.%d", + sprintf(tmp, "%d.%d.%d.%d", adapter->IP_v4[0], adapter->IP_v4[1], adapter->IP_v4[2], adapter->IP_v4[3]); ui->lblAdapterIP->setText(QString(tmp)); diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.ui b/src/frontend/qt_sdl/WifiSettingsDialog.ui index 174a3dc6..44a4232c 100644 --- a/src/frontend/qt_sdl/WifiSettingsDialog.ui +++ b/src/frontend/qt_sdl/WifiSettingsDialog.ui @@ -61,7 +61,7 @@ - Direct Mode Settings + Direct mode settings @@ -127,7 +127,7 @@ <html><head/><body><p>Indirect mode uses libslirp. It requires no extra setup and is easy to use.</p></body></html> - Indirect Mode (uses libslirp, recommended) + Indirect mode (uses libslirp, recommended) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index d7751b78..4d416aeb 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -363,6 +363,8 @@ void EmuThread::run() GPU::InitRenderer(videoRenderer); GPU::SetRenderSettings(videoRenderer, videoSettings); + SPU::SetInterpolation(Config::AudioInterp); + Input::Init(); u32 nframes = 0; @@ -2388,11 +2390,19 @@ void MainWindow::onOpenVideoSettings() void MainWindow::onOpenAudioSettings() { AudioSettingsDialog* dlg = AudioSettingsDialog::openDlg(this); + connect(dlg, &AudioSettingsDialog::updateAudioSettings, this, &MainWindow::onUpdateAudioSettings); connect(dlg, &AudioSettingsDialog::finished, this, &MainWindow::onAudioSettingsFinished); } +void MainWindow::onUpdateAudioSettings() +{ + SPU::SetInterpolation(Config::AudioInterp); +} + void MainWindow::onAudioSettingsFinished(int res) { + SPU::SetInterpolation(Config::AudioInterp); + if (Config::MicInputType == 3) { micLoadWav(Config::MicWavPath); @@ -2710,6 +2720,7 @@ int main(int argc, char** argv) ); SANITIZE(Config::ScreenVSyncInterval, 1, 20); SANITIZE(Config::GL_ScaleFactor, 1, 16); + SANITIZE(Config::AudioInterp, 0, 3); SANITIZE(Config::AudioVolume, 0, 256); SANITIZE(Config::MicInputType, 0, 3); SANITIZE(Config::ScreenRotation, 0, 3); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 9d9e40e9..062d26f7 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -243,6 +243,7 @@ private slots: void onInputConfigFinished(int res); void onOpenVideoSettings(); void onOpenAudioSettings(); + void onUpdateAudioSettings(); void onAudioSettingsFinished(int res); void onOpenWifiSettings(); void onWifiSettingsFinished(int res);