SPU2: merge initial code, remove plugin exports

This commit is contained in:
Gauvain 'GovanifY' Roussel-Tarbouriech 2020-09-24 12:50:56 +02:00 committed by refractionpcsx2
parent b8c3bd4fae
commit 3d3ccbfcd7
63 changed files with 16083 additions and 0 deletions

207
pcsx2/SPU2/ADSR.cpp Normal file
View File

@ -0,0 +1,207 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
static const s32 ADSR_MAX_VOL = 0x7fffffff;
static const int InvExpOffsets[] = {0, 4, 6, 8, 9, 10, 11, 12};
static u32 PsxRates[160];
void InitADSR() // INIT ADSR
{
for (int i = 0; i < (32 + 128); i++) {
int shift = (i - 32) >> 2;
s64 rate = (i & 3) + 4;
if (shift < 0)
rate >>= -shift;
else
rate <<= shift;
PsxRates[i] = (int)std::min(rate, (s64)0x3fffffffLL);
}
}
bool V_ADSR::Calculate()
{
pxAssume(Phase != 0);
if (Releasing && (Phase < 5))
Phase = 5;
switch (Phase) {
case 1: // attack
if (Value == ADSR_MAX_VOL) {
// Already maxed out. Progress phase and nothing more:
Phase++;
break;
}
// Case 1 below is for pseudo exponential below 75%.
// Pseudo Exp > 75% and Linear are the same.
if (AttackMode && (Value >= 0x60000000))
Value += PsxRates[(AttackRate ^ 0x7f) - 0x18 + 32];
else
Value += PsxRates[(AttackRate ^ 0x7f) - 0x10 + 32];
if (Value < 0) {
// We hit the ceiling.
Phase++;
Value = ADSR_MAX_VOL;
}
break;
case 2: // decay
{
u32 off = InvExpOffsets[(Value >> 28) & 7];
Value -= PsxRates[((DecayRate ^ 0x1f) * 4) - 0x18 + off + 32];
// calculate sustain level as a factor of the ADSR maximum volume.
s32 suslev = ((0x80000000 / 0x10) * (SustainLevel + 1)) - 1;
if (Value <= suslev) {
if (Value < 0)
Value = 0;
Phase++;
}
} break;
case 3: // sustain
{
// 0x7f disables sustain (infinite sustain)
if (SustainRate == 0x7f)
return true;
if (SustainMode & 2) // decreasing
{
if (SustainMode & 4) // exponential
{
u32 off = InvExpOffsets[(Value >> 28) & 7];
Value -= PsxRates[(SustainRate ^ 0x7f) - 0x1b + off + 32];
} else // linear
Value -= PsxRates[(SustainRate ^ 0x7f) - 0xf + 32];
if (Value <= 0) {
Value = 0;
Phase++;
}
} else { // increasing
if ((SustainMode & 4) && (Value >= 0x60000000))
Value += PsxRates[(SustainRate ^ 0x7f) - 0x18 + 32];
else
// linear / Pseudo below 75% (they're the same)
Value += PsxRates[(SustainRate ^ 0x7f) - 0x10 + 32];
if (Value < 0) {
Value = ADSR_MAX_VOL;
Phase++;
}
}
} break;
case 4: // sustain end
Value = (SustainMode & 2) ? 0 : ADSR_MAX_VOL;
if (Value == 0)
Phase = 6;
break;
case 5: // release
if (ReleaseMode) // exponential
{
u32 off = InvExpOffsets[(Value >> 28) & 7];
Value -= PsxRates[((ReleaseRate ^ 0x1f) * 4) - 0x18 + off + 32];
} else { // linear
//Value-=PsxRates[((ReleaseRate^0x1f)*4)-0xc+32];
if (ReleaseRate != 0x1f)
Value -= (1 << (0x1f - ReleaseRate));
}
if (Value <= 0) {
Value = 0;
Phase++;
}
break;
case 6: // release end
Value = 0;
break;
jNO_DEFAULT
}
// returns true if the voice is active, or false if it's stopping.
return Phase != 6;
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// //
#define VOLFLAG_REVERSE_PHASE (1ul << 0)
#define VOLFLAG_DECREMENT (1ul << 1)
#define VOLFLAG_EXPONENTIAL (1ul << 2)
#define VOLFLAG_SLIDE_ENABLE (1ul << 3)
void V_VolumeSlide::Update()
{
if (!(Mode & VOLFLAG_SLIDE_ENABLE))
return;
// Volume slides use the same basic logic as ADSR, but simplified (single-stage
// instead of multi-stage)
if (Increment == 0x7f)
return;
s32 value = abs(Value);
if (Mode & VOLFLAG_DECREMENT) {
// Decrement
if (Mode & VOLFLAG_EXPONENTIAL) {
u32 off = InvExpOffsets[(value >> 28) & 7];
value -= PsxRates[(Increment ^ 0x7f) - 0x1b + off + 32];
} else
value -= PsxRates[(Increment ^ 0x7f) - 0xf + 32];
if (value < 0) {
value = 0;
Mode = 0; // disable slide
}
} else {
// Increment
// Pseudo-exponential increments, as done by the SPU2 (really!)
// Above 75% slides slow, below 75% slides fast. It's exponential, pseudo'ly speaking.
if ((Mode & VOLFLAG_EXPONENTIAL) && (value >= 0x60000000))
value += PsxRates[(Increment ^ 0x7f) - 0x18 + 32];
else
// linear / Pseudo below 75% (they're the same)
value += PsxRates[(Increment ^ 0x7f) - 0x10 + 32];
if (value < 0) // wrapped around the "top"?
{
value = 0x7fffffff;
Mode = 0; // disable slide
}
}
Value = (Value < 0) ? -value : value;
}

158
pcsx2/SPU2/CMakeLists.txt Normal file
View File

@ -0,0 +1,158 @@
if (openSUSE)
# openSUSE don't install wx in a standard library system
# path. Let's bypass the dynamic linker and hardcode the path.
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH ON)
endif()
# Check that people use the good file
if(NOT TOP_CMAKE_WAS_SOURCED)
message(FATAL_ERROR "
You did not 'cmake' the good CMakeLists.txt file. Use the one in the top dir.
It is advice to delete all wrongly generated cmake stuff => CMakeFiles & CMakeCache.txt")
endif()
set(CommonFlags
-fvisibility=hidden
-Wall
-Wno-parentheses
)
# plugin name
set(Output spu2x-2.0.0)
if (UNIX)
if (SDL2_API)
set(spu2xFinalFlags "-DSPU2X_SDL2" ${CommonFlags})
else()
set(spu2xFinalFlags "-DSPU2X_SDL" ${CommonFlags})
endif()
else()
set(spu2xFinalFlags ${CommonFlags})
endif()
# spu2x sources
set(spu2xSources
ADSR.cpp
Debug.cpp
DplIIdecoder.cpp
Dma.cpp
Lowpass.cpp
Mixer.cpp
PrecompiledHeader.cpp
PS2E-spu2.cpp
ReadInput.cpp
RegLog.cpp
RegTable.cpp
Reverb.cpp
SndOut.cpp
SndOut_SDL.cpp
spu2freeze.cpp
Spu2replay.cpp
spu2sys.cpp
Timestretcher.cpp
Wavedump_wav.cpp
WavFile.cpp
)
# spu2x headers
set(spu2xHeaders
Config.h
Debug.h
defs.h
Dma.h
Global.h
Lowpass.h
Mixer.h
PS2E-spu2.h
regs.h
SndOut.h
spdif.h
Spu2replay.h
WavFile.h
)
if(Windows)
LIST(APPEND spu2xSources
Windows/SndOut_waveOut.cpp
Windows/SndOut_DSound.cpp
Windows/SndOut_XAudio2.cpp
Windows/UIHelpers.cpp
Windows/RealtimeDebugger.cpp
Windows/dsp.cpp
Windows/ConfigSoundtouch.cpp
)
LIST(APPEND spu2xHeaders
Windows/resource.h
Windows/WinConfig.h
Windows/dsp.h
)
include_directories("Windows")
else()
LIST(APPEND spu2xSources
Linux/Alsa.cpp
Linux/CfgHelpers.cpp
Linux/Config.cpp
Linux/ConfigDebug.cpp
Linux/ConfigSoundTouch.cpp
Linux/Dialogs.cpp
wx/wxConfig.cpp
)
LIST(APPEND spu2xHeaders
Linux/Alsa.h
Linux/Config.h
Linux/Dialogs.h
wx/wxConfig.h
)
include_directories(Linux)
endif()
set(spu2xFinalSources
${spu2xSources}
${spu2xHeaders}
${spu2xLinuxHeaders}
)
set(spu2xFinalLibs
Utilities_NO_TLS
${ALSA_LIBRARIES}
${GTK2_LIBRARIES}
${SOUNDTOUCH_LIBRARIES}
)
if (PORTAUDIO_FOUND)
set(spu2xFinalFlags
${spu2xFinalFlags}
"-DSPU2X_PORTAUDIO"
)
LIST(APPEND spu2xFinalSources
SndOut_Portaudio.cpp
)
set(spu2xFinalLibs
${spu2xFinalLibs}
${PORTAUDIO_LIBRARIES}
)
endif()
if (SDL2_API)
set(spu2xFinalLibs
${spu2xFinalLibs}
${SDL2_LIBRARIES}
)
else()
set(spu2xFinalLibs
${spu2xFinalLibs}
${SDL_LIBRARY}
)
endif()
if(BUILTIN_SPU2)
add_pcsx2_lib(${Output} "${spu2xFinalSources}" "${spu2xFinalLibs}" "${spu2xFinalFlags}")
else()
add_pcsx2_plugin(${Output} "${spu2xFinalSources}" "${spu2xFinalLibs}" "${spu2xFinalFlags}")
endif()

102
pcsx2/SPU2/Config.h Normal file
View File

@ -0,0 +1,102 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
extern bool DebugEnabled;
extern bool _MsgToConsole;
extern bool _MsgKeyOnOff;
extern bool _MsgVoiceOff;
extern bool _MsgDMA;
extern bool _MsgAutoDMA;
extern bool _MsgOverruns;
extern bool _MsgCache;
extern bool _AccessLog;
extern bool _DMALog;
extern bool _WaveLog;
extern bool _CoresDump;
extern bool _MemDump;
extern bool _RegDump;
extern bool _visual_debug_enabled;
static __forceinline bool MsgToConsole() { return _MsgToConsole & DebugEnabled; }
static __forceinline bool MsgKeyOnOff() { return _MsgKeyOnOff & MsgToConsole(); }
static __forceinline bool MsgVoiceOff() { return _MsgVoiceOff & MsgToConsole(); }
static __forceinline bool MsgDMA() { return _MsgDMA & MsgToConsole(); }
static __forceinline bool MsgAutoDMA() { return _MsgAutoDMA & MsgToConsole(); }
static __forceinline bool MsgOverruns() { return _MsgOverruns & MsgToConsole(); }
static __forceinline bool MsgCache() { return _MsgCache & MsgToConsole(); }
static __forceinline bool AccessLog() { return _AccessLog & DebugEnabled; }
static __forceinline bool DMALog() { return _DMALog & DebugEnabled; }
static __forceinline bool WaveLog() { return _WaveLog & DebugEnabled; }
static __forceinline bool CoresDump() { return _CoresDump & DebugEnabled; }
static __forceinline bool MemDump() { return _MemDump & DebugEnabled; }
static __forceinline bool RegDump() { return _RegDump & DebugEnabled; }
static __forceinline bool VisualDebug() { return _visual_debug_enabled & DebugEnabled; }
extern wxString AccessLogFileName;
extern wxString DMA4LogFileName;
extern wxString DMA7LogFileName;
extern wxString CoresDumpFileName;
extern wxString MemDumpFileName;
extern wxString RegDumpFileName;
extern int Interpolation;
extern int numSpeakers;
extern bool EffectsDisabled;
extern float FinalVolume; // Global / pre-scale
extern bool AdvancedVolumeControl;
extern float VolumeAdjustFLdb;
extern float VolumeAdjustCdb;
extern float VolumeAdjustFRdb;
extern float VolumeAdjustBLdb;
extern float VolumeAdjustBRdb;
extern float VolumeAdjustSLdb;
extern float VolumeAdjustSRdb;
extern float VolumeAdjustLFEdb;
extern bool postprocess_filter_enabled;
extern bool postprocess_filter_dealias;
extern int dplLevel;
extern u32 OutputModule;
extern int SndOutLatencyMS;
extern int SynchMode;
#ifndef __POSIX__
extern wchar_t dspPlugin[];
extern int dspPluginModule;
extern bool dspPluginEnabled;
#endif
namespace SoundtouchCfg
{
extern void ApplySettings(soundtouch::SoundTouch &sndtouch);
}
//////
extern void ReadSettings();
extern void WriteSettings();
extern void configure();

258
pcsx2/SPU2/Debug.cpp Normal file
View File

@ -0,0 +1,258 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
int crazy_debug = 0;
char s[4096];
FILE *spu2Log = NULL;
void FileLog(const char *fmt, ...)
{
#ifdef SPU2_LOG
va_list list;
if (!AccessLog())
return;
if (!spu2Log)
return;
va_start(list, fmt);
vsprintf(s, fmt, list);
va_end(list);
fputs(s, spu2Log);
fflush(spu2Log);
#if 0
if(crazy_debug)
{
fputs(s,stderr);
fflush(stderr);
}
#endif
#endif
}
//Note to developer on the usage of ConLog:
// while ConLog doesn't print anything if messages to console are disabled at the GUI,
// it's still better to outright not call it on tight loop scenarios, by testing MsgToConsole() (which is inline and very quick).
// Else, there's some (small) overhead in calling and returning from ConLog.
void ConLog(const char *fmt, ...)
{
if (!MsgToConsole())
return;
va_list list;
va_start(list, fmt);
vsprintf(s, fmt, list);
va_end(list);
fputs(s, stderr);
fflush(stderr);
if (spu2Log) {
fputs(s, spu2Log);
fflush(spu2Log);
}
}
void V_VolumeSlide::DebugDump(FILE *dump, const char *title, const char *nameLR)
{
fprintf(dump, "%s Volume for %s Channel:\t%x\n"
" - Value: %x\n"
" - Mode: %x\n"
" - Increment: %x\n",
title, nameLR, Reg_VOL, Value, Mode, Increment);
}
void V_VolumeSlideLR::DebugDump(FILE *dump, const char *title)
{
Left.DebugDump(dump, title, "Left");
Right.DebugDump(dump, title, "Right");
}
void V_VolumeLR::DebugDump(FILE *dump, const char *title)
{
fprintf(dump, "Volume for %s (%s Channel):\t%x\n", title, "Left", Left);
fprintf(dump, "Volume for %s (%s Channel):\t%x\n", title, "Right", Right);
}
void DoFullDump()
{
#ifdef _MSC_VER
#ifdef SPU2_LOG
FILE *dump;
u8 c = 0, v = 0;
if (MemDump()) {
dump = fopen(wxString(MemDumpFileName).ToUTF8(), "wb");
if (dump) {
fwrite(_spu2mem, 0x200000, 1, dump);
fclose(dump);
}
}
if (RegDump()) {
dump = fopen(wxString(RegDumpFileName).ToUTF8(), "wb");
if (dump) {
fwrite(spu2regs, 0x2000, 1, dump);
fclose(dump);
}
}
if (!CoresDump())
return;
dump = fopen(wxString(CoresDumpFileName).ToUTF8(), "wt");
if (dump) {
for (c = 0; c < 2; c++) {
fprintf(dump, "#### CORE %d DUMP.\n", c);
Cores[c].MasterVol.DebugDump(dump, "Master");
Cores[c].ExtVol.DebugDump(dump, "External Data Input");
Cores[c].InpVol.DebugDump(dump, "Voice Data Input [dry]");
Cores[c].FxVol.DebugDump(dump, "Effects/Reverb [wet]");
fprintf(dump, "Interrupt Address: %x\n", Cores[c].IRQA);
fprintf(dump, "DMA Transfer Start Address: %x\n", Cores[c].TSA);
fprintf(dump, "External Input to Direct Output (Left): %s\n", Cores[c].DryGate.ExtL ? "Yes" : "No");
fprintf(dump, "External Input to Direct Output (Right): %s\n", Cores[c].DryGate.ExtR ? "Yes" : "No");
fprintf(dump, "External Input to Effects (Left): %s\n", Cores[c].WetGate.ExtL ? "Yes" : "No");
fprintf(dump, "External Input to Effects (Right): %s\n", Cores[c].WetGate.ExtR ? "Yes" : "No");
fprintf(dump, "Sound Data Input to Direct Output (Left): %s\n", Cores[c].DryGate.SndL ? "Yes" : "No");
fprintf(dump, "Sound Data Input to Direct Output (Right): %s\n", Cores[c].DryGate.SndR ? "Yes" : "No");
fprintf(dump, "Sound Data Input to Effects (Left): %s\n", Cores[c].WetGate.SndL ? "Yes" : "No");
fprintf(dump, "Sound Data Input to Effects (Right): %s\n", Cores[c].WetGate.SndR ? "Yes" : "No");
fprintf(dump, "Voice Data Input to Direct Output (Left): %s\n", Cores[c].DryGate.InpL ? "Yes" : "No");
fprintf(dump, "Voice Data Input to Direct Output (Right): %s\n", Cores[c].DryGate.InpR ? "Yes" : "No");
fprintf(dump, "Voice Data Input to Effects (Left): %s\n", Cores[c].WetGate.InpL ? "Yes" : "No");
fprintf(dump, "Voice Data Input to Effects (Right): %s\n", Cores[c].WetGate.InpR ? "Yes" : "No");
fprintf(dump, "IRQ Enabled: %s\n", Cores[c].IRQEnable ? "Yes" : "No");
fprintf(dump, "Effects Enabled: %s\n", Cores[c].FxEnable ? "Yes" : "No");
fprintf(dump, "Mute Enabled: %s\n", Cores[c].Mute ? "Yes" : "No");
fprintf(dump, "Noise Clock: %d\n", Cores[c].NoiseClk);
fprintf(dump, "DMA Bits: %d\n", Cores[c].DMABits);
fprintf(dump, "Effects Start: %x\n", Cores[c].EffectsStartA);
fprintf(dump, "Effects End: %x\n", Cores[c].EffectsEndA);
fprintf(dump, "Registers:\n");
fprintf(dump, " - PMON: %x\n", Cores[c].Regs.PMON);
fprintf(dump, " - NON: %x\n", Cores[c].Regs.NON);
fprintf(dump, " - VMIXL: %x\n", Cores[c].Regs.VMIXL);
fprintf(dump, " - VMIXR: %x\n", Cores[c].Regs.VMIXR);
fprintf(dump, " - VMIXEL: %x\n", Cores[c].Regs.VMIXEL);
fprintf(dump, " - VMIXER: %x\n", Cores[c].Regs.VMIXER);
fprintf(dump, " - MMIX: %x\n", Cores[c].Regs.VMIXEL);
fprintf(dump, " - ENDX: %x\n", Cores[c].Regs.VMIXER);
fprintf(dump, " - STATX: %x\n", Cores[c].Regs.VMIXEL);
fprintf(dump, " - ATTR: %x\n", Cores[c].Regs.VMIXER);
for (v = 0; v < 24; v++) {
fprintf(dump, "Voice %d:\n", v);
Cores[c].Voices[v].Volume.DebugDump(dump, "");
fprintf(dump, " - ADSR Envelope: %x & %x\n"
" - Ar: %x\n"
" - Am: %x\n"
" - Dr: %x\n"
" - Sl: %x\n"
" - Sr: %x\n"
" - Sm: %x\n"
" - Rr: %x\n"
" - Rm: %x\n"
" - Phase: %x\n"
" - Value: %x\n",
Cores[c].Voices[v].ADSR.regADSR1,
Cores[c].Voices[v].ADSR.regADSR2,
Cores[c].Voices[v].ADSR.AttackRate,
Cores[c].Voices[v].ADSR.AttackMode,
Cores[c].Voices[v].ADSR.DecayRate,
Cores[c].Voices[v].ADSR.SustainLevel,
Cores[c].Voices[v].ADSR.SustainRate,
Cores[c].Voices[v].ADSR.SustainMode,
Cores[c].Voices[v].ADSR.ReleaseRate,
Cores[c].Voices[v].ADSR.ReleaseMode,
Cores[c].Voices[v].ADSR.Phase,
Cores[c].Voices[v].ADSR.Value);
fprintf(dump, " - Pitch: %x\n", Cores[c].Voices[v].Pitch);
fprintf(dump, " - Modulated: %s\n", Cores[c].Voices[v].Modulated ? "Yes" : "No");
fprintf(dump, " - Source: %s\n", Cores[c].Voices[v].Noise ? "Noise" : "Wave");
fprintf(dump, " - Direct Output for Left Channel: %s\n", Cores[c].VoiceGates[v].DryL ? "Yes" : "No");
fprintf(dump, " - Direct Output for Right Channel: %s\n", Cores[c].VoiceGates[v].DryR ? "Yes" : "No");
fprintf(dump, " - Effects Output for Left Channel: %s\n", Cores[c].VoiceGates[v].WetL ? "Yes" : "No");
fprintf(dump, " - Effects Output for Right Channel: %s\n", Cores[c].VoiceGates[v].WetR ? "Yes" : "No");
fprintf(dump, " - Loop Start Address: %x\n", Cores[c].Voices[v].LoopStartA);
fprintf(dump, " - Sound Start Address: %x\n", Cores[c].Voices[v].StartA);
fprintf(dump, " - Next Data Address: %x\n", Cores[c].Voices[v].NextA);
fprintf(dump, " - Play Start Cycle: %d\n", Cores[c].Voices[v].PlayCycle);
fprintf(dump, " - Play Status: %s\n", (Cores[c].Voices[v].ADSR.Phase > 0) ? "Playing" : "Not Playing");
fprintf(dump, " - Block Sample: %d\n", Cores[c].Voices[v].SCurrent);
}
fprintf(dump, "#### END OF DUMP.\n\n");
}
fclose(dump);
}
dump = fopen("logs/effects.txt", "wt");
if (dump) {
for (c = 0; c < 2; c++) {
fprintf(dump, "#### CORE %d EFFECTS PROCESSOR DUMP.\n", c);
fprintf(dump, " - IN_COEF_L: %x\n", Cores[c].Revb.IN_COEF_R);
fprintf(dump, " - IN_COEF_R: %x\n", Cores[c].Revb.IN_COEF_L);
fprintf(dump, " - APF1_VOL: %x\n", Cores[c].Revb.APF1_VOL);
fprintf(dump, " - APF2_VOL: %x\n", Cores[c].Revb.APF2_VOL);
fprintf(dump, " - APF1_SIZE: %x\n", Cores[c].Revb.APF1_SIZE);
fprintf(dump, " - APF2_SIZE: %x\n", Cores[c].Revb.APF2_SIZE);
fprintf(dump, " - IIR_VOL: %x\n", Cores[c].Revb.IIR_VOL);
fprintf(dump, " - WALL_VOL: %x\n", Cores[c].Revb.WALL_VOL);
fprintf(dump, " - SAME_L_SRC: %x\n", Cores[c].Revb.SAME_L_SRC);
fprintf(dump, " - SAME_R_SRC: %x\n", Cores[c].Revb.SAME_R_SRC);
fprintf(dump, " - DIFF_L_SRC: %x\n", Cores[c].Revb.DIFF_L_SRC);
fprintf(dump, " - DIFF_R_SRC: %x\n", Cores[c].Revb.DIFF_R_SRC);
fprintf(dump, " - SAME_L_DST: %x\n", Cores[c].Revb.SAME_L_DST);
fprintf(dump, " - SAME_R_DST: %x\n", Cores[c].Revb.SAME_R_DST);
fprintf(dump, " - DIFF_L_DST: %x\n", Cores[c].Revb.DIFF_L_DST);
fprintf(dump, " - DIFF_R_DST: %x\n", Cores[c].Revb.DIFF_R_DST);
fprintf(dump, " - COMB1_VOL: %x\n", Cores[c].Revb.COMB1_VOL);
fprintf(dump, " - COMB2_VOL: %x\n", Cores[c].Revb.COMB2_VOL);
fprintf(dump, " - COMB3_VOL: %x\n", Cores[c].Revb.COMB3_VOL);
fprintf(dump, " - COMB4_VOL: %x\n", Cores[c].Revb.COMB4_VOL);
fprintf(dump, " - COMB1_L_SRC: %x\n", Cores[c].Revb.COMB1_L_SRC);
fprintf(dump, " - COMB1_R_SRC: %x\n", Cores[c].Revb.COMB1_R_SRC);
fprintf(dump, " - COMB2_L_SRC: %x\n", Cores[c].Revb.COMB2_L_SRC);
fprintf(dump, " - COMB2_R_SRC: %x\n", Cores[c].Revb.COMB2_R_SRC);
fprintf(dump, " - COMB3_L_SRC: %x\n", Cores[c].Revb.COMB3_L_SRC);
fprintf(dump, " - COMB3_R_SRC: %x\n", Cores[c].Revb.COMB3_R_SRC);
fprintf(dump, " - COMB4_L_SRC: %x\n", Cores[c].Revb.COMB4_L_SRC);
fprintf(dump, " - COMB4_R_SRC: %x\n", Cores[c].Revb.COMB4_R_SRC);
fprintf(dump, " - APF1_L_DST: %x\n", Cores[c].Revb.APF1_L_DST);
fprintf(dump, " - APF1_R_DST: %x\n", Cores[c].Revb.APF1_R_DST);
fprintf(dump, " - APF2_L_DST: %x\n", Cores[c].Revb.APF2_L_DST);
fprintf(dump, " - APF2_R_DST: %x\n", Cores[c].Revb.APF2_R_DST);
fprintf(dump, "#### END OF DUMP.\n\n");
}
fclose(dump);
}
#endif
#endif
}

71
pcsx2/SPU2/Debug.h Normal file
View File

@ -0,0 +1,71 @@
//GiGaHeRz's SPU2 Driver
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
//
#ifndef DEBUG_H_INCLUDED
#define DEBUG_H_INCLUDED
extern FILE *spu2Log;
extern void FileLog(const char *fmt, ...);
extern void ConLog(const char *fmt, ...);
extern void DoFullDump();
extern FILE *OpenBinaryLog(const wxString &logfile);
extern FILE *OpenLog(const wxString &logfile);
extern FILE *OpenDump(const wxString &logfile);
namespace WaveDump
{
enum CoreSourceType {
// Core's input stream, usually pulled from ADMA streams.
CoreSrc_Input = 0,
// Output of the actual 24 input voices which have dry output enabled.
CoreSrc_DryVoiceMix,
// Output of the actual 24 input voices that have wet output enabled.
CoreSrc_WetVoiceMix,
// Wet mix including inputs and externals, prior to the application of reverb.
CoreSrc_PreReverb,
// Wet mix after reverb has turned it into a pile of garbly gook.
CoreSrc_PostReverb,
// Final output of the core. For core 0, it's the feed into Core1.
// For Core1, it's the feed into SndOut.
CoreSrc_External,
CoreSrc_Count
};
extern void Open();
extern void Close();
extern void WriteCore(uint coreidx, CoreSourceType src, s16 left, s16 right);
extern void WriteCore(uint coreidx, CoreSourceType src, const StereoOut16 &sample);
}
using WaveDump::CoreSrc_Input;
using WaveDump::CoreSrc_DryVoiceMix;
using WaveDump::CoreSrc_WetVoiceMix;
using WaveDump::CoreSrc_PreReverb;
using WaveDump::CoreSrc_PostReverb;
using WaveDump::CoreSrc_External;
#endif // DEBUG_H_INCLUDED //

416
pcsx2/SPU2/Dma.cpp Normal file
View File

@ -0,0 +1,416 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "Dma.h"
#include "PS2E-spu2.h" // temporary until I resolve cyclePtr/TimeUpdate dependencies.
extern u8 callirq;
static FILE *DMA4LogFile = NULL;
static FILE *DMA7LogFile = NULL;
static FILE *ADMA4LogFile = NULL;
static FILE *ADMA7LogFile = NULL;
static FILE *ADMAOutLogFile = NULL;
static FILE *REGWRTLogFile[2] = {0, 0};
void DMALogOpen()
{
if (!DMALog())
return;
DMA4LogFile = OpenBinaryLog(DMA4LogFileName);
DMA7LogFile = OpenBinaryLog(DMA7LogFileName);
ADMA4LogFile = OpenBinaryLog(L"adma4.raw");
ADMA7LogFile = OpenBinaryLog(L"adma7.raw");
ADMAOutLogFile = OpenBinaryLog(L"admaOut.raw");
}
void DMA4LogWrite(void *lpData, u32 ulSize)
{
if (!DMALog())
return;
if (!DMA4LogFile)
return;
fwrite(lpData, ulSize, 1, DMA4LogFile);
}
void DMA7LogWrite(void *lpData, u32 ulSize)
{
if (!DMALog())
return;
if (!DMA7LogFile)
return;
fwrite(lpData, ulSize, 1, DMA7LogFile);
}
void ADMAOutLogWrite(void *lpData, u32 ulSize)
{
if (!DMALog())
return;
if (!ADMAOutLogFile)
return;
fwrite(lpData, ulSize, 1, ADMAOutLogFile);
}
void RegWriteLog(u32 core, u16 value)
{
if (!DMALog())
return;
if (!REGWRTLogFile[core])
return;
fwrite(&value, 2, 1, REGWRTLogFile[core]);
}
void DMALogClose()
{
safe_fclose(DMA4LogFile);
safe_fclose(DMA7LogFile);
safe_fclose(REGWRTLogFile[0]);
safe_fclose(REGWRTLogFile[1]);
safe_fclose(ADMA4LogFile);
safe_fclose(ADMA7LogFile);
safe_fclose(ADMAOutLogFile);
}
void V_Core::LogAutoDMA(FILE *fp)
{
if (!DMALog() || !fp || !DMAPtr)
return;
fwrite(DMAPtr + InputDataProgress, 0x400, 1, fp);
}
void V_Core::AutoDMAReadBuffer(int mode) //mode: 0= split stereo; 1 = do not split stereo
{
int spos = ((InputPosRead + 0xff) & 0x100); //starting position of the free buffer
LogAutoDMA(Index ? ADMA7LogFile : ADMA4LogFile);
// HACKFIX!! DMAPtr can be invalid after a savestate load, so the savestate just forces it
// to NULL and we ignore it here. (used to work in old VM editions of PCSX2 with fixed
// addressing, but new PCSX2s have dynamic memory addressing).
if (mode) {
if (DMAPtr != NULL)
//memcpy((ADMATempBuffer+(spos<<1)),DMAPtr+InputDataProgress,0x400);
memcpy(GetMemPtr(0x2000 + (Index << 10) + spos), DMAPtr + InputDataProgress, 0x400);
MADR += 0x400;
InputDataLeft -= 0x200;
InputDataProgress += 0x200;
} else {
if (DMAPtr != NULL)
//memcpy((ADMATempBuffer+spos),DMAPtr+InputDataProgress,0x200);
memcpy(GetMemPtr(0x2000 + (Index << 10) + spos), DMAPtr + InputDataProgress, 0x200);
MADR += 0x200;
InputDataLeft -= 0x100;
InputDataProgress += 0x100;
if (DMAPtr != NULL)
//memcpy((ADMATempBuffer+spos+0x200),DMAPtr+InputDataProgress,0x200);
memcpy(GetMemPtr(0x2200 + (Index << 10) + spos), DMAPtr + InputDataProgress, 0x200);
MADR += 0x200;
InputDataLeft -= 0x100;
InputDataProgress += 0x100;
}
// See ReadInput at mixer.cpp for explanation on the commented out lines
//
}
void V_Core::StartADMAWrite(u16 *pMem, u32 sz)
{
int size = (sz) & (~511);
if (MsgAutoDMA())
ConLog("* SPU2-X: DMA%c AutoDMA Transfer of %d bytes to %x (%02x %x %04x).\n",
GetDmaIndexChar(), size << 1, TSA, DMABits, AutoDMACtrl, (~Regs.ATTR) & 0x7fff);
InputDataProgress = 0;
if ((AutoDMACtrl & (Index + 1)) == 0) {
TSA = 0x2000 + (Index << 10);
DMAICounter = size;
} else if (size >= 512) {
InputDataLeft = size;
if (AdmaInProgress == 0) {
#ifdef PCM24_S1_INTERLEAVE
if ((Index == 1) && ((PlayMode & 8) == 8)) {
AutoDMAReadBuffer(Index, 1);
} else {
AutoDMAReadBuffer(Index, 0);
}
#else
if (((PlayMode & 4) == 4) && (Index == 0))
Cores[0].InputPosRead = 0;
AutoDMAReadBuffer(0);
#endif
// Klonoa 2
if (size == 512)
DMAICounter = size;
}
AdmaInProgress = 1;
} else {
InputDataLeft = 0;
DMAICounter = 1;
}
TADR = MADR + (size << 1);
}
// HACKFIX: The BIOS breaks if we check the IRQA for both cores when issuing DMA writes. The
// breakage is a null psxRegs.pc being loaded form some memory address (haven't traced it deeper
// yet). We get around it by only checking the current core's IRQA, instead of doing the
// *correct* thing and checking both. This might break some games, but having a working BIOS
// is more important for now, until a proper fix can be uncovered.
//
// This problem might be caused by bad DMA timings in the IOP or a lack of proper IRQ
// handling by the Effects Processor. After those are implemented, let's hope it gets
// magically fixed?
//
// Note: This appears to affect DMA Writes only, so DMA Read DMAs are left intact (both core
// IRQAs are tested). Very few games use DMA reads tho, so it could just be a case of "works
// by the grace of not being used."
//
// Update: This hack is no longer needed when we don't do a core reset. Guess the null pc was in spu2 memory?
#define NO_BIOS_HACKFIX 1 // set to 1 to disable the hackfix
void V_Core::PlainDMAWrite(u16 *pMem, u32 size)
{
// Perform an alignment check.
// Not really important. Everything should work regardless,
// but it could be indicative of an emulation foopah elsewhere.
if (MsgToConsole()) {
// Don't need this anymore. Target may still be good to know though.
/*if((uptr)pMem & 15)
{
ConLog("* SPU2 DMA Write > Misaligned source. Core: %d IOP: %p TSA: 0x%x Size: 0x%x\n", Index, (void*)pMem, TSA, size);
}*/
if (TSA & 7) {
ConLog("* SPU2 DMA Write > Misaligned target. Core: %d IOP: %p TSA: 0x%x Size: 0x%x\n", Index, (void *)pMem, TSA, size);
}
}
if (Index == 0)
DMA4LogWrite(pMem, size << 1);
else
DMA7LogWrite(pMem, size << 1);
TSA &= 0xfffff;
u32 buff1end = TSA + size;
u32 buff2end = 0;
if (buff1end > 0x100000) {
buff2end = buff1end - 0x100000;
buff1end = 0x100000;
}
const int cacheIdxStart = TSA / pcm_WordsPerBlock;
const int cacheIdxEnd = (buff1end + pcm_WordsPerBlock - 1) / pcm_WordsPerBlock;
PcmCacheEntry *cacheLine = &pcm_cache_data[cacheIdxStart];
PcmCacheEntry &cacheEnd = pcm_cache_data[cacheIdxEnd];
do {
cacheLine->Validated = false;
cacheLine++;
} while (cacheLine != &cacheEnd);
//ConLog( "* SPU2-X: Cache Clear Range! TSA=0x%x, TDA=0x%x (low8=0x%x, high8=0x%x, len=0x%x)\n",
// TSA, buff1end, flagTSA, flagTDA, clearLen );
// First Branch needs cleared:
// It starts at TSA and goes to buff1end.
const u32 buff1size = (buff1end - TSA);
memcpy(GetMemPtr(TSA), pMem, buff1size * 2);
u32 TDA;
if (buff2end > 0) {
// second branch needs copied:
// It starts at the beginning of memory and moves forward to buff2end
// endpoint cache should be irrelevant, since it's almost certainly dynamic
// memory below 0x2800 (registers and such)
//const u32 endpt2 = (buff2end + roundUp) / indexer_scalar;
//memset( pcm_cache_flags, 0, endpt2 );
// Emulation Grayarea: Should addresses wrap around to zero, or wrap around to
// 0x2800? Hard to know for sure (almost no games depend on this)
memcpy(GetMemPtr(0), &pMem[buff1size], buff2end * 2);
TDA = (buff2end + 1) & 0xfffff;
// Flag interrupt? If IRQA occurs between start and dest, flag it.
// Important: Test both core IRQ settings for either DMA!
// Note: Because this buffer wraps, we use || instead of &&
#if NO_BIOS_HACKFIX
for (int i = 0; i < 2; i++) {
// Start is exclusive and end is inclusive... maybe? The end is documented to be inclusive,
// which suggests that memory access doesn't trigger interrupts, incrementing registers does
// (which would mean that if TSA=IRQA an interrupt doesn't fire... I guess?)
// Chaos Legion uses interrupt addresses set to the beginning of the two buffers in a double
// buffer scheme and sets LSA of one of the voices to the start of the opposite buffer.
// However it transfers to the same address right after setting IRQA, which by our previous
// understanding would trigger the interrupt early causing it to switch buffers again immediately
// and an interrupt never fires again, leaving the voices looping the same samples forever.
if (Cores[i].IRQEnable && (Cores[i].IRQA > TSA || Cores[i].IRQA <= TDA)) {
//ConLog("DMAwrite Core %d: IRQ Called (IRQ passed). IRQA = %x Cycles = %d\n", i, Cores[i].IRQA, Cycles );
SetIrqCall(i);
}
}
#else
if ((IRQEnable && (IRQA > TSA || IRQA <= TDA))
{
SetIrqCall(Index);
}
#endif
} else {
// Buffer doesn't wrap/overflow!
// Just set the TDA and check for an IRQ...
TDA = (buff1end + 1) & 0xfffff;
// Flag interrupt? If IRQA occurs between start and dest, flag it.
// Important: Test both core IRQ settings for either DMA!
#if NO_BIOS_HACKFIX
for (int i = 0; i < 2; i++) {
if (Cores[i].IRQEnable && (Cores[i].IRQA > TSA && Cores[i].IRQA <= TDA)) {
//ConLog("DMAwrite Core %d: IRQ Called (IRQ passed). IRQA = %x Cycles = %d\n", i, Cores[i].IRQA, Cycles );
SetIrqCall(i);
}
}
#else
if (IRQEnable && (IRQA > TSA) && (IRQA <= TDA)) {
SetIrqCall(Index);
}
#endif
}
TSA = TDA;
DMAICounter = size;
TADR = MADR + (size << 1);
}
void V_Core::DoDMAread(u16 *pMem, u32 size)
{
TSA &= 0xfffff;
u32 buff1end = TSA + size;
u32 buff2end = 0;
if (buff1end > 0x100000) {
buff2end = buff1end - 0x100000;
buff1end = 0x100000;
}
const u32 buff1size = (buff1end - TSA);
memcpy(pMem, GetMemPtr(TSA), buff1size * 2);
// Note on TSA's position after our copy finishes:
// IRQA should be measured by the end of the writepos+0x20. But the TDA
// should be written back at the precise endpoint of the xfer.
u32 TDA;
if (buff2end > 0) {
// second branch needs cleared:
// It starts at the beginning of memory and moves forward to buff2end
memcpy(&pMem[buff1size], GetMemPtr(0), buff2end * 2);
TDA = (buff2end + 0x20) & 0xfffff;
// Flag interrupt? If IRQA occurs between start and dest, flag it.
// Important: Test both core IRQ settings for either DMA!
// Note: Because this buffer wraps, we use || instead of &&
for (int i = 0; i < 2; i++) {
if (Cores[i].IRQEnable && (Cores[i].IRQA > TSA || Cores[i].IRQA <= TDA)) {
SetIrqCall(i);
}
}
} else {
// Buffer doesn't wrap/overflow!
// Just set the TDA and check for an IRQ...
TDA = (buff1end + 0x20) & 0xfffff;
// Flag interrupt? If IRQA occurs between start and dest, flag it.
// Important: Test both core IRQ settings for either DMA!
for (int i = 0; i < 2; i++) {
if (Cores[i].IRQEnable && (Cores[i].IRQA > TSA && Cores[i].IRQA <= TDA)) {
SetIrqCall(i);
}
}
}
TSA = TDA;
DMAICounter = size;
Regs.STATX &= ~0x80;
//Regs.ATTR |= 0x30;
TADR = MADR + (size << 1);
}
void V_Core::DoDMAwrite(u16 *pMem, u32 size)
{
DMAPtr = pMem;
if (size < 2) {
//if(dma7callback) dma7callback();
Regs.STATX &= ~0x80;
//Regs.ATTR |= 0x30;
DMAICounter = 1;
return;
}
if (IsDevBuild) {
DebugCores[Index].lastsize = size;
DebugCores[Index].dmaFlag = 2;
}
if (MsgToConsole()) {
if (TSA > 0xfffff) {
ConLog("* SPU2-X: Transfer Start Address out of bounds. TSA is %x\n", TSA);
}
}
TSA &= 0xfffff;
bool adma_enable = ((AutoDMACtrl & (Index + 1)) == (Index + 1));
if (adma_enable) {
TSA &= 0x1fff;
StartADMAWrite(pMem, size);
} else {
if (MsgDMA())
ConLog("* SPU2-X: DMA%c Transfer of %d bytes to %x (%02x %x %04x). IRQE = %d IRQA = %x \n",
GetDmaIndexChar(), size << 1, TSA, DMABits, AutoDMACtrl, (~Regs.ATTR) & 0x7fff,
Cores[0].IRQEnable, Cores[0].IRQA);
PlainDMAWrite(pMem, size);
}
Regs.STATX &= ~0x80;
//Regs.ATTR |= 0x30;
}

23
pcsx2/SPU2/Dma.h Normal file
View File

@ -0,0 +1,23 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
extern void DMALogOpen();
extern void DMA4LogWrite(void *lpData, u32 ulSize);
extern void DMA7LogWrite(void *lpData, u32 ulSize);
extern void DMALogClose();

175
pcsx2/SPU2/DplIIdecoder.cpp Normal file
View File

@ -0,0 +1,175 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "Global.h"
// FIXME Not yet used so let's comment it out.
/*static const u8 sLogTable[256] = {
0x00, 0x3C, 0x60, 0x78, 0x8C, 0x9C, 0xA8, 0xB4, 0xBE, 0xC8, 0xD0, 0xD8, 0xDE, 0xE4, 0xEA, 0xF0,
0xF6, 0xFA, 0xFE, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x16, 0x1A, 0x1E, 0x20, 0x24, 0x26, 0x2A, 0x2C,
0x2E, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3E, 0x40, 0x42, 0x44, 0x46, 0x48, 0x4A, 0x4C, 0x4E, 0x50,
0x50, 0x52, 0x54, 0x56, 0x58, 0x5A, 0x5A, 0x5C, 0x5E, 0x60, 0x60, 0x62, 0x64, 0x66, 0x66, 0x68,
0x6A, 0x6A, 0x6C, 0x6E, 0x6E, 0x70, 0x70, 0x72, 0x74, 0x74, 0x76, 0x76, 0x78, 0x7A, 0x7A, 0x7C,
0x7C, 0x7E, 0x7E, 0x80, 0x80, 0x82, 0x82, 0x84, 0x84, 0x86, 0x86, 0x88, 0x88, 0x8A, 0x8A, 0x8C,
0x8C, 0x8C, 0x8E, 0x8E, 0x90, 0x90, 0x92, 0x92, 0x92, 0x94, 0x94, 0x96, 0x96, 0x96, 0x98, 0x98,
0x9A, 0x9A, 0x9A, 0x9C, 0x9C, 0x9C, 0x9E, 0x9E, 0xA0, 0xA0, 0xA0, 0xA2, 0xA2, 0xA2, 0xA4, 0xA4,
0xA4, 0xA6, 0xA6, 0xA6, 0xA8, 0xA8, 0xA8, 0xAA, 0xAA, 0xAA, 0xAC, 0xAC, 0xAC, 0xAC, 0xAE, 0xAE,
0xAE, 0xB0, 0xB0, 0xB0, 0xB2, 0xB2, 0xB2, 0xB2, 0xB4, 0xB4, 0xB4, 0xB6, 0xB6, 0xB6, 0xB6, 0xB8,
0xB8, 0xB8, 0xB8, 0xBA, 0xBA, 0xBA, 0xBC, 0xBC, 0xBC, 0xBC, 0xBE, 0xBE, 0xBE, 0xBE, 0xC0, 0xC0,
0xC0, 0xC0, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC4, 0xC4, 0xC4, 0xC4, 0xC6, 0xC6, 0xC6, 0xC6, 0xC8,
0xC8, 0xC8, 0xC8, 0xC8, 0xCA, 0xCA, 0xCA, 0xCA, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCE, 0xCE, 0xCE,
0xCE, 0xCE, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD4, 0xD4, 0xD4, 0xD4,
0xD4, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xDA, 0xDA, 0xDA, 0xDA,
0xDA, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xE0, 0xE0, 0xE0,
};*/
static float Gfl = 0, Gfr = 0;
static float LMax = 0, RMax = 0;
static float AccL = 0;
static float AccR = 0;
const float Scale = 4294967296.0f; // tweak this value to change the overall output volume
const float GainL = 0.80f * Scale;
const float GainR = 0.80f * Scale;
const float GainC = 0.75f * Scale;
const float GainSL = 0.90f * Scale;
const float GainSR = 0.90f * Scale;
const float GainLFE = 0.90f * Scale;
const float AddCLR = 0.20f * Scale; // Stereo expansion
extern void ResetDplIIDecoder()
{
Gfl = 0;
Gfr = 0;
LMax = 0;
RMax = 0;
AccL = 0;
AccR = 0;
}
void ProcessDplIISample32(const StereoOut32 &src, Stereo51Out32DplII *s)
{
float IL = src.Left / (float)(1 << (SndOutVolumeShift + 16));
float IR = src.Right / (float)(1 << (SndOutVolumeShift + 16));
// Calculate center channel and LFE
float C = (IL + IR) * 0.5f;
float SUB = C; // no need to lowpass, the speaker amplifier should take care of it
float L = IL - C; // Effective L/R data
float R = IR - C;
// Peak L/R
float PL = std::abs(L);
float PR = std::abs(R);
AccL += (PL - AccL) * 0.1f;
AccR += (PR - AccR) * 0.1f;
// Calculate power balance
float Balance = (AccR - AccL); // -1 .. 1
// If the power levels are different, then the audio is meant for the front speakers
float Frontness = std::abs(Balance);
float Rearness = 1 - Frontness; // And the other way around
// Equalize the power levels for L/R
float B = std::min(0.9f, std::max(-0.9f, Balance));
float VL = L / (1 - B); // if B>0, it means R>L, so increase L, else decrease L
float VR = R / (1 + B); // vice-versa
// 1.73+1.22 = 2.94; 2.94 = 0.34 = 0.9996; Close enough.
// The range for VL/VR is approximately 0..1,
// But in the cases where VL/VR are > 0.5, Rearness is 0 so it should never overflow.
const float RearScale = 0.34f * 2;
float SL = (VR * 1.73f - VL * 1.22f) * RearScale * Rearness;
float SR = (VR * 1.22f - VL * 1.73f) * RearScale * Rearness;
// Possible experiment: Play with stereo expension levels on rear
// Adjust the volume of the front speakers based on what we calculated above
L *= Frontness;
R *= Frontness;
s32 CX = (s32)(C * AddCLR);
s->Left = (s32)(L * GainL) + CX;
s->Right = (s32)(R * GainR) + CX;
s->Center = (s32)(C * GainC);
s->LFE = (s32)(SUB * GainLFE);
s->LeftBack = (s32)(SL * GainSL);
s->RightBack = (s32)(SR * GainSR);
}
void ProcessDplIISample16(const StereoOut32 &src, Stereo51Out16DplII *s)
{
Stereo51Out32DplII ss;
ProcessDplIISample32(src, &ss);
s->Left = ss.Left >> 16;
s->Right = ss.Right >> 16;
s->Center = ss.Center >> 16;
s->LFE = ss.LFE >> 16;
s->LeftBack = ss.LeftBack >> 16;
s->RightBack = ss.RightBack >> 16;
}
void ProcessDplSample32(const StereoOut32 &src, Stereo51Out32Dpl *s)
{
float ValL = src.Left / (float)(1 << (SndOutVolumeShift + 16));
float ValR = src.Right / (float)(1 << (SndOutVolumeShift + 16));
float C = (ValL + ValR) * 0.5f; //+15.8
float S = (ValL - ValR) * 0.5f;
float L = ValL - C; //+15.8
float R = ValR - C;
float SUB = C;
s32 CX = (s32)(C * AddCLR); // +15.16
s->Left = (s32)(L * GainL) + CX; // +15.16 = +31, can grow to +32 if (GainL + AddCLR)>255
s->Right = (s32)(R * GainR) + CX;
s->Center = (s32)(C * GainC); // +15.16 = +31
s->LFE = (s32)(SUB * GainLFE);
s->LeftBack = (s32)(S * GainSL);
s->RightBack = (s32)(S * GainSR);
}
void ProcessDplSample16(const StereoOut32 &src, Stereo51Out16Dpl *s)
{
Stereo51Out32Dpl ss;
ProcessDplSample32(src, &ss);
s->Left = ss.Left >> 16;
s->Right = ss.Right >> 16;
s->Center = ss.Center >> 16;
s->LFE = ss.LFE >> 16;
s->LeftBack = ss.LeftBack >> 16;
s->RightBack = ss.RightBack >> 16;
}

108
pcsx2/SPU2/Global.h Normal file
View File

@ -0,0 +1,108 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _SPU2X_GLOBAL_H_
#define _SPU2X_GLOBAL_H_
#define NOMINMAX
extern bool psxmode;
struct StereoOut16;
struct StereoOut32;
struct StereoOutFloat;
struct V_Core;
namespace soundtouch
{
class SoundTouch;
}
#include <assert.h>
#include <cstdlib>
#include <cstdio>
#include <cstdarg>
#include <cmath>
#include <ctime>
#include <stdexcept>
#include "Utilities/Dependencies.h"
#include "Pcsx2Defs.h"
#include "Pcsx2Types.h"
namespace VersionInfo
{
static const u8 Release = 2;
static const u8 Revision = 0; // increase that with each version
}
//////////////////////////////////////////////////////////////////////////
// Override Win32 min/max macros with the STL's type safe and macro
// free varieties (much safer!)
#undef min
#undef max
template <typename T>
static __forceinline void Clampify(T &src, T min, T max)
{
src = std::min(std::max(src, min), max);
}
template <typename T>
static __forceinline T GetClamped(T src, T min, T max)
{
return std::min(std::max(src, min), max);
}
#ifdef __WXMAC__
#include "PS2Eext.h"
#else
extern void SysMessage(const char *fmt, ...);
#endif
extern void SysMessage(const wchar_t *fmt, ...);
//////////////////////////////////////////////////////////////
// Dev / Debug conditionals --
// Consts for using if() statements instead of uglier #ifdef macros.
// Abbreviated macros for dev/debug only consoles and msgboxes.
#ifdef PCSX2_DEVBUILD
#define DevMsg MsgBox
#else
#define DevMsg
#endif
#ifdef PCSX2_DEVBUILD
#define SPU2_LOG
#endif
// Uncomment to enable debug keys on numpad (0 to 5)
//#define DEBUG_KEYS
#include "Utilities/Exceptions.h"
#include "Utilities/SafeArray.h"
#include "defs.h"
#include "regs.h"
#include "Config.h"
#include "Debug.h"
#include "SndOut.h"
#endif

254
pcsx2/SPU2/Linux/Alsa.cpp Normal file
View File

@ -0,0 +1,254 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
// Adapted from ZeroSPU2 code by Zerofrog. Heavily modified by Arcum42.
#ifdef __linux__
#include <alsa/asoundlib.h>
#include "Global.h"
#include "Alsa.h"
#include "SndOut.h"
// Does not work, except as effectively a null plugin.
class AlsaMod : public SndOutModule
{
protected:
static const int PacketsPerBuffer = 1; // increase this if ALSA can't keep up with 512-sample packets
static const int MAX_BUFFER_COUNT = 4;
static const int NumBuffers = 4; // TODO: this should be configurable someday -- lower values reduce latency.
unsigned int pspeed;
snd_pcm_t *handle;
snd_pcm_uframes_t buffer_size;
snd_async_handler_t *pcm_callback;
uint period_time;
uint buffer_time;
protected:
// Invoked by the static ExternalCallback method below.
void _InternalCallback()
{
snd_pcm_sframes_t avail;
fprintf(stderr, "* SPU2-X:Iz in your internal callback.\n");
avail = snd_pcm_avail_update(handle);
while (avail >= (int)period_time) {
StereoOut16 buff[PacketsPerBuffer * SndOutPacketSize];
StereoOut16 *p1 = buff;
for (int p = 0; p < PacketsPerBuffer; p++, p1 += SndOutPacketSize)
SndBuffer::ReadSamples(p1);
snd_pcm_writei(handle, buff, period_time);
avail = snd_pcm_avail_update(handle);
}
}
// Preps and invokes the _InternalCallback above. This provides a cdecl-compliant
// entry point for our C++ified object state. :)
static void ExternalCallback(snd_async_handler_t *pcm_call)
{
fprintf(stderr, "* SPU2-X:Iz in your external callback.\n");
AlsaMod *data = (AlsaMod *)snd_async_handler_get_callback_private(pcm_call);
pxAssume(data != NULL);
//pxAssume( data->handle == snd_async_handler_get_pcm(pcm_call) );
// Not sure if we just need an assert, or something like this:
if (data->handle != snd_async_handler_get_pcm(pcm_call)) {
fprintf(stderr, "* SPU2-X: Failed to handle sound.\n");
return;
}
data->_InternalCallback();
}
public:
s32 Init()
{
//fprintf(stderr,"* SPU2-X: Initing Alsa\n");
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
snd_pcm_status_t *status;
int pchannels = 2;
snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
handle = NULL;
pcm_callback = NULL;
pspeed = SAMPLE_RATE;
// buffer time and period time are in microseconds...
// (don't simplify the equation below -- it'll just cause integer rounding errors.
period_time = (SndOutPacketSize * 1000) / (SampleRate / 1000);
buffer_time = period_time * NumBuffers;
int err;
err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC /*| SND_PCM_NONBLOCK*/);
if (err < 0) {
fprintf(stderr, "Audio open error: %s\n", snd_strerror(err));
return -1;
}
err = snd_pcm_nonblock(handle, 0);
if (err < 0) {
fprintf(stderr, "Can't set blocking mode: %s\n", snd_strerror(err));
return -1;
}
snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_sw_params_alloca(&swparams);
err = snd_pcm_hw_params_any(handle, hwparams);
if (err < 0) {
fprintf(stderr, "Broken configuration for this PCM: %s\n", snd_strerror(err));
return -1;
}
err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0) {
fprintf(stderr, "Access type not available: %s\n", snd_strerror(err));
return -1;
}
err = snd_pcm_hw_params_set_format(handle, hwparams, format);
if (err < 0) {
fprintf(stderr, "Sample format not available: %s\n", snd_strerror(err));
return -1;
}
err = snd_pcm_hw_params_set_channels(handle, hwparams, pchannels);
if (err < 0) {
fprintf(stderr, "Channels count not available: %s\n", snd_strerror(err));
return -1;
}
err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &pspeed, 0);
if (err < 0) {
fprintf(stderr, "Rate not available: %s\n", snd_strerror(err));
return -1;
}
err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, 0);
if (err < 0) {
fprintf(stderr, "Buffer time error: %s\n", snd_strerror(err));
return -1;
}
err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, 0);
if (err < 0) {
fprintf(stderr, "Period time error: %s\n", snd_strerror(err));
return -1;
}
err = snd_pcm_hw_params(handle, hwparams);
if (err < 0) {
fprintf(stderr, "Unable to install hw params: %s\n", snd_strerror(err));
return -1;
}
snd_pcm_status_alloca(&status);
err = snd_pcm_status(handle, status);
if (err < 0) {
fprintf(stderr, "Unable to get status: %s\n", snd_strerror(err));
return -1;
}
// Bind our asynchronous callback magic:
if (handle == NULL)
fprintf(stderr, "No handle.");
//fprintf(stderr,"* SPU2-X:Iz setting your internal callback.\n");
// The external handler never seems to get called after this.
snd_async_add_pcm_handler(&pcm_callback, handle, ExternalCallback, this);
err = snd_pcm_start(handle);
if (err < 0) {
fprintf(stderr, "Pcm start failed: %s\n", snd_strerror(err));
return -1;
}
// Diagnostic code:
//buffer_size = snd_pcm_status_get_avail(status);
//fprintf(stderr,"All set up.\n");
return 0;
}
void Close()
{
//fprintf(stderr,"* SPU2-X: Closing Alsa\n");
if (handle == NULL)
return;
snd_pcm_drop(handle);
snd_pcm_close(handle);
handle = NULL;
}
virtual void Configure(uptr parent)
{
}
virtual bool Is51Out() const { return false; }
s32 Test() const
{
return 0;
}
int GetEmptySampleCount()
{
if (handle == NULL) {
fprintf(stderr, "Handle is NULL!\n");
return 0;
}
// Returns the amount of free buffer space, in samples.
int l = snd_pcm_avail_update(handle);
if (l < 0)
return 0;
return (l / 1000) * (SampleRate / 1000);
}
const wchar_t *GetIdent() const
{
return L"Alsa";
}
const wchar_t *GetLongName() const
{
return L"Alsa";
}
void ReadSettings()
{
}
void SetApiSettings(wxString api)
{
}
void WriteSettings() const
{
}
} static Alsa;
SndOutModule *AlsaOut = &Alsa;
#endif

42
pcsx2/SPU2/Linux/Alsa.h Normal file
View File

@ -0,0 +1,42 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LINUX_H__
#define __LINUX_H__
#include <assert.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/soundcard.h>
#include <unistd.h>
#define SAMPLE_RATE 48000L
// Pull in from Alsa.cpp
extern int AlsaSetupSound();
extern void AlsaRemoveSound();
extern int AlsaSoundGetBytesBuffered();
extern void AlsaSoundFeedVoiceData(unsigned char *pSound, long lBytes);
extern int SetupSound();
extern void RemoveSound();
extern int SoundGetBytesBuffered();
extern void SoundFeedVoiceData(unsigned char *pSound, long lBytes);
#endif // __LINUX_H__

View File

@ -0,0 +1,108 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Dialogs.h"
#include <wx/fileconf.h>
wxFileConfig *spuConfig = nullptr;
wxString path(L"~/.pcsx2/inis/spu2-x.ini");
bool pathSet = false;
void initIni()
{
if (spuConfig == nullptr)
spuConfig = new wxFileConfig(L"", L"", path, L"", wxCONFIG_USE_LOCAL_FILE);
}
void setIni(const wchar_t *Section)
{
initIni();
spuConfig->SetPath(wxsFormat(L"/%s", Section));
}
void CfgSetSettingsDir(const char *dir)
{
FileLog("CfgSetSettingsDir(%s)\n", dir);
path = wxString::FromUTF8(dir) + L"/spu2-x.ini";
pathSet = true;
}
void CfgWriteBool(const wchar_t *Section, const wchar_t *Name, bool Value)
{
setIni(Section);
spuConfig->Write(Name, Value);
}
void CfgWriteInt(const wchar_t *Section, const wchar_t *Name, int Value)
{
setIni(Section);
spuConfig->Write(Name, Value);
}
void CfgWriteFloat(const wchar_t *Section, const wchar_t *Name, float Value)
{
setIni(Section);
spuConfig->Write(Name, (double)Value);
}
void CfgWriteStr(const wchar_t *Section, const wchar_t *Name, const wxString &Data)
{
setIni(Section);
spuConfig->Write(Name, Data);
}
bool CfgReadBool(const wchar_t *Section, const wchar_t *Name, bool Default)
{
bool ret;
setIni(Section);
spuConfig->Read(Name, &ret, Default);
return ret;
}
int CfgReadInt(const wchar_t *Section, const wchar_t *Name, int Default)
{
int ret;
setIni(Section);
spuConfig->Read(Name, &ret, Default);
return ret;
}
float CfgReadFloat(const wchar_t *Section, const wchar_t *Name, float Default)
{
double ret;
setIni(Section);
spuConfig->Read(Name, &ret, (double)Default);
return (float)ret;
}
void CfgReadStr(const wchar_t *Section, const wchar_t *Name, wchar_t *Data, int DataSize, const wchar_t *Default)
{
setIni(Section);
wcscpy(Data, spuConfig->Read(Name, Default).wc_str());
}
void CfgReadStr(const wchar_t *Section, const wchar_t *Name, wxString &Data, const wchar_t *Default)
{
setIni(Section);
Data = spuConfig->Read(Name, Default);
}

233
pcsx2/SPU2/Linux/Config.cpp Normal file
View File

@ -0,0 +1,233 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "Dialogs.h"
#include "Config.h"
#if defined(__unix__) || defined(__APPLE__)
#include <SDL.h>
#include <SDL_audio.h>
#include "wx/wxConfig.h"
#endif
int AutoDMAPlayRate[2] = {0, 0};
// Default settings.
// MIXING
int Interpolation = 4;
/* values:
0: no interpolation (use nearest)
1. linear interpolation
2. cubic interpolation
3. hermite interpolation
4. catmull-rom interpolation
*/
bool EffectsDisabled = false;
float FinalVolume; // global
bool AdvancedVolumeControl;
float VolumeAdjustFLdb; // decibels settings, cos audiophiles love that
float VolumeAdjustCdb;
float VolumeAdjustFRdb;
float VolumeAdjustBLdb;
float VolumeAdjustBRdb;
float VolumeAdjustSLdb;
float VolumeAdjustSRdb;
float VolumeAdjustLFEdb;
float VolumeAdjustFL; // linear coefs calculated from decibels,
float VolumeAdjustC;
float VolumeAdjustFR;
float VolumeAdjustBL;
float VolumeAdjustBR;
float VolumeAdjustSL;
float VolumeAdjustSR;
float VolumeAdjustLFE;
unsigned int delayCycles;
bool postprocess_filter_enabled = true;
bool postprocess_filter_dealias = false;
bool _visual_debug_enabled = false; // windows only feature
// OUTPUT
u32 OutputModule = 0;
int SndOutLatencyMS = 300;
int SynchMode = 0; // Time Stretch, Async or Disabled
#ifdef SPU2X_PORTAUDIO
u32 OutputAPI = 0;
#endif
u32 SdlOutputAPI = 0;
int numSpeakers = 0;
int dplLevel = 0;
bool temp_debug_state;
/*****************************************************************************/
void ReadSettings()
{
// For some reason this can be called before we know what ini file we're writing to.
// Lets not try to read it if that happens.
if (!pathSet) {
FileLog("Read called without the path set.\n");
return;
}
Interpolation = CfgReadInt(L"MIXING", L"Interpolation", 4);
EffectsDisabled = CfgReadBool(L"MIXING", L"Disable_Effects", false);
postprocess_filter_dealias = CfgReadBool(L"MIXING", L"DealiasFilter", false);
FinalVolume = ((float)CfgReadInt(L"MIXING", L"FinalVolume", 100)) / 100;
if (FinalVolume > 1.0f)
FinalVolume = 1.0f;
AdvancedVolumeControl = CfgReadBool(L"MIXING", L"AdvancedVolumeControl", false);
VolumeAdjustCdb = CfgReadFloat(L"MIXING", L"VolumeAdjustC(dB)", 0);
VolumeAdjustFLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFL(dB)", 0);
VolumeAdjustFRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFR(dB)", 0);
VolumeAdjustBLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBL(dB)", 0);
VolumeAdjustBRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBR(dB)", 0);
VolumeAdjustSLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSL(dB)", 0);
VolumeAdjustSRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSR(dB)", 0);
VolumeAdjustLFEdb = CfgReadFloat(L"MIXING", L"VolumeAdjustLFE(dB)", 0);
VolumeAdjustC = powf(10, VolumeAdjustCdb / 10);
VolumeAdjustFL = powf(10, VolumeAdjustFLdb / 10);
VolumeAdjustFR = powf(10, VolumeAdjustFRdb / 10);
VolumeAdjustBL = powf(10, VolumeAdjustBLdb / 10);
VolumeAdjustBR = powf(10, VolumeAdjustBRdb / 10);
VolumeAdjustSL = powf(10, VolumeAdjustSLdb / 10);
VolumeAdjustSR = powf(10, VolumeAdjustSRdb / 10);
VolumeAdjustLFE = powf(10, VolumeAdjustLFEdb / 10);
delayCycles = CfgReadInt(L"DEBUG", L"DelayCycles", 4);
wxString temp;
#if SDL_MAJOR_VERSION >= 2 || !defined(SPU2X_PORTAUDIO)
CfgReadStr(L"OUTPUT", L"Output_Module", temp, SDLOut->GetIdent());
#else
CfgReadStr(L"OUTPUT", L"Output_Module", temp, PortaudioOut->GetIdent());
#endif
OutputModule = FindOutputModuleById(temp.c_str()); // find the driver index of this module
// find current API
#ifdef SPU2X_PORTAUDIO
#ifdef __linux__
CfgReadStr(L"PORTAUDIO", L"HostApi", temp, L"ALSA");
if (temp == L"OSS")
OutputAPI = 1;
else if (temp == L"JACK")
OutputAPI = 2;
else // L"ALSA"
OutputAPI = 0;
#else
CfgReadStr(L"PORTAUDIO", L"HostApi", temp, L"OSS");
OutputAPI = 0; // L"OSS"
#endif
#endif
#if defined(__unix__) || defined(__APPLE__)
CfgReadStr(L"SDL", L"HostApi", temp, L"pulseaudio");
SdlOutputAPI = 0;
#if SDL_MAJOR_VERSION >= 2
// YES It sucks ...
for (int i = 0; i < SDL_GetNumAudioDrivers(); ++i) {
if (!temp.Cmp(wxString(SDL_GetAudioDriver(i), wxConvUTF8)))
SdlOutputAPI = i;
}
#endif
#endif
SndOutLatencyMS = CfgReadInt(L"OUTPUT", L"Latency", 300);
SynchMode = CfgReadInt(L"OUTPUT", L"Synch_Mode", 0);
numSpeakers = CfgReadInt(L"OUTPUT", L"SpeakerConfiguration", 0);
#ifdef SPU2X_PORTAUDIO
PortaudioOut->ReadSettings();
#endif
#if defined(__unix__) || defined(__APPLE__)
SDLOut->ReadSettings();
#endif
SoundtouchCfg::ReadSettings();
DebugConfig::ReadSettings();
// Sanity Checks
// -------------
Clampify(SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX);
if (mods[OutputModule] == nullptr) {
fwprintf(stderr, L"* SPU2-X: Unknown output module '%s' specified in configuration file.\n", temp.wc_str());
fprintf(stderr, "* SPU2-X: Defaulting to SDL (%S).\n", SDLOut->GetIdent());
OutputModule = FindOutputModuleById(SDLOut->GetIdent());
}
WriteSettings();
spuConfig->Flush();
}
/*****************************************************************************/
void WriteSettings()
{
if (!pathSet) {
FileLog("Write called without the path set.\n");
return;
}
CfgWriteInt(L"MIXING", L"Interpolation", Interpolation);
CfgWriteBool(L"MIXING", L"Disable_Effects", EffectsDisabled);
CfgWriteBool(L"MIXING", L"DealiasFilter", postprocess_filter_dealias);
CfgWriteInt(L"MIXING", L"FinalVolume", (int)(FinalVolume * 100 + 0.5f));
CfgWriteBool(L"MIXING", L"AdvancedVolumeControl", AdvancedVolumeControl);
CfgWriteFloat(L"MIXING", L"VolumeAdjustC(dB)", VolumeAdjustCdb);
CfgWriteFloat(L"MIXING", L"VolumeAdjustFL(dB)", VolumeAdjustFLdb);
CfgWriteFloat(L"MIXING", L"VolumeAdjustFR(dB)", VolumeAdjustFRdb);
CfgWriteFloat(L"MIXING", L"VolumeAdjustBL(dB)", VolumeAdjustBLdb);
CfgWriteFloat(L"MIXING", L"VolumeAdjustBR(dB)", VolumeAdjustBRdb);
CfgWriteFloat(L"MIXING", L"VolumeAdjustSL(dB)", VolumeAdjustSLdb);
CfgWriteFloat(L"MIXING", L"VolumeAdjustSR(dB)", VolumeAdjustSRdb);
CfgWriteFloat(L"MIXING", L"VolumeAdjustLFE(dB)", VolumeAdjustLFEdb);
CfgWriteStr(L"OUTPUT", L"Output_Module", mods[OutputModule]->GetIdent());
CfgWriteInt(L"OUTPUT", L"Latency", SndOutLatencyMS);
CfgWriteInt(L"OUTPUT", L"Synch_Mode", SynchMode);
CfgWriteInt(L"OUTPUT", L"SpeakerConfiguration", numSpeakers);
CfgWriteInt(L"DEBUG", L"DelayCycles", delayCycles);
#ifdef SPU2X_PORTAUDIO
PortaudioOut->WriteSettings();
#endif
#if defined(__unix__) || defined(__APPLE__)
SDLOut->WriteSettings();
#endif
SoundtouchCfg::WriteSettings();
DebugConfig::WriteSettings();
}
void configure()
{
auto *dialog = new Dialog;
initIni();
ReadSettings();
dialog->Display();
WriteSettings();
delete spuConfig;
spuConfig = nullptr;
wxDELETE(dialog);
}

127
pcsx2/SPU2/Linux/Config.h Normal file
View File

@ -0,0 +1,127 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONFIG_H_INCLUDED
#define CONFIG_H_INCLUDED
#include <string>
#include <wx/fileconf.h>
extern bool DebugEnabled;
extern bool _MsgToConsole;
extern bool _MsgKeyOnOff;
extern bool _MsgVoiceOff;
extern bool _MsgDMA;
extern bool _MsgAutoDMA;
extern bool _MsgOverruns;
extern bool _MsgCache;
extern bool _AccessLog;
extern bool _DMALog;
extern bool _WaveLog;
extern bool _CoresDump;
extern bool _MemDump;
extern bool _RegDump;
extern bool _visual_debug_enabled;
/*static __forceinline bool MsgToConsole() { return _MsgToConsole & DebugEnabled; }
static __forceinline bool MsgKeyOnOff() { return _MsgKeyOnOff & MsgToConsole(); }
static __forceinline bool MsgVoiceOff() { return _MsgVoiceOff & MsgToConsole(); }
static __forceinline bool MsgDMA() { return _MsgDMA & MsgToConsole(); }
static __forceinline bool MsgAutoDMA() { return _MsgAutoDMA & MsgDMA(); }
static __forceinline bool MsgOverruns() { return _MsgOverruns & MsgToConsole(); }
static __forceinline bool MsgCache() { return _MsgCache & MsgToConsole(); }
static __forceinline bool AccessLog() { return _AccessLog & DebugEnabled; }
static __forceinline bool DMALog() { return _DMALog & DebugEnabled; }
static __forceinline bool WaveLog() { return _WaveLog & DebugEnabled; }
static __forceinline bool CoresDump() { return _CoresDump & DebugEnabled; }
static __forceinline bool MemDump() { return _MemDump & DebugEnabled; }
static __forceinline bool RegDump() { return _RegDump & DebugEnabled; }*/
//extern wchar_t AccessLogFileName[255];
//extern wchar_t WaveLogFileName[255];
//extern wchar_t DMA4LogFileName[255];
//extern wchar_t DMA7LogFileName[255];
//extern wchar_t CoresDumpFileName[255];
//extern wchar_t MemDumpFileName[255];
//extern wchar_t RegDumpFileName[255];
extern int Interpolation;
extern bool EffectsDisabled;
extern float FinalVolume;
extern bool postprocess_filter_enabled;
extern bool postprocess_filter_dealias;
extern int AutoDMAPlayRate[2];
extern u32 OutputModule;
extern int SndOutLatencyMS;
extern wchar_t dspPlugin[];
extern int dspPluginModule;
extern bool dspPluginEnabled;
extern int SynchMode;
#ifdef SPU2X_PORTAUDIO
extern u32 OutputAPI;
#endif
extern u32 SdlOutputAPI;
#ifdef PCSX2_DEVBUILD
const int LATENCY_MAX = 3000;
#else
const int LATENCY_MAX = 750;
#endif
const int LATENCY_MIN = 3;
const int LATENCY_MIN_TIMESTRETCH = 15;
namespace SoundtouchCfg
{
extern const int SequenceLen_Min;
extern const int SequenceLen_Max;
extern const int SeekWindow_Min;
extern const int SeekWindow_Max;
extern const int Overlap_Min;
extern const int Overlap_Max;
extern int SequenceLenMS;
extern int SeekWindowMS;
extern int OverlapMS;
void ReadSettings();
void WriteSettings();
}; // namespace SoundtouchCfg
void ReadSettings();
void WriteSettings();
void DisplayDialog();
void configure();
extern wxFileConfig *spuConfig;
extern bool pathSet;
extern void initIni();
#endif // CONFIG_H_INCLUDED

View File

@ -0,0 +1,157 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "Dialogs.h"
#include "Config.h"
#include "Utilities/Path.h"
bool DebugEnabled = false;
bool _MsgToConsole = false;
bool _MsgKeyOnOff = false;
bool _MsgVoiceOff = false;
bool _MsgDMA = false;
bool _MsgAutoDMA = false;
bool _MsgOverruns = false;
bool _MsgCache = false;
bool _AccessLog = false;
bool _DMALog = false;
bool _WaveLog = false;
bool _CoresDump = false;
bool _MemDump = false;
bool _RegDump = false;
// this is set true if PCSX2 invokes the SetLogDir callback, which tells SPU2-X to use that over
// the configured crap in the ini file.
static bool LogLocationSetByPcsx2 = false;
static wxDirName LogsFolder;
static wxDirName DumpsFolder;
wxString AccessLogFileName;
wxString WaveLogFileName;
wxString DMA4LogFileName;
wxString DMA7LogFileName;
wxString CoresDumpFileName;
wxString MemDumpFileName;
wxString RegDumpFileName;
void CfgSetLogDir(const char *dir)
{
LogsFolder = (dir == NULL) ? wxString(L"logs") : fromUTF8(dir);
DumpsFolder = (dir == NULL) ? wxString(L"logs") : fromUTF8(dir);
LogLocationSetByPcsx2 = (dir != NULL);
}
FILE *OpenBinaryLog(const wxString &logfile)
{
return wxFopen(Path::Combine(LogsFolder, logfile), L"wb");
}
FILE *OpenLog(const wxString &logfile)
{
return wxFopen(Path::Combine(LogsFolder, logfile), L"w");
}
FILE *OpenDump(const wxString &logfile)
{
return wxFopen(Path::Combine(DumpsFolder, logfile), L"w");
}
namespace DebugConfig
{
static const wchar_t *Section = L"DEBUG";
static void set_default_filenames()
{
AccessLogFileName = L"SPU2Log.txt";
WaveLogFileName = L"SPU2log.wav";
DMA4LogFileName = L"SPU2dma4.dat";
DMA7LogFileName = L"SPU2dma7.dat";
CoresDumpFileName = L"SPU2Cores.txt";
MemDumpFileName = L"SPU2mem.dat";
RegDumpFileName = L"SPU2regs.dat";
}
void ReadSettings()
{
DebugEnabled = CfgReadBool(Section, L"Global_Enable", 0);
_MsgToConsole = CfgReadBool(Section, L"Show_Messages", 0);
_MsgKeyOnOff = CfgReadBool(Section, L"Show_Messages_Key_On_Off", 0);
_MsgVoiceOff = CfgReadBool(Section, L"Show_Messages_Voice_Off", 0);
_MsgDMA = CfgReadBool(Section, L"Show_Messages_DMA_Transfer", 0);
_MsgAutoDMA = CfgReadBool(Section, L"Show_Messages_AutoDMA", 0);
_MsgOverruns = CfgReadBool(Section, L"Show_Messages_Overruns", 0);
_MsgCache = CfgReadBool(Section, L"Show_Messages_CacheStats", 0);
_AccessLog = CfgReadBool(Section, L"Log_Register_Access", 0);
_DMALog = CfgReadBool(Section, L"Log_DMA_Transfers", 0);
_WaveLog = CfgReadBool(Section, L"Log_WAVE_Output", 0);
_CoresDump = CfgReadBool(Section, L"Dump_Info", 0);
_MemDump = CfgReadBool(Section, L"Dump_Memory", 0);
_RegDump = CfgReadBool(Section, L"Dump_Regs", 0);
set_default_filenames();
CfgReadStr(Section, L"Access_Log_Filename", AccessLogFileName, L"logs/SPU2Log.txt");
CfgReadStr(Section, L"WaveLog_Filename", WaveLogFileName, L"logs/SPU2log.wav");
CfgReadStr(Section, L"DMA4Log_Filename", DMA4LogFileName, L"logs/SPU2dma4.dat");
CfgReadStr(Section, L"DMA7Log_Filename", DMA7LogFileName, L"logs/SPU2dma7.dat");
CfgReadStr(Section, L"Info_Dump_Filename", CoresDumpFileName, L"logs/SPU2Cores.txt");
CfgReadStr(Section, L"Mem_Dump_Filename", MemDumpFileName, L"logs/SPU2mem.dat");
CfgReadStr(Section, L"Reg_Dump_Filename", RegDumpFileName, L"logs/SPU2regs.dat");
}
void WriteSettings()
{
CfgWriteBool(Section, L"Global_Enable", DebugEnabled);
CfgWriteBool(Section, L"Show_Messages", _MsgToConsole);
CfgWriteBool(Section, L"Show_Messages_Key_On_Off", _MsgKeyOnOff);
CfgWriteBool(Section, L"Show_Messages_Voice_Off", _MsgVoiceOff);
CfgWriteBool(Section, L"Show_Messages_DMA_Transfer", _MsgDMA);
CfgWriteBool(Section, L"Show_Messages_AutoDMA", _MsgAutoDMA);
CfgWriteBool(Section, L"Show_Messages_Overruns", _MsgOverruns);
CfgWriteBool(Section, L"Show_Messages_CacheStats", _MsgCache);
CfgWriteBool(Section, L"Log_Register_Access", _AccessLog);
CfgWriteBool(Section, L"Log_DMA_Transfers", _DMALog);
CfgWriteBool(Section, L"Log_WAVE_Output", _WaveLog);
CfgWriteBool(Section, L"Dump_Info", _CoresDump);
CfgWriteBool(Section, L"Dump_Memory", _MemDump);
CfgWriteBool(Section, L"Dump_Regs", _RegDump);
set_default_filenames();
CfgWriteStr(Section, L"Access_Log_Filename", AccessLogFileName);
CfgWriteStr(Section, L"WaveLog_Filename", WaveLogFileName);
CfgWriteStr(Section, L"DMA4Log_Filename", DMA4LogFileName);
CfgWriteStr(Section, L"DMA7Log_Filename", DMA7LogFileName);
CfgWriteStr(Section, L"Info_Dump_Filename", CoresDumpFileName);
CfgWriteStr(Section, L"Mem_Dump_Filename", MemDumpFileName);
CfgWriteStr(Section, L"Reg_Dump_Filename", RegDumpFileName);
}
} // namespace DebugConfig

View File

@ -0,0 +1,74 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with this library; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
#include "Global.h"
#include "Dialogs.h"
#include "Config.h"
#include "soundtouch/SoundTouch.h"
namespace SoundtouchCfg
{
// Timestretch Slider Bounds, Min/Max
const int SequenceLen_Min = 20;
const int SequenceLen_Max = 100;
const int SeekWindow_Min = 10;
const int SeekWindow_Max = 30;
const int Overlap_Min = 5;
const int Overlap_Max = 15;
int SequenceLenMS = 30;
int SeekWindowMS = 20;
int OverlapMS = 10;
static void ClampValues()
{
Clampify(SequenceLenMS, SequenceLen_Min, SequenceLen_Max);
Clampify(SeekWindowMS, SeekWindow_Min, SeekWindow_Max);
Clampify(OverlapMS, Overlap_Min, Overlap_Max);
}
void ApplySettings(soundtouch::SoundTouch &sndtouch)
{
sndtouch.setSetting(SETTING_SEQUENCE_MS, SequenceLenMS);
sndtouch.setSetting(SETTING_SEEKWINDOW_MS, SeekWindowMS);
sndtouch.setSetting(SETTING_OVERLAP_MS, OverlapMS);
}
void ReadSettings()
{
SequenceLenMS = CfgReadInt(L"SOUNDTOUCH", L"SequenceLengthMS", 30);
SeekWindowMS = CfgReadInt(L"SOUNDTOUCH", L"SeekWindowMS", 20);
OverlapMS = CfgReadInt(L"SOUNDTOUCH", L"OverlapMS", 10);
ClampValues();
WriteSettings();
}
void WriteSettings()
{
CfgWriteInt(L"SOUNDTOUCH", L"SequenceLengthMS", SequenceLenMS);
CfgWriteInt(L"SOUNDTOUCH", L"SeekWindowMS", SeekWindowMS);
CfgWriteInt(L"SOUNDTOUCH", L"OverlapMS", OverlapMS);
}
} // namespace SoundtouchCfg

View File

@ -0,0 +1,62 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
// To be continued...
#include "Dialogs.h"
#include <cstring>
#if defined(__unix__)
#include <wx/wx.h>
void SysMessage(const char *fmt, ...)
{
va_list list;
char msg[512];
va_start(list, fmt);
vsprintf(msg, fmt, list);
va_end(list);
if (msg[strlen(msg) - 1] == '\n')
msg[strlen(msg) - 1] = 0;
wxMessageDialog dialog(nullptr, msg, "Info", wxOK);
dialog.ShowModal();
}
void SysMessage(const wchar_t *fmt, ...)
{
va_list list;
va_start(list, fmt);
wxString msg;
msg.PrintfV(fmt, list);
va_end(list);
wxMessageDialog dialog(nullptr, msg, "Info", wxOK);
dialog.ShowModal();
}
#endif
void DspUpdate()
{
}
s32 DspLoadLibrary(wchar_t *fileName, int modnum)
{
return 0;
}

View File

@ -0,0 +1,44 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DIALOG_H_INCLUDED
#define DIALOG_H_INCLUDED
#include "../Global.h"
#include "../Config.h"
namespace DebugConfig
{
extern void ReadSettings();
extern void WriteSettings();
} // namespace DebugConfig
extern void CfgSetSettingsDir(const char *dir);
extern void CfgSetLogDir(const char *dir);
extern void CfgWriteBool(const wchar_t *Section, const wchar_t *Name, bool Value);
extern void CfgWriteInt(const wchar_t *Section, const wchar_t *Name, int Value);
extern void CfgWriteFloat(const wchar_t *Section, const wchar_t *Name, float Value);
extern void CfgWriteStr(const wchar_t *Section, const wchar_t *Name, const wxString &Data);
extern bool CfgReadBool(const wchar_t *Section, const wchar_t *Name, bool Default);
extern void CfgReadStr(const wchar_t *Section, const wchar_t *Name, wxString &Data, const wchar_t *Default);
//extern void CfgReadStr(const wchar_t* Section, const wchar_t* Name, wchar_t* Data, int DataSize, const wchar_t* Default);
extern int CfgReadInt(const wchar_t *Section, const wchar_t *Name, int Default);
extern float CfgReadFloat(const wchar_t *Section, const wchar_t *Name, float Default);
#endif

94
pcsx2/SPU2/Lowpass.cpp Normal file
View File

@ -0,0 +1,94 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "Lowpass.h"
#include <math.h>
#include <float.h>
template <typename FloatType>
__forceinline LowPassFilter<FloatType>::LowPassFilter(FloatType freq, FloatType srate)
{
typedef FloatType FT;
FloatType omega = (FT)2.0 * freq / srate;
static const FloatType g = (FT)1.0;
// calculating coefficients:
FloatType k, p, q, a;
FloatType a0, a1, a2, a3, a4;
k = ((FT)4.0 * g - (FT)3.0) / (g + (FT)1.0);
p = (FT)1.0 - (FT)0.25 * k;
p *= p;
// LP:
a = (FT)1.0 / (tan((FT)0.5 * omega) * ((FT)1.0 + p));
p = (FT)1.0 + a;
q = (FT)1.0 - a;
a0 = (FT)1.0 / (k + p * p * p * p);
a1 = (FT)4.0 * (k + p * p * p * q);
a2 = (FT)6.0 * (k + p * p * q * q);
a3 = (FT)4.0 * (k + p * q * q * q);
a4 = (k + q * q * q * q);
p = a0 * (k + (FT)1.0);
coef[0] = p;
coef[1] = (FT)4.0 * p;
coef[2] = (FT)6.0 * p;
coef[3] = (FT)4.0 * p;
coef[4] = p;
coef[5] = -a1 * a0;
coef[6] = -a2 * a0;
coef[7] = -a3 * a0;
coef[8] = -a4 * a0;
}
// Processes a single sample into the LPF.
template <typename FloatType>
__forceinline FloatType LowPassFilter<FloatType>::sample(FloatType inval)
{
const FloatType out = (coef[0] * inval) + d[0];
d[0] = (coef[1] * inval) + (coef[5] * out) + d[1];
d[1] = (coef[2] * inval) + (coef[6] * out) + d[2];
d[2] = (coef[3] * inval) + (coef[7] * out) + d[3];
d[3] = (coef[4] * inval) + (coef[8] * out);
return out;
}
LowPassFilter32::LowPassFilter32(float freq, float srate)
: impl_lpf(freq, srate)
{
}
LowPassFilter64::LowPassFilter64(double freq, double srate)
: impl_lpf(freq, srate)
{
}
float LowPassFilter32::sample(float inval)
{
return impl_lpf.sample(inval);
}
double LowPassFilter64::sample(double inval)
{
return impl_lpf.sample(inval);
}

44
pcsx2/SPU2/Lowpass.h Normal file
View File

@ -0,0 +1,44 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
template <typename FloatType>
struct LowPassFilter
{
FloatType coef[9];
FloatType d[4];
LowPassFilter(FloatType freq, FloatType srate);
FloatType sample(FloatType inval);
};
struct LowPassFilter32
{
LowPassFilter<float> impl_lpf;
LowPassFilter32(float freq, float srate);
float sample(float inval);
};
struct LowPassFilter64
{
LowPassFilter<double> impl_lpf;
LowPassFilter64(double freq, double srate);
double sample(double inval);
};

880
pcsx2/SPU2/Mixer.cpp Normal file
View File

@ -0,0 +1,880 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
// Games have turned out to be surprisingly sensitive to whether a parked, silent voice is being fully emulated.
// With Silent Hill: Shattered Memories requiring full processing for no obvious reason, we've decided to
// disable the optimisation until we can tie it to the game database.
#define NEVER_SKIP_VOICES 1
void ADMAOutLogWrite(void *lpData, u32 ulSize);
static const s32 tbl_XA_Factor[16][2] =
{
{0, 0},
{60, 0},
{115, -52},
{98, -55},
{122, -60}};
// Performs a 64-bit multiplication between two values and returns the
// high 32 bits as a result (discarding the fractional 32 bits).
// The combined fractional bits of both inputs must be 32 bits for this
// to work properly.
//
// This is meant to be a drop-in replacement for times when the 'div' part
// of a MulDiv is a constant. (example: 1<<8, or 4096, etc)
//
// [Air] Performance breakdown: This is over 10 times faster than MulDiv in
// a *worst case* scenario. It's also more accurate since it forces the
// caller to extend the inputs so that they make use of all 32 bits of
// precision.
//
static __forceinline s32 MulShr32(s32 srcval, s32 mulval)
{
return (s64)srcval * mulval >> 32;
}
__forceinline s32 clamp_mix(s32 x, u8 bitshift)
{
assert(bitshift <= 15);
return GetClamped(x, -(0x8000 << bitshift), 0x7fff << bitshift);
}
#if _MSC_VER
__forceinline
// Without the keyword static, gcc compilation fails on the inlining...
// Unfortunately the function is also used in Reverb.cpp. In order to keep the code
// clean we just disable it.
// We will need link-time code generation / Whole Program optimization to do a clean
// inline. Gcc 4.5 has the experimental options -flto, -fwhopr and -fwhole-program to
// do it but it still experimental...
#endif
StereoOut32
clamp_mix(const StereoOut32 &sample, u8 bitshift)
{
// We should clampify between -0x8000 and 0x7fff, however some audio output
// modules or sound drivers could (will :p) overshoot with that. So giving it a small safety.
return StereoOut32(
GetClamped(sample.Left, -(0x7f00 << bitshift), 0x7f00 << bitshift),
GetClamped(sample.Right, -(0x7f00 << bitshift), 0x7f00 << bitshift));
}
static void __forceinline XA_decode_block(s16 *buffer, const s16 *block, s32 &prev1, s32 &prev2)
{
const s32 header = *block;
const s32 shift = (header & 0xF) + 16;
const int id = header >> 4 & 0xF;
if (id > 4 && MsgToConsole())
ConLog("* SPU2-X: Unknown ADPCM coefficients table id %d\n", id);
const s32 pred1 = tbl_XA_Factor[id][0];
const s32 pred2 = tbl_XA_Factor[id][1];
const s8 *blockbytes = (s8 *)&block[1];
const s8 *blockend = &blockbytes[13];
for (; blockbytes <= blockend; ++blockbytes) {
s32 data = ((*blockbytes) << 28) & 0xF0000000;
s32 pcm = (data >> shift) + (((pred1 * prev1) + (pred2 * prev2) + 32) >> 6);
Clampify(pcm, -0x8000, 0x7fff);
*(buffer++) = pcm;
data = ((*blockbytes) << 24) & 0xF0000000;
s32 pcm2 = (data >> shift) + (((pred1 * pcm) + (pred2 * prev1) + 32) >> 6);
Clampify(pcm2, -0x8000, 0x7fff);
*(buffer++) = pcm2;
prev2 = pcm;
prev1 = pcm2;
}
}
static void __forceinline IncrementNextA(V_Core &thiscore, uint voiceidx)
{
V_Voice &vc(thiscore.Voices[voiceidx]);
// Important! Both cores signal IRQ when an address is read, regardless of
// which core actually reads the address.
for (int i = 0; i < 2; i++) {
if (Cores[i].IRQEnable && (vc.NextA == Cores[i].IRQA)) {
//if( IsDevBuild )
// ConLog(" * SPU2 Core %d: IRQ Requested (IRQA (%05X) passed; voice %d).\n", i, Cores[i].IRQA, thiscore.Index * 24 + voiceidx);
SetIrqCall(i);
}
}
vc.NextA++;
vc.NextA &= 0xFFFFF;
}
// decoded pcm data, used to cache the decoded data so that it needn't be decoded
// multiple times. Cache chunks are decoded when the mixer requests the blocks, and
// invalided when DMA transfers and memory writes are performed.
PcmCacheEntry *pcm_cache_data = NULL;
int g_counter_cache_hits = 0;
int g_counter_cache_misses = 0;
int g_counter_cache_ignores = 0;
// LOOP/END sets the ENDX bit and sets NAX to LSA, and the voice is muted if LOOP is not set
// LOOP seems to only have any effect on the block with LOOP/END set, where it prevents muting the voice
// (the documented requirement that every block in a loop has the LOOP bit set is nonsense according to tests)
// LOOP/START sets LSA to NAX unless LSA was written manually since sound generation started
// (see LoopMode, the method by which this is achieved on the real SPU2 is unknown)
#define XAFLAG_LOOP_END (1ul << 0)
#define XAFLAG_LOOP (1ul << 1)
#define XAFLAG_LOOP_START (1ul << 2)
static __forceinline s32 GetNextDataBuffered(V_Core &thiscore, uint voiceidx)
{
V_Voice &vc(thiscore.Voices[voiceidx]);
if ((vc.SCurrent & 3) == 0) {
IncrementNextA(thiscore, voiceidx);
if ((vc.NextA & 7) == 0) // vc.SCurrent == 24 equivalent
{
if (vc.LoopFlags & XAFLAG_LOOP_END) {
thiscore.Regs.ENDX |= (1 << voiceidx);
vc.NextA = vc.LoopStartA | 1;
if (!(vc.LoopFlags & XAFLAG_LOOP)) {
vc.Stop();
if (IsDevBuild) {
if (MsgVoiceOff())
ConLog("* SPU2-X: Voice Off by EndPoint: %d \n", voiceidx);
}
}
} else
vc.NextA++; // no, don't IncrementNextA here. We haven't read the header yet.
}
}
if (vc.SCurrent == 28) {
vc.SCurrent = 0;
// We'll need the loop flags and buffer pointers regardless of cache status:
for (int i = 0; i < 2; i++)
if (Cores[i].IRQEnable && Cores[i].IRQA == (vc.NextA & 0xFFFF8))
SetIrqCall(i);
s16 *memptr = GetMemPtr(vc.NextA & 0xFFFF8);
vc.LoopFlags = *memptr >> 8; // grab loop flags from the upper byte.
if ((vc.LoopFlags & XAFLAG_LOOP_START) && !vc.LoopMode)
vc.LoopStartA = vc.NextA & 0xFFFF8;
const int cacheIdx = vc.NextA / pcm_WordsPerBlock;
PcmCacheEntry &cacheLine = pcm_cache_data[cacheIdx];
vc.SBuffer = cacheLine.Sampledata;
if (cacheLine.Validated) {
// Cached block! Read from the cache directly.
// Make sure to propagate the prev1/prev2 ADPCM:
vc.Prev1 = vc.SBuffer[27];
vc.Prev2 = vc.SBuffer[26];
//ConLog( "* SPU2-X: Cache Hit! NextA=0x%x, cacheIdx=0x%x\n", vc.NextA, cacheIdx );
if (IsDevBuild)
g_counter_cache_hits++;
} else {
// Only flag the cache if it's a non-dynamic memory range.
if (vc.NextA >= SPU2_DYN_MEMLINE)
cacheLine.Validated = true;
if (IsDevBuild) {
if (vc.NextA < SPU2_DYN_MEMLINE)
g_counter_cache_ignores++;
else
g_counter_cache_misses++;
}
XA_decode_block(vc.SBuffer, memptr, vc.Prev1, vc.Prev2);
}
}
return vc.SBuffer[vc.SCurrent++];
}
static __forceinline void GetNextDataDummy(V_Core &thiscore, uint voiceidx)
{
V_Voice &vc(thiscore.Voices[voiceidx]);
IncrementNextA(thiscore, voiceidx);
if ((vc.NextA & 7) == 0) // vc.SCurrent == 24 equivalent
{
if (vc.LoopFlags & XAFLAG_LOOP_END) {
thiscore.Regs.ENDX |= (1 << voiceidx);
vc.NextA = vc.LoopStartA | 1;
} else
vc.NextA++; // no, don't IncrementNextA here. We haven't read the header yet.
}
if (vc.SCurrent == 28) {
for (int i = 0; i < 2; i++)
if (Cores[i].IRQEnable && Cores[i].IRQA == (vc.NextA & 0xFFFF8))
SetIrqCall(i);
vc.LoopFlags = *GetMemPtr(vc.NextA & 0xFFFF8) >> 8; // grab loop flags from the upper byte.
if ((vc.LoopFlags & XAFLAG_LOOP_START) && !vc.LoopMode)
vc.LoopStartA = vc.NextA & 0xFFFF8;
vc.SCurrent = 0;
}
vc.SP -= 4096 * (4 - (vc.SCurrent & 3));
vc.SCurrent += 4 - (vc.SCurrent & 3);
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
static s32 __forceinline GetNoiseValues()
{
static u16 lfsr = 0xC0FEu;
u16 bit = lfsr ^ (lfsr << 3) ^ (lfsr << 4) ^ (lfsr << 5);
lfsr = (lfsr << 1) | (bit >> 15);
return (s16)lfsr;
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// //
// Data is expected to be 16 bit signed (typical stuff!).
// volume is expected to be 32 bit signed (31 bits with reverse phase)
// Data is shifted up by 1 bit to give the output an effective 16 bit range.
static __forceinline s32 ApplyVolume(s32 data, s32 volume)
{
//return (volume * data) >> 15;
return MulShr32(data << 1, volume);
}
static __forceinline StereoOut32 ApplyVolume(const StereoOut32 &data, const V_VolumeLR &volume)
{
return StereoOut32(
ApplyVolume(data.Left, volume.Left),
ApplyVolume(data.Right, volume.Right));
}
static __forceinline StereoOut32 ApplyVolume(const StereoOut32 &data, const V_VolumeSlideLR &volume)
{
return StereoOut32(
ApplyVolume(data.Left, volume.Left.Value),
ApplyVolume(data.Right, volume.Right.Value));
}
static void __forceinline UpdatePitch(uint coreidx, uint voiceidx)
{
V_Voice &vc(Cores[coreidx].Voices[voiceidx]);
s32 pitch;
// [Air] : re-ordered comparisons: Modulated is much more likely to be zero than voice,
// and so the way it was before it's have to check both voice and modulated values
// most of the time. Now it'll just check Modulated and short-circuit past the voice
// check (not that it amounts to much, but eh every little bit helps).
if ((vc.Modulated == 0) || (voiceidx == 0))
pitch = vc.Pitch;
else
pitch = GetClamped((vc.Pitch * (32768 + Cores[coreidx].Voices[voiceidx - 1].OutX)) >> 15, 0, 0x3fff);
vc.SP += pitch;
}
static __forceinline void CalculateADSR(V_Core &thiscore, uint voiceidx)
{
V_Voice &vc(thiscore.Voices[voiceidx]);
if (vc.ADSR.Phase == 0) {
vc.ADSR.Value = 0;
return;
}
if (!vc.ADSR.Calculate()) {
if (IsDevBuild) {
if (MsgVoiceOff())
ConLog("* SPU2-X: Voice Off by ADSR: %d \n", voiceidx);
}
vc.Stop();
}
pxAssume(vc.ADSR.Value >= 0); // ADSR should never be negative...
}
/*
Tension: 65535 is high, 32768 is normal, 0 is low
*/
template <s32 i_tension>
__forceinline static s32 HermiteInterpolate(
s32 y0, // 16.0
s32 y1, // 16.0
s32 y2, // 16.0
s32 y3, // 16.0
s32 mu // 0.12
)
{
s32 m00 = ((y1 - y0) * i_tension) >> 16; // 16.0
s32 m01 = ((y2 - y1) * i_tension) >> 16; // 16.0
s32 m0 = m00 + m01;
s32 m10 = ((y2 - y1) * i_tension) >> 16; // 16.0
s32 m11 = ((y3 - y2) * i_tension) >> 16; // 16.0
s32 m1 = m10 + m11;
s32 val = ((2 * y1 + m0 + m1 - 2 * y2) * mu) >> 12; // 16.0
val = ((val - 3 * y1 - 2 * m0 - m1 + 3 * y2) * mu) >> 12; // 16.0
val = ((val + m0) * mu) >> 11; // 16.0
return (val + (y1 << 1));
}
__forceinline static s32 CatmullRomInterpolate(
s32 y0, // 16.0
s32 y1, // 16.0
s32 y2, // 16.0
s32 y3, // 16.0
s32 mu // 0.12
)
{
//q(t) = 0.5 *( (2 * P1) +
// (-P0 + P2) * t +
// (2*P0 - 5*P1 + 4*P2 - P3) * t2 +
// (-P0 + 3*P1- 3*P2 + P3) * t3)
s32 a3 = (-y0 + 3 * y1 - 3 * y2 + y3);
s32 a2 = (2 * y0 - 5 * y1 + 4 * y2 - y3);
s32 a1 = (-y0 + y2);
s32 a0 = (2 * y1);
s32 val = ((a3)*mu) >> 12;
val = ((a2 + val) * mu) >> 12;
val = ((a1 + val) * mu) >> 12;
return (a0 + val);
}
__forceinline static s32 CubicInterpolate(
s32 y0, // 16.0
s32 y1, // 16.0
s32 y2, // 16.0
s32 y3, // 16.0
s32 mu // 0.12
)
{
const s32 a0 = y3 - y2 - y0 + y1;
const s32 a1 = y0 - y1 - a0;
const s32 a2 = y2 - y0;
s32 val = ((a0)*mu) >> 12;
val = ((val + a1) * mu) >> 12;
val = ((val + a2) * mu) >> 11;
return (val + (y1 << 1));
}
// Returns a 16 bit result in Value.
// Uses standard template-style optimization techniques to statically generate five different
// versions of this function (one for each type of interpolation).
template <int InterpType>
static __forceinline s32 GetVoiceValues(V_Core &thiscore, uint voiceidx)
{
V_Voice &vc(thiscore.Voices[voiceidx]);
while (vc.SP > 0) {
if (InterpType >= 2) {
vc.PV4 = vc.PV3;
vc.PV3 = vc.PV2;
}
vc.PV2 = vc.PV1;
vc.PV1 = GetNextDataBuffered(thiscore, voiceidx);
vc.SP -= 4096;
}
const s32 mu = vc.SP + 4096;
switch (InterpType) {
case 0:
return vc.PV1 << 1;
case 1:
return (vc.PV1 << 1) - (((vc.PV2 - vc.PV1) * vc.SP) >> 11);
case 2:
return CubicInterpolate(vc.PV4, vc.PV3, vc.PV2, vc.PV1, mu);
case 3:
return HermiteInterpolate<16384>(vc.PV4, vc.PV3, vc.PV2, vc.PV1, mu);
case 4:
return CatmullRomInterpolate(vc.PV4, vc.PV3, vc.PV2, vc.PV1, mu);
jNO_DEFAULT;
}
return 0; // technically unreachable!
}
// Noise values need to be mixed without going through interpolation, since it
// can wreak havoc on the noise (causing muffling or popping). Not that this noise
// generator is accurate in its own right.. but eh, ah well :)
static __forceinline s32 GetNoiseValues(V_Core &thiscore, uint voiceidx)
{
// V_Voice &vc(thiscore.Voices[voiceidx]);
s32 retval = GetNoiseValues();
/*while(vc.SP>=4096)
{
retval = GetNoiseValues();
vc.SP-=4096;
}*/
// GetNoiseValues can't set the phase zero on us unexpectedly
// like GetVoiceValues can. Better assert just in case though..
// pxAssume(vc.ADSR.Phase != 0);
return retval;
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// //
// writes a signed value to the SPU2 ram
// Performs no cache invalidation -- use only for dynamic memory ranges
// of the SPU2 (between 0x0000 and SPU2_DYN_MEMLINE)
static __forceinline void spu2M_WriteFast(u32 addr, s16 value)
{
// Fixes some of the oldest hangs in pcsx2's history! :p
for (int i = 0; i < 2; i++) {
if (Cores[i].IRQEnable && Cores[i].IRQA == addr) {
//printf("Core %d special write IRQ Called (IRQ passed). IRQA = %x\n",i,addr);
SetIrqCall(i);
}
}
// throw an assertion if the memory range is invalid:
#ifndef DEBUG_FAST
pxAssume(addr < SPU2_DYN_MEMLINE);
#endif
*GetMemPtr(addr) = value;
}
static __forceinline StereoOut32 MixVoice(uint coreidx, uint voiceidx)
{
V_Core &thiscore(Cores[coreidx]);
V_Voice &vc(thiscore.Voices[voiceidx]);
// If this assertion fails, it mans SCurrent is being corrupted somewhere, or is not initialized
// properly. Invalid values in SCurrent will cause errant IRQs and corrupted audio.
pxAssertMsg((vc.SCurrent <= 28) && (vc.SCurrent != 0), "Current sample should always range from 1->28");
// Most games don't use much volume slide effects. So only call the UpdateVolume
// methods when needed by checking the flag outside the method here...
// (Note: Ys 6 : Ark of Nephistm uses these effects)
vc.Volume.Update();
// SPU2 Note: The spu2 continues to process voices for eternity, always, so we
// have to run through all the motions of updating the voice regardless of it's
// audible status. Otherwise IRQs might not trigger and emulation might fail.
if (vc.ADSR.Phase > 0) {
UpdatePitch(coreidx, voiceidx);
s32 Value = 0;
if (vc.Noise)
Value = GetNoiseValues(thiscore, voiceidx);
else {
// Optimization : Forceinline'd Templated Dispatch Table. Any halfwit compiler will
// turn this into a clever jump dispatch table (no call/rets, no compares, uber-efficient!)
switch (Interpolation) {
case 0:
Value = GetVoiceValues<0>(thiscore, voiceidx);
break;
case 1:
Value = GetVoiceValues<1>(thiscore, voiceidx);
break;
case 2:
Value = GetVoiceValues<2>(thiscore, voiceidx);
break;
case 3:
Value = GetVoiceValues<3>(thiscore, voiceidx);
break;
case 4:
Value = GetVoiceValues<4>(thiscore, voiceidx);
break;
jNO_DEFAULT;
}
}
// Update and Apply ADSR (applies to normal and noise sources)
//
// Note! It's very important that ADSR stay as accurate as possible. By the way
// it is used, various sound effects can end prematurely if we truncate more than
// one or two bits. Best result comes from no truncation at all, which is why we
// use a full 64-bit multiply/result here.
CalculateADSR(thiscore, voiceidx);
Value = MulShr32(Value, vc.ADSR.Value);
// Store Value for eventual modulation later
// Pseudonym's Crest calculation idea. Actually calculates a crest, unlike the old code which was just peak.
if (vc.PV1 < vc.NextCrest) {
vc.OutX = MulShr32(vc.NextCrest, vc.ADSR.Value);
vc.NextCrest = -0x8000;
}
if (vc.PV1 > vc.PV2) {
vc.NextCrest = vc.PV1;
}
if (IsDevBuild)
DebugCores[coreidx].Voices[voiceidx].displayPeak = std::max(DebugCores[coreidx].Voices[voiceidx].displayPeak, (s32)vc.OutX);
// Write-back of raw voice data (post ADSR applied)
if (voiceidx == 1)
spu2M_WriteFast(((0 == coreidx) ? 0x400 : 0xc00) + OutPos, vc.OutX);
else if (voiceidx == 3)
spu2M_WriteFast(((0 == coreidx) ? 0x600 : 0xe00) + OutPos, vc.OutX);
return ApplyVolume(StereoOut32(Value, Value), vc.Volume);
} else {
// Continue processing voice, even if it's "off". Or else we miss interrupts! (Fatal Frame engine died because of this.)
if (NEVER_SKIP_VOICES || (*GetMemPtr(vc.NextA & 0xFFFF8) >> 8 & 3) != 3 || vc.LoopStartA != (vc.NextA & ~7) // not in a tight loop
|| (Cores[0].IRQEnable && (Cores[0].IRQA & ~7) == vc.LoopStartA) // or should be interrupting regularly
|| (Cores[1].IRQEnable && (Cores[1].IRQA & ~7) == vc.LoopStartA) || !(thiscore.Regs.ENDX & 1 << voiceidx)) // or isn't currently flagged as having passed the endpoint
{
UpdatePitch(coreidx, voiceidx);
while (vc.SP > 0)
GetNextDataDummy(thiscore, voiceidx); // Dummy is enough
}
// Write-back of raw voice data (some zeros since the voice is "dead")
if (voiceidx == 1)
spu2M_WriteFast(((0 == coreidx) ? 0x400 : 0xc00) + OutPos, 0);
else if (voiceidx == 3)
spu2M_WriteFast(((0 == coreidx) ? 0x600 : 0xe00) + OutPos, 0);
return StereoOut32(0, 0);
}
}
const VoiceMixSet VoiceMixSet::Empty((StereoOut32()), (StereoOut32())); // Don't use SteroOut32::Empty because C++ doesn't make any dep/order checks on global initializers.
static __forceinline void MixCoreVoices(VoiceMixSet &dest, const uint coreidx)
{
V_Core &thiscore(Cores[coreidx]);
for (uint voiceidx = 0; voiceidx < V_Core::NumVoices; ++voiceidx) {
StereoOut32 VVal(MixVoice(coreidx, voiceidx));
// Note: Results from MixVoice are ranged at 16 bits.
dest.Dry.Left += VVal.Left & thiscore.VoiceGates[voiceidx].DryL;
dest.Dry.Right += VVal.Right & thiscore.VoiceGates[voiceidx].DryR;
dest.Wet.Left += VVal.Left & thiscore.VoiceGates[voiceidx].WetL;
dest.Wet.Right += VVal.Right & thiscore.VoiceGates[voiceidx].WetR;
}
}
StereoOut32 V_Core::Mix(const VoiceMixSet &inVoices, const StereoOut32 &Input, const StereoOut32 &Ext)
{
MasterVol.Update();
// Saturate final result to standard 16 bit range.
const VoiceMixSet Voices(clamp_mix(inVoices.Dry), clamp_mix(inVoices.Wet));
// Write Mixed results To Output Area
spu2M_WriteFast(((0 == Index) ? 0x1000 : 0x1800) + OutPos, Voices.Dry.Left);
spu2M_WriteFast(((0 == Index) ? 0x1200 : 0x1A00) + OutPos, Voices.Dry.Right);
spu2M_WriteFast(((0 == Index) ? 0x1400 : 0x1C00) + OutPos, Voices.Wet.Left);
spu2M_WriteFast(((0 == Index) ? 0x1600 : 0x1E00) + OutPos, Voices.Wet.Right);
// Write mixed results to logfile (if enabled)
WaveDump::WriteCore(Index, CoreSrc_DryVoiceMix, Voices.Dry);
WaveDump::WriteCore(Index, CoreSrc_WetVoiceMix, Voices.Wet);
// Mix in the Input data
StereoOut32 TD(
Input.Left & DryGate.InpL,
Input.Right & DryGate.InpR);
// Mix in the Voice data
TD.Left += Voices.Dry.Left & DryGate.SndL;
TD.Right += Voices.Dry.Right & DryGate.SndR;
// Mix in the External (nothing/core0) data
TD.Left += Ext.Left & DryGate.ExtL;
TD.Right += Ext.Right & DryGate.ExtR;
// User-level Effects disabling. Nice speedup but breaks games that depend on
// reverb IRQs (very few -- if you find one name it here!).
if (EffectsDisabled)
return TD;
// ----------------------------------------------------------------------------
// Reverberation Effects Processing
// ----------------------------------------------------------------------------
// SPU2 has an FxEnable bit which seems to disable all reverb processing *and*
// output, but does *not* disable the advancing buffers. IRQs are not triggered
// and reverb is rendered silent.
//
// Technically we should advance the buffers even when fx are disabled. However
// there are two things that make this very unlikely to matter:
//
// 1. Any SPU2 app wanting to avoid noise or pops needs to clear the reverb buffers
// when adjusting settings anyway; so the read/write positions in the reverb
// buffer after FxEnabled is set back to 1 doesn't really matter.
//
// 2. Writes to ESA (and possibly EEA) reset the buffer pointers to 0.
//
// On the other hand, updating the buffer is cheap and easy, so might as well. ;)
Reverb_AdvanceBuffer(); // Updates the reverb work area as well, if needed.
// ToDo:
// Bad EndA causes memory corruption. Bad for us, unknown on PS2!
// According to no$psx, effects always run but don't always write back, so the FxEnable check may be wrong
if (!FxEnable || EffectsEndA >= 0x100000)
return TD;
StereoOut32 TW;
// Mix Input, Voice, and External data:
TW.Left = Input.Left & WetGate.InpL;
TW.Right = Input.Right & WetGate.InpR;
TW.Left += Voices.Wet.Left & WetGate.SndL;
TW.Right += Voices.Wet.Right & WetGate.SndR;
TW.Left += Ext.Left & WetGate.ExtL;
TW.Right += Ext.Right & WetGate.ExtR;
WaveDump::WriteCore(Index, CoreSrc_PreReverb, TW);
StereoOut32 RV = DoReverb(TW);
WaveDump::WriteCore(Index, CoreSrc_PostReverb, RV);
// Mix Dry + Wet
// (master volume is applied later to the result of both outputs added together).
return TD + ApplyVolume(RV, FxVol);
}
// Filters that work on the final output to de-alias and equlize it.
// Taken from http://nenolod.net/projects/upse/
#define OVERALL_SCALE (0.87f)
StereoOut32 Apply_Frequency_Response_Filter(StereoOut32 &SoundStream)
{
static FrequencyResponseFilter FRF = FrequencyResponseFilter();
s32 in, out;
s32 l, r;
s32 mid, side;
l = SoundStream.Left;
r = SoundStream.Right;
mid = l + r;
side = l - r;
in = mid;
out = FRF.la0 * in + FRF.la1 * FRF.lx1 + FRF.la2 * FRF.lx2 - FRF.lb1 * FRF.ly1 - FRF.lb2 * FRF.ly2;
FRF.lx2 = FRF.lx1;
FRF.lx1 = in;
FRF.ly2 = FRF.ly1;
FRF.ly1 = out;
mid = out;
l = ((0.5) * (OVERALL_SCALE)) * (mid + side);
r = ((0.5) * (OVERALL_SCALE)) * (mid - side);
in = l;
out = FRF.ha0 * in + FRF.ha1 * FRF.History_One_In.Left + FRF.ha2 * FRF.History_Two_In.Left - FRF.hb1 * FRF.History_One_Out.Left - FRF.hb2 * FRF.History_Two_Out.Left;
FRF.History_Two_In.Left = FRF.History_One_In.Left;
FRF.History_One_In.Left = in;
FRF.History_Two_Out.Left = FRF.History_One_Out.Left;
FRF.History_One_Out.Left = out;
l = out;
in = r;
out = FRF.ha0 * in + FRF.ha1 * FRF.History_One_In.Right + FRF.ha2 * FRF.History_Two_In.Right - FRF.hb1 * FRF.History_One_Out.Right - FRF.hb2 * FRF.History_Two_Out.Right;
FRF.History_Two_In.Right = FRF.History_One_In.Right;
FRF.History_One_In.Right = in;
FRF.History_Two_Out.Right = FRF.History_One_Out.Right;
FRF.History_One_Out.Right = out;
r = out;
//clamp_mix(l);
//clamp_mix(r);
SoundStream.Left = l;
SoundStream.Right = r;
return SoundStream;
}
StereoOut32 Apply_Dealias_Filter(StereoOut32 &SoundStream)
{
static StereoOut32 Old = StereoOut32::Empty;
s32 l, r;
l = SoundStream.Left;
r = SoundStream.Right;
l += (l - Old.Left);
r += (r - Old.Right);
Old.Left = SoundStream.Left;
Old.Right = SoundStream.Right;
SoundStream.Left = l;
SoundStream.Right = r;
return SoundStream;
}
// used to throttle the output rate of cache stat reports
static int p_cachestat_counter = 0;
// Gcc does not want to inline it when lto is enabled because some functions growth too much.
// The function is big enought to see any speed impact. -- Gregory
#ifndef __POSIX__
__forceinline
#endif
void
Mix()
{
// Note: Playmode 4 is SPDIF, which overrides other inputs.
StereoOut32 InputData[2] =
{
// SPDIF is on Core 0:
// Fixme:
// 1. We do not have an AC3 decoder for the bitstream.
// 2. Games usually provide a normal ADMA stream as well and want to see it getting read!
/*(PlayMode&4) ? StereoOut32::Empty : */ ApplyVolume(Cores[0].ReadInput(), Cores[0].InpVol),
// CDDA is on Core 1:
(PlayMode & 8) ? StereoOut32::Empty : ApplyVolume(Cores[1].ReadInput(), Cores[1].InpVol)};
WaveDump::WriteCore(0, CoreSrc_Input, InputData[0]);
WaveDump::WriteCore(1, CoreSrc_Input, InputData[1]);
// Todo: Replace me with memzero initializer!
VoiceMixSet VoiceData[2] = {VoiceMixSet::Empty, VoiceMixSet::Empty}; // mixed voice data for each core.
MixCoreVoices(VoiceData[0], 0);
MixCoreVoices(VoiceData[1], 1);
StereoOut32 Ext(Cores[0].Mix(VoiceData[0], InputData[0], StereoOut32::Empty));
if ((PlayMode & 4) || (Cores[0].Mute != 0))
Ext = StereoOut32::Empty;
else {
Ext = clamp_mix(ApplyVolume(Ext, Cores[0].MasterVol));
}
// Commit Core 0 output to ram before mixing Core 1:
spu2M_WriteFast(0x800 + OutPos, Ext.Left);
spu2M_WriteFast(0xA00 + OutPos, Ext.Right);
WaveDump::WriteCore(0, CoreSrc_External, Ext);
Ext = ApplyVolume(Ext, Cores[1].ExtVol);
StereoOut32 Out(Cores[1].Mix(VoiceData[1], InputData[1], Ext));
if (PlayMode & 8) {
// Experimental CDDA support
// The CDDA overrides all other mixer output. It's a direct feed!
Out = Cores[1].ReadInput_HiFi();
//WaveLog::WriteCore( 1, "CDDA-32", OutL, OutR );
} else {
Out.Left = MulShr32(Out.Left << (SndOutVolumeShift + 1), Cores[1].MasterVol.Left.Value);
Out.Right = MulShr32(Out.Right << (SndOutVolumeShift + 1), Cores[1].MasterVol.Right.Value);
#ifdef DEBUG_KEYS
if (postprocess_filter_enabled)
#endif
{
if (postprocess_filter_dealias) {
// Dealias filter emphasizes the highs too much.
Out = Apply_Dealias_Filter(Out);
}
Out = Apply_Frequency_Response_Filter(Out);
}
// Final Clamp!
// Like any good audio system, the PS2 pumps the volume and incurs some distortion in its
// output, giving us a nice thumpy sound at times. So we add 1 above (2x volume pump) and
// then clamp it all here.
// Edit: I'm sorry Jake, but I know of no good audio system that arbitrary distorts and clips
// output by design.
// Good thing though that this code gets the volume exactly right, as per tests :)
Out = clamp_mix(Out, SndOutVolumeShift);
}
// Configurable output volume
Out.Left *= FinalVolume;
Out.Right *= FinalVolume;
SndBuffer::Write(Out);
// Update AutoDMA output positioning
OutPos++;
if (OutPos >= 0x200)
OutPos = 0;
if (IsDevBuild) {
p_cachestat_counter++;
if (p_cachestat_counter > (48000 * 10)) {
p_cachestat_counter = 0;
if (MsgCache())
ConLog(" * SPU2 > CacheStats > Hits: %d Misses: %d Ignores: %d\n",
g_counter_cache_hits,
g_counter_cache_misses,
g_counter_cache_ignores);
g_counter_cache_hits =
g_counter_cache_misses =
g_counter_cache_ignores = 0;
}
}
}

135
pcsx2/SPU2/Mixer.h Normal file
View File

@ -0,0 +1,135 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
// Implemented in Config.cpp
extern float VolumeAdjustFL;
extern float VolumeAdjustFR;
struct StereoOut32
{
static StereoOut32 Empty;
s32 Left;
s32 Right;
StereoOut32()
: Left(0)
, Right(0)
{
}
StereoOut32(s32 left, s32 right)
: Left(left)
, Right(right)
{
}
StereoOut32(const StereoOut16 &src);
explicit StereoOut32(const StereoOutFloat &src);
StereoOut16 DownSample() const;
StereoOut32 operator*(const int &factor) const
{
return StereoOut32(
Left * factor,
Right * factor);
}
StereoOut32 &operator*=(const int &factor)
{
Left *= factor;
Right *= factor;
return *this;
}
StereoOut32 operator+(const StereoOut32 &right) const
{
return StereoOut32(
Left + right.Left,
Right + right.Right);
}
StereoOut32 operator/(int src) const
{
return StereoOut32(Left / src, Right / src);
}
void ResampleFrom(const StereoOut32 &src)
{
this->Left = src.Left << 2;
this->Right = src.Right << 2;
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s32)(Left * VolumeAdjustFL);
Right = (s32)(Right * VolumeAdjustFR);
}
};
struct FrequencyResponseFilter
{
static FrequencyResponseFilter Empty;
StereoOut32 History_One_In;
StereoOut32 History_One_Out;
StereoOut32 History_Two_In;
StereoOut32 History_Two_Out;
s32 lx1;
s32 lx2;
s32 ly1;
s32 ly2;
float la0, la1, la2, lb1, lb2;
float ha0, ha1, ha2, hb1, hb2;
FrequencyResponseFilter()
: History_One_In(0, 0)
, History_One_Out(0, 0)
, History_Two_In(0, 0)
, History_Two_Out(0, 0)
, lx1(0)
, lx2(0)
, ly1(0)
, ly2(0)
, la0(1.00320890889339290000f)
, la1(-1.97516434134506300000f)
, la2(0.97243484967313087000f)
, lb1(-1.97525280404731810000f)
, lb2(0.97555529586426892000f)
, ha0(1.52690772687271160000f)
, ha1(-1.62653918974914990000f) //-1.72 = "common equilizer curve" --____--
, ha2(0.57997976029249387000f)
, hb1(-0.80955590379048203000f)
, hb2(0.28990420120653748000f)
{
}
};
extern void Mix();
extern s32 clamp_mix(s32 x, u8 bitshift = 0);
extern StereoOut32 clamp_mix(const StereoOut32 &sample, u8 bitshift = 0);

616
pcsx2/SPU2/PS2E-spu2.cpp Normal file
View File

@ -0,0 +1,616 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "PS2E-spu2.h"
#include "Dma.h"
#include "Dialogs.h"
#ifdef __APPLE__
#include "PS2Eext.h"
#endif
#include "svnrev.h"
#ifdef _MSC_VER
#define snprintf sprintf_s
#endif
// PCSX2 expects ASNI, not unicode, so this MUST always be char...
static char libraryName[256];
int SampleRate = 48000;
static bool IsOpened = false;
static bool IsInitialized = false;
static u32 pClocks = 0;
u32 *cyclePtr = NULL;
u32 lClocks = 0;
//static bool cpu_detected = false;
static bool CheckSSE()
{
return true;
#if 0
if( !cpu_detected )
{
cpudetectInit();
cpu_detected = true;
}
if( !x86caps.hasStreamingSIMDExtensions || !x86caps.hasStreamingSIMD2Extensions )
{
SysMessage( "Your CPU does not support SSE2 instructions.\nThe SPU2-X plugin requires SSE2 to run." );
return false;
}
return true;
#endif
}
void SPU2configure()
{
if (!CheckSSE())
return;
configure();
}
s32 SPU2test()
{
if (!CheckSSE())
return -1;
ReadSettings();
if (SndBuffer::Test() != 0) {
// TODO : Implement a proper dialog that allows the user to test different audio out drivers.
const wchar_t *wtf = mods[OutputModule]->GetIdent();
SysMessage(L"The '%s' driver test failed. Please configure\na different SoundOut module and try again.", wtf);
return -1;
}
return 0;
}
// --------------------------------------------------------------------------------------
// DMA 4/7 Callbacks from Core Emulator
// --------------------------------------------------------------------------------------
u16 *DMABaseAddr;
void (*_irqcallback)();
void (*dma4callback)();
void (*dma7callback)();
u32 CALLBACK SPU2ReadMemAddr(int core)
{
return Cores[core].MADR;
}
void CALLBACK SPU2WriteMemAddr(int core, u32 value)
{
Cores[core].MADR = value;
}
void CALLBACK SPU2setDMABaseAddr(uptr baseaddr)
{
DMABaseAddr = (u16 *)baseaddr;
}
void CALLBACK SPU2setSettingsDir(const char *dir)
{
CfgSetSettingsDir(dir);
}
void CALLBACK SPU2setLogDir(const char *dir)
{
CfgSetLogDir(dir);
}
void SPU2irqCallback(void (*SPU2callback)(), void (*DMA4callback)(), void (*DMA7callback)())
{
_irqcallback = SPU2callback;
dma4callback = DMA4callback;
dma7callback = DMA7callback;
}
void CALLBACK SPU2readDMA4Mem(u16 *pMem, u32 size) // size now in 16bit units
{
if (cyclePtr != NULL)
TimeUpdate(*cyclePtr);
FileLog("[%10d] SPU2 readDMA4Mem size %x\n", Cycles, size << 1);
Cores[0].DoDMAread(pMem, size);
}
void CALLBACK SPU2writeDMA4Mem(u16 *pMem, u32 size) // size now in 16bit units
{
if (cyclePtr != NULL)
TimeUpdate(*cyclePtr);
FileLog("[%10d] SPU2 writeDMA4Mem size %x at address %x\n", Cycles, size << 1, Cores[0].TSA);
#ifdef S2R_ENABLE
if (!replay_mode)
s2r_writedma4(Cycles, pMem, size);
#endif
Cores[0].DoDMAwrite(pMem, size);
}
void CALLBACK SPU2interruptDMA4()
{
FileLog("[%10d] SPU2 interruptDMA4\n", Cycles);
Cores[0].Regs.STATX |= 0x80;
//Cores[0].Regs.ATTR &= ~0x30;
}
void CALLBACK SPU2interruptDMA7()
{
FileLog("[%10d] SPU2 interruptDMA7\n", Cycles);
Cores[1].Regs.STATX |= 0x80;
//Cores[1].Regs.ATTR &= ~0x30;
}
void CALLBACK SPU2readDMA7Mem(u16 *pMem, u32 size)
{
if (cyclePtr != NULL)
TimeUpdate(*cyclePtr);
FileLog("[%10d] SPU2 readDMA7Mem size %x\n", Cycles, size << 1);
Cores[1].DoDMAread(pMem, size);
}
void CALLBACK SPU2writeDMA7Mem(u16 *pMem, u32 size)
{
if (cyclePtr != NULL)
TimeUpdate(*cyclePtr);
FileLog("[%10d] SPU2 writeDMA7Mem size %x at address %x\n", Cycles, size << 1, Cores[1].TSA);
#ifdef S2R_ENABLE
if (!replay_mode)
s2r_writedma7(Cycles, pMem, size);
#endif
Cores[1].DoDMAwrite(pMem, size);
}
s32 SPU2reset()
{
if (SndBuffer::Test() == 0 && SampleRate != 48000)
{
SampleRate = 48000;
SndBuffer::Cleanup();
try {
SndBuffer::Init();
}
catch (std::exception& ex) {
fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what());
SPU2close();
return -1;
}
}
else
SampleRate = 48000;
memset(spu2regs, 0, 0x010000);
memset(_spu2mem, 0, 0x200000);
memset(_spu2mem + 0x2800, 7, 0x10); // from BIOS reversal. Locks the voices so they don't run free.
Cores[0].Init(0);
Cores[1].Init(1);
return 0;
}
s32 SPU2ps1reset()
{
printf("RESET PS1 \n");
if (SndBuffer::Test() == 0 && SampleRate != 44100)
{
SampleRate = 44100;
SndBuffer::Cleanup();
try {
SndBuffer::Init();
}
catch (std::exception& ex) {
fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what());
SPU2close();
return -1;
}
}
else
SampleRate = 44100;
/* memset(spu2regs, 0, 0x010000);
memset(_spu2mem, 0, 0x200000);
memset(_spu2mem + 0x2800, 7, 0x10); // from BIOS reversal. Locks the voices so they don't run free.
Cores[0].Init(0);
Cores[1].Init(1);*/
return 0;
}
s32 SPU2init()
{
assert(regtable[0x400] == NULL);
if (IsInitialized) {
printf(" * SPU2-X: Already initialized - Ignoring SPU2init signal.");
return 0;
}
IsInitialized = true;
ReadSettings();
#ifdef SPU2_LOG
if (AccessLog()) {
spu2Log = OpenLog(AccessLogFileName);
setvbuf(spu2Log, NULL, _IONBF, 0);
FileLog("SPU2init\n");
}
#endif
srand((unsigned)time(NULL));
spu2regs = (s16 *)malloc(0x010000);
_spu2mem = (s16 *)malloc(0x200000);
// adpcm decoder cache:
// the cache data size is determined by taking the number of adpcm blocks
// (2MB / 16) and multiplying it by the decoded block size (28 samples).
// Thus: pcm_cache_data = 7,340,032 bytes (ouch!)
// Expanded: 16 bytes expands to 56 bytes [3.5:1 ratio]
// Resulting in 2MB * 3.5.
pcm_cache_data = (PcmCacheEntry *)calloc(pcm_BlockCount, sizeof(PcmCacheEntry));
if ((spu2regs == NULL) || (_spu2mem == NULL) || (pcm_cache_data == NULL)) {
SysMessage("SPU2-X: Error allocating Memory\n");
return -1;
}
// Patch up a copy of regtable that directly maps "NULLs" to SPU2 memory.
memcpy(regtable, regtable_original, sizeof(regtable));
for (uint mem = 0; mem < 0x800; mem++) {
u16 *ptr = regtable[mem >> 1];
if (!ptr) {
regtable[mem >> 1] = &(spu2Ru16(mem));
}
}
SPU2reset();
DMALogOpen();
InitADSR();
#ifdef S2R_ENABLE
if (!replay_mode)
s2r_open(Cycles, "replay_dump.s2r");
#endif
return 0;
}
#ifdef _MSC_VER
// Bit ugly to have this here instead of in RealttimeDebugger.cpp, but meh :p
extern bool debugDialogOpen;
extern HWND hDebugDialog;
static INT_PTR CALLBACK DebugProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int wmId;
switch (uMsg) {
case WM_PAINT:
return FALSE;
case WM_INITDIALOG: {
debugDialogOpen = true;
} break;
case WM_COMMAND:
wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId) {
case IDOK:
case IDCANCEL:
debugDialogOpen = false;
EndDialog(hWnd, 0);
break;
default:
return FALSE;
}
break;
default:
return FALSE;
}
return TRUE;
}
#endif
uptr gsWindowHandle = 0;
s32 SPU2open(void *pDsp)
{
if (IsOpened)
return 0;
FileLog("[%10d] SPU2 Open\n", Cycles);
if (pDsp != NULL)
gsWindowHandle = *(uptr *)pDsp;
else
gsWindowHandle = 0;
#ifdef _MSC_VER
#ifdef PCSX2_DEVBUILD // Define may not be needed but not tested yet. Better make sure.
if (IsDevBuild && VisualDebug()) {
if (debugDialogOpen == 0) {
hDebugDialog = CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_DEBUG), 0, DebugProc, 0);
ShowWindow(hDebugDialog, SW_SHOWNORMAL);
debugDialogOpen = 1;
}
} else if (debugDialogOpen) {
DestroyWindow(hDebugDialog);
debugDialogOpen = 0;
}
#endif
#endif
IsOpened = true;
lClocks = (cyclePtr != NULL) ? *cyclePtr : 0;
try {
SndBuffer::Init();
#ifndef __POSIX__
DspLoadLibrary(dspPlugin, dspPluginModule);
#endif
WaveDump::Open();
} catch (std::exception &ex) {
fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what());
SPU2close();
return -1;
}
return 0;
}
void SPU2close()
{
if (!IsOpened)
return;
IsOpened = false;
FileLog("[%10d] SPU2 Close\n", Cycles);
#ifndef __POSIX__
DspCloseLibrary();
#endif
SndBuffer::Cleanup();
}
void SPU2shutdown()
{
if (!IsInitialized)
return;
IsInitialized = false;
ConLog("* SPU2-X: Shutting down.\n");
SPU2close();
#ifdef S2R_ENABLE
if (!replay_mode)
s2r_close();
#endif
DoFullDump();
#ifdef STREAM_DUMP
fclose(il0);
fclose(il1);
#endif
#ifdef EFFECTS_DUMP
fclose(el0);
fclose(el1);
#endif
WaveDump::Close();
DMALogClose();
safe_free(spu2regs);
safe_free(_spu2mem);
safe_free(pcm_cache_data);
#ifdef SPU2_LOG
if (!AccessLog())
return;
FileLog("[%10d] SPU2shutdown\n", Cycles);
if (spu2Log)
fclose(spu2Log);
#endif
}
void SPU2setClockPtr(u32 *ptr)
{
cyclePtr = ptr;
}
#ifdef DEBUG_KEYS
static u32 lastTicks;
static bool lState[6];
#endif
void SPU2async(u32 cycles)
{
DspUpdate();
if (cyclePtr != NULL) {
TimeUpdate(*cyclePtr);
} else {
pClocks += cycles;
TimeUpdate(pClocks);
}
#ifdef DEBUG_KEYS
u32 curTicks = GetTickCount();
if ((curTicks - lastTicks) >= 50) {
int oldI = Interpolation;
bool cState[6];
for (int i = 0; i < 6; i++) {
cState[i] = !!(GetAsyncKeyState(VK_NUMPAD0 + i) & 0x8000);
if ((cState[i] && !lState[i]) && i != 5)
Interpolation = i;
if ((cState[i] && !lState[i]) && i == 5) {
postprocess_filter_enabled = !postprocess_filter_enabled;
printf("Post process filters %s \n", postprocess_filter_enabled ? "enabled" : "disabled");
}
lState[i] = cState[i];
}
if (Interpolation != oldI) {
printf("Interpolation set to %d", Interpolation);
switch (Interpolation) {
case 0:
printf(" - Nearest.\n");
break;
case 1:
printf(" - Linear.\n");
break;
case 2:
printf(" - Cubic.\n");
break;
case 3:
printf(" - Hermite.\n");
break;
case 4:
printf(" - Catmull-Rom.\n");
break;
default:
printf(" (unknown).\n");
break;
}
}
lastTicks = curTicks;
}
#endif
}
u16 SPU2read(u32 rmem)
{
// if(!replay_mode)
// s2r_readreg(Cycles,rmem);
u16 ret = 0xDEAD;
u32 core = 0, mem = rmem & 0xFFFF, omem = mem;
if (mem & 0x400) {
omem ^= 0x400;
core = 1;
}
if (omem == 0x1f9001AC) {
ret = Cores[core].DmaRead();
} else {
if (cyclePtr != NULL)
TimeUpdate(*cyclePtr);
if (rmem >> 16 == 0x1f80) {
ret = Cores[0].ReadRegPS1(rmem);
} else if (mem >= 0x800) {
ret = spu2Ru16(mem);
ConLog("* SPU2-X: Read from reg>=0x800: %x value %x\n", mem, ret);
} else {
ret = *(regtable[(mem >> 1)]);
//FileLog("[%10d] SPU2 read mem %x (core %d, register %x): %x\n",Cycles, mem, core, (omem & 0x7ff), ret);
SPU2writeLog("read", rmem, ret);
}
}
return ret;
}
void SPU2write(u32 rmem, u16 value)
{
#ifdef S2R_ENABLE
if (!replay_mode)
s2r_writereg(Cycles, rmem, value);
#endif
// Note: Reverb/Effects are very sensitive to having precise update timings.
// If the SPU2 isn't in in sync with the IOP, samples can end up playing at rather
// incorrect pitches and loop lengths.
if (cyclePtr != NULL)
TimeUpdate(*cyclePtr);
if (rmem >> 16 == 0x1f80)
Cores[0].WriteRegPS1(rmem, value);
else {
SPU2writeLog("write", rmem, value);
SPU2_FastWrite(rmem, value);
}
}
// if start is 1, starts recording spu2 data, else stops
// returns a non zero value if successful
// for now, pData is not used
int SPU2setupRecording(int start, std::wstring* filename)
{
if (start == 0)
RecordStop();
else if (start == 1)
RecordStart(filename);
return 0;
}
s32 SPU2freeze(int mode, freezeData *data)
{
pxAssume(data != NULL);
if (!data) {
printf("SPU2-X savestate null pointer!\n");
return -1;
}
if (mode == FREEZE_SIZE) {
data->size = Savestate::SizeIt();
return 0;
}
pxAssume(mode == FREEZE_LOAD || mode == FREEZE_SAVE);
if (data->data == NULL) {
printf("SPU2-X savestate null pointer!\n");
return -1;
}
Savestate::DataBlock &spud = (Savestate::DataBlock &)*(data->data);
switch (mode) {
case FREEZE_LOAD:
return Savestate::ThawIt(spud);
case FREEZE_SAVE:
return Savestate::FreezeIt(spud);
jNO_DEFAULT;
}
// technically unreachable, but kills a warning:
return 0;
}

70
pcsx2/SPU2/PS2E-spu2.h Normal file
View File

@ -0,0 +1,70 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "Pcsx2Defs.h"
#include "PS2Edefs.h"
s32 SPU2init();
s32 SPU2reset();
s32 SPU2ps1reset();
s32 SPU2open(void *pDsp);
void SPU2close();
void SPU2shutdown();
void SPU2write(u32 mem, u16 value);
u16 SPU2read(u32 mem);
// extended funcs
// if start is 1, starts recording spu2 data, else stops
// returns a non zero value if successful
// for now, pData is not used
int SPU2setupRecording(int start, std::wstring* filename);
void SPU2setClockPtr(u32 *ptr);
void SPU2async(u32 cycles);
s32 SPU2freeze(int mode, freezeData *data);
void SPU2configure();
void SPU2about();
s32 SPU2test();
#include "Spu2replay.h"
extern u8 callirq;
extern void (*_irqcallback)();
extern void (*dma4callback)();
extern void (*dma7callback)();
extern s16 *input_data;
extern u32 input_data_ptr;
extern double srate_pv;
extern int recording;
extern u32 lClocks;
extern u32 *cyclePtr;
extern void SPU2writeLog(const char *action, u32 rmem, u16 value);
extern void TimeUpdate(u32 cClocks);
extern void SPU2_FastWrite(u32 rmem, u16 value);
extern void LowPassFilterInit();
//#define PCM24_S1_INTERLEAVE

167
pcsx2/SPU2/ReadInput.cpp Normal file
View File

@ -0,0 +1,167 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "Dma.h"
#include "PS2E-spu2.h" // required for ENABLE_NEW_IOPDMA_SPU2 define
// Core 0 Input is "SPDIF mode" - Source audio is AC3 compressed.
// Core 1 Input is "CDDA mode" - Source audio data is 32 bits.
// PS2 note: Very! few PS2 games use this mode. Some PSX games used it, however no
// *known* PS2 game does since it was likely only available if the game was recorded to CD
// media (ie, not available in DVD mode, which almost all PS2 games use). Plus PS2 games
// generally prefer to use ADPCM streaming audio since they need as much storage space as
// possible for FMVs and high-def textures.
//
StereoOut32 V_Core::ReadInput_HiFi()
{
if (psxmode)
ConLog("ReadInput_HiFi!!!!!\n");
InputPosRead &= ~1;
//
//#ifdef PCM24_S1_INTERLEAVE
// StereoOut32 retval(
// *((s32*)(ADMATempBuffer+(InputPosRead<<1))),
// *((s32*)(ADMATempBuffer+(InputPosRead<<1)+2))
// );
//#else
// StereoOut32 retval(
// (s32&)(ADMATempBuffer[InputPosRead]),
// (s32&)(ADMATempBuffer[InputPosRead+0x200])
// );
//#endif
StereoOut32 retval(
(s32 &)(*GetMemPtr(0x2000 + (Index << 10) + InputPosRead)),
(s32 &)(*GetMemPtr(0x2200 + (Index << 10) + InputPosRead)));
if (Index == 1) {
// CDDA Mode:
// give 30 bit data (SndOut downsamples the rest of the way)
// HACKFIX: 28 bits seems better according to rama. I should take some time and do some
// bitcounting on this one. --air
retval.Left >>= 4;
retval.Right >>= 4;
}
InputPosRead += 2;
// Why does CDDA mode check for InputPos == 0x100? In the old code, SPDIF mode did not but CDDA did.
// One of these seems wrong, they should be the same. Since standard ADMA checks too I'm assuming that as default. -- air
if ((InputPosRead == 0x100) || (InputPosRead >= 0x200)) {
AdmaInProgress = 0;
if (InputDataLeft >= 0x200) {
#ifdef PCM24_S1_INTERLEAVE
AutoDMAReadBuffer(1);
#else
AutoDMAReadBuffer(0);
#endif
AdmaInProgress = 1;
TSA = (Index << 10) + InputPosRead;
if (InputDataLeft < 0x200) {
FileLog("[%10d] %s AutoDMA%c block end.\n", (Index == 1) ? "CDDA" : "SPDIF", Cycles, GetDmaIndexChar());
if (IsDevBuild) {
if (InputDataLeft > 0) {
if (MsgAutoDMA())
ConLog("WARNING: adma buffer didn't finish with a whole block!!\n");
}
}
InputDataLeft = 0;
// Hack, kinda. We call the interrupt early here, since PCSX2 doesn't like them delayed.
//DMAICounter = 1;
if (Index == 0) {
if (dma4callback)
dma4callback();
} else {
if (dma7callback)
dma7callback();
}
}
}
InputPosRead &= 0x1ff;
}
return retval;
}
StereoOut32 V_Core::ReadInput()
{
StereoOut32 retval;
if ((Index != 1) || ((PlayMode & 2) == 0)) {
for (int i = 0; i < 2; i++)
if (Cores[i].IRQEnable && 0x2000 + (Index << 10) + InputPosRead == (Cores[i].IRQA & 0xfffffdff))
SetIrqCall(i);
//retval = StereoOut32(
// (s32)ADMATempBuffer[InputPosRead],
// (s32)ADMATempBuffer[InputPosRead+0x200]
//);
retval = StereoOut32(
(s32)(*GetMemPtr(0x2000 + (Index << 10) + InputPosRead)),
(s32)(*GetMemPtr(0x2200 + (Index << 10) + InputPosRead)));
}
#ifdef PCSX2_DEVBUILD
DebugCores[Index].admaWaveformL[InputPosRead % 0x100] = retval.Left;
DebugCores[Index].admaWaveformR[InputPosRead % 0x100] = retval.Right;
#endif
InputPosRead++;
if (AutoDMACtrl & (Index + 1) && (InputPosRead == 0x100 || InputPosRead == 0x200)) {
AdmaInProgress = 0;
if (InputDataLeft >= 0x200) {
//u8 k=InputDataLeft>=InputDataProgress;
AutoDMAReadBuffer(0);
AdmaInProgress = 1;
TSA = (Index << 10) + InputPosRead;
if (InputDataLeft < 0x200) {
AutoDMACtrl |= ~3;
if (IsDevBuild) {
FileLog("[%10d] AutoDMA%c block end.\n", Cycles, GetDmaIndexChar());
if (InputDataLeft > 0) {
if (MsgAutoDMA())
ConLog("WARNING: adma buffer didn't finish with a whole block!!\n");
}
}
InputDataLeft = 0;
// Hack, kinda. We call the interrupt early here, since PCSX2 doesn't like them delayed.
//DMAICounter = 1;
if (Index == 0) {
if (dma4callback)
dma4callback();
} else {
if (dma7callback)
dma7callback();
}
}
}
}
InputPosRead &= 0x1ff;
return retval;
}

297
pcsx2/SPU2/RegLog.cpp Normal file
View File

@ -0,0 +1,297 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
const char *ParamNames[8] = {"VOLL", "VOLR", "PITCH", "ADSR1", "ADSR2", "ENVX", "VOLXL", "VOLXR"};
const char *AddressNames[6] = {"SSAH", "SSAL", "LSAH", "LSAL", "NAXH", "NAXL"};
__forceinline void _RegLog_(const char *action, int level, const char *RName, u32 mem, u32 core, u16 value)
{
if (level > 1)
FileLog("[%10d] SPU2 %s mem %08x (core %d, register %s) value %04x\n",
Cycles, action, mem, core, RName, value);
}
#define RegLog(lev, rname, mem, core, val) _RegLog_(action, lev, rname, mem, core, val)
void SPU2writeLog(const char *action, u32 rmem, u16 value)
{
if (!IsDevBuild)
return;
//u32 vx=0, vc=0;
u32 core = 0, omem, mem;
omem = mem = rmem & 0x7FF; //FFFF;
if (mem & 0x400) {
omem ^= 0x400;
core = 1;
}
if (omem < 0x0180) // Voice Params (VP)
{
const u32 voice = (omem & 0x1F0) >> 4;
const u32 param = (omem & 0xF) >> 1;
char dest[192];
sprintf(dest, "Voice %d %s", voice, ParamNames[param]);
RegLog(2, dest, rmem, core, value);
} else if ((omem >= 0x01C0) && (omem < 0x02E0)) // Voice Addressing Params (VA)
{
const u32 voice = ((omem - 0x01C0) / 12);
const u32 address = ((omem - 0x01C0) % 12) >> 1;
char dest[192];
sprintf(dest, "Voice %d %s", voice, AddressNames[address]);
RegLog(2, dest, rmem, core, value);
} else if ((mem >= 0x0760) && (mem < 0x07b0)) {
omem = mem;
core = 0;
if (mem >= 0x0788) {
omem -= 0x28;
core = 1;
}
switch (omem) {
case REG_P_EVOLL:
RegLog(2, "EVOLL", rmem, core, value);
break;
case REG_P_EVOLR:
RegLog(2, "EVOLR", rmem, core, value);
break;
case REG_P_AVOLL:
if (core) {
RegLog(2, "AVOLL", rmem, core, value);
}
break;
case REG_P_AVOLR:
if (core) {
RegLog(2, "AVOLR", rmem, core, value);
}
break;
case REG_P_BVOLL:
RegLog(2, "BVOLL", rmem, core, value);
break;
case REG_P_BVOLR:
RegLog(2, "BVOLR", rmem, core, value);
break;
case REG_P_MVOLXL:
RegLog(2, "MVOLXL", rmem, core, value);
break;
case REG_P_MVOLXR:
RegLog(2, "MVOLXR", rmem, core, value);
break;
case R_IIR_VOL:
RegLog(2, "IIR_VOL", rmem, core, value);
break;
case R_COMB1_VOL:
RegLog(2, "COMB1_VOL", rmem, core, value);
break;
case R_COMB2_VOL:
RegLog(2, "COMB2_VOL", rmem, core, value);
break;
case R_COMB3_VOL:
RegLog(2, "COMB3_VOL", rmem, core, value);
break;
case R_COMB4_VOL:
RegLog(2, "COMB4_VOL", rmem, core, value);
break;
case R_WALL_VOL:
RegLog(2, "WALL_VOL", rmem, core, value);
break;
case R_APF1_VOL:
RegLog(2, "APF1_VOL", rmem, core, value);
break;
case R_APF2_VOL:
RegLog(2, "APF2_VOL", rmem, core, value);
break;
case R_IN_COEF_L:
RegLog(2, "IN_COEF_L", rmem, core, value);
break;
case R_IN_COEF_R:
RegLog(2, "IN_COEF_R", rmem, core, value);
break;
}
} else if ((mem >= 0x07C0) && (mem < 0x07CE)) {
switch (mem) {
case SPDIF_OUT:
RegLog(2, "SPDIF_OUT", rmem, -1, value);
break;
case SPDIF_IRQINFO:
RegLog(2, "SPDIF_IRQINFO", rmem, -1, value);
break;
case 0x7c4:
if (Spdif.Unknown1 != value)
ConLog("* SPU2-X: SPDIF Unknown Register 1 set to %04x\n", value);
RegLog(2, "SPDIF_UNKNOWN1", rmem, -1, value);
break;
case SPDIF_MODE:
if (Spdif.Mode != value)
ConLog("* SPU2-X: SPDIF Mode set to %04x\n", value);
RegLog(2, "SPDIF_MODE", rmem, -1, value);
break;
case SPDIF_MEDIA:
if (Spdif.Media != value)
ConLog("* SPU2-X: SPDIF Media set to %04x\n", value);
RegLog(2, "SPDIF_MEDIA", rmem, -1, value);
break;
case 0x7ca:
if (Spdif.Unknown2 != value)
ConLog("* SPU2-X: SPDIF Unknown Register 2 set to %04x\n", value);
RegLog(2, "SPDIF_UNKNOWN2", rmem, -1, value);
break;
case SPDIF_PROTECT:
if (Spdif.Protection != value)
ConLog("* SPU2-X: SPDIF Copy set to %04x\n", value);
RegLog(2, "SPDIF_PROTECT", rmem, -1, value);
break;
}
UpdateSpdifMode();
} else {
switch (omem) {
case REG_C_ATTR:
RegLog(4, "ATTR", rmem, core, value);
break;
case REG_S_PMON:
RegLog(1, "PMON0", rmem, core, value);
break;
case (REG_S_PMON + 2):
RegLog(1, "PMON1", rmem, core, value);
break;
case REG_S_NON:
RegLog(1, "NON0", rmem, core, value);
break;
case (REG_S_NON + 2):
RegLog(1, "NON1", rmem, core, value);
break;
case REG_S_VMIXL:
RegLog(1, "VMIXL0", rmem, core, value);
break;
case (REG_S_VMIXL + 2):
RegLog(1, "VMIXL1", rmem, core, value);
break;
case REG_S_VMIXEL:
RegLog(1, "VMIXEL0", rmem, core, value);
break;
case (REG_S_VMIXEL + 2):
RegLog(1, "VMIXEL1", rmem, core, value);
break;
case REG_S_VMIXR:
RegLog(1, "VMIXR0", rmem, core, value);
break;
case (REG_S_VMIXR + 2):
RegLog(1, "VMIXR1", rmem, core, value);
break;
case REG_S_VMIXER:
RegLog(1, "VMIXER0", rmem, core, value);
break;
case (REG_S_VMIXER + 2):
RegLog(1, "VMIXER1", rmem, core, value);
break;
case REG_P_MMIX:
RegLog(1, "MMIX", rmem, core, value);
break;
case REG_A_IRQA:
RegLog(2, "IRQAH", rmem, core, value);
break;
case (REG_A_IRQA + 2):
RegLog(2, "IRQAL", rmem, core, value);
break;
case (REG_S_KON + 2):
RegLog(1, "KON1", rmem, core, value);
break;
case REG_S_KON:
RegLog(1, "KON0", rmem, core, value);
break;
case (REG_S_KOFF + 2):
RegLog(1, "KOFF1", rmem, core, value);
break;
case REG_S_KOFF:
RegLog(1, "KOFF0", rmem, core, value);
break;
case REG_A_TSA:
RegLog(2, "TSAH", rmem, core, value);
break;
case (REG_A_TSA + 2):
RegLog(2, "TSAL", rmem, core, value);
break;
case REG_S_ENDX:
//ConLog("* SPU2-X: Core %d ENDX cleared!\n",core);
RegLog(2, "ENDX0", rmem, core, value);
break;
case (REG_S_ENDX + 2):
//ConLog("* SPU2-X: Core %d ENDX cleared!\n",core);
RegLog(2, "ENDX1", rmem, core, value);
break;
case REG_P_MVOLL:
RegLog(1, "MVOLL", rmem, core, value);
break;
case REG_P_MVOLR:
RegLog(1, "MVOLR", rmem, core, value);
break;
case REG_S_ADMAS:
RegLog(3, "ADMAS", rmem, core, value);
//ConLog("* SPU2-X: Core %d AutoDMAControl set to %d\n",core,value);
break;
case REG_P_STATX:
RegLog(3, "STATX", rmem, core, value);
break;
case REG_A_ESA:
RegLog(2, "ESAH", rmem, core, value);
break;
case (REG_A_ESA + 2):
RegLog(2, "ESAL", rmem, core, value);
break;
case REG_A_EEA:
RegLog(2, "EEAH", rmem, core, value);
break;
#define LOG_REVB_REG(n, t) \
case R_##n: \
RegLog(2, t "H", mem, core, value); \
break; \
case (R_##n + 2): \
RegLog(2, t "L", mem, core, value); \
break;
LOG_REVB_REG(APF1_SIZE, "APF1_SIZE")
LOG_REVB_REG(APF2_SIZE, "APF2_SIZE")
LOG_REVB_REG(SAME_L_SRC, "SAME_L_SRC")
LOG_REVB_REG(SAME_R_SRC, "SAME_R_SRC")
LOG_REVB_REG(DIFF_L_SRC, "DIFF_L_SRC")
LOG_REVB_REG(DIFF_R_SRC, "DIFF_R_SRC")
LOG_REVB_REG(SAME_L_DST, "SAME_L_DST")
LOG_REVB_REG(SAME_R_DST, "SAME_R_DST")
LOG_REVB_REG(DIFF_L_DST, "DIFF_L_DST")
LOG_REVB_REG(DIFF_R_DST, "DIFF_R_DST")
LOG_REVB_REG(COMB1_L_SRC, "COMB1_L_SRC")
LOG_REVB_REG(COMB1_R_SRC, "COMB1_R_SRC")
LOG_REVB_REG(COMB2_L_SRC, "COMB2_L_SRC")
LOG_REVB_REG(COMB2_R_SRC, "COMB2_R_SRC")
LOG_REVB_REG(COMB3_L_SRC, "COMB3_L_SRC")
LOG_REVB_REG(COMB3_R_SRC, "COMB3_R_SRC")
LOG_REVB_REG(COMB4_L_SRC, "COMB4_L_SRC")
LOG_REVB_REG(COMB4_R_SRC, "COMB4_R_SRC")
LOG_REVB_REG(APF1_L_DST, "APF1_L_DST")
LOG_REVB_REG(APF1_R_DST, "APF1_R_DST")
LOG_REVB_REG(APF2_L_DST, "APF2_L_DST")
LOG_REVB_REG(APF2_R_DST, "APF2_R_DST")
default:
RegLog(2, "UNKNOWN", rmem, core, value);
spu2Ru16(mem) = value;
}
}
}

303
pcsx2/SPU2/RegTable.cpp Normal file
View File

@ -0,0 +1,303 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#define PCORE(c, p) \
U16P(Cores[c].p)
#define PVCP(c, v, p) \
PCORE(c, Voices[v].p)
#define PVC(c, v) \
PVCP(c, v, Volume.Left.Reg_VOL) \
, \
PVCP(c, v, Volume.Right.Reg_VOL), \
PVCP(c, v, Pitch), \
PVCP(c, v, ADSR.regADSR1), \
PVCP(c, v, ADSR.regADSR2), \
PVCP(c, v, ADSR.Value) + 1, \
PVCP(c, v, Volume.Left.Value) + 1, \
PVCP(c, v, Volume.Right.Value) + 1
#define PVCA(c, v) \
PVCP(c, v, StartA) + 1, \
PVCP(c, v, StartA), \
PVCP(c, v, LoopStartA) + 1, \
PVCP(c, v, LoopStartA), \
PVCP(c, v, NextA) + 1, \
PVCP(c, v, NextA)
#define PRAW(a) \
((u16 *)NULL)
#define PREVB_REG(c, n) \
PCORE(c, Revb.n) + 1, \
PCORE(c, Revb.n)
u16 *regtable[0x401];
u16 const *const regtable_original[0x401] =
{
// Voice Params: 8 params, 24 voices = 0x180 bytes
PVC(0, 0), PVC(0, 1), PVC(0, 2), PVC(0, 3), PVC(0, 4), PVC(0, 5),
PVC(0, 6), PVC(0, 7), PVC(0, 8), PVC(0, 9), PVC(0, 10), PVC(0, 11),
PVC(0, 12), PVC(0, 13), PVC(0, 14), PVC(0, 15), PVC(0, 16), PVC(0, 17),
PVC(0, 18), PVC(0, 19), PVC(0, 20), PVC(0, 21), PVC(0, 22), PVC(0, 23),
PCORE(0, Regs.PMON),
PCORE(0, Regs.PMON) + 1,
PCORE(0, Regs.NON),
PCORE(0, Regs.NON) + 1,
PCORE(0, Regs.VMIXL),
PCORE(0, Regs.VMIXL) + 1,
PCORE(0, Regs.VMIXEL),
PCORE(0, Regs.VMIXEL) + 1,
PCORE(0, Regs.VMIXR),
PCORE(0, Regs.VMIXR) + 1,
PCORE(0, Regs.VMIXER),
PCORE(0, Regs.VMIXER) + 1,
PCORE(0, Regs.MMIX),
PCORE(0, Regs.ATTR),
PCORE(0, IRQA) + 1,
PCORE(0, IRQA),
NULL, NULL,
NULL, NULL,
PCORE(0, TSA) + 1,
PCORE(0, TSA),
PRAW(REG__1AC), PRAW(REG__1AE),
PCORE(0, AutoDMACtrl),
PRAW(0x1b2), PRAW(0x1b4), PRAW(0x1b6), PRAW(0x1b8), PRAW(0x1ba), PRAW(0x1bc), PRAW(0x1be), // unknown
// Voice Addresses
PVCA(0, 0), PVCA(0, 1), PVCA(0, 2), PVCA(0, 3), PVCA(0, 4), PVCA(0, 5),
PVCA(0, 6), PVCA(0, 7), PVCA(0, 8), PVCA(0, 9), PVCA(0, 10), PVCA(0, 11),
PVCA(0, 12), PVCA(0, 13), PVCA(0, 14), PVCA(0, 15), PVCA(0, 16), PVCA(0, 17),
PVCA(0, 18), PVCA(0, 19), PVCA(0, 20), PVCA(0, 21), PVCA(0, 22), PVCA(0, 23),
PCORE(0, ExtEffectsStartA) + 1,
PCORE(0, ExtEffectsStartA),
PREVB_REG(0, APF1_SIZE),
PREVB_REG(0, APF2_SIZE),
PREVB_REG(0, SAME_L_DST),
PREVB_REG(0, SAME_R_DST),
PREVB_REG(0, COMB1_L_SRC),
PREVB_REG(0, COMB1_R_SRC),
PREVB_REG(0, COMB2_L_SRC),
PREVB_REG(0, COMB2_R_SRC),
PREVB_REG(0, SAME_L_SRC),
PREVB_REG(0, SAME_R_SRC),
PREVB_REG(0, DIFF_L_DST),
PREVB_REG(0, DIFF_R_DST),
PREVB_REG(0, COMB3_L_SRC),
PREVB_REG(0, COMB3_R_SRC),
PREVB_REG(0, COMB4_L_SRC),
PREVB_REG(0, COMB4_R_SRC),
PREVB_REG(0, DIFF_L_SRC),
PREVB_REG(0, DIFF_R_SRC),
PREVB_REG(0, APF1_L_DST),
PREVB_REG(0, APF1_R_DST),
PREVB_REG(0, APF2_L_DST),
PREVB_REG(0, APF2_R_DST),
PCORE(0, ExtEffectsEndA) + 1,
PCORE(0, ExtEffectsEndA),
PCORE(0, Regs.ENDX),
PCORE(0, Regs.ENDX) + 1,
PCORE(0, Regs.STATX),
//0x346 here
PRAW(0x346),
PRAW(0x348), PRAW(0x34A), PRAW(0x34C), PRAW(0x34E),
PRAW(0x350), PRAW(0x352), PRAW(0x354), PRAW(0x356),
PRAW(0x358), PRAW(0x35A), PRAW(0x35C), PRAW(0x35E),
PRAW(0x360), PRAW(0x362), PRAW(0x364), PRAW(0x366),
PRAW(0x368), PRAW(0x36A), PRAW(0x36C), PRAW(0x36E),
PRAW(0x370), PRAW(0x372), PRAW(0x374), PRAW(0x376),
PRAW(0x378), PRAW(0x37A), PRAW(0x37C), PRAW(0x37E),
PRAW(0x380), PRAW(0x382), PRAW(0x384), PRAW(0x386),
PRAW(0x388), PRAW(0x38A), PRAW(0x38C), PRAW(0x38E),
PRAW(0x390), PRAW(0x392), PRAW(0x394), PRAW(0x396),
PRAW(0x398), PRAW(0x39A), PRAW(0x39C), PRAW(0x39E),
PRAW(0x3A0), PRAW(0x3A2), PRAW(0x3A4), PRAW(0x3A6),
PRAW(0x3A8), PRAW(0x3AA), PRAW(0x3AC), PRAW(0x3AE),
PRAW(0x3B0), PRAW(0x3B2), PRAW(0x3B4), PRAW(0x3B6),
PRAW(0x3B8), PRAW(0x3BA), PRAW(0x3BC), PRAW(0x3BE),
PRAW(0x3C0), PRAW(0x3C2), PRAW(0x3C4), PRAW(0x3C6),
PRAW(0x3C8), PRAW(0x3CA), PRAW(0x3CC), PRAW(0x3CE),
PRAW(0x3D0), PRAW(0x3D2), PRAW(0x3D4), PRAW(0x3D6),
PRAW(0x3D8), PRAW(0x3DA), PRAW(0x3DC), PRAW(0x3DE),
PRAW(0x3E0), PRAW(0x3E2), PRAW(0x3E4), PRAW(0x3E6),
PRAW(0x3E8), PRAW(0x3EA), PRAW(0x3EC), PRAW(0x3EE),
PRAW(0x3F0), PRAW(0x3F2), PRAW(0x3F4), PRAW(0x3F6),
PRAW(0x3F8), PRAW(0x3FA), PRAW(0x3FC), PRAW(0x3FE),
//AND... we reached 0x400!
// Voice Params: 8 params, 24 voices = 0x180 bytes
PVC(1, 0), PVC(1, 1), PVC(1, 2), PVC(1, 3), PVC(1, 4), PVC(1, 5),
PVC(1, 6), PVC(1, 7), PVC(1, 8), PVC(1, 9), PVC(1, 10), PVC(1, 11),
PVC(1, 12), PVC(1, 13), PVC(1, 14), PVC(1, 15), PVC(1, 16), PVC(1, 17),
PVC(1, 18), PVC(1, 19), PVC(1, 20), PVC(1, 21), PVC(1, 22), PVC(1, 23),
PCORE(1, Regs.PMON),
PCORE(1, Regs.PMON) + 1,
PCORE(1, Regs.NON),
PCORE(1, Regs.NON) + 1,
PCORE(1, Regs.VMIXL),
PCORE(1, Regs.VMIXL) + 1,
PCORE(1, Regs.VMIXEL),
PCORE(1, Regs.VMIXEL) + 1,
PCORE(1, Regs.VMIXR),
PCORE(1, Regs.VMIXR) + 1,
PCORE(1, Regs.VMIXER),
PCORE(1, Regs.VMIXER) + 1,
PCORE(1, Regs.MMIX),
PCORE(1, Regs.ATTR),
PCORE(1, IRQA) + 1,
PCORE(1, IRQA),
NULL, NULL,
NULL, NULL,
PCORE(1, TSA) + 1,
PCORE(1, TSA),
PRAW(0x5ac), PRAW(0x5ae),
PCORE(1, AutoDMACtrl),
PRAW(0x5b2), PRAW(0x5b4), PRAW(0x5b6), PRAW(0x5b8), PRAW(0x5ba), PRAW(0x5bc), PRAW(0x5be), // unknown
// Voice Addresses
PVCA(1, 0), PVCA(1, 1), PVCA(1, 2), PVCA(1, 3), PVCA(1, 4), PVCA(1, 5),
PVCA(1, 6), PVCA(1, 7), PVCA(1, 8), PVCA(1, 9), PVCA(1, 10), PVCA(1, 11),
PVCA(1, 12), PVCA(1, 13), PVCA(1, 14), PVCA(1, 15), PVCA(1, 16), PVCA(1, 17),
PVCA(1, 18), PVCA(1, 19), PVCA(1, 20), PVCA(1, 21), PVCA(1, 22), PVCA(1, 23),
PCORE(1, ExtEffectsStartA) + 1,
PCORE(1, ExtEffectsStartA),
PREVB_REG(1, APF1_SIZE),
PREVB_REG(1, APF2_SIZE),
PREVB_REG(1, SAME_L_DST),
PREVB_REG(1, SAME_R_DST),
PREVB_REG(1, COMB1_L_SRC),
PREVB_REG(1, COMB1_R_SRC),
PREVB_REG(1, COMB2_L_SRC),
PREVB_REG(1, COMB2_R_SRC),
PREVB_REG(1, SAME_L_SRC),
PREVB_REG(1, SAME_R_SRC),
PREVB_REG(1, DIFF_L_DST),
PREVB_REG(1, DIFF_R_DST),
PREVB_REG(1, COMB3_L_SRC),
PREVB_REG(1, COMB3_R_SRC),
PREVB_REG(1, COMB4_L_SRC),
PREVB_REG(1, COMB4_R_SRC),
PREVB_REG(1, DIFF_L_SRC),
PREVB_REG(1, DIFF_R_SRC),
PREVB_REG(1, APF1_L_DST),
PREVB_REG(1, APF1_R_DST),
PREVB_REG(1, APF2_L_DST),
PREVB_REG(1, APF2_R_DST),
PCORE(1, ExtEffectsEndA) + 1,
PCORE(1, ExtEffectsEndA),
PCORE(1, Regs.ENDX),
PCORE(1, Regs.ENDX) + 1,
PCORE(1, Regs.STATX),
PRAW(0x746),
PRAW(0x748), PRAW(0x74A), PRAW(0x74C), PRAW(0x74E),
PRAW(0x750), PRAW(0x752), PRAW(0x754), PRAW(0x756),
PRAW(0x758), PRAW(0x75A), PRAW(0x75C), PRAW(0x75E),
//0x760: weird area
PCORE(0, MasterVol.Left.Reg_VOL),
PCORE(0, MasterVol.Right.Reg_VOL),
PCORE(0, FxVol.Left) + 1,
PCORE(0, FxVol.Right) + 1,
PCORE(0, ExtVol.Left) + 1,
PCORE(0, ExtVol.Right) + 1,
PCORE(0, InpVol.Left) + 1,
PCORE(0, InpVol.Right) + 1,
PCORE(0, MasterVol.Left.Value) + 1,
PCORE(0, MasterVol.Right.Value) + 1,
PCORE(0, Revb.IIR_VOL),
PCORE(0, Revb.COMB1_VOL),
PCORE(0, Revb.COMB2_VOL),
PCORE(0, Revb.COMB3_VOL),
PCORE(0, Revb.COMB4_VOL),
PCORE(0, Revb.WALL_VOL),
PCORE(0, Revb.APF1_VOL),
PCORE(0, Revb.APF2_VOL),
PCORE(0, Revb.IN_COEF_L),
PCORE(0, Revb.IN_COEF_R),
PCORE(1, MasterVol.Left.Reg_VOL),
PCORE(1, MasterVol.Right.Reg_VOL),
PCORE(1, FxVol.Left) + 1,
PCORE(1, FxVol.Right) + 1,
PCORE(1, ExtVol.Left) + 1,
PCORE(1, ExtVol.Right) + 1,
PCORE(1, InpVol.Left) + 1,
PCORE(1, InpVol.Right) + 1,
PCORE(1, MasterVol.Left.Value) + 1,
PCORE(1, MasterVol.Right.Value) + 1,
PCORE(1, Revb.IIR_VOL),
PCORE(1, Revb.COMB1_VOL),
PCORE(1, Revb.COMB2_VOL),
PCORE(1, Revb.COMB3_VOL),
PCORE(1, Revb.COMB4_VOL),
PCORE(1, Revb.WALL_VOL),
PCORE(1, Revb.APF1_VOL),
PCORE(1, Revb.APF2_VOL),
PCORE(1, Revb.IN_COEF_L),
PCORE(1, Revb.IN_COEF_R),
PRAW(0x7B0), PRAW(0x7B2), PRAW(0x7B4), PRAW(0x7B6),
PRAW(0x7B8), PRAW(0x7BA), PRAW(0x7BC), PRAW(0x7BE),
// SPDIF interface
U16P(Spdif.Out),
U16P(Spdif.Info),
U16P(Spdif.Unknown1),
U16P(Spdif.Mode),
U16P(Spdif.Media),
U16P(Spdif.Unknown2),
U16P(Spdif.Protection),
PRAW(0x7CE),
PRAW(0x7D0), PRAW(0x7D2), PRAW(0x7D4), PRAW(0x7D6),
PRAW(0x7D8), PRAW(0x7DA), PRAW(0x7DC), PRAW(0x7DE),
PRAW(0x7E0), PRAW(0x7E2), PRAW(0x7E4), PRAW(0x7E6),
PRAW(0x7E8), PRAW(0x7EA), PRAW(0x7EC), PRAW(0x7EE),
PRAW(0x7F0), PRAW(0x7F2), PRAW(0x7F4), PRAW(0x7F6),
PRAW(0x7F8), PRAW(0x7FA), PRAW(0x7FC), PRAW(0x7FE),
NULL};

133
pcsx2/SPU2/Reverb.cpp Normal file
View File

@ -0,0 +1,133 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
__forceinline s32 V_Core::RevbGetIndexer(s32 offset)
{
u32 pos = ReverbX + offset;
// Fast and simple single step wrapping, made possible by the preparation of the
// effects buffer addresses.
if (pos > EffectsEndA) {
pos -= EffectsEndA + 1;
pos += EffectsStartA;
}
assert(pos >= EffectsStartA && pos <= EffectsEndA);
return pos;
}
void V_Core::Reverb_AdvanceBuffer()
{
if (RevBuffers.NeedsUpdated)
UpdateEffectsBufferSize();
if ((Cycles & 1) && (EffectsBufferSize > 0)) {
ReverbX += 1;
if (ReverbX >= (u32)EffectsBufferSize)
ReverbX = 0;
}
}
/////////////////////////////////////////////////////////////////////////////////////////
StereoOut32 V_Core::DoReverb(const StereoOut32 &Input)
{
if (EffectsBufferSize <= 0) {
return StereoOut32::Empty;
}
bool R = Cycles & 1;
// Calculate the read/write addresses we'll be needing for this session of reverb.
const u32 same_src = RevbGetIndexer(R ? RevBuffers.SAME_R_SRC : RevBuffers.SAME_L_SRC);
const u32 same_dst = RevbGetIndexer(R ? RevBuffers.SAME_R_DST : RevBuffers.SAME_L_DST);
const u32 same_prv = RevbGetIndexer(R ? RevBuffers.SAME_R_PRV : RevBuffers.SAME_L_PRV);
const u32 diff_src = RevbGetIndexer(R ? RevBuffers.DIFF_L_SRC : RevBuffers.DIFF_R_SRC);
const u32 diff_dst = RevbGetIndexer(R ? RevBuffers.DIFF_R_DST : RevBuffers.DIFF_L_DST);
const u32 diff_prv = RevbGetIndexer(R ? RevBuffers.DIFF_R_PRV : RevBuffers.DIFF_L_PRV);
const u32 comb1_src = RevbGetIndexer(R ? RevBuffers.COMB1_R_SRC : RevBuffers.COMB1_L_SRC);
const u32 comb2_src = RevbGetIndexer(R ? RevBuffers.COMB2_R_SRC : RevBuffers.COMB2_L_SRC);
const u32 comb3_src = RevbGetIndexer(R ? RevBuffers.COMB3_R_SRC : RevBuffers.COMB3_L_SRC);
const u32 comb4_src = RevbGetIndexer(R ? RevBuffers.COMB4_R_SRC : RevBuffers.COMB4_L_SRC);
const u32 apf1_src = RevbGetIndexer(R ? RevBuffers.APF1_R_SRC : RevBuffers.APF1_L_SRC);
const u32 apf1_dst = RevbGetIndexer(R ? RevBuffers.APF1_R_DST : RevBuffers.APF1_L_DST);
const u32 apf2_src = RevbGetIndexer(R ? RevBuffers.APF2_R_SRC : RevBuffers.APF2_L_SRC);
const u32 apf2_dst = RevbGetIndexer(R ? RevBuffers.APF2_R_DST : RevBuffers.APF2_L_DST);
// -----------------------------------------
// Optimized IRQ Testing !
// -----------------------------------------
// This test is enhanced by using the reverb effects area begin/end test as a
// shortcut, since all buffer addresses are within that area. If the IRQA isn't
// within that zone then the "bulk" of the test is skipped, so this should only
// be a slowdown on a few evil games.
for (int i = 0; i < 2; i++) {
if (Cores[i].IRQEnable && ((Cores[i].IRQA >= EffectsStartA) && (Cores[i].IRQA <= EffectsEndA))) {
if ((Cores[i].IRQA == same_src) || (Cores[i].IRQA == diff_src) ||
(Cores[i].IRQA == same_dst) || (Cores[i].IRQA == diff_dst) ||
(Cores[i].IRQA == same_prv) || (Cores[i].IRQA == diff_prv) ||
(Cores[i].IRQA == comb1_src) || (Cores[i].IRQA == comb2_src) ||
(Cores[i].IRQA == comb3_src) || (Cores[i].IRQA == comb4_src) ||
(Cores[i].IRQA == apf1_dst) || (Cores[i].IRQA == apf1_src) ||
(Cores[i].IRQA == apf2_dst) || (Cores[i].IRQA == apf2_src)) {
//printf("Core %d IRQ Called (Reverb). IRQA = %x\n",i,addr);
SetIrqCall(i);
}
}
}
// Reverb algorithm pretty much directly ripped from http://drhell.web.fc2.com/ps1/
// minus the 35 step FIR which just seems to break things.
s32 in, same, diff, apf1, apf2, out;
#define MUL(x, y) ((x) * (y) >> 15)
in = MUL(R ? Revb.IN_COEF_R : Revb.IN_COEF_L, R ? Input.Right : Input.Left);
same = MUL(Revb.IIR_VOL, in + MUL(Revb.WALL_VOL, _spu2mem[same_src]) - _spu2mem[same_prv]) + _spu2mem[same_prv];
diff = MUL(Revb.IIR_VOL, in + MUL(Revb.WALL_VOL, _spu2mem[diff_src]) - _spu2mem[diff_prv]) + _spu2mem[diff_prv];
out = MUL(Revb.COMB1_VOL, _spu2mem[comb1_src]) + MUL(Revb.COMB2_VOL, _spu2mem[comb2_src]) + MUL(Revb.COMB3_VOL, _spu2mem[comb3_src]) + MUL(Revb.COMB4_VOL, _spu2mem[comb4_src]);
apf1 = out - MUL(Revb.APF1_VOL, _spu2mem[apf1_src]);
out = _spu2mem[apf1_src] + MUL(Revb.APF1_VOL, apf1);
apf2 = out - MUL(Revb.APF2_VOL, _spu2mem[apf2_src]);
out = _spu2mem[apf2_src] + MUL(Revb.APF2_VOL, apf2);
// According to no$psx the effects always run but don't always write back, see check in V_Core::Mix
if (FxEnable) {
_spu2mem[same_dst] = clamp_mix(same);
_spu2mem[diff_dst] = clamp_mix(diff);
_spu2mem[apf1_dst] = clamp_mix(apf1);
_spu2mem[apf2_dst] = clamp_mix(apf2);
}
(R ? LastEffect.Right : LastEffect.Left) = -clamp_mix(out);
return LastEffect;
}

495
pcsx2/SPU2/SndOut.cpp Normal file
View File

@ -0,0 +1,495 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
StereoOut32 StereoOut32::Empty(0, 0);
StereoOut32::StereoOut32(const StereoOut16 &src)
: Left(src.Left)
, Right(src.Right)
{
}
StereoOut32::StereoOut32(const StereoOutFloat &src)
: Left((s32)(src.Left * 2147483647.0f))
, Right((s32)(src.Right * 2147483647.0f))
{
}
StereoOut16 StereoOut32::DownSample() const
{
return StereoOut16(
Left >> SndOutVolumeShift,
Right >> SndOutVolumeShift);
}
StereoOut32 StereoOut16::UpSample() const
{
return StereoOut32(
Left << SndOutVolumeShift,
Right << SndOutVolumeShift);
}
class NullOutModule : public SndOutModule
{
public:
s32 Init() { return 0; }
void Close() {}
s32 Test() const { return 0; }
void Configure(uptr parent) {}
int GetEmptySampleCount() { return 0; }
const wchar_t *GetIdent() const
{
return L"nullout";
}
const wchar_t *GetLongName() const
{
return L"No Sound (Emulate SPU2 only)";
}
void ReadSettings()
{
}
void SetApiSettings(wxString api)
{
}
void WriteSettings() const
{
}
} NullOut;
SndOutModule *mods[] =
{
&NullOut,
#ifdef _MSC_VER
XAudio2Out,
DSoundOut,
WaveOut,
#endif
#if defined(_WIN32) || defined(SPU2X_PORTAUDIO)
PortaudioOut,
#endif
#if defined(SPU2X_SDL) || defined(SPU2X_SDL2)
SDLOut,
#endif
#if defined(__linux__) /* && defined(__ALSA__)*/
AlsaOut,
#endif
NULL // signals the end of our list
};
int FindOutputModuleById(const wchar_t *omodid)
{
int modcnt = 0;
while (mods[modcnt] != NULL) {
if (wcscmp(mods[modcnt]->GetIdent(), omodid) == 0)
break;
++modcnt;
}
return modcnt;
}
StereoOut32 *SndBuffer::m_buffer;
s32 SndBuffer::m_size;
__aligned(4) volatile s32 SndBuffer::m_rpos;
__aligned(4) volatile s32 SndBuffer::m_wpos;
bool SndBuffer::m_underrun_freeze;
StereoOut32 *SndBuffer::sndTempBuffer = NULL;
StereoOut16 *SndBuffer::sndTempBuffer16 = NULL;
int SndBuffer::sndTempProgress = 0;
int GetAlignedBufferSize(int comp)
{
return (comp + SndOutPacketSize - 1) & ~(SndOutPacketSize - 1);
}
// Returns TRUE if there is data to be output, or false if no data
// is available to be copied.
bool SndBuffer::CheckUnderrunStatus(int &nSamples, int &quietSampleCount)
{
quietSampleCount = 0;
int data = _GetApproximateDataInBuffer();
if (m_underrun_freeze) {
int toFill = m_size / ((SynchMode == 2) ? 32 : 400); // TimeStretch and Async off?
toFill = GetAlignedBufferSize(toFill);
// toFill is now aligned to a SndOutPacket
if (data < toFill) {
quietSampleCount = nSamples;
return false;
}
m_underrun_freeze = false;
if (MsgOverruns())
ConLog(" * SPU2 > Underrun compensation (%d packets buffered)\n", toFill / SndOutPacketSize);
lastPct = 0.0; // normalize timestretcher
} else if (data < nSamples) {
nSamples = data;
quietSampleCount = SndOutPacketSize - data;
m_underrun_freeze = true;
if (SynchMode == 0) // TimeStrech on
timeStretchUnderrun();
return nSamples != 0;
}
return true;
}
void SndBuffer::_InitFail()
{
// If a failure occurs, just initialize the NoSound driver. This'll allow
// the game to emulate properly (hopefully), albeit without sound.
OutputModule = FindOutputModuleById(NullOut.GetIdent());
mods[OutputModule]->Init();
}
int SndBuffer::_GetApproximateDataInBuffer()
{
// WARNING: not necessarily 100% up to date by the time it's used, but it will have to do.
return (m_wpos + m_size - m_rpos) % m_size;
}
void SndBuffer::_WriteSamples_Internal(StereoOut32 *bData, int nSamples)
{
// WARNING: This assumes the write will NOT wrap around,
// and also assumes there's enough free space in the buffer.
memcpy(m_buffer + m_wpos, bData, nSamples * sizeof(StereoOut32));
m_wpos = (m_wpos + nSamples) % m_size;
}
void SndBuffer::_DropSamples_Internal(int nSamples)
{
m_rpos = (m_rpos + nSamples) % m_size;
}
void SndBuffer::_ReadSamples_Internal(StereoOut32 *bData, int nSamples)
{
// WARNING: This assumes the read will NOT wrap around,
// and also assumes there's enough data in the buffer.
memcpy(bData, m_buffer + m_rpos, nSamples * sizeof(StereoOut32));
_DropSamples_Internal(nSamples);
}
void SndBuffer::_WriteSamples_Safe(StereoOut32 *bData, int nSamples)
{
// WARNING: This code assumes there's only ONE writing process.
if ((m_size - m_wpos) < nSamples) {
int b1 = m_size - m_wpos;
int b2 = nSamples - b1;
_WriteSamples_Internal(bData, b1);
_WriteSamples_Internal(bData + b1, b2);
} else {
_WriteSamples_Internal(bData, nSamples);
}
}
void SndBuffer::_ReadSamples_Safe(StereoOut32 *bData, int nSamples)
{
// WARNING: This code assumes there's only ONE reading process.
if ((m_size - m_rpos) < nSamples) {
int b1 = m_size - m_rpos;
int b2 = nSamples - b1;
_ReadSamples_Internal(bData, b1);
_ReadSamples_Internal(bData + b1, b2);
} else {
_ReadSamples_Internal(bData, nSamples);
}
}
// Note: When using with 32 bit output buffers, the user of this function is responsible
// for shifting the values to where they need to be manually. The fixed point depth of
// the sample output is determined by the SndOutVolumeShift, which is the number of bits
// to shift right to get a 16 bit result.
template <typename T>
void SndBuffer::ReadSamples(T *bData)
{
int nSamples = SndOutPacketSize;
// Problem:
// If the SPU2 gets even the least bit out of sync with the SndOut device,
// the readpos of the circular buffer will overtake the writepos,
// leading to a prolonged period of hopscotching read/write accesses (ie,
// lots of staticy crap sound for several seconds).
//
// Fix:
// If the read position overtakes the write position, abort the
// transfer immediately and force the SndOut driver to wait until
// the read buffer has filled up again before proceeding.
// This will cause one brief hiccup that can never exceed the user's
// set buffer length in duration.
int quietSamples;
if (CheckUnderrunStatus(nSamples, quietSamples)) {
pxAssume(nSamples <= SndOutPacketSize);
// WARNING: This code assumes there's only ONE reading process.
int b1 = m_size - m_rpos;
if (b1 > nSamples)
b1 = nSamples;
if (AdvancedVolumeControl) {
// First part
for (int i = 0; i < b1; i++)
bData[i].AdjustFrom(m_buffer[i + m_rpos]);
// Second part
int b2 = nSamples - b1;
for (int i = 0; i < b2; i++)
bData[i + b1].AdjustFrom(m_buffer[i]);
} else {
// First part
for (int i = 0; i < b1; i++)
bData[i].ResampleFrom(m_buffer[i + m_rpos]);
// Second part
int b2 = nSamples - b1;
for (int i = 0; i < b2; i++)
bData[i + b1].ResampleFrom(m_buffer[i]);
}
_DropSamples_Internal(nSamples);
}
// If quietSamples != 0 it means we have an underrun...
// Let's just dull out some silence, because that's usually the least
// painful way of dealing with underruns:
std::fill_n(bData, quietSamples, T{});
}
template void SndBuffer::ReadSamples(StereoOut16 *);
template void SndBuffer::ReadSamples(StereoOut32 *);
//template void SndBuffer::ReadSamples(StereoOutFloat*);
template void SndBuffer::ReadSamples(Stereo21Out16 *);
template void SndBuffer::ReadSamples(Stereo40Out16 *);
template void SndBuffer::ReadSamples(Stereo41Out16 *);
template void SndBuffer::ReadSamples(Stereo51Out16 *);
template void SndBuffer::ReadSamples(Stereo51Out16Dpl *);
template void SndBuffer::ReadSamples(Stereo51Out16DplII *);
template void SndBuffer::ReadSamples(Stereo71Out16 *);
template void SndBuffer::ReadSamples(Stereo20Out32 *);
template void SndBuffer::ReadSamples(Stereo21Out32 *);
template void SndBuffer::ReadSamples(Stereo40Out32 *);
template void SndBuffer::ReadSamples(Stereo41Out32 *);
template void SndBuffer::ReadSamples(Stereo51Out32 *);
template void SndBuffer::ReadSamples(Stereo51Out32Dpl *);
template void SndBuffer::ReadSamples(Stereo51Out32DplII *);
template void SndBuffer::ReadSamples(Stereo71Out32 *);
void SndBuffer::_WriteSamples(StereoOut32 *bData, int nSamples)
{
m_predictData = 0;
// Problem:
// If the SPU2 gets out of sync with the SndOut device, the writepos of the
// circular buffer will overtake the readpos, leading to a prolonged period
// of hopscotching read/write accesses (ie, lots of staticy crap sound for
// several seconds).
//
// Compromise:
// When an overrun occurs, we adapt by discarding a portion of the buffer.
// The older portion of the buffer is discarded rather than incoming data,
// so that the overall audio synchronization is better.
int free = m_size - _GetApproximateDataInBuffer(); // -1, but the <= handles that
if (free <= nSamples) {
// Disabled since the lock-free queue can't handle changing the read end from the write thread
#if 0
// Buffer overrun!
// Dump samples from the read portion of the buffer instead of dropping
// the newly written stuff.
s32 comp;
if( SynchMode == 0 ) // TimeStrech on
{
comp = timeStretchOverrun();
}
else
{
// Toss half the buffer plus whatever's being written anew:
comp = GetAlignedBufferSize( (m_size + nSamples ) / 16 );
if( comp > (m_size-SndOutPacketSize) ) comp = m_size-SndOutPacketSize;
}
_DropSamples_Internal(comp);
if( MsgOverruns() )
ConLog(" * SPU2 > Overrun Compensation (%d packets tossed)\n", comp / SndOutPacketSize );
lastPct = 0.0; // normalize the timestretcher
#else
if (MsgOverruns())
ConLog(" * SPU2 > Overrun! 1 packet tossed)\n");
lastPct = 0.0; // normalize the timestretcher
return;
#endif
}
_WriteSamples_Safe(bData, nSamples);
}
void SndBuffer::Init()
{
if (mods[OutputModule] == NULL) {
_InitFail();
return;
}
// initialize sound buffer
// Buffer actually attempts to run ~50%, so allocate near double what
// the requested latency is:
m_rpos = 0;
m_wpos = 0;
try {
const float latencyMS = SndOutLatencyMS * 16;
m_size = GetAlignedBufferSize((int)(latencyMS * SampleRate / 1000.0f));
printf("%d SampleRate: \n", SampleRate);
m_buffer = new StereoOut32[m_size];
m_underrun_freeze = false;
sndTempBuffer = new StereoOut32[SndOutPacketSize];
sndTempBuffer16 = new StereoOut16[SndOutPacketSize * 2]; // in case of leftovers.
} catch (std::bad_alloc &) {
// out of memory exception (most likely)
SysMessage("Out of memory error occurred while initializing SPU2.");
_InitFail();
return;
}
sndTempProgress = 0;
soundtouchInit(); // initializes the timestretching
// initialize module
if (mods[OutputModule]->Init() == -1)
_InitFail();
}
void SndBuffer::Cleanup()
{
mods[OutputModule]->Close();
soundtouchCleanup();
safe_delete_array(m_buffer);
safe_delete_array(sndTempBuffer);
safe_delete_array(sndTempBuffer16);
}
int SndBuffer::m_dsp_progress = 0;
int SndBuffer::m_timestretch_progress = 0;
int SndBuffer::ssFreeze = 0;
void SndBuffer::ClearContents()
{
SndBuffer::soundtouchClearContents();
SndBuffer::ssFreeze = 256; //Delays sound output for about 1 second.
}
void SndBuffer::Write(const StereoOut32 &Sample)
{
// Log final output to wavefile.
WaveDump::WriteCore(1, CoreSrc_External, Sample.DownSample());
if (WavRecordEnabled)
RecordWrite(Sample.DownSample());
if (mods[OutputModule] == &NullOut) // null output doesn't need buffering or stretching! :p
return;
sndTempBuffer[sndTempProgress++] = Sample;
// If we haven't accumulated a full packet yet, do nothing more:
if (sndTempProgress < SndOutPacketSize)
return;
sndTempProgress = 0;
//Don't play anything directly after loading a savestate, avoids static killing your speakers.
if (ssFreeze > 0) {
ssFreeze--;
// Play silence
std::fill_n(sndTempBuffer, SndOutPacketSize, StereoOut32{});
}
#ifndef __POSIX__
if (dspPluginEnabled) {
// Convert in, send to winamp DSP, and convert out.
int ei = m_dsp_progress;
for (int i = 0; i < SndOutPacketSize; ++i, ++ei) {
sndTempBuffer16[ei] = sndTempBuffer[i].DownSample();
}
m_dsp_progress += DspProcess((s16 *)sndTempBuffer16 + m_dsp_progress, SndOutPacketSize);
// Some ugly code to ensure full packet handling:
ei = 0;
while (m_dsp_progress >= SndOutPacketSize) {
for (int i = 0; i < SndOutPacketSize; ++i, ++ei) {
sndTempBuffer[i] = sndTempBuffer16[ei].UpSample();
}
if (SynchMode == 0) // TimeStrech on
timeStretchWrite();
else
_WriteSamples(sndTempBuffer, SndOutPacketSize);
m_dsp_progress -= SndOutPacketSize;
}
// copy any leftovers to the front of the dsp buffer.
if (m_dsp_progress > 0) {
memcpy(sndTempBuffer16, &sndTempBuffer16[ei],
sizeof(sndTempBuffer16[0]) * m_dsp_progress);
}
}
#endif
else {
if (SynchMode == 0) // TimeStrech on
timeStretchWrite();
else
_WriteSamples(sndTempBuffer, SndOutPacketSize);
}
}
s32 SndBuffer::Test()
{
if (mods[OutputModule] == NULL)
return -1;
return mods[OutputModule]->Test();
}

698
pcsx2/SPU2/SndOut.h Normal file
View File

@ -0,0 +1,698 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
// Number of stereo samples per SndOut block.
// All drivers must work in units of this size when communicating with
// SndOut.
static const int SndOutPacketSize = 64;
// Overall master volume shift; this is meant to be a precision value and does not affect
// actual output volumes. It converts SPU2 16 bit volumes to 32-bit volumes, and likewise
// downsamples 32 bit samples to 16 bit sound driver output (this way timestretching and
// DSP effects get better precision results)
static const int SndOutVolumeShift = 12;
static const int SndOutVolumeShift32 = 16 - SndOutVolumeShift; // shift up, not down
// Samplerate of the SPU2. For accurate playback we need to match this
// exactly. Trying to scale samplerates and maintain SPU2's Ts timing accuracy
// is too problematic. :)
extern int SampleRate;
extern int FindOutputModuleById(const wchar_t *omodid);
// Implemented in Config.cpp
extern float VolumeAdjustFL;
extern float VolumeAdjustC;
extern float VolumeAdjustFR;
extern float VolumeAdjustBL;
extern float VolumeAdjustBR;
extern float VolumeAdjustSL;
extern float VolumeAdjustSR;
extern float VolumeAdjustLFE;
extern unsigned int delayCycles;
struct Stereo51Out16DplII;
struct Stereo51Out32DplII;
struct Stereo51Out16Dpl; // similar to DplII but without rear balancing
struct Stereo51Out32Dpl;
extern void ResetDplIIDecoder();
extern void ProcessDplIISample16(const StereoOut32 &src, Stereo51Out16DplII *s);
extern void ProcessDplIISample32(const StereoOut32 &src, Stereo51Out32DplII *s);
extern void ProcessDplSample16(const StereoOut32 &src, Stereo51Out16Dpl *s);
extern void ProcessDplSample32(const StereoOut32 &src, Stereo51Out32Dpl *s);
struct StereoOut16
{
s16 Left;
s16 Right;
StereoOut16()
: Left(0)
, Right(0)
{
}
StereoOut16(const StereoOut32 &src)
: Left((s16)src.Left)
, Right((s16)src.Right)
{
}
StereoOut16(s16 left, s16 right)
: Left(left)
, Right(right)
{
}
StereoOut32 UpSample() const;
void ResampleFrom(const StereoOut32 &src)
{
// Use StereoOut32's built in conversion
*this = src.DownSample();
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s16)(Left * VolumeAdjustFL);
Right = (s16)(Right * VolumeAdjustFR);
}
};
struct StereoOutFloat
{
float Left;
float Right;
StereoOutFloat()
: Left(0)
, Right(0)
{
}
explicit StereoOutFloat(const StereoOut32 &src)
: Left(src.Left / 2147483647.0f)
, Right(src.Right / 2147483647.0f)
{
}
explicit StereoOutFloat(s32 left, s32 right)
: Left(left / 2147483647.0f)
, Right(right / 2147483647.0f)
{
}
StereoOutFloat(float left, float right)
: Left(left)
, Right(right)
{
}
};
struct Stereo21Out16
{
s16 Left;
s16 Right;
s16 LFE;
void ResampleFrom(const StereoOut32 &src)
{
Left = src.Left >> SndOutVolumeShift;
Right = src.Right >> SndOutVolumeShift;
LFE = (src.Left + src.Right) >> (SndOutVolumeShift + 1);
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s16)(Left * VolumeAdjustFL);
Right = (s16)(Right * VolumeAdjustFR);
LFE = (s16)(LFE * VolumeAdjustLFE);
}
};
struct Stereo40Out16
{
s16 Left;
s16 Right;
s16 LeftBack;
s16 RightBack;
void ResampleFrom(const StereoOut32 &src)
{
Left = src.Left >> SndOutVolumeShift;
Right = src.Right >> SndOutVolumeShift;
LeftBack = src.Left >> SndOutVolumeShift;
RightBack = src.Right >> SndOutVolumeShift;
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s16)(Left * VolumeAdjustFL);
Right = (s16)(Right * VolumeAdjustFR);
LeftBack = (s16)(LeftBack * VolumeAdjustBL);
RightBack = (s16)(RightBack * VolumeAdjustBR);
}
};
struct Stereo40Out32
{
s32 Left;
s32 Right;
s32 LeftBack;
s32 RightBack;
void ResampleFrom(const StereoOut32 &src)
{
Left = src.Left << SndOutVolumeShift32;
Right = src.Right << SndOutVolumeShift32;
LeftBack = src.Left << SndOutVolumeShift32;
RightBack = src.Right << SndOutVolumeShift32;
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s32)(Left * VolumeAdjustFL);
Right = (s32)(Right * VolumeAdjustFR);
LeftBack = (s32)(LeftBack * VolumeAdjustBL);
RightBack = (s32)(RightBack * VolumeAdjustBR);
}
};
struct Stereo41Out16
{
s16 Left;
s16 Right;
s16 LFE;
s16 LeftBack;
s16 RightBack;
void ResampleFrom(const StereoOut32 &src)
{
Left = src.Left >> SndOutVolumeShift;
Right = src.Right >> SndOutVolumeShift;
LFE = (src.Left + src.Right) >> (SndOutVolumeShift + 1);
LeftBack = src.Left >> SndOutVolumeShift;
RightBack = src.Right >> SndOutVolumeShift;
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s32)(Left * VolumeAdjustFL);
Right = (s32)(Right * VolumeAdjustFR);
LeftBack = (s32)(LeftBack * VolumeAdjustBL);
RightBack = (s32)(RightBack * VolumeAdjustBR);
LFE = (s32)(LFE * VolumeAdjustLFE);
}
};
struct Stereo51Out16
{
s16 Left;
s16 Right;
s16 Center;
s16 LFE;
s16 LeftBack;
s16 RightBack;
// Implementation Note: Center and Subwoofer/LFE -->
// This method is simple and sounds nice. It relies on the speaker/soundcard
// systems do to their own low pass / crossover. Manual lowpass is wasted effort
// and can't match solid state results anyway.
void ResampleFrom(const StereoOut32 &src)
{
Left = src.Left >> SndOutVolumeShift;
Right = src.Right >> SndOutVolumeShift;
Center = (src.Left + src.Right) >> (SndOutVolumeShift + 1);
LFE = Center;
LeftBack = src.Left >> SndOutVolumeShift;
RightBack = src.Right >> SndOutVolumeShift;
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s16)(Left * VolumeAdjustFL);
Right = (s16)(Right * VolumeAdjustFR);
LeftBack = (s16)(LeftBack * VolumeAdjustBL);
RightBack = (s16)(RightBack * VolumeAdjustBR);
Center = (s16)(Center * VolumeAdjustC);
LFE = (s16)(LFE * VolumeAdjustLFE);
}
};
struct Stereo51Out16DplII
{
s16 Left;
s16 Right;
s16 Center;
s16 LFE;
s16 LeftBack;
s16 RightBack;
void ResampleFrom(const StereoOut32 &src)
{
ProcessDplIISample16(src, this);
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s32)(Left * VolumeAdjustFL);
Right = (s32)(Right * VolumeAdjustFR);
LeftBack = (s32)(LeftBack * VolumeAdjustBL);
RightBack = (s32)(RightBack * VolumeAdjustBR);
Center = (s32)(Center * VolumeAdjustC);
LFE = (s32)(LFE * VolumeAdjustLFE);
}
};
struct Stereo51Out32DplII
{
s32 Left;
s32 Right;
s32 Center;
s32 LFE;
s32 LeftBack;
s32 RightBack;
void ResampleFrom(const StereoOut32 &src)
{
ProcessDplIISample32(src, this);
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s32)(Left * VolumeAdjustFL);
Right = (s32)(Right * VolumeAdjustFR);
LeftBack = (s32)(LeftBack * VolumeAdjustBL);
RightBack = (s32)(RightBack * VolumeAdjustBR);
Center = (s32)(Center * VolumeAdjustC);
LFE = (s32)(LFE * VolumeAdjustLFE);
}
};
struct Stereo51Out16Dpl
{
s16 Left;
s16 Right;
s16 Center;
s16 LFE;
s16 LeftBack;
s16 RightBack;
void ResampleFrom(const StereoOut32 &src)
{
ProcessDplSample16(src, this);
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s32)(Left * VolumeAdjustFL);
Right = (s32)(Right * VolumeAdjustFR);
LeftBack = (s32)(LeftBack * VolumeAdjustBL);
RightBack = (s32)(RightBack * VolumeAdjustBR);
Center = (s32)(Center * VolumeAdjustC);
LFE = (s32)(LFE * VolumeAdjustLFE);
}
};
struct Stereo51Out32Dpl
{
s32 Left;
s32 Right;
s32 Center;
s32 LFE;
s32 LeftBack;
s32 RightBack;
void ResampleFrom(const StereoOut32 &src)
{
ProcessDplSample32(src, this);
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s32)(Left * VolumeAdjustFL);
Right = (s32)(Right * VolumeAdjustFR);
LeftBack = (s32)(LeftBack * VolumeAdjustBL);
RightBack = (s32)(RightBack * VolumeAdjustBR);
Center = (s32)(Center * VolumeAdjustC);
LFE = (s32)(LFE * VolumeAdjustLFE);
}
};
struct Stereo71Out16
{
s16 Left;
s16 Right;
s16 Center;
s16 LFE;
s16 LeftBack;
s16 RightBack;
s16 LeftSide;
s16 RightSide;
void ResampleFrom(const StereoOut32 &src)
{
Left = src.Left >> SndOutVolumeShift;
Right = src.Right >> SndOutVolumeShift;
Center = (src.Left + src.Right) >> (SndOutVolumeShift + 1);
LFE = Center;
LeftBack = src.Left >> SndOutVolumeShift;
RightBack = src.Right >> SndOutVolumeShift;
LeftSide = src.Left >> (SndOutVolumeShift + 1);
RightSide = src.Right >> (SndOutVolumeShift + 1);
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s16)(Left * VolumeAdjustFL);
Right = (s16)(Right * VolumeAdjustFR);
LeftBack = (s16)(LeftBack * VolumeAdjustBL);
RightBack = (s16)(RightBack * VolumeAdjustBR);
LeftSide = (s16)(LeftBack * VolumeAdjustSL);
RightSide = (s16)(RightBack * VolumeAdjustSR);
Center = (s16)(Center * VolumeAdjustC);
LFE = (s16)(LFE * VolumeAdjustLFE);
}
};
struct Stereo71Out32
{
s32 Left;
s32 Right;
s32 Center;
s32 LFE;
s32 LeftBack;
s32 RightBack;
s32 LeftSide;
s32 RightSide;
void ResampleFrom(const StereoOut32 &src)
{
Left = src.Left << SndOutVolumeShift32;
Right = src.Right << SndOutVolumeShift32;
Center = (src.Left + src.Right) << (SndOutVolumeShift32 - 1);
LFE = Center;
LeftBack = src.Left << SndOutVolumeShift32;
RightBack = src.Right << SndOutVolumeShift32;
LeftSide = src.Left << (SndOutVolumeShift32 - 1);
RightSide = src.Right << (SndOutVolumeShift32 - 1);
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s32)(Left * VolumeAdjustFL);
Right = (s32)(Right * VolumeAdjustFR);
LeftBack = (s32)(LeftBack * VolumeAdjustBL);
RightBack = (s32)(RightBack * VolumeAdjustBR);
LeftSide = (s32)(LeftBack * VolumeAdjustSL);
RightSide = (s32)(RightBack * VolumeAdjustSR);
Center = (s32)(Center * VolumeAdjustC);
LFE = (s32)(LFE * VolumeAdjustLFE);
}
};
struct Stereo20Out32
{
s32 Left;
s32 Right;
void ResampleFrom(const StereoOut32 &src)
{
Left = src.Left << SndOutVolumeShift32;
Right = src.Right << SndOutVolumeShift32;
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s32)(Left * VolumeAdjustFL);
Right = (s32)(Right * VolumeAdjustFR);
}
};
struct Stereo21Out32
{
s32 Left;
s32 Right;
s32 LFE;
void ResampleFrom(const StereoOut32 &src)
{
Left = src.Left << SndOutVolumeShift32;
Right = src.Right << SndOutVolumeShift32;
LFE = (src.Left + src.Right) << (SndOutVolumeShift32 - 1);
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s32)(Left * VolumeAdjustFL);
Right = (s32)(Right * VolumeAdjustFR);
LFE = (s32)(LFE * VolumeAdjustLFE);
}
};
struct Stereo41Out32
{
s32 Left;
s32 Right;
s32 LFE;
s32 LeftBack;
s32 RightBack;
void ResampleFrom(const StereoOut32 &src)
{
Left = src.Left << SndOutVolumeShift32;
Right = src.Right << SndOutVolumeShift32;
LFE = (src.Left + src.Right) << (SndOutVolumeShift32 - 1);
LeftBack = src.Left << SndOutVolumeShift32;
RightBack = src.Right << SndOutVolumeShift32;
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s32)(Left * VolumeAdjustFL);
Right = (s32)(Right * VolumeAdjustFR);
LeftBack = (s32)(LeftBack * VolumeAdjustBL);
RightBack = (s32)(RightBack * VolumeAdjustBR);
LFE = (s32)(LFE * VolumeAdjustLFE);
}
};
struct Stereo51Out32
{
s32 Left;
s32 Right;
s32 Center;
s32 LFE;
s32 LeftBack;
s32 RightBack;
void ResampleFrom(const StereoOut32 &src)
{
Left = src.Left << SndOutVolumeShift32;
Right = src.Right << SndOutVolumeShift32;
Center = (src.Left + src.Right) << (SndOutVolumeShift32 - 1);
LFE = Center;
LeftBack = src.Left << SndOutVolumeShift32;
RightBack = src.Right << SndOutVolumeShift32;
}
void AdjustFrom(const StereoOut32 &src)
{
ResampleFrom(src);
Left = (s32)(Left * VolumeAdjustFL);
Right = (s32)(Right * VolumeAdjustFR);
LeftBack = (s32)(LeftBack * VolumeAdjustBL);
RightBack = (s32)(RightBack * VolumeAdjustBR);
Center = (s32)(Center * VolumeAdjustC);
LFE = (s32)(LFE * VolumeAdjustLFE);
}
};
// Developer Note: This is a static class only (all static members).
class SndBuffer
{
private:
static bool m_underrun_freeze;
static s32 m_predictData;
static float lastPct;
static StereoOut32 *sndTempBuffer;
static StereoOut16 *sndTempBuffer16;
static int sndTempProgress;
static int m_dsp_progress;
static int m_timestretch_progress;
static int m_timestretch_writepos;
static StereoOut32 *m_buffer;
static s32 m_size;
static __aligned(4) volatile s32 m_rpos;
static __aligned(4) volatile s32 m_wpos;
static float lastEmergencyAdj;
static float cTempo;
static float eTempo;
static int ssFreeze;
static void _InitFail();
static bool CheckUnderrunStatus(int &nSamples, int &quietSampleCount);
static void soundtouchInit();
static void soundtouchClearContents();
static void soundtouchCleanup();
static void timeStretchWrite();
static void timeStretchUnderrun();
static s32 timeStretchOverrun();
static void PredictDataWrite(int samples);
static float GetStatusPct();
static void UpdateTempoChangeSoundTouch();
static void UpdateTempoChangeSoundTouch2();
static void _WriteSamples(StereoOut32 *bData, int nSamples);
static void _WriteSamples_Safe(StereoOut32 *bData, int nSamples);
static void _ReadSamples_Safe(StereoOut32 *bData, int nSamples);
static void _WriteSamples_Internal(StereoOut32 *bData, int nSamples);
static void _DropSamples_Internal(int nSamples);
static void _ReadSamples_Internal(StereoOut32 *bData, int nSamples);
static int _GetApproximateDataInBuffer();
public:
static void UpdateTempoChangeAsyncMixing();
static void Init();
static void Cleanup();
static void Write(const StereoOut32 &Sample);
static s32 Test();
static void ClearContents();
// Note: When using with 32 bit output buffers, the user of this function is responsible
// for shifting the values to where they need to be manually. The fixed point depth of
// the sample output is determined by the SndOutVolumeShift, which is the number of bits
// to shift right to get a 16 bit result.
template <typename T>
static void ReadSamples(T *bData);
};
class SndOutModule
{
public:
// Virtual destructor, because it helps fight C+++ funny-business.
virtual ~SndOutModule() {}
// Returns a unique identification string for this driver.
// (usually just matches the driver's cpp filename)
virtual const wchar_t *GetIdent() const = 0;
// Returns the long name / description for this driver.
// (for use in configuration screen)
virtual const wchar_t *GetLongName() const = 0;
virtual s32 Init() = 0;
virtual void Close() = 0;
virtual s32 Test() const = 0;
// Gui function: Used to open the configuration box for this driver.
virtual void Configure(uptr parent) = 0;
// Loads settings from the INI file for this driver
virtual void ReadSettings() = 0;
// Set output API for this driver
virtual void SetApiSettings(wxString api) = 0;
// Saves settings to the INI file for this driver
virtual void WriteSettings() const = 0;
// Returns the number of empty samples in the output buffer.
// (which is effectively the amount of data played since the last update)
virtual int GetEmptySampleCount() = 0;
};
#ifdef _MSC_VER
//internal
extern SndOutModule *WaveOut;
extern SndOutModule *DSoundOut;
extern SndOutModule *XAudio2Out;
#endif
#if defined(_WIN32) || defined(SPU2X_PORTAUDIO)
extern SndOutModule *PortaudioOut;
#endif
#if defined(SPU2X_SDL) || defined(SPU2X_SDL2)
extern SndOutModule *const SDLOut;
#endif
#ifdef __linux__
extern SndOutModule *AlsaOut;
#endif
extern SndOutModule *mods[];
// =====================================================================================================
extern bool WavRecordEnabled;
extern void RecordStart(std::wstring* filename);
extern void RecordStop();
extern void RecordWrite(const StereoOut16 &sample);
extern s32 DspLoadLibrary(wchar_t *fileName, int modNum);
extern void DspCloseLibrary();
extern int DspProcess(s16 *buffer, int samples);
extern void DspUpdate(); // to let the Dsp process window messages

View File

@ -0,0 +1,686 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#define _WIN32_DCOM
#include "Dialogs.h"
#include "portaudio.h"
#include "wchar.h"
#include <vector>
#ifdef _WIN32
#include "pa_win_wasapi.h"
#endif
int PaCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData);
class Portaudio : public SndOutModule
{
private:
//////////////////////////////////////////////////////////////////////////////////////////
// Configuration Vars (unused still)
int m_ApiId;
wxString m_Device;
bool m_UseHardware;
bool m_WasapiExclusiveMode;
bool m_SuggestedLatencyMinimal;
int m_SuggestedLatencyMS;
//////////////////////////////////////////////////////////////////////////////////////////
// Instance vars
int writtenSoFar;
int writtenLastTime;
int availableLastTime;
int actualUsedChannels;
bool started;
PaStream *stream;
//////////////////////////////////////////////////////////////////////////////////////////
// Stuff necessary for speaker expansion
class SampleReader
{
public:
virtual int ReadSamples(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData) = 0;
};
template <class T>
class ConvertedSampleReader : public SampleReader
{
int *written;
public:
ConvertedSampleReader(int *pWritten)
{
written = pWritten;
}
virtual int ReadSamples(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData)
{
T *p1 = (T *)outputBuffer;
int packets = framesPerBuffer / SndOutPacketSize;
for (int p = 0; p < packets; p++, p1 += SndOutPacketSize)
SndBuffer::ReadSamples(p1);
(*written) += packets * SndOutPacketSize;
return 0;
}
};
public:
SampleReader *ActualPaCallback;
Portaudio()
{
m_SuggestedLatencyMinimal = true;
m_UseHardware = false;
m_WasapiExclusiveMode = false;
started = false;
stream = NULL;
ActualPaCallback = NULL;
m_ApiId = -1;
m_SuggestedLatencyMS = 20;
actualUsedChannels = 0;
writtenSoFar = 0;
writtenLastTime = 0;
availableLastTime = 0;
}
s32 Init()
{
started = false;
stream = NULL;
ReadSettings();
PaError err = Pa_Initialize();
if (err != paNoError) {
fprintf(stderr, "* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText(err));
return -1;
}
started = true;
int deviceIndex = -1;
fprintf(stderr, "* SPU2-X: Enumerating PortAudio devices:\n");
for (int i = 0, j = 0; i < Pa_GetDeviceCount(); i++) {
const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
if (info->maxOutputChannels > 0) {
const PaHostApiInfo *apiinfo = Pa_GetHostApiInfo(info->hostApi);
fprintf(stderr, " *** Device %d: '%s' (%s)", j, info->name, apiinfo->name);
if (apiinfo->type == m_ApiId) {
if (m_Device == wxString::FromUTF8(info->name)) {
deviceIndex = i;
fprintf(stderr, " (selected)");
}
}
fprintf(stderr, "\n");
j++;
}
}
fflush(stderr);
if (deviceIndex < 0 && m_ApiId >= 0) {
for (int i = 0; i < Pa_GetHostApiCount(); i++) {
const PaHostApiInfo *apiinfo = Pa_GetHostApiInfo(i);
if (apiinfo->type == m_ApiId) {
deviceIndex = apiinfo->defaultOutputDevice;
}
}
}
if (deviceIndex >= 0) {
void *infoPtr = NULL;
const PaDeviceInfo *devinfo = Pa_GetDeviceInfo(deviceIndex);
int speakers;
switch (numSpeakers) // speakers = (numSpeakers + 1) *2; ?
{
case 0:
speakers = 2;
break; // Stereo
case 1:
speakers = 4;
break; // Quadrafonic
case 2:
speakers = 6;
break; // Surround 5.1
case 3:
speakers = 8;
break; // Surround 7.1
default:
speakers = 2;
}
actualUsedChannels = std::min(speakers, devinfo->maxOutputChannels);
switch (actualUsedChannels) {
case 2:
ConLog("* SPU2 > Using normal 2 speaker stereo output.\n");
ActualPaCallback = new ConvertedSampleReader<Stereo20Out32>(&writtenSoFar);
break;
case 3:
ConLog("* SPU2 > 2.1 speaker expansion enabled.\n");
ActualPaCallback = new ConvertedSampleReader<Stereo21Out32>(&writtenSoFar);
break;
case 4:
ConLog("* SPU2 > 4 speaker expansion enabled [quadraphenia]\n");
ActualPaCallback = new ConvertedSampleReader<Stereo40Out32>(&writtenSoFar);
break;
case 5:
ConLog("* SPU2 > 4.1 speaker expansion enabled.\n");
ActualPaCallback = new ConvertedSampleReader<Stereo41Out32>(&writtenSoFar);
break;
case 6:
case 7:
switch (dplLevel) {
case 0:
ConLog("* SPU2 > 5.1 speaker expansion enabled.\n");
ActualPaCallback = new ConvertedSampleReader<Stereo51Out32>(&writtenSoFar); //"normal" stereo upmix
break;
case 1:
ConLog("* SPU2 > 5.1 speaker expansion with basic ProLogic dematrixing enabled.\n");
ActualPaCallback = new ConvertedSampleReader<Stereo51Out32Dpl>(&writtenSoFar); // basic Dpl decoder without rear stereo balancing
break;
case 2:
ConLog("* SPU2 > 5.1 speaker expansion with experimental ProLogicII dematrixing enabled.\n");
ActualPaCallback = new ConvertedSampleReader<Stereo51Out32DplII>(&writtenSoFar); //gigas PLII
break;
}
actualUsedChannels = 6; // we do not support 7.0 or 6.2 configurations, downgrade to 5.1
break;
default: // anything 8 or more gets the 7.1 treatment!
ConLog("* SPU2 > 7.1 speaker expansion enabled.\n");
ActualPaCallback = new ConvertedSampleReader<Stereo71Out32>(&writtenSoFar);
actualUsedChannels = 8; // we do not support 7.2 or more, downgrade to 7.1
break;
}
#ifdef _WIN32
PaWasapiStreamInfo info = {
sizeof(PaWasapiStreamInfo),
paWASAPI,
1,
paWinWasapiExclusive};
if ((m_ApiId == paWASAPI) && m_WasapiExclusiveMode) {
// Pass it the Exclusive mode enable flag
infoPtr = &info;
}
#endif
PaStreamParameters outParams = {
// PaDeviceIndex device;
// int channelCount;
// PaSampleFormat sampleFormat;
// PaTime suggestedLatency;
// void *hostApiSpecificStreamInfo;
deviceIndex,
actualUsedChannels,
paInt32,
m_SuggestedLatencyMinimal ? (SndOutPacketSize / (float)SampleRate) : (m_SuggestedLatencyMS / 1000.0f),
infoPtr};
err = Pa_OpenStream(&stream,
NULL, &outParams, SampleRate,
SndOutPacketSize,
paNoFlag,
PaCallback,
NULL);
} else {
err = Pa_OpenDefaultStream(&stream,
0, actualUsedChannels, paInt32, 48000,
SndOutPacketSize,
PaCallback,
NULL);
}
if (err != paNoError) {
fprintf(stderr, "* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText(err));
Pa_Terminate();
return -1;
}
err = Pa_StartStream(stream);
if (err != paNoError) {
fprintf(stderr, "* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText(err));
Pa_CloseStream(stream);
stream = NULL;
Pa_Terminate();
return -1;
}
return 0;
}
void Close()
{
PaError err;
if (started) {
if (stream) {
if (Pa_IsStreamActive(stream)) {
err = Pa_StopStream(stream);
if (err != paNoError)
fprintf(stderr, "* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText(err));
}
err = Pa_CloseStream(stream);
if (err != paNoError)
fprintf(stderr, "* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText(err));
stream = NULL;
}
// Seems to do more harm than good.
//PaError err = Pa_Terminate();
//if( err != paNoError )
// fprintf(stderr,"* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText( err ) );
started = false;
}
}
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
#ifdef _WIN32
private:
bool _ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
int tSel = 0;
switch (uMsg) {
case WM_INITDIALOG: {
wchar_t temp[128];
SendMessage(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_RESETCONTENT, 0, 0);
SendMessageA(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_ADDSTRING, 0, (LPARAM) "Default Device");
SendMessage(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_SETCURSEL, 0, 0);
SendMessage(GetDlgItem(hWnd, IDC_PA_HOSTAPI), CB_RESETCONTENT, 0, 0);
SendMessageA(GetDlgItem(hWnd, IDC_PA_HOSTAPI), CB_ADDSTRING, 0, (LPARAM) "Unspecified");
int idx = 0;
for (int i = 0; i < Pa_GetHostApiCount(); i++) {
const PaHostApiInfo *apiinfo = Pa_GetHostApiInfo(i);
if (apiinfo->deviceCount > 0) {
SendMessageA(GetDlgItem(hWnd, IDC_PA_HOSTAPI), CB_ADDSTRING, 0, (LPARAM)apiinfo->name);
SendMessageA(GetDlgItem(hWnd, IDC_PA_HOSTAPI), CB_SETITEMDATA, i + 1, apiinfo->type);
}
if (apiinfo->type == m_ApiId) {
idx = i + 1;
}
}
SendMessage(GetDlgItem(hWnd, IDC_PA_HOSTAPI), CB_SETCURSEL, idx, 0);
if (idx > 0) {
int api_idx = idx - 1;
SendMessage(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_RESETCONTENT, 0, 0);
SendMessageA(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_ADDSTRING, 0, (LPARAM) "Default Device");
SendMessage(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_SETITEMDATA, 0, 0);
int _idx = 0;
int i = 1;
for (int j = 0; j < Pa_GetDeviceCount(); j++) {
const PaDeviceInfo *info = Pa_GetDeviceInfo(j);
if (info->hostApi == api_idx && info->maxOutputChannels > 0) {
SendMessage(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_ADDSTRING, 0, (LPARAM)wxString::FromUTF8(info->name).wc_str());
SendMessage(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_SETITEMDATA, i, (LPARAM)info);
if (wxString::FromUTF8(info->name) == m_Device) {
_idx = i;
}
i++;
}
}
SendMessage(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_SETCURSEL, _idx, 0);
}
INIT_SLIDER(IDC_LATENCY, 10, 200, 10, 1, 1);
SendMessage(GetDlgItem(hWnd, IDC_LATENCY), TBM_SETPOS, TRUE, m_SuggestedLatencyMS);
swprintf_s(temp, L"%d ms", m_SuggestedLatencyMS);
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
if (m_SuggestedLatencyMinimal)
SET_CHECK(IDC_MINIMIZE, true);
else
SET_CHECK(IDC_MANUAL, true);
SET_CHECK(IDC_EXCLUSIVE, m_WasapiExclusiveMode);
} break;
case WM_COMMAND: {
//wchar_t temp[128];
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId) {
case IDOK: {
int idx = (int)SendMessage(GetDlgItem(hWnd, IDC_PA_HOSTAPI), CB_GETCURSEL, 0, 0);
m_ApiId = SendMessage(GetDlgItem(hWnd, IDC_PA_HOSTAPI), CB_GETITEMDATA, idx, 0);
idx = (int)SendMessage(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_GETCURSEL, 0, 0);
const PaDeviceInfo *info = (const PaDeviceInfo *)SendMessage(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_GETITEMDATA, idx, 0);
if (info)
m_Device = wxString::FromUTF8(info->name);
else
m_Device = L"default";
m_SuggestedLatencyMS = (int)SendMessage(GetDlgItem(hWnd, IDC_LATENCY), TBM_GETPOS, 0, 0);
if (m_SuggestedLatencyMS < 10)
m_SuggestedLatencyMS = 10;
if (m_SuggestedLatencyMS > 200)
m_SuggestedLatencyMS = 200;
m_SuggestedLatencyMinimal = SendMessage(GetDlgItem(hWnd, IDC_MINIMIZE), BM_GETCHECK, 0, 0) == BST_CHECKED;
m_WasapiExclusiveMode = SendMessage(GetDlgItem(hWnd, IDC_EXCLUSIVE), BM_GETCHECK, 0, 0) == BST_CHECKED;
EndDialog(hWnd, 0);
} break;
case IDCANCEL:
EndDialog(hWnd, 0);
break;
case IDC_PA_HOSTAPI: {
if (wmEvent == CBN_SELCHANGE) {
int api_idx = (int)SendMessage(GetDlgItem(hWnd, IDC_PA_HOSTAPI), CB_GETCURSEL, 0, 0) - 1;
int apiId = SendMessageA(GetDlgItem(hWnd, IDC_PA_HOSTAPI), CB_GETITEMDATA, api_idx, 0);
SendMessage(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_RESETCONTENT, 0, 0);
SendMessageA(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_ADDSTRING, 0, (LPARAM) "Default Device");
SendMessage(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_SETITEMDATA, 0, 0);
int idx = 0;
int i = 1;
for (int j = 0; j < Pa_GetDeviceCount(); j++) {
const PaDeviceInfo *info = Pa_GetDeviceInfo(j);
if (info->hostApi == api_idx && info->maxOutputChannels > 0) {
SendMessage(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_ADDSTRING, 0, (LPARAM)wxString::FromUTF8(info->name).wc_str());
SendMessage(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_SETITEMDATA, i, (LPARAM)info);
i++;
}
}
SendMessage(GetDlgItem(hWnd, IDC_PA_DEVICE), CB_SETCURSEL, idx, 0);
}
} break;
default:
return FALSE;
}
} break;
case WM_HSCROLL: {
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId) {
case TB_LINEUP:
case TB_LINEDOWN:
case TB_PAGEUP:
case TB_PAGEDOWN:
case TB_TOP:
case TB_BOTTOM:
wmEvent = (int)SendMessage((HWND)lParam, TBM_GETPOS, 0, 0);
case TB_THUMBPOSITION:
case TB_THUMBTRACK: {
wchar_t temp[128];
if (wmEvent < 10)
wmEvent = 10;
if (wmEvent > 200)
wmEvent = 200;
SendMessage((HWND)lParam, TBM_SETPOS, TRUE, wmEvent);
swprintf_s(temp, L"%d ms", wmEvent);
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
break;
}
default:
return FALSE;
}
} break;
default:
return FALSE;
}
return TRUE;
}
static BOOL CALLBACK ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static BOOL CALLBACK DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext);
public:
virtual void Configure(uptr parent)
{
PaError err = Pa_Initialize(); // Initialization can be done multiple times, PA keeps a counter
if (err != paNoError) {
fprintf(stderr, "* SPU2-X: PortAudio error: %s\n", Pa_GetErrorText(err));
return;
}
// keep portaudio initialized until the dialog closes
INT_PTR ret;
ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_PORTAUDIO), (HWND)parent, (DLGPROC)ConfigProc, 1);
if (ret == -1) {
MessageBox((HWND)parent, L"Error Opening the config dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
return;
}
Pa_Terminate();
}
#else
virtual void Configure(uptr parent)
{
}
#endif
s32 Test() const
{
return 0;
}
int GetEmptySampleCount()
{
long availableNow = Pa_GetStreamWriteAvailable(stream);
int playedSinceLastTime = (writtenSoFar - writtenLastTime) + (availableNow - availableLastTime);
writtenLastTime = writtenSoFar;
availableLastTime = availableNow;
// Lowest resolution here is the SndOutPacketSize we use.
return playedSinceLastTime;
}
const wchar_t *GetIdent() const
{
return L"portaudio";
}
const wchar_t *GetLongName() const
{
return L"PortAudio (Cross-platform)";
}
void ReadSettings()
{
wxString api(L"EMPTYEMPTYEMPTY");
m_Device = L"EMPTYEMPTYEMPTY";
#ifdef __linux__
// By default on linux use the ALSA API (+99% users) -- Gregory
CfgReadStr(L"PORTAUDIO", L"HostApi", api, L"ALSA");
#elif defined(__APPLE__)
// Suppose OSX only has CoreAudio...
CfgReadStr(L"PORTAUDIO", L"HostApi", api, L"CoreAudio");
#else
CfgReadStr(L"PORTAUDIO", L"HostApi", api, L"WASAPI");
#endif
CfgReadStr(L"PORTAUDIO", L"Device", m_Device, L"default");
SetApiSettings(api);
m_WasapiExclusiveMode = CfgReadBool(L"PORTAUDIO", L"Wasapi_Exclusive_Mode", false);
m_SuggestedLatencyMinimal = CfgReadBool(L"PORTAUDIO", L"Minimal_Suggested_Latency", true);
m_SuggestedLatencyMS = CfgReadInt(L"PORTAUDIO", L"Manual_Suggested_Latency_MS", 20);
if (m_SuggestedLatencyMS < 10)
m_SuggestedLatencyMS = 10;
if (m_SuggestedLatencyMS > 200)
m_SuggestedLatencyMS = 200;
}
void SetApiSettings(wxString api)
{
m_ApiId = -1;
if (api == L"InDevelopment")
m_ApiId = paInDevelopment; /* use while developing support for a new host API */
if (api == L"DirectSound")
m_ApiId = paDirectSound;
if (api == L"MME")
m_ApiId = paMME;
if (api == L"ASIO")
m_ApiId = paASIO;
if (api == L"SoundManager")
m_ApiId = paSoundManager;
if (api == L"CoreAudio")
m_ApiId = paCoreAudio;
if (api == L"OSS")
m_ApiId = paOSS;
if (api == L"ALSA")
m_ApiId = paALSA;
if (api == L"AL")
m_ApiId = paAL;
if (api == L"BeOS")
m_ApiId = paBeOS;
if (api == L"WDMKS")
m_ApiId = paWDMKS;
if (api == L"JACK")
m_ApiId = paJACK;
if (api == L"WASAPI")
m_ApiId = paWASAPI;
if (api == L"AudioScienceHPI")
m_ApiId = paAudioScienceHPI;
}
void WriteSettings() const
{
wxString api;
switch (m_ApiId) {
case paInDevelopment:
api = L"InDevelopment";
break; /* use while developing support for a new host API */
case paDirectSound:
api = L"DirectSound";
break;
case paMME:
api = L"MME";
break;
case paASIO:
api = L"ASIO";
break;
case paSoundManager:
api = L"SoundManager";
break;
case paCoreAudio:
api = L"CoreAudio";
break;
case paOSS:
api = L"OSS";
break;
case paALSA:
api = L"ALSA";
break;
case paAL:
api = L"AL";
break;
case paBeOS:
api = L"BeOS";
break;
case paWDMKS:
api = L"WDMKS";
break;
case paJACK:
api = L"JACK";
break;
case paWASAPI:
api = L"WASAPI";
break;
case paAudioScienceHPI:
api = L"AudioScienceHPI";
break;
default:
api = L"Unknown";
}
CfgWriteStr(L"PORTAUDIO", L"HostApi", api);
CfgWriteStr(L"PORTAUDIO", L"Device", m_Device);
CfgWriteBool(L"PORTAUDIO", L"Wasapi_Exclusive_Mode", m_WasapiExclusiveMode);
CfgWriteBool(L"PORTAUDIO", L"Minimal_Suggested_Latency", m_SuggestedLatencyMinimal);
CfgWriteInt(L"PORTAUDIO", L"Manual_Suggested_Latency_MS", m_SuggestedLatencyMS);
}
} static PA;
int PaCallback(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData)
{
return PA.ActualPaCallback->ReadSamples(inputBuffer, outputBuffer, framesPerBuffer, timeInfo, statusFlags, userData);
}
#ifdef _WIN32
BOOL CALLBACK Portaudio::ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return PA._ConfigProc(hWnd, uMsg, wParam, lParam);
}
#endif
SndOutModule *PortaudioOut = &PA;

189
pcsx2/SPU2/SndOut_SDL.cpp Normal file
View File

@ -0,0 +1,189 @@
/* SDL Audio sink for SPU2-X.
* Copyright (c) 2013, Matt Scheirer <matt.scheirer@gmail.com>
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <iostream>
#include "Global.h"
#include "SndOut.h"
#include "Dialogs.h"
#include <memory>
/* Using SDL2 requires other SDL dependencies in pcsx2 get upgraded as well, or the
* symbol tables would conflict. Other dependencies in Linux are wxwidgets (you can
* build wx without sdl support, though) and onepad at the time of writing this. */
#include <SDL.h>
#include <SDL_audio.h>
typedef StereoOut16 StereoOut_SDL;
namespace
{
/* Since spu2 only ever outputs stereo, we don't worry about emitting surround sound
* even though SDL2 supports it */
const Uint8 channels = 2;
/* SDL2 supports s32 audio */
/* Samples should vary from [512,8192] according to SDL spec. Take note this is the desired
* sample count and SDL may provide otherwise. Pulseaudio will cut this value in half if
* PA_STREAM_ADJUST_LATENCY is set in the backened, for example. */
const Uint16 desiredSamples = 2048;
const Uint16 format = AUDIO_S16SYS;
Uint16 samples = desiredSamples;
std::unique_ptr<StereoOut_SDL[]> buffer;
void callback_fillBuffer(void *userdata, Uint8 *stream, int len)
{
Uint16 sdl_samples = samples;
#if SDL_MAJOR_VERSION >= 2
memset(stream, 0, len);
// As of SDL 2.0.4 the buffer is too small to contains all samples
// len is 2048, samples is 1024 and sizeof(StereoOut_SDL) is 4
sdl_samples = len / sizeof(StereoOut_SDL);
#endif
// Length should always be samples in bytes.
assert(len / sizeof(StereoOut_SDL) == sdl_samples);
for (Uint16 i = 0; i < sdl_samples; i += SndOutPacketSize)
SndBuffer::ReadSamples(&buffer[i]);
SDL_MixAudio(stream, (Uint8 *)buffer.get(), len, SDL_MIX_MAXVOLUME);
}
}
struct SDLAudioMod : public SndOutModule
{
static SDLAudioMod mod;
std::string m_api;
s32 Init()
{
ReadSettings();
#if SDL_MAJOR_VERSION >= 2
std::cerr << "Request SDL audio driver: " << m_api.c_str() << std::endl;
#endif
/* SDL backends will mangle the AudioSpec and change the sample count. If we reopen
* the audio backend, we need to make sure we keep our desired samples in the spec */
spec.samples = desiredSamples;
// Mandatory otherwise, init will be redone in SDL_OpenAudio
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
std::cerr << "SPU2-X: SDL INIT audio error: " << SDL_GetError() << std::endl;
return -1;
}
#if SDL_MAJOR_VERSION >= 2
if (m_api.compare("pulseaudio")) {
// Close the audio, but keep the subsystem open
SDL_AudioQuit();
// Reopen the audio
if (SDL_AudioInit(m_api.c_str()) < 0) {
std::cerr << "SPU2-X: SDL audio init error: " << SDL_GetError() << std::endl;
return -1;
}
}
#endif
if (SDL_OpenAudio(&spec, NULL) < 0) {
std::cerr << "SPU2-X: SDL audio error: " << SDL_GetError() << std::endl;
return -1;
}
#if SDL_MAJOR_VERSION >= 2
std::cerr << "Opened SDL audio driver: " << SDL_GetCurrentAudioDriver() << std::endl;
#endif
/* This is so ugly. It is hilariously ugly. I didn't use a vector to save reallocs. */
if (samples != spec.samples || buffer == NULL)
buffer = std::unique_ptr<StereoOut_SDL[]>(new StereoOut_SDL[spec.samples]);
if (samples != spec.samples) {
fprintf(stderr, "SPU2-X: SDL failed to get desired samples (%d) got %d samples instead\n", samples, spec.samples);
// Samples must always be a multiple of packet size.
assert(spec.samples % SndOutPacketSize == 0);
samples = spec.samples;
}
SDL_PauseAudio(0);
return 0;
}
const wchar_t *GetIdent() const { return L"SDLAudio"; }
const wchar_t *GetLongName() const { return L"SDL Audio"; }
void Close()
{
// Related to SDL_Init(SDL_INIT_AUDIO)
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
~SDLAudioMod() { Close(); }
s32 Test() const { return 0; }
int GetEmptySampleCount() { return 0; }
void Configure(uptr parent) {}
void ReadSettings()
{
wxString api(L"EMPTYEMPTYEMPTY");
CfgReadStr(L"SDL", L"HostApi", api, L"pulseaudio");
SetApiSettings(api);
}
void WriteSettings() const
{
CfgWriteStr(L"SDL", L"HostApi", wxString(m_api.c_str(), wxConvUTF8));
};
void SetApiSettings(wxString api)
{
#if SDL_MAJOR_VERSION >= 2
// Validate the api name
bool valid = false;
std::string api_name = std::string(api.utf8_str());
for (int i = 0; i < SDL_GetNumAudioDrivers(); ++i) {
valid |= (api_name.compare(SDL_GetAudioDriver(i)) == 0);
}
if (valid) {
m_api = api.utf8_str();
} else {
std::cerr << "SDL audio driver configuration is invalid!" << std::endl
<< "It will be replaced by pulseaudio!" << std::endl;
m_api = "pulseaudio";
}
#endif
}
private:
SDL_AudioSpec spec;
SDLAudioMod()
: m_api("pulseaudio")
, spec({SampleRate, format, channels, 0,
desiredSamples, 0, 0, &callback_fillBuffer, nullptr})
{
// Number of samples must be a multiple of packet size.
assert(samples % SndOutPacketSize == 0);
}
};
SDLAudioMod SDLAudioMod::mod;
SndOutModule *const SDLOut = &SDLAudioMod::mod;

321
pcsx2/SPU2/Spu2replay.cpp Normal file
View File

@ -0,0 +1,321 @@
//GiGaHeRz's SPU2 Driver
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
//
#include "Global.h"
#include "PS2E-spu2.h"
#ifdef _MSC_VER
#include "Windows.h"
#endif
FILE *s2rfile;
void s2r_write16(s16 data)
{
fwrite(&data, 2, 1, s2rfile);
}
void s2r_write32(u32 data)
{
fwrite(&data, 4, 1, s2rfile);
}
static void EMITC(u32 i, u32 a)
{
s2r_write32(((i & 0x7u) << 29u) | (a & 0x1FFFFFFFu));
}
int s2r_open(u32 ticks, char *filename)
{
s2rfile = fopen(filename, "wb");
if (s2rfile)
s2r_write32(ticks);
return s2rfile ? 0 : -1;
}
void s2r_readreg(u32 ticks, u32 addr)
{
if (!s2rfile)
return;
s2r_write32(ticks);
EMITC(0, addr);
}
void s2r_writereg(u32 ticks, u32 addr, s16 value)
{
if (!s2rfile)
return;
s2r_write32(ticks);
EMITC(1, addr);
s2r_write16(value);
}
void s2r_writedma4(u32 ticks, u16 *data, u32 len)
{
u32 i;
if (!s2rfile)
return;
s2r_write32(ticks);
EMITC(2, len);
for (i = 0; i < len; i++, data++)
s2r_write16(*data);
}
void s2r_writedma7(u32 ticks, u16 *data, u32 len)
{
u32 i;
if (!s2rfile)
return;
s2r_write32(ticks);
EMITC(3, len);
for (i = 0; i < len; i++, data++)
s2r_write16(*data);
}
void s2r_close()
{
if (!s2rfile)
return;
fclose(s2rfile);
}
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// replay code
bool replay_mode = false;
u16 dmabuffer[0xFFFFF];
const u32 IOP_CLK = 768 * 48000;
const u32 IOPCiclesPerMS = 768 * 48;
u32 CurrentIOPCycle = 0;
u64 HighResFreq;
u64 HighResPrev;
double HighResScale;
bool Running = false;
#ifdef _MSC_VER
int conprintf(const char *fmt, ...)
{
#ifdef _WIN32
char s[1024];
va_list list;
va_start(list, fmt);
vsprintf(s, fmt, list);
va_end(list);
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (handle == INVALID_HANDLE_VALUE)
return 0;
DWORD written = 0;
WriteConsoleA(handle, s, strlen(s), &written, 0);
FlushFileBuffers(handle);
return written;
#else
va_list list;
va_start(list, fmt);
int ret = vsprintf(stderr, fmt, list);
va_end(list);
return ret;
#endif
}
void dummy1()
{
}
void dummy4()
{
SPU2interruptDMA4();
}
void dummy7()
{
SPU2interruptDMA7();
}
u64 HighResFrequency()
{
u64 freq;
#ifdef _WIN32
QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
#else
// TODO
#endif
return freq;
}
u64 HighResCounter()
{
u64 time;
#ifdef _WIN32
QueryPerformanceCounter((LARGE_INTEGER *)&time);
#else
// TODO
#endif
return time;
}
void InitWaitSync() // not extremely accurate but enough.
{
HighResFreq = HighResFrequency();
HighResPrev = HighResCounter();
HighResScale = (double)HighResFreq / (double)IOP_CLK;
}
u32 WaitSync(u32 TargetCycle)
{
u32 WaitCycles = (TargetCycle - CurrentIOPCycle);
u32 WaitTime = WaitCycles / IOPCiclesPerMS;
if (WaitTime > 10)
WaitTime = 10;
if (WaitTime == 0)
WaitTime = 1;
SleepEx(WaitTime, TRUE);
// Refresh current time after sleeping
u64 Current = HighResCounter();
u32 delta = (u32)floor((Current - HighResPrev) / HighResScale + 0.5); // We lose some precision here, cycles might drift away over long periods of time ;P
// Calculate time delta
CurrentIOPCycle += delta;
HighResPrev += (u64)floor(delta * HighResScale + 0.5); // Trying to compensate drifting mentioned above, not necessarily useful.
return delta;
}
#ifdef _WIN32
BOOL WINAPI HandlerRoutine(DWORD dwCtrlType)
{
Running = false;
return TRUE;
}
#endif
#include "Windows/Dialogs.h"
EXPORT_C_(void)
s2r_replay(HWND hwnd, HINSTANCE hinst, LPSTR filename, int nCmdShow)
{
int events = 0;
Running = true;
#ifdef _WIN32
AllocConsole();
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
conprintf("Playing %s file on %x...", filename, hwnd);
#endif
// load file
FILE *file = fopen(filename, "rb");
if (!file) {
conprintf("Could not open the replay file.");
return;
}
// if successful, init the plugin
#define TryRead(dest, size, count, file) \
if (fread(dest, size, count, file) < count) { \
conprintf("Error reading from file."); \
goto Finish; /* Need to exit the while() loop and maybe also the switch */ \
}
TryRead(&CurrentIOPCycle, 4, 1, file);
replay_mode = true;
InitWaitSync(); // Initialize the WaitSync stuff
SPU2init();
SPU2irqCallback(dummy1, dummy4, dummy7);
SPU2setClockPtr(&CurrentIOPCycle);
SPU2open(&hwnd);
CurrentIOPCycle = 0;
SPU2async(0);
while (!feof(file) && Running) {
u32 ccycle = 0;
u32 evid = 0;
u32 sval = 0;
u32 tval = 0;
TryRead(&ccycle, 4, 1, file);
TryRead(&sval, 4, 1, file);
evid = sval >> 29;
sval &= 0x1FFFFFFF;
u32 TargetCycle = ccycle * 768;
while (TargetCycle > CurrentIOPCycle) {
u32 delta = WaitSync(TargetCycle);
SPU2async(delta);
}
switch (evid) {
case 0:
SPU2read(sval);
break;
case 1:
TryRead(&tval, 2, 1, file);
SPU2write(sval, tval);
break;
case 2:
TryRead(dmabuffer, sval, 2, file);
SPU2writeDMA4Mem(dmabuffer, sval);
break;
case 3:
TryRead(dmabuffer, sval, 2, file);
SPU2writeDMA7Mem(dmabuffer, sval);
break;
default:
// not implemented
return;
break;
}
events++;
}
Finish:
//shutdown
SPU2close();
SPU2shutdown();
fclose(file);
conprintf("Finished playing %s file (%d cycles, %d events).", filename, CurrentIOPCycle, events);
#ifdef _WIN32
FreeConsole();
#endif
replay_mode = false;
}
#endif

31
pcsx2/SPU2/Spu2replay.h Normal file
View File

@ -0,0 +1,31 @@
//GiGaHeRz's SPU2 Driver
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
//
#pragma once
//#define S2R_ENABLE
// s2r dumping
int s2r_open(u32 ticks, char *filename);
void s2r_readreg(u32 ticks, u32 addr);
void s2r_writereg(u32 ticks, u32 addr, s16 value);
void s2r_writedma4(u32 ticks, u16 *data, u32 len);
void s2r_writedma7(u32 ticks, u16 *data, u32 len);
void s2r_close();
extern bool replay_mode;

View File

@ -0,0 +1,540 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "soundtouch/SoundTouch.h"
#include <wx/datetime.h>
#include <algorithm>
//Uncomment the next line to use the old time stretcher
//#define SPU2X_USE_OLD_STRETCHER
static soundtouch::SoundTouch *pSoundTouch = NULL;
// data prediction amount, used to "commit" data that hasn't
// finished timestretch processing.
s32 SndBuffer::m_predictData;
// records last buffer status (fill %, range -100 to 100, with 0 being 50% full)
float SndBuffer::lastPct;
float SndBuffer::lastEmergencyAdj;
float SndBuffer::cTempo = 1;
float SndBuffer::eTempo = 1;
void SndBuffer::PredictDataWrite(int samples)
{
m_predictData += samples;
}
// Calculate the buffer status percentage.
// Returns range from -1.0 to 1.0
// 1.0 = buffer overflow!
// 0.0 = buffer nominal (50% full)
// -1.0 = buffer underflow!
float SndBuffer::GetStatusPct()
{
// Get the buffer status of the output driver too, so that we can
// obtain a more accurate overall buffer status.
int drvempty = mods[OutputModule]->GetEmptySampleCount(); // / 2;
//ConLog( "Data %d >>> driver: %d predict: %d\n", m_data, drvempty, m_predictData );
int data = _GetApproximateDataInBuffer();
float result = (float)(data + m_predictData - drvempty) - (m_size / 16);
result /= (m_size / 16);
return result;
}
//Alternative simple tempo adjustment. Based only on the soundtouch buffer state.
//Base algorithm: aim at specific average number of samples at the buffer (by GUI), and adjust tempo simply by current/target.
//An extra mechanism is added to keep adjustment at perfect 1:1 ratio (when emulation speed is stable around 100%)
// to prevent constant stretching/shrinking of packets if possible.
// This mechanism is triggered when the adjustment is close to 1:1 for long enough (defaults to 100 iterations within hys_ok_factor - defaults to 3%).
// 1:1 state is aborted when required adjustment goes beyond hys_bad_factor (defaults to 20%).
//
//To compensate for wide variation of the <num of samples> ratio due to relatively small size of the buffer,
// The required tempo is a running average of STRETCH_AVERAGE_LEN (defaults to 50) last calculations.
// This averaging slows down the respons time of the algorithm, but greatly stablize it towards steady stretching.
//
//Keeping the buffer at required latency:
// This algorithm stabilises when the actual latency is <speed>*<required_latency>. While this is just fine at 100% speed,
// it's problematic especially for slow speeds, as the number of actual samples at the buffer gets very small on that case,
// which may lead to underruns (or just too much latency when running very fast).
//To compensate for that, the algorithm has a slowly moving compensation factor which will eventually bring the actual latency to the required one.
//compensationDivider defines how slow this compensation changes. By default it's set to 100,
// which will finalize the compensation after about 200 iterations.
//
// Note, this algorithm is intentionally simplified by not taking extreme actions at extreme scenarios (mostly underruns when speed drops sharply),
// and let's the overrun/underrun protections do what they should (doesn't happen much though in practice, even at big FPS variations).
//
// These params were tested to show good respond and stability, on all audio systems (dsound, wav, port audio, xaudio2),
// even at extreme small latency of 50ms which can handle 50%-100% variations without audible glitches.
int targetIPS = 750;
//Dynamic tuning changes the values of the base algorithm parameters (derived from targetIPS) to adapt, in real time, to
// different number of invocations/sec (mostly affects number of iterations to average).
// Dynamic tuning can have a slight negative effect on the behavior of the algorithm, so it's preferred to have it off.
//Currently it looks like it's around 750/sec on all systems when playing at 100% speed (50/60fps),
// and proportional to that speed otherwise.
//If changes are made to SPU2X which affects this number (but it's still the same on all systems), then just change targetIPS.
//If we find out that some systems are very different, we can turn on dynamic tuning by uncommenting the next line.
//#define NEWSTRETCHER_USE_DYNAMIC_TUNING
//Additional performance note: since MAX_STRETCH_AVERAGE_LEN = 128 (or any power of 2), the '%' below
//could be replaced with a faster '&'. The compiler is highly likely to do it since all the values are unsigned.
#define AVERAGING_BUFFER_SIZE 256U
unsigned int AVERAGING_WINDOW = 50.0 * targetIPS / 750;
#define STRETCHER_RESET_THRESHOLD 5
int gRequestStretcherReset = STRETCHER_RESET_THRESHOLD;
//Adds a value to the running average buffer, and return the new running average.
float addToAvg(float val)
{
static float avg_fullness[AVERAGING_BUFFER_SIZE];
static unsigned int nextAvgPos = 0;
static unsigned int available = 0; // Make sure we're not averaging AVERAGING_WINDOW items if we inserted less.
if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD)
available = 0;
if (available < AVERAGING_BUFFER_SIZE)
available++;
avg_fullness[nextAvgPos] = val;
nextAvgPos = (nextAvgPos + 1U) % AVERAGING_BUFFER_SIZE;
unsigned int actualWindow = std::min(available, AVERAGING_WINDOW);
unsigned int first = (nextAvgPos - actualWindow + AVERAGING_BUFFER_SIZE) % AVERAGING_BUFFER_SIZE;
// Possible optimization: if we know that actualWindow hasn't changed since
// last invocation, we could calculate the running average in O(1) instead of O(N)
// by keeping a running sum between invocations, and then
// do "runningSum = runningSum + val - avg_fullness[(first-1)%...]" instead of the following loop.
// Few gotchas: val overwrites first-1, handling actualWindow changes, etc.
// However, this isn't hot code, so unless proven otherwise, we can live with unoptimized code.
float sum = 0;
for (unsigned int i = first; i < first + actualWindow; i++) {
sum += avg_fullness[i % AVERAGING_BUFFER_SIZE];
}
sum = sum / actualWindow;
return sum ? sum : 1; // 1 because that's the 100% perfect speed value
}
template <class T>
bool IsInRange(const T &val, const T &min, const T &max)
{
return (min <= val && val <= max);
}
//actual stretch algorithm implementation
void SndBuffer::UpdateTempoChangeSoundTouch2()
{
long targetSamplesReservoir = 48 * SndOutLatencyMS; //48000*SndOutLatencyMS/1000
//base aim at buffer filled %
float baseTargetFullness = (double)targetSamplesReservoir; ///(double)m_size;//0.05;
//state vars
static bool inside_hysteresis; //=false;
static int hys_ok_count; //=0;
static float dynamicTargetFullness; //=baseTargetFullness;
if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD) {
ConLog("______> stretch: Reset.\n");
inside_hysteresis = false;
hys_ok_count = 0;
dynamicTargetFullness = baseTargetFullness;
}
int data = _GetApproximateDataInBuffer();
float bufferFullness = (float)data; ///(float)m_size;
#ifdef NEWSTRETCHER_USE_DYNAMIC_TUNING
{ //test current iterations/sec every 0.5s, and change algo params accordingly if different than previous IPS more than 30%
static long iters = 0;
static wxDateTime last = wxDateTime::UNow();
wxDateTime unow = wxDateTime::UNow();
wxTimeSpan delta = unow.Subtract(last);
if (delta.GetMilliseconds() > 500) {
int pot_targetIPS = 1000.0 / delta.GetMilliseconds().ToDouble() * iters;
if (!IsInRange(pot_targetIPS, int((float)targetIPS / 1.3f), int((float)targetIPS * 1.3f))) {
if (MsgOverruns())
ConLog("Stretcher: setting iters/sec from %d to %d\n", targetIPS, pot_targetIPS);
targetIPS = pot_targetIPS;
AVERAGING_WINDOW = GetClamped((int)(50.0f * (float)targetIPS / 750.0f), 3, (int)AVERAGING_BUFFER_SIZE);
}
last = unow;
iters = 0;
}
iters++;
}
#endif
//Algorithm params: (threshold params (hysteresis), etc)
const float hys_ok_factor = 1.04f;
const float hys_bad_factor = 1.2f;
int hys_min_ok_count = GetClamped((int)(50.0 * (float)targetIPS / 750.0), 2, 100); //consecutive iterations within hys_ok before going to 1:1 mode
int compensationDivider = GetClamped((int)(100.0 * (float)targetIPS / 750), 15, 150);
float tempoAdjust = bufferFullness / dynamicTargetFullness;
float avgerage = addToAvg(tempoAdjust);
tempoAdjust = avgerage;
// Dampen the adjustment to avoid overshoots (this means the average will compensate to the other side).
// This is different than simply bigger averaging window since bigger window also has bigger "momentum",
// so it's slower to slow down when it gets close to the equilibrium state and can therefore resonate.
// The dampening (sqrt was chosen for no very good reason) manages to mostly prevent that.
tempoAdjust = sqrt(tempoAdjust);
tempoAdjust = GetClamped(tempoAdjust, 0.05f, 10.0f);
if (tempoAdjust < 1)
baseTargetFullness /= sqrt(tempoAdjust); // slightly increase latency when running slow.
dynamicTargetFullness += (baseTargetFullness / tempoAdjust - dynamicTargetFullness) / (double)compensationDivider;
if (IsInRange(tempoAdjust, 0.9f, 1.1f) && IsInRange(dynamicTargetFullness, baseTargetFullness * 0.9f, baseTargetFullness * 1.1f))
dynamicTargetFullness = baseTargetFullness;
if (!inside_hysteresis) {
if (IsInRange(tempoAdjust, 1.0f / hys_ok_factor, hys_ok_factor))
hys_ok_count++;
else
hys_ok_count = 0;
if (hys_ok_count >= hys_min_ok_count) {
inside_hysteresis = true;
if (MsgOverruns())
ConLog("======> stretch: None (1:1)\n");
}
} else if (!IsInRange(tempoAdjust, 1.0f / hys_bad_factor, hys_bad_factor)) {
if (MsgOverruns())
ConLog("~~~~~~> stretch: Dynamic\n");
inside_hysteresis = false;
hys_ok_count = 0;
}
if (inside_hysteresis)
tempoAdjust = 1.0;
if (MsgOverruns()) {
static int iters = 0;
static wxDateTime last = wxDateTime::UNow();
wxDateTime unow = wxDateTime::UNow();
wxTimeSpan delta = unow.Subtract(last);
if (delta.GetMilliseconds() > 1000) { //report buffers state and tempo adjust every second
ConLog("buffers: %4d ms (%3.0f%%), tempo: %f, comp: %2.3f, iters: %d, (N-IPS:%d -> avg:%d, minokc:%d, div:%d) reset:%d\n",
(int)(data / 48), (double)(100.0 * bufferFullness / baseTargetFullness), (double)tempoAdjust, (double)(dynamicTargetFullness / baseTargetFullness), iters, (int)targetIPS, AVERAGING_WINDOW, hys_min_ok_count, compensationDivider, gRequestStretcherReset);
last = unow;
iters = 0;
}
iters++;
}
pSoundTouch->setTempo(tempoAdjust);
if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD)
gRequestStretcherReset = 0;
return;
}
void SndBuffer::UpdateTempoChangeSoundTouch()
{
float statusPct = GetStatusPct();
float pctChange = statusPct - lastPct;
float tempoChange;
float emergencyAdj = 0;
float newcee = cTempo; // workspace var. for cTempo
// IMPORTANT!
// If you plan to tweak these values, make sure you're using a release build
// OUTSIDE THE DEBUGGER to test it! The Visual Studio debugger can really cause
// erratic behavior in the audio buffers, and makes the timestretcher seem a
// lot more inconsistent than it really is.
// We have two factors.
// * Distance from nominal buffer status (50% full)
// * The change from previous update to this update.
// Prediction based on the buffer change:
// (linear seems to work better here)
tempoChange = pctChange * 0.75f;
if (statusPct * tempoChange < 0.0f) {
// only apply tempo change if it is in synch with the buffer status.
// In other words, if the buffer is high (over 0%), and is decreasing,
// ignore it. It'll just muck things up.
tempoChange = 0;
}
// Sudden spikes in framerate can cause the nominal buffer status
// to go critical, in which case we have to enact an emergency
// stretch. The following cubic formulas do that. Values near
// the extremeites give much larger results than those near 0.
// And the value is added only this time, and does not accumulate.
// (otherwise a large value like this would cause problems down the road)
// Constants:
// Weight - weights the statusPct's "emergency" consideration.
// higher values here will make the buffer perform more drastic
// compensations at the outer edges of the buffer (at -75 or +75%
// or beyond, for example).
// Range - scales the adjustment to the given range (more or less).
// The actual range is dependent on the weight used, so if you increase
// Weight you'll usually want to decrease Range somewhat to compensate.
// Prediction based on the buffer fill status:
const float statusWeight = 2.99f;
const float statusRange = 0.068f;
// "non-emergency" deadzone: In this area stretching will be strongly discouraged.
// Note: due tot he nature of timestretch latency, it's always a wee bit harder to
// cope with low fps (underruns) than it is high fps (overruns). So to help out a
// little, the low-end portions of this check are less forgiving than the high-sides.
if (cTempo < 0.965f || cTempo > 1.060f ||
pctChange < -0.38f || pctChange > 0.54f ||
statusPct < -0.42f || statusPct > 0.70f ||
eTempo < 0.89f || eTempo > 1.19f) {
//printf("Emergency stretch: cTempo = %f eTempo = %f pctChange = %f statusPct = %f\n",cTempo,eTempo,pctChange,statusPct);
emergencyAdj = (pow(statusPct * statusWeight, 3.0f) * statusRange);
}
// Smooth things out by factoring our previous adjustment into this one.
// It helps make the system 'feel' a little smarter by giving it at least
// one packet worth of history to help work off of:
emergencyAdj = (emergencyAdj * 0.75f) + (lastEmergencyAdj * 0.25f);
lastEmergencyAdj = emergencyAdj;
lastPct = statusPct;
// Accumulate a fraction of the tempo change into the tempo itself.
// This helps the system run "smarter" to games that run consistently
// fast or slow by altering the base tempo to something closer to the
// game's active speed. In tests most games normalize within 2 seconds
// at 100ms latency, which is pretty good (larger buffers normalize even
// quicker).
newcee += newcee * (tempoChange + emergencyAdj) * 0.03f;
// Apply tempoChange as a scale of cTempo. That way the effect is proportional
// to the current tempo. (otherwise tempos rate of change at the extremes would
// be too drastic)
float newTempo = newcee + (emergencyAdj * cTempo);
// ... and as a final optimization, only stretch if the new tempo is outside
// a nominal threshold. Keep this threshold check small, because it could
// cause some serious side effects otherwise. (enlarging the cTempo check above
// is usually better/safer)
if (newTempo < 0.970f || newTempo > 1.045f) {
cTempo = (float)newcee;
if (newTempo < 0.10f)
newTempo = 0.10f;
else if (newTempo > 10.0f)
newTempo = 10.0f;
if (cTempo < 0.15f)
cTempo = 0.15f;
else if (cTempo > 7.5f)
cTempo = 7.5f;
pSoundTouch->setTempo(eTempo = (float)newTempo);
/*ConLog("* SPU2-X: [Nominal %d%%] [Emergency: %d%%] (baseTempo: %d%% ) (newTempo: %d%%) (buffer: %d%%)\n",
//(relation < 0.0) ? "Normalize" : "",
(int)(tempoChange * 100.0 * 0.03),
(int)(emergencyAdj * 100.0),
(int)(cTempo * 100.0),
(int)(newTempo * 100.0),
(int)(statusPct * 100.0)
);*/
} else {
// Nominal operation -- turn off stretching.
// note: eTempo 'slides' toward 1.0 for smoother audio and better
// protection against spikes.
if (cTempo != 1.0f) {
cTempo = 1.0f;
eTempo = (1.0f + eTempo) * 0.5f;
pSoundTouch->setTempo(eTempo);
} else {
if (eTempo != cTempo)
pSoundTouch->setTempo(eTempo = cTempo);
}
}
}
extern uint TickInterval;
void SndBuffer::UpdateTempoChangeAsyncMixing()
{
float statusPct = GetStatusPct();
lastPct = statusPct;
if (statusPct < -0.1f) {
TickInterval -= 4;
if (statusPct < -0.3f)
TickInterval = 64;
if (TickInterval < 64)
TickInterval = 64;
//printf("-- %d, %f\n",TickInterval,statusPct);
} else if (statusPct > 0.2f) {
TickInterval += 1;
if (TickInterval >= 7000)
TickInterval = 7000;
//printf("++ %d, %f\n",TickInterval,statusPct);
} else
TickInterval = 768;
}
void SndBuffer::timeStretchUnderrun()
{
gRequestStretcherReset++;
// timeStretcher failed it's job. We need to slow down the audio some.
cTempo -= (cTempo * 0.12f);
eTempo -= (eTempo * 0.30f);
if (eTempo < 0.1f)
eTempo = 0.1f;
// pSoundTouch->setTempo( eTempo );
//pSoundTouch->setTempoChange(-30); // temporary (until stretcher is called) slow down
}
s32 SndBuffer::timeStretchOverrun()
{
// If we overran it means the timestretcher failed. We need to speed
// up audio playback.
cTempo += cTempo * 0.12f;
eTempo += eTempo * 0.40f;
if (eTempo > 7.5f)
eTempo = 7.5f;
//pSoundTouch->setTempo( eTempo );
//pSoundTouch->setTempoChange(30);// temporary (until stretcher is called) speed up
// Throw out just a little bit (two packets worth) to help
// give the TS some room to work:
gRequestStretcherReset++;
return SndOutPacketSize * 2;
}
static void CvtPacketToFloat(StereoOut32 *srcdest)
{
StereoOutFloat *dest = (StereoOutFloat *)srcdest;
const StereoOut32 *src = (StereoOut32 *)srcdest;
for (uint i = 0; i < SndOutPacketSize; ++i, ++dest, ++src)
*dest = (StereoOutFloat)*src;
}
// Parameter note: Size should always be a multiple of 128, thanks!
static void CvtPacketToInt(StereoOut32 *srcdest, uint size)
{
//pxAssume( (size & 127) == 0 );
const StereoOutFloat *src = (StereoOutFloat *)srcdest;
StereoOut32 *dest = srcdest;
for (uint i = 0; i < size; ++i, ++dest, ++src)
*dest = (StereoOut32)*src;
}
void SndBuffer::timeStretchWrite()
{
// data prediction helps keep the tempo adjustments more accurate.
// The timestretcher returns packets in belated "clump" form.
// Meaning that most of the time we'll get nothing back, and then
// suddenly we'll get several chunks back at once. Thus we use
// data prediction to make the timestretcher more responsive.
PredictDataWrite((int)(SndOutPacketSize / eTempo));
CvtPacketToFloat(sndTempBuffer);
pSoundTouch->putSamples((float *)sndTempBuffer, SndOutPacketSize);
int tempProgress;
while (tempProgress = pSoundTouch->receiveSamples((float *)sndTempBuffer, SndOutPacketSize),
tempProgress != 0) {
// Hint: It's assumed that pSoundTouch will return chunks of 128 bytes (it always does as
// long as the SSE optimizations are enabled), which means we can do our own SSE opts here.
CvtPacketToInt(sndTempBuffer, tempProgress);
_WriteSamples(sndTempBuffer, tempProgress);
}
#ifdef SPU2X_USE_OLD_STRETCHER
UpdateTempoChangeSoundTouch();
#else
UpdateTempoChangeSoundTouch2();
#endif
}
void SndBuffer::soundtouchInit()
{
pSoundTouch = new soundtouch::SoundTouch();
pSoundTouch->setSampleRate(SampleRate);
pSoundTouch->setChannels(2);
pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, 0);
pSoundTouch->setSetting(SETTING_USE_AA_FILTER, 0);
SoundtouchCfg::ApplySettings(*pSoundTouch);
pSoundTouch->setTempo(1);
// some timestretch management vars:
cTempo = 1.0;
eTempo = 1.0;
lastPct = 0;
lastEmergencyAdj = 0;
m_predictData = 0;
}
// reset timestretch management vars, and delay updates a bit:
void SndBuffer::soundtouchClearContents()
{
if (pSoundTouch == NULL)
return;
pSoundTouch->clear();
pSoundTouch->setTempo(1);
cTempo = 1.0;
eTempo = 1.0;
lastPct = 0;
lastEmergencyAdj = 0;
m_predictData = 0;
}
void SndBuffer::soundtouchCleanup()
{
safe_delete(pSoundTouch);
}

148
pcsx2/SPU2/WavFile.cpp Normal file
View File

@ -0,0 +1,148 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* The file is based on WavFile.h from SoundTouch library.
* Original portions are (c) 2009 by Olli Parviainen (oparviai 'at' iki.fi)
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
// Note the file is mostly a copy paste of the WavFile.h from SoundTouch library. It was
// shrunken to support only output 16 bits wav files
#include <stdio.h>
#include <stdexcept>
#include <string>
#include <cstring>
#include <assert.h>
#include <limits.h>
#include "WavFile.h"
using namespace std;
static const char riffStr[] = "RIFF";
static const char waveStr[] = "WAVE";
static const char fmtStr[] = "fmt ";
static const char dataStr[] = "data";
//////////////////////////////////////////////////////////////////////////////
//
// Class WavOutFile
//
WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int channels)
{
bytesWritten = 0;
fptr = fopen(fileName, "wb");
if (fptr == NULL) {
string msg = "Error : Unable to open file \"";
msg += fileName;
msg += "\" for writing.";
//pmsg = msg.c_str;
throw runtime_error(msg);
}
fillInHeader(sampleRate, bits, channels);
writeHeader();
}
WavOutFile::~WavOutFile()
{
if (fptr) {
finishHeader();
fclose(fptr);
}
}
void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels)
{
// fill in the 'riff' part..
// copy string 'RIFF' to riff_char
memcpy(&(header.riff.riff_char), riffStr, 4);
// package_len unknown so far
header.riff.package_len = 0;
// copy string 'WAVE' to wave
memcpy(&(header.riff.wave), waveStr, 4);
// fill in the 'format' part..
// copy string 'fmt ' to fmt
memcpy(&(header.format.fmt), fmtStr, 4);
header.format.format_len = 0x10;
header.format.fixed = 1;
header.format.channel_number = (short)channels;
header.format.sample_rate = (int)sampleRate;
header.format.bits_per_sample = (short)bits;
header.format.byte_per_sample = (short)(bits * channels / 8);
header.format.byte_rate = header.format.byte_per_sample * (int)sampleRate;
header.format.sample_rate = (int)sampleRate;
// fill in the 'data' part..
// copy string 'data' to data_field
memcpy(&(header.data.data_field), dataStr, 4);
// data_len unknown so far
header.data.data_len = 0;
}
void WavOutFile::finishHeader()
{
// supplement the file length into the header structure
header.riff.package_len = bytesWritten + 36;
header.data.data_len = bytesWritten;
writeHeader();
}
void WavOutFile::writeHeader()
{
int res;
// write the supplemented header in the beginning of the file
fseek(fptr, 0, SEEK_SET);
res = fwrite(&header, sizeof(header), 1, fptr);
if (res != 1) {
throw runtime_error("Error while writing to a wav file.");
}
// jump back to the end of the file
fseek(fptr, 0, SEEK_END);
}
void WavOutFile::write(const short *buffer, int numElems)
{
int res;
// 16bit format & 16 bit samples
assert(header.format.bits_per_sample == 16);
if (numElems < 1)
return; // nothing to do
res = fwrite(buffer, 2, numElems, fptr);
if (res != numElems) {
throw runtime_error("Error while writing to a wav file.");
}
bytesWritten += 2 * numElems;
}

112
pcsx2/SPU2/WavFile.h Normal file
View File

@ -0,0 +1,112 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* The file is based on WavFile.h from SoundTouch library.
* Original portions are (c) 2009 by Olli Parviainen (oparviai 'at' iki.fi)
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
// Note the file is mostly a copy paste of the WavFile.h from SoundTouch library. It was
// shrunken to support only output 16 bits wav files
#ifndef WAVFILE_H
#define WAVFILE_H
#include <stdio.h>
#ifndef uint
typedef unsigned int uint;
#endif
/// WAV audio file 'riff' section header
typedef struct
{
char riff_char[4];
int package_len;
char wave[4];
} WavRiff;
/// WAV audio file 'format' section header
typedef struct
{
char fmt[4];
int format_len;
short fixed;
short channel_number;
int sample_rate;
int byte_rate;
short byte_per_sample;
short bits_per_sample;
} WavFormat;
/// WAV audio file 'data' section header
typedef struct
{
char data_field[4];
uint data_len;
} WavData;
/// WAV audio file header
typedef struct
{
WavRiff riff;
WavFormat format;
WavData data;
} WavHeader;
/// Class for writing WAV audio files.
class WavOutFile
{
private:
/// Pointer to the WAV file
FILE *fptr;
/// WAV file header data.
WavHeader header;
/// Counter of how many bytes have been written to the file so far.
int bytesWritten;
/// Fills in WAV file header information.
void fillInHeader(const uint sampleRate, const uint bits, const uint channels);
/// Finishes the WAV file header by supplementing information of amount of
/// data written to file etc
void finishHeader();
/// Writes the WAV file header.
void writeHeader();
public:
/// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception
/// if file creation fails.
WavOutFile(const char *fileName, ///< Filename
int sampleRate, ///< Sample rate (e.g. 44100 etc)
int bits, ///< Bits per sample (8 or 16 bits)
int channels ///< Number of channels (1=mono, 2=stereo)
);
/// Destructor: Finalizes & closes the WAV file.
~WavOutFile();
/// Write data to WAV file. Throws a 'runtime_error' exception if writing to
/// file fails.
void write(const short *buffer, ///< Pointer to sample data buffer.
int numElems ///< How many array items are to be written to file.
);
};
#endif

142
pcsx2/SPU2/Wavedump_wav.cpp Normal file
View File

@ -0,0 +1,142 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#ifdef __POSIX__
#include "WavFile.h"
#else
#include "soundtouch/source/SoundStretch/WavFile.h"
#endif
static WavOutFile *_new_WavOutFile(const char *destfile)
{
return new WavOutFile(destfile, 48000, 16, 2);
}
namespace WaveDump
{
static WavOutFile *m_CoreWav[2][CoreSrc_Count];
static const char *m_tbl_CoreOutputTypeNames[CoreSrc_Count] =
{
"Input",
"DryVoiceMix",
"WetVoiceMix",
"PreReverb",
"PostReverb",
"External"};
void Open()
{
if (!IsDevBuild)
return;
if (!WaveLog())
return;
char wavfilename[256];
for (uint cidx = 0; cidx < 2; cidx++) {
for (int srcidx = 0; srcidx < CoreSrc_Count; srcidx++) {
safe_delete(m_CoreWav[cidx][srcidx]);
#ifdef __POSIX__
sprintf(wavfilename, "logs/spu2x-Core%ud-%s.wav",
cidx, m_tbl_CoreOutputTypeNames[srcidx]);
#else
sprintf(wavfilename, "logs\\spu2x-Core%ud-%s.wav",
cidx, m_tbl_CoreOutputTypeNames[srcidx]);
#endif
try {
m_CoreWav[cidx][srcidx] = _new_WavOutFile(wavfilename);
} catch (std::runtime_error &ex) {
printf("SPU2-X > %s.\n\tWave Log for this core source disabled.", ex.what());
m_CoreWav[cidx][srcidx] = NULL;
}
}
}
}
void Close()
{
if (!IsDevBuild)
return;
for (uint cidx = 0; cidx < 2; cidx++) {
for (int srcidx = 0; srcidx < CoreSrc_Count; srcidx++) {
safe_delete(m_CoreWav[cidx][srcidx]);
}
}
}
void WriteCore(uint coreidx, CoreSourceType src, const StereoOut16 &sample)
{
if (!IsDevBuild)
return;
if (m_CoreWav[coreidx][src] != NULL)
m_CoreWav[coreidx][src]->write((s16 *)&sample, 2);
}
void WriteCore(uint coreidx, CoreSourceType src, s16 left, s16 right)
{
WriteCore(coreidx, src, StereoOut16(left, right));
}
}
#include "Utilities/Threading.h"
using namespace Threading;
bool WavRecordEnabled = false;
static WavOutFile *m_wavrecord = NULL;
static Mutex WavRecordMutex;
void RecordStart(std::wstring* filename)
{
WavRecordEnabled = false;
try {
ScopedLock lock(WavRecordMutex);
safe_delete(m_wavrecord);
#ifdef _WIN32
if (filename)
m_wavrecord = new WavOutFile((*filename) + "wav", 48000, 16, 2);
else
m_wavrecord = new WavOutFile("audio_recording.wav", 48000, 16, 2);
#elif defined(__unix__)
m_wavrecord = new WavOutFile("audio_recording.wav", 48000, 16, 2);
#endif
WavRecordEnabled = true;
} catch (std::runtime_error &) {
m_wavrecord = NULL; // not needed, but what the heck. :)
SysMessage("SPU2-X couldn't open file for recording: %s.\nRecording to wavfile disabled.", "audio_recording.wav");
}
}
void RecordStop()
{
WavRecordEnabled = false;
ScopedLock lock(WavRecordMutex);
safe_delete(m_wavrecord);
}
void RecordWrite(const StereoOut16 &sample)
{
ScopedLock lock(WavRecordMutex);
if (m_wavrecord == NULL)
return;
m_wavrecord->write((s16 *)&sample, 2);
}

View File

@ -0,0 +1,197 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "Dialogs.h"
#include "Utilities/StringHelpers.h"
extern uptr gsWindowHandle;
void SysMessage(const char *fmt, ...)
{
va_list list;
char tmp[512];
wchar_t wtmp[512];
va_start(list, fmt);
vsprintf_s(tmp, fmt, list);
va_end(list);
swprintf_s(wtmp, L"%S", tmp);
MessageBox((!!gsWindowHandle) ? (HWND)gsWindowHandle : GetActiveWindow(), wtmp,
L"SPU2-X System Message", MB_OK | MB_SETFOREGROUND);
}
void SysMessage(const wchar_t *fmt, ...)
{
va_list list;
va_start(list, fmt);
wxString wtmp;
wtmp.PrintfV(fmt, list);
va_end(list);
MessageBox((!!gsWindowHandle) ? (HWND)gsWindowHandle : GetActiveWindow(), wtmp,
L"SPU2-X System Message", MB_OK | MB_SETFOREGROUND);
}
//////
#include "Utilities/Path.h"
static wxString CfgFile(L"inis/SPU2-X.ini");
void CfgSetSettingsDir(const char *dir)
{
CfgFile = Path::Combine((dir == NULL) ? wxString(L"inis") : wxString::FromUTF8(dir), L"SPU2-X.ini");
}
/*| Config File Format: |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*\
+--+---------------------+------------------------+
| |
| Option=Value |
| |
| |
| Boolean Values: TRUE,YES,1,T,Y mean 'true', |
| everything else means 'false'. |
| |
| All Values are limited to 255 chars. |
| |
+-------------------------------------------------+
\*_____________________________________________*/
void CfgWriteBool(const TCHAR *Section, const TCHAR *Name, bool Value)
{
const TCHAR *Data = Value ? L"TRUE" : L"FALSE";
WritePrivateProfileString(Section, Name, Data, CfgFile);
}
void CfgWriteInt(const TCHAR *Section, const TCHAR *Name, int Value)
{
TCHAR Data[32];
_itow(Value, Data, 10);
WritePrivateProfileString(Section, Name, Data, CfgFile);
}
void CfgWriteFloat(const TCHAR *Section, const TCHAR *Name, float Value)
{
TCHAR Data[32];
_swprintf(Data, L"%f", Value);
WritePrivateProfileString(Section, Name, Data, CfgFile);
}
/*void CfgWriteStr(const TCHAR* Section, const TCHAR* Name, const TCHAR *Data)
{
WritePrivateProfileString( Section, Name, Data, CfgFile );
}*/
void CfgWriteStr(const TCHAR *Section, const TCHAR *Name, const wxString &Data)
{
WritePrivateProfileString(Section, Name, Data, CfgFile);
}
/*****************************************************************************/
bool CfgReadBool(const TCHAR *Section, const TCHAR *Name, bool Default)
{
TCHAR Data[255] = {0};
GetPrivateProfileString(Section, Name, L"", Data, 255, CfgFile);
Data[254] = 0;
if (wcslen(Data) == 0) {
CfgWriteBool(Section, Name, Default);
return Default;
}
if (wcscmp(Data, L"1") == 0)
return true;
if (wcscmp(Data, L"Y") == 0)
return true;
if (wcscmp(Data, L"T") == 0)
return true;
if (wcscmp(Data, L"YES") == 0)
return true;
if (wcscmp(Data, L"TRUE") == 0)
return true;
return false;
}
int CfgReadInt(const TCHAR *Section, const TCHAR *Name, int Default)
{
TCHAR Data[255] = {0};
GetPrivateProfileString(Section, Name, L"", Data, 255, CfgFile);
Data[254] = 0;
if (wcslen(Data) == 0) {
CfgWriteInt(Section, Name, Default);
return Default;
}
return _wtoi(Data);
}
float CfgReadFloat(const TCHAR *Section, const TCHAR *Name, float Default)
{
TCHAR Data[255] = {0};
GetPrivateProfileString(Section, Name, L"", Data, 255, CfgFile);
Data[254] = 0;
if (wcslen(Data) == 0) {
CfgWriteFloat(Section, Name, Default);
return Default;
}
return (float)_wtof(Data);
}
void CfgReadStr(const TCHAR *Section, const TCHAR *Name, TCHAR *Data, int DataSize, const TCHAR *Default)
{
GetPrivateProfileString(Section, Name, L"", Data, DataSize, CfgFile);
if (wcslen(Data) == 0) {
swprintf_s(Data, DataSize, L"%s", Default);
CfgWriteStr(Section, Name, Data);
}
}
void CfgReadStr(const TCHAR *Section, const TCHAR *Name, wxString &Data, const TCHAR *Default)
{
wchar_t workspace[512];
GetPrivateProfileString(Section, Name, L"", workspace, ArraySize(workspace), CfgFile);
Data = workspace;
if (Data.empty()) {
Data = Default;
CfgWriteStr(Section, Name, Default);
}
}
// Tries to read the requested value.
// Returns FALSE if the value isn't found.
bool CfgFindName(const TCHAR *Section, const TCHAR *Name)
{
// Only load 24 characters. No need to load more.
TCHAR Data[24] = {0};
GetPrivateProfileString(Section, Name, L"", Data, 24, CfgFile);
Data[23] = 0;
if (wcslen(Data) == 0)
return false;
return true;
}

View File

@ -0,0 +1,428 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "Dialogs.h"
#include <math.h>
#ifdef PCSX2_DEVBUILD
static const int LATENCY_MAX = 3000;
#else
static const int LATENCY_MAX = 750;
#endif
static const int LATENCY_MIN = 3;
static const int LATENCY_MIN_TS = 15;
// MIXING
int Interpolation = 4;
/* values:
0: no interpolation (use nearest)
1. linear interpolation
2. cubic interpolation
3. hermite interpolation
4. catmull-rom interpolation
*/
bool EffectsDisabled = false;
float FinalVolume; // Global
bool AdvancedVolumeControl;
float VolumeAdjustFLdb; // decibels settings, cos audiophiles love that
float VolumeAdjustCdb;
float VolumeAdjustFRdb;
float VolumeAdjustBLdb;
float VolumeAdjustBRdb;
float VolumeAdjustSLdb;
float VolumeAdjustSRdb;
float VolumeAdjustLFEdb;
float VolumeAdjustFL; // linear coefs calcualted from decibels,
float VolumeAdjustC;
float VolumeAdjustFR;
float VolumeAdjustBL;
float VolumeAdjustBR;
float VolumeAdjustSL;
float VolumeAdjustSR;
float VolumeAdjustLFE;
unsigned int delayCycles;
bool postprocess_filter_enabled = 1;
bool postprocess_filter_dealias = false;
// OUTPUT
int SndOutLatencyMS = 100;
int SynchMode = 0; // Time Stretch, Async or Disabled
u32 OutputModule = 0;
CONFIG_WAVEOUT Config_WaveOut;
CONFIG_XAUDIO2 Config_XAudio2;
// DSP
bool dspPluginEnabled = false;
int dspPluginModule = 0;
wchar_t dspPlugin[256];
int numSpeakers = 0;
int dplLevel = 0;
/*****************************************************************************/
void ReadSettings()
{
Interpolation = CfgReadInt(L"MIXING", L"Interpolation", 4);
EffectsDisabled = CfgReadBool(L"MIXING", L"Disable_Effects", false);
postprocess_filter_dealias = CfgReadBool(L"MIXING", L"DealiasFilter", false);
FinalVolume = ((float)CfgReadInt(L"MIXING", L"FinalVolume", 100)) / 100;
if (FinalVolume > 1.0f)
FinalVolume = 1.0f;
AdvancedVolumeControl = CfgReadBool(L"MIXING", L"AdvancedVolumeControl", false);
VolumeAdjustCdb = CfgReadFloat(L"MIXING", L"VolumeAdjustC(dB)", 0);
VolumeAdjustFLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFL(dB)", 0);
VolumeAdjustFRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFR(dB)", 0);
VolumeAdjustBLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBL(dB)", 0);
VolumeAdjustBRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBR(dB)", 0);
VolumeAdjustSLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSL(dB)", 0);
VolumeAdjustSRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSR(dB)", 0);
VolumeAdjustLFEdb = CfgReadFloat(L"MIXING", L"VolumeAdjustLFE(dB)", 0);
delayCycles = CfgReadInt(L"DEBUG", L"DelayCycles", 4);
VolumeAdjustC = powf(10, VolumeAdjustCdb / 10);
VolumeAdjustFL = powf(10, VolumeAdjustFLdb / 10);
VolumeAdjustFR = powf(10, VolumeAdjustFRdb / 10);
VolumeAdjustBL = powf(10, VolumeAdjustBLdb / 10);
VolumeAdjustBR = powf(10, VolumeAdjustBRdb / 10);
VolumeAdjustSL = powf(10, VolumeAdjustSLdb / 10);
VolumeAdjustSR = powf(10, VolumeAdjustSRdb / 10);
VolumeAdjustLFE = powf(10, VolumeAdjustLFEdb / 10);
SynchMode = CfgReadInt(L"OUTPUT", L"Synch_Mode", 0);
numSpeakers = CfgReadInt(L"OUTPUT", L"SpeakerConfiguration", 0);
dplLevel = CfgReadInt(L"OUTPUT", L"DplDecodingLevel", 0);
SndOutLatencyMS = CfgReadInt(L"OUTPUT", L"Latency", 100);
if ((SynchMode == 0) && (SndOutLatencyMS < LATENCY_MIN_TS)) // can't use low-latency with timestretcher atm
SndOutLatencyMS = LATENCY_MIN_TS;
else if (SndOutLatencyMS < LATENCY_MIN)
SndOutLatencyMS = LATENCY_MIN;
wchar_t omodid[128];
// portaudio occasionally has issues selecting the proper default audio device.
// let's use xaudio2 until this is sorted (rama)
// CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, PortaudioOut->GetIdent());
CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, XAudio2Out->GetIdent());
// find the driver index of this module:
OutputModule = FindOutputModuleById(omodid);
CfgReadStr(L"DSP PLUGIN", L"Filename", dspPlugin, 255, L"");
dspPluginModule = CfgReadInt(L"DSP PLUGIN", L"ModuleNum", 0);
dspPluginEnabled = CfgReadBool(L"DSP PLUGIN", L"Enabled", false);
// Read DSOUNDOUT and WAVEOUT configs:
CfgReadStr(L"WAVEOUT", L"Device", Config_WaveOut.Device, L"default");
Config_WaveOut.NumBuffers = CfgReadInt(L"WAVEOUT", L"Buffer_Count", 4);
DSoundOut->ReadSettings();
PortaudioOut->ReadSettings();
SoundtouchCfg::ReadSettings();
DebugConfig::ReadSettings();
// Sanity Checks
// -------------
Clampify(SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX);
if (mods[OutputModule] == NULL) {
// Unsupported or legacy module.
fwprintf(stderr, L"* SPU2-X: Unknown output module '%s' specified in configuration file.\n", omodid);
fprintf(stderr, "* SPU2-X: Defaulting to DirectSound (%S).\n", DSoundOut->GetIdent());
OutputModule = FindOutputModuleById(DSoundOut->GetIdent());
}
}
/*****************************************************************************/
void WriteSettings()
{
CfgWriteInt(L"MIXING", L"Interpolation", Interpolation);
CfgWriteBool(L"MIXING", L"Disable_Effects", EffectsDisabled);
CfgWriteBool(L"MIXING", L"DealiasFilter", postprocess_filter_dealias);
CfgWriteInt(L"MIXING", L"FinalVolume", (int)(FinalVolume * 100 + 0.5f));
CfgWriteBool(L"MIXING", L"AdvancedVolumeControl", AdvancedVolumeControl);
CfgWriteFloat(L"MIXING", L"VolumeAdjustC(dB)", VolumeAdjustCdb);
CfgWriteFloat(L"MIXING", L"VolumeAdjustFL(dB)", VolumeAdjustFLdb);
CfgWriteFloat(L"MIXING", L"VolumeAdjustFR(dB)", VolumeAdjustFRdb);
CfgWriteFloat(L"MIXING", L"VolumeAdjustBL(dB)", VolumeAdjustBLdb);
CfgWriteFloat(L"MIXING", L"VolumeAdjustBR(dB)", VolumeAdjustBRdb);
CfgWriteFloat(L"MIXING", L"VolumeAdjustSL(dB)", VolumeAdjustSLdb);
CfgWriteFloat(L"MIXING", L"VolumeAdjustSR(dB)", VolumeAdjustSRdb);
CfgWriteFloat(L"MIXING", L"VolumeAdjustLFE(dB)", VolumeAdjustLFEdb);
CfgWriteStr(L"OUTPUT", L"Output_Module", mods[OutputModule]->GetIdent());
CfgWriteInt(L"OUTPUT", L"Latency", SndOutLatencyMS);
CfgWriteInt(L"OUTPUT", L"Synch_Mode", SynchMode);
CfgWriteInt(L"OUTPUT", L"SpeakerConfiguration", numSpeakers);
CfgWriteInt(L"OUTPUT", L"DplDecodingLevel", dplLevel);
CfgWriteInt(L"DEBUG", L"DelayCycles", delayCycles);
if (Config_WaveOut.Device.empty())
Config_WaveOut.Device = L"default";
CfgWriteStr(L"WAVEOUT", L"Device", Config_WaveOut.Device);
CfgWriteInt(L"WAVEOUT", L"Buffer_Count", Config_WaveOut.NumBuffers);
CfgWriteStr(L"DSP PLUGIN", L"Filename", dspPlugin);
CfgWriteInt(L"DSP PLUGIN", L"ModuleNum", dspPluginModule);
CfgWriteBool(L"DSP PLUGIN", L"Enabled", dspPluginEnabled);
PortaudioOut->WriteSettings();
DSoundOut->WriteSettings();
SoundtouchCfg::WriteSettings();
DebugConfig::WriteSettings();
}
void CheckOutputModule(HWND window)
{
OutputModule = SendMessage(GetDlgItem(window, IDC_OUTPUT), CB_GETCURSEL, 0, 0);
const bool IsConfigurable =
mods[OutputModule] == PortaudioOut ||
mods[OutputModule] == WaveOut ||
mods[OutputModule] == DSoundOut;
const bool AudioExpansion =
mods[OutputModule] == XAudio2Out ||
mods[OutputModule] == PortaudioOut;
EnableWindow(GetDlgItem(window, IDC_OUTCONF), IsConfigurable);
EnableWindow(GetDlgItem(window, IDC_SPEAKERS), AudioExpansion);
EnableWindow(GetDlgItem(window, IDC_SPEAKERS_TEXT), AudioExpansion);
}
BOOL CALLBACK ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
wchar_t temp[384] = {0};
switch (uMsg) {
case WM_PAINT:
return FALSE;
case WM_INITDIALOG: {
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_RESETCONTENT, 0, 0);
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"0 - Nearest (Fastest/bad quality)");
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"1 - Linear (Simple/okay sound)");
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"2 - Cubic (Artificial highs)");
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"3 - Hermite (Better highs)");
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"4 - Catmull-Rom (PS2-like/slow)");
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_SETCURSEL, Interpolation, 0);
SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_RESETCONTENT, 0, 0);
SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_ADDSTRING, 0, (LPARAM)L"TimeStretch (Recommended)");
SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_ADDSTRING, 0, (LPARAM)L"Async Mix (Breaks some games!)");
SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_ADDSTRING, 0, (LPARAM)L"None (Audio can skip.)");
SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_SETCURSEL, SynchMode, 0);
SendDialogMsg(hWnd, IDC_SPEAKERS, CB_RESETCONTENT, 0, 0);
SendDialogMsg(hWnd, IDC_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)L"Stereo (None, Default)");
SendDialogMsg(hWnd, IDC_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)L"Quadrafonic");
SendDialogMsg(hWnd, IDC_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)L"Surround 5.1");
SendDialogMsg(hWnd, IDC_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)L"Surround 7.1");
SendDialogMsg(hWnd, IDC_SPEAKERS, CB_SETCURSEL, numSpeakers, 0);
SendDialogMsg(hWnd, IDC_OUTPUT, CB_RESETCONTENT, 0, 0);
int modidx = 0;
while (mods[modidx] != NULL) {
swprintf_s(temp, 72, L"%d - %s", modidx, mods[modidx]->GetLongName());
SendDialogMsg(hWnd, IDC_OUTPUT, CB_ADDSTRING, 0, (LPARAM)temp);
++modidx;
}
SendDialogMsg(hWnd, IDC_OUTPUT, CB_SETCURSEL, OutputModule, 0);
double minlat = (SynchMode == 0) ? LATENCY_MIN_TS : LATENCY_MIN;
int minexp = (int)(pow(minlat + 1, 1.0 / 3.0) * 128.0);
int maxexp = (int)(pow((double)LATENCY_MAX + 2, 1.0 / 3.0) * 128.0);
INIT_SLIDER(IDC_LATENCY_SLIDER, minexp, maxexp, 200, 42, 1);
SendDialogMsg(hWnd, IDC_LATENCY_SLIDER, TBM_SETPOS, TRUE, (int)((pow((double)SndOutLatencyMS, 1.0 / 3.0) * 128.0) + 1));
swprintf_s(temp, L"%d ms (avg)", SndOutLatencyMS);
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
int configvol = (int)(FinalVolume * 100 + 0.5f);
INIT_SLIDER(IDC_VOLUME_SLIDER, 0, 100, 10, 42, 1);
SendDialogMsg(hWnd, IDC_VOLUME_SLIDER, TBM_SETPOS, TRUE, configvol);
swprintf_s(temp, L"%d%%", configvol);
SetWindowText(GetDlgItem(hWnd, IDC_VOLUME_LABEL), temp);
CheckOutputModule(hWnd);
EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_SOUNDTOUCH), (SynchMode == 0));
EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_DEBUG), DebugEnabled);
SET_CHECK(IDC_EFFECTS_DISABLE, EffectsDisabled);
SET_CHECK(IDC_DEALIASFILTER, postprocess_filter_dealias);
SET_CHECK(IDC_DEBUG_ENABLE, DebugEnabled);
SET_CHECK(IDC_DSP_ENABLE, dspPluginEnabled);
} break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId) {
case IDOK: {
double res = ((int)SendDialogMsg(hWnd, IDC_LATENCY_SLIDER, TBM_GETPOS, 0, 0)) / 128.0;
SndOutLatencyMS = (int)pow(res, 3.0);
Clampify(SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX);
FinalVolume = (float)(SendDialogMsg(hWnd, IDC_VOLUME_SLIDER, TBM_GETPOS, 0, 0)) / 100;
Interpolation = (int)SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_GETCURSEL, 0, 0);
OutputModule = (int)SendDialogMsg(hWnd, IDC_OUTPUT, CB_GETCURSEL, 0, 0);
SynchMode = (int)SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_GETCURSEL, 0, 0);
numSpeakers = (int)SendDialogMsg(hWnd, IDC_SPEAKERS, CB_GETCURSEL, 0, 0);
WriteSettings();
EndDialog(hWnd, 0);
} break;
case IDCANCEL:
EndDialog(hWnd, 0);
break;
case IDC_OUTPUT:
if (wmEvent == CBN_SELCHANGE) {
CheckOutputModule(hWnd);
}
break;
case IDC_OUTCONF: {
const int module = (int)SendMessage(GetDlgItem(hWnd, IDC_OUTPUT), CB_GETCURSEL, 0, 0);
if (mods[module] == NULL)
break;
mods[module]->Configure((uptr)hWnd);
} break;
case IDC_OPEN_CONFIG_DEBUG: {
// Quick Hack -- DebugEnabled is re-loaded with the DebugConfig's API,
// so we need to override it here:
bool dbgtmp = DebugEnabled;
DebugConfig::OpenDialog();
DebugEnabled = dbgtmp;
} break;
case IDC_SYNCHMODE: {
if (wmEvent == CBN_SELCHANGE) {
int sMode = (int)SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_GETCURSEL, 0, 0);
double minlat = (sMode == 0) ? LATENCY_MIN_TS : LATENCY_MIN;
int minexp = (int)(pow(minlat + 1, 1.0 / 3.0) * 128.0);
int maxexp = (int)(pow((double)LATENCY_MAX + 2, 1.0 / 3.0) * 128.0);
INIT_SLIDER(IDC_LATENCY_SLIDER, minexp, maxexp, 200, 42, 1);
int curpos = (int)SendMessage(GetDlgItem(hWnd, IDC_LATENCY_SLIDER), TBM_GETPOS, 0, 0);
double res = pow(curpos / 128.0, 3.0);
curpos = (int)res;
swprintf_s(temp, L"%d ms (avg)", curpos);
SetDlgItemText(hWnd, IDC_LATENCY_LABEL, temp);
bool soundtouch = sMode == 0;
EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_SOUNDTOUCH), soundtouch);
}
} break;
case IDC_OPEN_CONFIG_SOUNDTOUCH:
SoundtouchCfg::OpenDialog(hWnd);
break;
HANDLE_CHECK(IDC_EFFECTS_DISABLE, EffectsDisabled);
HANDLE_CHECK(IDC_DEALIASFILTER, postprocess_filter_dealias);
HANDLE_CHECK(IDC_DSP_ENABLE, dspPluginEnabled);
HANDLE_CHECKNB(IDC_DEBUG_ENABLE, DebugEnabled);
DebugConfig::EnableControls(hWnd);
EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_DEBUG), DebugEnabled);
break;
default:
return FALSE;
}
break;
case WM_HSCROLL: {
wmEvent = LOWORD(wParam);
HWND hwndDlg = (HWND)lParam;
int curpos = HIWORD(wParam);
switch (wmEvent) {
case TB_LINEUP:
case TB_LINEDOWN:
case TB_PAGEUP:
case TB_PAGEDOWN:
case TB_TOP:
case TB_BOTTOM:
curpos = (int)SendMessage(hwndDlg, TBM_GETPOS, 0, 0);
case TB_THUMBPOSITION:
case TB_THUMBTRACK:
Clampify(curpos,
(int)SendMessage(hwndDlg, TBM_GETRANGEMIN, 0, 0),
(int)SendMessage(hwndDlg, TBM_GETRANGEMAX, 0, 0));
SendMessage((HWND)lParam, TBM_SETPOS, TRUE, curpos);
if (hwndDlg == GetDlgItem(hWnd, IDC_LATENCY_SLIDER)) {
double res = pow(curpos / 128.0, 3.0);
curpos = (int)res;
swprintf_s(temp, L"%d ms (avg)", curpos);
SetDlgItemText(hWnd, IDC_LATENCY_LABEL, temp);
}
if (hwndDlg == GetDlgItem(hWnd, IDC_VOLUME_SLIDER)) {
swprintf_s(temp, L"%d%%", curpos);
SetDlgItemText(hWnd, IDC_VOLUME_LABEL, temp);
}
break;
default:
return FALSE;
}
} break;
default:
return FALSE;
}
return TRUE;
}
void configure()
{
INT_PTR ret;
ReadSettings();
ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CONFIG), GetActiveWindow(), (DLGPROC)ConfigProc, 1);
if (ret == -1) {
MessageBox(GetActiveWindow(), L"Error Opening the config dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
return;
}
ReadSettings();
}

View File

@ -0,0 +1,269 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "Dialogs.h"
#include "Utilities\Path.h"
bool DebugEnabled = false;
bool _MsgToConsole = false;
bool _MsgKeyOnOff = false;
bool _MsgVoiceOff = false;
bool _MsgDMA = false;
bool _MsgAutoDMA = false;
bool _MsgOverruns = false;
bool _MsgCache = false;
bool _AccessLog = false;
bool _DMALog = false;
bool _WaveLog = false;
bool _CoresDump = false;
bool _MemDump = false;
bool _RegDump = false;
bool _visual_debug_enabled = false;
// this is set true if PCSX2 invokes the SetLogDir callback, which tells SPU2-X to use that over
// the configured crap in the ini file.
static bool LogLocationSetByPcsx2 = false;
static wxString CfgLogsFolder;
static wxString CfgDumpsFolder;
static wxDirName LogsFolder;
static wxDirName DumpsFolder;
wxString AccessLogFileName;
wxString DMA4LogFileName;
wxString DMA7LogFileName;
wxString CoresDumpFileName;
wxString MemDumpFileName;
wxString RegDumpFileName;
void CfgSetLogDir(const char *dir)
{
LogsFolder = (dir == NULL) ? wxString(L"logs") : wxString(dir, wxConvFile);
DumpsFolder = (dir == NULL) ? wxString(L"logs") : wxString(dir, wxConvFile);
LogLocationSetByPcsx2 = (dir != NULL);
}
FILE *OpenBinaryLog(const wxString &logfile)
{
return wxFopen(Path::Combine(LogsFolder, logfile), L"wb");
}
FILE *OpenLog(const wxString &logfile)
{
return wxFopen(Path::Combine(LogsFolder, logfile), L"w");
}
FILE *OpenDump(const wxString &logfile)
{
return wxFopen(Path::Combine(DumpsFolder, logfile), L"w");
}
namespace DebugConfig
{
static const wxChar *Section = L"DEBUG";
void ReadSettings()
{
DebugEnabled = CfgReadBool(Section, L"Global_Enable", 0);
_MsgToConsole = CfgReadBool(Section, L"Show_Messages", 0);
_MsgKeyOnOff = CfgReadBool(Section, L"Show_Messages_Key_On_Off", 0);
_MsgVoiceOff = CfgReadBool(Section, L"Show_Messages_Voice_Off", 0);
_MsgDMA = CfgReadBool(Section, L"Show_Messages_DMA_Transfer", 0);
_MsgAutoDMA = CfgReadBool(Section, L"Show_Messages_AutoDMA", 0);
_MsgOverruns = CfgReadBool(Section, L"Show_Messages_Overruns", 0);
_MsgCache = CfgReadBool(Section, L"Show_Messages_CacheStats", 0);
_AccessLog = CfgReadBool(Section, L"Log_Register_Access", 0);
_DMALog = CfgReadBool(Section, L"Log_DMA_Transfers", 0);
_WaveLog = CfgReadBool(Section, L"Log_WAVE_Output", 0);
_CoresDump = CfgReadBool(Section, L"Dump_Info", 0);
_MemDump = CfgReadBool(Section, L"Dump_Memory", 0);
_RegDump = CfgReadBool(Section, L"Dump_Regs", 0);
_visual_debug_enabled = CfgReadBool(Section, L"Visual_Debug_Enabled", 0);
CfgReadStr(Section, L"Logs_Folder", CfgLogsFolder, L"logs");
CfgReadStr(Section, L"Dumps_Folder", CfgDumpsFolder, L"logs");
CfgReadStr(Section, L"Access_Log_Filename", AccessLogFileName, L"SPU2Log.txt");
CfgReadStr(Section, L"DMA4Log_Filename", DMA4LogFileName, L"SPU2dma4.dat");
CfgReadStr(Section, L"DMA7Log_Filename", DMA7LogFileName, L"SPU2dma7.dat");
CfgReadStr(Section, L"Info_Dump_Filename", CoresDumpFileName, L"SPU2Cores.txt");
CfgReadStr(Section, L"Mem_Dump_Filename", MemDumpFileName, L"SPU2mem.dat");
CfgReadStr(Section, L"Reg_Dump_Filename", RegDumpFileName, L"SPU2regs.dat");
if (!LogLocationSetByPcsx2) {
LogsFolder = CfgLogsFolder;
DumpsFolder = CfgLogsFolder;
}
}
void WriteSettings()
{
CfgWriteBool(Section, L"Global_Enable", DebugEnabled);
CfgWriteBool(Section, L"Show_Messages", _MsgToConsole);
CfgWriteBool(Section, L"Show_Messages_Key_On_Off", _MsgKeyOnOff);
CfgWriteBool(Section, L"Show_Messages_Voice_Off", _MsgVoiceOff);
CfgWriteBool(Section, L"Show_Messages_DMA_Transfer", _MsgDMA);
CfgWriteBool(Section, L"Show_Messages_AutoDMA", _MsgAutoDMA);
CfgWriteBool(Section, L"Show_Messages_Overruns", _MsgOverruns);
CfgWriteBool(Section, L"Show_Messages_CacheStats", _MsgCache);
CfgWriteBool(Section, L"Log_Register_Access", _AccessLog);
CfgWriteBool(Section, L"Log_DMA_Transfers", _DMALog);
CfgWriteBool(Section, L"Log_WAVE_Output", _WaveLog);
CfgWriteBool(Section, L"Dump_Info", _CoresDump);
CfgWriteBool(Section, L"Dump_Memory", _MemDump);
CfgWriteBool(Section, L"Dump_Regs", _RegDump);
CfgWriteBool(Section, L"Visual_Debug_Enabled", _visual_debug_enabled);
// None of the logs strings are changable via GUI, so no point in bothering to
// write them back out.
CfgWriteStr(Section, L"Logs_Folder", CfgLogsFolder);
CfgWriteStr(Section, L"Dumps_Folder", CfgDumpsFolder);
CfgWriteStr(Section, L"Access_Log_Filename", AccessLogFileName);
CfgWriteStr(Section, L"DMA4Log_Filename", DMA4LogFileName);
CfgWriteStr(Section, L"DMA7Log_Filename", DMA7LogFileName);
CfgWriteStr(Section, L"Info_Dump_Filename", CoresDumpFileName);
CfgWriteStr(Section, L"Mem_Dump_Filename", MemDumpFileName);
CfgWriteStr(Section, L"Reg_Dump_Filename", RegDumpFileName);
}
static void EnableMessages(HWND hWnd)
{
ENABLE_CONTROL(IDC_MSGSHOW, DebugEnabled);
ENABLE_CONTROL(IDC_MSGKEY, MsgToConsole());
ENABLE_CONTROL(IDC_MSGVOICE, MsgToConsole());
ENABLE_CONTROL(IDC_MSGDMA, MsgToConsole());
ENABLE_CONTROL(IDC_MSGADMA, MsgToConsole());
ENABLE_CONTROL(IDC_DBG_OVERRUNS, MsgToConsole());
ENABLE_CONTROL(IDC_DBG_CACHE, MsgToConsole());
}
void EnableControls(HWND hWnd)
{
EnableMessages(hWnd);
ENABLE_CONTROL(IDC_LOGDMA, DebugEnabled);
ENABLE_CONTROL(IDC_LOGREGS, IsDevBuild ? DebugEnabled : false);
ENABLE_CONTROL(IDC_LOGWAVE, IsDevBuild ? DebugEnabled : false);
ENABLE_CONTROL(IDC_DUMPCORE, DebugEnabled);
ENABLE_CONTROL(IDC_DUMPMEM, DebugEnabled);
ENABLE_CONTROL(IDC_DUMPREGS, DebugEnabled);
ENABLE_CONTROL(IDC_DEBUG_VISUAL, IsDevBuild ? DebugEnabled : false);
}
static BOOL CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int wmId;
//wchar_t temp[384]={0};
switch (uMsg) {
case WM_PAINT:
return FALSE;
case WM_INITDIALOG: {
EnableControls(hWnd);
// Debugging / Logging Flags:
SET_CHECK(IDC_DEBUG, DebugEnabled);
SET_CHECK(IDC_MSGSHOW, _MsgToConsole);
SET_CHECK(IDC_MSGKEY, _MsgKeyOnOff);
SET_CHECK(IDC_MSGVOICE, _MsgVoiceOff);
SET_CHECK(IDC_MSGDMA, _MsgDMA);
SET_CHECK(IDC_MSGADMA, _MsgAutoDMA);
SET_CHECK(IDC_DBG_OVERRUNS, _MsgOverruns);
SET_CHECK(IDC_DBG_CACHE, _MsgCache);
SET_CHECK(IDC_LOGREGS, _AccessLog);
SET_CHECK(IDC_LOGDMA, _DMALog);
SET_CHECK(IDC_LOGWAVE, _WaveLog);
SET_CHECK(IDC_DUMPCORE, _CoresDump);
SET_CHECK(IDC_DUMPMEM, _MemDump);
SET_CHECK(IDC_DUMPREGS, _RegDump);
SET_CHECK(IDC_DEBUG_VISUAL, _visual_debug_enabled);
ShowWindow(GetDlgItem(hWnd, IDC_MSG_PUBLIC_BUILD), !IsDevBuild);
} break;
case WM_COMMAND:
wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId) {
case IDOK:
WriteSettings();
EndDialog(hWnd, 0);
break;
case IDCANCEL:
EndDialog(hWnd, 0);
break;
HANDLE_CHECKNB(IDC_MSGSHOW, _MsgToConsole);
EnableMessages(hWnd);
break;
HANDLE_CHECK(IDC_MSGKEY, _MsgKeyOnOff);
HANDLE_CHECK(IDC_MSGVOICE, _MsgVoiceOff);
HANDLE_CHECK(IDC_MSGDMA, _MsgDMA);
HANDLE_CHECK(IDC_MSGADMA, _MsgAutoDMA);
break;
HANDLE_CHECK(IDC_DBG_OVERRUNS, _MsgOverruns);
HANDLE_CHECK(IDC_DBG_CACHE, _MsgCache);
HANDLE_CHECK(IDC_LOGREGS, _AccessLog);
HANDLE_CHECK(IDC_LOGDMA, _DMALog);
HANDLE_CHECK(IDC_LOGWAVE, _WaveLog);
HANDLE_CHECK(IDC_DUMPCORE, _CoresDump);
HANDLE_CHECK(IDC_DUMPMEM, _MemDump);
HANDLE_CHECK(IDC_DUMPREGS, _RegDump);
HANDLE_CHECK(IDC_DEBUG_VISUAL, _visual_debug_enabled);
default:
return FALSE;
}
break;
default:
return FALSE;
}
return TRUE;
}
void OpenDialog()
{
INT_PTR ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CONFIG_DEBUG), GetActiveWindow(), (DLGPROC)DialogProc, 1);
if (ret == -1) {
MessageBox(GetActiveWindow(), L"Error Opening the debug configuration dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
return;
}
ReadSettings();
}
}

View File

@ -0,0 +1,131 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "Dialogs.h"
#include "soundtouch/SoundTouch.h"
static int SequenceLenMS = 30;
static int SeekWindowMS = 20;
static int OverlapMS = 10;
// Timestretch Slider Bounds, Min/Max
static const int SequenceLen_Min = 20;
static const int SequenceLen_Max = 100;
static const int SeekWindow_Min = 10;
static const int SeekWindow_Max = 30;
static const int Overlap_Min = 5;
static const int Overlap_Max = 15;
void SoundtouchCfg::ApplySettings(soundtouch::SoundTouch &sndtouch)
{
sndtouch.setSetting(SETTING_SEQUENCE_MS, SequenceLenMS);
sndtouch.setSetting(SETTING_SEEKWINDOW_MS, SeekWindowMS);
sndtouch.setSetting(SETTING_OVERLAP_MS, OverlapMS);
}
static void ClampValues()
{
Clampify(SequenceLenMS, SequenceLen_Min, SequenceLen_Max);
Clampify(SeekWindowMS, SeekWindow_Min, SeekWindow_Max);
Clampify(OverlapMS, Overlap_Min, Overlap_Max);
}
void SoundtouchCfg::ReadSettings()
{
SequenceLenMS = CfgReadInt(L"SOUNDTOUCH", L"SequenceLengthMS", 30);
SeekWindowMS = CfgReadInt(L"SOUNDTOUCH", L"SeekWindowMS", 20);
OverlapMS = CfgReadInt(L"SOUNDTOUCH", L"OverlapMS", 10);
ClampValues();
}
void SoundtouchCfg::WriteSettings()
{
CfgWriteInt(L"SOUNDTOUCH", L"SequenceLengthMS", SequenceLenMS);
CfgWriteInt(L"SOUNDTOUCH", L"SeekWindowMS", SeekWindowMS);
CfgWriteInt(L"SOUNDTOUCH", L"OverlapMS", OverlapMS);
}
BOOL CALLBACK SoundtouchCfg::DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int wmId;
//wchar_t temp[384]={0};
switch (uMsg) {
case WM_PAINT:
return FALSE;
case WM_INITDIALOG: {
INIT_SLIDER(IDC_SEQLEN_SLIDER, SequenceLen_Min, SequenceLen_Max, 20, 5, 1);
INIT_SLIDER(IDC_SEEKWIN_SLIDER, SeekWindow_Min, SeekWindow_Max, 5, 2, 1);
INIT_SLIDER(IDC_OVERLAP_SLIDER, Overlap_Min, Overlap_Max, 3, 2, 1);
SendDialogMsg(hWnd, IDC_SEQLEN_SLIDER, TBM_SETPOS, TRUE, SequenceLenMS);
SendDialogMsg(hWnd, IDC_SEEKWIN_SLIDER, TBM_SETPOS, TRUE, SeekWindowMS);
SendDialogMsg(hWnd, IDC_OVERLAP_SLIDER, TBM_SETPOS, TRUE, OverlapMS);
}
case WM_COMMAND:
wmId = LOWORD(wParam);
// Parse the menu selections:
if (wmId == IDOK) {
SequenceLenMS = (int)SendDialogMsg(hWnd, IDC_SEQLEN_SLIDER, TBM_GETPOS, 0, 0);
SeekWindowMS = (int)SendDialogMsg(hWnd, IDC_SEEKWIN_SLIDER, TBM_GETPOS, 0, 0);
OverlapMS = (int)SendDialogMsg(hWnd, IDC_OVERLAP_SLIDER, TBM_GETPOS, 0, 0);
ClampValues();
WriteSettings();
EndDialog(hWnd, 0);
} else if (wmId == IDC_RESET_DEFAULTS) {
SequenceLenMS = 30;
SeekWindowMS = 20;
OverlapMS = 10;
SendDialogMsg(hWnd, IDC_SEQLEN_SLIDER, TBM_SETPOS, TRUE, SequenceLenMS);
SendDialogMsg(hWnd, IDC_SEEKWIN_SLIDER, TBM_SETPOS, TRUE, SeekWindowMS);
SendDialogMsg(hWnd, IDC_OVERLAP_SLIDER, TBM_SETPOS, TRUE, OverlapMS);
AssignSliderValue((HWND)lParam, hWnd, SequenceLenMS);
} else if (wmId == IDCANCEL) {
EndDialog(hWnd, 0);
}
break;
case WM_HSCROLL:
DoHandleScrollMessage(hWnd, wParam, lParam);
break;
default:
return FALSE;
}
return TRUE;
}
void SoundtouchCfg::OpenDialog(HWND hWnd)
{
INT_PTR ret;
ret = DialogBox(hInstance, MAKEINTRESOURCE(IDD_CONFIG_SOUNDTOUCH), hWnd, (DLGPROC)DialogProc);
if (ret == -1) {
MessageBox(GetActiveWindow(), L"Error Opening the Soundtouch advanced dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
return;
}
ReadSettings();
}

View File

@ -0,0 +1,76 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef _WIN32
#include "WinConfig.h"
#else
#include "LnxConfig.h"
#endif
namespace DebugConfig
{
extern void ReadSettings();
extern void WriteSettings();
extern void OpenDialog();
extern void EnableControls(HWND hWnd);
}
namespace SoundtouchCfg
{
extern void ReadSettings();
extern void WriteSettings();
extern void OpenDialog(HWND hWnd);
extern BOOL CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
}
extern int SendDialogMsg(HWND hwnd, int dlgId, UINT code, WPARAM wParam, LPARAM lParam);
extern void AssignSliderValue(HWND idcwnd, HWND hwndDisplay, int value);
extern void AssignSliderValue(HWND hWnd, int idc, int editbox, int value);
extern int GetSliderValue(HWND hWnd, int idc);
extern BOOL DoHandleScrollMessage(HWND hwndDisplay, WPARAM wParam, LPARAM lParam);
extern void CfgSetSettingsDir(const char *dir);
extern void CfgSetLogDir(const char *dir);
extern bool CfgFindName(const TCHAR *Section, const TCHAR *Name);
extern void CfgWriteBool(const TCHAR *Section, const TCHAR *Name, bool Value);
extern void CfgWriteInt(const TCHAR *Section, const TCHAR *Name, int Value);
extern void CfgWriteFloat(const TCHAR *Section, const TCHAR *Name, float Value);
extern void CfgWriteStr(const TCHAR *Section, const TCHAR *Name, const wxString &Data);
extern bool CfgReadBool(const TCHAR *Section, const TCHAR *Name, bool Default);
extern void CfgReadStr(const TCHAR *Section, const TCHAR *Name, wxString &Data, const TCHAR *Default);
extern void CfgReadStr(const TCHAR *Section, const TCHAR *Name, TCHAR *Data, int DataSize, const TCHAR *Default);
extern int CfgReadInt(const TCHAR *Section, const TCHAR *Name, int Default);
extern float CfgReadFloat(const TCHAR *Section, const TCHAR *Name, float Default);
// Items Specific to DirectSound
#define STRFY(x) #x
#define verifyc(x) Verifyc(x, STRFY(x))
extern void Verifyc(HRESULT hr, const char *fn);
struct ds_device_data
{
wxString name;
GUID guid;
bool hasGuid;
};

View File

@ -0,0 +1,269 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "Dialogs.h"
bool debugDialogOpen = false;
HWND hDebugDialog = NULL;
#ifdef PCSX2_DEVBUILD
int FillRectangle(HDC dc, int left, int top, int width, int height)
{
RECT r = {left, top, left + width, top + height};
return FillRect(dc, &r, (HBRUSH)GetStockObject(DC_BRUSH));
}
BOOL DrawRectangle(HDC dc, int left, int top, int width, int height)
{
RECT r = {left, top, left + width, top + height};
POINT p[5] = {
{r.left, r.top},
{r.right, r.top},
{r.right, r.bottom},
{r.left, r.bottom},
{r.left, r.top},
};
return Polyline(dc, p, 5);
}
HFONT hf = NULL;
int lCount = 0;
void UpdateDebugDialog()
{
if (!debugDialogOpen)
return;
lCount++;
if (lCount >= (SampleRate / 100)) // Increase to SampleRate/200 for smooth display.
{
HDC hdc = GetDC(hDebugDialog);
if (!hf) {
hf = CreateFont(12, 0, 0, 0, 0, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Lucida Console");
}
SelectObject(hdc, hf);
SelectObject(hdc, GetStockObject(DC_BRUSH));
SelectObject(hdc, GetStockObject(DC_PEN));
for (int c = 0; c < 2; c++) {
V_Core &cx(Cores[c]);
V_CoreDebug &cd(DebugCores[c]);
for (int v = 0; v < 24; v++) {
int cc = c * 2 + (v / 12);
int vv = v % 12;
int IX = 8 + 128 * cc;
int IY = 8 + 48 * vv;
V_Voice &vc(cx.Voices[v]);
V_VoiceDebug &vcd(cd.Voices[v]);
SetDCBrushColor(hdc, RGB(0, 0, 0));
if ((vc.ADSR.Phase > 0) && (vc.ADSR.Phase < 6)) {
SetDCBrushColor(hdc, RGB(0, 0, 128)); // light blue for playing voice
if (vc.Modulated) {
SetDCBrushColor(hdc, RGB(0, 128, 0)); // light green for playing voice with modulation enabled
}
if (vc.Noise) {
SetDCBrushColor(hdc, RGB(128, 0, 0)); // light red for playing voice with noise enabled
}
}
/*
else
{
if(vcd.lastStopReason==1)
{
SetDCBrushColor(hdc,RGB(128, 0, 0));
}
if(vcd.lastStopReason==2)
{
SetDCBrushColor(hdc,RGB( 0,128, 0));
}
}*/
FillRectangle(hdc, IX, IY, 124, 46);
SetDCPenColor(hdc, RGB(255, 128, 32));
DrawRectangle(hdc, IX, IY, 124, 46);
SetDCBrushColor(hdc, RGB(0, 255, 0));
int vl = abs(((vc.Volume.Left.Value >> 16) * 38) >> 15);
int vr = abs(((vc.Volume.Right.Value >> 16) * 38) >> 15);
FillRectangle(hdc, IX + 58, IY + 42 - vl, 4, vl);
FillRectangle(hdc, IX + 62, IY + 42 - vr, 4, vr);
int adsr = ((vc.ADSR.Value >> 16) * 38) / 32768;
FillRectangle(hdc, IX + 66, IY + 42 - adsr, 4, adsr);
int peak = (vcd.displayPeak * 38) / 32768;
if (vcd.displayPeak >= 32700) // leave a little bit of margin
{
SetDCBrushColor(hdc, RGB(255, 0, 0));
}
FillRectangle(hdc, IX + 70, IY + 42 - peak, 4, peak);
if (vc.ADSR.Value > 0) {
if (vc.SBuffer)
for (int i = 0; i < 28; i++) {
int val = ((int)vc.SBuffer[i] * 20) / 32768;
int y = 0;
if (val > 0) {
y = val;
} else
val = -val;
if (val != 0) {
FillRectangle(hdc, IX + 90 + i, IY + 24 - y, 1, val);
}
}
}
SetTextColor(hdc, RGB(0, 255, 0));
SetBkColor(hdc, RGB(0, 0, 0));
static wchar_t t[256];
swprintf_s(t, L"%06x", vc.StartA);
TextOut(hdc, IX + 4, IY + 4, t, 6);
swprintf_s(t, L"%06x", vc.NextA);
TextOut(hdc, IX + 4, IY + 18, t, 6);
swprintf_s(t, L"%06x", vc.LoopStartA);
TextOut(hdc, IX + 4, IY + 32, t, 6);
vcd.displayPeak = 0;
}
// top now: 400
int JX = 8 + c * 256;
int JY = 584;
SetDCBrushColor(hdc, RGB(0, 0, 0));
SetDCPenColor(hdc, RGB(255, 128, 32));
FillRectangle(hdc, JX, JY, 252, 60);
DrawRectangle(hdc, JX, JY, 252, 60);
SetTextColor(hdc, RGB(255, 255, 255));
SetBkColor(hdc, RGB(0, 0, 0));
static wchar_t t[256];
TextOut(hdc, JX + 4, JY + 4, L"REVB", 4);
TextOut(hdc, JX + 4, JY + 18, L"IRQE", 4);
TextOut(hdc, JX + 4, JY + 32, L"ADMA", 4);
swprintf_s(t, L"DMA%s", c == 0 ? L"4" : L"7");
TextOut(hdc, JX + 4, JY + 46, t, 4);
SetTextColor(hdc, RGB(0, 255, 0));
memset(t, 0, sizeof(t));
swprintf_s(t, L"ESA %x", cx.EffectsStartA);
TextOut(hdc, JX + 56, JY + 4, t, 9);
memset(t, 0, sizeof(t));
swprintf_s(t, L"EEA %x", cx.EffectsEndA);
TextOut(hdc, JX + 128, JY + 4, t, 9);
memset(t, 0, sizeof(t));
swprintf_s(t, L"(%x)", cx.EffectsBufferSize);
TextOut(hdc, JX + 200, JY + 4, t, 7);
memset(t, 0, sizeof(t));
swprintf_s(t, L"IRQA %x", cx.IRQA);
TextOut(hdc, JX + 56, JY + 18, t, 12);
SetTextColor(hdc, RGB(255, 255, 255));
SetDCBrushColor(hdc, RGB(255, 0, 0));
if (cx.FxEnable) {
FillRectangle(hdc, JX + 40, JY + 4, 10, 10);
}
if (cx.IRQEnable) {
FillRectangle(hdc, JX + 40, JY + 18, 10, 10);
}
if (cx.AutoDMACtrl != 0) {
FillRectangle(hdc, JX + 40, JY + 32, 10, 10);
for (int j = 0; j < 64; j++) {
int i = j * 256 / 64;
int val = (cd.admaWaveformL[i] * 26) / 32768;
int y = 0;
if (val > 0)
y = val;
else
val = -val;
if (val != 0) {
FillRectangle(hdc, JX + 60 + j, JY + 30 - y, 1, val);
}
}
for (int j = 0; j < 64; j++) {
int i = j * 256 / 64;
int val = (cd.admaWaveformR[i] * 26) / 32768;
int y = 0;
if (val > 0)
y = val;
else
val = -val;
if (val != 0) {
FillRectangle(hdc, JX + 136 + j, JY + 30 - y, 1, val);
}
}
}
if (cd.dmaFlag > 0) // So it shows x times this is called, since dmas are so fast
{
swprintf_s(t, L"size = %d", cd.lastsize);
TextOut(hdc, JX + 64, JY + 46, t, wcslen(t));
FillRectangle(hdc, JX + 40, JY + 46, 10, 10);
cd.dmaFlag--;
}
}
ReleaseDC(hDebugDialog, hdc);
lCount = 0;
}
MSG msg;
while (PeekMessage(&msg, hDebugDialog, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
#else
void UpdateDebugDialog()
{
// Release mode. Nothing to do
}
#endif

View File

@ -0,0 +1,457 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#define _WIN32_DCOM
#include "Dialogs.h"
#define DIRECTSOUND_VERSION 0x1000
#include <dsound.h>
class DSound : public SndOutModule
{
private:
static const uint MAX_BUFFER_COUNT = 8;
static const int PacketsPerBuffer = 8;
static const int BufferSize = SndOutPacketSize * PacketsPerBuffer;
//////////////////////////////////////////////////////////////////////////////////////////
// Configuration Vars
wxString m_Device;
u8 m_NumBuffers;
bool m_DisableGlobalFocus;
bool m_UseHardware;
ds_device_data m_devices[32];
int ndevs;
GUID DevGuid; // currently employed GUID.
bool haveGuid;
//////////////////////////////////////////////////////////////////////////////////////////
// Instance vars
int channel;
int myLastWrite; // last write position, in bytes
bool dsound_running;
HANDLE thread;
DWORD tid;
IDirectSound8 *dsound;
IDirectSoundBuffer8 *buffer;
IDirectSoundNotify8 *buffer_notify;
HANDLE buffer_events[MAX_BUFFER_COUNT];
WAVEFORMATEX wfx;
HANDLE waitEvent;
template <typename T>
static DWORD CALLBACK RThread(DSound *obj)
{
return obj->Thread<T>();
}
template <typename T>
DWORD CALLBACK Thread()
{
static const int BufferSizeBytes = BufferSize * sizeof(T);
while (dsound_running) {
u32 rv = WaitForMultipleObjects(m_NumBuffers, buffer_events, FALSE, 200);
T *p1, *oldp1;
LPVOID p2;
DWORD s1, s2;
u32 poffset = BufferSizeBytes * rv;
if (FAILED(buffer->Lock(poffset, BufferSizeBytes, (LPVOID *)&p1, &s1, &p2, &s2, 0))) {
assert(0);
fputs("* SPU2-X: Directsound Warning > Buffer lock failure. You may need to increase\n\tyour configured DSound buffer count.\n", stderr);
continue;
}
oldp1 = p1;
for (int p = 0; p < PacketsPerBuffer; p++, p1 += SndOutPacketSize)
SndBuffer::ReadSamples(p1);
buffer->Unlock(oldp1, s1, p2, s2);
// Set the write pointer to the beginning of the next block.
myLastWrite = (poffset + BufferSizeBytes) & ~BufferSizeBytes;
}
return 0;
}
public:
s32 Init()
{
CoInitializeEx(NULL, COINIT_MULTITHREADED);
//
// Initialize DSound
//
GUID cGuid;
try {
if (m_Device.empty())
throw std::runtime_error("screw it");
if ((FAILED(IIDFromString(m_Device, &cGuid))) ||
FAILED(DirectSoundCreate8(&cGuid, &dsound, NULL)))
throw std::runtime_error("try again?");
} catch (std::runtime_error &) {
// if the GUID failed, just open up the default dsound driver:
if (FAILED(DirectSoundCreate8(NULL, &dsound, NULL)))
throw std::runtime_error("DirectSound failed to initialize!");
}
if (FAILED(dsound->SetCooperativeLevel(GetDesktopWindow(), DSSCL_PRIORITY)))
throw std::runtime_error("DirectSound Error: Cooperative level could not be set.");
// Determine the user's speaker configuration, and select an expansion option as needed.
// FAIL : Directsound doesn't appear to support audio expansion >_<
DWORD speakerConfig = 2;
//dsound->GetSpeakerConfig( &speakerConfig );
IDirectSoundBuffer *buffer_;
DSBUFFERDESC desc;
// Set up WAV format structure.
memset(&wfx, 0, sizeof(WAVEFORMATEX));
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nSamplesPerSec = SampleRate;
wfx.nChannels = (WORD)speakerConfig;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = 2 * (WORD)speakerConfig;
wfx.nAvgBytesPerSec = SampleRate * wfx.nBlockAlign;
wfx.cbSize = 0;
uint BufferSizeBytes = BufferSize * wfx.nBlockAlign;
// Set up DSBUFFERDESC structure.
memset(&desc, 0, sizeof(DSBUFFERDESC));
desc.dwSize = sizeof(DSBUFFERDESC);
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY;
desc.dwBufferBytes = BufferSizeBytes * m_NumBuffers;
desc.lpwfxFormat = &wfx;
// Try a hardware buffer first, and then fall back on a software buffer if
// that one fails.
desc.dwFlags |= m_UseHardware ? DSBCAPS_LOCHARDWARE : DSBCAPS_LOCSOFTWARE;
desc.dwFlags |= m_DisableGlobalFocus ? DSBCAPS_STICKYFOCUS : DSBCAPS_GLOBALFOCUS;
if (FAILED(dsound->CreateSoundBuffer(&desc, &buffer_, 0))) {
if (m_UseHardware) {
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_LOCSOFTWARE;
desc.dwFlags |= m_DisableGlobalFocus ? DSBCAPS_STICKYFOCUS : DSBCAPS_GLOBALFOCUS;
if (FAILED(dsound->CreateSoundBuffer(&desc, &buffer_, 0)))
throw std::runtime_error("DirectSound Error: Buffer could not be created.");
}
throw std::runtime_error("DirectSound Error: Buffer could not be created.");
}
if (FAILED(buffer_->QueryInterface(IID_IDirectSoundBuffer8, (void **)&buffer)) || buffer == NULL)
throw std::runtime_error("DirectSound Error: Interface could not be queried.");
buffer_->Release();
verifyc(buffer->QueryInterface(IID_IDirectSoundNotify8, (void **)&buffer_notify));
DSBPOSITIONNOTIFY not[MAX_BUFFER_COUNT];
for (uint i = 0; i < m_NumBuffers; i++) {
buffer_events[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
not[i].dwOffset = (wfx.nBlockAlign + BufferSizeBytes * (i + 1)) % desc.dwBufferBytes;
not[i].hEventNotify = buffer_events[i];
}
buffer_notify->SetNotificationPositions(m_NumBuffers, not);
LPVOID p1 = 0, p2 = 0;
DWORD s1 = 0, s2 = 0;
verifyc(buffer->Lock(0, desc.dwBufferBytes, &p1, &s1, &p2, &s2, 0));
assert(p2 == 0);
memset(p1, 0, s1);
verifyc(buffer->Unlock(p1, s1, p2, s2));
//Play the buffer !
verifyc(buffer->Play(0, 0, DSBPLAY_LOOPING));
// Start Thread
myLastWrite = 0;
dsound_running = true;
thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RThread<StereoOut16>, this, 0, &tid);
SetThreadPriority(thread, THREAD_PRIORITY_ABOVE_NORMAL);
return 0;
}
void Close()
{
// Stop Thread
fprintf(stderr, "* SPU2-X: Waiting for DSound thread to finish...");
dsound_running = false;
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
fprintf(stderr, " Done.\n");
//
// Clean up
//
if (buffer != NULL) {
buffer->Stop();
for (u32 i = 0; i < m_NumBuffers; i++) {
if (buffer_events[i] != NULL)
CloseHandle(buffer_events[i]);
buffer_events[i] = NULL;
}
safe_release(buffer_notify);
safe_release(buffer);
}
safe_release(dsound);
CoUninitialize();
}
private:
bool _DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext)
{
m_devices[ndevs].name = lpcstrDescription;
if (lpGuid) {
m_devices[ndevs].guid = *lpGuid;
m_devices[ndevs].hasGuid = true;
} else {
m_devices[ndevs].hasGuid = false;
}
ndevs++;
if (ndevs < 32)
return TRUE;
return FALSE;
}
bool _ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
int tSel = 0;
switch (uMsg) {
case WM_INITDIALOG: {
wchar_t temp[128];
haveGuid = !FAILED(IIDFromString(m_Device, &DevGuid));
SendMessage(GetDlgItem(hWnd, IDC_DS_DEVICE), CB_RESETCONTENT, 0, 0);
ndevs = 0;
DirectSoundEnumerate(DSEnumCallback, NULL);
tSel = -1;
for (int i = 0; i < ndevs; i++) {
SendMessage(GetDlgItem(hWnd, IDC_DS_DEVICE), CB_ADDSTRING, 0, (LPARAM)m_devices[i].name.wc_str());
if (haveGuid && IsEqualGUID(m_devices[i].guid, DevGuid) || tSel < 0 && !m_devices[i].hasGuid)
tSel = i;
}
if (tSel >= 0)
SendMessage(GetDlgItem(hWnd, IDC_DS_DEVICE), CB_SETCURSEL, tSel, 0);
INIT_SLIDER(IDC_BUFFERS_SLIDER, 2, MAX_BUFFER_COUNT, 2, 1, 1);
SendMessage(GetDlgItem(hWnd, IDC_BUFFERS_SLIDER), TBM_SETPOS, TRUE, m_NumBuffers);
swprintf_s(temp, L"%d (%d ms latency)", m_NumBuffers, 1000 / (96000 / (m_NumBuffers * BufferSize)));
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
SET_CHECK(IDC_GLOBALFOCUS_DISABLE, m_DisableGlobalFocus);
SET_CHECK(IDC_USE_HARDWARE, m_UseHardware);
} break;
case WM_COMMAND: {
wchar_t temp[128];
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId) {
case IDOK: {
int i = (int)SendMessage(GetDlgItem(hWnd, IDC_DS_DEVICE), CB_GETCURSEL, 0, 0);
if (!m_devices[i].hasGuid) {
m_Device[0] = 0; // clear device name to ""
} else {
swprintf_s(temp, L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
m_devices[i].guid.Data1,
m_devices[i].guid.Data2,
m_devices[i].guid.Data3,
m_devices[i].guid.Data4[0],
m_devices[i].guid.Data4[1],
m_devices[i].guid.Data4[2],
m_devices[i].guid.Data4[3],
m_devices[i].guid.Data4[4],
m_devices[i].guid.Data4[5],
m_devices[i].guid.Data4[6],
m_devices[i].guid.Data4[7]);
m_Device = temp;
}
m_NumBuffers = (int)SendMessage(GetDlgItem(hWnd, IDC_BUFFERS_SLIDER), TBM_GETPOS, 0, 0);
if (m_NumBuffers < 2)
m_NumBuffers = 2;
if (m_NumBuffers > MAX_BUFFER_COUNT)
m_NumBuffers = MAX_BUFFER_COUNT;
EndDialog(hWnd, 0);
} break;
case IDCANCEL:
EndDialog(hWnd, 0);
break;
HANDLE_CHECK(IDC_GLOBALFOCUS_DISABLE, m_DisableGlobalFocus);
HANDLE_CHECK(IDC_USE_HARDWARE, m_UseHardware);
default:
return FALSE;
}
} break;
case WM_HSCROLL: {
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId) {
case TB_LINEUP:
case TB_LINEDOWN:
case TB_PAGEUP:
case TB_PAGEDOWN:
case TB_TOP:
case TB_BOTTOM:
wmEvent = (int)SendMessage((HWND)lParam, TBM_GETPOS, 0, 0);
case TB_THUMBPOSITION:
case TB_THUMBTRACK: {
wchar_t temp[128];
if (wmEvent < 2)
wmEvent = 2;
if (wmEvent > MAX_BUFFER_COUNT)
wmEvent = MAX_BUFFER_COUNT;
SendMessage((HWND)lParam, TBM_SETPOS, TRUE, wmEvent);
swprintf_s(temp, L"%d (%d ms latency)", wmEvent, 1000 / (96000 / (wmEvent * BufferSize)));
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
break;
}
default:
return FALSE;
}
} break;
default:
return FALSE;
}
return TRUE;
}
static BOOL CALLBACK ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static BOOL CALLBACK DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext);
public:
virtual void Configure(uptr parent)
{
INT_PTR ret;
ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_DSOUND), (HWND)parent, (DLGPROC)ConfigProc, 1);
if (ret == -1) {
MessageBox((HWND)parent, L"Error Opening the config dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
return;
}
}
s32 Test() const
{
return 0;
}
int GetEmptySampleCount()
{
DWORD play, write;
buffer->GetCurrentPosition(&play, &write);
// Note: Dsound's write cursor is bogus. Use our own instead:
int empty = play - myLastWrite;
if (empty < 0)
empty = -empty;
return empty / 2;
}
const wchar_t *GetIdent() const
{
return L"dsound";
}
const wchar_t *GetLongName() const
{
return L"DirectSound (Nice)";
}
void ReadSettings()
{
CfgReadStr(L"DSOUNDOUT", L"Device", m_Device, L"default");
m_NumBuffers = CfgReadInt(L"DSOUNDOUT", L"Buffer_Count", 5);
m_DisableGlobalFocus = CfgReadBool(L"DSOUNDOUT", L"Disable_Global_Focus", false);
m_UseHardware = CfgReadBool(L"DSOUNDOUT", L"Use_Hardware", false);
Clampify(m_NumBuffers, (u8)3, (u8)8);
}
void SetApiSettings(wxString api)
{
}
void WriteSettings() const
{
CfgWriteStr(L"DSOUNDOUT", L"Device", m_Device.empty() ? L"default" : m_Device);
CfgWriteInt(L"DSOUNDOUT", L"Buffer_Count", m_NumBuffers);
CfgWriteBool(L"DSOUNDOUT", L"Disable_Global_Focus", m_DisableGlobalFocus);
CfgWriteBool(L"DSOUNDOUT", L"Use_Hardware", m_UseHardware);
}
} static DS;
BOOL CALLBACK DSound::ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DS._ConfigProc(hWnd, uMsg, wParam, lParam);
}
BOOL CALLBACK DSound::DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext)
{
pxAssume(DSoundOut != NULL);
return DS._DSEnumCallback(lpGuid, lpcstrDescription, lpcstrModule, lpContext);
}
SndOutModule *DSoundOut = &DS;

View File

@ -0,0 +1,423 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "Dialogs.h"
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0602
#include <xaudio2.h>
#include <atlcomcli.h>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
namespace Exception
{
class XAudio2Error : public std::runtime_error
{
private:
static std::string CreateErrorMessage(const HRESULT result, const std::string &msg)
{
std::stringstream ss;
ss << " (code 0x" << std::hex << result << ")\n\n";
switch (result) {
case XAUDIO2_E_INVALID_CALL:
ss << "Invalid call for the XA2 object state.";
break;
case XAUDIO2_E_DEVICE_INVALIDATED:
ss << "Device is unavailable, unplugged, unsupported, or has been consumed by The Nothing.";
break;
default:
ss << "Unknown error code!";
break;
}
return msg + ss.str();
}
public:
explicit XAudio2Error(const HRESULT result, const std::string &msg)
: std::runtime_error(CreateErrorMessage(result, msg))
{
}
};
}
static const double SndOutNormalizer = (double)(1UL << (SndOutVolumeShift + 16));
class XAudio2Mod : public SndOutModule
{
private:
static const int PacketsPerBuffer = 8;
static const int MAX_BUFFER_COUNT = 3;
class BaseStreamingVoice : public IXAudio2VoiceCallback
{
protected:
IXAudio2SourceVoice *pSourceVoice;
std::unique_ptr<s16[]> m_buffer;
const uint m_nBuffers;
const uint m_nChannels;
const uint m_BufferSize;
const uint m_BufferSizeBytes;
CRITICAL_SECTION cs;
public:
int GetEmptySampleCount()
{
XAUDIO2_VOICE_STATE state;
pSourceVoice->GetState(&state);
return state.SamplesPlayed & (m_BufferSize - 1);
}
virtual ~BaseStreamingVoice()
{
DeleteCriticalSection(&cs);
}
BaseStreamingVoice(uint numChannels)
: pSourceVoice(nullptr)
, m_nBuffers(Config_XAudio2.NumBuffers)
, m_nChannels(numChannels)
, m_BufferSize(SndOutPacketSize * m_nChannels * PacketsPerBuffer)
, m_BufferSizeBytes(m_BufferSize * sizeof(s16))
{
InitializeCriticalSection(&cs);
}
virtual void Init(IXAudio2 *pXAudio2) = 0;
protected:
// Several things must be initialized separate of the constructor, due to the fact that
// virtual calls can't be made from the constructor's context.
void _init(IXAudio2 *pXAudio2, uint chanConfig)
{
WAVEFORMATEXTENSIBLE wfx;
memset(&wfx, 0, sizeof(WAVEFORMATEXTENSIBLE));
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wfx.Format.nSamplesPerSec = SampleRate;
wfx.Format.nChannels = m_nChannels;
wfx.Format.wBitsPerSample = 16;
wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8;
wfx.Format.nAvgBytesPerSec = SampleRate * wfx.Format.nBlockAlign;
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
wfx.Samples.wValidBitsPerSample = 16;
wfx.dwChannelMask = chanConfig;
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
HRESULT hr;
if (FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX *)&wfx,
XAUDIO2_VOICE_NOSRC, 1.0f, this))) {
throw Exception::XAudio2Error(hr, "XAudio2 CreateSourceVoice failure: ");
}
EnterCriticalSection(&cs);
pSourceVoice->FlushSourceBuffers();
pSourceVoice->Start(0, 0);
m_buffer = std::make_unique<s16[]>(m_nBuffers * m_BufferSize);
// Start some buffers.
for (uint i = 0; i < m_nBuffers; i++) {
XAUDIO2_BUFFER buf = {0};
buf.AudioBytes = m_BufferSizeBytes;
buf.pContext = &m_buffer[i * m_BufferSize];
buf.pAudioData = (BYTE *)buf.pContext;
pSourceVoice->SubmitSourceBuffer(&buf);
}
LeaveCriticalSection(&cs);
}
STDMETHOD_(void, OnVoiceProcessingPassStart)
() {}
STDMETHOD_(void, OnVoiceProcessingPassStart)
(UINT32) {}
STDMETHOD_(void, OnVoiceProcessingPassEnd)
() {}
STDMETHOD_(void, OnStreamEnd)
() {}
STDMETHOD_(void, OnBufferStart)
(void *) {}
STDMETHOD_(void, OnLoopEnd)
(void *) {}
STDMETHOD_(void, OnVoiceError)
(THIS_ void *pBufferContext, HRESULT Error) {}
};
template <typename T>
class StreamingVoice : public BaseStreamingVoice
{
public:
StreamingVoice()
: BaseStreamingVoice(sizeof(T) / sizeof(s16))
{
}
virtual ~StreamingVoice()
{
IXAudio2SourceVoice *killMe = pSourceVoice;
// XXX: Potentially leads to a race condition that causes a nullptr
// dereference when SubmitSourceBuffer is called in OnBufferEnd?
pSourceVoice = nullptr;
if (killMe != nullptr) {
killMe->FlushSourceBuffers();
killMe->DestroyVoice();
}
// XXX: Not sure we even need a critical section - DestroyVoice is
// blocking, and the documentation states no callbacks are called
// or audio data is read after it returns, so it's safe to free up
// resources.
EnterCriticalSection(&cs);
m_buffer = nullptr;
LeaveCriticalSection(&cs);
}
void Init(IXAudio2 *pXAudio2)
{
int chanMask = 0;
switch (m_nChannels) {
case 1:
chanMask |= SPEAKER_FRONT_CENTER;
break;
case 2:
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
break;
case 3:
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY;
break;
case 4:
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
break;
case 5:
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
break;
case 6:
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY;
break;
case 8:
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT | SPEAKER_LOW_FREQUENCY;
break;
}
_init(pXAudio2, chanMask);
}
protected:
STDMETHOD_(void, OnBufferEnd)
(void *context)
{
EnterCriticalSection(&cs);
// All of these checks are necessary because XAudio2 is wonky shizat.
// XXX: The pSourceVoice nullptr check seems a bit self-inflicted
// due to the destructor logic.
if (pSourceVoice == nullptr || context == nullptr) {
LeaveCriticalSection(&cs);
return;
}
T *qb = (T *)context;
for (int p = 0; p < PacketsPerBuffer; p++, qb += SndOutPacketSize)
SndBuffer::ReadSamples(qb);
XAUDIO2_BUFFER buf = {0};
buf.AudioBytes = m_BufferSizeBytes;
buf.pAudioData = (BYTE *)context;
buf.pContext = context;
pSourceVoice->SubmitSourceBuffer(&buf);
LeaveCriticalSection(&cs);
}
};
HMODULE xAudio2DLL = nullptr;
decltype(&XAudio2Create) pXAudio2Create = nullptr;
CComPtr<IXAudio2> pXAudio2;
IXAudio2MasteringVoice *pMasteringVoice = nullptr;
std::unique_ptr<BaseStreamingVoice> m_voiceContext;
public:
s32 Init()
{
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
try {
HRESULT hr;
xAudio2DLL = LoadLibraryEx(XAUDIO2_DLL, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (xAudio2DLL == nullptr)
throw std::runtime_error("Could not load " XAUDIO2_DLL_A ". Error code:" + std::to_string(GetLastError()));
pXAudio2Create = reinterpret_cast<decltype(&XAudio2Create)>(GetProcAddress(xAudio2DLL, "XAudio2Create"));
if (pXAudio2Create == nullptr)
throw std::runtime_error("XAudio2Create not found. Error code: " + std::to_string(GetLastError()));
if (FAILED(hr = pXAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR)))
throw Exception::XAudio2Error(hr, "Failed to init XAudio2 engine. Error Details:");
// Stereo Expansion was planned to grab the currently configured number of
// Speakers from Windows's audio config.
// This doesn't always work though, so let it be a user configurable option.
int speakers;
// speakers = (numSpeakers + 1) *2; ?
switch (numSpeakers) {
case 0: // Stereo
speakers = 2;
break;
case 1: // Quadrafonic
speakers = 4;
break;
case 2: // Surround 5.1
speakers = 6;
break;
case 3: // Surround 7.1
speakers = 8;
break;
default:
speakers = 2;
}
if (FAILED(hr = pXAudio2->CreateMasteringVoice(&pMasteringVoice, speakers, SampleRate)))
throw Exception::XAudio2Error(hr, "Failed creating mastering voice: ");
switch (speakers) {
case 2:
ConLog("* SPU2 > Using normal 2 speaker stereo output.\n");
m_voiceContext = std::make_unique<StreamingVoice<StereoOut16>>();
break;
case 3:
ConLog("* SPU2 > 2.1 speaker expansion enabled.\n");
m_voiceContext = std::make_unique<StreamingVoice<Stereo21Out16>>();
break;
case 4:
ConLog("* SPU2 > 4 speaker expansion enabled [quadraphenia]\n");
m_voiceContext = std::make_unique<StreamingVoice<Stereo40Out16>>();
break;
case 5:
ConLog("* SPU2 > 4.1 speaker expansion enabled.\n");
m_voiceContext = std::make_unique<StreamingVoice<Stereo41Out16>>();
break;
case 6:
case 7:
switch (dplLevel) {
case 0: // "normal" stereo upmix
ConLog("* SPU2 > 5.1 speaker expansion enabled.\n");
m_voiceContext = std::make_unique<StreamingVoice<Stereo51Out16>>();
break;
case 1: // basic Dpl decoder without rear stereo balancing
ConLog("* SPU2 > 5.1 speaker expansion with basic ProLogic dematrixing enabled.\n");
m_voiceContext = std::make_unique<StreamingVoice<Stereo51Out16Dpl>>();
break;
case 2: // gigas PLII
ConLog("* SPU2 > 5.1 speaker expansion with experimental ProLogicII dematrixing enabled.\n");
m_voiceContext = std::make_unique<StreamingVoice<Stereo51Out16DplII>>();
break;
}
break;
default: // anything 8 or more gets the 7.1 treatment!
ConLog("* SPU2 > 7.1 speaker expansion enabled.\n");
m_voiceContext = std::make_unique<StreamingVoice<Stereo51Out16>>();
break;
}
m_voiceContext->Init(pXAudio2);
} catch (std::runtime_error &ex) {
SysMessage(ex.what());
Close();
return -1;
}
return 0;
}
void Close()
{
// Clean up?
// All XAudio2 interfaces are released when the engine is destroyed,
// but being tidy never hurt.
// Actually it can hurt. As of DXSDK Aug 2008, doing a full cleanup causes
// XA2 on Vista to crash. Even if you copy/paste code directly from Microsoft.
// But doing no cleanup at all causes XA2 under XP to crash. So after much trial
// and error we found a happy compromise as follows:
m_voiceContext = nullptr;
if (pMasteringVoice != nullptr)
pMasteringVoice->DestroyVoice();
pMasteringVoice = nullptr;
pXAudio2.Release();
CoUninitialize();
if (xAudio2DLL) {
FreeLibrary(xAudio2DLL);
xAudio2DLL = nullptr;
pXAudio2Create = nullptr;
}
}
virtual void Configure(uptr parent)
{
}
s32 Test() const
{
return 0;
}
int GetEmptySampleCount()
{
if (m_voiceContext == nullptr)
return 0;
return m_voiceContext->GetEmptySampleCount();
}
const wchar_t *GetIdent() const
{
return L"xaudio2";
}
const wchar_t *GetLongName() const
{
return L"XAudio 2 (Recommended)";
}
void ReadSettings()
{
}
void SetApiSettings(wxString api)
{
}
void WriteSettings() const
{
}
} static XA2;
SndOutModule *XAudio2Out = &XA2;

View File

@ -0,0 +1,323 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "Dialogs.h"
class WaveOutModule : public SndOutModule
{
private:
static const uint MAX_BUFFER_COUNT = 8;
static const int PacketsPerBuffer = (1024 / SndOutPacketSize);
static const int BufferSize = SndOutPacketSize * PacketsPerBuffer;
u32 numBuffers;
HWAVEOUT hwodevice;
WAVEFORMATEX wformat;
WAVEHDR whbuffer[MAX_BUFFER_COUNT];
StereoOut16 *qbuffer;
#define QBUFFER(x) (qbuffer + BufferSize * (x))
bool waveout_running;
HANDLE thread;
DWORD tid;
wchar_t ErrText[256];
template <typename T>
DWORD CALLBACK Thread()
{
static const int BufferSizeBytes = BufferSize * sizeof(T);
while (waveout_running) {
bool didsomething = false;
for (u32 i = 0; i < numBuffers; i++) {
if (!(whbuffer[i].dwFlags & WHDR_DONE))
continue;
WAVEHDR *buf = whbuffer + i;
buf->dwBytesRecorded = buf->dwBufferLength;
T *t = (T *)buf->lpData;
for (int p = 0; p < PacketsPerBuffer; p++, t += SndOutPacketSize)
SndBuffer::ReadSamples(t);
whbuffer[i].dwFlags &= ~WHDR_DONE;
waveOutWrite(hwodevice, buf, sizeof(WAVEHDR));
didsomething = true;
}
if (didsomething)
Sleep(1);
else
Sleep(0);
}
return 0;
}
template <typename T>
static DWORD CALLBACK RThread(WaveOutModule *obj)
{
return obj->Thread<T>();
}
public:
s32 Init()
{
numBuffers = Config_WaveOut.NumBuffers;
MMRESULT woores;
if (Test())
return -1;
// TODO : Use dsound to determine the speaker configuration, and expand audio from there.
#if 0
int speakerConfig;
//if( StereoExpansionEnabled )
speakerConfig = 2; // better not mess with this in wavout :p (rama)
// Any windows driver should support stereo at the software level, I should think!
pxAssume( speakerConfig > 1 );
LPTHREAD_START_ROUTINE threadproc;
switch( speakerConfig )
{
case 2:
ConLog( "* SPU2 > Using normal 2 speaker stereo output.\n" );
threadproc = (LPTHREAD_START_ROUTINE)&RThread<StereoOut16>;
speakerConfig = 2;
break;
case 4:
ConLog( "* SPU2 > 4 speaker expansion enabled [quadraphenia]\n" );
threadproc = (LPTHREAD_START_ROUTINE)&RThread<StereoQuadOut16>;
speakerConfig = 4;
break;
case 6:
case 7:
ConLog( "* SPU2 > 5.1 speaker expansion enabled.\n" );
threadproc = (LPTHREAD_START_ROUTINE)&RThread<Stereo51Out16>;
speakerConfig = 6;
break;
default:
ConLog( "* SPU2 > 7.1 speaker expansion enabled.\n" );
threadproc = (LPTHREAD_START_ROUTINE)&RThread<Stereo51Out16>;
speakerConfig = 8;
break;
}
#endif
wformat.wFormatTag = WAVE_FORMAT_PCM;
wformat.nSamplesPerSec = SampleRate;
wformat.wBitsPerSample = 16;
wformat.nChannels = 2;
wformat.nBlockAlign = ((wformat.wBitsPerSample * wformat.nChannels) / 8);
wformat.nAvgBytesPerSec = (wformat.nSamplesPerSec * wformat.nBlockAlign);
wformat.cbSize = 0;
qbuffer = new StereoOut16[BufferSize * numBuffers];
woores = waveOutOpen(&hwodevice, WAVE_MAPPER, &wformat, 0, 0, 0);
if (woores != MMSYSERR_NOERROR) {
waveOutGetErrorText(woores, (wchar_t *)&ErrText, 255);
SysMessage("WaveOut Error: %s", ErrText);
return -1;
}
const int BufferSizeBytes = wformat.nBlockAlign * BufferSize;
for (u32 i = 0; i < numBuffers; i++) {
whbuffer[i].dwBufferLength = BufferSizeBytes;
whbuffer[i].dwBytesRecorded = BufferSizeBytes;
whbuffer[i].dwFlags = 0;
whbuffer[i].dwLoops = 0;
whbuffer[i].dwUser = 0;
whbuffer[i].lpData = (LPSTR)QBUFFER(i);
whbuffer[i].lpNext = 0;
whbuffer[i].reserved = 0;
waveOutPrepareHeader(hwodevice, whbuffer + i, sizeof(WAVEHDR));
whbuffer[i].dwFlags |= WHDR_DONE; //avoid deadlock
}
// Start Thread
// [Air]: The waveout code does not use wait objects, so setting a time critical
// priority level is a bad idea. Standard priority will do fine. The buffer will get the
// love it needs and won't suck resources idling pointlessly. Just don't try to
// run it in uber-low-latency mode.
waveout_running = true;
thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RThread<StereoOut16>, this, 0, &tid);
return 0;
}
void Close()
{
// Stop Thread
fprintf(stderr, "* SPU2-X: Waiting for waveOut thread to finish...");
waveout_running = false;
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
fprintf(stderr, " Done.\n");
//
// Clean up
//
waveOutReset(hwodevice);
for (u32 i = 0; i < numBuffers; i++) {
waveOutUnprepareHeader(hwodevice, &whbuffer[i], sizeof(WAVEHDR));
}
waveOutClose(hwodevice);
safe_delete_array(qbuffer);
}
private:
static BOOL CALLBACK ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
switch (uMsg) {
case WM_INITDIALOG:
wchar_t temp[128];
INIT_SLIDER(IDC_BUFFERS_SLIDER, 3, MAX_BUFFER_COUNT, 2, 1, 1);
SendMessage(GetDlgItem(hWnd, IDC_BUFFERS_SLIDER), TBM_SETPOS, TRUE, Config_WaveOut.NumBuffers);
swprintf_s(temp, 128, L"%d (%d ms latency)", Config_WaveOut.NumBuffers, 1000 / (96000 / (Config_WaveOut.NumBuffers * BufferSize)));
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId) {
case IDOK: {
Config_WaveOut.NumBuffers = (int)SendMessage(GetDlgItem(hWnd, IDC_BUFFERS_SLIDER), TBM_GETPOS, 0, 0);
if (Config_WaveOut.NumBuffers < 3)
Config_WaveOut.NumBuffers = 3;
if (Config_WaveOut.NumBuffers > MAX_BUFFER_COUNT)
Config_WaveOut.NumBuffers = MAX_BUFFER_COUNT;
}
EndDialog(hWnd, 0);
break;
case IDCANCEL:
EndDialog(hWnd, 0);
break;
default:
return FALSE;
}
break;
case WM_HSCROLL:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId) {
case TB_LINEUP:
case TB_LINEDOWN:
case TB_PAGEUP:
case TB_PAGEDOWN:
case TB_TOP:
case TB_BOTTOM:
wmEvent = (int)SendMessage((HWND)lParam, TBM_GETPOS, 0, 0);
case TB_THUMBPOSITION:
case TB_THUMBTRACK:
if (wmEvent < 3)
wmEvent = 3;
if (wmEvent > MAX_BUFFER_COUNT)
wmEvent = MAX_BUFFER_COUNT;
SendMessage((HWND)lParam, TBM_SETPOS, TRUE, wmEvent);
swprintf_s(temp, L"%d (%d ms latency)", wmEvent, 1000 / (96000 / (wmEvent * BufferSize)));
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
break;
default:
return FALSE;
}
break;
default:
return FALSE;
}
return TRUE;
}
public:
virtual void Configure(uptr parent)
{
INT_PTR ret;
ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_WAVEOUT), (HWND)parent, (DLGPROC)ConfigProc, 1);
if (ret == -1) {
MessageBox((HWND)parent, L"Error Opening the config dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
return;
}
}
s32 Test() const
{
if (waveOutGetNumDevs() == 0) {
SysMessage("No waveOut Devices Present\n");
return -1;
}
return 0;
}
int GetEmptySampleCount()
{
int result = 0;
for (int i = 0; i < MAX_BUFFER_COUNT; i++) {
result += (whbuffer[i].dwFlags & WHDR_DONE) ? BufferSize : 0;
}
return result;
}
const wchar_t *GetIdent() const
{
return L"waveout";
}
const wchar_t *GetLongName() const
{
return L"WaveOut (Laggy)";
}
void ReadSettings()
{
}
void SetApiSettings(wxString api)
{
}
void WriteSettings() const
{
}
} static WO;
SndOutModule *WaveOut = &WO;

View File

@ -0,0 +1,62 @@
; * SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
; * Developed and maintained by the Pcsx2 Development Team.
; *
; * Originally based on SPU2ghz v1.9 beta, by David Quintana.
; *
; * SPU2-X is free software: you can redistribute it and/or modify it under the terms
; * of the GNU Lesser General Public License as published by the Free Software Found-
; * ation, either version 3 of the License, or (at your option) any later version.
; *
; * SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
; * PURPOSE. See the GNU Lesser General Public License for more details.
; *
; * You should have received a copy of the GNU Lesser General Public License
; * along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
; SPU2-X.def : Declares the module parameters for the DLL.
;LIBRARY "SPU2-X"
EXPORTS
; Explicit exports can go here
PS2EgetLibType @2
PS2EgetLibName @3
PS2EgetLibVersion2 @4
SPU2init @5
SPU2shutdown @6
SPU2open @7
SPU2close @8
SPU2configure @9
SPU2test @10
SPU2freeze @12
SPU2setSettingsDir @13
SPU2setLogDir @14
SPU2write @15
SPU2read @16
SPU2async @17
SPU2readDMA4Mem @18
SPU2writeDMA4Mem @19
SPU2readDMA7Mem @20
SPU2writeDMA7Mem @21
SPU2interruptDMA4 @22
SPU2interruptDMA7 @23
SPU2irqCallback @24
SPU2setupRecording @25
SPU2ReadMemAddr @26
SPU2WriteMemAddr @27
SPU2setClockPtr @28
SPU2setDMABaseAddr @29
SPU2replay = s2r_replay @30
SPU2reset @31
SPU2ps1reset @32

View File

@ -0,0 +1,359 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "svnrev.h"
#include "afxresmw.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_CONFIG DIALOGEX 0, 0, 310, 260
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "SPU2-X Settings"
FONT 8, "MS Shell Dlg", 400, 0, 0x0
BEGIN
PUSHBUTTON "OK",IDOK,101,238,50,14,NOT WS_TABSTOP
PUSHBUTTON "Cancel",IDCANCEL,157,238,50,14,NOT WS_TABSTOP
GROUPBOX "Mixing Settings",IDC_STATIC,6,5,145,115
LTEXT "Interpolation:",IDC_STATIC,12,16,61,10,NOT WS_GROUP
COMBOBOX IDC_INTERPOLATE,14,26,129,84,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
CHECKBOX "Disable Effects Processing",IDC_EFFECTS_DISABLE,14,47,126,10
LTEXT "(speedup!) Skips reverb effects processing, but won't sound as good in most games.",IDC_STATIC,26,59,110,36
CONTROL "Use the de-alias filter",IDC_DEALIASFILTER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,92,126,10
LTEXT "(overemphasizes the highs)",IDC_STATIC,26,104,114,12,NOT WS_GROUP
GROUPBOX "",IDC_STATIC,6,123,145,45
CHECKBOX "Enable Debug Options",IDC_DEBUG_ENABLE,14,135,118,10,NOT WS_TABSTOP
PUSHBUTTON "Configure...",IDC_OPEN_CONFIG_DEBUG,14,147,52,13
CONTROL 116,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZECONTROL,6,175,145,55,WS_EX_CLIENTEDGE
GROUPBOX "Output Settings",IDC_STATIC,157,5,145,225
LTEXT "Module:",IDC_STATIC,163,16,50,9,NOT WS_GROUP
COMBOBOX IDC_OUTPUT,165,26,129,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "Configure...",IDC_OUTCONF,165,42,52,13
LTEXT "Volume:",IDC_STATIC,192,61,27,8,NOT WS_GROUP
CTEXT "100%",IDC_VOLUME_LABEL,226,61,58,9
CONTROL "",IDC_VOLUME_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,165,72,129,10
LTEXT "Latency:",IDC_STATIC,190,86,29,8,NOT WS_GROUP
CTEXT "100 ms (avg)",IDC_LATENCY_LABEL,227,86,58,9
CONTROL "Slider2",IDC_LATENCY_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,165,96,129,10
CONTROL "Synchronizing Mode:",IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,163,116,133,8
COMBOBOX IDC_SYNCHMODE,165,126,129,62,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
PUSHBUTTON "Advanced...",IDC_OPEN_CONFIG_SOUNDTOUCH,165,142,52,13
LTEXT "Audio Expansion Mode:",IDC_SPEAKERS_TEXT,163,162,137,10,NOT WS_GROUP
COMBOBOX IDC_SPEAKERS,165,172,129,84,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
CONTROL "Use a Winamp DSP plugin",IDC_DSP_ENABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,165,195,129,11
LTEXT "(currently requires manual configuration via the ini file)",IDC_STATIC,177,207,100,20
END
IDD_DEBUG DIALOGEX 0, 0, 303, 473
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "SPU2-X Debug"
FONT 9, "Lucida Console", 400, 0, 0x0
BEGIN
DEFPUSHBUTTON "Close",IDOK,246,451,50,14
END
IDD_DSOUND DIALOGEX 0, 0, 196, 218
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "DirectSound Output Module Settings"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,45,196,50,14
PUSHBUTTON "Cancel",IDCANCEL,102,196,50,14
COMBOBOX IDC_DS_DEVICE,8,13,180,62,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "DirectSound Device:",IDC_STATIC,6,3,65,8
CONTROL "",IDC_BUFFERS_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,40,137,108,10
LTEXT "Increase the buffer count if you are experiencing loopy or stuttery audio even when games run at high FPS.",IDC_STATIC,13,157,169,27
CTEXT "8 (80 ms latency)",IDC_LATENCY_LABEL,46,123,95,11
LTEXT "The options above are useful for compatibility with older and/or buggy sound drivers ONLY, and should not be checked unless you experience sound problems (such as crackly audio or silence).",IDC_STATIC,18,68,172,38
CONTROL "Disable Global Focus",IDC_GLOBALFOCUS_DISABLE,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,6,38,140,10
CONTROL "Use a crappy alternate buffering mode",IDC_USE_HARDWARE,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,53,140,10
GROUPBOX "Output Buffers",IDC_STATIC,6,111,183,77
END
IDD_WAVEOUT DIALOGEX 0, 0, 170, 122
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "WaveOut Output Module Settings"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,32,100,50,14
PUSHBUTTON "Cancel",IDCANCEL,88,100,50,14
COMBOBOX IDC_DS_DEVICE,6,13,159,62,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "WaveOut Device:",IDC_STATIC,4,3,60,8
LTEXT "Number of Buffers",IDC_STATIC,4,39,61,15
CONTROL "",IDC_BUFFERS_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,71,48,94,10
LTEXT "Use extra buffers if you are experiencing loopy or studdery audio even when games run at high FPS.",IDC_STATIC,8,66,151,27
CTEXT "8 (80 ms latency)",IDC_LATENCY_LABEL,70,37,95,11
END
IDD_CONFIG_SOUNDTOUCH DIALOGEX 0, 0, 206, 223
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Soundtouch Advanced Configuration - SPU2-X"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,50,201,50,14
PUSHBUTTON "Cancel",IDCANCEL,106,201,50,14
CTEXT "These are advanced configuration options for fine tuning time stretching behavior. Larger values are better for slowdown, while smaller values are better for speed-up (better than 60fps).\n\nAll options are in milliseconds (ms).",IDC_STATIC,5,5,196,52
CONTROL "",IDC_SEQLEN_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,50,98,105,10
CTEXT "Sequence Length",IDC_STATIC,72,86,64,9
CONTROL "",IDC_SEEKWIN_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,50,134,105,10
CTEXT "Seekwindow Size",IDC_STATIC,70,123,66,9
CONTROL "",IDC_OVERLAP_SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,50,168,105,10
CTEXT "Overlap",IDC_STATIC,86,160,34,9
LTEXT "20",IDC_STATIC,50,110,9,8
LTEXT "100",IDC_STATIC,146,110,13,8
LTEXT "10",IDC_STATIC,50,147,9,8
LTEXT "30",IDC_STATIC,146,147,9,8
LTEXT "5",IDC_STATIC,52,182,8,8
LTEXT "15",IDC_STATIC,146,182,9,8
PUSHBUTTON "Reset Defaults",IDC_RESET_DEFAULTS,61,62,82,12
END
IDD_CONFIG_DEBUG DIALOGEX 0, 0, 292, 239
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "SPU2-X Debugging Options"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,93,217,50,14
PUSHBUTTON "Cancel",IDCANCEL,149,217,50,14
GROUPBOX "",IDC_STATIC,5,5,135,93
GROUPBOX "Logfile-only Logs",IDC_STATIC,151,5,136,53
GROUPBOX "Dumps (on close)",IDC_STATIC,151,60,135,54
CONTROL "Show In Console",IDC_MSGSHOW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,5,69,10
CHECKBOX "KeyOn/Off Events",IDC_MSGKEY,17,18,74,9,NOT WS_TABSTOP
CONTROL "Voice Stop Events",IDC_MSGVOICE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,31,75,9
CONTROL "DMA Operations",IDC_MSGDMA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,44,68,9
CONTROL "AutoDMA Operations",IDC_MSGADMA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,57,83,9
CONTROL "Buffer Over/Underruns",IDC_DBG_OVERRUNS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,70,97,9
CONTROL "ADPCM Cache Statistics",IDC_DBG_CACHE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,83,114,9
CHECKBOX "Dump Core and Voice State",IDC_DUMPCORE,159,74,104,10,NOT WS_TABSTOP
CHECKBOX "Dump Memory Contents",IDC_DUMPMEM,159,87,91,10,NOT WS_TABSTOP
CHECKBOX "Dump Register Data",IDC_DUMPREGS,159,100,80,10,NOT WS_TABSTOP
CHECKBOX "Log Register/DMA Actions",IDC_LOGREGS,159,18,101,10,WS_GROUP | NOT WS_TABSTOP
CHECKBOX "Log DMA Writes",IDC_LOGDMA,159,31,68,10,NOT WS_TABSTOP
CHECKBOX "Log Audio Output",IDC_LOGWAVE,159,44,71,10,NOT WS_TABSTOP
LTEXT "Note: This is a non-devel build. For performance reasons, some logging options are disabled; and only available in devel/debug builds.",IDC_MSG_PUBLIC_BUILD,9,187,174,30
GROUPBOX "Others",IDC_DEBUG_OTHERS,5,104,135,68
CONTROL "Show Core Activity",IDC_DEBUG_VISUAL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,17,116,90,11
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_CONFIG, DIALOG
BEGIN
LEFTMARGIN, 6
RIGHTMARGIN, 314
VERTGUIDE, 218
VERTGUIDE, 282
BOTTOMMARGIN, 292
HORZGUIDE, 268
END
IDD_DEBUG, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 296
TOPMARGIN, 7
BOTTOMMARGIN, 465
END
IDD_DSOUND, DIALOG
BEGIN
LEFTMARGIN, 4
RIGHTMARGIN, 191
TOPMARGIN, 3
BOTTOMMARGIN, 214
END
IDD_WAVEOUT, DIALOG
BEGIN
LEFTMARGIN, 4
RIGHTMARGIN, 165
TOPMARGIN, 3
BOTTOMMARGIN, 118
END
IDD_CONFIG_SOUNDTOUCH, DIALOG
BEGIN
LEFTMARGIN, 5
RIGHTMARGIN, 201
TOPMARGIN, 5
BOTTOMMARGIN, 218
END
IDD_CONFIG_DEBUG, DIALOG
BEGIN
LEFTMARGIN, 5
RIGHTMARGIN, 287
TOPMARGIN, 5
BOTTOMMARGIN, 234
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//
IDB_SPU2X_SMALL BITMAP "..\\..\\spu2-x-sm.bmp"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,0,0,4851
PRODUCTVERSION 2,0,0,4851
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "SPU2 Plugin for PS2 Emulators"
VALUE "CompanyName", "PCSX2 Dev Team"
VALUE "FileDescription", "SPU2-X Plugin (git build)"
VALUE "FileVersion", "2.0.GIT"
VALUE "InternalName", "SPU2-X"
VALUE "LegalCopyright", "Copyright (C) 2011"
VALUE "OriginalFilename", "SPU2-X-2.0.dll"
VALUE "ProductName", "SPU2-X"
VALUE "ProductVersion", "2.0.GIT"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// English (United Kingdom) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
#pragma code_page(1252)
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_PORTAUDIO DIALOGEX 0, 0, 316, 166
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Portaudio Output Module Settings"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,105,144,50,14
PUSHBUTTON "Cancel",IDCANCEL,161,144,50,14
COMBOBOX IDC_PA_HOSTAPI,7,17,95,62,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Host API:",IDC_STATIC,7,7,32,8
COMBOBOX IDC_PA_DEVICE,108,17,201,62,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Device Name:",IDC_STATIC,107,7,45,8
CONTROL "",IDC_LATENCY,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,96,93,155,10
LTEXT "NOTE: Depending on the hardware and drivers, the suggested manual latency might not be used, and portaudio will choose the closest possible value.",IDC_STATIC,12,111,286,19
CTEXT "20ms",IDC_LATENCY_LABEL,264,93,35,11
GROUPBOX "Output Latency",IDC_STATIC,7,57,302,79
CONTROL "Use smallest possible (may cause issues if the actual value is too small)",IDC_MINIMIZE,
"Button",BS_AUTORADIOBUTTON | WS_GROUP,12,75,258,10
CONTROL "Use this latency:",IDC_MANUAL,"Button",BS_AUTORADIOBUTTON,12,93,69,10
CONTROL "WASAPI Exclusive Mode",IDC_EXCLUSIVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,39,93,10
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_PORTAUDIO, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 309
TOPMARGIN, 7
BOTTOMMARGIN, 159
END
END
#endif // APSTUDIO_INVOKED
#endif // English (United Kingdom) resources
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Spanish (Spain, International Sort) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ESN)
LANGUAGE LANG_SPANISH, SUBLANG_SPANISH_MODERN
#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""svnrev.h""\r\n"
"#include ""afxresmw.h""\0"
END
3 TEXTINCLUDE
BEGIN
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // Spanish (Spain, International Sort) resources
/////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugStrict|Win32">
<Configuration>DebugStrict</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="DebugStrict|x64">
<Configuration>DebugStrict</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Devel|Win32">
<Configuration>Devel</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Devel|x64">
<Configuration>Devel</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<Import Project="$(SolutionDir)common\vsprops\WinSDK.props" />
<PropertyGroup Label="Globals">
<ProjectGuid>{5307BBB7-EBB9-4AA4-8CB6-A94EC473C8C4}</ProjectGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization Condition="$(Configuration.Contains(Release))">true</WholeProgramOptimization>
<UseDebugLibraries Condition="$(Configuration.Contains(Debug))">true</UseDebugLibraries>
<UseDebugLibraries Condition="!$(Configuration.Contains(Debug))">false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="PropertySheets">
<Import Project="..\..\..\..\common\vsprops\plugin_svnroot.props" />
<Import Project="..\..\..\..\common\vsprops\BaseProperties.props" />
<Import Project="..\..\..\..\common\vsprops\3rdpartyDeps.props" />
<Import Project="..\..\..\..\common\vsprops\pthreads.props" />
<Import Condition="$(Configuration.Contains(Debug))" Project="..\..\..\..\common\vsprops\CodeGen_Debug.props" />
<Import Condition="$(Configuration.Contains(Devel))" Project="..\..\..\..\common\vsprops\CodeGen_Devel.props" />
<Import Condition="$(Configuration.Contains(Release))" Project="..\..\..\..\common\vsprops\CodeGen_Release.props" />
<Import Condition="!$(Configuration.Contains(Release))" Project="..\..\..\..\common\vsprops\IncrementalLinking.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions Condition="'$(Configuration)'=='Debug'">DEBUG_FAST;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\portaudio\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<ExceptionHandling>Async</ExceptionHandling>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>Global.h</PrecompiledHeaderFile>
<CompileAs>Default</CompileAs>
</ClCompile>
<Link>
<AdditionalDependencies>rpcrt4.lib;winmm.lib;dsound.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>.\Spu2-X.def</ModuleDefinitionFile>
<TargetMachine Condition="'$(Platform)'=='Win32'">MachineX86</TargetMachine>
<TargetMachine Condition="'$(Platform)'=='x64'">MachineX64</TargetMachine>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\Config.h" />
<ClInclude Include="..\Global.h" />
<ClInclude Include="..\spu2replay.h" />
<ClInclude Include="..\Lowpass.h" />
<ClInclude Include="..\SndOut.h" />
<ClInclude Include="..\Linux\Alsa.h" />
<ClInclude Include="..\spdif.h" />
<ClInclude Include="..\defs.h" />
<ClInclude Include="..\Dma.h" />
<ClInclude Include="..\regs.h" />
<ClInclude Include="..\Mixer.h" />
<ClInclude Include="dsp.h" />
<ClInclude Include="..\Linux\Config.h" />
<ClInclude Include="..\Linux\Dialogs.h" />
<ClInclude Include="Dialogs.h" />
<ClInclude Include="WinConfig.h" />
<ClInclude Include="..\PS2E-spu2.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\DplIIdecoder.cpp" />
<ClCompile Include="..\PrecompiledHeader.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\debug.cpp" />
<ClCompile Include="..\RegLog.cpp" />
<ClCompile Include="..\spu2replay.cpp" />
<ClCompile Include="..\wavedump_wav.cpp" />
<ClCompile Include="..\Lowpass.cpp" />
<ClCompile Include="..\SndOut.cpp" />
<ClCompile Include="..\Timestretcher.cpp" />
<ClCompile Include="SndOut_DSound.cpp" />
<ClCompile Include="SndOut_waveOut.cpp" />
<ClCompile Include="SndOut_XAudio2.cpp" />
<ClCompile Include="..\Linux\Alsa.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\SndOut_Portaudio.cpp" />
<ClCompile Include="..\dma.cpp" />
<ClCompile Include="..\RegTable.cpp" />
<ClCompile Include="..\spu2freeze.cpp" />
<ClCompile Include="..\spu2sys.cpp" />
<ClCompile Include="..\ADSR.cpp" />
<ClCompile Include="..\Mixer.cpp" />
<ClCompile Include="..\ReadInput.cpp" />
<ClCompile Include="..\Reverb.cpp" />
<ClCompile Include="dsp.cpp" />
<ClCompile Include="..\Linux\Config.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\Linux\Dialogs.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="CfgHelpers.cpp" />
<ClCompile Include="Config.cpp" />
<ClCompile Include="ConfigDebug.cpp" />
<ClCompile Include="ConfigSoundtouch.cpp" />
<ClCompile Include="RealtimeDebugger.cpp" />
<ClCompile Include="UIHelpers.cpp" />
<ClCompile Include="..\PS2E-spu2.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\LGPL.txt" />
<None Include="..\..\License.txt" />
<None Include="..\..\spu2-x-sm.bmp" />
<None Include="Spu2-X.def" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Spu2-X.rc" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\3rdparty\portaudio\build\msvc\portaudio.vcxproj">
<Project>{0a18a071-125e-442f-aff7-a3f68abecf99}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
<ProjectReference Include="..\..\..\..\3rdparty\pthreads4w\build\pthreads4w.vcxproj">
<Project>{0fae817d-9a32-4830-857e-81da57246e16}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
<ProjectReference Include="..\..\..\..\3rdparty\soundtouch\SoundTouch.vcxproj">
<Project>{e9b51944-7e6d-4bcd-83f2-7bbd5a46182d}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
<ProjectReference Include="..\..\..\..\3rdparty\wxwidgets3.0\build\msw\wx30_base.vcxproj">
<Project>{3fcc50c2-81e9-5db2-b8d8-2129427568b1}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\common\build\Utilities\utilities.vcxproj">
<Project>{4639972e-424e-4e13-8b07-ca403c481346}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>

View File

@ -0,0 +1,233 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{21596259-72f4-41c0-b499-40839e39f043}</UniqueIdentifier>
<Extensions>h;hpp;cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
</Filter>
<Filter Include="Source Files\debug">
<UniqueIdentifier>{30e5b8dd-2015-4c3d-a663-992fb42627b5}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Sound Output">
<UniqueIdentifier>{ed4df93e-4e4b-4675-a181-c5084146df7c}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Sound Output\Windows">
<UniqueIdentifier>{1cb77247-dec9-4a0a-867f-7353b14decac}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Sound Output\Linux">
<UniqueIdentifier>{f87cf8e1-d654-4214-b7f4-6fb697f3941a}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Sound Output\Crossplatform">
<UniqueIdentifier>{10ccccc1-642a-42ec-900c-801f707bd776}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\decoder">
<UniqueIdentifier>{03be4765-f627-4c0a-9d40-f3c0f86f1e57}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\decoder\ac3%28a/52%29">
<UniqueIdentifier>{ea7e8889-2015-40b9-ac77-b4a3a58c41af}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\SPU2">
<UniqueIdentifier>{63ac2d88-b5c3-41a3-bd2c-a04c80acd35f}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\SPU2\Mixer">
<UniqueIdentifier>{825046cd-874f-45f4-8622-14c146a97b46}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Winamp DSP">
<UniqueIdentifier>{3924e038-9ec9-4f1c-89b9-6d03c46566ac}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\GUI">
<UniqueIdentifier>{efaf786c-f09d-49f7-b5f3-8bb7078e9c44}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\GUI\Linux">
<UniqueIdentifier>{00c53f28-fde8-4f60-88d2-d223584f1db5}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\GUI\Linux\Include">
<UniqueIdentifier>{95111121-7e1f-4624-8765-a17e8f13827f}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\GUI\Windows">
<UniqueIdentifier>{70e9653e-fba5-4c6e-ad9b-185006096d49}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\PluginInterface">
<UniqueIdentifier>{6fcf6c91-7afe-4ade-bb30-79d63901d590}</UniqueIdentifier>
</Filter>
<Filter Include="Documents">
<UniqueIdentifier>{381a6e5e-bdd5-48b0-81f8-e2775eff9585}</UniqueIdentifier>
</Filter>
<Filter Include="Resources">
<UniqueIdentifier>{d26e57df-a97e-4ba5-801a-c0c52b5d061f}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\Config.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\Global.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="..\spu2replay.h">
<Filter>Source Files\debug</Filter>
</ClInclude>
<ClInclude Include="..\Lowpass.h">
<Filter>Source Files\Sound Output</Filter>
</ClInclude>
<ClInclude Include="..\SndOut.h">
<Filter>Source Files\Sound Output</Filter>
</ClInclude>
<ClInclude Include="..\Linux\Alsa.h">
<Filter>Source Files\Sound Output\Linux</Filter>
</ClInclude>
<ClInclude Include="..\spdif.h">
<Filter>Source Files\decoder</Filter>
</ClInclude>
<ClInclude Include="..\defs.h">
<Filter>Source Files\SPU2</Filter>
</ClInclude>
<ClInclude Include="..\Dma.h">
<Filter>Source Files\SPU2</Filter>
</ClInclude>
<ClInclude Include="..\regs.h">
<Filter>Source Files\SPU2</Filter>
</ClInclude>
<ClInclude Include="..\Mixer.h">
<Filter>Source Files\SPU2\Mixer</Filter>
</ClInclude>
<ClInclude Include="dsp.h">
<Filter>Source Files\Winamp DSP</Filter>
</ClInclude>
<ClInclude Include="..\Linux\Config.h">
<Filter>Source Files\GUI\Linux\Include</Filter>
</ClInclude>
<ClInclude Include="..\Linux\Dialogs.h">
<Filter>Source Files\GUI\Linux\Include</Filter>
</ClInclude>
<ClInclude Include="Dialogs.h">
<Filter>Source Files\GUI\Windows</Filter>
</ClInclude>
<ClInclude Include="WinConfig.h">
<Filter>Source Files\GUI\Windows</Filter>
</ClInclude>
<ClInclude Include="..\PS2E-spu2.h">
<Filter>Source Files\PluginInterface</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Resources</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\PrecompiledHeader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\debug.cpp">
<Filter>Source Files\debug</Filter>
</ClCompile>
<ClCompile Include="..\RegLog.cpp">
<Filter>Source Files\debug</Filter>
</ClCompile>
<ClCompile Include="..\spu2replay.cpp">
<Filter>Source Files\debug</Filter>
</ClCompile>
<ClCompile Include="..\wavedump_wav.cpp">
<Filter>Source Files\debug</Filter>
</ClCompile>
<ClCompile Include="..\Lowpass.cpp">
<Filter>Source Files\Sound Output</Filter>
</ClCompile>
<ClCompile Include="..\SndOut.cpp">
<Filter>Source Files\Sound Output</Filter>
</ClCompile>
<ClCompile Include="..\Timestretcher.cpp">
<Filter>Source Files\Sound Output</Filter>
</ClCompile>
<ClCompile Include="SndOut_DSound.cpp">
<Filter>Source Files\Sound Output\Windows</Filter>
</ClCompile>
<ClCompile Include="SndOut_waveOut.cpp">
<Filter>Source Files\Sound Output\Windows</Filter>
</ClCompile>
<ClCompile Include="..\Linux\Alsa.cpp">
<Filter>Source Files\Sound Output\Linux</Filter>
</ClCompile>
<ClCompile Include="..\SndOut_Portaudio.cpp">
<Filter>Source Files\Sound Output\Crossplatform</Filter>
</ClCompile>
<ClCompile Include="..\dma.cpp">
<Filter>Source Files\SPU2</Filter>
</ClCompile>
<ClCompile Include="..\RegTable.cpp">
<Filter>Source Files\SPU2</Filter>
</ClCompile>
<ClCompile Include="..\spu2freeze.cpp">
<Filter>Source Files\SPU2</Filter>
</ClCompile>
<ClCompile Include="..\spu2sys.cpp">
<Filter>Source Files\SPU2</Filter>
</ClCompile>
<ClCompile Include="..\ADSR.cpp">
<Filter>Source Files\SPU2\Mixer</Filter>
</ClCompile>
<ClCompile Include="..\Mixer.cpp">
<Filter>Source Files\SPU2\Mixer</Filter>
</ClCompile>
<ClCompile Include="..\ReadInput.cpp">
<Filter>Source Files\SPU2\Mixer</Filter>
</ClCompile>
<ClCompile Include="..\Reverb.cpp">
<Filter>Source Files\SPU2\Mixer</Filter>
</ClCompile>
<ClCompile Include="dsp.cpp">
<Filter>Source Files\Winamp DSP</Filter>
</ClCompile>
<ClCompile Include="..\Linux\Config.cpp">
<Filter>Source Files\GUI\Linux</Filter>
</ClCompile>
<ClCompile Include="..\Linux\Dialogs.cpp">
<Filter>Source Files\GUI\Linux</Filter>
</ClCompile>
<ClCompile Include="CfgHelpers.cpp">
<Filter>Source Files\GUI\Windows</Filter>
</ClCompile>
<ClCompile Include="Config.cpp">
<Filter>Source Files\GUI\Windows</Filter>
</ClCompile>
<ClCompile Include="ConfigDebug.cpp">
<Filter>Source Files\GUI\Windows</Filter>
</ClCompile>
<ClCompile Include="ConfigSoundtouch.cpp">
<Filter>Source Files\GUI\Windows</Filter>
</ClCompile>
<ClCompile Include="RealtimeDebugger.cpp">
<Filter>Source Files\GUI\Windows</Filter>
</ClCompile>
<ClCompile Include="UIHelpers.cpp">
<Filter>Source Files\GUI\Windows</Filter>
</ClCompile>
<ClCompile Include="..\PS2E-spu2.cpp">
<Filter>Source Files\PluginInterface</Filter>
</ClCompile>
<ClCompile Include="..\DplIIdecoder.cpp">
<Filter>Source Files\Sound Output</Filter>
</ClCompile>
<ClCompile Include="SndOut_XAudio2.cpp">
<Filter>Source Files\Sound Output\Windows</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\LGPL.txt">
<Filter>Documents</Filter>
</None>
<None Include="..\..\License.txt">
<Filter>Documents</Filter>
</None>
<None Include="..\..\spu2-x-sm.bmp">
<Filter>Resources</Filter>
</None>
<None Include="Spu2-X.def">
<Filter>Resources</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Spu2-X.rc">
<Filter>Resources</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,79 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "Dialogs.h"
int SendDialogMsg(HWND hwnd, int dlgId, UINT code, WPARAM wParam, LPARAM lParam)
{
return SendMessage(GetDlgItem(hwnd, dlgId), code, wParam, lParam);
}
__forceinline void Verifyc(HRESULT hr, const char *fn)
{
if (FAILED(hr)) {
assert(0);
throw std::runtime_error("DirectSound returned an error from %s");
}
}
void AssignSliderValue(HWND idcwnd, HWND hwndDisplay, int value)
{
value = std::min(std::max(value, 0), 512);
SendMessage(idcwnd, TBM_SETPOS, TRUE, value);
wchar_t tbox[32];
swprintf_s(tbox, L"%d", value);
SetWindowText(hwndDisplay, tbox);
}
void AssignSliderValue(HWND hWnd, int idc, int editbox, int value)
{
AssignSliderValue(GetDlgItem(hWnd, idc), GetDlgItem(hWnd, editbox), value);
}
// Generic slider/scroller message handler. This is succient so long as you
// don't need some kind of secondary event handling functionality, such as
// updating a custom label.
BOOL DoHandleScrollMessage(HWND hwndDisplay, WPARAM wParam, LPARAM lParam)
{
int wmId = LOWORD(wParam);
int wmEvent = HIWORD(wParam);
switch (wmId) {
//case TB_ENDTRACK:
//case TB_THUMBPOSITION:
case TB_LINEUP:
case TB_LINEDOWN:
case TB_PAGEUP:
case TB_PAGEDOWN:
wmEvent = (int)SendMessage((HWND)lParam, TBM_GETPOS, 0, 0);
case TB_THUMBTRACK:
AssignSliderValue((HWND)lParam, hwndDisplay, wmEvent);
break;
default:
return FALSE;
}
return TRUE;
}
int GetSliderValue(HWND hWnd, int idc)
{
int retval = (int)SendMessage(GetDlgItem(hWnd, idc), TBM_GETPOS, 0, 0);
return GetClamped(retval, 0, 512);
}

View File

@ -0,0 +1,86 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
#include <windows.h>
#include <mmsystem.h>
#include <commctrl.h>
#include <initguid.h>
#include <tchar.h>
#include <VersionHelpers.h>
#include "resource.h"
extern HINSTANCE hInstance;
#define SET_CHECK(idc, value) SendMessage(GetDlgItem(hWnd, idc), BM_SETCHECK, ((value) == 0) ? BST_UNCHECKED : BST_CHECKED, 0)
#define HANDLE_CHECK(idc, hvar) \
case idc: \
(hvar) = !(hvar); \
SendMessage(GetDlgItem(hWnd, idc), BM_SETCHECK, (hvar) ? BST_CHECKED : BST_UNCHECKED, 0); \
break
#define HANDLE_CHECKNB(idc, hvar) \
case idc: \
(hvar) = !(hvar); \
SendMessage(GetDlgItem(hWnd, idc), BM_SETCHECK, (hvar) ? BST_CHECKED : BST_UNCHECKED, 0)
#define ENABLE_CONTROL(idc, value) EnableWindow(GetDlgItem(hWnd, idc), value)
#define INIT_SLIDER(idc, minrange, maxrange, tickfreq, pagesize, linesize) \
SendMessage(GetDlgItem(hWnd, idc), TBM_SETRANGEMIN, FALSE, minrange); \
SendMessage(GetDlgItem(hWnd, idc), TBM_SETRANGEMAX, FALSE, maxrange); \
SendMessage(GetDlgItem(hWnd, idc), TBM_SETTICFREQ, tickfreq, 0); \
SendMessage(GetDlgItem(hWnd, idc), TBM_SETPAGESIZE, 0, pagesize); \
SendMessage(GetDlgItem(hWnd, idc), TBM_SETLINESIZE, 0, linesize)
#define HANDLE_SCROLL_MESSAGE(idc, idcDisplay) \
if ((HWND)lParam == GetDlgItem(hWnd, idc)) \
return DoHandleScrollMessage(GetDlgItem(hWnd, idcDisplay), wParam, lParam)
// *** BEGIN DRIVER-SPECIFIC CONFIGURATION ***
// -------------------------------------------
struct CONFIG_XAUDIO2
{
wxString Device;
s8 NumBuffers;
CONFIG_XAUDIO2()
: Device()
, NumBuffers(2)
{
}
};
struct CONFIG_WAVEOUT
{
wxString Device;
s8 NumBuffers;
CONFIG_WAVEOUT()
: Device()
, NumBuffers(4)
{
}
};
extern CONFIG_WAVEOUT Config_WaveOut;
extern CONFIG_XAUDIO2 Config_XAudio2;

179
pcsx2/SPU2/Windows/dsp.cpp Normal file
View File

@ -0,0 +1,179 @@
//GiGaHeRz's SPU2 Driver
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
//
#include "Global.h"
#define _WIN32_WINNT 0x0600
#include <windows.h>
#include <mmsystem.h>
extern "C" {
#include "dsp.h"
typedef winampDSPHeader *(*pWinampDSPGetHeader2)();
}
HMODULE hLib = NULL;
pWinampDSPGetHeader2 pGetHeader = NULL;
winampDSPHeader *pHeader = NULL;
winampDSPModule *pModule = NULL;
HWND hTemp;
#define USE_A_THREAD
#ifdef USE_A_THREAD
HANDLE hUpdateThread;
DWORD UpdateThreadId;
bool running;
DWORD WINAPI DspUpdateThread(PVOID param);
#endif
s32 DspLoadLibrary(wchar_t *fileName, int modNum)
#ifdef USE_A_THREAD
{
if (!dspPluginEnabled)
return -1;
running = true;
hUpdateThread = CreateThread(NULL, 0, DspUpdateThread, NULL, 0, &UpdateThreadId);
return (hUpdateThread == INVALID_HANDLE_VALUE);
}
s32 DspLoadLibrary2(wchar_t *fileName, int modNum)
#endif
{
if (!dspPluginEnabled)
return -1;
hLib = LoadLibraryW(fileName);
if (!hLib) {
return 1;
}
pGetHeader = (pWinampDSPGetHeader2)GetProcAddress(hLib, "winampDSPGetHeader2");
if (!pGetHeader) {
FreeLibrary(hLib);
hLib = NULL;
return 1;
}
pHeader = pGetHeader();
pModule = pHeader->getModule(modNum);
if (!pModule) {
pGetHeader = NULL;
pHeader = NULL;
FreeLibrary(hLib);
hLib = NULL;
return -1;
}
pModule->hDllInstance = hLib;
pModule->hwndParent = 0;
pModule->Init(pModule);
return 0;
}
void DspCloseLibrary()
#ifdef USE_A_THREAD
{
if (!dspPluginEnabled)
return;
PostThreadMessage(UpdateThreadId, WM_QUIT, 0, 0);
running = false;
if (WaitForSingleObject(hUpdateThread, 1000) == WAIT_TIMEOUT) {
ConLog("SPU2-X: WARNING: DSP Thread did not close itself in time. Assuming hung. Terminating.\n");
TerminateThread(hUpdateThread, 1);
}
}
void DspCloseLibrary2()
#endif
{
if (!dspPluginEnabled)
return;
if (hLib) {
pModule->Quit(pModule);
FreeLibrary(hLib);
}
pModule = NULL;
pHeader = NULL;
pGetHeader = NULL;
hLib = NULL;
}
int DspProcess(s16 *buffer, int samples)
{
if (!dspPluginEnabled)
return samples;
if (hLib) {
return pModule->ModifySamples(pModule, buffer, samples, 16, 2, SampleRate);
}
return samples;
}
void DspUpdate()
#ifdef USE_A_THREAD
{
}
DWORD WINAPI DspUpdateThread(PVOID param)
{
if (!dspPluginEnabled)
return -1;
if (DspLoadLibrary2(dspPlugin, dspPluginModule))
return -1;
MSG msg;
while (running) {
GetMessage(&msg, 0, 0, 0);
if ((msg.hwnd == NULL) && (msg.message == WM_QUIT)) {
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DspCloseLibrary2();
return 0;
}
#else
{
if (!dspPluginEnabled)
return;
MSG msg;
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
#endif

60
pcsx2/SPU2/Windows/dsp.h Normal file
View File

@ -0,0 +1,60 @@
//GiGaHeRz's SPU2 Driver
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
//
// DSP plugin interface
// notes:
// any window that remains in foreground should optimally pass unused
// keystrokes to the parent (winamp's) window, so that the user
// can still control it. As for storing configuration,
// Configuration data should be stored in <dll directory>\plugin.ini
// (look at the vis plugin for configuration code)
#pragma once
typedef struct winampDSPModule
{
char *description; // description
HWND hwndParent; // parent window (filled in by calling app)
HINSTANCE hDllInstance; // instance handle to this DLL (filled in by calling app)
void (*Config)(struct winampDSPModule *this_mod); // configuration dialog (if needed)
int (*Init)(struct winampDSPModule *this_mod); // 0 on success, creates window, etc (if needed)
// modify waveform samples: returns number of samples to actually write
// (typically numsamples, but no more than twice numsamples, and no less than half numsamples)
// numsamples should always be at least 128. should, but I'm not sure
int (*ModifySamples)(struct winampDSPModule *this_mod, short int *samples, int numsamples, int bps, int nch, int srate);
void (*Quit)(struct winampDSPModule *this_mod); // called when unloading
void *userData; // user data, optional
} winampDSPModule;
typedef struct
{
int version; // DSP_HDRVER
char *description; // description of library
winampDSPModule *(*getModule)(int); // module retrieval function
} winampDSPHeader;
// exported symbols
typedef winampDSPHeader *(*winampDSPGetHeaderType)();
// header version: 0x20 == 0.20 == winamp 2.0
#define DSP_HDRVER 0x20

View File

@ -0,0 +1,73 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Spu2-X.rc
//
#define IDD_CONFIG 9
#define IDD_DEBUG 105
#define IDD_DSOUND 106
#define IDD_WAVEOUT 109
#define IDB_SPU2X_SMALL 116
#define IDD_CONFIG_SOUNDTOUCH 117
#define IDD_CONFIG_DEBUG 118
#define IDD_PORTAUDIO 119
#define IDC_EFFECTS_DISABLE 1001
#define IDC_DUMPREGS 1003
#define IDC_DUMPMEM 1004
#define IDC_DUMPCORE 1005
#define IDC_LOGWAVE 1006
#define IDC_LOGDMA 1007
#define IDC_LOGREGS 1008
#define IDC_DEBUG 1009
#define IDC_DEBUG_ENABLE 1010
#define IDC_INTERPOLATE 1011
#define IDC_DEALIASFILTER 1012
#define IDC_OUTPUT 1013
#define IDC_BUFFERS_SLIDER 1014
#define IDC_SPEAKERS 1015
#define IDC_SPEAKERS_TEXT 1016
#define IDC_MSGKEY 1020
#define IDC_MSGDMA 1021
#define IDC_MSGADMA 1022
#define IDC_MSGVOICE 1023
#define IDC_MSGSHOW 1024
#define IDC_OUTCONF 1028
#define IDC_DSP_ENABLE 1029
#define IDC_DS_DEVICE 1032
#define IDC_PA_DEVICE 1033
#define IDC_DBG_OVERRUNS 1038
#define IDC_DBG_CACHE 1039
#define IDC_LATENCY_SLIDER 1040
#define IDC_LATENCY_LABEL 1041
#define ICD_LR_CENTER_SLIDER 1042
#define IDC_SEQLEN_SLIDER 1043
#define IDC_SEEKWIN_SLIDER 1044
#define IDC_OVERLAP_SLIDER 1045
#define IDC_MSG_PUBLIC_BUILD 1048
#define IDC_RESET_DEFAULTS 1057
#define IDC_OPEN_CONFIG_SOUNDTOUCH 1058
#define IDC_OPEN_CONFIG_DEBUG 1059
#define IDC_GLOBALFOCUS_DISABLE 1060
#define IDC_GLOBALFOCUS_DISABLE2 1061
#define IDC_USE_HARDWARE 1062
#define IDC_COMBO1 1063
#define IDC_SYNCHMODE 1064
#define IDC_DEBUG_OTHERS 1065
#define IDC_DEBUG_VISUAL 1066
#define IDC_VOLUME_LABEL 1067
#define IDC_VOLUME_SLIDER 1068
#define IDC_MINIMIZE 1069
#define IDC_MANUAL 1070
#define IDC_PA_HOSTAPI 1071
#define IDC_LATENCY 1072
#define IDC_EXCLUSIVE 1073
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 120
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1074
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

589
pcsx2/SPU2/defs.h Normal file
View File

@ -0,0 +1,589 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "Mixer.h"
#include "SndOut.h"
// --------------------------------------------------------------------------------------
// SPU2 Memory Indexers
// --------------------------------------------------------------------------------------
#define spu2Rs16(mmem) (*(s16 *)((s8 *)spu2regs + ((mmem)&0x1fff)))
#define spu2Ru16(mmem) (*(u16 *)((s8 *)spu2regs + ((mmem)&0x1fff)))
extern s16 *GetMemPtr(u32 addr);
extern s16 spu2M_Read(u32 addr);
extern void spu2M_Write(u32 addr, s16 value);
extern void spu2M_Write(u32 addr, u16 value);
struct V_VolumeLR
{
static V_VolumeLR Max;
s32 Left;
s32 Right;
V_VolumeLR() = default;
V_VolumeLR(s32 both)
: Left(both)
, Right(both)
{
}
void DebugDump(FILE *dump, const char *title);
};
struct V_VolumeSlide
{
// Holds the "original" value of the volume for this voice, prior to slides.
// (ie, the volume as written to the register)
s16 Reg_VOL;
s32 Value;
s8 Increment;
s8 Mode;
public:
V_VolumeSlide() = default;
V_VolumeSlide(s16 regval, s32 fullvol)
: Reg_VOL(regval)
, Value(fullvol)
, Increment(0)
, Mode(0)
{
}
void Update();
void RegSet(u16 src); // used to set the volume from a register source (16 bit signed)
void DebugDump(FILE *dump, const char *title, const char *nameLR);
};
struct V_VolumeSlideLR
{
static V_VolumeSlideLR Max;
V_VolumeSlide Left;
V_VolumeSlide Right;
public:
V_VolumeSlideLR() = default;
V_VolumeSlideLR(s16 regval, s32 bothval)
: Left(regval, bothval)
, Right(regval, bothval)
{
}
void Update()
{
Left.Update();
Right.Update();
}
void DebugDump(FILE *dump, const char *title);
};
struct V_ADSR
{
union
{
u32 reg32;
struct
{
u16 regADSR1;
u16 regADSR2;
};
struct
{
u32 SustainLevel : 4,
DecayRate : 4,
AttackRate : 7,
AttackMode : 1, // 0 for linear (+lin), 1 for pseudo exponential (+exp)
ReleaseRate : 5,
ReleaseMode : 1, // 0 for linear (-lin), 1 for exponential (-exp)
SustainRate : 7,
SustainMode : 3; // 0 = +lin, 1 = -lin, 2 = +exp, 3 = -exp
};
};
s32 Value; // Ranges from 0 to 0x7fffffff (signed values are clamped to 0) [Reg_ENVX]
u8 Phase; // monitors current phase of ADSR envelope
bool Releasing; // Ready To Release, triggered by Voice.Stop();
public:
bool Calculate();
};
struct V_Voice
{
u32 PlayCycle; // SPU2 cycle where the Playing started
V_VolumeSlideLR Volume;
// Envelope
V_ADSR ADSR;
// Pitch (also Reg_PITCH)
u16 Pitch;
// Loop Start address (also Reg_LSAH/L)
u32 LoopStartA;
// Sound Start address (also Reg_SSAH/L)
u32 StartA;
// Next Read Data address (also Reg_NAXH/L)
u32 NextA;
// Voice Decoding State
s32 Prev1;
s32 Prev2;
// Pitch Modulated by previous voice
bool Modulated;
// Source (Wave/Noise)
bool Noise;
s8 LoopMode;
s8 LoopFlags;
// Sample pointer (19:12 bit fixed point)
s32 SP;
// Sample pointer for Cubic Interpolation
// Cubic interpolation mixes a sample behind Linear, so that it
// can have sample data to either side of the end points from which
// to extrapolate. This SP represents that late sample position.
s32 SPc;
// Previous sample values - used for interpolation
// Inverted order of these members to match the access order in the
// code (might improve cache hits).
s32 PV4;
s32 PV3;
s32 PV2;
s32 PV1;
// Last outputted audio value, used for voice modulation.
s32 OutX;
s32 NextCrest; // temp value for Crest calculation
// SBuffer now points directly to an ADPCM cache entry.
s16 *SBuffer;
// sample position within the current decoded packet.
s32 SCurrent;
// it takes a few ticks for voices to start on the real SPU2?
void QueueStart();
bool Start();
void Stop();
};
// ** Begin Debug-only variables section **
// Separated from the V_Voice struct to improve cache performance of
// the Public Release build.
struct V_VoiceDebug
{
s8 FirstBlock;
s32 SampleData;
s32 PeakX;
s32 displayPeak;
s32 lastSetStartA;
};
struct V_CoreDebug
{
V_VoiceDebug Voices[24];
// Last Transfer Size
u32 lastsize;
// draw adma waveform in the visual debugger
s32 admaWaveformL[0x100];
s32 admaWaveformR[0x100];
// Enabled when a dma write starts, disabled when the visual debugger showed it once
s32 dmaFlag;
};
// Debug tracking information - 24 voices and 2 cores.
extern V_CoreDebug DebugCores[2];
struct V_Reverb
{
s16 IN_COEF_L;
s16 IN_COEF_R;
u32 APF1_SIZE;
u32 APF2_SIZE;
s16 APF1_VOL;
s16 APF2_VOL;
u32 SAME_L_SRC;
u32 SAME_R_SRC;
u32 DIFF_L_SRC;
u32 DIFF_R_SRC;
u32 SAME_L_DST;
u32 SAME_R_DST;
u32 DIFF_L_DST;
u32 DIFF_R_DST;
s16 IIR_VOL;
s16 WALL_VOL;
u32 COMB1_L_SRC;
u32 COMB1_R_SRC;
u32 COMB2_L_SRC;
u32 COMB2_R_SRC;
u32 COMB3_L_SRC;
u32 COMB3_R_SRC;
u32 COMB4_L_SRC;
u32 COMB4_R_SRC;
s16 COMB1_VOL;
s16 COMB2_VOL;
s16 COMB3_VOL;
s16 COMB4_VOL;
u32 APF1_L_DST;
u32 APF1_R_DST;
u32 APF2_L_DST;
u32 APF2_R_DST;
};
struct V_ReverbBuffers
{
s32 SAME_L_SRC;
s32 SAME_R_SRC;
s32 DIFF_R_SRC;
s32 DIFF_L_SRC;
s32 SAME_L_DST;
s32 SAME_R_DST;
s32 DIFF_L_DST;
s32 DIFF_R_DST;
s32 COMB1_L_SRC;
s32 COMB1_R_SRC;
s32 COMB2_L_SRC;
s32 COMB2_R_SRC;
s32 COMB3_L_SRC;
s32 COMB3_R_SRC;
s32 COMB4_L_SRC;
s32 COMB4_R_SRC;
s32 APF1_L_DST;
s32 APF1_R_DST;
s32 APF2_L_DST;
s32 APF2_R_DST;
s32 SAME_L_PRV;
s32 SAME_R_PRV;
s32 DIFF_L_PRV;
s32 DIFF_R_PRV;
s32 APF1_L_SRC;
s32 APF1_R_SRC;
s32 APF2_L_SRC;
s32 APF2_R_SRC;
bool NeedsUpdated;
};
struct V_SPDIF
{
u16 Out;
u16 Info;
u16 Unknown1;
u16 Mode;
u16 Media;
u16 Unknown2;
u16 Protection;
};
struct V_CoreRegs
{
u32 PMON;
u32 NON;
u32 VMIXL;
u32 VMIXR;
u32 VMIXEL;
u32 VMIXER;
u32 ENDX;
u16 MMIX;
u16 STATX;
u16 ATTR;
u16 _1AC;
};
struct V_VoiceGates
{
s16 DryL; // 'AND Gate' for Direct Output to Left Channel
s16 DryR; // 'AND Gate' for Direct Output for Right Channel
s16 WetL; // 'AND Gate' for Effect Output for Left Channel
s16 WetR; // 'AND Gate' for Effect Output for Right Channel
};
struct V_CoreGates
{
union
{
u128 v128;
struct
{
s16 InpL; // Sound Data Input to Direct Output (Left)
s16 InpR; // Sound Data Input to Direct Output (Right)
s16 SndL; // Voice Data to Direct Output (Left)
s16 SndR; // Voice Data to Direct Output (Right)
s16 ExtL; // External Input to Direct Output (Left)
s16 ExtR; // External Input to Direct Output (Right)
};
};
};
struct VoiceMixSet
{
static const VoiceMixSet Empty;
StereoOut32 Dry, Wet;
VoiceMixSet() {}
VoiceMixSet(const StereoOut32 &dry, const StereoOut32 &wet)
: Dry(dry)
, Wet(wet)
{
}
};
struct V_Core
{
static const uint NumVoices = 24;
int Index; // Core index identifier.
// Voice Gates -- These are SSE-related values, and must always be
// first to ensure 16 byte alignment
V_VoiceGates VoiceGates[NumVoices];
V_CoreGates DryGate;
V_CoreGates WetGate;
V_VolumeSlideLR MasterVol; // Master Volume
V_VolumeLR ExtVol; // Volume for External Data Input
V_VolumeLR InpVol; // Volume for Sound Data Input
V_VolumeLR FxVol; // Volume for Output from Effects
V_Voice Voices[NumVoices];
u32 IRQA; // Interrupt Address
u32 TSA; // DMA Transfer Start Address
bool IRQEnable; // Interrupt Enable
bool FxEnable; // Effect Enable
bool Mute; // Mute
bool AdmaInProgress;
s8 DMABits; // DMA related?
s8 NoiseClk; // Noise Clock
u16 AutoDMACtrl; // AutoDMA Status
s32 DMAICounter; // DMA Interrupt Counter
u32 InputDataLeft; // Input Buffer
u32 InputPosRead;
u32 InputPosWrite;
u32 InputDataProgress;
V_Reverb Revb; // Reverb Registers
V_ReverbBuffers RevBuffers; // buffer pointers for reverb, pre-calculated and pre-clipped.
u32 EffectsStartA;
u32 EffectsEndA;
u32 ExtEffectsStartA;
u32 ExtEffectsEndA;
u32 ReverbX;
// Current size of and position of the effects buffer. Pre-caculated when the effects start
// or end position registers are written. CAN BE NEGATIVE OR ZERO, in which
// case reverb should be disabled.
s32 EffectsBufferSize;
u32 EffectsBufferStart;
V_CoreRegs Regs; // Registers
// Preserves the channel processed last cycle
StereoOut32 LastEffect;
u8 CoreEnabled;
u8 AttrBit0;
u8 DmaMode;
// new dma only
bool DmaStarted;
u32 AutoDmaFree;
// old dma only
u16 *DMAPtr;
u32 MADR;
u32 TADR;
u32 KeyOn; // not the KON register (though maybe it is)
// psxmode caches
u16 psxSoundDataTransferControl;
u16 psxSPUSTAT;
// HACK -- This is a temp buffer which is (or isn't?) used to circumvent some memory
// corruption that originates elsewhere in the plugin. >_< The actual ADMA buffer
// is an area mapped to SPU2 main memory.
//s16 ADMATempBuffer[0x1000];
// ----------------------------------------------------------------------------------
// V_Core Methods
// ----------------------------------------------------------------------------------
// uninitialized constructor
V_Core()
: Index(-1)
, DMAPtr(NULL)
{
}
V_Core(int idx); // our badass constructor
~V_Core() throw();
void Init(int index);
void UpdateEffectsBufferSize();
void AnalyzeReverbPreset();
s32 EffectsBufferIndexer(s32 offset) const;
void WriteRegPS1(u32 mem, u16 value);
u16 ReadRegPS1(u32 mem);
// --------------------------------------------------------------------------------------
// Mixer Section
// --------------------------------------------------------------------------------------
StereoOut32 Mix(const VoiceMixSet &inVoices, const StereoOut32 &Input, const StereoOut32 &Ext);
void Reverb_AdvanceBuffer();
StereoOut32 DoReverb(const StereoOut32 &Input);
s32 RevbGetIndexer(s32 offset);
StereoOut32 ReadInput();
StereoOut32 ReadInput_HiFi();
// --------------------------------------------------------------------------
// DMA Section
// --------------------------------------------------------------------------
// Returns the index of the DMA channel (4 for Core 0, or 7 for Core 1)
int GetDmaIndex() const
{
return (Index == 0) ? 4 : 7;
}
// returns either '4' or '7'
char GetDmaIndexChar() const
{
return 0x30 + GetDmaIndex();
}
__forceinline u16 DmaRead()
{
const u16 ret = (u16)spu2M_Read(TSA);
++TSA;
TSA &= 0xfffff;
return ret;
}
__forceinline void DmaWrite(u16 value)
{
spu2M_Write(TSA, value);
++TSA;
TSA &= 0xfffff;
}
void LogAutoDMA(FILE *fp);
s32 NewDmaRead(u32 *data, u32 bytesLeft, u32 *bytesProcessed);
s32 NewDmaWrite(u32 *data, u32 bytesLeft, u32 *bytesProcessed);
void NewDmaInterrupt();
// old dma only
void DoDMAwrite(u16 *pMem, u32 size);
void DoDMAread(u16 *pMem, u32 size);
void AutoDMAReadBuffer(int mode);
void StartADMAWrite(u16 *pMem, u32 sz);
void PlainDMAWrite(u16 *pMem, u32 sz);
};
extern V_Core Cores[2];
extern V_SPDIF Spdif;
// Output Buffer Writing Position (the same for all data);
extern s16 OutPos;
// Input Buffer Reading Position (the same for all data);
extern s16 InputPos;
// SPU Mixing Cycles ("Ticks mixed" counter)
extern u32 Cycles;
extern s16 *spu2regs;
extern s16 *_spu2mem;
extern int PlayMode;
extern void SetIrqCall(int core);
extern void StartVoices(int core, u32 value);
extern void StopVoices(int core, u32 value);
extern void InitADSR();
extern void CalculateADSR(V_Voice &vc);
extern void UpdateSpdifMode();
namespace Savestate
{
struct DataBlock;
extern s32 __fastcall FreezeIt(DataBlock &spud);
extern s32 __fastcall ThawIt(DataBlock &spud);
extern s32 __fastcall SizeIt();
}
// --------------------------------------------------------------------------------------
// ADPCM Decoder Cache
// --------------------------------------------------------------------------------------
// The SPU2 has a dynamic memory range which is used for several internal operations, such as
// registers, CORE 1/2 mixing, AutoDMAs, and some other fancy stuff. We exclude this range
// from the cache here:
static const s32 SPU2_DYN_MEMLINE = 0x2800;
// 8 short words per encoded PCM block. (as stored in SPU2 ram)
static const int pcm_WordsPerBlock = 8;
// number of cachable ADPCM blocks (any blocks above the SPU2_DYN_MEMLINE)
static const int pcm_BlockCount = 0x100000 / pcm_WordsPerBlock;
// 28 samples per decoded PCM block (as stored in our cache)
static const int pcm_DecodedSamplesPerBlock = 28;
struct PcmCacheEntry
{
bool Validated;
s16 Sampledata[pcm_DecodedSamplesPerBlock];
};
extern PcmCacheEntry *pcm_cache_data;

195
pcsx2/SPU2/regs.h Normal file
View File

@ -0,0 +1,195 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define SPU2_CORE0 0x00000000
#define SPU2_CORE1 0x00000400
#define SPU2_VP(voice) ((voice)*16)
#define SPU2_VA(voice) ((voice)*12)
#define REG_VP_VOLL 0x0000 // Voice Volume Left
#define REG_VP_VOLR 0x0002 // Voice Volume Right
#define REG_VP_PITCH 0x0004 // Pitch
#define REG_VP_ADSR1 0x0006 // Envelope 1 (Attack-Decay-Sustain-Release)
#define REG_VP_ADSR2 0x0008 // Envelope 2 (Attack-Decay-Sustain-Release)
#define REG_VP_ENVX 0x000A // Current Envelope
#define REG_VP_VOLXL 0x000C // Current Voice Volume Left
#define REG_VP_VOLXR 0x000E // Current Voice Volume Right
// .. repeated for each voice ..
#define REG_S_PMON 0x0180 // Pitch Modulation Spec.
#define REG_S_NON 0x0184 // Alloc Noise Generator
#define REG_S_VMIXL 0x0188 // Voice Output Mix Left (Dry)
#define REG_S_VMIXEL 0x018C // Voice Output Mix Left (Wet)
#define REG_S_VMIXR 0x0190 // Voice Output Mix Right (Dry)
#define REG_S_VMIXER 0x0194 // Voice Output Mix Right (Wet)
#define REG_P_MMIX 0x0198 // Output Spec. After Voice Mix
#define REG_C_ATTR 0x019A // Core X Attrib
#define REG_A_IRQA 0x019C // Interrupt Address Spec.
#define REG_S_KON 0x01A0 // Key On 0/1
#define REG_S_KOFF 0x01A4 // Key Off 0/1
#define REG_A_TSA 0x01A8 // Transfer starting address
#define REG__1AC 0x01AC // Transfer data
#define REG__1AE 0x01AE
#define REG_S_ADMAS 0x01B0 // AutoDMA Status
// 1b2, 1b4, 1b6, 1b8, 1ba, 1bc, 1be are unknown
#define REG_VA_SSA 0x01C0 // Waveform data starting address
#define REG_VA_LSAX 0x01C4 // Loop point address
#define REG_VA_NAX 0x01C8 // Waveform data that should be read next
// .. repeated for each voice ..
#define REG_A_ESA 0x02E0 //Address: Top address of working area for effects processing
#define R_APF1_SIZE 0x02E4 // Feedback Source A
#define R_APF2_SIZE 0x02E8 // Feedback Source B
#define R_SAME_L_DST 0x02EC
#define R_SAME_R_DST 0x02F0
#define R_COMB1_L_SRC 0x02F4
#define R_COMB1_R_SRC 0x02F8
#define R_COMB2_L_SRC 0x02FC
#define R_COMB2_R_SRC 0x0300
#define R_SAME_L_SRC 0x0304
#define R_SAME_R_SRC 0x0308
#define R_DIFF_L_DST 0x030C
#define R_DIFF_R_DST 0x0310
#define R_COMB3_L_SRC 0x0314
#define R_COMB3_R_SRC 0x0318
#define R_COMB4_L_SRC 0x031C
#define R_COMB4_R_SRC 0x0320
#define R_DIFF_L_SRC 0x0324 // Some sources have R_DIFF_R_SRC and R_DIFF_L_SRC swapped ><
#define R_DIFF_R_SRC 0x0328
#define R_APF1_L_DST 0x032C
#define R_APF1_R_DST 0x0330
#define R_APF2_L_DST 0x0334
#define R_APF2_R_DST 0x0338
#define REG_A_EEA 0x033C // Address: End address of working area for effects processing (upper part of address only!)
#define REG_S_ENDX 0x0340 // End Point passed flag
#define REG_P_STATX 0x0344 // Status register?
// 0x346 .. 0x3fe are unknown (unused?)
// core 1 has the same registers with 0x400 added.
// core 1 ends at 0x746
// 0x746 .. 0x75e are unknown
// "Different" register area
#define REG_P_MVOLL 0x0760 // Master Volume Left
#define REG_P_MVOLR 0x0762 // Master Volume Right
#define REG_P_EVOLL 0x0764 // Effect Volume Left
#define REG_P_EVOLR 0x0766 // Effect Volume Right
#define REG_P_AVOLL 0x0768 // Core External Input Volume Left (Only Core 1)
#define REG_P_AVOLR 0x076A // Core External Input Volume Right (Only Core 1)
#define REG_P_BVOLL 0x076C // Sound Data Volume Left
#define REG_P_BVOLR 0x076E // Sound Data Volume Right
#define REG_P_MVOLXL 0x0770 // Current Master Volume Left
#define REG_P_MVOLXR 0x0772 // Current Master Volume Right
#define R_IIR_VOL 0x0774
#define R_COMB1_VOL 0x0776
#define R_COMB2_VOL 0x0778
#define R_COMB3_VOL 0x077A
#define R_COMB4_VOL 0x077C
#define R_WALL_VOL 0x077E
#define R_APF1_VOL 0x0780
#define R_APF2_VOL 0x0782
#define R_IN_COEF_L 0x0784
#define R_IN_COEF_R 0x0786
// values repeat for core1
// End OF "Different" register area
// SPDIF interface
#define SPDIF_OUT 0x07C0 // SPDIF Out: OFF/'PCM'/Bitstream/Bypass
#define SPDIF_IRQINFO 0x07C2
#define SPDIF_MODE 0x07C6
#define SPDIF_MEDIA 0x07C8 // SPDIF Media: 'CD'/DVD
#define SPDIF_PROTECT 0x07CC // SPDIF Copy Protection
/*********************************************************************
Core attributes (SD_C)
bit 1 - Unknown (this bit is sometimes set)
bit 2..3 - Unknown (usually never set)
bit 4..5 - DMA related
bit 6 - IRQ? DMA mode? wtf?
bit 7 - effect enable (reverb enable)
bit 8 - IRQ enable?
bit 9..14 - noise clock
bit 15 - mute
bit 16 - reset
*********************************************************************/
#define SPDIF_OUT_OFF 0x0000 // no spdif output
#define SPDIF_OUT_PCM 0x0020 // encode spdif from spu2 pcm output
#define SPDIF_OUT_BYPASS 0x0100 // bypass spu2 processing
#define SPDIF_MODE_BYPASS_BITSTREAM 0x0002 // bypass mode for digital bitstream data
#define SPDIF_MODE_BYPASS_PCM 0x0000 // bypass mode for pcm data (using analog output)
#define SPDIF_MODE_MEDIA_CD 0x0800 // source media is a CD
#define SPDIF_MODE_MEDIA_DVD 0x0000 // source media is a DVD
#define SPDIF_MEDIA_CDVD 0x0200
#define SPDIF_MEDIA_400 0x0000
#define SPDIF_PROTECT_NORMAL 0x0000 // spdif stream is not protected
#define SPDIF_PROTECT_PROHIBIT 0x8000 // spdif stream can't be copied
/********************************************************************/
#define VOICE_PARAM_VOLL 0x0 // Voice Volume Left
#define VOICE_PARAM_VOLR 0x2 // Voice Volume Right
#define VOICE_PARAM_PITCH 0x4 // Pitch
#define VOICE_PARAM_ADSR1 0x6 // Envelope 1 (Attack-Delay-Sustain-Release)
#define VOICE_PARAM_ADSR2 0x8 // Envelope 2 (Attack-Delay-Sustain-Release)
#define VOICE_PARAM_ENVX 0xA // Current Envelope
#define VOICE_PARAM_VOLXL 0xC // Current Voice Volume Left
#define VOICE_PARAM_VOLXR 0xE // Current Voice Volume Right
/********************************************************************/
#define VOICE_ADDR_SSA 0x0 // Waveform data starting address
#define VOICE_ADDR_LSAX 0x4 // Loop point address
#define VOICE_ADDR_NAX 0x8 // Waveform data that should be read next
// --------------------------------------------------------------------------------------
// SPU2-X Register Table LUT
// --------------------------------------------------------------------------------------
#define U16P(x) ((u16 *)&(x))
// Returns the hiword of a 32 bit integer.
#define U16P_HI(x) (((u16 *)&(x)) + 1)
extern u16 *regtable[0x401];
extern u16 const *const regtable_original[0x401];

110
pcsx2/SPU2/spdif.h Normal file
View File

@ -0,0 +1,110 @@
//GiGaHeRz's SPU2 Driver
//Copyright (c) 2003-2008, David Quintana <gigaherz@gmail.com>
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
//
#pragma once
#ifndef u32
typedef unsigned int u32;
#endif
/*
Preamble cell-order cell-order
(last cell "0") (last cell "1")
----------------------------------------------
"B" 11101000 00010111
"M" 11100010 00011101
"W" 11100100 00011011
Only the lower 4 bits are used.
Preamble B: Marks a word containing data for channel A (left)
at the start of the data-block.
Preamble M: Marks a word with data for channel A that isn't
at the start of the data-block.
Preamble W: Marks a word containing data for channel B.
(right, for stereo). When using more than 2
channels, this could also be any other channel
(except for A).
bits meaning
----------------------------------------------------------
0-3 Preamble (see above; special structure)
4-7 Auxillary-audio-databits
8-27 Sample
(A 24-bit sample can be used (using bits 4-27).
A CD-player uses only 16 bits, so only bits
13 (LSB) to 27 (MSB) are used. Bits 4-12 are
set to 0).
28 Validity
(When this bit is set, the sample should not
be used by the receiver. A CD-player uses
the 'error-flag' to set this bit).
29 Subcode-data
30 Channel-status-information
31 Parity (bit 0-3 are not included)
*/
typedef struct _subframe
{
u32 preamble : 4;
u32 aux_data : 4;
u32 snd_data : 20;
u32 validity : 1;
u32 subcode : 1;
u32 chstatus : 1;
u32 parity : 1;
} subframe;
/*
bit meaning
-------------------------------------------------------------
0-3 controlbits:
bit 0: 0 (is set to 1 during 4 channel transmission)
bit 1: 0=Digital audio, 1=Non-audio (reserved to be 0 on old S/PDIF specs)
bit 2: copy-protection. Copying is allowed when this bit is set.
bit 3: is set when pre-emphasis is used.
4-7 0 (reserved)
9-15 catagory-code:
0 = common 2-channel format
1 = 2-channel CD-format
(set by a CD-player when a subcode is transmitted)
2 = 2-channel PCM-encoder-decoder format
others are not used
19-191 0 (reserved)
*/
typedef struct _chstatus
{
u8 ctrlbits : 4;
u8 reservd1 : 4;
u8 category;
u8 reservd2[22];
} chstatus:

147
pcsx2/SPU2/spu2freeze.cpp Normal file
View File

@ -0,0 +1,147 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "PS2E-spu2.h" // hopefully temporary, until I resolve lClocks depdendency
namespace Savestate
{
// Arbitrary ID to identify SPU2-X saves.
static const u32 SAVE_ID = 0x1227521;
// versioning for saves.
// Increment this when changes to the savestate system are made.
static const u32 SAVE_VERSION = 0x000e;
static void wipe_the_cache()
{
memset(pcm_cache_data, 0, pcm_BlockCount * sizeof(PcmCacheEntry));
}
}
struct Savestate::DataBlock
{
u32 spu2id; // SPU2-X state identifier lets ZeroGS/PeopsSPU2 know this isn't their state)
u8 unkregs[0x10000]; // SPU2 raw register memory
u8 mem[0x200000]; // SPU2 raw sample memory
u32 version; // SPU2-X version identifier
V_Core Cores[2];
V_SPDIF Spdif;
s16 OutPos;
s16 InputPos;
u32 Cycles;
u32 lClocks;
int PlayMode;
};
s32 __fastcall Savestate::FreezeIt(DataBlock &spud)
{
spud.spu2id = SAVE_ID;
spud.version = SAVE_VERSION;
pxAssertMsg(spu2regs && _spu2mem, "Looks like PCSX2 is trying to savestate while pluigns are shut down. That's a no-no! It shouldn't crash, but the savestate will probably be corrupted.");
if (spu2regs != NULL)
memcpy(spud.unkregs, spu2regs, sizeof(spud.unkregs));
if (_spu2mem != NULL)
memcpy(spud.mem, _spu2mem, sizeof(spud.mem));
memcpy(spud.Cores, Cores, sizeof(Cores));
memcpy(&spud.Spdif, &Spdif, sizeof(Spdif));
spud.OutPos = OutPos;
spud.InputPos = InputPos;
spud.Cycles = Cycles;
spud.lClocks = lClocks;
spud.PlayMode = PlayMode;
// note: Don't save the cache. PCSX2 doesn't offer a safe method of predicting
// the required size of the savestate prior to saving, plus this is just too
// "implementation specific" for the intended spec of a savestate. Let's just
// force the user to rebuild their cache instead.
return 0;
}
s32 __fastcall Savestate::ThawIt(DataBlock &spud)
{
if (spud.spu2id != SAVE_ID || spud.version < SAVE_VERSION) {
fprintf(stderr, "\n*** SPU2-X Warning:\n");
if (spud.spu2id == SAVE_ID)
fprintf(stderr, "\tSavestate version is from an older version of this plugin.\n");
else
fprintf(stderr, "\tThe savestate you are trying to load was not made with this plugin.\n");
fprintf(stderr,
"\tAudio may not recover correctly. Save your game to memorycard, reset,\n\n"
"\tand then continue from there.\n\n");
// Do *not* reset the cores.
// We'll need some "hints" as to how the cores should be initialized, and the
// only way to get that is to use the game's existing core settings and hope
// they kinda match the settings for the savestate (IRQ enables and such).
// adpcm cache : Clear all the cache flags and buffers.
wipe_the_cache();
} else {
SndBuffer::ClearContents();
pxAssertMsg(spu2regs && _spu2mem, "Looks like PCSX2 is trying to loadstate while pluigns are shut down. That's a no-no! It shouldn't crash, but the savestate will probably be corrupted.");
// base stuff
if (spu2regs)
memcpy(spu2regs, spud.unkregs, sizeof(spud.unkregs));
if (_spu2mem)
memcpy(_spu2mem, spud.mem, sizeof(spud.mem));
memcpy(Cores, spud.Cores, sizeof(Cores));
memcpy(&Spdif, &spud.Spdif, sizeof(Spdif));
OutPos = spud.OutPos;
InputPos = spud.InputPos;
Cycles = spud.Cycles;
lClocks = spud.lClocks;
PlayMode = spud.PlayMode;
wipe_the_cache();
// Go through the V_Voice structs and recalculate SBuffer pointer from
// the NextA setting.
for (int c = 0; c < 2; c++) {
for (int v = 0; v < 24; v++) {
const int cacheIdx = Cores[c].Voices[v].NextA / pcm_WordsPerBlock;
Cores[c].Voices[v].SBuffer = pcm_cache_data[cacheIdx].Sampledata;
}
}
// HACKFIX!! DMAPtr can be invalid after a savestate load, so force it to NULL and
// ignore it on any pending ADMA writes. (the DMAPtr concept used to work in old VM
// editions of PCSX2 with fixed addressing, but new PCSX2s have dynamic memory
// addressing).
Cores[0].DMAPtr = Cores[1].DMAPtr = NULL;
}
return 0;
}
s32 __fastcall Savestate::SizeIt()
{
return sizeof(DataBlock);
}

1828
pcsx2/SPU2/spu2sys.cpp Normal file

File diff suppressed because it is too large Load Diff

503
pcsx2/SPU2/wx/wxConfig.cpp Normal file
View File

@ -0,0 +1,503 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Global.h"
#include "wxConfig.h"
MixerTab::MixerTab(wxWindow* parent)
: wxPanel(parent, wxID_ANY)
{
auto* top_box = new wxBoxSizer(wxVERTICAL);
// Mixing Settings
top_box->Add(new wxStaticText(this, wxID_ANY, "Interpolation"), wxSizerFlags().Centre());
m_interpolation.Add("Nearest (Fastest/bad quality)");
m_interpolation.Add("Linear (Simple/okay sound)");
m_interpolation.Add("Cubic (Artificial highs)");
m_interpolation.Add("Hermite (Better highs)");
m_interpolation.Add("Catmull-Rom (PS2-like/slow)");
m_inter_select = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_interpolation);
effect_check = new wxCheckBox(this, wxID_ANY, "Disable Effects Processing (Speedup)");
dealias_check = new wxCheckBox(this, wxID_ANY, "Use the de-alias filter (Overemphasizes the highs) ");
// Latency Slider
const int min_latency = SynchMode == 0 ? LATENCY_MIN_TIMESTRETCH : LATENCY_MIN;
m_latency_box = new wxStaticBoxSizer(wxVERTICAL, this, "Latency");
m_latency_slider = new wxSlider(this, wxID_ANY, SndOutLatencyMS, min_latency, LATENCY_MAX, wxDefaultPosition, wxDefaultSize, wxSL_LABELS);
m_latency_box->Add(m_latency_slider, wxSizerFlags().Expand());
// Volume Slider
m_volume_box = new wxStaticBoxSizer(wxVERTICAL, this, "Volume");
m_volume_slider = new wxSlider(this, wxID_ANY, FinalVolume * 100, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_LABELS);
m_volume_box->Add(m_volume_slider, wxSizerFlags().Expand());
m_audio_box = new wxBoxSizer(wxVERTICAL);
m_audio_box->Add(new wxStaticText(this, wxID_ANY, "Audio Expansion Mode"), wxSizerFlags().Centre());
m_audio.Add("Stereo (None, Default)");
m_audio.Add("Quadrafonic");
m_audio.Add("Surround 5.1");
m_audio.Add("Surround 7.1");
m_audio_select = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_audio);
m_audio_box->Add(m_audio_select, wxSizerFlags().Expand());
top_box->Add(m_inter_select, wxSizerFlags().Centre());
top_box->Add(effect_check, wxSizerFlags().Centre());
top_box->Add(dealias_check, wxSizerFlags().Centre());
top_box->Add(m_latency_box, wxSizerFlags().Expand());
top_box->Add(m_volume_box, wxSizerFlags().Expand());
top_box->Add(m_audio_box, wxSizerFlags().Expand());
SetSizerAndFit(top_box);
}
void MixerTab::Load()
{
m_inter_select->SetSelection(Interpolation);
effect_check->SetValue(EffectsDisabled);
dealias_check->SetValue(postprocess_filter_dealias);
m_audio_select->SetSelection(numSpeakers);
m_volume_slider->SetValue(FinalVolume * 100);
m_latency_slider->SetValue(SndOutLatencyMS);
}
void MixerTab::Save()
{
Interpolation = m_inter_select->GetSelection();
EffectsDisabled = effect_check->GetValue();
postprocess_filter_dealias = dealias_check->GetValue();
numSpeakers = m_audio_select->GetSelection();
FinalVolume = m_volume_slider->GetValue() / 100.0;
SndOutLatencyMS = m_latency_slider->GetValue();
}
void MixerTab::Update()
{
}
void MixerTab::CallUpdate(wxCommandEvent& /*event*/)
{
Update();
}
SyncTab::SyncTab(wxWindow* parent)
: wxPanel(parent, wxID_ANY)
{
auto* top_box = new wxBoxSizer(wxVERTICAL);
top_box->Add(new wxStaticText(this, wxID_ANY, "Synchronization"), wxSizerFlags().Centre());
m_sync.Add("TimeStretch (Recommended)");
m_sync.Add("Async Mix (Breaks some games!)");
m_sync.Add("None (Audio can skip.)");
m_sync_select = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_sync);
auto* adv_box = new wxStaticBoxSizer(wxVERTICAL, this, "Advanced");
auto* babble_label = new wxStaticText(this, wxID_ANY, \
"For fine-tuning time stretching.\n"\
"Larger is better for slowdown, && smaller for speedup (60+ fps).\n"\
"All options in microseconds.",\
wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL);
babble_label->Wrap(300);
adv_box->Add(babble_label, wxSizerFlags().Centre());
auto* soundtouch_grid = new wxFlexGridSizer(2, 10, 50);
seq_spin = new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT, SoundtouchCfg::SequenceLen_Min, SoundtouchCfg::SequenceLen_Max, SoundtouchCfg::SequenceLenMS);
auto* seq_label = new wxStaticText(this, wxID_ANY, "Sequence Length");
seek_spin = new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT, SoundtouchCfg::SeekWindow_Min, SoundtouchCfg::SeekWindow_Max, SoundtouchCfg::SeekWindowMS);
auto* seek_label = new wxStaticText(this, wxID_ANY, "Seek Window Size");
overlap_spin = new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT, SoundtouchCfg::Overlap_Min, SoundtouchCfg::Overlap_Max, SoundtouchCfg::OverlapMS);
auto* overlap_label = new wxStaticText(this, wxID_ANY, "Overlap");
soundtouch_grid->Add(seq_label, wxSizerFlags().Border(wxALL, 5));
soundtouch_grid->Add(seq_spin, wxSizerFlags().Expand().Right());
soundtouch_grid->Add(seek_label, wxSizerFlags().Border(wxALL, 5));
soundtouch_grid->Add(seek_spin, wxSizerFlags().Expand().Right());
soundtouch_grid->Add(overlap_label, wxSizerFlags().Border(wxALL, 5));
soundtouch_grid->Add(overlap_spin, wxSizerFlags().Expand().Right());
adv_box->Add(soundtouch_grid);
reset_button = new wxButton(this, wxID_ANY, "Reset To Defaults");
adv_box->Add(reset_button, wxSizerFlags().Centre().Border(wxALL, 5));
top_box->Add(m_sync_select, wxSizerFlags().Centre());
top_box->Add(adv_box, wxSizerFlags().Expand().Centre());
SetSizerAndFit(top_box);
Bind(wxEVT_BUTTON, &SyncTab::OnButtonClicked, this);
Bind(wxEVT_CHOICE, &SyncTab::CallUpdate, this);
}
void SyncTab::Load()
{
m_sync_select->SetSelection(SynchMode);
SoundtouchCfg::ReadSettings();
seq_spin->SetValue(SoundtouchCfg::SequenceLenMS);
seek_spin->SetValue(SoundtouchCfg::SeekWindowMS);
overlap_spin->SetValue(SoundtouchCfg::OverlapMS);
}
void SyncTab::Save()
{
SynchMode = m_sync_select->GetSelection();
SoundtouchCfg::SequenceLenMS = seq_spin->GetValue();
SoundtouchCfg::SeekWindowMS = seek_spin->GetValue();
SoundtouchCfg::OverlapMS = overlap_spin->GetValue();
SoundtouchCfg::WriteSettings();
}
void SyncTab::Update()
{
seq_spin->Enable(m_sync_select->GetCurrentSelection() == 0);
seek_spin->Enable(m_sync_select->GetCurrentSelection() == 0);
overlap_spin->Enable(m_sync_select->GetCurrentSelection() == 0);
}
void SyncTab::CallUpdate(wxCommandEvent& /*event*/)
{
Update();
}
void SyncTab::OnButtonClicked(wxCommandEvent& event)
{
seq_spin->SetValue(30);
seek_spin->SetValue(20);
overlap_spin->SetValue(10);
}
DebugTab::DebugTab(wxWindow* parent)
: wxPanel(parent, wxID_ANY)
{
auto* top_box = new wxBoxSizer(wxVERTICAL);
debug_check = new wxCheckBox(this, wxID_ANY, "Enable Debug Options");
show_check = new wxCheckBox(this, wxID_ANY, "Show in console");
top_box->Add(debug_check, wxSizerFlags().Expand());
top_box->Add(show_check);
m_console_box = new wxStaticBoxSizer(wxVERTICAL, this, "Events");
auto* console_grid = new wxFlexGridSizer(2, 0, 0);
key_check = new wxCheckBox(this, wxID_ANY, "Key On/Off");
voice_check = new wxCheckBox(this, wxID_ANY, "Voice Stop");
dma_check = new wxCheckBox(this, wxID_ANY, "DMA Operations");
autodma_check = new wxCheckBox(this, wxID_ANY, "AutoDMA Operations");
buffer_check = new wxCheckBox(this, wxID_ANY, "Buffer Over/Underruns");
adpcm_check = new wxCheckBox(this, wxID_ANY, "ADPCM Cache");
console_grid->Add(key_check);
console_grid->Add(voice_check);
console_grid->Add(dma_check);
console_grid->Add(autodma_check);
console_grid->Add(buffer_check);
console_grid->Add(adpcm_check);
m_console_box->Add(console_grid);
m_log_only_box = new wxStaticBoxSizer(wxVERTICAL, this, "Log Only");
auto* log_grid = new wxFlexGridSizer(2, 0, 0);
dma_actions_check = new wxCheckBox(this, wxID_ANY, "Register/DMA Actions");
dma_writes_check = new wxCheckBox(this, wxID_ANY, "DMA Writes");
auto_output_check = new wxCheckBox(this, wxID_ANY, "Audio Output");
log_grid->Add(dma_actions_check);
log_grid->Add(dma_writes_check);
log_grid->Add(auto_output_check);
m_log_only_box->Add(log_grid);
dump_box = new wxStaticBoxSizer(wxVERTICAL, this, "Dump on Close");
auto* dump_grid = new wxFlexGridSizer(2, 0, 0);
core_voice_check = new wxCheckBox(this, wxID_ANY, "Core && Voice Stats");
memory_check = new wxCheckBox(this, wxID_ANY, "Memory Contents");
register_check = new wxCheckBox(this, wxID_ANY, "Register Data");
dump_grid->Add(core_voice_check);
dump_grid->Add(memory_check);
dump_grid->Add(register_check);
dump_box->Add(dump_grid);
top_box->Add(m_console_box, wxSizerFlags().Expand());
top_box->Add(m_log_only_box, wxSizerFlags().Expand());
top_box->Add(dump_box, wxSizerFlags().Expand());
SetSizerAndFit(top_box);
Bind(wxEVT_CHECKBOX, &DebugTab::CallUpdate, this);
}
void DebugTab::Load()
{
debug_check->SetValue(DebugEnabled);
show_check->SetValue(_MsgToConsole);
key_check->SetValue(_MsgKeyOnOff);
voice_check->SetValue(_MsgVoiceOff);
dma_check->SetValue(_MsgDMA);
autodma_check->SetValue(_MsgAutoDMA);
buffer_check->SetValue(_MsgOverruns);
adpcm_check->SetValue(_MsgCache);
dma_actions_check->SetValue(_AccessLog);
dma_writes_check->SetValue(_DMALog);
auto_output_check->SetValue(_WaveLog);
core_voice_check->SetValue(_CoresDump);
memory_check->SetValue(_MemDump);
register_check->SetValue(_RegDump);
Update();
}
void DebugTab::Save()
{
DebugEnabled = debug_check->GetValue();
_MsgToConsole = show_check->GetValue();
_MsgKeyOnOff = key_check->GetValue();
_MsgVoiceOff = voice_check->GetValue();
_MsgDMA = dma_check->GetValue();
_MsgAutoDMA = autodma_check->GetValue();
_MsgOverruns = buffer_check->GetValue();
_MsgCache = adpcm_check->GetValue();
_AccessLog = dma_actions_check->GetValue();
_DMALog = dma_writes_check->GetValue();
_WaveLog = auto_output_check->GetValue();
_CoresDump = core_voice_check->GetValue();
_MemDump = memory_check->GetValue();
_RegDump = register_check->GetValue();
}
void DebugTab::Update()
{
if (debug_check->GetValue())
{
show_check->Enable();
key_check->Enable();
voice_check->Enable();
dma_check->Enable();
autodma_check->Enable();
buffer_check->Enable();
adpcm_check->Enable();
dma_actions_check->Enable();
dma_writes_check->Enable();
auto_output_check->Enable();
core_voice_check->Enable();
memory_check->Enable();
register_check->Enable();
}
else
{
show_check->Disable();
key_check->Disable();
voice_check->Disable();
dma_check->Disable();
autodma_check->Disable();
buffer_check->Disable();
adpcm_check->Disable();
dma_actions_check->Disable();
dma_writes_check->Disable();
auto_output_check->Disable();
core_voice_check->Disable();
memory_check->Disable();
register_check->Disable();
}
}
void DebugTab::CallUpdate(wxCommandEvent& /*event*/)
{
Update();
}
Dialog::Dialog()
: wxDialog(nullptr, wxID_ANY, "SPU2 Config", wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
{
m_top_box = new wxBoxSizer(wxVERTICAL);
auto* module_box = new wxBoxSizer(wxVERTICAL);
// Module
module_box->Add(new wxStaticText(this, wxID_ANY, "Module"), wxSizerFlags().Centre());
m_module.Add("No Sound (Emulate SPU2 only)");
#ifdef SPU2X_PORTAUDIO
m_module.Add("PortAudio (Cross-platform)");
#endif
m_module.Add("SDL Audio (Recommended for PulseAudio)");
m_module_select = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_module);
module_box->Add(m_module_select, wxSizerFlags().Centre());
#ifdef SPU2X_PORTAUDIO
// Portaudio
m_portaudio_box = new wxBoxSizer(wxVERTICAL);
m_portaudio_text = new wxStaticText(this, wxID_ANY, "Portaudio API");
m_portaudio_box->Add(m_portaudio_text, wxSizerFlags().Centre());
#ifdef __linux__
m_portaudio.Add("ALSA (recommended)");
m_portaudio.Add("OSS (legacy)");
m_portaudio.Add("JACK");
#elif defined(__APPLE__)
m_portaudio.Add("CoreAudio");
#else
m_portaudio.Add("OSS");
#endif
m_portaudio_select = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_portaudio);
m_portaudio_box->Add(m_portaudio_select, wxSizerFlags().Centre());
#endif
// SDL
m_sdl_box = new wxBoxSizer(wxVERTICAL);
m_sdl_text = new wxStaticText(this, wxID_ANY, "SDL API");
m_sdl_box->Add(m_sdl_text, wxSizerFlags().Centre());
for (int i = 0; i < SDL_GetNumAudioDrivers(); ++i)
m_sdl.Add(SDL_GetAudioDriver(i));
m_sdl_select = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_sdl);
m_sdl_box->Add(m_sdl_select, wxSizerFlags().Centre());
#ifdef SPU2X_PORTAUDIO
module_box->Add(m_portaudio_box, wxSizerFlags().Expand());
#endif
module_box->Add(m_sdl_box, wxSizerFlags().Expand());
m_top_box->Add(module_box, wxSizerFlags().Centre().Border(wxALL, 5));
auto* book = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize);
m_mixer_panel = new MixerTab(book);
m_sync_panel = new SyncTab(book);
m_debug_panel = new DebugTab(book);
book->AddPage(m_mixer_panel, "Mixing", true);
book->AddPage(m_sync_panel, "Sync");
book->AddPage(m_debug_panel, "Debug");
m_top_box->Add(book, wxSizerFlags().Centre());
m_top_box->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL), wxSizerFlags().Right());
SetSizerAndFit(m_top_box);
Bind(wxEVT_CHOICE, &Dialog::CallReconfigure, this);
Bind(wxEVT_CHECKBOX, &Dialog::CallReconfigure, this);
}
Dialog::~Dialog()
{
}
void Dialog::Reconfigure()
{
const int mod = m_module_select->GetCurrentSelection();
bool show_portaudio = false, show_sdl = false;
switch (mod)
{
case 0:
show_portaudio = false;
show_sdl = false;
break;
case 1:
show_portaudio = true;
show_sdl = false;
break;
case 2:
show_portaudio = false;
show_sdl = true;
break;
default:
show_portaudio = false;
show_sdl = false;
break;
}
#ifdef SPU2X_PORTAUDIO
m_top_box->Show(m_portaudio_box, show_portaudio, true);
#endif
m_top_box->Show(m_sdl_box, show_sdl, true);
// Recalculating both of these accounts for if neither was showing initially.
m_top_box->Layout();
SetSizerAndFit(m_top_box);
}
void Dialog::CallReconfigure(wxCommandEvent& event)
{
Reconfigure();
}
void Dialog::Load()
{
m_module_select->SetSelection(OutputModule);
#ifdef SPU2X_PORTAUDIO
m_portaudio_select->SetSelection(OutputAPI);
#endif
m_sdl_select->SetSelection(SdlOutputAPI);
m_mixer_panel->Load();
m_sync_panel->Load();
m_debug_panel->Load();
Reconfigure();
}
void Dialog::Save()
{
OutputModule = m_module_select->GetSelection();
#ifdef SPU2X_PORTAUDIO
OutputAPI = m_portaudio_select->GetSelection();
wxString p_api(m_portaudio_select->GetStringSelection());
if (p_api.Find("ALSA") != wxNOT_FOUND)
p_api = "ALSA";
if (p_api.Find("OSS") != wxNOT_FOUND)
p_api = "OSS";
PortaudioOut->SetApiSettings(p_api);
#endif
SdlOutputAPI = m_sdl_select->GetSelection();
SDLOut->SetApiSettings(m_sdl_select->GetStringSelection());
m_mixer_panel->Save();
m_sync_panel->Save();
m_debug_panel->Save();
}
// Main
void Dialog::Display()
{
Load();
if (ShowModal() == wxID_OK)
Save();
}

110
pcsx2/SPU2/wx/wxConfig.h Normal file
View File

@ -0,0 +1,110 @@
/* SPU2-X, A plugin for Emulating the Sound Processing Unit of the Playstation 2
* Developed and maintained by the Pcsx2 Development Team.
*
* Original portions from SPU2ghz are (c) 2008 by David Quintana [gigaherz]
*
* SPU2-X is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SPU2-X is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPU2-X. If not, see <http://www.gnu.org/licenses/>.
*/
#include <wx/wx.h>
#include <wx/panel.h>
#include <wx/wrapsizer.h>
#include <wx/notebook.h>
#include <wx/spinctrl.h>
#if defined(__unix__) || defined(__APPLE__)
#include <SDL.h>
#include <SDL_audio.h>
#include "Linux/Config.h"
#endif
class MixerTab : public wxPanel
{
public:
wxArrayString m_interpolation;
wxChoice* m_inter_select;
wxCheckBox *effect_check, *dealias_check;
wxSlider *m_latency_slider, *m_volume_slider;
wxArrayString m_audio;
wxChoice* m_audio_select;
wxStaticBoxSizer *m_mix_box, *m_volume_box, *m_latency_box;
wxBoxSizer* m_audio_box;
MixerTab(wxWindow* parent);
void Load();
void Save();
void Update();
void CallUpdate(wxCommandEvent& event);
};
class SyncTab : public wxPanel
{
public:
wxStaticBoxSizer* m_sync_box;
wxArrayString m_sync;
wxChoice* m_sync_select;
wxButton* launch_adv_dialog;
wxButton* reset_button;
wxSpinCtrl *seq_spin, *seek_spin, *overlap_spin;
SyncTab(wxWindow* parent);
void Load();
void Save();
void Update();
void CallUpdate(wxCommandEvent& event);
void OnButtonClicked(wxCommandEvent& event);
};
class DebugTab : public wxPanel
{
public:
wxCheckBox* debug_check;
wxButton* launch_debug_dialog;
wxBoxSizer* m_together_box;
wxStaticBoxSizer *m_console_box, *m_log_only_box, *dump_box;
wxCheckBox* show_check;
wxCheckBox *key_check, *voice_check, *dma_check, *autodma_check, *buffer_check, *adpcm_check;
wxCheckBox *dma_actions_check, *dma_writes_check, *auto_output_check;
wxCheckBox *core_voice_check, *memory_check, *register_check;
DebugTab(wxWindow* parent);
void Load();
void Save();
void Update();
void CallUpdate(wxCommandEvent& event);
};
class Dialog : public wxDialog
{
wxBoxSizer *m_top_box, *m_left_box, *m_right_box;
wxBoxSizer *m_portaudio_box, *m_sdl_box;
wxStaticBoxSizer* m_output_box;
wxArrayString m_module, m_portaudio, m_sdl;
wxChoice *m_module_select, *m_portaudio_select, *m_sdl_select;
wxStaticText *m_portaudio_text, *m_sdl_text;
MixerTab* m_mixer_panel;
SyncTab* m_sync_panel;
DebugTab* m_debug_panel;
public:
Dialog();
~Dialog();
void Display();
void Load();
void Save();
void Reconfigure();
void CallReconfigure(wxCommandEvent& event);
};