mirror of https://github.com/PCSX2/pcsx2.git
SPU2: clang-format
This commit is contained in:
parent
b0b4adea75
commit
6dc3966be9
|
@ -23,129 +23,144 @@ static u32 PsxRates[160];
|
||||||
|
|
||||||
void InitADSR() // INIT ADSR
|
void InitADSR() // INIT ADSR
|
||||||
{
|
{
|
||||||
for (int i = 0; i < (32 + 128); i++) {
|
for (int i = 0; i < (32 + 128); i++)
|
||||||
int shift = (i - 32) >> 2;
|
{
|
||||||
s64 rate = (i & 3) + 4;
|
int shift = (i - 32) >> 2;
|
||||||
if (shift < 0)
|
s64 rate = (i & 3) + 4;
|
||||||
rate >>= -shift;
|
if (shift < 0)
|
||||||
else
|
rate >>= -shift;
|
||||||
rate <<= shift;
|
else
|
||||||
|
rate <<= shift;
|
||||||
|
|
||||||
PsxRates[i] = (int)std::min(rate, (s64)0x3fffffffLL);
|
PsxRates[i] = (int)std::min(rate, (s64)0x3fffffffLL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool V_ADSR::Calculate()
|
bool V_ADSR::Calculate()
|
||||||
{
|
{
|
||||||
pxAssume(Phase != 0);
|
pxAssume(Phase != 0);
|
||||||
|
|
||||||
if (Releasing && (Phase < 5))
|
if (Releasing && (Phase < 5))
|
||||||
Phase = 5;
|
Phase = 5;
|
||||||
|
|
||||||
switch (Phase) {
|
switch (Phase)
|
||||||
case 1: // attack
|
{
|
||||||
if (Value == ADSR_MAX_VOL) {
|
case 1: // attack
|
||||||
// Already maxed out. Progress phase and nothing more:
|
if (Value == ADSR_MAX_VOL)
|
||||||
Phase++;
|
{
|
||||||
break;
|
// Already maxed out. Progress phase and nothing more:
|
||||||
}
|
Phase++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Case 1 below is for pseudo exponential below 75%.
|
// Case 1 below is for pseudo exponential below 75%.
|
||||||
// Pseudo Exp > 75% and Linear are the same.
|
// Pseudo Exp > 75% and Linear are the same.
|
||||||
|
|
||||||
if (AttackMode && (Value >= 0x60000000))
|
if (AttackMode && (Value >= 0x60000000))
|
||||||
Value += PsxRates[(AttackRate ^ 0x7f) - 0x18 + 32];
|
Value += PsxRates[(AttackRate ^ 0x7f) - 0x18 + 32];
|
||||||
else
|
else
|
||||||
Value += PsxRates[(AttackRate ^ 0x7f) - 0x10 + 32];
|
Value += PsxRates[(AttackRate ^ 0x7f) - 0x10 + 32];
|
||||||
|
|
||||||
if (Value < 0) {
|
if (Value < 0)
|
||||||
// We hit the ceiling.
|
{
|
||||||
Phase++;
|
// We hit the ceiling.
|
||||||
Value = ADSR_MAX_VOL;
|
Phase++;
|
||||||
}
|
Value = ADSR_MAX_VOL;
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 2: // decay
|
case 2: // decay
|
||||||
{
|
{
|
||||||
u32 off = InvExpOffsets[(Value >> 28) & 7];
|
u32 off = InvExpOffsets[(Value >> 28) & 7];
|
||||||
Value -= PsxRates[((DecayRate ^ 0x1f) * 4) - 0x18 + off + 32];
|
Value -= PsxRates[((DecayRate ^ 0x1f) * 4) - 0x18 + off + 32];
|
||||||
|
|
||||||
// calculate sustain level as a factor of the ADSR maximum volume.
|
// calculate sustain level as a factor of the ADSR maximum volume.
|
||||||
|
|
||||||
s32 suslev = ((0x80000000 / 0x10) * (SustainLevel + 1)) - 1;
|
s32 suslev = ((0x80000000 / 0x10) * (SustainLevel + 1)) - 1;
|
||||||
|
|
||||||
if (Value <= suslev) {
|
if (Value <= suslev)
|
||||||
if (Value < 0)
|
{
|
||||||
Value = 0;
|
if (Value < 0)
|
||||||
Phase++;
|
Value = 0;
|
||||||
}
|
Phase++;
|
||||||
} break;
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 3: // sustain
|
case 3: // sustain
|
||||||
{
|
{
|
||||||
// 0x7f disables sustain (infinite sustain)
|
// 0x7f disables sustain (infinite sustain)
|
||||||
if (SustainRate == 0x7f)
|
if (SustainRate == 0x7f)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (SustainMode & 2) // decreasing
|
if (SustainMode & 2) // decreasing
|
||||||
{
|
{
|
||||||
if (SustainMode & 4) // exponential
|
if (SustainMode & 4) // exponential
|
||||||
{
|
{
|
||||||
u32 off = InvExpOffsets[(Value >> 28) & 7];
|
u32 off = InvExpOffsets[(Value >> 28) & 7];
|
||||||
Value -= PsxRates[(SustainRate ^ 0x7f) - 0x1b + off + 32];
|
Value -= PsxRates[(SustainRate ^ 0x7f) - 0x1b + off + 32];
|
||||||
} else // linear
|
}
|
||||||
Value -= PsxRates[(SustainRate ^ 0x7f) - 0xf + 32];
|
else // linear
|
||||||
|
Value -= PsxRates[(SustainRate ^ 0x7f) - 0xf + 32];
|
||||||
|
|
||||||
if (Value <= 0) {
|
if (Value <= 0)
|
||||||
Value = 0;
|
{
|
||||||
Phase++;
|
Value = 0;
|
||||||
}
|
Phase++;
|
||||||
} else { // increasing
|
}
|
||||||
if ((SustainMode & 4) && (Value >= 0x60000000))
|
}
|
||||||
Value += PsxRates[(SustainRate ^ 0x7f) - 0x18 + 32];
|
else
|
||||||
else
|
{ // increasing
|
||||||
// linear / Pseudo below 75% (they're the same)
|
if ((SustainMode & 4) && (Value >= 0x60000000))
|
||||||
Value += PsxRates[(SustainRate ^ 0x7f) - 0x10 + 32];
|
Value += PsxRates[(SustainRate ^ 0x7f) - 0x18 + 32];
|
||||||
|
else
|
||||||
|
// linear / Pseudo below 75% (they're the same)
|
||||||
|
Value += PsxRates[(SustainRate ^ 0x7f) - 0x10 + 32];
|
||||||
|
|
||||||
if (Value < 0) {
|
if (Value < 0)
|
||||||
Value = ADSR_MAX_VOL;
|
{
|
||||||
Phase++;
|
Value = ADSR_MAX_VOL;
|
||||||
}
|
Phase++;
|
||||||
}
|
}
|
||||||
} break;
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 4: // sustain end
|
case 4: // sustain end
|
||||||
Value = (SustainMode & 2) ? 0 : ADSR_MAX_VOL;
|
Value = (SustainMode & 2) ? 0 : ADSR_MAX_VOL;
|
||||||
if (Value == 0)
|
if (Value == 0)
|
||||||
Phase = 6;
|
Phase = 6;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5: // release
|
case 5: // release
|
||||||
if (ReleaseMode) // exponential
|
if (ReleaseMode) // exponential
|
||||||
{
|
{
|
||||||
u32 off = InvExpOffsets[(Value >> 28) & 7];
|
u32 off = InvExpOffsets[(Value >> 28) & 7];
|
||||||
Value -= PsxRates[((ReleaseRate ^ 0x1f) * 4) - 0x18 + off + 32];
|
Value -= PsxRates[((ReleaseRate ^ 0x1f) * 4) - 0x18 + off + 32];
|
||||||
} else { // linear
|
}
|
||||||
//Value-=PsxRates[((ReleaseRate^0x1f)*4)-0xc+32];
|
else
|
||||||
if (ReleaseRate != 0x1f)
|
{ // linear
|
||||||
Value -= (1 << (0x1f - ReleaseRate));
|
//Value-=PsxRates[((ReleaseRate^0x1f)*4)-0xc+32];
|
||||||
}
|
if (ReleaseRate != 0x1f)
|
||||||
|
Value -= (1 << (0x1f - ReleaseRate));
|
||||||
|
}
|
||||||
|
|
||||||
if (Value <= 0) {
|
if (Value <= 0)
|
||||||
Value = 0;
|
{
|
||||||
Phase++;
|
Value = 0;
|
||||||
}
|
Phase++;
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 6: // release end
|
case 6: // release end
|
||||||
Value = 0;
|
Value = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
jNO_DEFAULT
|
jNO_DEFAULT
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if the voice is active, or false if it's stopping.
|
// returns true if the voice is active, or false if it's stopping.
|
||||||
return Phase != 6;
|
return Phase != 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -159,47 +174,53 @@ bool V_ADSR::Calculate()
|
||||||
|
|
||||||
void V_VolumeSlide::Update()
|
void V_VolumeSlide::Update()
|
||||||
{
|
{
|
||||||
if (!(Mode & VOLFLAG_SLIDE_ENABLE))
|
if (!(Mode & VOLFLAG_SLIDE_ENABLE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Volume slides use the same basic logic as ADSR, but simplified (single-stage
|
// Volume slides use the same basic logic as ADSR, but simplified (single-stage
|
||||||
// instead of multi-stage)
|
// instead of multi-stage)
|
||||||
|
|
||||||
if (Increment == 0x7f)
|
if (Increment == 0x7f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
s32 value = abs(Value);
|
s32 value = abs(Value);
|
||||||
|
|
||||||
if (Mode & VOLFLAG_DECREMENT) {
|
if (Mode & VOLFLAG_DECREMENT)
|
||||||
// Decrement
|
{
|
||||||
|
// Decrement
|
||||||
|
|
||||||
if (Mode & VOLFLAG_EXPONENTIAL) {
|
if (Mode & VOLFLAG_EXPONENTIAL)
|
||||||
u32 off = InvExpOffsets[(value >> 28) & 7];
|
{
|
||||||
value -= PsxRates[(Increment ^ 0x7f) - 0x1b + off + 32];
|
u32 off = InvExpOffsets[(value >> 28) & 7];
|
||||||
} else
|
value -= PsxRates[(Increment ^ 0x7f) - 0x1b + off + 32];
|
||||||
value -= PsxRates[(Increment ^ 0x7f) - 0xf + 32];
|
}
|
||||||
|
else
|
||||||
|
value -= PsxRates[(Increment ^ 0x7f) - 0xf + 32];
|
||||||
|
|
||||||
if (value < 0) {
|
if (value < 0)
|
||||||
value = 0;
|
{
|
||||||
Mode = 0; // disable slide
|
value = 0;
|
||||||
}
|
Mode = 0; // disable slide
|
||||||
} else {
|
}
|
||||||
// Increment
|
}
|
||||||
// Pseudo-exponential increments, as done by the SPU2 (really!)
|
else
|
||||||
// Above 75% slides slow, below 75% slides fast. It's exponential, pseudo'ly speaking.
|
{
|
||||||
|
// 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))
|
if ((Mode & VOLFLAG_EXPONENTIAL) && (value >= 0x60000000))
|
||||||
value += PsxRates[(Increment ^ 0x7f) - 0x18 + 32];
|
value += PsxRates[(Increment ^ 0x7f) - 0x18 + 32];
|
||||||
else
|
else
|
||||||
// linear / Pseudo below 75% (they're the same)
|
// linear / Pseudo below 75% (they're the same)
|
||||||
value += PsxRates[(Increment ^ 0x7f) - 0x10 + 32];
|
value += PsxRates[(Increment ^ 0x7f) - 0x10 + 32];
|
||||||
|
|
||||||
if (value < 0) // wrapped around the "top"?
|
if (value < 0) // wrapped around the "top"?
|
||||||
{
|
{
|
||||||
value = 0x7fffffff;
|
value = 0x7fffffff;
|
||||||
Mode = 0; // disable slide
|
Mode = 0; // disable slide
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Value = (Value < 0) ? -value : value;
|
Value = (Value < 0) ? -value : value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@ extern bool dspPluginEnabled;
|
||||||
|
|
||||||
namespace SoundtouchCfg
|
namespace SoundtouchCfg
|
||||||
{
|
{
|
||||||
extern void ApplySettings(soundtouch::SoundTouch &sndtouch);
|
extern void ApplySettings(soundtouch::SoundTouch& sndtouch);
|
||||||
}
|
}
|
||||||
|
|
||||||
//////
|
//////
|
||||||
|
|
|
@ -19,24 +19,24 @@ int crazy_debug = 0;
|
||||||
|
|
||||||
char s[4096];
|
char s[4096];
|
||||||
|
|
||||||
FILE *spu2Log = NULL;
|
FILE* spu2Log = NULL;
|
||||||
|
|
||||||
void FileLog(const char *fmt, ...)
|
void FileLog(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
#ifdef SPU2_LOG
|
#ifdef SPU2_LOG
|
||||||
va_list list;
|
va_list list;
|
||||||
|
|
||||||
if (!AccessLog())
|
if (!AccessLog())
|
||||||
return;
|
return;
|
||||||
if (!spu2Log)
|
if (!spu2Log)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
va_start(list, fmt);
|
va_start(list, fmt);
|
||||||
vsprintf(s, fmt, list);
|
vsprintf(s, fmt, list);
|
||||||
va_end(list);
|
va_end(list);
|
||||||
|
|
||||||
fputs(s, spu2Log);
|
fputs(s, spu2Log);
|
||||||
fflush(spu2Log);
|
fflush(spu2Log);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
if(crazy_debug)
|
if(crazy_debug)
|
||||||
|
@ -52,205 +52,215 @@ void FileLog(const char *fmt, ...)
|
||||||
// while ConLog doesn't print anything if messages to console are disabled at the GUI,
|
// 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).
|
// 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.
|
// Else, there's some (small) overhead in calling and returning from ConLog.
|
||||||
void ConLog(const char *fmt, ...)
|
void ConLog(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
if (!MsgToConsole())
|
if (!MsgToConsole())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
va_list list;
|
va_list list;
|
||||||
va_start(list, fmt);
|
va_start(list, fmt);
|
||||||
vsprintf(s, fmt, list);
|
vsprintf(s, fmt, list);
|
||||||
va_end(list);
|
va_end(list);
|
||||||
|
|
||||||
fputs(s, stderr);
|
fputs(s, stderr);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
|
|
||||||
if (spu2Log) {
|
if (spu2Log)
|
||||||
fputs(s, spu2Log);
|
{
|
||||||
fflush(spu2Log);
|
fputs(s, spu2Log);
|
||||||
}
|
fflush(spu2Log);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void V_VolumeSlide::DebugDump(FILE *dump, const char *title, const char *nameLR)
|
void V_VolumeSlide::DebugDump(FILE* dump, const char* title, const char* nameLR)
|
||||||
{
|
{
|
||||||
fprintf(dump, "%s Volume for %s Channel:\t%x\n"
|
fprintf(dump, "%s Volume for %s Channel:\t%x\n"
|
||||||
" - Value: %x\n"
|
" - Value: %x\n"
|
||||||
" - Mode: %x\n"
|
" - Mode: %x\n"
|
||||||
" - Increment: %x\n",
|
" - Increment: %x\n",
|
||||||
title, nameLR, Reg_VOL, Value, Mode, Increment);
|
title, nameLR, Reg_VOL, Value, Mode, Increment);
|
||||||
}
|
}
|
||||||
|
|
||||||
void V_VolumeSlideLR::DebugDump(FILE *dump, const char *title)
|
void V_VolumeSlideLR::DebugDump(FILE* dump, const char* title)
|
||||||
{
|
{
|
||||||
Left.DebugDump(dump, title, "Left");
|
Left.DebugDump(dump, title, "Left");
|
||||||
Right.DebugDump(dump, title, "Right");
|
Right.DebugDump(dump, title, "Right");
|
||||||
}
|
}
|
||||||
|
|
||||||
void V_VolumeLR::DebugDump(FILE *dump, const char *title)
|
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, "Left", Left);
|
||||||
fprintf(dump, "Volume for %s (%s Channel):\t%x\n", title, "Right", Right);
|
fprintf(dump, "Volume for %s (%s Channel):\t%x\n", title, "Right", Right);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoFullDump()
|
void DoFullDump()
|
||||||
{
|
{
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#ifdef SPU2_LOG
|
#ifdef SPU2_LOG
|
||||||
FILE *dump;
|
FILE* dump;
|
||||||
u8 c = 0, v = 0;
|
u8 c = 0, v = 0;
|
||||||
|
|
||||||
if (MemDump()) {
|
if (MemDump())
|
||||||
dump = fopen(wxString(MemDumpFileName).ToUTF8(), "wb");
|
{
|
||||||
if (dump) {
|
dump = fopen(wxString(MemDumpFileName).ToUTF8(), "wb");
|
||||||
fwrite(_spu2mem, 0x200000, 1, dump);
|
if (dump)
|
||||||
fclose(dump);
|
{
|
||||||
}
|
fwrite(_spu2mem, 0x200000, 1, dump);
|
||||||
}
|
fclose(dump);
|
||||||
if (RegDump()) {
|
}
|
||||||
dump = fopen(wxString(RegDumpFileName).ToUTF8(), "wb");
|
}
|
||||||
if (dump) {
|
if (RegDump())
|
||||||
fwrite(spu2regs, 0x2000, 1, dump);
|
{
|
||||||
fclose(dump);
|
dump = fopen(wxString(RegDumpFileName).ToUTF8(), "wb");
|
||||||
}
|
if (dump)
|
||||||
}
|
{
|
||||||
|
fwrite(spu2regs, 0x2000, 1, dump);
|
||||||
|
fclose(dump);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!CoresDump())
|
if (!CoresDump())
|
||||||
return;
|
return;
|
||||||
dump = fopen(wxString(CoresDumpFileName).ToUTF8(), "wt");
|
dump = fopen(wxString(CoresDumpFileName).ToUTF8(), "wt");
|
||||||
if (dump) {
|
if (dump)
|
||||||
for (c = 0; c < 2; c++) {
|
{
|
||||||
fprintf(dump, "#### CORE %d DUMP.\n", c);
|
for (c = 0; c < 2; c++)
|
||||||
|
{
|
||||||
|
fprintf(dump, "#### CORE %d DUMP.\n", c);
|
||||||
|
|
||||||
Cores[c].MasterVol.DebugDump(dump, "Master");
|
Cores[c].MasterVol.DebugDump(dump, "Master");
|
||||||
|
|
||||||
Cores[c].ExtVol.DebugDump(dump, "External Data Input");
|
Cores[c].ExtVol.DebugDump(dump, "External Data Input");
|
||||||
Cores[c].InpVol.DebugDump(dump, "Voice Data Input [dry]");
|
Cores[c].InpVol.DebugDump(dump, "Voice Data Input [dry]");
|
||||||
Cores[c].FxVol.DebugDump(dump, "Effects/Reverb [wet]");
|
Cores[c].FxVol.DebugDump(dump, "Effects/Reverb [wet]");
|
||||||
|
|
||||||
fprintf(dump, "Interrupt Address: %x\n", Cores[c].IRQA);
|
fprintf(dump, "Interrupt Address: %x\n", Cores[c].IRQA);
|
||||||
fprintf(dump, "DMA Transfer Start Address: %x\n", Cores[c].TSA);
|
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 (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 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 (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, "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 (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 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 (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, "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 (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 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 (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, "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, "IRQ Enabled: %s\n", Cores[c].IRQEnable ? "Yes" : "No");
|
||||||
fprintf(dump, "Effects Enabled: %s\n", Cores[c].FxEnable ? "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, "Mute Enabled: %s\n", Cores[c].Mute ? "Yes" : "No");
|
||||||
fprintf(dump, "Noise Clock: %d\n", Cores[c].NoiseClk);
|
fprintf(dump, "Noise Clock: %d\n", Cores[c].NoiseClk);
|
||||||
fprintf(dump, "DMA Bits: %d\n", Cores[c].DMABits);
|
fprintf(dump, "DMA Bits: %d\n", Cores[c].DMABits);
|
||||||
fprintf(dump, "Effects Start: %x\n", Cores[c].EffectsStartA);
|
fprintf(dump, "Effects Start: %x\n", Cores[c].EffectsStartA);
|
||||||
fprintf(dump, "Effects End: %x\n", Cores[c].EffectsEndA);
|
fprintf(dump, "Effects End: %x\n", Cores[c].EffectsEndA);
|
||||||
fprintf(dump, "Registers:\n");
|
fprintf(dump, "Registers:\n");
|
||||||
fprintf(dump, " - PMON: %x\n", Cores[c].Regs.PMON);
|
fprintf(dump, " - PMON: %x\n", Cores[c].Regs.PMON);
|
||||||
fprintf(dump, " - NON: %x\n", Cores[c].Regs.NON);
|
fprintf(dump, " - NON: %x\n", Cores[c].Regs.NON);
|
||||||
fprintf(dump, " - VMIXL: %x\n", Cores[c].Regs.VMIXL);
|
fprintf(dump, " - VMIXL: %x\n", Cores[c].Regs.VMIXL);
|
||||||
fprintf(dump, " - VMIXR: %x\n", Cores[c].Regs.VMIXR);
|
fprintf(dump, " - VMIXR: %x\n", Cores[c].Regs.VMIXR);
|
||||||
fprintf(dump, " - VMIXEL: %x\n", Cores[c].Regs.VMIXEL);
|
fprintf(dump, " - VMIXEL: %x\n", Cores[c].Regs.VMIXEL);
|
||||||
fprintf(dump, " - VMIXER: %x\n", Cores[c].Regs.VMIXER);
|
fprintf(dump, " - VMIXER: %x\n", Cores[c].Regs.VMIXER);
|
||||||
fprintf(dump, " - MMIX: %x\n", Cores[c].Regs.VMIXEL);
|
fprintf(dump, " - MMIX: %x\n", Cores[c].Regs.VMIXEL);
|
||||||
fprintf(dump, " - ENDX: %x\n", Cores[c].Regs.VMIXER);
|
fprintf(dump, " - ENDX: %x\n", Cores[c].Regs.VMIXER);
|
||||||
fprintf(dump, " - STATX: %x\n", Cores[c].Regs.VMIXEL);
|
fprintf(dump, " - STATX: %x\n", Cores[c].Regs.VMIXEL);
|
||||||
fprintf(dump, " - ATTR: %x\n", Cores[c].Regs.VMIXER);
|
fprintf(dump, " - ATTR: %x\n", Cores[c].Regs.VMIXER);
|
||||||
for (v = 0; v < 24; v++) {
|
for (v = 0; v < 24; v++)
|
||||||
fprintf(dump, "Voice %d:\n", v);
|
{
|
||||||
Cores[c].Voices[v].Volume.DebugDump(dump, "");
|
fprintf(dump, "Voice %d:\n", v);
|
||||||
|
Cores[c].Voices[v].Volume.DebugDump(dump, "");
|
||||||
|
|
||||||
fprintf(dump, " - ADSR Envelope: %x & %x\n"
|
fprintf(dump, " - ADSR Envelope: %x & %x\n"
|
||||||
" - Ar: %x\n"
|
" - Ar: %x\n"
|
||||||
" - Am: %x\n"
|
" - Am: %x\n"
|
||||||
" - Dr: %x\n"
|
" - Dr: %x\n"
|
||||||
" - Sl: %x\n"
|
" - Sl: %x\n"
|
||||||
" - Sr: %x\n"
|
" - Sr: %x\n"
|
||||||
" - Sm: %x\n"
|
" - Sm: %x\n"
|
||||||
" - Rr: %x\n"
|
" - Rr: %x\n"
|
||||||
" - Rm: %x\n"
|
" - Rm: %x\n"
|
||||||
" - Phase: %x\n"
|
" - Phase: %x\n"
|
||||||
" - Value: %x\n",
|
" - Value: %x\n",
|
||||||
Cores[c].Voices[v].ADSR.regADSR1,
|
Cores[c].Voices[v].ADSR.regADSR1,
|
||||||
Cores[c].Voices[v].ADSR.regADSR2,
|
Cores[c].Voices[v].ADSR.regADSR2,
|
||||||
Cores[c].Voices[v].ADSR.AttackRate,
|
Cores[c].Voices[v].ADSR.AttackRate,
|
||||||
Cores[c].Voices[v].ADSR.AttackMode,
|
Cores[c].Voices[v].ADSR.AttackMode,
|
||||||
Cores[c].Voices[v].ADSR.DecayRate,
|
Cores[c].Voices[v].ADSR.DecayRate,
|
||||||
Cores[c].Voices[v].ADSR.SustainLevel,
|
Cores[c].Voices[v].ADSR.SustainLevel,
|
||||||
Cores[c].Voices[v].ADSR.SustainRate,
|
Cores[c].Voices[v].ADSR.SustainRate,
|
||||||
Cores[c].Voices[v].ADSR.SustainMode,
|
Cores[c].Voices[v].ADSR.SustainMode,
|
||||||
Cores[c].Voices[v].ADSR.ReleaseRate,
|
Cores[c].Voices[v].ADSR.ReleaseRate,
|
||||||
Cores[c].Voices[v].ADSR.ReleaseMode,
|
Cores[c].Voices[v].ADSR.ReleaseMode,
|
||||||
Cores[c].Voices[v].ADSR.Phase,
|
Cores[c].Voices[v].ADSR.Phase,
|
||||||
Cores[c].Voices[v].ADSR.Value);
|
Cores[c].Voices[v].ADSR.Value);
|
||||||
|
|
||||||
fprintf(dump, " - Pitch: %x\n", Cores[c].Voices[v].Pitch);
|
fprintf(dump, " - Pitch: %x\n", Cores[c].Voices[v].Pitch);
|
||||||
fprintf(dump, " - Modulated: %s\n", Cores[c].Voices[v].Modulated ? "Yes" : "No");
|
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, " - 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 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, " - 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 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, " - 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, " - Loop Start Address: %x\n", Cores[c].Voices[v].LoopStartA);
|
||||||
fprintf(dump, " - Sound Start Address: %x\n", Cores[c].Voices[v].StartA);
|
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, " - 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 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, " - 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, " - Block Sample: %d\n", Cores[c].Voices[v].SCurrent);
|
||||||
}
|
}
|
||||||
fprintf(dump, "#### END OF DUMP.\n\n");
|
fprintf(dump, "#### END OF DUMP.\n\n");
|
||||||
}
|
}
|
||||||
fclose(dump);
|
fclose(dump);
|
||||||
}
|
}
|
||||||
|
|
||||||
dump = fopen("logs/effects.txt", "wt");
|
dump = fopen("logs/effects.txt", "wt");
|
||||||
if (dump) {
|
if (dump)
|
||||||
for (c = 0; c < 2; c++) {
|
{
|
||||||
fprintf(dump, "#### CORE %d EFFECTS PROCESSOR DUMP.\n", c);
|
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_L: %x\n", Cores[c].Revb.IN_COEF_R);
|
||||||
fprintf(dump, " - IN_COEF_R: %x\n", Cores[c].Revb.IN_COEF_L);
|
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, " - APF1_VOL: %x\n", Cores[c].Revb.APF1_VOL);
|
||||||
fprintf(dump, " - APF2_VOL: %x\n", Cores[c].Revb.APF2_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, " - APF1_SIZE: %x\n", Cores[c].Revb.APF1_SIZE);
|
||||||
fprintf(dump, " - APF2_SIZE: %x\n", Cores[c].Revb.APF2_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, " - IIR_VOL: %x\n", Cores[c].Revb.IIR_VOL);
|
||||||
fprintf(dump, " - WALL_VOL: %x\n", Cores[c].Revb.WALL_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_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, " - 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_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, " - 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_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, " - 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_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, " - DIFF_R_DST: %x\n", Cores[c].Revb.DIFF_R_DST);
|
||||||
|
|
||||||
fprintf(dump, " - COMB1_VOL: %x\n", Cores[c].Revb.COMB1_VOL);
|
fprintf(dump, " - COMB1_VOL: %x\n", Cores[c].Revb.COMB1_VOL);
|
||||||
fprintf(dump, " - COMB2_VOL: %x\n", Cores[c].Revb.COMB2_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, " - COMB3_VOL: %x\n", Cores[c].Revb.COMB3_VOL);
|
||||||
fprintf(dump, " - COMB4_VOL: %x\n", Cores[c].Revb.COMB4_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_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, " - 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_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, " - 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_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, " - 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_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, " - 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_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, " - 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_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, " - APF2_R_DST: %x\n", Cores[c].Revb.APF2_R_DST);
|
||||||
fprintf(dump, "#### END OF DUMP.\n\n");
|
fprintf(dump, "#### END OF DUMP.\n\n");
|
||||||
}
|
}
|
||||||
fclose(dump);
|
fclose(dump);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,53 +16,54 @@
|
||||||
#ifndef DEBUG_H_INCLUDED
|
#ifndef DEBUG_H_INCLUDED
|
||||||
#define DEBUG_H_INCLUDED
|
#define DEBUG_H_INCLUDED
|
||||||
|
|
||||||
extern FILE *spu2Log;
|
extern FILE* spu2Log;
|
||||||
|
|
||||||
extern void FileLog(const char *fmt, ...);
|
extern void FileLog(const char* fmt, ...);
|
||||||
extern void ConLog(const char *fmt, ...);
|
extern void ConLog(const char* fmt, ...);
|
||||||
|
|
||||||
extern void DoFullDump();
|
extern void DoFullDump();
|
||||||
|
|
||||||
extern FILE *OpenBinaryLog(const wxString &logfile);
|
extern FILE* OpenBinaryLog(const wxString& logfile);
|
||||||
extern FILE *OpenLog(const wxString &logfile);
|
extern FILE* OpenLog(const wxString& logfile);
|
||||||
extern FILE *OpenDump(const wxString &logfile);
|
extern FILE* OpenDump(const wxString& logfile);
|
||||||
|
|
||||||
namespace WaveDump
|
namespace WaveDump
|
||||||
{
|
{
|
||||||
enum CoreSourceType {
|
enum CoreSourceType
|
||||||
// Core's input stream, usually pulled from ADMA streams.
|
{
|
||||||
CoreSrc_Input = 0,
|
// Core's input stream, usually pulled from ADMA streams.
|
||||||
|
CoreSrc_Input = 0,
|
||||||
|
|
||||||
// Output of the actual 24 input voices which have dry output enabled.
|
// Output of the actual 24 input voices which have dry output enabled.
|
||||||
CoreSrc_DryVoiceMix,
|
CoreSrc_DryVoiceMix,
|
||||||
|
|
||||||
// Output of the actual 24 input voices that have wet output enabled.
|
// Output of the actual 24 input voices that have wet output enabled.
|
||||||
CoreSrc_WetVoiceMix,
|
CoreSrc_WetVoiceMix,
|
||||||
|
|
||||||
// Wet mix including inputs and externals, prior to the application of reverb.
|
// Wet mix including inputs and externals, prior to the application of reverb.
|
||||||
CoreSrc_PreReverb,
|
CoreSrc_PreReverb,
|
||||||
|
|
||||||
// Wet mix after reverb has turned it into a pile of garbly gook.
|
// Wet mix after reverb has turned it into a pile of garbly gook.
|
||||||
CoreSrc_PostReverb,
|
CoreSrc_PostReverb,
|
||||||
|
|
||||||
// Final output of the core. For core 0, it's the feed into Core1.
|
// Final output of the core. For core 0, it's the feed into Core1.
|
||||||
// For Core1, it's the feed into SndOut.
|
// For Core1, it's the feed into SndOut.
|
||||||
CoreSrc_External,
|
CoreSrc_External,
|
||||||
|
|
||||||
CoreSrc_Count
|
CoreSrc_Count
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void Open();
|
extern void Open();
|
||||||
extern void Close();
|
extern void Close();
|
||||||
extern void WriteCore(uint coreidx, CoreSourceType src, s16 left, s16 right);
|
extern void WriteCore(uint coreidx, CoreSourceType src, s16 left, s16 right);
|
||||||
extern void WriteCore(uint coreidx, CoreSourceType src, const StereoOut16 &sample);
|
extern void WriteCore(uint coreidx, CoreSourceType src, const StereoOut16& sample);
|
||||||
}
|
} // namespace WaveDump
|
||||||
|
|
||||||
using WaveDump::CoreSrc_Input;
|
|
||||||
using WaveDump::CoreSrc_DryVoiceMix;
|
using WaveDump::CoreSrc_DryVoiceMix;
|
||||||
using WaveDump::CoreSrc_WetVoiceMix;
|
|
||||||
using WaveDump::CoreSrc_PreReverb;
|
|
||||||
using WaveDump::CoreSrc_PostReverb;
|
|
||||||
using WaveDump::CoreSrc_External;
|
using WaveDump::CoreSrc_External;
|
||||||
|
using WaveDump::CoreSrc_Input;
|
||||||
|
using WaveDump::CoreSrc_PostReverb;
|
||||||
|
using WaveDump::CoreSrc_PreReverb;
|
||||||
|
using WaveDump::CoreSrc_WetVoiceMix;
|
||||||
|
|
||||||
#endif // DEBUG_H_INCLUDED //
|
#endif // DEBUG_H_INCLUDED //
|
||||||
|
|
|
@ -20,153 +20,165 @@
|
||||||
|
|
||||||
extern u8 callirq;
|
extern u8 callirq;
|
||||||
|
|
||||||
static FILE *DMA4LogFile = NULL;
|
static FILE* DMA4LogFile = NULL;
|
||||||
static FILE *DMA7LogFile = NULL;
|
static FILE* DMA7LogFile = NULL;
|
||||||
static FILE *ADMA4LogFile = NULL;
|
static FILE* ADMA4LogFile = NULL;
|
||||||
static FILE *ADMA7LogFile = NULL;
|
static FILE* ADMA7LogFile = NULL;
|
||||||
static FILE *ADMAOutLogFile = NULL;
|
static FILE* ADMAOutLogFile = NULL;
|
||||||
|
|
||||||
static FILE *REGWRTLogFile[2] = {0, 0};
|
static FILE* REGWRTLogFile[2] = {0, 0};
|
||||||
|
|
||||||
void DMALogOpen()
|
void DMALogOpen()
|
||||||
{
|
{
|
||||||
if (!DMALog())
|
if (!DMALog())
|
||||||
return;
|
return;
|
||||||
DMA4LogFile = OpenBinaryLog(DMA4LogFileName);
|
DMA4LogFile = OpenBinaryLog(DMA4LogFileName);
|
||||||
DMA7LogFile = OpenBinaryLog(DMA7LogFileName);
|
DMA7LogFile = OpenBinaryLog(DMA7LogFileName);
|
||||||
ADMA4LogFile = OpenBinaryLog(L"adma4.raw");
|
ADMA4LogFile = OpenBinaryLog(L"adma4.raw");
|
||||||
ADMA7LogFile = OpenBinaryLog(L"adma7.raw");
|
ADMA7LogFile = OpenBinaryLog(L"adma7.raw");
|
||||||
ADMAOutLogFile = OpenBinaryLog(L"admaOut.raw");
|
ADMAOutLogFile = OpenBinaryLog(L"admaOut.raw");
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMA4LogWrite(void *lpData, u32 ulSize)
|
void DMA4LogWrite(void* lpData, u32 ulSize)
|
||||||
{
|
{
|
||||||
if (!DMALog())
|
if (!DMALog())
|
||||||
return;
|
return;
|
||||||
if (!DMA4LogFile)
|
if (!DMA4LogFile)
|
||||||
return;
|
return;
|
||||||
fwrite(lpData, ulSize, 1, DMA4LogFile);
|
fwrite(lpData, ulSize, 1, DMA4LogFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMA7LogWrite(void *lpData, u32 ulSize)
|
void DMA7LogWrite(void* lpData, u32 ulSize)
|
||||||
{
|
{
|
||||||
if (!DMALog())
|
if (!DMALog())
|
||||||
return;
|
return;
|
||||||
if (!DMA7LogFile)
|
if (!DMA7LogFile)
|
||||||
return;
|
return;
|
||||||
fwrite(lpData, ulSize, 1, DMA7LogFile);
|
fwrite(lpData, ulSize, 1, DMA7LogFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADMAOutLogWrite(void *lpData, u32 ulSize)
|
void ADMAOutLogWrite(void* lpData, u32 ulSize)
|
||||||
{
|
{
|
||||||
if (!DMALog())
|
if (!DMALog())
|
||||||
return;
|
return;
|
||||||
if (!ADMAOutLogFile)
|
if (!ADMAOutLogFile)
|
||||||
return;
|
return;
|
||||||
fwrite(lpData, ulSize, 1, ADMAOutLogFile);
|
fwrite(lpData, ulSize, 1, ADMAOutLogFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegWriteLog(u32 core, u16 value)
|
void RegWriteLog(u32 core, u16 value)
|
||||||
{
|
{
|
||||||
if (!DMALog())
|
if (!DMALog())
|
||||||
return;
|
return;
|
||||||
if (!REGWRTLogFile[core])
|
if (!REGWRTLogFile[core])
|
||||||
return;
|
return;
|
||||||
fwrite(&value, 2, 1, REGWRTLogFile[core]);
|
fwrite(&value, 2, 1, REGWRTLogFile[core]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMALogClose()
|
void DMALogClose()
|
||||||
{
|
{
|
||||||
safe_fclose(DMA4LogFile);
|
safe_fclose(DMA4LogFile);
|
||||||
safe_fclose(DMA7LogFile);
|
safe_fclose(DMA7LogFile);
|
||||||
safe_fclose(REGWRTLogFile[0]);
|
safe_fclose(REGWRTLogFile[0]);
|
||||||
safe_fclose(REGWRTLogFile[1]);
|
safe_fclose(REGWRTLogFile[1]);
|
||||||
safe_fclose(ADMA4LogFile);
|
safe_fclose(ADMA4LogFile);
|
||||||
safe_fclose(ADMA7LogFile);
|
safe_fclose(ADMA7LogFile);
|
||||||
safe_fclose(ADMAOutLogFile);
|
safe_fclose(ADMAOutLogFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void V_Core::LogAutoDMA(FILE *fp)
|
void V_Core::LogAutoDMA(FILE* fp)
|
||||||
{
|
{
|
||||||
if (!DMALog() || !fp || !DMAPtr)
|
if (!DMALog() || !fp || !DMAPtr)
|
||||||
return;
|
return;
|
||||||
fwrite(DMAPtr + InputDataProgress, 0x400, 1, fp);
|
fwrite(DMAPtr + InputDataProgress, 0x400, 1, fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void V_Core::AutoDMAReadBuffer(int mode) //mode: 0= split stereo; 1 = do not split stereo
|
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
|
int spos = ((InputPosRead + 0xff) & 0x100); //starting position of the free buffer
|
||||||
|
|
||||||
LogAutoDMA(Index ? ADMA7LogFile : ADMA4LogFile);
|
LogAutoDMA(Index ? ADMA7LogFile : ADMA4LogFile);
|
||||||
|
|
||||||
// HACKFIX!! DMAPtr can be invalid after a savestate load, so the savestate just forces it
|
// 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
|
// 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).
|
// addressing, but new PCSX2s have dynamic memory addressing).
|
||||||
|
|
||||||
if (mode) {
|
if (mode)
|
||||||
if (DMAPtr != NULL)
|
{
|
||||||
//memcpy((ADMATempBuffer+(spos<<1)),DMAPtr+InputDataProgress,0x400);
|
if (DMAPtr != NULL)
|
||||||
memcpy(GetMemPtr(0x2000 + (Index << 10) + spos), DMAPtr + InputDataProgress, 0x400);
|
//memcpy((ADMATempBuffer+(spos<<1)),DMAPtr+InputDataProgress,0x400);
|
||||||
MADR += 0x400;
|
memcpy(GetMemPtr(0x2000 + (Index << 10) + spos), DMAPtr + InputDataProgress, 0x400);
|
||||||
InputDataLeft -= 0x200;
|
MADR += 0x400;
|
||||||
InputDataProgress += 0x200;
|
InputDataLeft -= 0x200;
|
||||||
} else {
|
InputDataProgress += 0x200;
|
||||||
if (DMAPtr != NULL)
|
}
|
||||||
//memcpy((ADMATempBuffer+spos),DMAPtr+InputDataProgress,0x200);
|
else
|
||||||
memcpy(GetMemPtr(0x2000 + (Index << 10) + spos), DMAPtr + InputDataProgress, 0x200);
|
{
|
||||||
MADR += 0x200;
|
if (DMAPtr != NULL)
|
||||||
InputDataLeft -= 0x100;
|
//memcpy((ADMATempBuffer+spos),DMAPtr+InputDataProgress,0x200);
|
||||||
InputDataProgress += 0x100;
|
memcpy(GetMemPtr(0x2000 + (Index << 10) + spos), DMAPtr + InputDataProgress, 0x200);
|
||||||
|
MADR += 0x200;
|
||||||
|
InputDataLeft -= 0x100;
|
||||||
|
InputDataProgress += 0x100;
|
||||||
|
|
||||||
if (DMAPtr != NULL)
|
if (DMAPtr != NULL)
|
||||||
//memcpy((ADMATempBuffer+spos+0x200),DMAPtr+InputDataProgress,0x200);
|
//memcpy((ADMATempBuffer+spos+0x200),DMAPtr+InputDataProgress,0x200);
|
||||||
memcpy(GetMemPtr(0x2200 + (Index << 10) + spos), DMAPtr + InputDataProgress, 0x200);
|
memcpy(GetMemPtr(0x2200 + (Index << 10) + spos), DMAPtr + InputDataProgress, 0x200);
|
||||||
MADR += 0x200;
|
MADR += 0x200;
|
||||||
InputDataLeft -= 0x100;
|
InputDataLeft -= 0x100;
|
||||||
InputDataProgress += 0x100;
|
InputDataProgress += 0x100;
|
||||||
}
|
}
|
||||||
// See ReadInput at mixer.cpp for explanation on the commented out lines
|
// See ReadInput at mixer.cpp for explanation on the commented out lines
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
void V_Core::StartADMAWrite(u16 *pMem, u32 sz)
|
void V_Core::StartADMAWrite(u16* pMem, u32 sz)
|
||||||
{
|
{
|
||||||
int size = (sz) & (~511);
|
int size = (sz) & (~511);
|
||||||
|
|
||||||
if (MsgAutoDMA())
|
if (MsgAutoDMA())
|
||||||
ConLog("* SPU2-X: DMA%c AutoDMA Transfer of %d bytes to %x (%02x %x %04x).\n",
|
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);
|
GetDmaIndexChar(), size << 1, TSA, DMABits, AutoDMACtrl, (~Regs.ATTR) & 0x7fff);
|
||||||
|
|
||||||
InputDataProgress = 0;
|
InputDataProgress = 0;
|
||||||
if ((AutoDMACtrl & (Index + 1)) == 0) {
|
if ((AutoDMACtrl & (Index + 1)) == 0)
|
||||||
TSA = 0x2000 + (Index << 10);
|
{
|
||||||
DMAICounter = size;
|
TSA = 0x2000 + (Index << 10);
|
||||||
} else if (size >= 512) {
|
DMAICounter = size;
|
||||||
InputDataLeft = size;
|
}
|
||||||
if (AdmaInProgress == 0) {
|
else if (size >= 512)
|
||||||
|
{
|
||||||
|
InputDataLeft = size;
|
||||||
|
if (AdmaInProgress == 0)
|
||||||
|
{
|
||||||
#ifdef PCM24_S1_INTERLEAVE
|
#ifdef PCM24_S1_INTERLEAVE
|
||||||
if ((Index == 1) && ((PlayMode & 8) == 8)) {
|
if ((Index == 1) && ((PlayMode & 8) == 8))
|
||||||
AutoDMAReadBuffer(Index, 1);
|
{
|
||||||
} else {
|
AutoDMAReadBuffer(Index, 1);
|
||||||
AutoDMAReadBuffer(Index, 0);
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
AutoDMAReadBuffer(Index, 0);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
if (((PlayMode & 4) == 4) && (Index == 0))
|
if (((PlayMode & 4) == 4) && (Index == 0))
|
||||||
Cores[0].InputPosRead = 0;
|
Cores[0].InputPosRead = 0;
|
||||||
|
|
||||||
AutoDMAReadBuffer(0);
|
AutoDMAReadBuffer(0);
|
||||||
#endif
|
#endif
|
||||||
// Klonoa 2
|
// Klonoa 2
|
||||||
if (size == 512)
|
if (size == 512)
|
||||||
DMAICounter = size;
|
DMAICounter = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
AdmaInProgress = 1;
|
AdmaInProgress = 1;
|
||||||
} else {
|
}
|
||||||
InputDataLeft = 0;
|
else
|
||||||
DMAICounter = 1;
|
{
|
||||||
}
|
InputDataLeft = 0;
|
||||||
TADR = MADR + (size << 1);
|
DMAICounter = 1;
|
||||||
|
}
|
||||||
|
TADR = MADR + (size << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACKFIX: The BIOS breaks if we check the IRQA for both cores when issuing DMA writes. The
|
// HACKFIX: The BIOS breaks if we check the IRQA for both cores when issuing DMA writes. The
|
||||||
|
@ -186,228 +198,255 @@ void V_Core::StartADMAWrite(u16 *pMem, u32 sz)
|
||||||
// Update: This hack is no longer needed when we don't do a core reset. Guess the null pc was in spu2 memory?
|
// 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
|
#define NO_BIOS_HACKFIX 1 // set to 1 to disable the hackfix
|
||||||
|
|
||||||
void V_Core::PlainDMAWrite(u16 *pMem, u32 size)
|
void V_Core::PlainDMAWrite(u16* pMem, u32 size)
|
||||||
{
|
{
|
||||||
// Perform an alignment check.
|
// Perform an alignment check.
|
||||||
// Not really important. Everything should work regardless,
|
// Not really important. Everything should work regardless,
|
||||||
// but it could be indicative of an emulation foopah elsewhere.
|
// but it could be indicative of an emulation foopah elsewhere.
|
||||||
|
|
||||||
if (MsgToConsole()) {
|
if (MsgToConsole())
|
||||||
// Don't need this anymore. Target may still be good to know though.
|
{
|
||||||
/*if((uptr)pMem & 15)
|
// 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);
|
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) {
|
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);
|
{
|
||||||
}
|
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)
|
if (Index == 0)
|
||||||
DMA4LogWrite(pMem, size << 1);
|
DMA4LogWrite(pMem, size << 1);
|
||||||
else
|
else
|
||||||
DMA7LogWrite(pMem, size << 1);
|
DMA7LogWrite(pMem, size << 1);
|
||||||
|
|
||||||
TSA &= 0xfffff;
|
TSA &= 0xfffff;
|
||||||
|
|
||||||
u32 buff1end = TSA + size;
|
u32 buff1end = TSA + size;
|
||||||
u32 buff2end = 0;
|
u32 buff2end = 0;
|
||||||
if (buff1end > 0x100000) {
|
if (buff1end > 0x100000)
|
||||||
buff2end = buff1end - 0x100000;
|
{
|
||||||
buff1end = 0x100000;
|
buff2end = buff1end - 0x100000;
|
||||||
}
|
buff1end = 0x100000;
|
||||||
|
}
|
||||||
|
|
||||||
const int cacheIdxStart = TSA / pcm_WordsPerBlock;
|
const int cacheIdxStart = TSA / pcm_WordsPerBlock;
|
||||||
const int cacheIdxEnd = (buff1end + pcm_WordsPerBlock - 1) / pcm_WordsPerBlock;
|
const int cacheIdxEnd = (buff1end + pcm_WordsPerBlock - 1) / pcm_WordsPerBlock;
|
||||||
PcmCacheEntry *cacheLine = &pcm_cache_data[cacheIdxStart];
|
PcmCacheEntry* cacheLine = &pcm_cache_data[cacheIdxStart];
|
||||||
PcmCacheEntry &cacheEnd = pcm_cache_data[cacheIdxEnd];
|
PcmCacheEntry& cacheEnd = pcm_cache_data[cacheIdxEnd];
|
||||||
|
|
||||||
do {
|
do
|
||||||
cacheLine->Validated = false;
|
{
|
||||||
cacheLine++;
|
cacheLine->Validated = false;
|
||||||
} while (cacheLine != &cacheEnd);
|
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",
|
//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 );
|
// TSA, buff1end, flagTSA, flagTDA, clearLen );
|
||||||
|
|
||||||
|
|
||||||
// First Branch needs cleared:
|
// First Branch needs cleared:
|
||||||
// It starts at TSA and goes to buff1end.
|
// It starts at TSA and goes to buff1end.
|
||||||
|
|
||||||
const u32 buff1size = (buff1end - TSA);
|
const u32 buff1size = (buff1end - TSA);
|
||||||
memcpy(GetMemPtr(TSA), pMem, buff1size * 2);
|
memcpy(GetMemPtr(TSA), pMem, buff1size * 2);
|
||||||
|
|
||||||
u32 TDA;
|
u32 TDA;
|
||||||
|
|
||||||
if (buff2end > 0) {
|
if (buff2end > 0)
|
||||||
// second branch needs copied:
|
{
|
||||||
// It starts at the beginning of memory and moves forward to buff2end
|
// 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
|
// endpoint cache should be irrelevant, since it's almost certainly dynamic
|
||||||
// memory below 0x2800 (registers and such)
|
// memory below 0x2800 (registers and such)
|
||||||
//const u32 endpt2 = (buff2end + roundUp) / indexer_scalar;
|
//const u32 endpt2 = (buff2end + roundUp) / indexer_scalar;
|
||||||
//memset( pcm_cache_flags, 0, endpt2 );
|
//memset( pcm_cache_flags, 0, endpt2 );
|
||||||
|
|
||||||
// Emulation Grayarea: Should addresses wrap around to zero, or wrap around to
|
// Emulation Grayarea: Should addresses wrap around to zero, or wrap around to
|
||||||
// 0x2800? Hard to know for sure (almost no games depend on this)
|
// 0x2800? Hard to know for sure (almost no games depend on this)
|
||||||
|
|
||||||
memcpy(GetMemPtr(0), &pMem[buff1size], buff2end * 2);
|
memcpy(GetMemPtr(0), &pMem[buff1size], buff2end * 2);
|
||||||
TDA = (buff2end + 1) & 0xfffff;
|
TDA = (buff2end + 1) & 0xfffff;
|
||||||
|
|
||||||
// Flag interrupt? If IRQA occurs between start and dest, flag it.
|
// Flag interrupt? If IRQA occurs between start and dest, flag it.
|
||||||
// Important: Test both core IRQ settings for either DMA!
|
// Important: Test both core IRQ settings for either DMA!
|
||||||
// Note: Because this buffer wraps, we use || instead of &&
|
// Note: Because this buffer wraps, we use || instead of &&
|
||||||
|
|
||||||
#if NO_BIOS_HACKFIX
|
#if NO_BIOS_HACKFIX
|
||||||
for (int i = 0; i < 2; i++) {
|
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);
|
// 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
|
#endif
|
||||||
} else {
|
}
|
||||||
// Buffer doesn't wrap/overflow!
|
else
|
||||||
// Just set the TDA and check for an IRQ...
|
{
|
||||||
|
// Buffer doesn't wrap/overflow!
|
||||||
|
// Just set the TDA and check for an IRQ...
|
||||||
|
|
||||||
TDA = (buff1end + 1) & 0xfffff;
|
TDA = (buff1end + 1) & 0xfffff;
|
||||||
|
|
||||||
// Flag interrupt? If IRQA occurs between start and dest, flag it.
|
// Flag interrupt? If IRQA occurs between start and dest, flag it.
|
||||||
// Important: Test both core IRQ settings for either DMA!
|
// Important: Test both core IRQ settings for either DMA!
|
||||||
|
|
||||||
#if NO_BIOS_HACKFIX
|
#if NO_BIOS_HACKFIX
|
||||||
for (int i = 0; i < 2; i++) {
|
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 );
|
if (Cores[i].IRQEnable && (Cores[i].IRQA > TSA && Cores[i].IRQA <= TDA))
|
||||||
SetIrqCall(i);
|
{
|
||||||
}
|
//ConLog("DMAwrite Core %d: IRQ Called (IRQ passed). IRQA = %x Cycles = %d\n", i, Cores[i].IRQA, Cycles );
|
||||||
}
|
SetIrqCall(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
if (IRQEnable && (IRQA > TSA) && (IRQA <= TDA)) {
|
if (IRQEnable && (IRQA > TSA) && (IRQA <= TDA))
|
||||||
SetIrqCall(Index);
|
{
|
||||||
}
|
SetIrqCall(Index);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TSA = TDA;
|
TSA = TDA;
|
||||||
DMAICounter = size;
|
DMAICounter = size;
|
||||||
TADR = MADR + (size << 1);
|
TADR = MADR + (size << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void V_Core::DoDMAread(u16 *pMem, u32 size)
|
void V_Core::DoDMAread(u16* pMem, u32 size)
|
||||||
{
|
{
|
||||||
TSA &= 0xfffff;
|
TSA &= 0xfffff;
|
||||||
|
|
||||||
u32 buff1end = TSA + size;
|
u32 buff1end = TSA + size;
|
||||||
u32 buff2end = 0;
|
u32 buff2end = 0;
|
||||||
if (buff1end > 0x100000) {
|
if (buff1end > 0x100000)
|
||||||
buff2end = buff1end - 0x100000;
|
{
|
||||||
buff1end = 0x100000;
|
buff2end = buff1end - 0x100000;
|
||||||
}
|
buff1end = 0x100000;
|
||||||
|
}
|
||||||
|
|
||||||
const u32 buff1size = (buff1end - TSA);
|
const u32 buff1size = (buff1end - TSA);
|
||||||
memcpy(pMem, GetMemPtr(TSA), buff1size * 2);
|
memcpy(pMem, GetMemPtr(TSA), buff1size * 2);
|
||||||
|
|
||||||
// Note on TSA's position after our copy finishes:
|
// Note on TSA's position after our copy finishes:
|
||||||
// IRQA should be measured by the end of the writepos+0x20. But the TDA
|
// 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.
|
// should be written back at the precise endpoint of the xfer.
|
||||||
|
|
||||||
u32 TDA;
|
u32 TDA;
|
||||||
|
|
||||||
if (buff2end > 0) {
|
if (buff2end > 0)
|
||||||
// second branch needs cleared:
|
{
|
||||||
// It starts at the beginning of memory and moves forward to buff2end
|
// second branch needs cleared:
|
||||||
|
// It starts at the beginning of memory and moves forward to buff2end
|
||||||
|
|
||||||
memcpy(&pMem[buff1size], GetMemPtr(0), buff2end * 2);
|
memcpy(&pMem[buff1size], GetMemPtr(0), buff2end * 2);
|
||||||
|
|
||||||
TDA = (buff2end + 0x20) & 0xfffff;
|
TDA = (buff2end + 0x20) & 0xfffff;
|
||||||
|
|
||||||
// Flag interrupt? If IRQA occurs between start and dest, flag it.
|
// Flag interrupt? If IRQA occurs between start and dest, flag it.
|
||||||
// Important: Test both core IRQ settings for either DMA!
|
// Important: Test both core IRQ settings for either DMA!
|
||||||
// Note: Because this buffer wraps, we use || instead of &&
|
// Note: Because this buffer wraps, we use || instead of &&
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++)
|
||||||
if (Cores[i].IRQEnable && (Cores[i].IRQA > TSA || Cores[i].IRQA <= TDA)) {
|
{
|
||||||
SetIrqCall(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...
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Buffer doesn't wrap/overflow!
|
||||||
|
// Just set the TDA and check for an IRQ...
|
||||||
|
|
||||||
TDA = (buff1end + 0x20) & 0xfffff;
|
TDA = (buff1end + 0x20) & 0xfffff;
|
||||||
|
|
||||||
// Flag interrupt? If IRQA occurs between start and dest, flag it.
|
// Flag interrupt? If IRQA occurs between start and dest, flag it.
|
||||||
// Important: Test both core IRQ settings for either DMA!
|
// Important: Test both core IRQ settings for either DMA!
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++) {
|
for (int i = 0; i < 2; i++)
|
||||||
if (Cores[i].IRQEnable && (Cores[i].IRQA > TSA && Cores[i].IRQA <= TDA)) {
|
{
|
||||||
SetIrqCall(i);
|
if (Cores[i].IRQEnable && (Cores[i].IRQA > TSA && Cores[i].IRQA <= TDA))
|
||||||
}
|
{
|
||||||
}
|
SetIrqCall(i);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TSA = TDA;
|
TSA = TDA;
|
||||||
|
|
||||||
DMAICounter = size;
|
DMAICounter = size;
|
||||||
Regs.STATX &= ~0x80;
|
Regs.STATX &= ~0x80;
|
||||||
//Regs.ATTR |= 0x30;
|
//Regs.ATTR |= 0x30;
|
||||||
TADR = MADR + (size << 1);
|
TADR = MADR + (size << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void V_Core::DoDMAwrite(u16 *pMem, u32 size)
|
void V_Core::DoDMAwrite(u16* pMem, u32 size)
|
||||||
{
|
{
|
||||||
DMAPtr = pMem;
|
DMAPtr = pMem;
|
||||||
|
|
||||||
if (size < 2) {
|
if (size < 2)
|
||||||
Regs.STATX &= ~0x80;
|
{
|
||||||
//Regs.ATTR |= 0x30;
|
Regs.STATX &= ~0x80;
|
||||||
DMAICounter = 1;
|
//Regs.ATTR |= 0x30;
|
||||||
|
DMAICounter = 1;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsDevBuild) {
|
if (IsDevBuild)
|
||||||
DebugCores[Index].lastsize = size;
|
{
|
||||||
DebugCores[Index].dmaFlag = 2;
|
DebugCores[Index].lastsize = size;
|
||||||
}
|
DebugCores[Index].dmaFlag = 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (MsgToConsole()) {
|
if (MsgToConsole())
|
||||||
if (TSA > 0xfffff) {
|
{
|
||||||
ConLog("* SPU2-X: Transfer Start Address out of bounds. TSA is %x\n", TSA);
|
if (TSA > 0xfffff)
|
||||||
}
|
{
|
||||||
}
|
ConLog("* SPU2-X: Transfer Start Address out of bounds. TSA is %x\n", TSA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TSA &= 0xfffff;
|
TSA &= 0xfffff;
|
||||||
|
|
||||||
bool adma_enable = ((AutoDMACtrl & (Index + 1)) == (Index + 1));
|
bool adma_enable = ((AutoDMACtrl & (Index + 1)) == (Index + 1));
|
||||||
|
|
||||||
if (adma_enable) {
|
if (adma_enable)
|
||||||
TSA &= 0x1fff;
|
{
|
||||||
StartADMAWrite(pMem, size);
|
TSA &= 0x1fff;
|
||||||
} else {
|
StartADMAWrite(pMem, size);
|
||||||
if (MsgDMA())
|
}
|
||||||
ConLog("* SPU2-X: DMA%c Transfer of %d bytes to %x (%02x %x %04x). IRQE = %d IRQA = %x \n",
|
else
|
||||||
GetDmaIndexChar(), size << 1, TSA, DMABits, AutoDMACtrl, (~Regs.ATTR) & 0x7fff,
|
{
|
||||||
Cores[0].IRQEnable, Cores[0].IRQA);
|
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);
|
PlainDMAWrite(pMem, size);
|
||||||
}
|
}
|
||||||
Regs.STATX &= ~0x80;
|
Regs.STATX &= ~0x80;
|
||||||
//Regs.ATTR |= 0x30;
|
//Regs.ATTR |= 0x30;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
extern void DMALogOpen();
|
extern void DMALogOpen();
|
||||||
extern void DMA4LogWrite(void *lpData, u32 ulSize);
|
extern void DMA4LogWrite(void* lpData, u32 ulSize);
|
||||||
extern void DMA7LogWrite(void *lpData, u32 ulSize);
|
extern void DMA7LogWrite(void* lpData, u32 ulSize);
|
||||||
extern void DMALogClose();
|
extern void DMALogClose();
|
||||||
|
|
|
@ -57,114 +57,114 @@ const float AddCLR = 0.20f * Scale; // Stereo expansion
|
||||||
|
|
||||||
extern void ResetDplIIDecoder()
|
extern void ResetDplIIDecoder()
|
||||||
{
|
{
|
||||||
Gfl = 0;
|
Gfl = 0;
|
||||||
Gfr = 0;
|
Gfr = 0;
|
||||||
LMax = 0;
|
LMax = 0;
|
||||||
RMax = 0;
|
RMax = 0;
|
||||||
AccL = 0;
|
AccL = 0;
|
||||||
AccR = 0;
|
AccR = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessDplIISample32(const StereoOut32 &src, Stereo51Out32DplII *s)
|
void ProcessDplIISample32(const StereoOut32& src, Stereo51Out32DplII* s)
|
||||||
{
|
{
|
||||||
float IL = src.Left / (float)(1 << (SndOutVolumeShift + 16));
|
float IL = src.Left / (float)(1 << (SndOutVolumeShift + 16));
|
||||||
float IR = src.Right / (float)(1 << (SndOutVolumeShift + 16));
|
float IR = src.Right / (float)(1 << (SndOutVolumeShift + 16));
|
||||||
|
|
||||||
// Calculate center channel and LFE
|
// Calculate center channel and LFE
|
||||||
float C = (IL + IR) * 0.5f;
|
float C = (IL + IR) * 0.5f;
|
||||||
float SUB = C; // no need to lowpass, the speaker amplifier should take care of it
|
float SUB = C; // no need to lowpass, the speaker amplifier should take care of it
|
||||||
|
|
||||||
float L = IL - C; // Effective L/R data
|
float L = IL - C; // Effective L/R data
|
||||||
float R = IR - C;
|
float R = IR - C;
|
||||||
|
|
||||||
// Peak L/R
|
// Peak L/R
|
||||||
float PL = std::abs(L);
|
float PL = std::abs(L);
|
||||||
float PR = std::abs(R);
|
float PR = std::abs(R);
|
||||||
|
|
||||||
AccL += (PL - AccL) * 0.1f;
|
AccL += (PL - AccL) * 0.1f;
|
||||||
AccR += (PR - AccR) * 0.1f;
|
AccR += (PR - AccR) * 0.1f;
|
||||||
|
|
||||||
// Calculate power balance
|
// Calculate power balance
|
||||||
float Balance = (AccR - AccL); // -1 .. 1
|
float Balance = (AccR - AccL); // -1 .. 1
|
||||||
|
|
||||||
// If the power levels are different, then the audio is meant for the front speakers
|
// If the power levels are different, then the audio is meant for the front speakers
|
||||||
float Frontness = std::abs(Balance);
|
float Frontness = std::abs(Balance);
|
||||||
float Rearness = 1 - Frontness; // And the other way around
|
float Rearness = 1 - Frontness; // And the other way around
|
||||||
|
|
||||||
// Equalize the power levels for L/R
|
// Equalize the power levels for L/R
|
||||||
float B = std::min(0.9f, std::max(-0.9f, Balance));
|
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 VL = L / (1 - B); // if B>0, it means R>L, so increase L, else decrease L
|
||||||
float VR = R / (1 + B); // vice-versa
|
float VR = R / (1 + B); // vice-versa
|
||||||
|
|
||||||
// 1.73+1.22 = 2.94; 2.94 = 0.34 = 0.9996; Close enough.
|
// 1.73+1.22 = 2.94; 2.94 = 0.34 = 0.9996; Close enough.
|
||||||
// The range for VL/VR is approximately 0..1,
|
// 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.
|
// But in the cases where VL/VR are > 0.5, Rearness is 0 so it should never overflow.
|
||||||
const float RearScale = 0.34f * 2;
|
const float RearScale = 0.34f * 2;
|
||||||
|
|
||||||
float SL = (VR * 1.73f - VL * 1.22f) * RearScale * Rearness;
|
float SL = (VR * 1.73f - VL * 1.22f) * RearScale * Rearness;
|
||||||
float SR = (VR * 1.22f - VL * 1.73f) * RearScale * Rearness;
|
float SR = (VR * 1.22f - VL * 1.73f) * RearScale * Rearness;
|
||||||
// Possible experiment: Play with stereo expension levels on rear
|
// Possible experiment: Play with stereo expension levels on rear
|
||||||
|
|
||||||
// Adjust the volume of the front speakers based on what we calculated above
|
// Adjust the volume of the front speakers based on what we calculated above
|
||||||
L *= Frontness;
|
L *= Frontness;
|
||||||
R *= Frontness;
|
R *= Frontness;
|
||||||
|
|
||||||
s32 CX = (s32)(C * AddCLR);
|
s32 CX = (s32)(C * AddCLR);
|
||||||
|
|
||||||
s->Left = (s32)(L * GainL) + CX;
|
s->Left = (s32)(L * GainL) + CX;
|
||||||
s->Right = (s32)(R * GainR) + CX;
|
s->Right = (s32)(R * GainR) + CX;
|
||||||
s->Center = (s32)(C * GainC);
|
s->Center = (s32)(C * GainC);
|
||||||
s->LFE = (s32)(SUB * GainLFE);
|
s->LFE = (s32)(SUB * GainLFE);
|
||||||
s->LeftBack = (s32)(SL * GainSL);
|
s->LeftBack = (s32)(SL * GainSL);
|
||||||
s->RightBack = (s32)(SR * GainSR);
|
s->RightBack = (s32)(SR * GainSR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessDplIISample16(const StereoOut32 &src, Stereo51Out16DplII *s)
|
void ProcessDplIISample16(const StereoOut32& src, Stereo51Out16DplII* s)
|
||||||
{
|
{
|
||||||
Stereo51Out32DplII ss;
|
Stereo51Out32DplII ss;
|
||||||
ProcessDplIISample32(src, &ss);
|
ProcessDplIISample32(src, &ss);
|
||||||
|
|
||||||
s->Left = ss.Left >> 16;
|
s->Left = ss.Left >> 16;
|
||||||
s->Right = ss.Right >> 16;
|
s->Right = ss.Right >> 16;
|
||||||
s->Center = ss.Center >> 16;
|
s->Center = ss.Center >> 16;
|
||||||
s->LFE = ss.LFE >> 16;
|
s->LFE = ss.LFE >> 16;
|
||||||
s->LeftBack = ss.LeftBack >> 16;
|
s->LeftBack = ss.LeftBack >> 16;
|
||||||
s->RightBack = ss.RightBack >> 16;
|
s->RightBack = ss.RightBack >> 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessDplSample32(const StereoOut32 &src, Stereo51Out32Dpl *s)
|
void ProcessDplSample32(const StereoOut32& src, Stereo51Out32Dpl* s)
|
||||||
{
|
{
|
||||||
float ValL = src.Left / (float)(1 << (SndOutVolumeShift + 16));
|
float ValL = src.Left / (float)(1 << (SndOutVolumeShift + 16));
|
||||||
float ValR = src.Right / (float)(1 << (SndOutVolumeShift + 16));
|
float ValR = src.Right / (float)(1 << (SndOutVolumeShift + 16));
|
||||||
|
|
||||||
float C = (ValL + ValR) * 0.5f; //+15.8
|
float C = (ValL + ValR) * 0.5f; //+15.8
|
||||||
float S = (ValL - ValR) * 0.5f;
|
float S = (ValL - ValR) * 0.5f;
|
||||||
|
|
||||||
float L = ValL - C; //+15.8
|
float L = ValL - C; //+15.8
|
||||||
float R = ValR - C;
|
float R = ValR - C;
|
||||||
|
|
||||||
float SUB = C;
|
float SUB = C;
|
||||||
|
|
||||||
s32 CX = (s32)(C * AddCLR); // +15.16
|
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->Left = (s32)(L * GainL) + CX; // +15.16 = +31, can grow to +32 if (GainL + AddCLR)>255
|
||||||
s->Right = (s32)(R * GainR) + CX;
|
s->Right = (s32)(R * GainR) + CX;
|
||||||
s->Center = (s32)(C * GainC); // +15.16 = +31
|
s->Center = (s32)(C * GainC); // +15.16 = +31
|
||||||
s->LFE = (s32)(SUB * GainLFE);
|
s->LFE = (s32)(SUB * GainLFE);
|
||||||
s->LeftBack = (s32)(S * GainSL);
|
s->LeftBack = (s32)(S * GainSL);
|
||||||
s->RightBack = (s32)(S * GainSR);
|
s->RightBack = (s32)(S * GainSR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessDplSample16(const StereoOut32 &src, Stereo51Out16Dpl *s)
|
void ProcessDplSample16(const StereoOut32& src, Stereo51Out16Dpl* s)
|
||||||
{
|
{
|
||||||
Stereo51Out32Dpl ss;
|
Stereo51Out32Dpl ss;
|
||||||
ProcessDplSample32(src, &ss);
|
ProcessDplSample32(src, &ss);
|
||||||
|
|
||||||
s->Left = ss.Left >> 16;
|
s->Left = ss.Left >> 16;
|
||||||
s->Right = ss.Right >> 16;
|
s->Right = ss.Right >> 16;
|
||||||
s->Center = ss.Center >> 16;
|
s->Center = ss.Center >> 16;
|
||||||
s->LFE = ss.LFE >> 16;
|
s->LFE = ss.LFE >> 16;
|
||||||
s->LeftBack = ss.LeftBack >> 16;
|
s->LeftBack = ss.LeftBack >> 16;
|
||||||
s->RightBack = ss.RightBack >> 16;
|
s->RightBack = ss.RightBack >> 16;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ struct V_Core;
|
||||||
|
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
class SoundTouch;
|
class SoundTouch;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
@ -44,9 +44,9 @@ class SoundTouch;
|
||||||
|
|
||||||
namespace VersionInfo
|
namespace VersionInfo
|
||||||
{
|
{
|
||||||
static const u8 Release = 2;
|
static const u8 Release = 2;
|
||||||
static const u8 Revision = 0; // increase that with each version
|
static const u8 Revision = 0; // increase that with each version
|
||||||
}
|
} // namespace VersionInfo
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// Override Win32 min/max macros with the STL's type safe and macro
|
// Override Win32 min/max macros with the STL's type safe and macro
|
||||||
|
@ -56,22 +56,22 @@ static const u8 Revision = 0; // increase that with each version
|
||||||
#undef max
|
#undef max
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static __forceinline void Clampify(T &src, T min, T max)
|
static __forceinline void Clampify(T& src, T min, T max)
|
||||||
{
|
{
|
||||||
src = std::min(std::max(src, min), max);
|
src = std::min(std::max(src, min), max);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static __forceinline T GetClamped(T src, T min, T max)
|
static __forceinline T GetClamped(T src, T min, T max)
|
||||||
{
|
{
|
||||||
return std::min(std::max(src, min), max);
|
return std::min(std::max(src, min), max);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __WXMAC__
|
#ifdef __WXMAC__
|
||||||
#else
|
#else
|
||||||
extern void SysMessage(const char *fmt, ...);
|
extern void SysMessage(const char* fmt, ...);
|
||||||
#endif
|
#endif
|
||||||
extern void SysMessage(const wchar_t *fmt, ...);
|
extern void SysMessage(const wchar_t* fmt, ...);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
// Dev / Debug conditionals --
|
// Dev / Debug conditionals --
|
||||||
|
@ -100,4 +100,3 @@ extern void SysMessage(const wchar_t *fmt, ...);
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
#include "SndOut.h"
|
#include "SndOut.h"
|
||||||
|
|
||||||
|
|
|
@ -27,226 +27,241 @@
|
||||||
class AlsaMod : public SndOutModule
|
class AlsaMod : public SndOutModule
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
static const int PacketsPerBuffer = 1; // increase this if ALSA can't keep up with 512-sample packets
|
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 MAX_BUFFER_COUNT = 4;
|
||||||
static const int NumBuffers = 4; // TODO: this should be configurable someday -- lower values reduce latency.
|
static const int NumBuffers = 4; // TODO: this should be configurable someday -- lower values reduce latency.
|
||||||
unsigned int pspeed;
|
unsigned int pspeed;
|
||||||
|
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t* handle;
|
||||||
snd_pcm_uframes_t buffer_size;
|
snd_pcm_uframes_t buffer_size;
|
||||||
snd_async_handler_t *pcm_callback;
|
snd_async_handler_t* pcm_callback;
|
||||||
|
|
||||||
uint period_time;
|
uint period_time;
|
||||||
uint buffer_time;
|
uint buffer_time;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Invoked by the static ExternalCallback method below.
|
// Invoked by the static ExternalCallback method below.
|
||||||
void _InternalCallback()
|
void _InternalCallback()
|
||||||
{
|
{
|
||||||
snd_pcm_sframes_t avail;
|
snd_pcm_sframes_t avail;
|
||||||
fprintf(stderr, "* SPU2-X:Iz in your internal callback.\n");
|
fprintf(stderr, "* SPU2-X:Iz in your internal callback.\n");
|
||||||
|
|
||||||
avail = snd_pcm_avail_update(handle);
|
avail = snd_pcm_avail_update(handle);
|
||||||
while (avail >= (int)period_time) {
|
while (avail >= (int)period_time)
|
||||||
StereoOut16 buff[PacketsPerBuffer * SndOutPacketSize];
|
{
|
||||||
StereoOut16 *p1 = buff;
|
StereoOut16 buff[PacketsPerBuffer * SndOutPacketSize];
|
||||||
|
StereoOut16* p1 = buff;
|
||||||
|
|
||||||
for (int p = 0; p < PacketsPerBuffer; p++, p1 += SndOutPacketSize)
|
for (int p = 0; p < PacketsPerBuffer; p++, p1 += SndOutPacketSize)
|
||||||
SndBuffer::ReadSamples(p1);
|
SndBuffer::ReadSamples(p1);
|
||||||
|
|
||||||
snd_pcm_writei(handle, buff, period_time);
|
snd_pcm_writei(handle, buff, period_time);
|
||||||
avail = snd_pcm_avail_update(handle);
|
avail = snd_pcm_avail_update(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preps and invokes the _InternalCallback above. This provides a cdecl-compliant
|
// Preps and invokes the _InternalCallback above. This provides a cdecl-compliant
|
||||||
// entry point for our C++ified object state. :)
|
// entry point for our C++ified object state. :)
|
||||||
static void ExternalCallback(snd_async_handler_t *pcm_call)
|
static void ExternalCallback(snd_async_handler_t* pcm_call)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "* SPU2-X:Iz in your external callback.\n");
|
fprintf(stderr, "* SPU2-X:Iz in your external callback.\n");
|
||||||
AlsaMod *data = (AlsaMod *)snd_async_handler_get_callback_private(pcm_call);
|
AlsaMod* data = (AlsaMod*)snd_async_handler_get_callback_private(pcm_call);
|
||||||
|
|
||||||
pxAssume(data != NULL);
|
pxAssume(data != NULL);
|
||||||
//pxAssume( data->handle == snd_async_handler_get_pcm(pcm_call) );
|
//pxAssume( data->handle == snd_async_handler_get_pcm(pcm_call) );
|
||||||
|
|
||||||
// Not sure if we just need an assert, or something like this:
|
// Not sure if we just need an assert, or something like this:
|
||||||
if (data->handle != snd_async_handler_get_pcm(pcm_call)) {
|
if (data->handle != snd_async_handler_get_pcm(pcm_call))
|
||||||
fprintf(stderr, "* SPU2-X: Failed to handle sound.\n");
|
{
|
||||||
return;
|
fprintf(stderr, "* SPU2-X: Failed to handle sound.\n");
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
data->_InternalCallback();
|
data->_InternalCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
s32 Init()
|
s32 Init()
|
||||||
{
|
{
|
||||||
//fprintf(stderr,"* SPU2-X: Initing Alsa\n");
|
//fprintf(stderr,"* SPU2-X: Initing Alsa\n");
|
||||||
snd_pcm_hw_params_t *hwparams;
|
snd_pcm_hw_params_t* hwparams;
|
||||||
snd_pcm_sw_params_t *swparams;
|
snd_pcm_sw_params_t* swparams;
|
||||||
snd_pcm_status_t *status;
|
snd_pcm_status_t* status;
|
||||||
int pchannels = 2;
|
int pchannels = 2;
|
||||||
snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
|
snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
|
||||||
|
|
||||||
handle = NULL;
|
handle = NULL;
|
||||||
pcm_callback = NULL;
|
pcm_callback = NULL;
|
||||||
pspeed = SAMPLE_RATE;
|
pspeed = SAMPLE_RATE;
|
||||||
|
|
||||||
// buffer time and period time are in microseconds...
|
// buffer time and period time are in microseconds...
|
||||||
// (don't simplify the equation below -- it'll just cause integer rounding errors.
|
// (don't simplify the equation below -- it'll just cause integer rounding errors.
|
||||||
period_time = (SndOutPacketSize * 1000) / (SampleRate / 1000);
|
period_time = (SndOutPacketSize * 1000) / (SampleRate / 1000);
|
||||||
buffer_time = period_time * NumBuffers;
|
buffer_time = period_time * NumBuffers;
|
||||||
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC /*| SND_PCM_NONBLOCK*/);
|
err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC /*| SND_PCM_NONBLOCK*/);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
fprintf(stderr, "Audio open error: %s\n", snd_strerror(err));
|
{
|
||||||
return -1;
|
fprintf(stderr, "Audio open error: %s\n", snd_strerror(err));
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
err = snd_pcm_nonblock(handle, 0);
|
err = snd_pcm_nonblock(handle, 0);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
fprintf(stderr, "Can't set blocking mode: %s\n", snd_strerror(err));
|
{
|
||||||
return -1;
|
fprintf(stderr, "Can't set blocking mode: %s\n", snd_strerror(err));
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
snd_pcm_hw_params_alloca(&hwparams);
|
snd_pcm_hw_params_alloca(&hwparams);
|
||||||
snd_pcm_sw_params_alloca(&swparams);
|
snd_pcm_sw_params_alloca(&swparams);
|
||||||
|
|
||||||
err = snd_pcm_hw_params_any(handle, hwparams);
|
err = snd_pcm_hw_params_any(handle, hwparams);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
fprintf(stderr, "Broken configuration for this PCM: %s\n", snd_strerror(err));
|
{
|
||||||
return -1;
|
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);
|
err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
fprintf(stderr, "Access type not available: %s\n", snd_strerror(err));
|
{
|
||||||
return -1;
|
fprintf(stderr, "Access type not available: %s\n", snd_strerror(err));
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
err = snd_pcm_hw_params_set_format(handle, hwparams, format);
|
err = snd_pcm_hw_params_set_format(handle, hwparams, format);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
fprintf(stderr, "Sample format not available: %s\n", snd_strerror(err));
|
{
|
||||||
return -1;
|
fprintf(stderr, "Sample format not available: %s\n", snd_strerror(err));
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
err = snd_pcm_hw_params_set_channels(handle, hwparams, pchannels);
|
err = snd_pcm_hw_params_set_channels(handle, hwparams, pchannels);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
fprintf(stderr, "Channels count not available: %s\n", snd_strerror(err));
|
{
|
||||||
return -1;
|
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) {
|
err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &pspeed, 0);
|
||||||
fprintf(stderr, "Rate not available: %s\n", snd_strerror(err));
|
if (err < 0)
|
||||||
return -1;
|
{
|
||||||
}
|
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);
|
err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, 0);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
fprintf(stderr, "Buffer time error: %s\n", snd_strerror(err));
|
{
|
||||||
return -1;
|
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);
|
err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, 0);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
fprintf(stderr, "Period time error: %s\n", snd_strerror(err));
|
{
|
||||||
return -1;
|
fprintf(stderr, "Period time error: %s\n", snd_strerror(err));
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
err = snd_pcm_hw_params(handle, hwparams);
|
err = snd_pcm_hw_params(handle, hwparams);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
fprintf(stderr, "Unable to install hw params: %s\n", snd_strerror(err));
|
{
|
||||||
return -1;
|
fprintf(stderr, "Unable to install hw params: %s\n", snd_strerror(err));
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
snd_pcm_status_alloca(&status);
|
snd_pcm_status_alloca(&status);
|
||||||
err = snd_pcm_status(handle, status);
|
err = snd_pcm_status(handle, status);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
fprintf(stderr, "Unable to get status: %s\n", snd_strerror(err));
|
{
|
||||||
return -1;
|
fprintf(stderr, "Unable to get status: %s\n", snd_strerror(err));
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Bind our asynchronous callback magic:
|
// Bind our asynchronous callback magic:
|
||||||
|
|
||||||
if (handle == NULL)
|
if (handle == NULL)
|
||||||
fprintf(stderr, "No handle.");
|
fprintf(stderr, "No handle.");
|
||||||
|
|
||||||
//fprintf(stderr,"* SPU2-X:Iz setting your internal callback.\n");
|
//fprintf(stderr,"* SPU2-X:Iz setting your internal callback.\n");
|
||||||
// The external handler never seems to get called after this.
|
// The external handler never seems to get called after this.
|
||||||
snd_async_add_pcm_handler(&pcm_callback, handle, ExternalCallback, this);
|
snd_async_add_pcm_handler(&pcm_callback, handle, ExternalCallback, this);
|
||||||
err = snd_pcm_start(handle);
|
err = snd_pcm_start(handle);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
fprintf(stderr, "Pcm start failed: %s\n", snd_strerror(err));
|
{
|
||||||
return -1;
|
fprintf(stderr, "Pcm start failed: %s\n", snd_strerror(err));
|
||||||
}
|
return -1;
|
||||||
// Diagnostic code:
|
}
|
||||||
//buffer_size = snd_pcm_status_get_avail(status);
|
// Diagnostic code:
|
||||||
|
//buffer_size = snd_pcm_status_get_avail(status);
|
||||||
|
|
||||||
//fprintf(stderr,"All set up.\n");
|
//fprintf(stderr,"All set up.\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Close()
|
void Close()
|
||||||
{
|
{
|
||||||
//fprintf(stderr,"* SPU2-X: Closing Alsa\n");
|
//fprintf(stderr,"* SPU2-X: Closing Alsa\n");
|
||||||
if (handle == NULL)
|
if (handle == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
snd_pcm_drop(handle);
|
snd_pcm_drop(handle);
|
||||||
snd_pcm_close(handle);
|
snd_pcm_close(handle);
|
||||||
handle = NULL;
|
handle = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Configure(uptr parent)
|
virtual void Configure(uptr parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool Is51Out() const { return false; }
|
virtual bool Is51Out() const { return false; }
|
||||||
|
|
||||||
s32 Test() const
|
s32 Test() const
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetEmptySampleCount()
|
int GetEmptySampleCount()
|
||||||
{
|
{
|
||||||
if (handle == NULL) {
|
if (handle == NULL)
|
||||||
fprintf(stderr, "Handle is NULL!\n");
|
{
|
||||||
return 0;
|
fprintf(stderr, "Handle is NULL!\n");
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the amount of free buffer space, in samples.
|
// Returns the amount of free buffer space, in samples.
|
||||||
int l = snd_pcm_avail_update(handle);
|
int l = snd_pcm_avail_update(handle);
|
||||||
if (l < 0)
|
if (l < 0)
|
||||||
return 0;
|
return 0;
|
||||||
return (l / 1000) * (SampleRate / 1000);
|
return (l / 1000) * (SampleRate / 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *GetIdent() const
|
const wchar_t* GetIdent() const
|
||||||
{
|
{
|
||||||
return L"Alsa";
|
return L"Alsa";
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *GetLongName() const
|
const wchar_t* GetLongName() const
|
||||||
{
|
{
|
||||||
return L"Alsa";
|
return L"Alsa";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadSettings()
|
void ReadSettings()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetApiSettings(wxString api)
|
void SetApiSettings(wxString api)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteSettings() const
|
void WriteSettings() const
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
} static Alsa;
|
} static Alsa;
|
||||||
|
|
||||||
SndOutModule *AlsaOut = &Alsa;
|
SndOutModule* AlsaOut = &Alsa;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -30,11 +30,11 @@
|
||||||
extern int AlsaSetupSound();
|
extern int AlsaSetupSound();
|
||||||
extern void AlsaRemoveSound();
|
extern void AlsaRemoveSound();
|
||||||
extern int AlsaSoundGetBytesBuffered();
|
extern int AlsaSoundGetBytesBuffered();
|
||||||
extern void AlsaSoundFeedVoiceData(unsigned char *pSound, long lBytes);
|
extern void AlsaSoundFeedVoiceData(unsigned char* pSound, long lBytes);
|
||||||
|
|
||||||
extern int SetupSound();
|
extern int SetupSound();
|
||||||
extern void RemoveSound();
|
extern void RemoveSound();
|
||||||
extern int SoundGetBytesBuffered();
|
extern int SoundGetBytesBuffered();
|
||||||
extern void SoundFeedVoiceData(unsigned char *pSound, long lBytes);
|
extern void SoundFeedVoiceData(unsigned char* pSound, long lBytes);
|
||||||
|
|
||||||
#endif // __LINUX_H__
|
#endif // __LINUX_H__
|
||||||
|
|
|
@ -16,91 +16,91 @@
|
||||||
#include "Dialogs.h"
|
#include "Dialogs.h"
|
||||||
#include <wx/fileconf.h>
|
#include <wx/fileconf.h>
|
||||||
|
|
||||||
wxFileConfig *spuConfig = nullptr;
|
wxFileConfig* spuConfig = nullptr;
|
||||||
wxString path(L"~/.config/PCSX2/inis/SPU2.ini");
|
wxString path(L"~/.config/PCSX2/inis/SPU2.ini");
|
||||||
bool pathSet = false;
|
bool pathSet = false;
|
||||||
|
|
||||||
void initIni()
|
void initIni()
|
||||||
{
|
{
|
||||||
if (spuConfig == nullptr)
|
if (spuConfig == nullptr)
|
||||||
spuConfig = new wxFileConfig(L"", L"", path, L"", wxCONFIG_USE_LOCAL_FILE);
|
spuConfig = new wxFileConfig(L"", L"", path, L"", wxCONFIG_USE_LOCAL_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setIni(const wchar_t *Section)
|
void setIni(const wchar_t* Section)
|
||||||
{
|
{
|
||||||
initIni();
|
initIni();
|
||||||
spuConfig->SetPath(wxsFormat(L"/%s", Section));
|
spuConfig->SetPath(wxsFormat(L"/%s", Section));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CfgSetSettingsDir(const char *dir)
|
void CfgSetSettingsDir(const char* dir)
|
||||||
{
|
{
|
||||||
FileLog("CfgSetSettingsDir(%s)\n", dir);
|
FileLog("CfgSetSettingsDir(%s)\n", dir);
|
||||||
path = wxString::FromUTF8(dir) + L"/SPU2.ini";
|
path = wxString::FromUTF8(dir) + L"/SPU2.ini";
|
||||||
pathSet = true;
|
pathSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CfgWriteBool(const wchar_t *Section, const wchar_t *Name, bool Value)
|
void CfgWriteBool(const wchar_t* Section, const wchar_t* Name, bool Value)
|
||||||
{
|
{
|
||||||
setIni(Section);
|
setIni(Section);
|
||||||
spuConfig->Write(Name, Value);
|
spuConfig->Write(Name, Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CfgWriteInt(const wchar_t *Section, const wchar_t *Name, int Value)
|
void CfgWriteInt(const wchar_t* Section, const wchar_t* Name, int Value)
|
||||||
{
|
{
|
||||||
setIni(Section);
|
setIni(Section);
|
||||||
spuConfig->Write(Name, Value);
|
spuConfig->Write(Name, Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CfgWriteFloat(const wchar_t *Section, const wchar_t *Name, float Value)
|
void CfgWriteFloat(const wchar_t* Section, const wchar_t* Name, float Value)
|
||||||
{
|
{
|
||||||
setIni(Section);
|
setIni(Section);
|
||||||
spuConfig->Write(Name, (double)Value);
|
spuConfig->Write(Name, (double)Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CfgWriteStr(const wchar_t *Section, const wchar_t *Name, const wxString &Data)
|
void CfgWriteStr(const wchar_t* Section, const wchar_t* Name, const wxString& Data)
|
||||||
{
|
{
|
||||||
setIni(Section);
|
setIni(Section);
|
||||||
spuConfig->Write(Name, Data);
|
spuConfig->Write(Name, Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CfgReadBool(const wchar_t *Section, const wchar_t *Name, bool Default)
|
bool CfgReadBool(const wchar_t* Section, const wchar_t* Name, bool Default)
|
||||||
{
|
{
|
||||||
bool ret;
|
bool ret;
|
||||||
|
|
||||||
setIni(Section);
|
setIni(Section);
|
||||||
spuConfig->Read(Name, &ret, Default);
|
spuConfig->Read(Name, &ret, Default);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CfgReadInt(const wchar_t *Section, const wchar_t *Name, int Default)
|
int CfgReadInt(const wchar_t* Section, const wchar_t* Name, int Default)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
setIni(Section);
|
setIni(Section);
|
||||||
spuConfig->Read(Name, &ret, Default);
|
spuConfig->Read(Name, &ret, Default);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
float CfgReadFloat(const wchar_t *Section, const wchar_t *Name, float Default)
|
float CfgReadFloat(const wchar_t* Section, const wchar_t* Name, float Default)
|
||||||
{
|
{
|
||||||
double ret;
|
double ret;
|
||||||
|
|
||||||
setIni(Section);
|
setIni(Section);
|
||||||
spuConfig->Read(Name, &ret, (double)Default);
|
spuConfig->Read(Name, &ret, (double)Default);
|
||||||
|
|
||||||
return (float)ret;
|
return (float)ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CfgReadStr(const wchar_t *Section, const wchar_t *Name, wchar_t *Data, int DataSize, const wchar_t *Default)
|
void CfgReadStr(const wchar_t* Section, const wchar_t* Name, wchar_t* Data, int DataSize, const wchar_t* Default)
|
||||||
{
|
{
|
||||||
setIni(Section);
|
setIni(Section);
|
||||||
wcscpy(Data, spuConfig->Read(Name, Default).wc_str());
|
wcscpy(Data, spuConfig->Read(Name, Default).wc_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CfgReadStr(const wchar_t *Section, const wchar_t *Name, wxString &Data, const wchar_t *Default)
|
void CfgReadStr(const wchar_t* Section, const wchar_t* Name, wxString& Data, const wchar_t* Default)
|
||||||
{
|
{
|
||||||
setIni(Section);
|
setIni(Section);
|
||||||
Data = spuConfig->Read(Name, Default);
|
Data = spuConfig->Read(Name, Default);
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,153 +79,157 @@ bool temp_debug_state;
|
||||||
|
|
||||||
void ReadSettings()
|
void ReadSettings()
|
||||||
{
|
{
|
||||||
// For some reason this can be called before we know what ini file we're writing to.
|
// 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.
|
// Lets not try to read it if that happens.
|
||||||
if (!pathSet) {
|
if (!pathSet)
|
||||||
FileLog("Read called without the path set.\n");
|
{
|
||||||
return;
|
FileLog("Read called without the path set.\n");
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Interpolation = CfgReadInt(L"MIXING", L"Interpolation", 4);
|
Interpolation = CfgReadInt(L"MIXING", L"Interpolation", 4);
|
||||||
EffectsDisabled = CfgReadBool(L"MIXING", L"Disable_Effects", false);
|
EffectsDisabled = CfgReadBool(L"MIXING", L"Disable_Effects", false);
|
||||||
postprocess_filter_dealias = CfgReadBool(L"MIXING", L"DealiasFilter", false);
|
postprocess_filter_dealias = CfgReadBool(L"MIXING", L"DealiasFilter", false);
|
||||||
FinalVolume = ((float)CfgReadInt(L"MIXING", L"FinalVolume", 100)) / 100;
|
FinalVolume = ((float)CfgReadInt(L"MIXING", L"FinalVolume", 100)) / 100;
|
||||||
if (FinalVolume > 1.0f)
|
if (FinalVolume > 1.0f)
|
||||||
FinalVolume = 1.0f;
|
FinalVolume = 1.0f;
|
||||||
|
|
||||||
AdvancedVolumeControl = CfgReadBool(L"MIXING", L"AdvancedVolumeControl", false);
|
AdvancedVolumeControl = CfgReadBool(L"MIXING", L"AdvancedVolumeControl", false);
|
||||||
VolumeAdjustCdb = CfgReadFloat(L"MIXING", L"VolumeAdjustC(dB)", 0);
|
VolumeAdjustCdb = CfgReadFloat(L"MIXING", L"VolumeAdjustC(dB)", 0);
|
||||||
VolumeAdjustFLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFL(dB)", 0);
|
VolumeAdjustFLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFL(dB)", 0);
|
||||||
VolumeAdjustFRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFR(dB)", 0);
|
VolumeAdjustFRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFR(dB)", 0);
|
||||||
VolumeAdjustBLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBL(dB)", 0);
|
VolumeAdjustBLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBL(dB)", 0);
|
||||||
VolumeAdjustBRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBR(dB)", 0);
|
VolumeAdjustBRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBR(dB)", 0);
|
||||||
VolumeAdjustSLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSL(dB)", 0);
|
VolumeAdjustSLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSL(dB)", 0);
|
||||||
VolumeAdjustSRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSR(dB)", 0);
|
VolumeAdjustSRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSR(dB)", 0);
|
||||||
VolumeAdjustLFEdb = CfgReadFloat(L"MIXING", L"VolumeAdjustLFE(dB)", 0);
|
VolumeAdjustLFEdb = CfgReadFloat(L"MIXING", L"VolumeAdjustLFE(dB)", 0);
|
||||||
VolumeAdjustC = powf(10, VolumeAdjustCdb / 10);
|
VolumeAdjustC = powf(10, VolumeAdjustCdb / 10);
|
||||||
VolumeAdjustFL = powf(10, VolumeAdjustFLdb / 10);
|
VolumeAdjustFL = powf(10, VolumeAdjustFLdb / 10);
|
||||||
VolumeAdjustFR = powf(10, VolumeAdjustFRdb / 10);
|
VolumeAdjustFR = powf(10, VolumeAdjustFRdb / 10);
|
||||||
VolumeAdjustBL = powf(10, VolumeAdjustBLdb / 10);
|
VolumeAdjustBL = powf(10, VolumeAdjustBLdb / 10);
|
||||||
VolumeAdjustBR = powf(10, VolumeAdjustBRdb / 10);
|
VolumeAdjustBR = powf(10, VolumeAdjustBRdb / 10);
|
||||||
VolumeAdjustSL = powf(10, VolumeAdjustSLdb / 10);
|
VolumeAdjustSL = powf(10, VolumeAdjustSLdb / 10);
|
||||||
VolumeAdjustSR = powf(10, VolumeAdjustSRdb / 10);
|
VolumeAdjustSR = powf(10, VolumeAdjustSRdb / 10);
|
||||||
VolumeAdjustLFE = powf(10, VolumeAdjustLFEdb / 10);
|
VolumeAdjustLFE = powf(10, VolumeAdjustLFEdb / 10);
|
||||||
delayCycles = CfgReadInt(L"DEBUG", L"DelayCycles", 4);
|
delayCycles = CfgReadInt(L"DEBUG", L"DelayCycles", 4);
|
||||||
|
|
||||||
wxString temp;
|
wxString temp;
|
||||||
|
|
||||||
#if SDL_MAJOR_VERSION >= 2 || !defined(SPU2X_PORTAUDIO)
|
#if SDL_MAJOR_VERSION >= 2 || !defined(SPU2X_PORTAUDIO)
|
||||||
CfgReadStr(L"OUTPUT", L"Output_Module", temp, SDLOut->GetIdent());
|
CfgReadStr(L"OUTPUT", L"Output_Module", temp, SDLOut->GetIdent());
|
||||||
#else
|
#else
|
||||||
CfgReadStr(L"OUTPUT", L"Output_Module", temp, PortaudioOut->GetIdent());
|
CfgReadStr(L"OUTPUT", L"Output_Module", temp, PortaudioOut->GetIdent());
|
||||||
#endif
|
#endif
|
||||||
OutputModule = FindOutputModuleById(temp.c_str()); // find the driver index of this module
|
OutputModule = FindOutputModuleById(temp.c_str()); // find the driver index of this module
|
||||||
|
|
||||||
// find current API
|
// find current API
|
||||||
#ifdef SPU2X_PORTAUDIO
|
#ifdef SPU2X_PORTAUDIO
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
CfgReadStr(L"PORTAUDIO", L"HostApi", temp, L"ALSA");
|
CfgReadStr(L"PORTAUDIO", L"HostApi", temp, L"ALSA");
|
||||||
if (temp == L"OSS")
|
if (temp == L"OSS")
|
||||||
OutputAPI = 1;
|
OutputAPI = 1;
|
||||||
else if (temp == L"JACK")
|
else if (temp == L"JACK")
|
||||||
OutputAPI = 2;
|
OutputAPI = 2;
|
||||||
else // L"ALSA"
|
else // L"ALSA"
|
||||||
OutputAPI = 0;
|
OutputAPI = 0;
|
||||||
#else
|
#else
|
||||||
CfgReadStr(L"PORTAUDIO", L"HostApi", temp, L"OSS");
|
CfgReadStr(L"PORTAUDIO", L"HostApi", temp, L"OSS");
|
||||||
OutputAPI = 0; // L"OSS"
|
OutputAPI = 0; // L"OSS"
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__unix__) || defined(__APPLE__)
|
#if defined(__unix__) || defined(__APPLE__)
|
||||||
CfgReadStr(L"SDL", L"HostApi", temp, L"pulseaudio");
|
CfgReadStr(L"SDL", L"HostApi", temp, L"pulseaudio");
|
||||||
SdlOutputAPI = 0;
|
SdlOutputAPI = 0;
|
||||||
#if SDL_MAJOR_VERSION >= 2
|
#if SDL_MAJOR_VERSION >= 2
|
||||||
// YES It sucks ...
|
// YES It sucks ...
|
||||||
for (int i = 0; i < SDL_GetNumAudioDrivers(); ++i) {
|
for (int i = 0; i < SDL_GetNumAudioDrivers(); ++i)
|
||||||
if (!temp.Cmp(wxString(SDL_GetAudioDriver(i), wxConvUTF8)))
|
{
|
||||||
SdlOutputAPI = i;
|
if (!temp.Cmp(wxString(SDL_GetAudioDriver(i), wxConvUTF8)))
|
||||||
}
|
SdlOutputAPI = i;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SndOutLatencyMS = CfgReadInt(L"OUTPUT", L"Latency", 300);
|
SndOutLatencyMS = CfgReadInt(L"OUTPUT", L"Latency", 300);
|
||||||
SynchMode = CfgReadInt(L"OUTPUT", L"Synch_Mode", 0);
|
SynchMode = CfgReadInt(L"OUTPUT", L"Synch_Mode", 0);
|
||||||
numSpeakers = CfgReadInt(L"OUTPUT", L"SpeakerConfiguration", 0);
|
numSpeakers = CfgReadInt(L"OUTPUT", L"SpeakerConfiguration", 0);
|
||||||
|
|
||||||
#ifdef SPU2X_PORTAUDIO
|
#ifdef SPU2X_PORTAUDIO
|
||||||
PortaudioOut->ReadSettings();
|
PortaudioOut->ReadSettings();
|
||||||
#endif
|
#endif
|
||||||
#if defined(__unix__) || defined(__APPLE__)
|
#if defined(__unix__) || defined(__APPLE__)
|
||||||
SDLOut->ReadSettings();
|
SDLOut->ReadSettings();
|
||||||
#endif
|
#endif
|
||||||
SoundtouchCfg::ReadSettings();
|
SoundtouchCfg::ReadSettings();
|
||||||
DebugConfig::ReadSettings();
|
DebugConfig::ReadSettings();
|
||||||
|
|
||||||
// Sanity Checks
|
// Sanity Checks
|
||||||
// -------------
|
// -------------
|
||||||
|
|
||||||
Clampify(SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX);
|
Clampify(SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX);
|
||||||
|
|
||||||
if (mods[OutputModule] == nullptr) {
|
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());
|
fwprintf(stderr, L"* SPU2-X: Unknown output module '%s' specified in configuration file.\n", temp.wc_str());
|
||||||
OutputModule = FindOutputModuleById(SDLOut->GetIdent());
|
fprintf(stderr, "* SPU2-X: Defaulting to SDL (%S).\n", SDLOut->GetIdent());
|
||||||
}
|
OutputModule = FindOutputModuleById(SDLOut->GetIdent());
|
||||||
|
}
|
||||||
|
|
||||||
WriteSettings();
|
WriteSettings();
|
||||||
spuConfig->Flush();
|
spuConfig->Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
void WriteSettings()
|
void WriteSettings()
|
||||||
{
|
{
|
||||||
if (!pathSet) {
|
if (!pathSet)
|
||||||
FileLog("Write called without the path set.\n");
|
{
|
||||||
return;
|
FileLog("Write called without the path set.\n");
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CfgWriteInt(L"MIXING", L"Interpolation", Interpolation);
|
CfgWriteInt(L"MIXING", L"Interpolation", Interpolation);
|
||||||
CfgWriteBool(L"MIXING", L"Disable_Effects", EffectsDisabled);
|
CfgWriteBool(L"MIXING", L"Disable_Effects", EffectsDisabled);
|
||||||
CfgWriteBool(L"MIXING", L"DealiasFilter", postprocess_filter_dealias);
|
CfgWriteBool(L"MIXING", L"DealiasFilter", postprocess_filter_dealias);
|
||||||
CfgWriteInt(L"MIXING", L"FinalVolume", (int)(FinalVolume * 100 + 0.5f));
|
CfgWriteInt(L"MIXING", L"FinalVolume", (int)(FinalVolume * 100 + 0.5f));
|
||||||
|
|
||||||
CfgWriteBool(L"MIXING", L"AdvancedVolumeControl", AdvancedVolumeControl);
|
CfgWriteBool(L"MIXING", L"AdvancedVolumeControl", AdvancedVolumeControl);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustC(dB)", VolumeAdjustCdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustC(dB)", VolumeAdjustCdb);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustFL(dB)", VolumeAdjustFLdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustFL(dB)", VolumeAdjustFLdb);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustFR(dB)", VolumeAdjustFRdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustFR(dB)", VolumeAdjustFRdb);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustBL(dB)", VolumeAdjustBLdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustBL(dB)", VolumeAdjustBLdb);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustBR(dB)", VolumeAdjustBRdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustBR(dB)", VolumeAdjustBRdb);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustSL(dB)", VolumeAdjustSLdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustSL(dB)", VolumeAdjustSLdb);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustSR(dB)", VolumeAdjustSRdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustSR(dB)", VolumeAdjustSRdb);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustLFE(dB)", VolumeAdjustLFEdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustLFE(dB)", VolumeAdjustLFEdb);
|
||||||
|
|
||||||
CfgWriteStr(L"OUTPUT", L"Output_Module", mods[OutputModule]->GetIdent());
|
CfgWriteStr(L"OUTPUT", L"Output_Module", mods[OutputModule]->GetIdent());
|
||||||
CfgWriteInt(L"OUTPUT", L"Latency", SndOutLatencyMS);
|
CfgWriteInt(L"OUTPUT", L"Latency", SndOutLatencyMS);
|
||||||
CfgWriteInt(L"OUTPUT", L"Synch_Mode", SynchMode);
|
CfgWriteInt(L"OUTPUT", L"Synch_Mode", SynchMode);
|
||||||
CfgWriteInt(L"OUTPUT", L"SpeakerConfiguration", numSpeakers);
|
CfgWriteInt(L"OUTPUT", L"SpeakerConfiguration", numSpeakers);
|
||||||
CfgWriteInt(L"DEBUG", L"DelayCycles", delayCycles);
|
CfgWriteInt(L"DEBUG", L"DelayCycles", delayCycles);
|
||||||
|
|
||||||
#ifdef SPU2X_PORTAUDIO
|
#ifdef SPU2X_PORTAUDIO
|
||||||
PortaudioOut->WriteSettings();
|
PortaudioOut->WriteSettings();
|
||||||
#endif
|
#endif
|
||||||
#if defined(__unix__) || defined(__APPLE__)
|
#if defined(__unix__) || defined(__APPLE__)
|
||||||
SDLOut->WriteSettings();
|
SDLOut->WriteSettings();
|
||||||
#endif
|
#endif
|
||||||
SoundtouchCfg::WriteSettings();
|
SoundtouchCfg::WriteSettings();
|
||||||
DebugConfig::WriteSettings();
|
DebugConfig::WriteSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void configure()
|
void configure()
|
||||||
{
|
{
|
||||||
auto *dialog = new Dialog;
|
auto* dialog = new Dialog;
|
||||||
|
|
||||||
initIni();
|
initIni();
|
||||||
ReadSettings();
|
ReadSettings();
|
||||||
dialog->Display();
|
dialog->Display();
|
||||||
WriteSettings();
|
WriteSettings();
|
||||||
delete spuConfig;
|
delete spuConfig;
|
||||||
spuConfig = nullptr;
|
spuConfig = nullptr;
|
||||||
wxDELETE(dialog);
|
wxDELETE(dialog);
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,21 +97,21 @@ const int LATENCY_MIN_TIMESTRETCH = 15;
|
||||||
|
|
||||||
namespace SoundtouchCfg
|
namespace SoundtouchCfg
|
||||||
{
|
{
|
||||||
extern const int SequenceLen_Min;
|
extern const int SequenceLen_Min;
|
||||||
extern const int SequenceLen_Max;
|
extern const int SequenceLen_Max;
|
||||||
|
|
||||||
extern const int SeekWindow_Min;
|
extern const int SeekWindow_Min;
|
||||||
extern const int SeekWindow_Max;
|
extern const int SeekWindow_Max;
|
||||||
|
|
||||||
extern const int Overlap_Min;
|
extern const int Overlap_Min;
|
||||||
extern const int Overlap_Max;
|
extern const int Overlap_Max;
|
||||||
|
|
||||||
extern int SequenceLenMS;
|
extern int SequenceLenMS;
|
||||||
extern int SeekWindowMS;
|
extern int SeekWindowMS;
|
||||||
extern int OverlapMS;
|
extern int OverlapMS;
|
||||||
|
|
||||||
void ReadSettings();
|
void ReadSettings();
|
||||||
void WriteSettings();
|
void WriteSettings();
|
||||||
}; // namespace SoundtouchCfg
|
}; // namespace SoundtouchCfg
|
||||||
|
|
||||||
void ReadSettings();
|
void ReadSettings();
|
||||||
|
@ -119,7 +119,7 @@ void WriteSettings();
|
||||||
void DisplayDialog();
|
void DisplayDialog();
|
||||||
|
|
||||||
void configure();
|
void configure();
|
||||||
extern wxFileConfig *spuConfig;
|
extern wxFileConfig* spuConfig;
|
||||||
extern bool pathSet;
|
extern bool pathSet;
|
||||||
extern void initIni();
|
extern void initIni();
|
||||||
#endif // CONFIG_H_INCLUDED
|
#endif // CONFIG_H_INCLUDED
|
||||||
|
|
|
@ -51,105 +51,105 @@ wxString CoresDumpFileName;
|
||||||
wxString MemDumpFileName;
|
wxString MemDumpFileName;
|
||||||
wxString RegDumpFileName;
|
wxString RegDumpFileName;
|
||||||
|
|
||||||
void CfgSetLogDir(const char *dir)
|
void CfgSetLogDir(const char* dir)
|
||||||
{
|
{
|
||||||
LogsFolder = (dir == NULL) ? wxString(L"logs") : fromUTF8(dir);
|
LogsFolder = (dir == NULL) ? wxString(L"logs") : fromUTF8(dir);
|
||||||
DumpsFolder = (dir == NULL) ? wxString(L"logs") : fromUTF8(dir);
|
DumpsFolder = (dir == NULL) ? wxString(L"logs") : fromUTF8(dir);
|
||||||
LogLocationSetByPcsx2 = (dir != NULL);
|
LogLocationSetByPcsx2 = (dir != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *OpenBinaryLog(const wxString &logfile)
|
FILE* OpenBinaryLog(const wxString& logfile)
|
||||||
{
|
{
|
||||||
return wxFopen(Path::Combine(LogsFolder, logfile), L"wb");
|
return wxFopen(Path::Combine(LogsFolder, logfile), L"wb");
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *OpenLog(const wxString &logfile)
|
FILE* OpenLog(const wxString& logfile)
|
||||||
{
|
{
|
||||||
return wxFopen(Path::Combine(LogsFolder, logfile), L"w");
|
return wxFopen(Path::Combine(LogsFolder, logfile), L"w");
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *OpenDump(const wxString &logfile)
|
FILE* OpenDump(const wxString& logfile)
|
||||||
{
|
{
|
||||||
return wxFopen(Path::Combine(DumpsFolder, logfile), L"w");
|
return wxFopen(Path::Combine(DumpsFolder, logfile), L"w");
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace DebugConfig
|
namespace DebugConfig
|
||||||
{
|
{
|
||||||
static const wchar_t *Section = L"DEBUG";
|
static const wchar_t* Section = L"DEBUG";
|
||||||
|
|
||||||
static void set_default_filenames()
|
static void set_default_filenames()
|
||||||
{
|
{
|
||||||
AccessLogFileName = L"SPU2Log.txt";
|
AccessLogFileName = L"SPU2Log.txt";
|
||||||
WaveLogFileName = L"SPU2log.wav";
|
WaveLogFileName = L"SPU2log.wav";
|
||||||
DMA4LogFileName = L"SPU2dma4.dat";
|
DMA4LogFileName = L"SPU2dma4.dat";
|
||||||
DMA7LogFileName = L"SPU2dma7.dat";
|
DMA7LogFileName = L"SPU2dma7.dat";
|
||||||
|
|
||||||
CoresDumpFileName = L"SPU2Cores.txt";
|
CoresDumpFileName = L"SPU2Cores.txt";
|
||||||
MemDumpFileName = L"SPU2mem.dat";
|
MemDumpFileName = L"SPU2mem.dat";
|
||||||
RegDumpFileName = L"SPU2regs.dat";
|
RegDumpFileName = L"SPU2regs.dat";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadSettings()
|
void ReadSettings()
|
||||||
{
|
{
|
||||||
DebugEnabled = CfgReadBool(Section, L"Global_Enable", 0);
|
DebugEnabled = CfgReadBool(Section, L"Global_Enable", 0);
|
||||||
_MsgToConsole = CfgReadBool(Section, L"Show_Messages", 0);
|
_MsgToConsole = CfgReadBool(Section, L"Show_Messages", 0);
|
||||||
_MsgKeyOnOff = CfgReadBool(Section, L"Show_Messages_Key_On_Off", 0);
|
_MsgKeyOnOff = CfgReadBool(Section, L"Show_Messages_Key_On_Off", 0);
|
||||||
_MsgVoiceOff = CfgReadBool(Section, L"Show_Messages_Voice_Off", 0);
|
_MsgVoiceOff = CfgReadBool(Section, L"Show_Messages_Voice_Off", 0);
|
||||||
_MsgDMA = CfgReadBool(Section, L"Show_Messages_DMA_Transfer", 0);
|
_MsgDMA = CfgReadBool(Section, L"Show_Messages_DMA_Transfer", 0);
|
||||||
_MsgAutoDMA = CfgReadBool(Section, L"Show_Messages_AutoDMA", 0);
|
_MsgAutoDMA = CfgReadBool(Section, L"Show_Messages_AutoDMA", 0);
|
||||||
_MsgOverruns = CfgReadBool(Section, L"Show_Messages_Overruns", 0);
|
_MsgOverruns = CfgReadBool(Section, L"Show_Messages_Overruns", 0);
|
||||||
_MsgCache = CfgReadBool(Section, L"Show_Messages_CacheStats", 0);
|
_MsgCache = CfgReadBool(Section, L"Show_Messages_CacheStats", 0);
|
||||||
|
|
||||||
_AccessLog = CfgReadBool(Section, L"Log_Register_Access", 0);
|
_AccessLog = CfgReadBool(Section, L"Log_Register_Access", 0);
|
||||||
_DMALog = CfgReadBool(Section, L"Log_DMA_Transfers", 0);
|
_DMALog = CfgReadBool(Section, L"Log_DMA_Transfers", 0);
|
||||||
_WaveLog = CfgReadBool(Section, L"Log_WAVE_Output", 0);
|
_WaveLog = CfgReadBool(Section, L"Log_WAVE_Output", 0);
|
||||||
|
|
||||||
_CoresDump = CfgReadBool(Section, L"Dump_Info", 0);
|
_CoresDump = CfgReadBool(Section, L"Dump_Info", 0);
|
||||||
_MemDump = CfgReadBool(Section, L"Dump_Memory", 0);
|
_MemDump = CfgReadBool(Section, L"Dump_Memory", 0);
|
||||||
_RegDump = CfgReadBool(Section, L"Dump_Regs", 0);
|
_RegDump = CfgReadBool(Section, L"Dump_Regs", 0);
|
||||||
|
|
||||||
set_default_filenames();
|
set_default_filenames();
|
||||||
|
|
||||||
CfgReadStr(Section, L"Access_Log_Filename", AccessLogFileName, L"logs/SPU2Log.txt");
|
CfgReadStr(Section, L"Access_Log_Filename", AccessLogFileName, L"logs/SPU2Log.txt");
|
||||||
CfgReadStr(Section, L"WaveLog_Filename", WaveLogFileName, L"logs/SPU2log.wav");
|
CfgReadStr(Section, L"WaveLog_Filename", WaveLogFileName, L"logs/SPU2log.wav");
|
||||||
CfgReadStr(Section, L"DMA4Log_Filename", DMA4LogFileName, L"logs/SPU2dma4.dat");
|
CfgReadStr(Section, L"DMA4Log_Filename", DMA4LogFileName, L"logs/SPU2dma4.dat");
|
||||||
CfgReadStr(Section, L"DMA7Log_Filename", DMA7LogFileName, L"logs/SPU2dma7.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"Info_Dump_Filename", CoresDumpFileName, L"logs/SPU2Cores.txt");
|
||||||
CfgReadStr(Section, L"Mem_Dump_Filename", MemDumpFileName, L"logs/SPU2mem.dat");
|
CfgReadStr(Section, L"Mem_Dump_Filename", MemDumpFileName, L"logs/SPU2mem.dat");
|
||||||
CfgReadStr(Section, L"Reg_Dump_Filename", RegDumpFileName, L"logs/SPU2regs.dat");
|
CfgReadStr(Section, L"Reg_Dump_Filename", RegDumpFileName, L"logs/SPU2regs.dat");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WriteSettings()
|
void WriteSettings()
|
||||||
{
|
{
|
||||||
CfgWriteBool(Section, L"Global_Enable", DebugEnabled);
|
CfgWriteBool(Section, L"Global_Enable", DebugEnabled);
|
||||||
|
|
||||||
CfgWriteBool(Section, L"Show_Messages", _MsgToConsole);
|
CfgWriteBool(Section, L"Show_Messages", _MsgToConsole);
|
||||||
CfgWriteBool(Section, L"Show_Messages_Key_On_Off", _MsgKeyOnOff);
|
CfgWriteBool(Section, L"Show_Messages_Key_On_Off", _MsgKeyOnOff);
|
||||||
CfgWriteBool(Section, L"Show_Messages_Voice_Off", _MsgVoiceOff);
|
CfgWriteBool(Section, L"Show_Messages_Voice_Off", _MsgVoiceOff);
|
||||||
CfgWriteBool(Section, L"Show_Messages_DMA_Transfer", _MsgDMA);
|
CfgWriteBool(Section, L"Show_Messages_DMA_Transfer", _MsgDMA);
|
||||||
CfgWriteBool(Section, L"Show_Messages_AutoDMA", _MsgAutoDMA);
|
CfgWriteBool(Section, L"Show_Messages_AutoDMA", _MsgAutoDMA);
|
||||||
CfgWriteBool(Section, L"Show_Messages_Overruns", _MsgOverruns);
|
CfgWriteBool(Section, L"Show_Messages_Overruns", _MsgOverruns);
|
||||||
CfgWriteBool(Section, L"Show_Messages_CacheStats", _MsgCache);
|
CfgWriteBool(Section, L"Show_Messages_CacheStats", _MsgCache);
|
||||||
|
|
||||||
CfgWriteBool(Section, L"Log_Register_Access", _AccessLog);
|
CfgWriteBool(Section, L"Log_Register_Access", _AccessLog);
|
||||||
CfgWriteBool(Section, L"Log_DMA_Transfers", _DMALog);
|
CfgWriteBool(Section, L"Log_DMA_Transfers", _DMALog);
|
||||||
CfgWriteBool(Section, L"Log_WAVE_Output", _WaveLog);
|
CfgWriteBool(Section, L"Log_WAVE_Output", _WaveLog);
|
||||||
|
|
||||||
CfgWriteBool(Section, L"Dump_Info", _CoresDump);
|
CfgWriteBool(Section, L"Dump_Info", _CoresDump);
|
||||||
CfgWriteBool(Section, L"Dump_Memory", _MemDump);
|
CfgWriteBool(Section, L"Dump_Memory", _MemDump);
|
||||||
CfgWriteBool(Section, L"Dump_Regs", _RegDump);
|
CfgWriteBool(Section, L"Dump_Regs", _RegDump);
|
||||||
|
|
||||||
set_default_filenames();
|
set_default_filenames();
|
||||||
CfgWriteStr(Section, L"Access_Log_Filename", AccessLogFileName);
|
CfgWriteStr(Section, L"Access_Log_Filename", AccessLogFileName);
|
||||||
CfgWriteStr(Section, L"WaveLog_Filename", WaveLogFileName);
|
CfgWriteStr(Section, L"WaveLog_Filename", WaveLogFileName);
|
||||||
CfgWriteStr(Section, L"DMA4Log_Filename", DMA4LogFileName);
|
CfgWriteStr(Section, L"DMA4Log_Filename", DMA4LogFileName);
|
||||||
CfgWriteStr(Section, L"DMA7Log_Filename", DMA7LogFileName);
|
CfgWriteStr(Section, L"DMA7Log_Filename", DMA7LogFileName);
|
||||||
|
|
||||||
CfgWriteStr(Section, L"Info_Dump_Filename", CoresDumpFileName);
|
CfgWriteStr(Section, L"Info_Dump_Filename", CoresDumpFileName);
|
||||||
CfgWriteStr(Section, L"Mem_Dump_Filename", MemDumpFileName);
|
CfgWriteStr(Section, L"Mem_Dump_Filename", MemDumpFileName);
|
||||||
CfgWriteStr(Section, L"Reg_Dump_Filename", RegDumpFileName);
|
CfgWriteStr(Section, L"Reg_Dump_Filename", RegDumpFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace DebugConfig
|
} // namespace DebugConfig
|
||||||
|
|
|
@ -20,49 +20,49 @@
|
||||||
|
|
||||||
namespace SoundtouchCfg
|
namespace SoundtouchCfg
|
||||||
{
|
{
|
||||||
// Timestretch Slider Bounds, Min/Max
|
// Timestretch Slider Bounds, Min/Max
|
||||||
const int SequenceLen_Min = 20;
|
const int SequenceLen_Min = 20;
|
||||||
const int SequenceLen_Max = 100;
|
const int SequenceLen_Max = 100;
|
||||||
|
|
||||||
const int SeekWindow_Min = 10;
|
const int SeekWindow_Min = 10;
|
||||||
const int SeekWindow_Max = 30;
|
const int SeekWindow_Max = 30;
|
||||||
|
|
||||||
const int Overlap_Min = 5;
|
const int Overlap_Min = 5;
|
||||||
const int Overlap_Max = 15;
|
const int Overlap_Max = 15;
|
||||||
|
|
||||||
int SequenceLenMS = 30;
|
int SequenceLenMS = 30;
|
||||||
int SeekWindowMS = 20;
|
int SeekWindowMS = 20;
|
||||||
int OverlapMS = 10;
|
int OverlapMS = 10;
|
||||||
|
|
||||||
static void ClampValues()
|
static void ClampValues()
|
||||||
{
|
{
|
||||||
Clampify(SequenceLenMS, SequenceLen_Min, SequenceLen_Max);
|
Clampify(SequenceLenMS, SequenceLen_Min, SequenceLen_Max);
|
||||||
Clampify(SeekWindowMS, SeekWindow_Min, SeekWindow_Max);
|
Clampify(SeekWindowMS, SeekWindow_Min, SeekWindow_Max);
|
||||||
Clampify(OverlapMS, Overlap_Min, Overlap_Max);
|
Clampify(OverlapMS, Overlap_Min, Overlap_Max);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplySettings(soundtouch::SoundTouch &sndtouch)
|
void ApplySettings(soundtouch::SoundTouch& sndtouch)
|
||||||
{
|
{
|
||||||
sndtouch.setSetting(SETTING_SEQUENCE_MS, SequenceLenMS);
|
sndtouch.setSetting(SETTING_SEQUENCE_MS, SequenceLenMS);
|
||||||
sndtouch.setSetting(SETTING_SEEKWINDOW_MS, SeekWindowMS);
|
sndtouch.setSetting(SETTING_SEEKWINDOW_MS, SeekWindowMS);
|
||||||
sndtouch.setSetting(SETTING_OVERLAP_MS, OverlapMS);
|
sndtouch.setSetting(SETTING_OVERLAP_MS, OverlapMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadSettings()
|
void ReadSettings()
|
||||||
{
|
{
|
||||||
SequenceLenMS = CfgReadInt(L"SOUNDTOUCH", L"SequenceLengthMS", 30);
|
SequenceLenMS = CfgReadInt(L"SOUNDTOUCH", L"SequenceLengthMS", 30);
|
||||||
SeekWindowMS = CfgReadInt(L"SOUNDTOUCH", L"SeekWindowMS", 20);
|
SeekWindowMS = CfgReadInt(L"SOUNDTOUCH", L"SeekWindowMS", 20);
|
||||||
OverlapMS = CfgReadInt(L"SOUNDTOUCH", L"OverlapMS", 10);
|
OverlapMS = CfgReadInt(L"SOUNDTOUCH", L"OverlapMS", 10);
|
||||||
|
|
||||||
ClampValues();
|
ClampValues();
|
||||||
WriteSettings();
|
WriteSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteSettings()
|
void WriteSettings()
|
||||||
{
|
{
|
||||||
CfgWriteInt(L"SOUNDTOUCH", L"SequenceLengthMS", SequenceLenMS);
|
CfgWriteInt(L"SOUNDTOUCH", L"SequenceLengthMS", SequenceLenMS);
|
||||||
CfgWriteInt(L"SOUNDTOUCH", L"SeekWindowMS", SeekWindowMS);
|
CfgWriteInt(L"SOUNDTOUCH", L"SeekWindowMS", SeekWindowMS);
|
||||||
CfgWriteInt(L"SOUNDTOUCH", L"OverlapMS", OverlapMS);
|
CfgWriteInt(L"SOUNDTOUCH", L"OverlapMS", OverlapMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace SoundtouchCfg
|
} // namespace SoundtouchCfg
|
||||||
|
|
|
@ -21,32 +21,32 @@
|
||||||
#if defined(__unix__)
|
#if defined(__unix__)
|
||||||
#include <wx/wx.h>
|
#include <wx/wx.h>
|
||||||
|
|
||||||
void SysMessage(const char *fmt, ...)
|
void SysMessage(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
va_list list;
|
va_list list;
|
||||||
char msg[512];
|
char msg[512];
|
||||||
|
|
||||||
va_start(list, fmt);
|
va_start(list, fmt);
|
||||||
vsprintf(msg, fmt, list);
|
vsprintf(msg, fmt, list);
|
||||||
va_end(list);
|
va_end(list);
|
||||||
|
|
||||||
if (msg[strlen(msg) - 1] == '\n')
|
if (msg[strlen(msg) - 1] == '\n')
|
||||||
msg[strlen(msg) - 1] = 0;
|
msg[strlen(msg) - 1] = 0;
|
||||||
|
|
||||||
wxMessageDialog dialog(nullptr, msg, "Info", wxOK);
|
wxMessageDialog dialog(nullptr, msg, "Info", wxOK);
|
||||||
dialog.ShowModal();
|
dialog.ShowModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysMessage(const wchar_t *fmt, ...)
|
void SysMessage(const wchar_t* fmt, ...)
|
||||||
{
|
{
|
||||||
va_list list;
|
va_list list;
|
||||||
va_start(list, fmt);
|
va_start(list, fmt);
|
||||||
wxString msg;
|
wxString msg;
|
||||||
msg.PrintfV(fmt, list);
|
msg.PrintfV(fmt, list);
|
||||||
va_end(list);
|
va_end(list);
|
||||||
|
|
||||||
wxMessageDialog dialog(nullptr, msg, "Info", wxOK);
|
wxMessageDialog dialog(nullptr, msg, "Info", wxOK);
|
||||||
dialog.ShowModal();
|
dialog.ShowModal();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ void DspUpdate()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 DspLoadLibrary(wchar_t *fileName, int modnum)
|
s32 DspLoadLibrary(wchar_t* fileName, int modnum)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,22 +21,22 @@
|
||||||
|
|
||||||
namespace DebugConfig
|
namespace DebugConfig
|
||||||
{
|
{
|
||||||
extern void ReadSettings();
|
extern void ReadSettings();
|
||||||
extern void WriteSettings();
|
extern void WriteSettings();
|
||||||
} // namespace DebugConfig
|
} // namespace DebugConfig
|
||||||
|
|
||||||
extern void CfgSetSettingsDir(const char *dir);
|
extern void CfgSetSettingsDir(const char* dir);
|
||||||
extern void CfgSetLogDir(const char *dir);
|
extern void CfgSetLogDir(const char* dir);
|
||||||
|
|
||||||
extern void CfgWriteBool(const wchar_t *Section, const wchar_t *Name, bool Value);
|
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 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 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 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 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, 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 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 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);
|
extern float CfgReadFloat(const wchar_t* Section, const wchar_t* Name, float Default);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -21,72 +21,72 @@
|
||||||
template <typename FloatType>
|
template <typename FloatType>
|
||||||
__forceinline LowPassFilter<FloatType>::LowPassFilter(FloatType freq, FloatType srate)
|
__forceinline LowPassFilter<FloatType>::LowPassFilter(FloatType freq, FloatType srate)
|
||||||
{
|
{
|
||||||
typedef FloatType FT;
|
typedef FloatType FT;
|
||||||
|
|
||||||
FloatType omega = (FT)2.0 * freq / srate;
|
FloatType omega = (FT)2.0 * freq / srate;
|
||||||
static const FloatType g = (FT)1.0;
|
static const FloatType g = (FT)1.0;
|
||||||
|
|
||||||
// calculating coefficients:
|
// calculating coefficients:
|
||||||
|
|
||||||
FloatType k, p, q, a;
|
FloatType k, p, q, a;
|
||||||
FloatType a0, a1, a2, a3, a4;
|
FloatType a0, a1, a2, a3, a4;
|
||||||
|
|
||||||
k = ((FT)4.0 * g - (FT)3.0) / (g + (FT)1.0);
|
k = ((FT)4.0 * g - (FT)3.0) / (g + (FT)1.0);
|
||||||
p = (FT)1.0 - (FT)0.25 * k;
|
p = (FT)1.0 - (FT)0.25 * k;
|
||||||
p *= p;
|
p *= p;
|
||||||
|
|
||||||
// LP:
|
// LP:
|
||||||
a = (FT)1.0 / (tan((FT)0.5 * omega) * ((FT)1.0 + p));
|
a = (FT)1.0 / (tan((FT)0.5 * omega) * ((FT)1.0 + p));
|
||||||
p = (FT)1.0 + a;
|
p = (FT)1.0 + a;
|
||||||
q = (FT)1.0 - a;
|
q = (FT)1.0 - a;
|
||||||
|
|
||||||
a0 = (FT)1.0 / (k + p * p * p * p);
|
a0 = (FT)1.0 / (k + p * p * p * p);
|
||||||
a1 = (FT)4.0 * (k + p * p * p * q);
|
a1 = (FT)4.0 * (k + p * p * p * q);
|
||||||
a2 = (FT)6.0 * (k + p * p * q * q);
|
a2 = (FT)6.0 * (k + p * p * q * q);
|
||||||
a3 = (FT)4.0 * (k + p * q * q * q);
|
a3 = (FT)4.0 * (k + p * q * q * q);
|
||||||
a4 = (k + q * q * q * q);
|
a4 = (k + q * q * q * q);
|
||||||
p = a0 * (k + (FT)1.0);
|
p = a0 * (k + (FT)1.0);
|
||||||
|
|
||||||
coef[0] = p;
|
coef[0] = p;
|
||||||
coef[1] = (FT)4.0 * p;
|
coef[1] = (FT)4.0 * p;
|
||||||
coef[2] = (FT)6.0 * p;
|
coef[2] = (FT)6.0 * p;
|
||||||
coef[3] = (FT)4.0 * p;
|
coef[3] = (FT)4.0 * p;
|
||||||
coef[4] = p;
|
coef[4] = p;
|
||||||
coef[5] = -a1 * a0;
|
coef[5] = -a1 * a0;
|
||||||
coef[6] = -a2 * a0;
|
coef[6] = -a2 * a0;
|
||||||
coef[7] = -a3 * a0;
|
coef[7] = -a3 * a0;
|
||||||
coef[8] = -a4 * a0;
|
coef[8] = -a4 * a0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processes a single sample into the LPF.
|
// Processes a single sample into the LPF.
|
||||||
template <typename FloatType>
|
template <typename FloatType>
|
||||||
__forceinline FloatType LowPassFilter<FloatType>::sample(FloatType inval)
|
__forceinline FloatType LowPassFilter<FloatType>::sample(FloatType inval)
|
||||||
{
|
{
|
||||||
const FloatType out = (coef[0] * inval) + d[0];
|
const FloatType out = (coef[0] * inval) + d[0];
|
||||||
d[0] = (coef[1] * inval) + (coef[5] * out) + d[1];
|
d[0] = (coef[1] * inval) + (coef[5] * out) + d[1];
|
||||||
d[1] = (coef[2] * inval) + (coef[6] * out) + d[2];
|
d[1] = (coef[2] * inval) + (coef[6] * out) + d[2];
|
||||||
d[2] = (coef[3] * inval) + (coef[7] * out) + d[3];
|
d[2] = (coef[3] * inval) + (coef[7] * out) + d[3];
|
||||||
d[3] = (coef[4] * inval) + (coef[8] * out);
|
d[3] = (coef[4] * inval) + (coef[8] * out);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
LowPassFilter32::LowPassFilter32(float freq, float srate)
|
LowPassFilter32::LowPassFilter32(float freq, float srate)
|
||||||
: impl_lpf(freq, srate)
|
: impl_lpf(freq, srate)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
LowPassFilter64::LowPassFilter64(double freq, double srate)
|
LowPassFilter64::LowPassFilter64(double freq, double srate)
|
||||||
: impl_lpf(freq, srate)
|
: impl_lpf(freq, srate)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
float LowPassFilter32::sample(float inval)
|
float LowPassFilter32::sample(float inval)
|
||||||
{
|
{
|
||||||
return impl_lpf.sample(inval);
|
return impl_lpf.sample(inval);
|
||||||
}
|
}
|
||||||
|
|
||||||
double LowPassFilter64::sample(double inval)
|
double LowPassFilter64::sample(double inval)
|
||||||
{
|
{
|
||||||
return impl_lpf.sample(inval);
|
return impl_lpf.sample(inval);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,25 +18,25 @@
|
||||||
template <typename FloatType>
|
template <typename FloatType>
|
||||||
struct LowPassFilter
|
struct LowPassFilter
|
||||||
{
|
{
|
||||||
FloatType coef[9];
|
FloatType coef[9];
|
||||||
FloatType d[4];
|
FloatType d[4];
|
||||||
|
|
||||||
LowPassFilter(FloatType freq, FloatType srate);
|
LowPassFilter(FloatType freq, FloatType srate);
|
||||||
FloatType sample(FloatType inval);
|
FloatType sample(FloatType inval);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LowPassFilter32
|
struct LowPassFilter32
|
||||||
{
|
{
|
||||||
LowPassFilter<float> impl_lpf;
|
LowPassFilter<float> impl_lpf;
|
||||||
|
|
||||||
LowPassFilter32(float freq, float srate);
|
LowPassFilter32(float freq, float srate);
|
||||||
float sample(float inval);
|
float sample(float inval);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LowPassFilter64
|
struct LowPassFilter64
|
||||||
{
|
{
|
||||||
LowPassFilter<double> impl_lpf;
|
LowPassFilter<double> impl_lpf;
|
||||||
|
|
||||||
LowPassFilter64(double freq, double srate);
|
LowPassFilter64(double freq, double srate);
|
||||||
double sample(double inval);
|
double sample(double inval);
|
||||||
};
|
};
|
||||||
|
|
1117
pcsx2/SPU2/Mixer.cpp
1117
pcsx2/SPU2/Mixer.cpp
File diff suppressed because it is too large
Load Diff
|
@ -21,112 +21,112 @@ extern float VolumeAdjustFR;
|
||||||
|
|
||||||
struct StereoOut32
|
struct StereoOut32
|
||||||
{
|
{
|
||||||
static StereoOut32 Empty;
|
static StereoOut32 Empty;
|
||||||
|
|
||||||
s32 Left;
|
s32 Left;
|
||||||
s32 Right;
|
s32 Right;
|
||||||
|
|
||||||
StereoOut32()
|
StereoOut32()
|
||||||
: Left(0)
|
: Left(0)
|
||||||
, Right(0)
|
, Right(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
StereoOut32(s32 left, s32 right)
|
StereoOut32(s32 left, s32 right)
|
||||||
: Left(left)
|
: Left(left)
|
||||||
, Right(right)
|
, Right(right)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
StereoOut32(const StereoOut16 &src);
|
StereoOut32(const StereoOut16& src);
|
||||||
explicit StereoOut32(const StereoOutFloat &src);
|
explicit StereoOut32(const StereoOutFloat& src);
|
||||||
|
|
||||||
StereoOut16 DownSample() const;
|
StereoOut16 DownSample() const;
|
||||||
|
|
||||||
StereoOut32 operator*(const int &factor) const
|
StereoOut32 operator*(const int& factor) const
|
||||||
{
|
{
|
||||||
return StereoOut32(
|
return StereoOut32(
|
||||||
Left * factor,
|
Left * factor,
|
||||||
Right * factor);
|
Right * factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
StereoOut32 &operator*=(const int &factor)
|
StereoOut32& operator*=(const int& factor)
|
||||||
{
|
{
|
||||||
Left *= factor;
|
Left *= factor;
|
||||||
Right *= factor;
|
Right *= factor;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
StereoOut32 operator+(const StereoOut32 &right) const
|
StereoOut32 operator+(const StereoOut32& right) const
|
||||||
{
|
{
|
||||||
return StereoOut32(
|
return StereoOut32(
|
||||||
Left + right.Left,
|
Left + right.Left,
|
||||||
Right + right.Right);
|
Right + right.Right);
|
||||||
}
|
}
|
||||||
|
|
||||||
StereoOut32 operator/(int src) const
|
StereoOut32 operator/(int src) const
|
||||||
{
|
{
|
||||||
return StereoOut32(Left / src, Right / src);
|
return StereoOut32(Left / src, Right / src);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResampleFrom(const StereoOut32 &src)
|
void ResampleFrom(const StereoOut32& src)
|
||||||
{
|
{
|
||||||
this->Left = src.Left << 2;
|
this->Left = src.Left << 2;
|
||||||
this->Right = src.Right << 2;
|
this->Right = src.Right << 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdjustFrom(const StereoOut32 &src)
|
void AdjustFrom(const StereoOut32& src)
|
||||||
{
|
{
|
||||||
ResampleFrom(src);
|
ResampleFrom(src);
|
||||||
|
|
||||||
Left = (s32)(Left * VolumeAdjustFL);
|
Left = (s32)(Left * VolumeAdjustFL);
|
||||||
Right = (s32)(Right * VolumeAdjustFR);
|
Right = (s32)(Right * VolumeAdjustFR);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FrequencyResponseFilter
|
struct FrequencyResponseFilter
|
||||||
{
|
{
|
||||||
static FrequencyResponseFilter Empty;
|
static FrequencyResponseFilter Empty;
|
||||||
|
|
||||||
StereoOut32 History_One_In;
|
StereoOut32 History_One_In;
|
||||||
StereoOut32 History_One_Out;
|
StereoOut32 History_One_Out;
|
||||||
StereoOut32 History_Two_In;
|
StereoOut32 History_Two_In;
|
||||||
StereoOut32 History_Two_Out;
|
StereoOut32 History_Two_Out;
|
||||||
|
|
||||||
s32 lx1;
|
s32 lx1;
|
||||||
s32 lx2;
|
s32 lx2;
|
||||||
s32 ly1;
|
s32 ly1;
|
||||||
s32 ly2;
|
s32 ly2;
|
||||||
|
|
||||||
float la0, la1, la2, lb1, lb2;
|
float la0, la1, la2, lb1, lb2;
|
||||||
float ha0, ha1, ha2, hb1, hb2;
|
float ha0, ha1, ha2, hb1, hb2;
|
||||||
|
|
||||||
FrequencyResponseFilter()
|
FrequencyResponseFilter()
|
||||||
: History_One_In(0, 0)
|
: History_One_In(0, 0)
|
||||||
, History_One_Out(0, 0)
|
, History_One_Out(0, 0)
|
||||||
, History_Two_In(0, 0)
|
, History_Two_In(0, 0)
|
||||||
, History_Two_Out(0, 0)
|
, History_Two_Out(0, 0)
|
||||||
, lx1(0)
|
, lx1(0)
|
||||||
, lx2(0)
|
, lx2(0)
|
||||||
, ly1(0)
|
, ly1(0)
|
||||||
, ly2(0)
|
, ly2(0)
|
||||||
|
|
||||||
, la0(1.00320890889339290000f)
|
, la0(1.00320890889339290000f)
|
||||||
, la1(-1.97516434134506300000f)
|
, la1(-1.97516434134506300000f)
|
||||||
, la2(0.97243484967313087000f)
|
, la2(0.97243484967313087000f)
|
||||||
, lb1(-1.97525280404731810000f)
|
, lb1(-1.97525280404731810000f)
|
||||||
, lb2(0.97555529586426892000f)
|
, lb2(0.97555529586426892000f)
|
||||||
|
|
||||||
, ha0(1.52690772687271160000f)
|
, ha0(1.52690772687271160000f)
|
||||||
, ha1(-1.62653918974914990000f) //-1.72 = "common equilizer curve" --____--
|
, ha1(-1.62653918974914990000f) //-1.72 = "common equilizer curve" --____--
|
||||||
, ha2(0.57997976029249387000f)
|
, ha2(0.57997976029249387000f)
|
||||||
, hb1(-0.80955590379048203000f)
|
, hb1(-0.80955590379048203000f)
|
||||||
, hb2(0.28990420120653748000f)
|
, hb2(0.28990420120653748000f)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void Mix();
|
extern void Mix();
|
||||||
extern s32 clamp_mix(s32 x, u8 bitshift = 0);
|
extern s32 clamp_mix(s32 x, u8 bitshift = 0);
|
||||||
|
|
||||||
extern StereoOut32 clamp_mix(const StereoOut32 &sample, u8 bitshift = 0);
|
extern StereoOut32 clamp_mix(const StereoOut32& sample, u8 bitshift = 0);
|
||||||
|
|
|
@ -30,145 +30,163 @@
|
||||||
//
|
//
|
||||||
StereoOut32 V_Core::ReadInput_HiFi()
|
StereoOut32 V_Core::ReadInput_HiFi()
|
||||||
{
|
{
|
||||||
if (psxmode)
|
if (psxmode)
|
||||||
ConLog("ReadInput_HiFi!!!!!\n");
|
ConLog("ReadInput_HiFi!!!!!\n");
|
||||||
InputPosRead &= ~1;
|
InputPosRead &= ~1;
|
||||||
//
|
//
|
||||||
//#ifdef PCM24_S1_INTERLEAVE
|
//#ifdef PCM24_S1_INTERLEAVE
|
||||||
// StereoOut32 retval(
|
// StereoOut32 retval(
|
||||||
// *((s32*)(ADMATempBuffer+(InputPosRead<<1))),
|
// *((s32*)(ADMATempBuffer+(InputPosRead<<1))),
|
||||||
// *((s32*)(ADMATempBuffer+(InputPosRead<<1)+2))
|
// *((s32*)(ADMATempBuffer+(InputPosRead<<1)+2))
|
||||||
// );
|
// );
|
||||||
//#else
|
//#else
|
||||||
// StereoOut32 retval(
|
// StereoOut32 retval(
|
||||||
// (s32&)(ADMATempBuffer[InputPosRead]),
|
// (s32&)(ADMATempBuffer[InputPosRead]),
|
||||||
// (s32&)(ADMATempBuffer[InputPosRead+0x200])
|
// (s32&)(ADMATempBuffer[InputPosRead+0x200])
|
||||||
// );
|
// );
|
||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
StereoOut32 retval(
|
StereoOut32 retval(
|
||||||
(s32 &)(*GetMemPtr(0x2000 + (Index << 10) + InputPosRead)),
|
(s32&)(*GetMemPtr(0x2000 + (Index << 10) + InputPosRead)),
|
||||||
(s32 &)(*GetMemPtr(0x2200 + (Index << 10) + InputPosRead)));
|
(s32&)(*GetMemPtr(0x2200 + (Index << 10) + InputPosRead)));
|
||||||
|
|
||||||
if (Index == 1) {
|
if (Index == 1)
|
||||||
// CDDA Mode:
|
{
|
||||||
// give 30 bit data (SndOut downsamples the rest of the way)
|
// CDDA Mode:
|
||||||
// HACKFIX: 28 bits seems better according to rama. I should take some time and do some
|
// give 30 bit data (SndOut downsamples the rest of the way)
|
||||||
// bitcounting on this one. --air
|
// HACKFIX: 28 bits seems better according to rama. I should take some time and do some
|
||||||
retval.Left >>= 4;
|
// bitcounting on this one. --air
|
||||||
retval.Right >>= 4;
|
retval.Left >>= 4;
|
||||||
}
|
retval.Right >>= 4;
|
||||||
|
}
|
||||||
|
|
||||||
InputPosRead += 2;
|
InputPosRead += 2;
|
||||||
|
|
||||||
// Why does CDDA mode check for InputPos == 0x100? In the old code, SPDIF mode did not but CDDA did.
|
// 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
|
// 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)) {
|
if ((InputPosRead == 0x100) || (InputPosRead >= 0x200))
|
||||||
AdmaInProgress = 0;
|
{
|
||||||
if (InputDataLeft >= 0x200) {
|
AdmaInProgress = 0;
|
||||||
|
if (InputDataLeft >= 0x200)
|
||||||
|
{
|
||||||
#ifdef PCM24_S1_INTERLEAVE
|
#ifdef PCM24_S1_INTERLEAVE
|
||||||
AutoDMAReadBuffer(1);
|
AutoDMAReadBuffer(1);
|
||||||
#else
|
#else
|
||||||
AutoDMAReadBuffer(0);
|
AutoDMAReadBuffer(0);
|
||||||
#endif
|
#endif
|
||||||
AdmaInProgress = 1;
|
AdmaInProgress = 1;
|
||||||
|
|
||||||
TSA = (Index << 10) + InputPosRead;
|
TSA = (Index << 10) + InputPosRead;
|
||||||
|
|
||||||
if (InputDataLeft < 0x200) {
|
if (InputDataLeft < 0x200)
|
||||||
FileLog("[%10d] %s AutoDMA%c block end.\n", (Index == 1) ? "CDDA" : "SPDIF", Cycles, GetDmaIndexChar());
|
{
|
||||||
|
FileLog("[%10d] %s AutoDMA%c block end.\n", (Index == 1) ? "CDDA" : "SPDIF", Cycles, GetDmaIndexChar());
|
||||||
|
|
||||||
if (IsDevBuild) {
|
if (IsDevBuild)
|
||||||
if (InputDataLeft > 0) {
|
{
|
||||||
if (MsgAutoDMA())
|
if (InputDataLeft > 0)
|
||||||
ConLog("WARNING: adma buffer didn't finish with a whole block!!\n");
|
{
|
||||||
}
|
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;
|
InputDataLeft = 0;
|
||||||
if (Index == 0) {
|
// Hack, kinda. We call the interrupt early here, since PCSX2 doesn't like them delayed.
|
||||||
if(!SPU2_dummy_callback)
|
//DMAICounter = 1;
|
||||||
spu2DMA4Irq();
|
if (Index == 0)
|
||||||
else
|
{
|
||||||
SPU2interruptDMA4();
|
if (!SPU2_dummy_callback)
|
||||||
} else {
|
spu2DMA4Irq();
|
||||||
if(!SPU2_dummy_callback)
|
else
|
||||||
spu2DMA7Irq();
|
SPU2interruptDMA4();
|
||||||
else
|
}
|
||||||
SPU2interruptDMA7();
|
else
|
||||||
}
|
{
|
||||||
}
|
if (!SPU2_dummy_callback)
|
||||||
}
|
spu2DMA7Irq();
|
||||||
InputPosRead &= 0x1ff;
|
else
|
||||||
}
|
SPU2interruptDMA7();
|
||||||
return retval;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InputPosRead &= 0x1ff;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
StereoOut32 V_Core::ReadInput()
|
StereoOut32 V_Core::ReadInput()
|
||||||
{
|
{
|
||||||
StereoOut32 retval;
|
StereoOut32 retval;
|
||||||
|
|
||||||
if ((Index != 1) || ((PlayMode & 2) == 0)) {
|
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))
|
for (int i = 0; i < 2; i++)
|
||||||
SetIrqCall(i);
|
if (Cores[i].IRQEnable && 0x2000 + (Index << 10) + InputPosRead == (Cores[i].IRQA & 0xfffffdff))
|
||||||
|
SetIrqCall(i);
|
||||||
|
|
||||||
//retval = StereoOut32(
|
//retval = StereoOut32(
|
||||||
// (s32)ADMATempBuffer[InputPosRead],
|
// (s32)ADMATempBuffer[InputPosRead],
|
||||||
// (s32)ADMATempBuffer[InputPosRead+0x200]
|
// (s32)ADMATempBuffer[InputPosRead+0x200]
|
||||||
//);
|
//);
|
||||||
retval = StereoOut32(
|
retval = StereoOut32(
|
||||||
(s32)(*GetMemPtr(0x2000 + (Index << 10) + InputPosRead)),
|
(s32)(*GetMemPtr(0x2000 + (Index << 10) + InputPosRead)),
|
||||||
(s32)(*GetMemPtr(0x2200 + (Index << 10) + InputPosRead)));
|
(s32)(*GetMemPtr(0x2200 + (Index << 10) + InputPosRead)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef PCSX2_DEVBUILD
|
#ifdef PCSX2_DEVBUILD
|
||||||
DebugCores[Index].admaWaveformL[InputPosRead % 0x100] = retval.Left;
|
DebugCores[Index].admaWaveformL[InputPosRead % 0x100] = retval.Left;
|
||||||
DebugCores[Index].admaWaveformR[InputPosRead % 0x100] = retval.Right;
|
DebugCores[Index].admaWaveformR[InputPosRead % 0x100] = retval.Right;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
InputPosRead++;
|
InputPosRead++;
|
||||||
|
|
||||||
if (AutoDMACtrl & (Index + 1) && (InputPosRead == 0x100 || InputPosRead == 0x200)) {
|
if (AutoDMACtrl & (Index + 1) && (InputPosRead == 0x100 || InputPosRead == 0x200))
|
||||||
AdmaInProgress = 0;
|
{
|
||||||
if (InputDataLeft >= 0x200) {
|
AdmaInProgress = 0;
|
||||||
//u8 k=InputDataLeft>=InputDataProgress;
|
if (InputDataLeft >= 0x200)
|
||||||
|
{
|
||||||
|
//u8 k=InputDataLeft>=InputDataProgress;
|
||||||
|
|
||||||
AutoDMAReadBuffer(0);
|
AutoDMAReadBuffer(0);
|
||||||
|
|
||||||
AdmaInProgress = 1;
|
AdmaInProgress = 1;
|
||||||
TSA = (Index << 10) + InputPosRead;
|
TSA = (Index << 10) + InputPosRead;
|
||||||
|
|
||||||
if (InputDataLeft < 0x200) {
|
if (InputDataLeft < 0x200)
|
||||||
AutoDMACtrl |= ~3;
|
{
|
||||||
|
AutoDMACtrl |= ~3;
|
||||||
|
|
||||||
if (IsDevBuild) {
|
if (IsDevBuild)
|
||||||
FileLog("[%10d] AutoDMA%c block end.\n", Cycles, GetDmaIndexChar());
|
{
|
||||||
if (InputDataLeft > 0) {
|
FileLog("[%10d] AutoDMA%c block end.\n", Cycles, GetDmaIndexChar());
|
||||||
if (MsgAutoDMA())
|
if (InputDataLeft > 0)
|
||||||
ConLog("WARNING: adma buffer didn't finish with a whole block!!\n");
|
{
|
||||||
}
|
if (MsgAutoDMA())
|
||||||
}
|
ConLog("WARNING: adma buffer didn't finish with a whole block!!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
InputDataLeft = 0;
|
InputDataLeft = 0;
|
||||||
// Hack, kinda. We call the interrupt early here, since PCSX2 doesn't like them delayed.
|
// Hack, kinda. We call the interrupt early here, since PCSX2 doesn't like them delayed.
|
||||||
//DMAICounter = 1;
|
//DMAICounter = 1;
|
||||||
if (Index == 0) {
|
if (Index == 0)
|
||||||
if(!SPU2_dummy_callback)
|
{
|
||||||
spu2DMA4Irq();
|
if (!SPU2_dummy_callback)
|
||||||
else
|
spu2DMA4Irq();
|
||||||
SPU2interruptDMA4();
|
else
|
||||||
} else {
|
SPU2interruptDMA4();
|
||||||
if(!SPU2_dummy_callback)
|
}
|
||||||
spu2DMA7Irq();
|
else
|
||||||
else
|
{
|
||||||
SPU2interruptDMA7();
|
if (!SPU2_dummy_callback)
|
||||||
}
|
spu2DMA7Irq();
|
||||||
}
|
else
|
||||||
}
|
SPU2interruptDMA7();
|
||||||
}
|
}
|
||||||
InputPosRead &= 0x1ff;
|
}
|
||||||
return retval;
|
}
|
||||||
|
}
|
||||||
|
InputPosRead &= 0x1ff;
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,281 +15,295 @@
|
||||||
|
|
||||||
#include "Global.h"
|
#include "Global.h"
|
||||||
|
|
||||||
const char *ParamNames[8] = {"VOLL", "VOLR", "PITCH", "ADSR1", "ADSR2", "ENVX", "VOLXL", "VOLXR"};
|
const char* ParamNames[8] = {"VOLL", "VOLR", "PITCH", "ADSR1", "ADSR2", "ENVX", "VOLXL", "VOLXR"};
|
||||||
const char *AddressNames[6] = {"SSAH", "SSAL", "LSAH", "LSAL", "NAXH", "NAXL"};
|
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)
|
__forceinline void _RegLog_(const char* action, int level, const char* RName, u32 mem, u32 core, u16 value)
|
||||||
{
|
{
|
||||||
if (level > 1)
|
if (level > 1)
|
||||||
FileLog("[%10d] SPU2 %s mem %08x (core %d, register %s) value %04x\n",
|
FileLog("[%10d] SPU2 %s mem %08x (core %d, register %s) value %04x\n",
|
||||||
Cycles, action, mem, core, RName, value);
|
Cycles, action, mem, core, RName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define RegLog(lev, rname, mem, core, val) _RegLog_(action, lev, rname, mem, core, val)
|
#define RegLog(lev, rname, mem, core, val) _RegLog_(action, lev, rname, mem, core, val)
|
||||||
|
|
||||||
void SPU2writeLog(const char *action, u32 rmem, u16 value)
|
void SPU2writeLog(const char* action, u32 rmem, u16 value)
|
||||||
{
|
{
|
||||||
if (!IsDevBuild)
|
if (!IsDevBuild)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//u32 vx=0, vc=0;
|
//u32 vx=0, vc=0;
|
||||||
u32 core = 0, omem, mem;
|
u32 core = 0, omem, mem;
|
||||||
omem = mem = rmem & 0x7FF; //FFFF;
|
omem = mem = rmem & 0x7FF; //FFFF;
|
||||||
if (mem & 0x400) {
|
if (mem & 0x400)
|
||||||
omem ^= 0x400;
|
{
|
||||||
core = 1;
|
omem ^= 0x400;
|
||||||
}
|
core = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (omem < 0x0180) // Voice Params (VP)
|
if (omem < 0x0180) // Voice Params (VP)
|
||||||
{
|
{
|
||||||
const u32 voice = (omem & 0x1F0) >> 4;
|
const u32 voice = (omem & 0x1F0) >> 4;
|
||||||
const u32 param = (omem & 0xF) >> 1;
|
const u32 param = (omem & 0xF) >> 1;
|
||||||
char dest[192];
|
char dest[192];
|
||||||
sprintf(dest, "Voice %d %s", voice, ParamNames[param]);
|
sprintf(dest, "Voice %d %s", voice, ParamNames[param]);
|
||||||
RegLog(2, dest, rmem, core, value);
|
RegLog(2, dest, rmem, core, value);
|
||||||
} else if ((omem >= 0x01C0) && (omem < 0x02E0)) // Voice Addressing Params (VA)
|
}
|
||||||
{
|
else if ((omem >= 0x01C0) && (omem < 0x02E0)) // Voice Addressing Params (VA)
|
||||||
const u32 voice = ((omem - 0x01C0) / 12);
|
{
|
||||||
const u32 address = ((omem - 0x01C0) % 12) >> 1;
|
const u32 voice = ((omem - 0x01C0) / 12);
|
||||||
|
const u32 address = ((omem - 0x01C0) % 12) >> 1;
|
||||||
|
|
||||||
char dest[192];
|
char dest[192];
|
||||||
sprintf(dest, "Voice %d %s", voice, AddressNames[address]);
|
sprintf(dest, "Voice %d %s", voice, AddressNames[address]);
|
||||||
RegLog(2, dest, rmem, core, value);
|
RegLog(2, dest, rmem, core, value);
|
||||||
} else if ((mem >= 0x0760) && (mem < 0x07b0)) {
|
}
|
||||||
omem = mem;
|
else if ((mem >= 0x0760) && (mem < 0x07b0))
|
||||||
core = 0;
|
{
|
||||||
if (mem >= 0x0788) {
|
omem = mem;
|
||||||
omem -= 0x28;
|
core = 0;
|
||||||
core = 1;
|
if (mem >= 0x0788)
|
||||||
}
|
{
|
||||||
switch (omem) {
|
omem -= 0x28;
|
||||||
case REG_P_EVOLL:
|
core = 1;
|
||||||
RegLog(2, "EVOLL", rmem, core, value);
|
}
|
||||||
break;
|
switch (omem)
|
||||||
case REG_P_EVOLR:
|
{
|
||||||
RegLog(2, "EVOLR", rmem, core, value);
|
case REG_P_EVOLL:
|
||||||
break;
|
RegLog(2, "EVOLL", rmem, core, value);
|
||||||
case REG_P_AVOLL:
|
break;
|
||||||
if (core) {
|
case REG_P_EVOLR:
|
||||||
RegLog(2, "AVOLL", rmem, core, value);
|
RegLog(2, "EVOLR", rmem, core, value);
|
||||||
}
|
break;
|
||||||
break;
|
case REG_P_AVOLL:
|
||||||
case REG_P_AVOLR:
|
if (core)
|
||||||
if (core) {
|
{
|
||||||
RegLog(2, "AVOLR", rmem, core, value);
|
RegLog(2, "AVOLL", rmem, core, value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REG_P_BVOLL:
|
case REG_P_AVOLR:
|
||||||
RegLog(2, "BVOLL", rmem, core, value);
|
if (core)
|
||||||
break;
|
{
|
||||||
case REG_P_BVOLR:
|
RegLog(2, "AVOLR", rmem, core, value);
|
||||||
RegLog(2, "BVOLR", rmem, core, value);
|
}
|
||||||
break;
|
break;
|
||||||
case REG_P_MVOLXL:
|
case REG_P_BVOLL:
|
||||||
RegLog(2, "MVOLXL", rmem, core, value);
|
RegLog(2, "BVOLL", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case REG_P_MVOLXR:
|
case REG_P_BVOLR:
|
||||||
RegLog(2, "MVOLXR", rmem, core, value);
|
RegLog(2, "BVOLR", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case R_IIR_VOL:
|
case REG_P_MVOLXL:
|
||||||
RegLog(2, "IIR_VOL", rmem, core, value);
|
RegLog(2, "MVOLXL", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case R_COMB1_VOL:
|
case REG_P_MVOLXR:
|
||||||
RegLog(2, "COMB1_VOL", rmem, core, value);
|
RegLog(2, "MVOLXR", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case R_COMB2_VOL:
|
case R_IIR_VOL:
|
||||||
RegLog(2, "COMB2_VOL", rmem, core, value);
|
RegLog(2, "IIR_VOL", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case R_COMB3_VOL:
|
case R_COMB1_VOL:
|
||||||
RegLog(2, "COMB3_VOL", rmem, core, value);
|
RegLog(2, "COMB1_VOL", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case R_COMB4_VOL:
|
case R_COMB2_VOL:
|
||||||
RegLog(2, "COMB4_VOL", rmem, core, value);
|
RegLog(2, "COMB2_VOL", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case R_WALL_VOL:
|
case R_COMB3_VOL:
|
||||||
RegLog(2, "WALL_VOL", rmem, core, value);
|
RegLog(2, "COMB3_VOL", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case R_APF1_VOL:
|
case R_COMB4_VOL:
|
||||||
RegLog(2, "APF1_VOL", rmem, core, value);
|
RegLog(2, "COMB4_VOL", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case R_APF2_VOL:
|
case R_WALL_VOL:
|
||||||
RegLog(2, "APF2_VOL", rmem, core, value);
|
RegLog(2, "WALL_VOL", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case R_IN_COEF_L:
|
case R_APF1_VOL:
|
||||||
RegLog(2, "IN_COEF_L", rmem, core, value);
|
RegLog(2, "APF1_VOL", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case R_IN_COEF_R:
|
case R_APF2_VOL:
|
||||||
RegLog(2, "IN_COEF_R", rmem, core, value);
|
RegLog(2, "APF2_VOL", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
}
|
case R_IN_COEF_L:
|
||||||
} else if ((mem >= 0x07C0) && (mem < 0x07CE)) {
|
RegLog(2, "IN_COEF_L", rmem, core, value);
|
||||||
switch (mem) {
|
break;
|
||||||
case SPDIF_OUT:
|
case R_IN_COEF_R:
|
||||||
RegLog(2, "SPDIF_OUT", rmem, -1, value);
|
RegLog(2, "IN_COEF_R", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case SPDIF_IRQINFO:
|
}
|
||||||
RegLog(2, "SPDIF_IRQINFO", rmem, -1, value);
|
}
|
||||||
break;
|
else if ((mem >= 0x07C0) && (mem < 0x07CE))
|
||||||
case 0x7c4:
|
{
|
||||||
if (Spdif.Unknown1 != value)
|
switch (mem)
|
||||||
ConLog("* SPU2-X: SPDIF Unknown Register 1 set to %04x\n", value);
|
{
|
||||||
RegLog(2, "SPDIF_UNKNOWN1", rmem, -1, value);
|
case SPDIF_OUT:
|
||||||
break;
|
RegLog(2, "SPDIF_OUT", rmem, -1, value);
|
||||||
case SPDIF_MODE:
|
break;
|
||||||
if (Spdif.Mode != value)
|
case SPDIF_IRQINFO:
|
||||||
ConLog("* SPU2-X: SPDIF Mode set to %04x\n", value);
|
RegLog(2, "SPDIF_IRQINFO", rmem, -1, value);
|
||||||
RegLog(2, "SPDIF_MODE", rmem, -1, value);
|
break;
|
||||||
break;
|
case 0x7c4:
|
||||||
case SPDIF_MEDIA:
|
if (Spdif.Unknown1 != value)
|
||||||
if (Spdif.Media != value)
|
ConLog("* SPU2-X: SPDIF Unknown Register 1 set to %04x\n", value);
|
||||||
ConLog("* SPU2-X: SPDIF Media set to %04x\n", value);
|
RegLog(2, "SPDIF_UNKNOWN1", rmem, -1, value);
|
||||||
RegLog(2, "SPDIF_MEDIA", rmem, -1, value);
|
break;
|
||||||
break;
|
case SPDIF_MODE:
|
||||||
case 0x7ca:
|
if (Spdif.Mode != value)
|
||||||
if (Spdif.Unknown2 != value)
|
ConLog("* SPU2-X: SPDIF Mode set to %04x\n", value);
|
||||||
ConLog("* SPU2-X: SPDIF Unknown Register 2 set to %04x\n", value);
|
RegLog(2, "SPDIF_MODE", rmem, -1, value);
|
||||||
RegLog(2, "SPDIF_UNKNOWN2", rmem, -1, value);
|
break;
|
||||||
break;
|
case SPDIF_MEDIA:
|
||||||
case SPDIF_PROTECT:
|
if (Spdif.Media != value)
|
||||||
if (Spdif.Protection != value)
|
ConLog("* SPU2-X: SPDIF Media set to %04x\n", value);
|
||||||
ConLog("* SPU2-X: SPDIF Copy set to %04x\n", value);
|
RegLog(2, "SPDIF_MEDIA", rmem, -1, value);
|
||||||
RegLog(2, "SPDIF_PROTECT", rmem, -1, value);
|
break;
|
||||||
break;
|
case 0x7ca:
|
||||||
}
|
if (Spdif.Unknown2 != value)
|
||||||
UpdateSpdifMode();
|
ConLog("* SPU2-X: SPDIF Unknown Register 2 set to %04x\n", value);
|
||||||
} else {
|
RegLog(2, "SPDIF_UNKNOWN2", rmem, -1, value);
|
||||||
switch (omem) {
|
break;
|
||||||
case REG_C_ATTR:
|
case SPDIF_PROTECT:
|
||||||
RegLog(4, "ATTR", rmem, core, value);
|
if (Spdif.Protection != value)
|
||||||
break;
|
ConLog("* SPU2-X: SPDIF Copy set to %04x\n", value);
|
||||||
case REG_S_PMON:
|
RegLog(2, "SPDIF_PROTECT", rmem, -1, value);
|
||||||
RegLog(1, "PMON0", rmem, core, value);
|
break;
|
||||||
break;
|
}
|
||||||
case (REG_S_PMON + 2):
|
UpdateSpdifMode();
|
||||||
RegLog(1, "PMON1", rmem, core, value);
|
}
|
||||||
break;
|
else
|
||||||
case REG_S_NON:
|
{
|
||||||
RegLog(1, "NON0", rmem, core, value);
|
switch (omem)
|
||||||
break;
|
{
|
||||||
case (REG_S_NON + 2):
|
case REG_C_ATTR:
|
||||||
RegLog(1, "NON1", rmem, core, value);
|
RegLog(4, "ATTR", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case REG_S_VMIXL:
|
case REG_S_PMON:
|
||||||
RegLog(1, "VMIXL0", rmem, core, value);
|
RegLog(1, "PMON0", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case (REG_S_VMIXL + 2):
|
case (REG_S_PMON + 2):
|
||||||
RegLog(1, "VMIXL1", rmem, core, value);
|
RegLog(1, "PMON1", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case REG_S_VMIXEL:
|
case REG_S_NON:
|
||||||
RegLog(1, "VMIXEL0", rmem, core, value);
|
RegLog(1, "NON0", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case (REG_S_VMIXEL + 2):
|
case (REG_S_NON + 2):
|
||||||
RegLog(1, "VMIXEL1", rmem, core, value);
|
RegLog(1, "NON1", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case REG_S_VMIXR:
|
case REG_S_VMIXL:
|
||||||
RegLog(1, "VMIXR0", rmem, core, value);
|
RegLog(1, "VMIXL0", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case (REG_S_VMIXR + 2):
|
case (REG_S_VMIXL + 2):
|
||||||
RegLog(1, "VMIXR1", rmem, core, value);
|
RegLog(1, "VMIXL1", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case REG_S_VMIXER:
|
case REG_S_VMIXEL:
|
||||||
RegLog(1, "VMIXER0", rmem, core, value);
|
RegLog(1, "VMIXEL0", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case (REG_S_VMIXER + 2):
|
case (REG_S_VMIXEL + 2):
|
||||||
RegLog(1, "VMIXER1", rmem, core, value);
|
RegLog(1, "VMIXEL1", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case REG_P_MMIX:
|
case REG_S_VMIXR:
|
||||||
RegLog(1, "MMIX", rmem, core, value);
|
RegLog(1, "VMIXR0", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case REG_A_IRQA:
|
case (REG_S_VMIXR + 2):
|
||||||
RegLog(2, "IRQAH", rmem, core, value);
|
RegLog(1, "VMIXR1", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case (REG_A_IRQA + 2):
|
case REG_S_VMIXER:
|
||||||
RegLog(2, "IRQAL", rmem, core, value);
|
RegLog(1, "VMIXER0", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case (REG_S_KON + 2):
|
case (REG_S_VMIXER + 2):
|
||||||
RegLog(1, "KON1", rmem, core, value);
|
RegLog(1, "VMIXER1", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case REG_S_KON:
|
case REG_P_MMIX:
|
||||||
RegLog(1, "KON0", rmem, core, value);
|
RegLog(1, "MMIX", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case (REG_S_KOFF + 2):
|
case REG_A_IRQA:
|
||||||
RegLog(1, "KOFF1", rmem, core, value);
|
RegLog(2, "IRQAH", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case REG_S_KOFF:
|
case (REG_A_IRQA + 2):
|
||||||
RegLog(1, "KOFF0", rmem, core, value);
|
RegLog(2, "IRQAL", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case REG_A_TSA:
|
case (REG_S_KON + 2):
|
||||||
RegLog(2, "TSAH", rmem, core, value);
|
RegLog(1, "KON1", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case (REG_A_TSA + 2):
|
case REG_S_KON:
|
||||||
RegLog(2, "TSAL", rmem, core, value);
|
RegLog(1, "KON0", rmem, core, value);
|
||||||
break;
|
break;
|
||||||
case REG_S_ENDX:
|
case (REG_S_KOFF + 2):
|
||||||
//ConLog("* SPU2-X: Core %d ENDX cleared!\n",core);
|
RegLog(1, "KOFF1", rmem, core, value);
|
||||||
RegLog(2, "ENDX0", rmem, core, value);
|
break;
|
||||||
break;
|
case REG_S_KOFF:
|
||||||
case (REG_S_ENDX + 2):
|
RegLog(1, "KOFF0", rmem, core, value);
|
||||||
//ConLog("* SPU2-X: Core %d ENDX cleared!\n",core);
|
break;
|
||||||
RegLog(2, "ENDX1", rmem, core, value);
|
case REG_A_TSA:
|
||||||
break;
|
RegLog(2, "TSAH", rmem, core, value);
|
||||||
case REG_P_MVOLL:
|
break;
|
||||||
RegLog(1, "MVOLL", rmem, core, value);
|
case (REG_A_TSA + 2):
|
||||||
break;
|
RegLog(2, "TSAL", rmem, core, value);
|
||||||
case REG_P_MVOLR:
|
break;
|
||||||
RegLog(1, "MVOLR", rmem, core, value);
|
case REG_S_ENDX:
|
||||||
break;
|
//ConLog("* SPU2-X: Core %d ENDX cleared!\n",core);
|
||||||
case REG_S_ADMAS:
|
RegLog(2, "ENDX0", rmem, core, value);
|
||||||
RegLog(3, "ADMAS", rmem, core, value);
|
break;
|
||||||
//ConLog("* SPU2-X: Core %d AutoDMAControl set to %d\n",core,value);
|
case (REG_S_ENDX + 2):
|
||||||
break;
|
//ConLog("* SPU2-X: Core %d ENDX cleared!\n",core);
|
||||||
case REG_P_STATX:
|
RegLog(2, "ENDX1", rmem, core, value);
|
||||||
RegLog(3, "STATX", rmem, core, value);
|
break;
|
||||||
break;
|
case REG_P_MVOLL:
|
||||||
case REG_A_ESA:
|
RegLog(1, "MVOLL", rmem, core, value);
|
||||||
RegLog(2, "ESAH", rmem, core, value);
|
break;
|
||||||
break;
|
case REG_P_MVOLR:
|
||||||
case (REG_A_ESA + 2):
|
RegLog(1, "MVOLR", rmem, core, value);
|
||||||
RegLog(2, "ESAL", rmem, core, value);
|
break;
|
||||||
break;
|
case REG_S_ADMAS:
|
||||||
case REG_A_EEA:
|
RegLog(3, "ADMAS", rmem, core, value);
|
||||||
RegLog(2, "EEAH", rmem, core, value);
|
//ConLog("* SPU2-X: Core %d AutoDMAControl set to %d\n",core,value);
|
||||||
break;
|
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) \
|
#define LOG_REVB_REG(n, t) \
|
||||||
case R_##n: \
|
case R_##n: \
|
||||||
RegLog(2, t "H", mem, core, value); \
|
RegLog(2, t "H", mem, core, value); \
|
||||||
break; \
|
break; \
|
||||||
case (R_##n + 2): \
|
case (R_##n + 2): \
|
||||||
RegLog(2, t "L", mem, core, value); \
|
RegLog(2, t "L", mem, core, value); \
|
||||||
break;
|
break;
|
||||||
|
|
||||||
LOG_REVB_REG(APF1_SIZE, "APF1_SIZE")
|
LOG_REVB_REG(APF1_SIZE, "APF1_SIZE")
|
||||||
LOG_REVB_REG(APF2_SIZE, "APF2_SIZE")
|
LOG_REVB_REG(APF2_SIZE, "APF2_SIZE")
|
||||||
LOG_REVB_REG(SAME_L_SRC, "SAME_L_SRC")
|
LOG_REVB_REG(SAME_L_SRC, "SAME_L_SRC")
|
||||||
LOG_REVB_REG(SAME_R_SRC, "SAME_R_SRC")
|
LOG_REVB_REG(SAME_R_SRC, "SAME_R_SRC")
|
||||||
LOG_REVB_REG(DIFF_L_SRC, "DIFF_L_SRC")
|
LOG_REVB_REG(DIFF_L_SRC, "DIFF_L_SRC")
|
||||||
LOG_REVB_REG(DIFF_R_SRC, "DIFF_R_SRC")
|
LOG_REVB_REG(DIFF_R_SRC, "DIFF_R_SRC")
|
||||||
LOG_REVB_REG(SAME_L_DST, "SAME_L_DST")
|
LOG_REVB_REG(SAME_L_DST, "SAME_L_DST")
|
||||||
LOG_REVB_REG(SAME_R_DST, "SAME_R_DST")
|
LOG_REVB_REG(SAME_R_DST, "SAME_R_DST")
|
||||||
LOG_REVB_REG(DIFF_L_DST, "DIFF_L_DST")
|
LOG_REVB_REG(DIFF_L_DST, "DIFF_L_DST")
|
||||||
LOG_REVB_REG(DIFF_R_DST, "DIFF_R_DST")
|
LOG_REVB_REG(DIFF_R_DST, "DIFF_R_DST")
|
||||||
LOG_REVB_REG(COMB1_L_SRC, "COMB1_L_SRC")
|
LOG_REVB_REG(COMB1_L_SRC, "COMB1_L_SRC")
|
||||||
LOG_REVB_REG(COMB1_R_SRC, "COMB1_R_SRC")
|
LOG_REVB_REG(COMB1_R_SRC, "COMB1_R_SRC")
|
||||||
LOG_REVB_REG(COMB2_L_SRC, "COMB2_L_SRC")
|
LOG_REVB_REG(COMB2_L_SRC, "COMB2_L_SRC")
|
||||||
LOG_REVB_REG(COMB2_R_SRC, "COMB2_R_SRC")
|
LOG_REVB_REG(COMB2_R_SRC, "COMB2_R_SRC")
|
||||||
LOG_REVB_REG(COMB3_L_SRC, "COMB3_L_SRC")
|
LOG_REVB_REG(COMB3_L_SRC, "COMB3_L_SRC")
|
||||||
LOG_REVB_REG(COMB3_R_SRC, "COMB3_R_SRC")
|
LOG_REVB_REG(COMB3_R_SRC, "COMB3_R_SRC")
|
||||||
LOG_REVB_REG(COMB4_L_SRC, "COMB4_L_SRC")
|
LOG_REVB_REG(COMB4_L_SRC, "COMB4_L_SRC")
|
||||||
LOG_REVB_REG(COMB4_R_SRC, "COMB4_R_SRC")
|
LOG_REVB_REG(COMB4_R_SRC, "COMB4_R_SRC")
|
||||||
LOG_REVB_REG(APF1_L_DST, "APF1_L_DST")
|
LOG_REVB_REG(APF1_L_DST, "APF1_L_DST")
|
||||||
LOG_REVB_REG(APF1_R_DST, "APF1_R_DST")
|
LOG_REVB_REG(APF1_R_DST, "APF1_R_DST")
|
||||||
LOG_REVB_REG(APF2_L_DST, "APF2_L_DST")
|
LOG_REVB_REG(APF2_L_DST, "APF2_L_DST")
|
||||||
LOG_REVB_REG(APF2_R_DST, "APF2_R_DST")
|
LOG_REVB_REG(APF2_R_DST, "APF2_R_DST")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
RegLog(2, "UNKNOWN", rmem, core, value);
|
RegLog(2, "UNKNOWN", rmem, core, value);
|
||||||
spu2Ru16(mem) = value;
|
spu2Ru16(mem) = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,286 +16,286 @@
|
||||||
#include "Global.h"
|
#include "Global.h"
|
||||||
|
|
||||||
#define PCORE(c, p) \
|
#define PCORE(c, p) \
|
||||||
U16P(Cores[c].p)
|
U16P(Cores[c].p)
|
||||||
|
|
||||||
#define PVCP(c, v, p) \
|
#define PVCP(c, v, p) \
|
||||||
PCORE(c, Voices[v].p)
|
PCORE(c, Voices[v].p)
|
||||||
|
|
||||||
#define PVC(c, v) \
|
#define PVC(c, v) \
|
||||||
PVCP(c, v, Volume.Left.Reg_VOL) \
|
PVCP(c, v, Volume.Left.Reg_VOL) \
|
||||||
, \
|
, \
|
||||||
PVCP(c, v, Volume.Right.Reg_VOL), \
|
PVCP(c, v, Volume.Right.Reg_VOL), \
|
||||||
PVCP(c, v, Pitch), \
|
PVCP(c, v, Pitch), \
|
||||||
PVCP(c, v, ADSR.regADSR1), \
|
PVCP(c, v, ADSR.regADSR1), \
|
||||||
PVCP(c, v, ADSR.regADSR2), \
|
PVCP(c, v, ADSR.regADSR2), \
|
||||||
PVCP(c, v, ADSR.Value) + 1, \
|
PVCP(c, v, ADSR.Value) + 1, \
|
||||||
PVCP(c, v, Volume.Left.Value) + 1, \
|
PVCP(c, v, Volume.Left.Value) + 1, \
|
||||||
PVCP(c, v, Volume.Right.Value) + 1
|
PVCP(c, v, Volume.Right.Value) + 1
|
||||||
|
|
||||||
#define PVCA(c, v) \
|
#define PVCA(c, v) \
|
||||||
PVCP(c, v, StartA) + 1, \
|
PVCP(c, v, StartA) + 1, \
|
||||||
PVCP(c, v, StartA), \
|
PVCP(c, v, StartA), \
|
||||||
PVCP(c, v, LoopStartA) + 1, \
|
PVCP(c, v, LoopStartA) + 1, \
|
||||||
PVCP(c, v, LoopStartA), \
|
PVCP(c, v, LoopStartA), \
|
||||||
PVCP(c, v, NextA) + 1, \
|
PVCP(c, v, NextA) + 1, \
|
||||||
PVCP(c, v, NextA)
|
PVCP(c, v, NextA)
|
||||||
|
|
||||||
#define PRAW(a) \
|
#define PRAW(a) \
|
||||||
((u16 *)NULL)
|
((u16*)NULL)
|
||||||
|
|
||||||
#define PREVB_REG(c, n) \
|
#define PREVB_REG(c, n) \
|
||||||
PCORE(c, Revb.n) + 1, \
|
PCORE(c, Revb.n) + 1, \
|
||||||
PCORE(c, Revb.n)
|
PCORE(c, Revb.n)
|
||||||
|
|
||||||
u16 *regtable[0x401];
|
u16* regtable[0x401];
|
||||||
|
|
||||||
u16 const *const regtable_original[0x401] =
|
u16 const* const regtable_original[0x401] =
|
||||||
{
|
{
|
||||||
// Voice Params: 8 params, 24 voices = 0x180 bytes
|
// 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, 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, 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, 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),
|
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),
|
||||||
PCORE(0, Regs.PMON) + 1,
|
PCORE(0, Regs.PMON) + 1,
|
||||||
PCORE(0, Regs.NON),
|
PCORE(0, Regs.NON),
|
||||||
PCORE(0, Regs.NON) + 1,
|
PCORE(0, Regs.NON) + 1,
|
||||||
PCORE(0, Regs.VMIXL),
|
PCORE(0, Regs.VMIXL),
|
||||||
PCORE(0, Regs.VMIXL) + 1,
|
PCORE(0, Regs.VMIXL) + 1,
|
||||||
PCORE(0, Regs.VMIXEL),
|
PCORE(0, Regs.VMIXEL),
|
||||||
PCORE(0, Regs.VMIXEL) + 1,
|
PCORE(0, Regs.VMIXEL) + 1,
|
||||||
PCORE(0, Regs.VMIXR),
|
PCORE(0, Regs.VMIXR),
|
||||||
PCORE(0, Regs.VMIXR) + 1,
|
PCORE(0, Regs.VMIXR) + 1,
|
||||||
PCORE(0, Regs.VMIXER),
|
PCORE(0, Regs.VMIXER),
|
||||||
PCORE(0, Regs.VMIXER) + 1,
|
PCORE(0, Regs.VMIXER) + 1,
|
||||||
|
|
||||||
PCORE(0, Regs.MMIX),
|
PCORE(0, Regs.MMIX),
|
||||||
PCORE(0, Regs.ATTR),
|
PCORE(0, Regs.ATTR),
|
||||||
|
|
||||||
PCORE(0, IRQA) + 1,
|
PCORE(0, IRQA) + 1,
|
||||||
PCORE(0, IRQA),
|
PCORE(0, IRQA),
|
||||||
|
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
|
||||||
PCORE(0, TSA) + 1,
|
PCORE(0, TSA) + 1,
|
||||||
PCORE(0, TSA),
|
PCORE(0, TSA),
|
||||||
|
|
||||||
PRAW(REG__1AC), PRAW(REG__1AE),
|
PRAW(REG__1AC), PRAW(REG__1AE),
|
||||||
|
|
||||||
PCORE(0, AutoDMACtrl),
|
PCORE(0, AutoDMACtrl),
|
||||||
|
|
||||||
PRAW(0x1b2), PRAW(0x1b4), PRAW(0x1b6), PRAW(0x1b8), PRAW(0x1ba), PRAW(0x1bc), PRAW(0x1be), // unknown
|
PRAW(0x1b2), PRAW(0x1b4), PRAW(0x1b6), PRAW(0x1b8), PRAW(0x1ba), PRAW(0x1bc), PRAW(0x1be), // unknown
|
||||||
|
|
||||||
// Voice Addresses
|
// Voice Addresses
|
||||||
PVCA(0, 0), PVCA(0, 1), PVCA(0, 2), PVCA(0, 3), PVCA(0, 4), PVCA(0, 5),
|
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, 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, 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),
|
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) + 1,
|
||||||
PCORE(0, ExtEffectsStartA),
|
PCORE(0, ExtEffectsStartA),
|
||||||
|
|
||||||
PREVB_REG(0, APF1_SIZE),
|
PREVB_REG(0, APF1_SIZE),
|
||||||
PREVB_REG(0, APF2_SIZE),
|
PREVB_REG(0, APF2_SIZE),
|
||||||
PREVB_REG(0, SAME_L_DST),
|
PREVB_REG(0, SAME_L_DST),
|
||||||
PREVB_REG(0, SAME_R_DST),
|
PREVB_REG(0, SAME_R_DST),
|
||||||
PREVB_REG(0, COMB1_L_SRC),
|
PREVB_REG(0, COMB1_L_SRC),
|
||||||
PREVB_REG(0, COMB1_R_SRC),
|
PREVB_REG(0, COMB1_R_SRC),
|
||||||
PREVB_REG(0, COMB2_L_SRC),
|
PREVB_REG(0, COMB2_L_SRC),
|
||||||
PREVB_REG(0, COMB2_R_SRC),
|
PREVB_REG(0, COMB2_R_SRC),
|
||||||
PREVB_REG(0, SAME_L_SRC),
|
PREVB_REG(0, SAME_L_SRC),
|
||||||
PREVB_REG(0, SAME_R_SRC),
|
PREVB_REG(0, SAME_R_SRC),
|
||||||
PREVB_REG(0, DIFF_L_DST),
|
PREVB_REG(0, DIFF_L_DST),
|
||||||
PREVB_REG(0, DIFF_R_DST),
|
PREVB_REG(0, DIFF_R_DST),
|
||||||
PREVB_REG(0, COMB3_L_SRC),
|
PREVB_REG(0, COMB3_L_SRC),
|
||||||
PREVB_REG(0, COMB3_R_SRC),
|
PREVB_REG(0, COMB3_R_SRC),
|
||||||
PREVB_REG(0, COMB4_L_SRC),
|
PREVB_REG(0, COMB4_L_SRC),
|
||||||
PREVB_REG(0, COMB4_R_SRC),
|
PREVB_REG(0, COMB4_R_SRC),
|
||||||
PREVB_REG(0, DIFF_L_SRC),
|
PREVB_REG(0, DIFF_L_SRC),
|
||||||
PREVB_REG(0, DIFF_R_SRC),
|
PREVB_REG(0, DIFF_R_SRC),
|
||||||
PREVB_REG(0, APF1_L_DST),
|
PREVB_REG(0, APF1_L_DST),
|
||||||
PREVB_REG(0, APF1_R_DST),
|
PREVB_REG(0, APF1_R_DST),
|
||||||
PREVB_REG(0, APF2_L_DST),
|
PREVB_REG(0, APF2_L_DST),
|
||||||
PREVB_REG(0, APF2_R_DST),
|
PREVB_REG(0, APF2_R_DST),
|
||||||
|
|
||||||
PCORE(0, ExtEffectsEndA) + 1,
|
PCORE(0, ExtEffectsEndA) + 1,
|
||||||
PCORE(0, ExtEffectsEndA),
|
PCORE(0, ExtEffectsEndA),
|
||||||
|
|
||||||
PCORE(0, Regs.ENDX),
|
PCORE(0, Regs.ENDX),
|
||||||
PCORE(0, Regs.ENDX) + 1,
|
PCORE(0, Regs.ENDX) + 1,
|
||||||
PCORE(0, Regs.STATX),
|
PCORE(0, Regs.STATX),
|
||||||
|
|
||||||
//0x346 here
|
//0x346 here
|
||||||
PRAW(0x346),
|
PRAW(0x346),
|
||||||
PRAW(0x348), PRAW(0x34A), PRAW(0x34C), PRAW(0x34E),
|
PRAW(0x348), PRAW(0x34A), PRAW(0x34C), PRAW(0x34E),
|
||||||
PRAW(0x350), PRAW(0x352), PRAW(0x354), PRAW(0x356),
|
PRAW(0x350), PRAW(0x352), PRAW(0x354), PRAW(0x356),
|
||||||
PRAW(0x358), PRAW(0x35A), PRAW(0x35C), PRAW(0x35E),
|
PRAW(0x358), PRAW(0x35A), PRAW(0x35C), PRAW(0x35E),
|
||||||
PRAW(0x360), PRAW(0x362), PRAW(0x364), PRAW(0x366),
|
PRAW(0x360), PRAW(0x362), PRAW(0x364), PRAW(0x366),
|
||||||
PRAW(0x368), PRAW(0x36A), PRAW(0x36C), PRAW(0x36E),
|
PRAW(0x368), PRAW(0x36A), PRAW(0x36C), PRAW(0x36E),
|
||||||
PRAW(0x370), PRAW(0x372), PRAW(0x374), PRAW(0x376),
|
PRAW(0x370), PRAW(0x372), PRAW(0x374), PRAW(0x376),
|
||||||
PRAW(0x378), PRAW(0x37A), PRAW(0x37C), PRAW(0x37E),
|
PRAW(0x378), PRAW(0x37A), PRAW(0x37C), PRAW(0x37E),
|
||||||
PRAW(0x380), PRAW(0x382), PRAW(0x384), PRAW(0x386),
|
PRAW(0x380), PRAW(0x382), PRAW(0x384), PRAW(0x386),
|
||||||
PRAW(0x388), PRAW(0x38A), PRAW(0x38C), PRAW(0x38E),
|
PRAW(0x388), PRAW(0x38A), PRAW(0x38C), PRAW(0x38E),
|
||||||
PRAW(0x390), PRAW(0x392), PRAW(0x394), PRAW(0x396),
|
PRAW(0x390), PRAW(0x392), PRAW(0x394), PRAW(0x396),
|
||||||
PRAW(0x398), PRAW(0x39A), PRAW(0x39C), PRAW(0x39E),
|
PRAW(0x398), PRAW(0x39A), PRAW(0x39C), PRAW(0x39E),
|
||||||
PRAW(0x3A0), PRAW(0x3A2), PRAW(0x3A4), PRAW(0x3A6),
|
PRAW(0x3A0), PRAW(0x3A2), PRAW(0x3A4), PRAW(0x3A6),
|
||||||
PRAW(0x3A8), PRAW(0x3AA), PRAW(0x3AC), PRAW(0x3AE),
|
PRAW(0x3A8), PRAW(0x3AA), PRAW(0x3AC), PRAW(0x3AE),
|
||||||
PRAW(0x3B0), PRAW(0x3B2), PRAW(0x3B4), PRAW(0x3B6),
|
PRAW(0x3B0), PRAW(0x3B2), PRAW(0x3B4), PRAW(0x3B6),
|
||||||
PRAW(0x3B8), PRAW(0x3BA), PRAW(0x3BC), PRAW(0x3BE),
|
PRAW(0x3B8), PRAW(0x3BA), PRAW(0x3BC), PRAW(0x3BE),
|
||||||
PRAW(0x3C0), PRAW(0x3C2), PRAW(0x3C4), PRAW(0x3C6),
|
PRAW(0x3C0), PRAW(0x3C2), PRAW(0x3C4), PRAW(0x3C6),
|
||||||
PRAW(0x3C8), PRAW(0x3CA), PRAW(0x3CC), PRAW(0x3CE),
|
PRAW(0x3C8), PRAW(0x3CA), PRAW(0x3CC), PRAW(0x3CE),
|
||||||
PRAW(0x3D0), PRAW(0x3D2), PRAW(0x3D4), PRAW(0x3D6),
|
PRAW(0x3D0), PRAW(0x3D2), PRAW(0x3D4), PRAW(0x3D6),
|
||||||
PRAW(0x3D8), PRAW(0x3DA), PRAW(0x3DC), PRAW(0x3DE),
|
PRAW(0x3D8), PRAW(0x3DA), PRAW(0x3DC), PRAW(0x3DE),
|
||||||
PRAW(0x3E0), PRAW(0x3E2), PRAW(0x3E4), PRAW(0x3E6),
|
PRAW(0x3E0), PRAW(0x3E2), PRAW(0x3E4), PRAW(0x3E6),
|
||||||
PRAW(0x3E8), PRAW(0x3EA), PRAW(0x3EC), PRAW(0x3EE),
|
PRAW(0x3E8), PRAW(0x3EA), PRAW(0x3EC), PRAW(0x3EE),
|
||||||
PRAW(0x3F0), PRAW(0x3F2), PRAW(0x3F4), PRAW(0x3F6),
|
PRAW(0x3F0), PRAW(0x3F2), PRAW(0x3F4), PRAW(0x3F6),
|
||||||
PRAW(0x3F8), PRAW(0x3FA), PRAW(0x3FC), PRAW(0x3FE),
|
PRAW(0x3F8), PRAW(0x3FA), PRAW(0x3FC), PRAW(0x3FE),
|
||||||
|
|
||||||
//AND... we reached 0x400!
|
//AND... we reached 0x400!
|
||||||
// Voice Params: 8 params, 24 voices = 0x180 bytes
|
// 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, 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, 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, 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),
|
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),
|
||||||
PCORE(1, Regs.PMON) + 1,
|
PCORE(1, Regs.PMON) + 1,
|
||||||
PCORE(1, Regs.NON),
|
PCORE(1, Regs.NON),
|
||||||
PCORE(1, Regs.NON) + 1,
|
PCORE(1, Regs.NON) + 1,
|
||||||
PCORE(1, Regs.VMIXL),
|
PCORE(1, Regs.VMIXL),
|
||||||
PCORE(1, Regs.VMIXL) + 1,
|
PCORE(1, Regs.VMIXL) + 1,
|
||||||
PCORE(1, Regs.VMIXEL),
|
PCORE(1, Regs.VMIXEL),
|
||||||
PCORE(1, Regs.VMIXEL) + 1,
|
PCORE(1, Regs.VMIXEL) + 1,
|
||||||
PCORE(1, Regs.VMIXR),
|
PCORE(1, Regs.VMIXR),
|
||||||
PCORE(1, Regs.VMIXR) + 1,
|
PCORE(1, Regs.VMIXR) + 1,
|
||||||
PCORE(1, Regs.VMIXER),
|
PCORE(1, Regs.VMIXER),
|
||||||
PCORE(1, Regs.VMIXER) + 1,
|
PCORE(1, Regs.VMIXER) + 1,
|
||||||
PCORE(1, Regs.MMIX),
|
PCORE(1, Regs.MMIX),
|
||||||
|
|
||||||
PCORE(1, Regs.ATTR),
|
PCORE(1, Regs.ATTR),
|
||||||
|
|
||||||
PCORE(1, IRQA) + 1,
|
PCORE(1, IRQA) + 1,
|
||||||
PCORE(1, IRQA),
|
PCORE(1, IRQA),
|
||||||
|
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
|
|
||||||
PCORE(1, TSA) + 1,
|
PCORE(1, TSA) + 1,
|
||||||
PCORE(1, TSA),
|
PCORE(1, TSA),
|
||||||
|
|
||||||
PRAW(0x5ac), PRAW(0x5ae),
|
PRAW(0x5ac), PRAW(0x5ae),
|
||||||
|
|
||||||
PCORE(1, AutoDMACtrl),
|
PCORE(1, AutoDMACtrl),
|
||||||
|
|
||||||
PRAW(0x5b2), PRAW(0x5b4), PRAW(0x5b6), PRAW(0x5b8), PRAW(0x5ba), PRAW(0x5bc), PRAW(0x5be), // unknown
|
PRAW(0x5b2), PRAW(0x5b4), PRAW(0x5b6), PRAW(0x5b8), PRAW(0x5ba), PRAW(0x5bc), PRAW(0x5be), // unknown
|
||||||
|
|
||||||
// Voice Addresses
|
// Voice Addresses
|
||||||
PVCA(1, 0), PVCA(1, 1), PVCA(1, 2), PVCA(1, 3), PVCA(1, 4), PVCA(1, 5),
|
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, 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, 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),
|
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) + 1,
|
||||||
PCORE(1, ExtEffectsStartA),
|
PCORE(1, ExtEffectsStartA),
|
||||||
|
|
||||||
PREVB_REG(1, APF1_SIZE),
|
PREVB_REG(1, APF1_SIZE),
|
||||||
PREVB_REG(1, APF2_SIZE),
|
PREVB_REG(1, APF2_SIZE),
|
||||||
PREVB_REG(1, SAME_L_DST),
|
PREVB_REG(1, SAME_L_DST),
|
||||||
PREVB_REG(1, SAME_R_DST),
|
PREVB_REG(1, SAME_R_DST),
|
||||||
PREVB_REG(1, COMB1_L_SRC),
|
PREVB_REG(1, COMB1_L_SRC),
|
||||||
PREVB_REG(1, COMB1_R_SRC),
|
PREVB_REG(1, COMB1_R_SRC),
|
||||||
PREVB_REG(1, COMB2_L_SRC),
|
PREVB_REG(1, COMB2_L_SRC),
|
||||||
PREVB_REG(1, COMB2_R_SRC),
|
PREVB_REG(1, COMB2_R_SRC),
|
||||||
PREVB_REG(1, SAME_L_SRC),
|
PREVB_REG(1, SAME_L_SRC),
|
||||||
PREVB_REG(1, SAME_R_SRC),
|
PREVB_REG(1, SAME_R_SRC),
|
||||||
PREVB_REG(1, DIFF_L_DST),
|
PREVB_REG(1, DIFF_L_DST),
|
||||||
PREVB_REG(1, DIFF_R_DST),
|
PREVB_REG(1, DIFF_R_DST),
|
||||||
PREVB_REG(1, COMB3_L_SRC),
|
PREVB_REG(1, COMB3_L_SRC),
|
||||||
PREVB_REG(1, COMB3_R_SRC),
|
PREVB_REG(1, COMB3_R_SRC),
|
||||||
PREVB_REG(1, COMB4_L_SRC),
|
PREVB_REG(1, COMB4_L_SRC),
|
||||||
PREVB_REG(1, COMB4_R_SRC),
|
PREVB_REG(1, COMB4_R_SRC),
|
||||||
PREVB_REG(1, DIFF_L_SRC),
|
PREVB_REG(1, DIFF_L_SRC),
|
||||||
PREVB_REG(1, DIFF_R_SRC),
|
PREVB_REG(1, DIFF_R_SRC),
|
||||||
PREVB_REG(1, APF1_L_DST),
|
PREVB_REG(1, APF1_L_DST),
|
||||||
PREVB_REG(1, APF1_R_DST),
|
PREVB_REG(1, APF1_R_DST),
|
||||||
PREVB_REG(1, APF2_L_DST),
|
PREVB_REG(1, APF2_L_DST),
|
||||||
PREVB_REG(1, APF2_R_DST),
|
PREVB_REG(1, APF2_R_DST),
|
||||||
|
|
||||||
PCORE(1, ExtEffectsEndA) + 1,
|
PCORE(1, ExtEffectsEndA) + 1,
|
||||||
PCORE(1, ExtEffectsEndA),
|
PCORE(1, ExtEffectsEndA),
|
||||||
|
|
||||||
PCORE(1, Regs.ENDX),
|
PCORE(1, Regs.ENDX),
|
||||||
PCORE(1, Regs.ENDX) + 1,
|
PCORE(1, Regs.ENDX) + 1,
|
||||||
PCORE(1, Regs.STATX),
|
PCORE(1, Regs.STATX),
|
||||||
|
|
||||||
PRAW(0x746),
|
PRAW(0x746),
|
||||||
PRAW(0x748), PRAW(0x74A), PRAW(0x74C), PRAW(0x74E),
|
PRAW(0x748), PRAW(0x74A), PRAW(0x74C), PRAW(0x74E),
|
||||||
PRAW(0x750), PRAW(0x752), PRAW(0x754), PRAW(0x756),
|
PRAW(0x750), PRAW(0x752), PRAW(0x754), PRAW(0x756),
|
||||||
PRAW(0x758), PRAW(0x75A), PRAW(0x75C), PRAW(0x75E),
|
PRAW(0x758), PRAW(0x75A), PRAW(0x75C), PRAW(0x75E),
|
||||||
|
|
||||||
//0x760: weird area
|
//0x760: weird area
|
||||||
PCORE(0, MasterVol.Left.Reg_VOL),
|
PCORE(0, MasterVol.Left.Reg_VOL),
|
||||||
PCORE(0, MasterVol.Right.Reg_VOL),
|
PCORE(0, MasterVol.Right.Reg_VOL),
|
||||||
PCORE(0, FxVol.Left) + 1,
|
PCORE(0, FxVol.Left) + 1,
|
||||||
PCORE(0, FxVol.Right) + 1,
|
PCORE(0, FxVol.Right) + 1,
|
||||||
PCORE(0, ExtVol.Left) + 1,
|
PCORE(0, ExtVol.Left) + 1,
|
||||||
PCORE(0, ExtVol.Right) + 1,
|
PCORE(0, ExtVol.Right) + 1,
|
||||||
PCORE(0, InpVol.Left) + 1,
|
PCORE(0, InpVol.Left) + 1,
|
||||||
PCORE(0, InpVol.Right) + 1,
|
PCORE(0, InpVol.Right) + 1,
|
||||||
PCORE(0, MasterVol.Left.Value) + 1,
|
PCORE(0, MasterVol.Left.Value) + 1,
|
||||||
PCORE(0, MasterVol.Right.Value) + 1,
|
PCORE(0, MasterVol.Right.Value) + 1,
|
||||||
PCORE(0, Revb.IIR_VOL),
|
PCORE(0, Revb.IIR_VOL),
|
||||||
PCORE(0, Revb.COMB1_VOL),
|
PCORE(0, Revb.COMB1_VOL),
|
||||||
PCORE(0, Revb.COMB2_VOL),
|
PCORE(0, Revb.COMB2_VOL),
|
||||||
PCORE(0, Revb.COMB3_VOL),
|
PCORE(0, Revb.COMB3_VOL),
|
||||||
PCORE(0, Revb.COMB4_VOL),
|
PCORE(0, Revb.COMB4_VOL),
|
||||||
PCORE(0, Revb.WALL_VOL),
|
PCORE(0, Revb.WALL_VOL),
|
||||||
PCORE(0, Revb.APF1_VOL),
|
PCORE(0, Revb.APF1_VOL),
|
||||||
PCORE(0, Revb.APF2_VOL),
|
PCORE(0, Revb.APF2_VOL),
|
||||||
PCORE(0, Revb.IN_COEF_L),
|
PCORE(0, Revb.IN_COEF_L),
|
||||||
PCORE(0, Revb.IN_COEF_R),
|
PCORE(0, Revb.IN_COEF_R),
|
||||||
|
|
||||||
PCORE(1, MasterVol.Left.Reg_VOL),
|
PCORE(1, MasterVol.Left.Reg_VOL),
|
||||||
PCORE(1, MasterVol.Right.Reg_VOL),
|
PCORE(1, MasterVol.Right.Reg_VOL),
|
||||||
PCORE(1, FxVol.Left) + 1,
|
PCORE(1, FxVol.Left) + 1,
|
||||||
PCORE(1, FxVol.Right) + 1,
|
PCORE(1, FxVol.Right) + 1,
|
||||||
PCORE(1, ExtVol.Left) + 1,
|
PCORE(1, ExtVol.Left) + 1,
|
||||||
PCORE(1, ExtVol.Right) + 1,
|
PCORE(1, ExtVol.Right) + 1,
|
||||||
PCORE(1, InpVol.Left) + 1,
|
PCORE(1, InpVol.Left) + 1,
|
||||||
PCORE(1, InpVol.Right) + 1,
|
PCORE(1, InpVol.Right) + 1,
|
||||||
PCORE(1, MasterVol.Left.Value) + 1,
|
PCORE(1, MasterVol.Left.Value) + 1,
|
||||||
PCORE(1, MasterVol.Right.Value) + 1,
|
PCORE(1, MasterVol.Right.Value) + 1,
|
||||||
PCORE(1, Revb.IIR_VOL),
|
PCORE(1, Revb.IIR_VOL),
|
||||||
PCORE(1, Revb.COMB1_VOL),
|
PCORE(1, Revb.COMB1_VOL),
|
||||||
PCORE(1, Revb.COMB2_VOL),
|
PCORE(1, Revb.COMB2_VOL),
|
||||||
PCORE(1, Revb.COMB3_VOL),
|
PCORE(1, Revb.COMB3_VOL),
|
||||||
PCORE(1, Revb.COMB4_VOL),
|
PCORE(1, Revb.COMB4_VOL),
|
||||||
PCORE(1, Revb.WALL_VOL),
|
PCORE(1, Revb.WALL_VOL),
|
||||||
PCORE(1, Revb.APF1_VOL),
|
PCORE(1, Revb.APF1_VOL),
|
||||||
PCORE(1, Revb.APF2_VOL),
|
PCORE(1, Revb.APF2_VOL),
|
||||||
PCORE(1, Revb.IN_COEF_L),
|
PCORE(1, Revb.IN_COEF_L),
|
||||||
PCORE(1, Revb.IN_COEF_R),
|
PCORE(1, Revb.IN_COEF_R),
|
||||||
|
|
||||||
PRAW(0x7B0), PRAW(0x7B2), PRAW(0x7B4), PRAW(0x7B6),
|
PRAW(0x7B0), PRAW(0x7B2), PRAW(0x7B4), PRAW(0x7B6),
|
||||||
PRAW(0x7B8), PRAW(0x7BA), PRAW(0x7BC), PRAW(0x7BE),
|
PRAW(0x7B8), PRAW(0x7BA), PRAW(0x7BC), PRAW(0x7BE),
|
||||||
|
|
||||||
// SPDIF interface
|
// SPDIF interface
|
||||||
U16P(Spdif.Out),
|
U16P(Spdif.Out),
|
||||||
U16P(Spdif.Info),
|
U16P(Spdif.Info),
|
||||||
U16P(Spdif.Unknown1),
|
U16P(Spdif.Unknown1),
|
||||||
U16P(Spdif.Mode),
|
U16P(Spdif.Mode),
|
||||||
U16P(Spdif.Media),
|
U16P(Spdif.Media),
|
||||||
U16P(Spdif.Unknown2),
|
U16P(Spdif.Unknown2),
|
||||||
U16P(Spdif.Protection),
|
U16P(Spdif.Protection),
|
||||||
|
|
||||||
PRAW(0x7CE),
|
PRAW(0x7CE),
|
||||||
PRAW(0x7D0), PRAW(0x7D2), PRAW(0x7D4), PRAW(0x7D6),
|
PRAW(0x7D0), PRAW(0x7D2), PRAW(0x7D4), PRAW(0x7D6),
|
||||||
PRAW(0x7D8), PRAW(0x7DA), PRAW(0x7DC), PRAW(0x7DE),
|
PRAW(0x7D8), PRAW(0x7DA), PRAW(0x7DC), PRAW(0x7DE),
|
||||||
PRAW(0x7E0), PRAW(0x7E2), PRAW(0x7E4), PRAW(0x7E6),
|
PRAW(0x7E0), PRAW(0x7E2), PRAW(0x7E4), PRAW(0x7E6),
|
||||||
PRAW(0x7E8), PRAW(0x7EA), PRAW(0x7EC), PRAW(0x7EE),
|
PRAW(0x7E8), PRAW(0x7EA), PRAW(0x7EC), PRAW(0x7EE),
|
||||||
PRAW(0x7F0), PRAW(0x7F2), PRAW(0x7F4), PRAW(0x7F6),
|
PRAW(0x7F0), PRAW(0x7F2), PRAW(0x7F4), PRAW(0x7F6),
|
||||||
PRAW(0x7F8), PRAW(0x7FA), PRAW(0x7FC), PRAW(0x7FE),
|
PRAW(0x7F8), PRAW(0x7FA), PRAW(0x7FC), PRAW(0x7FE),
|
||||||
|
|
||||||
NULL};
|
NULL};
|
||||||
|
|
|
@ -17,115 +17,122 @@
|
||||||
|
|
||||||
__forceinline s32 V_Core::RevbGetIndexer(s32 offset)
|
__forceinline s32 V_Core::RevbGetIndexer(s32 offset)
|
||||||
{
|
{
|
||||||
u32 pos = ReverbX + offset;
|
u32 pos = ReverbX + offset;
|
||||||
|
|
||||||
// Fast and simple single step wrapping, made possible by the preparation of the
|
// Fast and simple single step wrapping, made possible by the preparation of the
|
||||||
// effects buffer addresses.
|
// effects buffer addresses.
|
||||||
|
|
||||||
if (pos > EffectsEndA) {
|
if (pos > EffectsEndA)
|
||||||
pos -= EffectsEndA + 1;
|
{
|
||||||
pos += EffectsStartA;
|
pos -= EffectsEndA + 1;
|
||||||
}
|
pos += EffectsStartA;
|
||||||
|
}
|
||||||
|
|
||||||
assert(pos >= EffectsStartA && pos <= EffectsEndA);
|
assert(pos >= EffectsStartA && pos <= EffectsEndA);
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void V_Core::Reverb_AdvanceBuffer()
|
void V_Core::Reverb_AdvanceBuffer()
|
||||||
{
|
{
|
||||||
if (RevBuffers.NeedsUpdated)
|
if (RevBuffers.NeedsUpdated)
|
||||||
UpdateEffectsBufferSize();
|
UpdateEffectsBufferSize();
|
||||||
|
|
||||||
if ((Cycles & 1) && (EffectsBufferSize > 0)) {
|
if ((Cycles & 1) && (EffectsBufferSize > 0))
|
||||||
ReverbX += 1;
|
{
|
||||||
if (ReverbX >= (u32)EffectsBufferSize)
|
ReverbX += 1;
|
||||||
ReverbX = 0;
|
if (ReverbX >= (u32)EffectsBufferSize)
|
||||||
}
|
ReverbX = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
StereoOut32 V_Core::DoReverb(const StereoOut32 &Input)
|
StereoOut32 V_Core::DoReverb(const StereoOut32& Input)
|
||||||
{
|
{
|
||||||
if (EffectsBufferSize <= 0) {
|
if (EffectsBufferSize <= 0)
|
||||||
return StereoOut32::Empty;
|
{
|
||||||
}
|
return StereoOut32::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
bool R = Cycles & 1;
|
bool R = Cycles & 1;
|
||||||
|
|
||||||
// Calculate the read/write addresses we'll be needing for this session of reverb.
|
// 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_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_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 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_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_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 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 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 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 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 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_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 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_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);
|
const u32 apf2_dst = RevbGetIndexer(R ? RevBuffers.APF2_R_DST : RevBuffers.APF2_L_DST);
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
// Optimized IRQ Testing !
|
// Optimized IRQ Testing !
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
// This test is enhanced by using the reverb effects area begin/end test as a
|
// 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
|
// 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
|
// within that zone then the "bulk" of the test is skipped, so this should only
|
||||||
// be a slowdown on a few evil games.
|
// be a slowdown on a few evil games.
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++) {
|
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) ||
|
if (Cores[i].IRQEnable && ((Cores[i].IRQA >= EffectsStartA) && (Cores[i].IRQA <= EffectsEndA)))
|
||||||
(Cores[i].IRQA == same_dst) || (Cores[i].IRQA == diff_dst) ||
|
{
|
||||||
(Cores[i].IRQA == same_prv) || (Cores[i].IRQA == diff_prv) ||
|
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 == comb1_src) || (Cores[i].IRQA == comb2_src) ||
|
||||||
(Cores[i].IRQA == comb3_src) || (Cores[i].IRQA == comb4_src) ||
|
(Cores[i].IRQA == comb3_src) || (Cores[i].IRQA == comb4_src) ||
|
||||||
|
|
||||||
(Cores[i].IRQA == apf1_dst) || (Cores[i].IRQA == apf1_src) ||
|
(Cores[i].IRQA == apf1_dst) || (Cores[i].IRQA == apf1_src) ||
|
||||||
(Cores[i].IRQA == apf2_dst) || (Cores[i].IRQA == apf2_src)) {
|
(Cores[i].IRQA == apf2_dst) || (Cores[i].IRQA == apf2_src))
|
||||||
//printf("Core %d IRQ Called (Reverb). IRQA = %x\n",i,addr);
|
{
|
||||||
SetIrqCall(i);
|
//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/
|
// Reverb algorithm pretty much directly ripped from http://drhell.web.fc2.com/ps1/
|
||||||
// minus the 35 step FIR which just seems to break things.
|
// minus the 35 step FIR which just seems to break things.
|
||||||
|
|
||||||
s32 in, same, diff, apf1, apf2, out;
|
s32 in, same, diff, apf1, apf2, out;
|
||||||
|
|
||||||
#define MUL(x, y) ((x) * (y) >> 15)
|
#define MUL(x, y) ((x) * (y) >> 15)
|
||||||
in = MUL(R ? Revb.IN_COEF_R : Revb.IN_COEF_L, R ? Input.Right : Input.Left);
|
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];
|
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];
|
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]);
|
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]);
|
apf1 = out - MUL(Revb.APF1_VOL, _spu2mem[apf1_src]);
|
||||||
out = _spu2mem[apf1_src] + MUL(Revb.APF1_VOL, apf1);
|
out = _spu2mem[apf1_src] + MUL(Revb.APF1_VOL, apf1);
|
||||||
apf2 = out - MUL(Revb.APF2_VOL, _spu2mem[apf2_src]);
|
apf2 = out - MUL(Revb.APF2_VOL, _spu2mem[apf2_src]);
|
||||||
out = _spu2mem[apf2_src] + MUL(Revb.APF2_VOL, apf2);
|
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
|
// According to no$psx the effects always run but don't always write back, see check in V_Core::Mix
|
||||||
if (FxEnable) {
|
if (FxEnable)
|
||||||
_spu2mem[same_dst] = clamp_mix(same);
|
{
|
||||||
_spu2mem[diff_dst] = clamp_mix(diff);
|
_spu2mem[same_dst] = clamp_mix(same);
|
||||||
_spu2mem[apf1_dst] = clamp_mix(apf1);
|
_spu2mem[diff_dst] = clamp_mix(diff);
|
||||||
_spu2mem[apf2_dst] = clamp_mix(apf2);
|
_spu2mem[apf1_dst] = clamp_mix(apf1);
|
||||||
}
|
_spu2mem[apf2_dst] = clamp_mix(apf2);
|
||||||
|
}
|
||||||
|
|
||||||
(R ? LastEffect.Right : LastEffect.Left) = -clamp_mix(out);
|
(R ? LastEffect.Right : LastEffect.Left) = -clamp_mix(out);
|
||||||
|
|
||||||
return LastEffect;
|
return LastEffect;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,208 +18,219 @@
|
||||||
|
|
||||||
StereoOut32 StereoOut32::Empty(0, 0);
|
StereoOut32 StereoOut32::Empty(0, 0);
|
||||||
|
|
||||||
StereoOut32::StereoOut32(const StereoOut16 &src)
|
StereoOut32::StereoOut32(const StereoOut16& src)
|
||||||
: Left(src.Left)
|
: Left(src.Left)
|
||||||
, Right(src.Right)
|
, Right(src.Right)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
StereoOut32::StereoOut32(const StereoOutFloat &src)
|
StereoOut32::StereoOut32(const StereoOutFloat& src)
|
||||||
: Left((s32)(src.Left * 2147483647.0f))
|
: Left((s32)(src.Left * 2147483647.0f))
|
||||||
, Right((s32)(src.Right * 2147483647.0f))
|
, Right((s32)(src.Right * 2147483647.0f))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
StereoOut16 StereoOut32::DownSample() const
|
StereoOut16 StereoOut32::DownSample() const
|
||||||
{
|
{
|
||||||
return StereoOut16(
|
return StereoOut16(
|
||||||
Left >> SndOutVolumeShift,
|
Left >> SndOutVolumeShift,
|
||||||
Right >> SndOutVolumeShift);
|
Right >> SndOutVolumeShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
StereoOut32 StereoOut16::UpSample() const
|
StereoOut32 StereoOut16::UpSample() const
|
||||||
{
|
{
|
||||||
return StereoOut32(
|
return StereoOut32(
|
||||||
Left << SndOutVolumeShift,
|
Left << SndOutVolumeShift,
|
||||||
Right << SndOutVolumeShift);
|
Right << SndOutVolumeShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class NullOutModule : public SndOutModule
|
class NullOutModule : public SndOutModule
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
s32 Init() { return 0; }
|
s32 Init() { return 0; }
|
||||||
void Close() {}
|
void Close() {}
|
||||||
s32 Test() const { return 0; }
|
s32 Test() const { return 0; }
|
||||||
void Configure(uptr parent) {}
|
void Configure(uptr parent) {}
|
||||||
int GetEmptySampleCount() { return 0; }
|
int GetEmptySampleCount() { return 0; }
|
||||||
|
|
||||||
const wchar_t *GetIdent() const
|
const wchar_t* GetIdent() const
|
||||||
{
|
{
|
||||||
return L"nullout";
|
return L"nullout";
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *GetLongName() const
|
const wchar_t* GetLongName() const
|
||||||
{
|
{
|
||||||
return L"No Sound (Emulate SPU2 only)";
|
return L"No Sound (Emulate SPU2 only)";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadSettings()
|
void ReadSettings()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetApiSettings(wxString api)
|
void SetApiSettings(wxString api)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteSettings() const
|
void WriteSettings() const
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
} NullOut;
|
} NullOut;
|
||||||
|
|
||||||
SndOutModule *mods[] =
|
SndOutModule* mods[] =
|
||||||
{
|
{
|
||||||
&NullOut,
|
&NullOut,
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
XAudio2Out,
|
XAudio2Out,
|
||||||
DSoundOut,
|
DSoundOut,
|
||||||
WaveOut,
|
WaveOut,
|
||||||
#endif
|
#endif
|
||||||
#if defined(_WIN32) || defined(SPU2X_PORTAUDIO)
|
#if defined(_WIN32) || defined(SPU2X_PORTAUDIO)
|
||||||
PortaudioOut,
|
PortaudioOut,
|
||||||
#endif
|
#endif
|
||||||
SDLOut,
|
SDLOut,
|
||||||
#if defined(__linux__) /* && defined(__ALSA__)*/
|
#if defined(__linux__) /* && defined(__ALSA__)*/
|
||||||
AlsaOut,
|
AlsaOut,
|
||||||
#endif
|
#endif
|
||||||
NULL // signals the end of our list
|
NULL // signals the end of our list
|
||||||
};
|
};
|
||||||
|
|
||||||
int FindOutputModuleById(const wchar_t *omodid)
|
int FindOutputModuleById(const wchar_t* omodid)
|
||||||
{
|
{
|
||||||
int modcnt = 0;
|
int modcnt = 0;
|
||||||
while (mods[modcnt] != NULL) {
|
while (mods[modcnt] != NULL)
|
||||||
if (wcscmp(mods[modcnt]->GetIdent(), omodid) == 0)
|
{
|
||||||
break;
|
if (wcscmp(mods[modcnt]->GetIdent(), omodid) == 0)
|
||||||
++modcnt;
|
break;
|
||||||
}
|
++modcnt;
|
||||||
return modcnt;
|
}
|
||||||
|
return modcnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
StereoOut32 *SndBuffer::m_buffer;
|
StereoOut32* SndBuffer::m_buffer;
|
||||||
s32 SndBuffer::m_size;
|
s32 SndBuffer::m_size;
|
||||||
__aligned(4) volatile s32 SndBuffer::m_rpos;
|
__aligned(4) volatile s32 SndBuffer::m_rpos;
|
||||||
__aligned(4) volatile s32 SndBuffer::m_wpos;
|
__aligned(4) volatile s32 SndBuffer::m_wpos;
|
||||||
|
|
||||||
bool SndBuffer::m_underrun_freeze;
|
bool SndBuffer::m_underrun_freeze;
|
||||||
StereoOut32 *SndBuffer::sndTempBuffer = NULL;
|
StereoOut32* SndBuffer::sndTempBuffer = NULL;
|
||||||
StereoOut16 *SndBuffer::sndTempBuffer16 = NULL;
|
StereoOut16* SndBuffer::sndTempBuffer16 = NULL;
|
||||||
int SndBuffer::sndTempProgress = 0;
|
int SndBuffer::sndTempProgress = 0;
|
||||||
|
|
||||||
int GetAlignedBufferSize(int comp)
|
int GetAlignedBufferSize(int comp)
|
||||||
{
|
{
|
||||||
return (comp + SndOutPacketSize - 1) & ~(SndOutPacketSize - 1);
|
return (comp + SndOutPacketSize - 1) & ~(SndOutPacketSize - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns TRUE if there is data to be output, or false if no data
|
// Returns TRUE if there is data to be output, or false if no data
|
||||||
// is available to be copied.
|
// is available to be copied.
|
||||||
bool SndBuffer::CheckUnderrunStatus(int &nSamples, int &quietSampleCount)
|
bool SndBuffer::CheckUnderrunStatus(int& nSamples, int& quietSampleCount)
|
||||||
{
|
{
|
||||||
quietSampleCount = 0;
|
quietSampleCount = 0;
|
||||||
|
|
||||||
int data = _GetApproximateDataInBuffer();
|
int data = _GetApproximateDataInBuffer();
|
||||||
if (m_underrun_freeze) {
|
if (m_underrun_freeze)
|
||||||
int toFill = m_size / ((SynchMode == 2) ? 32 : 400); // TimeStretch and Async off?
|
{
|
||||||
toFill = GetAlignedBufferSize(toFill);
|
int toFill = m_size / ((SynchMode == 2) ? 32 : 400); // TimeStretch and Async off?
|
||||||
|
toFill = GetAlignedBufferSize(toFill);
|
||||||
|
|
||||||
// toFill is now aligned to a SndOutPacket
|
// toFill is now aligned to a SndOutPacket
|
||||||
|
|
||||||
if (data < toFill) {
|
if (data < toFill)
|
||||||
quietSampleCount = nSamples;
|
{
|
||||||
return false;
|
quietSampleCount = nSamples;
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_underrun_freeze = false;
|
m_underrun_freeze = false;
|
||||||
if (MsgOverruns())
|
if (MsgOverruns())
|
||||||
ConLog(" * SPU2 > Underrun compensation (%d packets buffered)\n", toFill / SndOutPacketSize);
|
ConLog(" * SPU2 > Underrun compensation (%d packets buffered)\n", toFill / SndOutPacketSize);
|
||||||
lastPct = 0.0; // normalize timestretcher
|
lastPct = 0.0; // normalize timestretcher
|
||||||
} else if (data < nSamples) {
|
}
|
||||||
nSamples = data;
|
else if (data < nSamples)
|
||||||
quietSampleCount = SndOutPacketSize - data;
|
{
|
||||||
m_underrun_freeze = true;
|
nSamples = data;
|
||||||
|
quietSampleCount = SndOutPacketSize - data;
|
||||||
|
m_underrun_freeze = true;
|
||||||
|
|
||||||
if (SynchMode == 0) // TimeStrech on
|
if (SynchMode == 0) // TimeStrech on
|
||||||
timeStretchUnderrun();
|
timeStretchUnderrun();
|
||||||
|
|
||||||
return nSamples != 0;
|
return nSamples != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndBuffer::_InitFail()
|
void SndBuffer::_InitFail()
|
||||||
{
|
{
|
||||||
// If a failure occurs, just initialize the NoSound driver. This'll allow
|
// If a failure occurs, just initialize the NoSound driver. This'll allow
|
||||||
// the game to emulate properly (hopefully), albeit without sound.
|
// the game to emulate properly (hopefully), albeit without sound.
|
||||||
OutputModule = FindOutputModuleById(NullOut.GetIdent());
|
OutputModule = FindOutputModuleById(NullOut.GetIdent());
|
||||||
mods[OutputModule]->Init();
|
mods[OutputModule]->Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
int SndBuffer::_GetApproximateDataInBuffer()
|
int SndBuffer::_GetApproximateDataInBuffer()
|
||||||
{
|
{
|
||||||
// WARNING: not necessarily 100% up to date by the time it's used, but it will have to do.
|
// 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;
|
return (m_wpos + m_size - m_rpos) % m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndBuffer::_WriteSamples_Internal(StereoOut32 *bData, int nSamples)
|
void SndBuffer::_WriteSamples_Internal(StereoOut32* bData, int nSamples)
|
||||||
{
|
{
|
||||||
// WARNING: This assumes the write will NOT wrap around,
|
// WARNING: This assumes the write will NOT wrap around,
|
||||||
// and also assumes there's enough free space in the buffer.
|
// and also assumes there's enough free space in the buffer.
|
||||||
|
|
||||||
memcpy(m_buffer + m_wpos, bData, nSamples * sizeof(StereoOut32));
|
memcpy(m_buffer + m_wpos, bData, nSamples * sizeof(StereoOut32));
|
||||||
m_wpos = (m_wpos + nSamples) % m_size;
|
m_wpos = (m_wpos + nSamples) % m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndBuffer::_DropSamples_Internal(int nSamples)
|
void SndBuffer::_DropSamples_Internal(int nSamples)
|
||||||
{
|
{
|
||||||
m_rpos = (m_rpos + nSamples) % m_size;
|
m_rpos = (m_rpos + nSamples) % m_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndBuffer::_ReadSamples_Internal(StereoOut32 *bData, int nSamples)
|
void SndBuffer::_ReadSamples_Internal(StereoOut32* bData, int nSamples)
|
||||||
{
|
{
|
||||||
// WARNING: This assumes the read will NOT wrap around,
|
// WARNING: This assumes the read will NOT wrap around,
|
||||||
// and also assumes there's enough data in the buffer.
|
// and also assumes there's enough data in the buffer.
|
||||||
memcpy(bData, m_buffer + m_rpos, nSamples * sizeof(StereoOut32));
|
memcpy(bData, m_buffer + m_rpos, nSamples * sizeof(StereoOut32));
|
||||||
_DropSamples_Internal(nSamples);
|
_DropSamples_Internal(nSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndBuffer::_WriteSamples_Safe(StereoOut32 *bData, int nSamples)
|
void SndBuffer::_WriteSamples_Safe(StereoOut32* bData, int nSamples)
|
||||||
{
|
{
|
||||||
// WARNING: This code assumes there's only ONE writing process.
|
// WARNING: This code assumes there's only ONE writing process.
|
||||||
if ((m_size - m_wpos) < nSamples) {
|
if ((m_size - m_wpos) < nSamples)
|
||||||
int b1 = m_size - m_wpos;
|
{
|
||||||
int b2 = nSamples - b1;
|
int b1 = m_size - m_wpos;
|
||||||
|
int b2 = nSamples - b1;
|
||||||
|
|
||||||
_WriteSamples_Internal(bData, b1);
|
_WriteSamples_Internal(bData, b1);
|
||||||
_WriteSamples_Internal(bData + b1, b2);
|
_WriteSamples_Internal(bData + b1, b2);
|
||||||
} else {
|
}
|
||||||
_WriteSamples_Internal(bData, nSamples);
|
else
|
||||||
}
|
{
|
||||||
|
_WriteSamples_Internal(bData, nSamples);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndBuffer::_ReadSamples_Safe(StereoOut32 *bData, int nSamples)
|
void SndBuffer::_ReadSamples_Safe(StereoOut32* bData, int nSamples)
|
||||||
{
|
{
|
||||||
// WARNING: This code assumes there's only ONE reading process.
|
// WARNING: This code assumes there's only ONE reading process.
|
||||||
if ((m_size - m_rpos) < nSamples) {
|
if ((m_size - m_rpos) < nSamples)
|
||||||
int b1 = m_size - m_rpos;
|
{
|
||||||
int b2 = nSamples - b1;
|
int b1 = m_size - m_rpos;
|
||||||
|
int b2 = nSamples - b1;
|
||||||
|
|
||||||
_ReadSamples_Internal(bData, b1);
|
_ReadSamples_Internal(bData, b1);
|
||||||
_ReadSamples_Internal(bData + b1, b2);
|
_ReadSamples_Internal(bData + b1, b2);
|
||||||
} else {
|
}
|
||||||
_ReadSamples_Internal(bData, nSamples);
|
else
|
||||||
}
|
{
|
||||||
|
_ReadSamples_Internal(bData, nSamples);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: When using with 32 bit output buffers, the user of this function is responsible
|
// Note: When using with 32 bit output buffers, the user of this function is responsible
|
||||||
|
@ -227,100 +238,105 @@ void SndBuffer::_ReadSamples_Safe(StereoOut32 *bData, int nSamples)
|
||||||
// the sample output is determined by the SndOutVolumeShift, which is the number of bits
|
// the sample output is determined by the SndOutVolumeShift, which is the number of bits
|
||||||
// to shift right to get a 16 bit result.
|
// to shift right to get a 16 bit result.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void SndBuffer::ReadSamples(T *bData)
|
void SndBuffer::ReadSamples(T* bData)
|
||||||
{
|
{
|
||||||
int nSamples = SndOutPacketSize;
|
int nSamples = SndOutPacketSize;
|
||||||
|
|
||||||
// Problem:
|
// Problem:
|
||||||
// If the SPU2 gets even the least bit out of sync with the SndOut device,
|
// 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,
|
// the readpos of the circular buffer will overtake the writepos,
|
||||||
// leading to a prolonged period of hopscotching read/write accesses (ie,
|
// leading to a prolonged period of hopscotching read/write accesses (ie,
|
||||||
// lots of staticy crap sound for several seconds).
|
// lots of staticy crap sound for several seconds).
|
||||||
//
|
//
|
||||||
// Fix:
|
// Fix:
|
||||||
// If the read position overtakes the write position, abort the
|
// If the read position overtakes the write position, abort the
|
||||||
// transfer immediately and force the SndOut driver to wait until
|
// transfer immediately and force the SndOut driver to wait until
|
||||||
// the read buffer has filled up again before proceeding.
|
// the read buffer has filled up again before proceeding.
|
||||||
// This will cause one brief hiccup that can never exceed the user's
|
// This will cause one brief hiccup that can never exceed the user's
|
||||||
// set buffer length in duration.
|
// set buffer length in duration.
|
||||||
|
|
||||||
int quietSamples;
|
int quietSamples;
|
||||||
if (CheckUnderrunStatus(nSamples, quietSamples)) {
|
if (CheckUnderrunStatus(nSamples, quietSamples))
|
||||||
pxAssume(nSamples <= SndOutPacketSize);
|
{
|
||||||
|
pxAssume(nSamples <= SndOutPacketSize);
|
||||||
|
|
||||||
// WARNING: This code assumes there's only ONE reading process.
|
// WARNING: This code assumes there's only ONE reading process.
|
||||||
int b1 = m_size - m_rpos;
|
int b1 = m_size - m_rpos;
|
||||||
|
|
||||||
if (b1 > nSamples)
|
if (b1 > nSamples)
|
||||||
b1 = nSamples;
|
b1 = nSamples;
|
||||||
|
|
||||||
if (AdvancedVolumeControl) {
|
if (AdvancedVolumeControl)
|
||||||
// First part
|
{
|
||||||
for (int i = 0; i < b1; i++)
|
// First part
|
||||||
bData[i].AdjustFrom(m_buffer[i + m_rpos]);
|
for (int i = 0; i < b1; i++)
|
||||||
|
bData[i].AdjustFrom(m_buffer[i + m_rpos]);
|
||||||
|
|
||||||
// Second part
|
// Second part
|
||||||
int b2 = nSamples - b1;
|
int b2 = nSamples - b1;
|
||||||
for (int i = 0; i < b2; i++)
|
for (int i = 0; i < b2; i++)
|
||||||
bData[i + b1].AdjustFrom(m_buffer[i]);
|
bData[i + b1].AdjustFrom(m_buffer[i]);
|
||||||
} else {
|
}
|
||||||
// First part
|
else
|
||||||
for (int i = 0; i < b1; i++)
|
{
|
||||||
bData[i].ResampleFrom(m_buffer[i + m_rpos]);
|
// First part
|
||||||
|
for (int i = 0; i < b1; i++)
|
||||||
|
bData[i].ResampleFrom(m_buffer[i + m_rpos]);
|
||||||
|
|
||||||
// Second part
|
// Second part
|
||||||
int b2 = nSamples - b1;
|
int b2 = nSamples - b1;
|
||||||
for (int i = 0; i < b2; i++)
|
for (int i = 0; i < b2; i++)
|
||||||
bData[i + b1].ResampleFrom(m_buffer[i]);
|
bData[i + b1].ResampleFrom(m_buffer[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
_DropSamples_Internal(nSamples);
|
_DropSamples_Internal(nSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If quietSamples != 0 it means we have an underrun...
|
// If quietSamples != 0 it means we have an underrun...
|
||||||
// Let's just dull out some silence, because that's usually the least
|
// Let's just dull out some silence, because that's usually the least
|
||||||
// painful way of dealing with underruns:
|
// painful way of dealing with underruns:
|
||||||
std::fill_n(bData, quietSamples, T{});
|
std::fill_n(bData, quietSamples, T{});
|
||||||
}
|
}
|
||||||
|
|
||||||
template void SndBuffer::ReadSamples(StereoOut16 *);
|
template void SndBuffer::ReadSamples(StereoOut16*);
|
||||||
template void SndBuffer::ReadSamples(StereoOut32 *);
|
template void SndBuffer::ReadSamples(StereoOut32*);
|
||||||
|
|
||||||
//template void SndBuffer::ReadSamples(StereoOutFloat*);
|
//template void SndBuffer::ReadSamples(StereoOutFloat*);
|
||||||
template void SndBuffer::ReadSamples(Stereo21Out16 *);
|
template void SndBuffer::ReadSamples(Stereo21Out16*);
|
||||||
template void SndBuffer::ReadSamples(Stereo40Out16 *);
|
template void SndBuffer::ReadSamples(Stereo40Out16*);
|
||||||
template void SndBuffer::ReadSamples(Stereo41Out16 *);
|
template void SndBuffer::ReadSamples(Stereo41Out16*);
|
||||||
template void SndBuffer::ReadSamples(Stereo51Out16 *);
|
template void SndBuffer::ReadSamples(Stereo51Out16*);
|
||||||
template void SndBuffer::ReadSamples(Stereo51Out16Dpl *);
|
template void SndBuffer::ReadSamples(Stereo51Out16Dpl*);
|
||||||
template void SndBuffer::ReadSamples(Stereo51Out16DplII *);
|
template void SndBuffer::ReadSamples(Stereo51Out16DplII*);
|
||||||
template void SndBuffer::ReadSamples(Stereo71Out16 *);
|
template void SndBuffer::ReadSamples(Stereo71Out16*);
|
||||||
|
|
||||||
template void SndBuffer::ReadSamples(Stereo20Out32 *);
|
template void SndBuffer::ReadSamples(Stereo20Out32*);
|
||||||
template void SndBuffer::ReadSamples(Stereo21Out32 *);
|
template void SndBuffer::ReadSamples(Stereo21Out32*);
|
||||||
template void SndBuffer::ReadSamples(Stereo40Out32 *);
|
template void SndBuffer::ReadSamples(Stereo40Out32*);
|
||||||
template void SndBuffer::ReadSamples(Stereo41Out32 *);
|
template void SndBuffer::ReadSamples(Stereo41Out32*);
|
||||||
template void SndBuffer::ReadSamples(Stereo51Out32 *);
|
template void SndBuffer::ReadSamples(Stereo51Out32*);
|
||||||
template void SndBuffer::ReadSamples(Stereo51Out32Dpl *);
|
template void SndBuffer::ReadSamples(Stereo51Out32Dpl*);
|
||||||
template void SndBuffer::ReadSamples(Stereo51Out32DplII *);
|
template void SndBuffer::ReadSamples(Stereo51Out32DplII*);
|
||||||
template void SndBuffer::ReadSamples(Stereo71Out32 *);
|
template void SndBuffer::ReadSamples(Stereo71Out32*);
|
||||||
|
|
||||||
void SndBuffer::_WriteSamples(StereoOut32 *bData, int nSamples)
|
void SndBuffer::_WriteSamples(StereoOut32* bData, int nSamples)
|
||||||
{
|
{
|
||||||
m_predictData = 0;
|
m_predictData = 0;
|
||||||
|
|
||||||
// Problem:
|
// Problem:
|
||||||
// If the SPU2 gets out of sync with the SndOut device, the writepos of the
|
// 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
|
// circular buffer will overtake the readpos, leading to a prolonged period
|
||||||
// of hopscotching read/write accesses (ie, lots of staticy crap sound for
|
// of hopscotching read/write accesses (ie, lots of staticy crap sound for
|
||||||
// several seconds).
|
// several seconds).
|
||||||
//
|
//
|
||||||
// Compromise:
|
// Compromise:
|
||||||
// When an overrun occurs, we adapt by discarding a portion of the buffer.
|
// 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,
|
// The older portion of the buffer is discarded rather than incoming data,
|
||||||
// so that the overall audio synchronization is better.
|
// so that the overall audio synchronization is better.
|
||||||
|
|
||||||
int free = m_size - _GetApproximateDataInBuffer(); // -1, but the <= handles that
|
int free = m_size - _GetApproximateDataInBuffer(); // -1, but the <= handles that
|
||||||
if (free <= nSamples) {
|
if (free <= nSamples)
|
||||||
|
{
|
||||||
// Disabled since the lock-free queue can't handle changing the read end from the write thread
|
// Disabled since the lock-free queue can't handle changing the read end from the write thread
|
||||||
#if 0
|
#if 0
|
||||||
// Buffer overrun!
|
// Buffer overrun!
|
||||||
|
@ -346,65 +362,69 @@ void SndBuffer::_WriteSamples(StereoOut32 *bData, int nSamples)
|
||||||
ConLog(" * SPU2 > Overrun Compensation (%d packets tossed)\n", comp / SndOutPacketSize );
|
ConLog(" * SPU2 > Overrun Compensation (%d packets tossed)\n", comp / SndOutPacketSize );
|
||||||
lastPct = 0.0; // normalize the timestretcher
|
lastPct = 0.0; // normalize the timestretcher
|
||||||
#else
|
#else
|
||||||
if (MsgOverruns())
|
if (MsgOverruns())
|
||||||
ConLog(" * SPU2 > Overrun! 1 packet tossed)\n");
|
ConLog(" * SPU2 > Overrun! 1 packet tossed)\n");
|
||||||
lastPct = 0.0; // normalize the timestretcher
|
lastPct = 0.0; // normalize the timestretcher
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
_WriteSamples_Safe(bData, nSamples);
|
_WriteSamples_Safe(bData, nSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndBuffer::Init()
|
void SndBuffer::Init()
|
||||||
{
|
{
|
||||||
if (mods[OutputModule] == NULL) {
|
if (mods[OutputModule] == NULL)
|
||||||
_InitFail();
|
{
|
||||||
return;
|
_InitFail();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// initialize sound buffer
|
// initialize sound buffer
|
||||||
// Buffer actually attempts to run ~50%, so allocate near double what
|
// Buffer actually attempts to run ~50%, so allocate near double what
|
||||||
// the requested latency is:
|
// the requested latency is:
|
||||||
|
|
||||||
m_rpos = 0;
|
m_rpos = 0;
|
||||||
m_wpos = 0;
|
m_wpos = 0;
|
||||||
|
|
||||||
try {
|
try
|
||||||
const float latencyMS = SndOutLatencyMS * 16;
|
{
|
||||||
m_size = GetAlignedBufferSize((int)(latencyMS * SampleRate / 1000.0f));
|
const float latencyMS = SndOutLatencyMS * 16;
|
||||||
printf("%d SampleRate: \n", SampleRate);
|
m_size = GetAlignedBufferSize((int)(latencyMS * SampleRate / 1000.0f));
|
||||||
m_buffer = new StereoOut32[m_size];
|
printf("%d SampleRate: \n", SampleRate);
|
||||||
m_underrun_freeze = false;
|
m_buffer = new StereoOut32[m_size];
|
||||||
|
m_underrun_freeze = false;
|
||||||
|
|
||||||
sndTempBuffer = new StereoOut32[SndOutPacketSize];
|
sndTempBuffer = new StereoOut32[SndOutPacketSize];
|
||||||
sndTempBuffer16 = new StereoOut16[SndOutPacketSize * 2]; // in case of leftovers.
|
sndTempBuffer16 = new StereoOut16[SndOutPacketSize * 2]; // in case of leftovers.
|
||||||
} catch (std::bad_alloc &) {
|
}
|
||||||
// out of memory exception (most likely)
|
catch (std::bad_alloc&)
|
||||||
|
{
|
||||||
|
// out of memory exception (most likely)
|
||||||
|
|
||||||
SysMessage("Out of memory error occurred while initializing SPU2.");
|
SysMessage("Out of memory error occurred while initializing SPU2.");
|
||||||
_InitFail();
|
_InitFail();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sndTempProgress = 0;
|
sndTempProgress = 0;
|
||||||
|
|
||||||
soundtouchInit(); // initializes the timestretching
|
soundtouchInit(); // initializes the timestretching
|
||||||
|
|
||||||
// initialize module
|
// initialize module
|
||||||
if (mods[OutputModule]->Init() == -1)
|
if (mods[OutputModule]->Init() == -1)
|
||||||
_InitFail();
|
_InitFail();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndBuffer::Cleanup()
|
void SndBuffer::Cleanup()
|
||||||
{
|
{
|
||||||
mods[OutputModule]->Close();
|
mods[OutputModule]->Close();
|
||||||
|
|
||||||
soundtouchCleanup();
|
soundtouchCleanup();
|
||||||
|
|
||||||
safe_delete_array(m_buffer);
|
safe_delete_array(m_buffer);
|
||||||
safe_delete_array(sndTempBuffer);
|
safe_delete_array(sndTempBuffer);
|
||||||
safe_delete_array(sndTempBuffer16);
|
safe_delete_array(sndTempBuffer16);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SndBuffer::m_dsp_progress = 0;
|
int SndBuffer::m_dsp_progress = 0;
|
||||||
|
@ -414,78 +434,85 @@ int SndBuffer::ssFreeze = 0;
|
||||||
|
|
||||||
void SndBuffer::ClearContents()
|
void SndBuffer::ClearContents()
|
||||||
{
|
{
|
||||||
SndBuffer::soundtouchClearContents();
|
SndBuffer::soundtouchClearContents();
|
||||||
SndBuffer::ssFreeze = 256; //Delays sound output for about 1 second.
|
SndBuffer::ssFreeze = 256; //Delays sound output for about 1 second.
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndBuffer::Write(const StereoOut32 &Sample)
|
void SndBuffer::Write(const StereoOut32& Sample)
|
||||||
{
|
{
|
||||||
// Log final output to wavefile.
|
// Log final output to wavefile.
|
||||||
WaveDump::WriteCore(1, CoreSrc_External, Sample.DownSample());
|
WaveDump::WriteCore(1, CoreSrc_External, Sample.DownSample());
|
||||||
|
|
||||||
if (WavRecordEnabled)
|
if (WavRecordEnabled)
|
||||||
RecordWrite(Sample.DownSample());
|
RecordWrite(Sample.DownSample());
|
||||||
|
|
||||||
if (mods[OutputModule] == &NullOut) // null output doesn't need buffering or stretching! :p
|
if (mods[OutputModule] == &NullOut) // null output doesn't need buffering or stretching! :p
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sndTempBuffer[sndTempProgress++] = Sample;
|
sndTempBuffer[sndTempProgress++] = Sample;
|
||||||
|
|
||||||
// If we haven't accumulated a full packet yet, do nothing more:
|
// If we haven't accumulated a full packet yet, do nothing more:
|
||||||
if (sndTempProgress < SndOutPacketSize)
|
if (sndTempProgress < SndOutPacketSize)
|
||||||
return;
|
return;
|
||||||
sndTempProgress = 0;
|
sndTempProgress = 0;
|
||||||
|
|
||||||
//Don't play anything directly after loading a savestate, avoids static killing your speakers.
|
//Don't play anything directly after loading a savestate, avoids static killing your speakers.
|
||||||
if (ssFreeze > 0) {
|
if (ssFreeze > 0)
|
||||||
ssFreeze--;
|
{
|
||||||
// Play silence
|
ssFreeze--;
|
||||||
std::fill_n(sndTempBuffer, SndOutPacketSize, StereoOut32{});
|
// Play silence
|
||||||
}
|
std::fill_n(sndTempBuffer, SndOutPacketSize, StereoOut32{});
|
||||||
|
}
|
||||||
#ifndef __POSIX__
|
#ifndef __POSIX__
|
||||||
if (dspPluginEnabled) {
|
if (dspPluginEnabled)
|
||||||
// Convert in, send to winamp DSP, and convert out.
|
{
|
||||||
|
// Convert in, send to winamp DSP, and convert out.
|
||||||
|
|
||||||
int ei = m_dsp_progress;
|
int ei = m_dsp_progress;
|
||||||
for (int i = 0; i < SndOutPacketSize; ++i, ++ei) {
|
for (int i = 0; i < SndOutPacketSize; ++i, ++ei)
|
||||||
sndTempBuffer16[ei] = sndTempBuffer[i].DownSample();
|
{
|
||||||
}
|
sndTempBuffer16[ei] = sndTempBuffer[i].DownSample();
|
||||||
m_dsp_progress += DspProcess((s16 *)sndTempBuffer16 + m_dsp_progress, SndOutPacketSize);
|
}
|
||||||
|
m_dsp_progress += DspProcess((s16*)sndTempBuffer16 + m_dsp_progress, SndOutPacketSize);
|
||||||
|
|
||||||
// Some ugly code to ensure full packet handling:
|
// Some ugly code to ensure full packet handling:
|
||||||
ei = 0;
|
ei = 0;
|
||||||
while (m_dsp_progress >= SndOutPacketSize) {
|
while (m_dsp_progress >= SndOutPacketSize)
|
||||||
for (int i = 0; i < SndOutPacketSize; ++i, ++ei) {
|
{
|
||||||
sndTempBuffer[i] = sndTempBuffer16[ei].UpSample();
|
for (int i = 0; i < SndOutPacketSize; ++i, ++ei)
|
||||||
}
|
{
|
||||||
|
sndTempBuffer[i] = sndTempBuffer16[ei].UpSample();
|
||||||
|
}
|
||||||
|
|
||||||
if (SynchMode == 0) // TimeStrech on
|
if (SynchMode == 0) // TimeStrech on
|
||||||
timeStretchWrite();
|
timeStretchWrite();
|
||||||
else
|
else
|
||||||
_WriteSamples(sndTempBuffer, SndOutPacketSize);
|
_WriteSamples(sndTempBuffer, SndOutPacketSize);
|
||||||
|
|
||||||
m_dsp_progress -= SndOutPacketSize;
|
m_dsp_progress -= SndOutPacketSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy any leftovers to the front of the dsp buffer.
|
// copy any leftovers to the front of the dsp buffer.
|
||||||
if (m_dsp_progress > 0) {
|
if (m_dsp_progress > 0)
|
||||||
memcpy(sndTempBuffer16, &sndTempBuffer16[ei],
|
{
|
||||||
sizeof(sndTempBuffer16[0]) * m_dsp_progress);
|
memcpy(sndTempBuffer16, &sndTempBuffer16[ei],
|
||||||
}
|
sizeof(sndTempBuffer16[0]) * m_dsp_progress);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
else {
|
else
|
||||||
if (SynchMode == 0) // TimeStrech on
|
{
|
||||||
timeStretchWrite();
|
if (SynchMode == 0) // TimeStrech on
|
||||||
else
|
timeStretchWrite();
|
||||||
_WriteSamples(sndTempBuffer, SndOutPacketSize);
|
else
|
||||||
}
|
_WriteSamples(sndTempBuffer, SndOutPacketSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 SndBuffer::Test()
|
s32 SndBuffer::Test()
|
||||||
{
|
{
|
||||||
if (mods[OutputModule] == NULL)
|
if (mods[OutputModule] == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return mods[OutputModule]->Test();
|
return mods[OutputModule]->Test();
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -35,159 +35,168 @@ typedef StereoOut16 StereoOut_SDL;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
/* Since spu2 only ever outputs stereo, we don't worry about emitting surround sound
|
/* Since spu2 only ever outputs stereo, we don't worry about emitting surround sound
|
||||||
* even though SDL2 supports it */
|
* even though SDL2 supports it */
|
||||||
const Uint8 channels = 2;
|
const Uint8 channels = 2;
|
||||||
/* SDL2 supports s32 audio */
|
/* SDL2 supports s32 audio */
|
||||||
/* Samples should vary from [512,8192] according to SDL spec. Take note this is the desired
|
/* 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
|
* 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. */
|
* PA_STREAM_ADJUST_LATENCY is set in the backened, for example. */
|
||||||
const Uint16 desiredSamples = 2048;
|
const Uint16 desiredSamples = 2048;
|
||||||
const Uint16 format = AUDIO_S16SYS;
|
const Uint16 format = AUDIO_S16SYS;
|
||||||
|
|
||||||
Uint16 samples = desiredSamples;
|
Uint16 samples = desiredSamples;
|
||||||
|
|
||||||
std::unique_ptr<StereoOut_SDL[]> buffer;
|
std::unique_ptr<StereoOut_SDL[]> buffer;
|
||||||
|
|
||||||
void callback_fillBuffer(void *userdata, Uint8 *stream, int len)
|
void callback_fillBuffer(void* userdata, Uint8* stream, int len)
|
||||||
{
|
{
|
||||||
Uint16 sdl_samples = samples;
|
Uint16 sdl_samples = samples;
|
||||||
|
|
||||||
#if SDL_MAJOR_VERSION >= 2
|
#if SDL_MAJOR_VERSION >= 2
|
||||||
memset(stream, 0, len);
|
memset(stream, 0, len);
|
||||||
// As of SDL 2.0.4 the buffer is too small to contains all samples
|
// 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
|
// len is 2048, samples is 1024 and sizeof(StereoOut_SDL) is 4
|
||||||
sdl_samples = len / sizeof(StereoOut_SDL);
|
sdl_samples = len / sizeof(StereoOut_SDL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Length should always be samples in bytes.
|
// Length should always be samples in bytes.
|
||||||
assert(len / sizeof(StereoOut_SDL) == sdl_samples);
|
assert(len / sizeof(StereoOut_SDL) == sdl_samples);
|
||||||
|
|
||||||
for (Uint16 i = 0; i < sdl_samples; i += SndOutPacketSize)
|
for (Uint16 i = 0; i < sdl_samples; i += SndOutPacketSize)
|
||||||
SndBuffer::ReadSamples(&buffer[i]);
|
SndBuffer::ReadSamples(&buffer[i]);
|
||||||
SDL_MixAudio(stream, (Uint8 *)buffer.get(), len, SDL_MIX_MAXVOLUME);
|
SDL_MixAudio(stream, (Uint8*)buffer.get(), len, SDL_MIX_MAXVOLUME);
|
||||||
}
|
}
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
struct SDLAudioMod : public SndOutModule
|
struct SDLAudioMod : public SndOutModule
|
||||||
{
|
{
|
||||||
static SDLAudioMod mod;
|
static SDLAudioMod mod;
|
||||||
std::string m_api;
|
std::string m_api;
|
||||||
|
|
||||||
s32 Init()
|
s32 Init()
|
||||||
{
|
{
|
||||||
ReadSettings();
|
ReadSettings();
|
||||||
|
|
||||||
#if SDL_MAJOR_VERSION >= 2
|
#if SDL_MAJOR_VERSION >= 2
|
||||||
std::cerr << "Request SDL audio driver: " << m_api.c_str() << std::endl;
|
std::cerr << "Request SDL audio driver: " << m_api.c_str() << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* SDL backends will mangle the AudioSpec and change the sample count. If we reopen
|
/* 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 */
|
* the audio backend, we need to make sure we keep our desired samples in the spec */
|
||||||
spec.samples = desiredSamples;
|
spec.samples = desiredSamples;
|
||||||
|
|
||||||
// Mandatory otherwise, init will be redone in SDL_OpenAudio
|
// Mandatory otherwise, init will be redone in SDL_OpenAudio
|
||||||
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
|
if (SDL_Init(SDL_INIT_AUDIO) < 0)
|
||||||
std::cerr << "SPU2-X: SDL INIT audio error: " << SDL_GetError() << std::endl;
|
{
|
||||||
return -1;
|
std::cerr << "SPU2-X: SDL INIT audio error: " << SDL_GetError() << std::endl;
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
#if SDL_MAJOR_VERSION >= 2
|
#if SDL_MAJOR_VERSION >= 2
|
||||||
if (m_api.compare("pulseaudio")) {
|
if (m_api.compare("pulseaudio"))
|
||||||
// Close the audio, but keep the subsystem open
|
{
|
||||||
SDL_AudioQuit();
|
// Close the audio, but keep the subsystem open
|
||||||
// Reopen the audio
|
SDL_AudioQuit();
|
||||||
if (SDL_AudioInit(m_api.c_str()) < 0) {
|
// Reopen the audio
|
||||||
std::cerr << "SPU2-X: SDL audio init error: " << SDL_GetError() << std::endl;
|
if (SDL_AudioInit(m_api.c_str()) < 0)
|
||||||
return -1;
|
{
|
||||||
}
|
std::cerr << "SPU2-X: SDL audio init error: " << SDL_GetError() << std::endl;
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (SDL_OpenAudio(&spec, NULL) < 0) {
|
if (SDL_OpenAudio(&spec, NULL) < 0)
|
||||||
std::cerr << "SPU2-X: SDL audio error: " << SDL_GetError() << std::endl;
|
{
|
||||||
return -1;
|
std::cerr << "SPU2-X: SDL audio error: " << SDL_GetError() << std::endl;
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
#if SDL_MAJOR_VERSION >= 2
|
#if SDL_MAJOR_VERSION >= 2
|
||||||
std::cerr << "Opened SDL audio driver: " << SDL_GetCurrentAudioDriver() << std::endl;
|
std::cerr << "Opened SDL audio driver: " << SDL_GetCurrentAudioDriver() << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* This is so ugly. It is hilariously ugly. I didn't use a vector to save reallocs. */
|
/* This is so ugly. It is hilariously ugly. I didn't use a vector to save reallocs. */
|
||||||
if (samples != spec.samples || buffer == NULL)
|
if (samples != spec.samples || buffer == NULL)
|
||||||
buffer = std::unique_ptr<StereoOut_SDL[]>(new StereoOut_SDL[spec.samples]);
|
buffer = std::unique_ptr<StereoOut_SDL[]>(new StereoOut_SDL[spec.samples]);
|
||||||
if (samples != 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);
|
{
|
||||||
|
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.
|
// Samples must always be a multiple of packet size.
|
||||||
assert(spec.samples % SndOutPacketSize == 0);
|
assert(spec.samples % SndOutPacketSize == 0);
|
||||||
samples = spec.samples;
|
samples = spec.samples;
|
||||||
}
|
}
|
||||||
SDL_PauseAudio(0);
|
SDL_PauseAudio(0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *GetIdent() const { return L"SDLAudio"; }
|
const wchar_t* GetIdent() const { return L"SDLAudio"; }
|
||||||
const wchar_t *GetLongName() const { return L"SDL Audio"; }
|
const wchar_t* GetLongName() const { return L"SDL Audio"; }
|
||||||
|
|
||||||
void Close()
|
void Close()
|
||||||
{
|
{
|
||||||
// Related to SDL_Init(SDL_INIT_AUDIO)
|
// Related to SDL_Init(SDL_INIT_AUDIO)
|
||||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
~SDLAudioMod() { Close(); }
|
~SDLAudioMod() { Close(); }
|
||||||
|
|
||||||
s32 Test() const { return 0; }
|
s32 Test() const { return 0; }
|
||||||
int GetEmptySampleCount() { return 0; }
|
int GetEmptySampleCount() { return 0; }
|
||||||
|
|
||||||
void Configure(uptr parent) {}
|
void Configure(uptr parent) {}
|
||||||
|
|
||||||
void ReadSettings()
|
void ReadSettings()
|
||||||
{
|
{
|
||||||
wxString api(L"EMPTYEMPTYEMPTY");
|
wxString api(L"EMPTYEMPTYEMPTY");
|
||||||
CfgReadStr(L"SDL", L"HostApi", api, L"pulseaudio");
|
CfgReadStr(L"SDL", L"HostApi", api, L"pulseaudio");
|
||||||
SetApiSettings(api);
|
SetApiSettings(api);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteSettings() const
|
void WriteSettings() const
|
||||||
{
|
{
|
||||||
CfgWriteStr(L"SDL", L"HostApi", wxString(m_api.c_str(), wxConvUTF8));
|
CfgWriteStr(L"SDL", L"HostApi", wxString(m_api.c_str(), wxConvUTF8));
|
||||||
};
|
};
|
||||||
|
|
||||||
void SetApiSettings(wxString api)
|
void SetApiSettings(wxString api)
|
||||||
{
|
{
|
||||||
#if SDL_MAJOR_VERSION >= 2
|
#if SDL_MAJOR_VERSION >= 2
|
||||||
// Validate the api name
|
// Validate the api name
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
std::string api_name = std::string(api.utf8_str());
|
std::string api_name = std::string(api.utf8_str());
|
||||||
for (int i = 0; i < SDL_GetNumAudioDrivers(); ++i) {
|
for (int i = 0; i < SDL_GetNumAudioDrivers(); ++i)
|
||||||
valid |= (api_name.compare(SDL_GetAudioDriver(i)) == 0);
|
{
|
||||||
}
|
valid |= (api_name.compare(SDL_GetAudioDriver(i)) == 0);
|
||||||
if (valid) {
|
}
|
||||||
m_api = api.utf8_str();
|
if (valid)
|
||||||
} else {
|
{
|
||||||
std::cerr << "SDL audio driver configuration is invalid!" << std::endl
|
m_api = api.utf8_str();
|
||||||
<< "It will be replaced by pulseaudio!" << std::endl;
|
}
|
||||||
m_api = "pulseaudio";
|
else
|
||||||
}
|
{
|
||||||
|
std::cerr << "SDL audio driver configuration is invalid!" << std::endl
|
||||||
|
<< "It will be replaced by pulseaudio!" << std::endl;
|
||||||
|
m_api = "pulseaudio";
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SDL_AudioSpec spec;
|
SDL_AudioSpec spec;
|
||||||
|
|
||||||
SDLAudioMod()
|
SDLAudioMod()
|
||||||
: m_api("pulseaudio")
|
: m_api("pulseaudio")
|
||||||
, spec({SampleRate, format, channels, 0,
|
, spec({SampleRate, format, channels, 0,
|
||||||
desiredSamples, 0, 0, &callback_fillBuffer, nullptr})
|
desiredSamples, 0, 0, &callback_fillBuffer, nullptr})
|
||||||
{
|
{
|
||||||
// Number of samples must be a multiple of packet size.
|
// Number of samples must be a multiple of packet size.
|
||||||
assert(samples % SndOutPacketSize == 0);
|
assert(samples % SndOutPacketSize == 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
SDLAudioMod SDLAudioMod::mod;
|
SDLAudioMod SDLAudioMod::mod;
|
||||||
|
|
||||||
SndOutModule *const SDLOut = &SDLAudioMod::mod;
|
SndOutModule* const SDLOut = &SDLAudioMod::mod;
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
//Uncomment the next line to use the old time stretcher
|
//Uncomment the next line to use the old time stretcher
|
||||||
//#define SPU2X_USE_OLD_STRETCHER
|
//#define SPU2X_USE_OLD_STRETCHER
|
||||||
|
|
||||||
static soundtouch::SoundTouch *pSoundTouch = NULL;
|
static soundtouch::SoundTouch* pSoundTouch = NULL;
|
||||||
|
|
||||||
// data prediction amount, used to "commit" data that hasn't
|
// data prediction amount, used to "commit" data that hasn't
|
||||||
// finished timestretch processing.
|
// finished timestretch processing.
|
||||||
|
@ -36,7 +36,7 @@ float SndBuffer::eTempo = 1;
|
||||||
|
|
||||||
void SndBuffer::PredictDataWrite(int samples)
|
void SndBuffer::PredictDataWrite(int samples)
|
||||||
{
|
{
|
||||||
m_predictData += samples;
|
m_predictData += samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the buffer status percentage.
|
// Calculate the buffer status percentage.
|
||||||
|
@ -46,17 +46,17 @@ void SndBuffer::PredictDataWrite(int samples)
|
||||||
// -1.0 = buffer underflow!
|
// -1.0 = buffer underflow!
|
||||||
float SndBuffer::GetStatusPct()
|
float SndBuffer::GetStatusPct()
|
||||||
{
|
{
|
||||||
// Get the buffer status of the output driver too, so that we can
|
// Get the buffer status of the output driver too, so that we can
|
||||||
// obtain a more accurate overall buffer status.
|
// obtain a more accurate overall buffer status.
|
||||||
|
|
||||||
int drvempty = mods[OutputModule]->GetEmptySampleCount(); // / 2;
|
int drvempty = mods[OutputModule]->GetEmptySampleCount(); // / 2;
|
||||||
|
|
||||||
//ConLog( "Data %d >>> driver: %d predict: %d\n", m_data, drvempty, m_predictData );
|
//ConLog( "Data %d >>> driver: %d predict: %d\n", m_data, drvempty, m_predictData );
|
||||||
|
|
||||||
int data = _GetApproximateDataInBuffer();
|
int data = _GetApproximateDataInBuffer();
|
||||||
float result = (float)(data + m_predictData - drvempty) - (m_size / 16);
|
float result = (float)(data + m_predictData - drvempty) - (m_size / 16);
|
||||||
result /= (m_size / 16);
|
result /= (m_size / 16);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,266 +108,278 @@ int gRequestStretcherReset = STRETCHER_RESET_THRESHOLD;
|
||||||
//Adds a value to the running average buffer, and return the new running average.
|
//Adds a value to the running average buffer, and return the new running average.
|
||||||
float addToAvg(float val)
|
float addToAvg(float val)
|
||||||
{
|
{
|
||||||
static float avg_fullness[AVERAGING_BUFFER_SIZE];
|
static float avg_fullness[AVERAGING_BUFFER_SIZE];
|
||||||
static unsigned int nextAvgPos = 0;
|
static unsigned int nextAvgPos = 0;
|
||||||
static unsigned int available = 0; // Make sure we're not averaging AVERAGING_WINDOW items if we inserted less.
|
static unsigned int available = 0; // Make sure we're not averaging AVERAGING_WINDOW items if we inserted less.
|
||||||
if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD)
|
if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD)
|
||||||
available = 0;
|
available = 0;
|
||||||
|
|
||||||
if (available < AVERAGING_BUFFER_SIZE)
|
if (available < AVERAGING_BUFFER_SIZE)
|
||||||
available++;
|
available++;
|
||||||
|
|
||||||
avg_fullness[nextAvgPos] = val;
|
avg_fullness[nextAvgPos] = val;
|
||||||
nextAvgPos = (nextAvgPos + 1U) % AVERAGING_BUFFER_SIZE;
|
nextAvgPos = (nextAvgPos + 1U) % AVERAGING_BUFFER_SIZE;
|
||||||
|
|
||||||
unsigned int actualWindow = std::min(available, AVERAGING_WINDOW);
|
unsigned int actualWindow = std::min(available, AVERAGING_WINDOW);
|
||||||
unsigned int first = (nextAvgPos - actualWindow + AVERAGING_BUFFER_SIZE) % AVERAGING_BUFFER_SIZE;
|
unsigned int first = (nextAvgPos - actualWindow + AVERAGING_BUFFER_SIZE) % AVERAGING_BUFFER_SIZE;
|
||||||
|
|
||||||
// Possible optimization: if we know that actualWindow hasn't changed since
|
// 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)
|
// last invocation, we could calculate the running average in O(1) instead of O(N)
|
||||||
// by keeping a running sum between invocations, and then
|
// by keeping a running sum between invocations, and then
|
||||||
// do "runningSum = runningSum + val - avg_fullness[(first-1)%...]" instead of the following loop.
|
// do "runningSum = runningSum + val - avg_fullness[(first-1)%...]" instead of the following loop.
|
||||||
// Few gotchas: val overwrites first-1, handling actualWindow changes, etc.
|
// 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.
|
// However, this isn't hot code, so unless proven otherwise, we can live with unoptimized code.
|
||||||
float sum = 0;
|
float sum = 0;
|
||||||
for (unsigned int i = first; i < first + actualWindow; i++) {
|
for (unsigned int i = first; i < first + actualWindow; i++)
|
||||||
sum += avg_fullness[i % AVERAGING_BUFFER_SIZE];
|
{
|
||||||
}
|
sum += avg_fullness[i % AVERAGING_BUFFER_SIZE];
|
||||||
sum = sum / actualWindow;
|
}
|
||||||
|
sum = sum / actualWindow;
|
||||||
|
|
||||||
return sum ? sum : 1; // 1 because that's the 100% perfect speed value
|
return sum ? sum : 1; // 1 because that's the 100% perfect speed value
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
bool IsInRange(const T &val, const T &min, const T &max)
|
bool IsInRange(const T& val, const T& min, const T& max)
|
||||||
{
|
{
|
||||||
return (min <= val && val <= max);
|
return (min <= val && val <= max);
|
||||||
}
|
}
|
||||||
|
|
||||||
//actual stretch algorithm implementation
|
//actual stretch algorithm implementation
|
||||||
void SndBuffer::UpdateTempoChangeSoundTouch2()
|
void SndBuffer::UpdateTempoChangeSoundTouch2()
|
||||||
{
|
{
|
||||||
|
|
||||||
long targetSamplesReservoir = 48 * SndOutLatencyMS; //48000*SndOutLatencyMS/1000
|
long targetSamplesReservoir = 48 * SndOutLatencyMS; //48000*SndOutLatencyMS/1000
|
||||||
//base aim at buffer filled %
|
//base aim at buffer filled %
|
||||||
float baseTargetFullness = (double)targetSamplesReservoir; ///(double)m_size;//0.05;
|
float baseTargetFullness = (double)targetSamplesReservoir; ///(double)m_size;//0.05;
|
||||||
|
|
||||||
//state vars
|
//state vars
|
||||||
static bool inside_hysteresis; //=false;
|
static bool inside_hysteresis; //=false;
|
||||||
static int hys_ok_count; //=0;
|
static int hys_ok_count; //=0;
|
||||||
static float dynamicTargetFullness; //=baseTargetFullness;
|
static float dynamicTargetFullness; //=baseTargetFullness;
|
||||||
if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD) {
|
if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD)
|
||||||
ConLog("______> stretch: Reset.\n");
|
{
|
||||||
inside_hysteresis = false;
|
ConLog("______> stretch: Reset.\n");
|
||||||
hys_ok_count = 0;
|
inside_hysteresis = false;
|
||||||
dynamicTargetFullness = baseTargetFullness;
|
hys_ok_count = 0;
|
||||||
}
|
dynamicTargetFullness = baseTargetFullness;
|
||||||
|
}
|
||||||
|
|
||||||
int data = _GetApproximateDataInBuffer();
|
int data = _GetApproximateDataInBuffer();
|
||||||
float bufferFullness = (float)data; ///(float)m_size;
|
float bufferFullness = (float)data; ///(float)m_size;
|
||||||
|
|
||||||
#ifdef NEWSTRETCHER_USE_DYNAMIC_TUNING
|
#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%
|
{ //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 long iters = 0;
|
||||||
static wxDateTime last = wxDateTime::UNow();
|
static wxDateTime last = wxDateTime::UNow();
|
||||||
wxDateTime unow = wxDateTime::UNow();
|
wxDateTime unow = wxDateTime::UNow();
|
||||||
wxTimeSpan delta = unow.Subtract(last);
|
wxTimeSpan delta = unow.Subtract(last);
|
||||||
if (delta.GetMilliseconds() > 500) {
|
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))) {
|
int pot_targetIPS = 1000.0 / delta.GetMilliseconds().ToDouble() * iters;
|
||||||
if (MsgOverruns())
|
if (!IsInRange(pot_targetIPS, int((float)targetIPS / 1.3f), int((float)targetIPS * 1.3f)))
|
||||||
ConLog("Stretcher: setting iters/sec from %d to %d\n", targetIPS, pot_targetIPS);
|
{
|
||||||
targetIPS = pot_targetIPS;
|
if (MsgOverruns())
|
||||||
AVERAGING_WINDOW = GetClamped((int)(50.0f * (float)targetIPS / 750.0f), 3, (int)AVERAGING_BUFFER_SIZE);
|
ConLog("Stretcher: setting iters/sec from %d to %d\n", targetIPS, pot_targetIPS);
|
||||||
}
|
targetIPS = pot_targetIPS;
|
||||||
last = unow;
|
AVERAGING_WINDOW = GetClamped((int)(50.0f * (float)targetIPS / 750.0f), 3, (int)AVERAGING_BUFFER_SIZE);
|
||||||
iters = 0;
|
}
|
||||||
}
|
last = unow;
|
||||||
iters++;
|
iters = 0;
|
||||||
}
|
}
|
||||||
|
iters++;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//Algorithm params: (threshold params (hysteresis), etc)
|
//Algorithm params: (threshold params (hysteresis), etc)
|
||||||
const float hys_ok_factor = 1.04f;
|
const float hys_ok_factor = 1.04f;
|
||||||
const float hys_bad_factor = 1.2f;
|
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 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);
|
int compensationDivider = GetClamped((int)(100.0 * (float)targetIPS / 750), 15, 150);
|
||||||
|
|
||||||
float tempoAdjust = bufferFullness / dynamicTargetFullness;
|
float tempoAdjust = bufferFullness / dynamicTargetFullness;
|
||||||
float avgerage = addToAvg(tempoAdjust);
|
float avgerage = addToAvg(tempoAdjust);
|
||||||
tempoAdjust = avgerage;
|
tempoAdjust = avgerage;
|
||||||
|
|
||||||
// Dampen the adjustment to avoid overshoots (this means the average will compensate to the other side).
|
// 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",
|
// 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.
|
// 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.
|
// The dampening (sqrt was chosen for no very good reason) manages to mostly prevent that.
|
||||||
tempoAdjust = sqrt(tempoAdjust);
|
tempoAdjust = sqrt(tempoAdjust);
|
||||||
|
|
||||||
tempoAdjust = GetClamped(tempoAdjust, 0.05f, 10.0f);
|
tempoAdjust = GetClamped(tempoAdjust, 0.05f, 10.0f);
|
||||||
|
|
||||||
if (tempoAdjust < 1)
|
if (tempoAdjust < 1)
|
||||||
baseTargetFullness /= sqrt(tempoAdjust); // slightly increase latency when running slow.
|
baseTargetFullness /= sqrt(tempoAdjust); // slightly increase latency when running slow.
|
||||||
|
|
||||||
dynamicTargetFullness += (baseTargetFullness / tempoAdjust - dynamicTargetFullness) / (double)compensationDivider;
|
dynamicTargetFullness += (baseTargetFullness / tempoAdjust - dynamicTargetFullness) / (double)compensationDivider;
|
||||||
if (IsInRange(tempoAdjust, 0.9f, 1.1f) && IsInRange(dynamicTargetFullness, baseTargetFullness * 0.9f, baseTargetFullness * 1.1f))
|
if (IsInRange(tempoAdjust, 0.9f, 1.1f) && IsInRange(dynamicTargetFullness, baseTargetFullness * 0.9f, baseTargetFullness * 1.1f))
|
||||||
dynamicTargetFullness = baseTargetFullness;
|
dynamicTargetFullness = baseTargetFullness;
|
||||||
|
|
||||||
if (!inside_hysteresis) {
|
if (!inside_hysteresis)
|
||||||
if (IsInRange(tempoAdjust, 1.0f / hys_ok_factor, hys_ok_factor))
|
{
|
||||||
hys_ok_count++;
|
if (IsInRange(tempoAdjust, 1.0f / hys_ok_factor, hys_ok_factor))
|
||||||
else
|
hys_ok_count++;
|
||||||
hys_ok_count = 0;
|
else
|
||||||
|
hys_ok_count = 0;
|
||||||
|
|
||||||
if (hys_ok_count >= hys_min_ok_count) {
|
if (hys_ok_count >= hys_min_ok_count)
|
||||||
inside_hysteresis = true;
|
{
|
||||||
if (MsgOverruns())
|
inside_hysteresis = true;
|
||||||
ConLog("======> stretch: None (1:1)\n");
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (!IsInRange(tempoAdjust, 1.0f / hys_bad_factor, hys_bad_factor)) {
|
if (inside_hysteresis)
|
||||||
if (MsgOverruns())
|
tempoAdjust = 1.0;
|
||||||
ConLog("~~~~~~> stretch: Dynamic\n");
|
|
||||||
inside_hysteresis = false;
|
|
||||||
hys_ok_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inside_hysteresis)
|
if (MsgOverruns())
|
||||||
tempoAdjust = 1.0;
|
{
|
||||||
|
static int iters = 0;
|
||||||
|
static wxDateTime last = wxDateTime::UNow();
|
||||||
|
wxDateTime unow = wxDateTime::UNow();
|
||||||
|
wxTimeSpan delta = unow.Subtract(last);
|
||||||
|
|
||||||
if (MsgOverruns()) {
|
if (delta.GetMilliseconds() > 1000)
|
||||||
static int iters = 0;
|
{ //report buffers state and tempo adjust every second
|
||||||
static wxDateTime last = wxDateTime::UNow();
|
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",
|
||||||
wxDateTime unow = wxDateTime::UNow();
|
(int)(data / 48), (double)(100.0 * bufferFullness / baseTargetFullness), (double)tempoAdjust, (double)(dynamicTargetFullness / baseTargetFullness), iters, (int)targetIPS, AVERAGING_WINDOW, hys_min_ok_count, compensationDivider, gRequestStretcherReset);
|
||||||
wxTimeSpan delta = unow.Subtract(last);
|
last = unow;
|
||||||
|
iters = 0;
|
||||||
|
}
|
||||||
|
iters++;
|
||||||
|
}
|
||||||
|
|
||||||
if (delta.GetMilliseconds() > 1000) { //report buffers state and tempo adjust every second
|
pSoundTouch->setTempo(tempoAdjust);
|
||||||
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",
|
if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD)
|
||||||
(int)(data / 48), (double)(100.0 * bufferFullness / baseTargetFullness), (double)tempoAdjust, (double)(dynamicTargetFullness / baseTargetFullness), iters, (int)targetIPS, AVERAGING_WINDOW, hys_min_ok_count, compensationDivider, gRequestStretcherReset);
|
gRequestStretcherReset = 0;
|
||||||
last = unow;
|
|
||||||
iters = 0;
|
|
||||||
}
|
|
||||||
iters++;
|
|
||||||
}
|
|
||||||
|
|
||||||
pSoundTouch->setTempo(tempoAdjust);
|
return;
|
||||||
if (gRequestStretcherReset >= STRETCHER_RESET_THRESHOLD)
|
|
||||||
gRequestStretcherReset = 0;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SndBuffer::UpdateTempoChangeSoundTouch()
|
void SndBuffer::UpdateTempoChangeSoundTouch()
|
||||||
{
|
{
|
||||||
float statusPct = GetStatusPct();
|
float statusPct = GetStatusPct();
|
||||||
float pctChange = statusPct - lastPct;
|
float pctChange = statusPct - lastPct;
|
||||||
|
|
||||||
float tempoChange;
|
float tempoChange;
|
||||||
float emergencyAdj = 0;
|
float emergencyAdj = 0;
|
||||||
float newcee = cTempo; // workspace var. for cTempo
|
float newcee = cTempo; // workspace var. for cTempo
|
||||||
|
|
||||||
// IMPORTANT!
|
// IMPORTANT!
|
||||||
// If you plan to tweak these values, make sure you're using a release build
|
// 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
|
// 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
|
// erratic behavior in the audio buffers, and makes the timestretcher seem a
|
||||||
// lot more inconsistent than it really is.
|
// lot more inconsistent than it really is.
|
||||||
|
|
||||||
// We have two factors.
|
// We have two factors.
|
||||||
// * Distance from nominal buffer status (50% full)
|
// * Distance from nominal buffer status (50% full)
|
||||||
// * The change from previous update to this update.
|
// * The change from previous update to this update.
|
||||||
|
|
||||||
// Prediction based on the buffer change:
|
// Prediction based on the buffer change:
|
||||||
// (linear seems to work better here)
|
// (linear seems to work better here)
|
||||||
|
|
||||||
tempoChange = pctChange * 0.75f;
|
tempoChange = pctChange * 0.75f;
|
||||||
|
|
||||||
if (statusPct * tempoChange < 0.0f) {
|
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,
|
// only apply tempo change if it is in synch with the buffer status.
|
||||||
// ignore it. It'll just muck things up.
|
// In other words, if the buffer is high (over 0%), and is decreasing,
|
||||||
|
// ignore it. It'll just muck things up.
|
||||||
|
|
||||||
tempoChange = 0;
|
tempoChange = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sudden spikes in framerate can cause the nominal buffer status
|
// Sudden spikes in framerate can cause the nominal buffer status
|
||||||
// to go critical, in which case we have to enact an emergency
|
// to go critical, in which case we have to enact an emergency
|
||||||
// stretch. The following cubic formulas do that. Values near
|
// stretch. The following cubic formulas do that. Values near
|
||||||
// the extremeites give much larger results than those near 0.
|
// the extremeites give much larger results than those near 0.
|
||||||
// And the value is added only this time, and does not accumulate.
|
// And the value is added only this time, and does not accumulate.
|
||||||
// (otherwise a large value like this would cause problems down the road)
|
// (otherwise a large value like this would cause problems down the road)
|
||||||
|
|
||||||
// Constants:
|
// Constants:
|
||||||
// Weight - weights the statusPct's "emergency" consideration.
|
// Weight - weights the statusPct's "emergency" consideration.
|
||||||
// higher values here will make the buffer perform more drastic
|
// higher values here will make the buffer perform more drastic
|
||||||
// compensations at the outer edges of the buffer (at -75 or +75%
|
// compensations at the outer edges of the buffer (at -75 or +75%
|
||||||
// or beyond, for example).
|
// or beyond, for example).
|
||||||
|
|
||||||
// Range - scales the adjustment to the given range (more or less).
|
// Range - scales the adjustment to the given range (more or less).
|
||||||
// The actual range is dependent on the weight used, so if you increase
|
// The actual range is dependent on the weight used, so if you increase
|
||||||
// Weight you'll usually want to decrease Range somewhat to compensate.
|
// Weight you'll usually want to decrease Range somewhat to compensate.
|
||||||
|
|
||||||
// Prediction based on the buffer fill status:
|
// Prediction based on the buffer fill status:
|
||||||
|
|
||||||
const float statusWeight = 2.99f;
|
const float statusWeight = 2.99f;
|
||||||
const float statusRange = 0.068f;
|
const float statusRange = 0.068f;
|
||||||
|
|
||||||
// "non-emergency" deadzone: In this area stretching will be strongly discouraged.
|
// "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
|
// 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
|
// 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.
|
// little, the low-end portions of this check are less forgiving than the high-sides.
|
||||||
|
|
||||||
if (cTempo < 0.965f || cTempo > 1.060f ||
|
if (cTempo < 0.965f || cTempo > 1.060f ||
|
||||||
pctChange < -0.38f || pctChange > 0.54f ||
|
pctChange < -0.38f || pctChange > 0.54f ||
|
||||||
statusPct < -0.42f || statusPct > 0.70f ||
|
statusPct < -0.42f || statusPct > 0.70f ||
|
||||||
eTempo < 0.89f || eTempo > 1.19f) {
|
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);
|
//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.
|
// 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
|
// It helps make the system 'feel' a little smarter by giving it at least
|
||||||
// one packet worth of history to help work off of:
|
// one packet worth of history to help work off of:
|
||||||
|
|
||||||
emergencyAdj = (emergencyAdj * 0.75f) + (lastEmergencyAdj * 0.25f);
|
emergencyAdj = (emergencyAdj * 0.75f) + (lastEmergencyAdj * 0.25f);
|
||||||
|
|
||||||
lastEmergencyAdj = emergencyAdj;
|
lastEmergencyAdj = emergencyAdj;
|
||||||
lastPct = statusPct;
|
lastPct = statusPct;
|
||||||
|
|
||||||
// Accumulate a fraction of the tempo change into the tempo itself.
|
// Accumulate a fraction of the tempo change into the tempo itself.
|
||||||
// This helps the system run "smarter" to games that run consistently
|
// This helps the system run "smarter" to games that run consistently
|
||||||
// fast or slow by altering the base tempo to something closer to the
|
// 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
|
// game's active speed. In tests most games normalize within 2 seconds
|
||||||
// at 100ms latency, which is pretty good (larger buffers normalize even
|
// at 100ms latency, which is pretty good (larger buffers normalize even
|
||||||
// quicker).
|
// quicker).
|
||||||
|
|
||||||
newcee += newcee * (tempoChange + emergencyAdj) * 0.03f;
|
newcee += newcee * (tempoChange + emergencyAdj) * 0.03f;
|
||||||
|
|
||||||
// Apply tempoChange as a scale of cTempo. That way the effect is proportional
|
// 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
|
// to the current tempo. (otherwise tempos rate of change at the extremes would
|
||||||
// be too drastic)
|
// be too drastic)
|
||||||
|
|
||||||
float newTempo = newcee + (emergencyAdj * cTempo);
|
float newTempo = newcee + (emergencyAdj * cTempo);
|
||||||
|
|
||||||
// ... and as a final optimization, only stretch if the new tempo is outside
|
// ... and as a final optimization, only stretch if the new tempo is outside
|
||||||
// a nominal threshold. Keep this threshold check small, because it could
|
// a nominal threshold. Keep this threshold check small, because it could
|
||||||
// cause some serious side effects otherwise. (enlarging the cTempo check above
|
// cause some serious side effects otherwise. (enlarging the cTempo check above
|
||||||
// is usually better/safer)
|
// is usually better/safer)
|
||||||
if (newTempo < 0.970f || newTempo > 1.045f) {
|
if (newTempo < 0.970f || newTempo > 1.045f)
|
||||||
cTempo = (float)newcee;
|
{
|
||||||
|
cTempo = (float)newcee;
|
||||||
|
|
||||||
if (newTempo < 0.10f)
|
if (newTempo < 0.10f)
|
||||||
newTempo = 0.10f;
|
newTempo = 0.10f;
|
||||||
else if (newTempo > 10.0f)
|
else if (newTempo > 10.0f)
|
||||||
newTempo = 10.0f;
|
newTempo = 10.0f;
|
||||||
|
|
||||||
if (cTempo < 0.15f)
|
if (cTempo < 0.15f)
|
||||||
cTempo = 0.15f;
|
cTempo = 0.15f;
|
||||||
else if (cTempo > 7.5f)
|
else if (cTempo > 7.5f)
|
||||||
cTempo = 7.5f;
|
cTempo = 7.5f;
|
||||||
|
|
||||||
pSoundTouch->setTempo(eTempo = (float)newTempo);
|
pSoundTouch->setTempo(eTempo = (float)newTempo);
|
||||||
|
|
||||||
/*ConLog("* SPU2-X: [Nominal %d%%] [Emergency: %d%%] (baseTempo: %d%% ) (newTempo: %d%%) (buffer: %d%%)\n",
|
/*ConLog("* SPU2-X: [Nominal %d%%] [Emergency: %d%%] (baseTempo: %d%% ) (newTempo: %d%%) (buffer: %d%%)\n",
|
||||||
//(relation < 0.0) ? "Normalize" : "",
|
//(relation < 0.0) ? "Normalize" : "",
|
||||||
(int)(tempoChange * 100.0 * 0.03),
|
(int)(tempoChange * 100.0 * 0.03),
|
||||||
(int)(emergencyAdj * 100.0),
|
(int)(emergencyAdj * 100.0),
|
||||||
|
@ -375,164 +387,174 @@ void SndBuffer::UpdateTempoChangeSoundTouch()
|
||||||
(int)(newTempo * 100.0),
|
(int)(newTempo * 100.0),
|
||||||
(int)(statusPct * 100.0)
|
(int)(statusPct * 100.0)
|
||||||
);*/
|
);*/
|
||||||
} else {
|
}
|
||||||
// Nominal operation -- turn off stretching.
|
else
|
||||||
// note: eTempo 'slides' toward 1.0 for smoother audio and better
|
{
|
||||||
// protection against spikes.
|
// Nominal operation -- turn off stretching.
|
||||||
if (cTempo != 1.0f) {
|
// note: eTempo 'slides' toward 1.0 for smoother audio and better
|
||||||
cTempo = 1.0f;
|
// protection against spikes.
|
||||||
eTempo = (1.0f + eTempo) * 0.5f;
|
if (cTempo != 1.0f)
|
||||||
pSoundTouch->setTempo(eTempo);
|
{
|
||||||
} else {
|
cTempo = 1.0f;
|
||||||
if (eTempo != cTempo)
|
eTempo = (1.0f + eTempo) * 0.5f;
|
||||||
pSoundTouch->setTempo(eTempo = cTempo);
|
pSoundTouch->setTempo(eTempo);
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
if (eTempo != cTempo)
|
||||||
|
pSoundTouch->setTempo(eTempo = cTempo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern uint TickInterval;
|
extern uint TickInterval;
|
||||||
void SndBuffer::UpdateTempoChangeAsyncMixing()
|
void SndBuffer::UpdateTempoChangeAsyncMixing()
|
||||||
{
|
{
|
||||||
float statusPct = GetStatusPct();
|
float statusPct = GetStatusPct();
|
||||||
|
|
||||||
lastPct = statusPct;
|
lastPct = statusPct;
|
||||||
if (statusPct < -0.1f) {
|
if (statusPct < -0.1f)
|
||||||
TickInterval -= 4;
|
{
|
||||||
if (statusPct < -0.3f)
|
TickInterval -= 4;
|
||||||
TickInterval = 64;
|
if (statusPct < -0.3f)
|
||||||
if (TickInterval < 64)
|
TickInterval = 64;
|
||||||
TickInterval = 64;
|
if (TickInterval < 64)
|
||||||
//printf("-- %d, %f\n",TickInterval,statusPct);
|
TickInterval = 64;
|
||||||
} else if (statusPct > 0.2f) {
|
//printf("-- %d, %f\n",TickInterval,statusPct);
|
||||||
TickInterval += 1;
|
}
|
||||||
if (TickInterval >= 7000)
|
else if (statusPct > 0.2f)
|
||||||
TickInterval = 7000;
|
{
|
||||||
//printf("++ %d, %f\n",TickInterval,statusPct);
|
TickInterval += 1;
|
||||||
} else
|
if (TickInterval >= 7000)
|
||||||
TickInterval = 768;
|
TickInterval = 7000;
|
||||||
|
//printf("++ %d, %f\n",TickInterval,statusPct);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
TickInterval = 768;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndBuffer::timeStretchUnderrun()
|
void SndBuffer::timeStretchUnderrun()
|
||||||
{
|
{
|
||||||
gRequestStretcherReset++;
|
gRequestStretcherReset++;
|
||||||
// timeStretcher failed it's job. We need to slow down the audio some.
|
// timeStretcher failed it's job. We need to slow down the audio some.
|
||||||
|
|
||||||
cTempo -= (cTempo * 0.12f);
|
cTempo -= (cTempo * 0.12f);
|
||||||
eTempo -= (eTempo * 0.30f);
|
eTempo -= (eTempo * 0.30f);
|
||||||
if (eTempo < 0.1f)
|
if (eTempo < 0.1f)
|
||||||
eTempo = 0.1f;
|
eTempo = 0.1f;
|
||||||
// pSoundTouch->setTempo( eTempo );
|
// pSoundTouch->setTempo( eTempo );
|
||||||
//pSoundTouch->setTempoChange(-30); // temporary (until stretcher is called) slow down
|
//pSoundTouch->setTempoChange(-30); // temporary (until stretcher is called) slow down
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 SndBuffer::timeStretchOverrun()
|
s32 SndBuffer::timeStretchOverrun()
|
||||||
{
|
{
|
||||||
// If we overran it means the timestretcher failed. We need to speed
|
// If we overran it means the timestretcher failed. We need to speed
|
||||||
// up audio playback.
|
// up audio playback.
|
||||||
cTempo += cTempo * 0.12f;
|
cTempo += cTempo * 0.12f;
|
||||||
eTempo += eTempo * 0.40f;
|
eTempo += eTempo * 0.40f;
|
||||||
if (eTempo > 7.5f)
|
if (eTempo > 7.5f)
|
||||||
eTempo = 7.5f;
|
eTempo = 7.5f;
|
||||||
//pSoundTouch->setTempo( eTempo );
|
//pSoundTouch->setTempo( eTempo );
|
||||||
//pSoundTouch->setTempoChange(30);// temporary (until stretcher is called) speed up
|
//pSoundTouch->setTempoChange(30);// temporary (until stretcher is called) speed up
|
||||||
|
|
||||||
// Throw out just a little bit (two packets worth) to help
|
// Throw out just a little bit (two packets worth) to help
|
||||||
// give the TS some room to work:
|
// give the TS some room to work:
|
||||||
gRequestStretcherReset++;
|
gRequestStretcherReset++;
|
||||||
return SndOutPacketSize * 2;
|
return SndOutPacketSize * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CvtPacketToFloat(StereoOut32 *srcdest)
|
static void CvtPacketToFloat(StereoOut32* srcdest)
|
||||||
{
|
{
|
||||||
StereoOutFloat *dest = (StereoOutFloat *)srcdest;
|
StereoOutFloat* dest = (StereoOutFloat*)srcdest;
|
||||||
const StereoOut32 *src = (StereoOut32 *)srcdest;
|
const StereoOut32* src = (StereoOut32*)srcdest;
|
||||||
for (uint i = 0; i < SndOutPacketSize; ++i, ++dest, ++src)
|
for (uint i = 0; i < SndOutPacketSize; ++i, ++dest, ++src)
|
||||||
*dest = (StereoOutFloat)*src;
|
*dest = (StereoOutFloat)*src;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parameter note: Size should always be a multiple of 128, thanks!
|
// Parameter note: Size should always be a multiple of 128, thanks!
|
||||||
static void CvtPacketToInt(StereoOut32 *srcdest, uint size)
|
static void CvtPacketToInt(StereoOut32* srcdest, uint size)
|
||||||
{
|
{
|
||||||
//pxAssume( (size & 127) == 0 );
|
//pxAssume( (size & 127) == 0 );
|
||||||
|
|
||||||
const StereoOutFloat *src = (StereoOutFloat *)srcdest;
|
const StereoOutFloat* src = (StereoOutFloat*)srcdest;
|
||||||
StereoOut32 *dest = srcdest;
|
StereoOut32* dest = srcdest;
|
||||||
|
|
||||||
for (uint i = 0; i < size; ++i, ++dest, ++src)
|
for (uint i = 0; i < size; ++i, ++dest, ++src)
|
||||||
*dest = (StereoOut32)*src;
|
*dest = (StereoOut32)*src;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndBuffer::timeStretchWrite()
|
void SndBuffer::timeStretchWrite()
|
||||||
{
|
{
|
||||||
// data prediction helps keep the tempo adjustments more accurate.
|
// data prediction helps keep the tempo adjustments more accurate.
|
||||||
// The timestretcher returns packets in belated "clump" form.
|
// The timestretcher returns packets in belated "clump" form.
|
||||||
// Meaning that most of the time we'll get nothing back, and then
|
// 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
|
// suddenly we'll get several chunks back at once. Thus we use
|
||||||
// data prediction to make the timestretcher more responsive.
|
// data prediction to make the timestretcher more responsive.
|
||||||
|
|
||||||
PredictDataWrite((int)(SndOutPacketSize / eTempo));
|
PredictDataWrite((int)(SndOutPacketSize / eTempo));
|
||||||
CvtPacketToFloat(sndTempBuffer);
|
CvtPacketToFloat(sndTempBuffer);
|
||||||
|
|
||||||
pSoundTouch->putSamples((float *)sndTempBuffer, SndOutPacketSize);
|
pSoundTouch->putSamples((float*)sndTempBuffer, SndOutPacketSize);
|
||||||
|
|
||||||
int tempProgress;
|
int tempProgress;
|
||||||
while (tempProgress = pSoundTouch->receiveSamples((float *)sndTempBuffer, SndOutPacketSize),
|
while (tempProgress = pSoundTouch->receiveSamples((float*)sndTempBuffer, SndOutPacketSize),
|
||||||
tempProgress != 0) {
|
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.
|
// 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);
|
CvtPacketToInt(sndTempBuffer, tempProgress);
|
||||||
_WriteSamples(sndTempBuffer, tempProgress);
|
_WriteSamples(sndTempBuffer, tempProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SPU2X_USE_OLD_STRETCHER
|
#ifdef SPU2X_USE_OLD_STRETCHER
|
||||||
UpdateTempoChangeSoundTouch();
|
UpdateTempoChangeSoundTouch();
|
||||||
#else
|
#else
|
||||||
UpdateTempoChangeSoundTouch2();
|
UpdateTempoChangeSoundTouch2();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndBuffer::soundtouchInit()
|
void SndBuffer::soundtouchInit()
|
||||||
{
|
{
|
||||||
pSoundTouch = new soundtouch::SoundTouch();
|
pSoundTouch = new soundtouch::SoundTouch();
|
||||||
pSoundTouch->setSampleRate(SampleRate);
|
pSoundTouch->setSampleRate(SampleRate);
|
||||||
pSoundTouch->setChannels(2);
|
pSoundTouch->setChannels(2);
|
||||||
|
|
||||||
pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, 0);
|
pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, 0);
|
||||||
pSoundTouch->setSetting(SETTING_USE_AA_FILTER, 0);
|
pSoundTouch->setSetting(SETTING_USE_AA_FILTER, 0);
|
||||||
|
|
||||||
SoundtouchCfg::ApplySettings(*pSoundTouch);
|
SoundtouchCfg::ApplySettings(*pSoundTouch);
|
||||||
|
|
||||||
pSoundTouch->setTempo(1);
|
pSoundTouch->setTempo(1);
|
||||||
|
|
||||||
// some timestretch management vars:
|
// some timestretch management vars:
|
||||||
|
|
||||||
cTempo = 1.0;
|
cTempo = 1.0;
|
||||||
eTempo = 1.0;
|
eTempo = 1.0;
|
||||||
lastPct = 0;
|
lastPct = 0;
|
||||||
lastEmergencyAdj = 0;
|
lastEmergencyAdj = 0;
|
||||||
|
|
||||||
m_predictData = 0;
|
m_predictData = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset timestretch management vars, and delay updates a bit:
|
// reset timestretch management vars, and delay updates a bit:
|
||||||
void SndBuffer::soundtouchClearContents()
|
void SndBuffer::soundtouchClearContents()
|
||||||
{
|
{
|
||||||
if (pSoundTouch == NULL)
|
if (pSoundTouch == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pSoundTouch->clear();
|
pSoundTouch->clear();
|
||||||
pSoundTouch->setTempo(1);
|
pSoundTouch->setTempo(1);
|
||||||
|
|
||||||
cTempo = 1.0;
|
cTempo = 1.0;
|
||||||
eTempo = 1.0;
|
eTempo = 1.0;
|
||||||
lastPct = 0;
|
lastPct = 0;
|
||||||
lastEmergencyAdj = 0;
|
lastEmergencyAdj = 0;
|
||||||
|
|
||||||
m_predictData = 0;
|
m_predictData = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SndBuffer::soundtouchCleanup()
|
void SndBuffer::soundtouchCleanup()
|
||||||
{
|
{
|
||||||
safe_delete(pSoundTouch);
|
safe_delete(pSoundTouch);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,109 +37,113 @@ static const char dataStr[] = "data";
|
||||||
// Class WavOutFile
|
// Class WavOutFile
|
||||||
//
|
//
|
||||||
|
|
||||||
WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int channels)
|
WavOutFile::WavOutFile(const char* fileName, int sampleRate, int bits, int channels)
|
||||||
{
|
{
|
||||||
bytesWritten = 0;
|
bytesWritten = 0;
|
||||||
fptr = fopen(fileName, "wb");
|
fptr = fopen(fileName, "wb");
|
||||||
if (fptr == NULL) {
|
if (fptr == NULL)
|
||||||
string msg = "Error : Unable to open file \"";
|
{
|
||||||
msg += fileName;
|
string msg = "Error : Unable to open file \"";
|
||||||
msg += "\" for writing.";
|
msg += fileName;
|
||||||
//pmsg = msg.c_str;
|
msg += "\" for writing.";
|
||||||
throw runtime_error(msg);
|
//pmsg = msg.c_str;
|
||||||
}
|
throw runtime_error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
fillInHeader(sampleRate, bits, channels);
|
fillInHeader(sampleRate, bits, channels);
|
||||||
writeHeader();
|
writeHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
WavOutFile::~WavOutFile()
|
WavOutFile::~WavOutFile()
|
||||||
{
|
{
|
||||||
if (fptr) {
|
if (fptr)
|
||||||
finishHeader();
|
{
|
||||||
fclose(fptr);
|
finishHeader();
|
||||||
}
|
fclose(fptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels)
|
void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels)
|
||||||
{
|
{
|
||||||
// fill in the 'riff' part..
|
// fill in the 'riff' part..
|
||||||
|
|
||||||
// copy string 'RIFF' to riff_char
|
// copy string 'RIFF' to riff_char
|
||||||
memcpy(&(header.riff.riff_char), riffStr, 4);
|
memcpy(&(header.riff.riff_char), riffStr, 4);
|
||||||
// package_len unknown so far
|
// package_len unknown so far
|
||||||
header.riff.package_len = 0;
|
header.riff.package_len = 0;
|
||||||
// copy string 'WAVE' to wave
|
// copy string 'WAVE' to wave
|
||||||
memcpy(&(header.riff.wave), waveStr, 4);
|
memcpy(&(header.riff.wave), waveStr, 4);
|
||||||
|
|
||||||
|
|
||||||
// fill in the 'format' part..
|
// fill in the 'format' part..
|
||||||
|
|
||||||
// copy string 'fmt ' to fmt
|
// copy string 'fmt ' to fmt
|
||||||
memcpy(&(header.format.fmt), fmtStr, 4);
|
memcpy(&(header.format.fmt), fmtStr, 4);
|
||||||
|
|
||||||
header.format.format_len = 0x10;
|
header.format.format_len = 0x10;
|
||||||
header.format.fixed = 1;
|
header.format.fixed = 1;
|
||||||
header.format.channel_number = (short)channels;
|
header.format.channel_number = (short)channels;
|
||||||
header.format.sample_rate = (int)sampleRate;
|
header.format.sample_rate = (int)sampleRate;
|
||||||
header.format.bits_per_sample = (short)bits;
|
header.format.bits_per_sample = (short)bits;
|
||||||
header.format.byte_per_sample = (short)(bits * channels / 8);
|
header.format.byte_per_sample = (short)(bits * channels / 8);
|
||||||
header.format.byte_rate = header.format.byte_per_sample * (int)sampleRate;
|
header.format.byte_rate = header.format.byte_per_sample * (int)sampleRate;
|
||||||
header.format.sample_rate = (int)sampleRate;
|
header.format.sample_rate = (int)sampleRate;
|
||||||
|
|
||||||
// fill in the 'data' part..
|
// fill in the 'data' part..
|
||||||
|
|
||||||
// copy string 'data' to data_field
|
// copy string 'data' to data_field
|
||||||
memcpy(&(header.data.data_field), dataStr, 4);
|
memcpy(&(header.data.data_field), dataStr, 4);
|
||||||
// data_len unknown so far
|
// data_len unknown so far
|
||||||
header.data.data_len = 0;
|
header.data.data_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WavOutFile::finishHeader()
|
void WavOutFile::finishHeader()
|
||||||
{
|
{
|
||||||
// supplement the file length into the header structure
|
// supplement the file length into the header structure
|
||||||
header.riff.package_len = bytesWritten + 36;
|
header.riff.package_len = bytesWritten + 36;
|
||||||
header.data.data_len = bytesWritten;
|
header.data.data_len = bytesWritten;
|
||||||
|
|
||||||
writeHeader();
|
writeHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void WavOutFile::writeHeader()
|
void WavOutFile::writeHeader()
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
// write the supplemented header in the beginning of the file
|
// write the supplemented header in the beginning of the file
|
||||||
fseek(fptr, 0, SEEK_SET);
|
fseek(fptr, 0, SEEK_SET);
|
||||||
res = fwrite(&header, sizeof(header), 1, fptr);
|
res = fwrite(&header, sizeof(header), 1, fptr);
|
||||||
if (res != 1) {
|
if (res != 1)
|
||||||
throw runtime_error("Error while writing to a wav file.");
|
{
|
||||||
}
|
throw runtime_error("Error while writing to a wav file.");
|
||||||
|
}
|
||||||
|
|
||||||
// jump back to the end of the file
|
// jump back to the end of the file
|
||||||
fseek(fptr, 0, SEEK_END);
|
fseek(fptr, 0, SEEK_END);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WavOutFile::write(const short *buffer, int numElems)
|
void WavOutFile::write(const short* buffer, int numElems)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
// 16bit format & 16 bit samples
|
// 16bit format & 16 bit samples
|
||||||
|
|
||||||
assert(header.format.bits_per_sample == 16);
|
assert(header.format.bits_per_sample == 16);
|
||||||
if (numElems < 1)
|
if (numElems < 1)
|
||||||
return; // nothing to do
|
return; // nothing to do
|
||||||
|
|
||||||
res = fwrite(buffer, 2, numElems, fptr);
|
res = fwrite(buffer, 2, numElems, fptr);
|
||||||
|
|
||||||
if (res != numElems) {
|
if (res != numElems)
|
||||||
throw runtime_error("Error while writing to a wav file.");
|
{
|
||||||
}
|
throw runtime_error("Error while writing to a wav file.");
|
||||||
bytesWritten += 2 * numElems;
|
}
|
||||||
|
bytesWritten += 2 * numElems;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,38 +29,38 @@ typedef unsigned int uint;
|
||||||
/// WAV audio file 'riff' section header
|
/// WAV audio file 'riff' section header
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char riff_char[4];
|
char riff_char[4];
|
||||||
int package_len;
|
int package_len;
|
||||||
char wave[4];
|
char wave[4];
|
||||||
} WavRiff;
|
} WavRiff;
|
||||||
|
|
||||||
/// WAV audio file 'format' section header
|
/// WAV audio file 'format' section header
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char fmt[4];
|
char fmt[4];
|
||||||
int format_len;
|
int format_len;
|
||||||
short fixed;
|
short fixed;
|
||||||
short channel_number;
|
short channel_number;
|
||||||
int sample_rate;
|
int sample_rate;
|
||||||
int byte_rate;
|
int byte_rate;
|
||||||
short byte_per_sample;
|
short byte_per_sample;
|
||||||
short bits_per_sample;
|
short bits_per_sample;
|
||||||
} WavFormat;
|
} WavFormat;
|
||||||
|
|
||||||
/// WAV audio file 'data' section header
|
/// WAV audio file 'data' section header
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char data_field[4];
|
char data_field[4];
|
||||||
uint data_len;
|
uint data_len;
|
||||||
} WavData;
|
} WavData;
|
||||||
|
|
||||||
|
|
||||||
/// WAV audio file header
|
/// WAV audio file header
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
WavRiff riff;
|
WavRiff riff;
|
||||||
WavFormat format;
|
WavFormat format;
|
||||||
WavData data;
|
WavData data;
|
||||||
} WavHeader;
|
} WavHeader;
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,42 +68,42 @@ typedef struct
|
||||||
class WavOutFile
|
class WavOutFile
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
/// Pointer to the WAV file
|
/// Pointer to the WAV file
|
||||||
FILE *fptr;
|
FILE* fptr;
|
||||||
|
|
||||||
/// WAV file header data.
|
/// WAV file header data.
|
||||||
WavHeader header;
|
WavHeader header;
|
||||||
|
|
||||||
/// Counter of how many bytes have been written to the file so far.
|
/// Counter of how many bytes have been written to the file so far.
|
||||||
int bytesWritten;
|
int bytesWritten;
|
||||||
|
|
||||||
/// Fills in WAV file header information.
|
/// Fills in WAV file header information.
|
||||||
void fillInHeader(const uint sampleRate, const uint bits, const uint channels);
|
void fillInHeader(const uint sampleRate, const uint bits, const uint channels);
|
||||||
|
|
||||||
/// Finishes the WAV file header by supplementing information of amount of
|
/// Finishes the WAV file header by supplementing information of amount of
|
||||||
/// data written to file etc
|
/// data written to file etc
|
||||||
void finishHeader();
|
void finishHeader();
|
||||||
|
|
||||||
/// Writes the WAV file header.
|
/// Writes the WAV file header.
|
||||||
void writeHeader();
|
void writeHeader();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception
|
/// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception
|
||||||
/// if file creation fails.
|
/// if file creation fails.
|
||||||
WavOutFile(const char *fileName, ///< Filename
|
WavOutFile(const char* fileName, ///< Filename
|
||||||
int sampleRate, ///< Sample rate (e.g. 44100 etc)
|
int sampleRate, ///< Sample rate (e.g. 44100 etc)
|
||||||
int bits, ///< Bits per sample (8 or 16 bits)
|
int bits, ///< Bits per sample (8 or 16 bits)
|
||||||
int channels ///< Number of channels (1=mono, 2=stereo)
|
int channels ///< Number of channels (1=mono, 2=stereo)
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Destructor: Finalizes & closes the WAV file.
|
/// Destructor: Finalizes & closes the WAV file.
|
||||||
~WavOutFile();
|
~WavOutFile();
|
||||||
|
|
||||||
/// Write data to WAV file. Throws a 'runtime_error' exception if writing to
|
/// Write data to WAV file. Throws a 'runtime_error' exception if writing to
|
||||||
/// file fails.
|
/// file fails.
|
||||||
void write(const short *buffer, ///< Pointer to sample data buffer.
|
void write(const short* buffer, ///< Pointer to sample data buffer.
|
||||||
int numElems ///< How many array items are to be written to file.
|
int numElems ///< How many array items are to be written to file.
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,78 +20,85 @@
|
||||||
#include "soundtouch/source/SoundStretch/WavFile.h"
|
#include "soundtouch/source/SoundStretch/WavFile.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static WavOutFile *_new_WavOutFile(const char *destfile)
|
static WavOutFile* _new_WavOutFile(const char* destfile)
|
||||||
{
|
{
|
||||||
return new WavOutFile(destfile, 48000, 16, 2);
|
return new WavOutFile(destfile, 48000, 16, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace WaveDump
|
namespace WaveDump
|
||||||
{
|
{
|
||||||
static WavOutFile *m_CoreWav[2][CoreSrc_Count];
|
static WavOutFile* m_CoreWav[2][CoreSrc_Count];
|
||||||
|
|
||||||
static const char *m_tbl_CoreOutputTypeNames[CoreSrc_Count] =
|
static const char* m_tbl_CoreOutputTypeNames[CoreSrc_Count] =
|
||||||
{
|
{
|
||||||
"Input",
|
"Input",
|
||||||
"DryVoiceMix",
|
"DryVoiceMix",
|
||||||
"WetVoiceMix",
|
"WetVoiceMix",
|
||||||
"PreReverb",
|
"PreReverb",
|
||||||
"PostReverb",
|
"PostReverb",
|
||||||
"External"};
|
"External"};
|
||||||
|
|
||||||
void Open()
|
void Open()
|
||||||
{
|
{
|
||||||
if (!IsDevBuild)
|
if (!IsDevBuild)
|
||||||
return;
|
return;
|
||||||
if (!WaveLog())
|
if (!WaveLog())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
char wavfilename[256];
|
char wavfilename[256];
|
||||||
|
|
||||||
for (uint cidx = 0; cidx < 2; cidx++) {
|
for (uint cidx = 0; cidx < 2; cidx++)
|
||||||
for (int srcidx = 0; srcidx < CoreSrc_Count; srcidx++) {
|
{
|
||||||
safe_delete(m_CoreWav[cidx][srcidx]);
|
for (int srcidx = 0; srcidx < CoreSrc_Count; srcidx++)
|
||||||
|
{
|
||||||
|
safe_delete(m_CoreWav[cidx][srcidx]);
|
||||||
#ifdef __POSIX__
|
#ifdef __POSIX__
|
||||||
sprintf(wavfilename, "logs/spu2x-Core%ud-%s.wav",
|
sprintf(wavfilename, "logs/spu2x-Core%ud-%s.wav",
|
||||||
cidx, m_tbl_CoreOutputTypeNames[srcidx]);
|
cidx, m_tbl_CoreOutputTypeNames[srcidx]);
|
||||||
#else
|
#else
|
||||||
sprintf(wavfilename, "logs\\spu2x-Core%ud-%s.wav",
|
sprintf(wavfilename, "logs\\spu2x-Core%ud-%s.wav",
|
||||||
cidx, m_tbl_CoreOutputTypeNames[srcidx]);
|
cidx, m_tbl_CoreOutputTypeNames[srcidx]);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
try {
|
try
|
||||||
m_CoreWav[cidx][srcidx] = _new_WavOutFile(wavfilename);
|
{
|
||||||
} catch (std::runtime_error &ex) {
|
m_CoreWav[cidx][srcidx] = _new_WavOutFile(wavfilename);
|
||||||
printf("SPU2-X > %s.\n\tWave Log for this core source disabled.", ex.what());
|
}
|
||||||
m_CoreWav[cidx][srcidx] = NULL;
|
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()
|
void Close()
|
||||||
{
|
{
|
||||||
if (!IsDevBuild)
|
if (!IsDevBuild)
|
||||||
return;
|
return;
|
||||||
for (uint cidx = 0; cidx < 2; cidx++) {
|
for (uint cidx = 0; cidx < 2; cidx++)
|
||||||
for (int srcidx = 0; srcidx < CoreSrc_Count; srcidx++) {
|
{
|
||||||
safe_delete(m_CoreWav[cidx][srcidx]);
|
for (int srcidx = 0; srcidx < CoreSrc_Count; srcidx++)
|
||||||
}
|
{
|
||||||
}
|
safe_delete(m_CoreWav[cidx][srcidx]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void WriteCore(uint coreidx, CoreSourceType src, const StereoOut16 &sample)
|
void WriteCore(uint coreidx, CoreSourceType src, const StereoOut16& sample)
|
||||||
{
|
{
|
||||||
if (!IsDevBuild)
|
if (!IsDevBuild)
|
||||||
return;
|
return;
|
||||||
if (m_CoreWav[coreidx][src] != NULL)
|
if (m_CoreWav[coreidx][src] != NULL)
|
||||||
m_CoreWav[coreidx][src]->write((s16 *)&sample, 2);
|
m_CoreWav[coreidx][src]->write((s16*)&sample, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteCore(uint coreidx, CoreSourceType src, s16 left, s16 right)
|
void WriteCore(uint coreidx, CoreSourceType src, s16 left, s16 right)
|
||||||
{
|
{
|
||||||
WriteCore(coreidx, src, StereoOut16(left, right));
|
WriteCore(coreidx, src, StereoOut16(left, right));
|
||||||
}
|
}
|
||||||
}
|
} // namespace WaveDump
|
||||||
|
|
||||||
#include "Utilities/Threading.h"
|
#include "Utilities/Threading.h"
|
||||||
|
|
||||||
|
@ -99,42 +106,45 @@ using namespace Threading;
|
||||||
|
|
||||||
bool WavRecordEnabled = false;
|
bool WavRecordEnabled = false;
|
||||||
|
|
||||||
static WavOutFile *m_wavrecord = NULL;
|
static WavOutFile* m_wavrecord = NULL;
|
||||||
static Mutex WavRecordMutex;
|
static Mutex WavRecordMutex;
|
||||||
|
|
||||||
void RecordStart(std::wstring* filename)
|
void RecordStart(std::wstring* filename)
|
||||||
{
|
{
|
||||||
WavRecordEnabled = false;
|
WavRecordEnabled = false;
|
||||||
|
|
||||||
try {
|
try
|
||||||
ScopedLock lock(WavRecordMutex);
|
{
|
||||||
safe_delete(m_wavrecord);
|
ScopedLock lock(WavRecordMutex);
|
||||||
|
safe_delete(m_wavrecord);
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (filename)
|
if (filename)
|
||||||
m_wavrecord = new WavOutFile((*filename) + "wav", 48000, 16, 2);
|
m_wavrecord = new WavOutFile((*filename) + "wav", 48000, 16, 2);
|
||||||
else
|
else
|
||||||
m_wavrecord = new WavOutFile("audio_recording.wav", 48000, 16, 2);
|
m_wavrecord = new WavOutFile("audio_recording.wav", 48000, 16, 2);
|
||||||
#elif defined(__unix__)
|
#elif defined(__unix__)
|
||||||
m_wavrecord = new WavOutFile("audio_recording.wav", 48000, 16, 2);
|
m_wavrecord = new WavOutFile("audio_recording.wav", 48000, 16, 2);
|
||||||
#endif
|
#endif
|
||||||
WavRecordEnabled = true;
|
WavRecordEnabled = true;
|
||||||
} catch (std::runtime_error &) {
|
}
|
||||||
m_wavrecord = NULL; // not needed, but what the heck. :)
|
catch (std::runtime_error&)
|
||||||
SysMessage("SPU2-X couldn't open file for recording: %s.\nRecording to wavfile disabled.", "audio_recording.wav");
|
{
|
||||||
}
|
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()
|
void RecordStop()
|
||||||
{
|
{
|
||||||
WavRecordEnabled = false;
|
WavRecordEnabled = false;
|
||||||
ScopedLock lock(WavRecordMutex);
|
ScopedLock lock(WavRecordMutex);
|
||||||
safe_delete(m_wavrecord);
|
safe_delete(m_wavrecord);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecordWrite(const StereoOut16 &sample)
|
void RecordWrite(const StereoOut16& sample)
|
||||||
{
|
{
|
||||||
ScopedLock lock(WavRecordMutex);
|
ScopedLock lock(WavRecordMutex);
|
||||||
if (m_wavrecord == NULL)
|
if (m_wavrecord == NULL)
|
||||||
return;
|
return;
|
||||||
m_wavrecord->write((s16 *)&sample, 2);
|
m_wavrecord->write((s16*)&sample, 2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,29 +20,29 @@
|
||||||
|
|
||||||
extern uptr gsWindowHandle;
|
extern uptr gsWindowHandle;
|
||||||
|
|
||||||
void SysMessage(const char *fmt, ...)
|
void SysMessage(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
va_list list;
|
va_list list;
|
||||||
char tmp[512];
|
char tmp[512];
|
||||||
wchar_t wtmp[512];
|
wchar_t wtmp[512];
|
||||||
|
|
||||||
va_start(list, fmt);
|
va_start(list, fmt);
|
||||||
vsprintf_s(tmp, fmt, list);
|
vsprintf_s(tmp, fmt, list);
|
||||||
va_end(list);
|
va_end(list);
|
||||||
swprintf_s(wtmp, L"%S", tmp);
|
swprintf_s(wtmp, L"%S", tmp);
|
||||||
MessageBox((!!gsWindowHandle) ? (HWND)gsWindowHandle : GetActiveWindow(), wtmp,
|
MessageBox((!!gsWindowHandle) ? (HWND)gsWindowHandle : GetActiveWindow(), wtmp,
|
||||||
L"SPU2-X System Message", MB_OK | MB_SETFOREGROUND);
|
L"SPU2-X System Message", MB_OK | MB_SETFOREGROUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SysMessage(const wchar_t *fmt, ...)
|
void SysMessage(const wchar_t* fmt, ...)
|
||||||
{
|
{
|
||||||
va_list list;
|
va_list list;
|
||||||
va_start(list, fmt);
|
va_start(list, fmt);
|
||||||
wxString wtmp;
|
wxString wtmp;
|
||||||
wtmp.PrintfV(fmt, list);
|
wtmp.PrintfV(fmt, list);
|
||||||
va_end(list);
|
va_end(list);
|
||||||
MessageBox((!!gsWindowHandle) ? (HWND)gsWindowHandle : GetActiveWindow(), wtmp,
|
MessageBox((!!gsWindowHandle) ? (HWND)gsWindowHandle : GetActiveWindow(), wtmp,
|
||||||
L"SPU2-X System Message", MB_OK | MB_SETFOREGROUND);
|
L"SPU2-X System Message", MB_OK | MB_SETFOREGROUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
//////
|
//////
|
||||||
|
@ -51,9 +51,9 @@ void SysMessage(const wchar_t *fmt, ...)
|
||||||
|
|
||||||
static wxString CfgFile(L"inis/SPU2.ini");
|
static wxString CfgFile(L"inis/SPU2.ini");
|
||||||
|
|
||||||
void CfgSetSettingsDir(const char *dir)
|
void CfgSetSettingsDir(const char* dir)
|
||||||
{
|
{
|
||||||
CfgFile = Path::Combine((dir == NULL) ? wxString(L"inis") : wxString::FromUTF8(dir), L"SPU2.ini");
|
CfgFile = Path::Combine((dir == NULL) ? wxString(L"inis") : wxString::FromUTF8(dir), L"SPU2.ini");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,24 +72,24 @@ void CfgSetSettingsDir(const char *dir)
|
||||||
\*_____________________________________________*/
|
\*_____________________________________________*/
|
||||||
|
|
||||||
|
|
||||||
void CfgWriteBool(const TCHAR *Section, const TCHAR *Name, bool Value)
|
void CfgWriteBool(const TCHAR* Section, const TCHAR* Name, bool Value)
|
||||||
{
|
{
|
||||||
const TCHAR *Data = Value ? L"TRUE" : L"FALSE";
|
const TCHAR* Data = Value ? L"TRUE" : L"FALSE";
|
||||||
WritePrivateProfileString(Section, Name, Data, CfgFile);
|
WritePrivateProfileString(Section, Name, Data, CfgFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CfgWriteInt(const TCHAR *Section, const TCHAR *Name, int Value)
|
void CfgWriteInt(const TCHAR* Section, const TCHAR* Name, int Value)
|
||||||
{
|
{
|
||||||
TCHAR Data[32];
|
TCHAR Data[32];
|
||||||
_itow(Value, Data, 10);
|
_itow(Value, Data, 10);
|
||||||
WritePrivateProfileString(Section, Name, Data, CfgFile);
|
WritePrivateProfileString(Section, Name, Data, CfgFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CfgWriteFloat(const TCHAR *Section, const TCHAR *Name, float Value)
|
void CfgWriteFloat(const TCHAR* Section, const TCHAR* Name, float Value)
|
||||||
{
|
{
|
||||||
TCHAR Data[32];
|
TCHAR Data[32];
|
||||||
_swprintf(Data, L"%f", Value);
|
_swprintf(Data, L"%f", Value);
|
||||||
WritePrivateProfileString(Section, Name, Data, CfgFile);
|
WritePrivateProfileString(Section, Name, Data, CfgFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*void CfgWriteStr(const TCHAR* Section, const TCHAR* Name, const TCHAR *Data)
|
/*void CfgWriteStr(const TCHAR* Section, const TCHAR* Name, const TCHAR *Data)
|
||||||
|
@ -97,99 +97,104 @@ void CfgWriteFloat(const TCHAR *Section, const TCHAR *Name, float Value)
|
||||||
WritePrivateProfileString( Section, Name, Data, CfgFile );
|
WritePrivateProfileString( Section, Name, Data, CfgFile );
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
void CfgWriteStr(const TCHAR *Section, const TCHAR *Name, const wxString &Data)
|
void CfgWriteStr(const TCHAR* Section, const TCHAR* Name, const wxString& Data)
|
||||||
{
|
{
|
||||||
WritePrivateProfileString(Section, Name, Data, CfgFile);
|
WritePrivateProfileString(Section, Name, Data, CfgFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
bool CfgReadBool(const TCHAR *Section, const TCHAR *Name, bool Default)
|
bool CfgReadBool(const TCHAR* Section, const TCHAR* Name, bool Default)
|
||||||
{
|
{
|
||||||
TCHAR Data[255] = {0};
|
TCHAR Data[255] = {0};
|
||||||
|
|
||||||
GetPrivateProfileString(Section, Name, L"", Data, 255, CfgFile);
|
GetPrivateProfileString(Section, Name, L"", Data, 255, CfgFile);
|
||||||
Data[254] = 0;
|
Data[254] = 0;
|
||||||
if (wcslen(Data) == 0) {
|
if (wcslen(Data) == 0)
|
||||||
CfgWriteBool(Section, Name, Default);
|
{
|
||||||
return Default;
|
CfgWriteBool(Section, Name, Default);
|
||||||
}
|
return Default;
|
||||||
|
}
|
||||||
|
|
||||||
if (wcscmp(Data, L"1") == 0)
|
if (wcscmp(Data, L"1") == 0)
|
||||||
return true;
|
return true;
|
||||||
if (wcscmp(Data, L"Y") == 0)
|
if (wcscmp(Data, L"Y") == 0)
|
||||||
return true;
|
return true;
|
||||||
if (wcscmp(Data, L"T") == 0)
|
if (wcscmp(Data, L"T") == 0)
|
||||||
return true;
|
return true;
|
||||||
if (wcscmp(Data, L"YES") == 0)
|
if (wcscmp(Data, L"YES") == 0)
|
||||||
return true;
|
return true;
|
||||||
if (wcscmp(Data, L"TRUE") == 0)
|
if (wcscmp(Data, L"TRUE") == 0)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int CfgReadInt(const TCHAR *Section, const TCHAR *Name, int Default)
|
int CfgReadInt(const TCHAR* Section, const TCHAR* Name, int Default)
|
||||||
{
|
{
|
||||||
TCHAR Data[255] = {0};
|
TCHAR Data[255] = {0};
|
||||||
GetPrivateProfileString(Section, Name, L"", Data, 255, CfgFile);
|
GetPrivateProfileString(Section, Name, L"", Data, 255, CfgFile);
|
||||||
Data[254] = 0;
|
Data[254] = 0;
|
||||||
|
|
||||||
if (wcslen(Data) == 0) {
|
if (wcslen(Data) == 0)
|
||||||
CfgWriteInt(Section, Name, Default);
|
{
|
||||||
return Default;
|
CfgWriteInt(Section, Name, Default);
|
||||||
}
|
return Default;
|
||||||
|
}
|
||||||
|
|
||||||
return _wtoi(Data);
|
return _wtoi(Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
float CfgReadFloat(const TCHAR *Section, const TCHAR *Name, float Default)
|
float CfgReadFloat(const TCHAR* Section, const TCHAR* Name, float Default)
|
||||||
{
|
{
|
||||||
TCHAR Data[255] = {0};
|
TCHAR Data[255] = {0};
|
||||||
GetPrivateProfileString(Section, Name, L"", Data, 255, CfgFile);
|
GetPrivateProfileString(Section, Name, L"", Data, 255, CfgFile);
|
||||||
Data[254] = 0;
|
Data[254] = 0;
|
||||||
|
|
||||||
if (wcslen(Data) == 0) {
|
if (wcslen(Data) == 0)
|
||||||
CfgWriteFloat(Section, Name, Default);
|
{
|
||||||
return Default;
|
CfgWriteFloat(Section, Name, Default);
|
||||||
}
|
return Default;
|
||||||
|
}
|
||||||
|
|
||||||
return (float)_wtof(Data);
|
return (float)_wtof(Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CfgReadStr(const TCHAR *Section, const TCHAR *Name, TCHAR *Data, int DataSize, const TCHAR *Default)
|
void CfgReadStr(const TCHAR* Section, const TCHAR* Name, TCHAR* Data, int DataSize, const TCHAR* Default)
|
||||||
{
|
{
|
||||||
GetPrivateProfileString(Section, Name, L"", Data, DataSize, CfgFile);
|
GetPrivateProfileString(Section, Name, L"", Data, DataSize, CfgFile);
|
||||||
|
|
||||||
if (wcslen(Data) == 0) {
|
if (wcslen(Data) == 0)
|
||||||
swprintf_s(Data, DataSize, L"%s", Default);
|
{
|
||||||
CfgWriteStr(Section, Name, Data);
|
swprintf_s(Data, DataSize, L"%s", Default);
|
||||||
}
|
CfgWriteStr(Section, Name, Data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CfgReadStr(const TCHAR *Section, const TCHAR *Name, wxString &Data, const TCHAR *Default)
|
void CfgReadStr(const TCHAR* Section, const TCHAR* Name, wxString& Data, const TCHAR* Default)
|
||||||
{
|
{
|
||||||
wchar_t workspace[512];
|
wchar_t workspace[512];
|
||||||
GetPrivateProfileString(Section, Name, L"", workspace, ArraySize(workspace), CfgFile);
|
GetPrivateProfileString(Section, Name, L"", workspace, ArraySize(workspace), CfgFile);
|
||||||
|
|
||||||
Data = workspace;
|
Data = workspace;
|
||||||
|
|
||||||
if (Data.empty()) {
|
if (Data.empty())
|
||||||
Data = Default;
|
{
|
||||||
CfgWriteStr(Section, Name, Default);
|
Data = Default;
|
||||||
}
|
CfgWriteStr(Section, Name, Default);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tries to read the requested value.
|
// Tries to read the requested value.
|
||||||
// Returns FALSE if the value isn't found.
|
// Returns FALSE if the value isn't found.
|
||||||
bool CfgFindName(const TCHAR *Section, const TCHAR *Name)
|
bool CfgFindName(const TCHAR* Section, const TCHAR* Name)
|
||||||
{
|
{
|
||||||
// Only load 24 characters. No need to load more.
|
// Only load 24 characters. No need to load more.
|
||||||
TCHAR Data[24] = {0};
|
TCHAR Data[24] = {0};
|
||||||
GetPrivateProfileString(Section, Name, L"", Data, 24, CfgFile);
|
GetPrivateProfileString(Section, Name, L"", Data, 24, CfgFile);
|
||||||
Data[23] = 0;
|
Data[23] = 0;
|
||||||
|
|
||||||
if (wcslen(Data) == 0)
|
if (wcslen(Data) == 0)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,344 +83,366 @@ int dplLevel = 0;
|
||||||
|
|
||||||
void ReadSettings()
|
void ReadSettings()
|
||||||
{
|
{
|
||||||
Interpolation = CfgReadInt(L"MIXING", L"Interpolation", 4);
|
Interpolation = CfgReadInt(L"MIXING", L"Interpolation", 4);
|
||||||
|
|
||||||
EffectsDisabled = CfgReadBool(L"MIXING", L"Disable_Effects", false);
|
EffectsDisabled = CfgReadBool(L"MIXING", L"Disable_Effects", false);
|
||||||
postprocess_filter_dealias = CfgReadBool(L"MIXING", L"DealiasFilter", false);
|
postprocess_filter_dealias = CfgReadBool(L"MIXING", L"DealiasFilter", false);
|
||||||
FinalVolume = ((float)CfgReadInt(L"MIXING", L"FinalVolume", 100)) / 100;
|
FinalVolume = ((float)CfgReadInt(L"MIXING", L"FinalVolume", 100)) / 100;
|
||||||
if (FinalVolume > 1.0f)
|
if (FinalVolume > 1.0f)
|
||||||
FinalVolume = 1.0f;
|
FinalVolume = 1.0f;
|
||||||
|
|
||||||
AdvancedVolumeControl = CfgReadBool(L"MIXING", L"AdvancedVolumeControl", false);
|
AdvancedVolumeControl = CfgReadBool(L"MIXING", L"AdvancedVolumeControl", false);
|
||||||
VolumeAdjustCdb = CfgReadFloat(L"MIXING", L"VolumeAdjustC(dB)", 0);
|
VolumeAdjustCdb = CfgReadFloat(L"MIXING", L"VolumeAdjustC(dB)", 0);
|
||||||
VolumeAdjustFLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFL(dB)", 0);
|
VolumeAdjustFLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFL(dB)", 0);
|
||||||
VolumeAdjustFRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFR(dB)", 0);
|
VolumeAdjustFRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustFR(dB)", 0);
|
||||||
VolumeAdjustBLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBL(dB)", 0);
|
VolumeAdjustBLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBL(dB)", 0);
|
||||||
VolumeAdjustBRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBR(dB)", 0);
|
VolumeAdjustBRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustBR(dB)", 0);
|
||||||
VolumeAdjustSLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSL(dB)", 0);
|
VolumeAdjustSLdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSL(dB)", 0);
|
||||||
VolumeAdjustSRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSR(dB)", 0);
|
VolumeAdjustSRdb = CfgReadFloat(L"MIXING", L"VolumeAdjustSR(dB)", 0);
|
||||||
VolumeAdjustLFEdb = CfgReadFloat(L"MIXING", L"VolumeAdjustLFE(dB)", 0);
|
VolumeAdjustLFEdb = CfgReadFloat(L"MIXING", L"VolumeAdjustLFE(dB)", 0);
|
||||||
delayCycles = CfgReadInt(L"DEBUG", L"DelayCycles", 4);
|
delayCycles = CfgReadInt(L"DEBUG", L"DelayCycles", 4);
|
||||||
VolumeAdjustC = powf(10, VolumeAdjustCdb / 10);
|
VolumeAdjustC = powf(10, VolumeAdjustCdb / 10);
|
||||||
VolumeAdjustFL = powf(10, VolumeAdjustFLdb / 10);
|
VolumeAdjustFL = powf(10, VolumeAdjustFLdb / 10);
|
||||||
VolumeAdjustFR = powf(10, VolumeAdjustFRdb / 10);
|
VolumeAdjustFR = powf(10, VolumeAdjustFRdb / 10);
|
||||||
VolumeAdjustBL = powf(10, VolumeAdjustBLdb / 10);
|
VolumeAdjustBL = powf(10, VolumeAdjustBLdb / 10);
|
||||||
VolumeAdjustBR = powf(10, VolumeAdjustBRdb / 10);
|
VolumeAdjustBR = powf(10, VolumeAdjustBRdb / 10);
|
||||||
VolumeAdjustSL = powf(10, VolumeAdjustSLdb / 10);
|
VolumeAdjustSL = powf(10, VolumeAdjustSLdb / 10);
|
||||||
VolumeAdjustSR = powf(10, VolumeAdjustSRdb / 10);
|
VolumeAdjustSR = powf(10, VolumeAdjustSRdb / 10);
|
||||||
VolumeAdjustLFE = powf(10, VolumeAdjustLFEdb / 10);
|
VolumeAdjustLFE = powf(10, VolumeAdjustLFEdb / 10);
|
||||||
|
|
||||||
SynchMode = CfgReadInt(L"OUTPUT", L"Synch_Mode", 0);
|
SynchMode = CfgReadInt(L"OUTPUT", L"Synch_Mode", 0);
|
||||||
numSpeakers = CfgReadInt(L"OUTPUT", L"SpeakerConfiguration", 0);
|
numSpeakers = CfgReadInt(L"OUTPUT", L"SpeakerConfiguration", 0);
|
||||||
dplLevel = CfgReadInt(L"OUTPUT", L"DplDecodingLevel", 0);
|
dplLevel = CfgReadInt(L"OUTPUT", L"DplDecodingLevel", 0);
|
||||||
SndOutLatencyMS = CfgReadInt(L"OUTPUT", L"Latency", 100);
|
SndOutLatencyMS = CfgReadInt(L"OUTPUT", L"Latency", 100);
|
||||||
|
|
||||||
if ((SynchMode == 0) && (SndOutLatencyMS < LATENCY_MIN_TS)) // can't use low-latency with timestretcher atm
|
if ((SynchMode == 0) && (SndOutLatencyMS < LATENCY_MIN_TS)) // can't use low-latency with timestretcher atm
|
||||||
SndOutLatencyMS = LATENCY_MIN_TS;
|
SndOutLatencyMS = LATENCY_MIN_TS;
|
||||||
else if (SndOutLatencyMS < LATENCY_MIN)
|
else if (SndOutLatencyMS < LATENCY_MIN)
|
||||||
SndOutLatencyMS = LATENCY_MIN;
|
SndOutLatencyMS = LATENCY_MIN;
|
||||||
|
|
||||||
wchar_t omodid[128];
|
wchar_t omodid[128];
|
||||||
|
|
||||||
// portaudio occasionally has issues selecting the proper default audio device.
|
// portaudio occasionally has issues selecting the proper default audio device.
|
||||||
// let's use xaudio2 until this is sorted (rama)
|
// 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, PortaudioOut->GetIdent());
|
||||||
CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, XAudio2Out->GetIdent());
|
CfgReadStr(L"OUTPUT", L"Output_Module", omodid, 127, XAudio2Out->GetIdent());
|
||||||
|
|
||||||
// find the driver index of this module:
|
// find the driver index of this module:
|
||||||
OutputModule = FindOutputModuleById(omodid);
|
OutputModule = FindOutputModuleById(omodid);
|
||||||
|
|
||||||
CfgReadStr(L"DSP PLUGIN", L"Filename", dspPlugin, 255, L"");
|
CfgReadStr(L"DSP PLUGIN", L"Filename", dspPlugin, 255, L"");
|
||||||
dspPluginModule = CfgReadInt(L"DSP PLUGIN", L"ModuleNum", 0);
|
dspPluginModule = CfgReadInt(L"DSP PLUGIN", L"ModuleNum", 0);
|
||||||
dspPluginEnabled = CfgReadBool(L"DSP PLUGIN", L"Enabled", false);
|
dspPluginEnabled = CfgReadBool(L"DSP PLUGIN", L"Enabled", false);
|
||||||
|
|
||||||
// Read DSOUNDOUT and WAVEOUT configs:
|
// Read DSOUNDOUT and WAVEOUT configs:
|
||||||
CfgReadStr(L"WAVEOUT", L"Device", Config_WaveOut.Device, L"default");
|
CfgReadStr(L"WAVEOUT", L"Device", Config_WaveOut.Device, L"default");
|
||||||
Config_WaveOut.NumBuffers = CfgReadInt(L"WAVEOUT", L"Buffer_Count", 4);
|
Config_WaveOut.NumBuffers = CfgReadInt(L"WAVEOUT", L"Buffer_Count", 4);
|
||||||
|
|
||||||
DSoundOut->ReadSettings();
|
DSoundOut->ReadSettings();
|
||||||
PortaudioOut->ReadSettings();
|
PortaudioOut->ReadSettings();
|
||||||
|
|
||||||
SoundtouchCfg::ReadSettings();
|
SoundtouchCfg::ReadSettings();
|
||||||
DebugConfig::ReadSettings();
|
DebugConfig::ReadSettings();
|
||||||
|
|
||||||
// Sanity Checks
|
// Sanity Checks
|
||||||
// -------------
|
// -------------
|
||||||
|
|
||||||
Clampify(SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX);
|
Clampify(SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX);
|
||||||
|
|
||||||
if (mods[OutputModule] == NULL) {
|
if (mods[OutputModule] == NULL)
|
||||||
// Unsupported or legacy module.
|
{
|
||||||
fwprintf(stderr, L"* SPU2-X: Unknown output module '%s' specified in configuration file.\n", omodid);
|
// Unsupported or legacy module.
|
||||||
fprintf(stderr, "* SPU2-X: Defaulting to DirectSound (%S).\n", DSoundOut->GetIdent());
|
fwprintf(stderr, L"* SPU2-X: Unknown output module '%s' specified in configuration file.\n", omodid);
|
||||||
OutputModule = FindOutputModuleById(DSoundOut->GetIdent());
|
fprintf(stderr, "* SPU2-X: Defaulting to DirectSound (%S).\n", DSoundOut->GetIdent());
|
||||||
}
|
OutputModule = FindOutputModuleById(DSoundOut->GetIdent());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
void WriteSettings()
|
void WriteSettings()
|
||||||
{
|
{
|
||||||
CfgWriteInt(L"MIXING", L"Interpolation", Interpolation);
|
CfgWriteInt(L"MIXING", L"Interpolation", Interpolation);
|
||||||
|
|
||||||
CfgWriteBool(L"MIXING", L"Disable_Effects", EffectsDisabled);
|
CfgWriteBool(L"MIXING", L"Disable_Effects", EffectsDisabled);
|
||||||
CfgWriteBool(L"MIXING", L"DealiasFilter", postprocess_filter_dealias);
|
CfgWriteBool(L"MIXING", L"DealiasFilter", postprocess_filter_dealias);
|
||||||
CfgWriteInt(L"MIXING", L"FinalVolume", (int)(FinalVolume * 100 + 0.5f));
|
CfgWriteInt(L"MIXING", L"FinalVolume", (int)(FinalVolume * 100 + 0.5f));
|
||||||
|
|
||||||
CfgWriteBool(L"MIXING", L"AdvancedVolumeControl", AdvancedVolumeControl);
|
CfgWriteBool(L"MIXING", L"AdvancedVolumeControl", AdvancedVolumeControl);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustC(dB)", VolumeAdjustCdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustC(dB)", VolumeAdjustCdb);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustFL(dB)", VolumeAdjustFLdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustFL(dB)", VolumeAdjustFLdb);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustFR(dB)", VolumeAdjustFRdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustFR(dB)", VolumeAdjustFRdb);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustBL(dB)", VolumeAdjustBLdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustBL(dB)", VolumeAdjustBLdb);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustBR(dB)", VolumeAdjustBRdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustBR(dB)", VolumeAdjustBRdb);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustSL(dB)", VolumeAdjustSLdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustSL(dB)", VolumeAdjustSLdb);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustSR(dB)", VolumeAdjustSRdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustSR(dB)", VolumeAdjustSRdb);
|
||||||
CfgWriteFloat(L"MIXING", L"VolumeAdjustLFE(dB)", VolumeAdjustLFEdb);
|
CfgWriteFloat(L"MIXING", L"VolumeAdjustLFE(dB)", VolumeAdjustLFEdb);
|
||||||
|
|
||||||
CfgWriteStr(L"OUTPUT", L"Output_Module", mods[OutputModule]->GetIdent());
|
CfgWriteStr(L"OUTPUT", L"Output_Module", mods[OutputModule]->GetIdent());
|
||||||
CfgWriteInt(L"OUTPUT", L"Latency", SndOutLatencyMS);
|
CfgWriteInt(L"OUTPUT", L"Latency", SndOutLatencyMS);
|
||||||
CfgWriteInt(L"OUTPUT", L"Synch_Mode", SynchMode);
|
CfgWriteInt(L"OUTPUT", L"Synch_Mode", SynchMode);
|
||||||
CfgWriteInt(L"OUTPUT", L"SpeakerConfiguration", numSpeakers);
|
CfgWriteInt(L"OUTPUT", L"SpeakerConfiguration", numSpeakers);
|
||||||
CfgWriteInt(L"OUTPUT", L"DplDecodingLevel", dplLevel);
|
CfgWriteInt(L"OUTPUT", L"DplDecodingLevel", dplLevel);
|
||||||
CfgWriteInt(L"DEBUG", L"DelayCycles", delayCycles);
|
CfgWriteInt(L"DEBUG", L"DelayCycles", delayCycles);
|
||||||
|
|
||||||
if (Config_WaveOut.Device.empty())
|
if (Config_WaveOut.Device.empty())
|
||||||
Config_WaveOut.Device = L"default";
|
Config_WaveOut.Device = L"default";
|
||||||
CfgWriteStr(L"WAVEOUT", L"Device", Config_WaveOut.Device);
|
CfgWriteStr(L"WAVEOUT", L"Device", Config_WaveOut.Device);
|
||||||
CfgWriteInt(L"WAVEOUT", L"Buffer_Count", Config_WaveOut.NumBuffers);
|
CfgWriteInt(L"WAVEOUT", L"Buffer_Count", Config_WaveOut.NumBuffers);
|
||||||
|
|
||||||
CfgWriteStr(L"DSP PLUGIN", L"Filename", dspPlugin);
|
CfgWriteStr(L"DSP PLUGIN", L"Filename", dspPlugin);
|
||||||
CfgWriteInt(L"DSP PLUGIN", L"ModuleNum", dspPluginModule);
|
CfgWriteInt(L"DSP PLUGIN", L"ModuleNum", dspPluginModule);
|
||||||
CfgWriteBool(L"DSP PLUGIN", L"Enabled", dspPluginEnabled);
|
CfgWriteBool(L"DSP PLUGIN", L"Enabled", dspPluginEnabled);
|
||||||
|
|
||||||
PortaudioOut->WriteSettings();
|
PortaudioOut->WriteSettings();
|
||||||
DSoundOut->WriteSettings();
|
DSoundOut->WriteSettings();
|
||||||
SoundtouchCfg::WriteSettings();
|
SoundtouchCfg::WriteSettings();
|
||||||
DebugConfig::WriteSettings();
|
DebugConfig::WriteSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckOutputModule(HWND window)
|
void CheckOutputModule(HWND window)
|
||||||
{
|
{
|
||||||
OutputModule = SendMessage(GetDlgItem(window, IDC_OUTPUT), CB_GETCURSEL, 0, 0);
|
OutputModule = SendMessage(GetDlgItem(window, IDC_OUTPUT), CB_GETCURSEL, 0, 0);
|
||||||
const bool IsConfigurable =
|
const bool IsConfigurable =
|
||||||
mods[OutputModule] == PortaudioOut ||
|
mods[OutputModule] == PortaudioOut ||
|
||||||
mods[OutputModule] == WaveOut ||
|
mods[OutputModule] == WaveOut ||
|
||||||
mods[OutputModule] == DSoundOut;
|
mods[OutputModule] == DSoundOut;
|
||||||
|
|
||||||
const bool AudioExpansion =
|
const bool AudioExpansion =
|
||||||
mods[OutputModule] == XAudio2Out ||
|
mods[OutputModule] == XAudio2Out ||
|
||||||
mods[OutputModule] == PortaudioOut;
|
mods[OutputModule] == PortaudioOut;
|
||||||
|
|
||||||
EnableWindow(GetDlgItem(window, IDC_OUTCONF), IsConfigurable);
|
EnableWindow(GetDlgItem(window, IDC_OUTCONF), IsConfigurable);
|
||||||
EnableWindow(GetDlgItem(window, IDC_SPEAKERS), AudioExpansion);
|
EnableWindow(GetDlgItem(window, IDC_SPEAKERS), AudioExpansion);
|
||||||
EnableWindow(GetDlgItem(window, IDC_SPEAKERS_TEXT), AudioExpansion);
|
EnableWindow(GetDlgItem(window, IDC_SPEAKERS_TEXT), AudioExpansion);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL CALLBACK ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
BOOL CALLBACK ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
int wmId, wmEvent;
|
int wmId, wmEvent;
|
||||||
wchar_t temp[384] = {0};
|
wchar_t temp[384] = {0};
|
||||||
|
|
||||||
switch (uMsg) {
|
switch (uMsg)
|
||||||
case WM_PAINT:
|
{
|
||||||
return FALSE;
|
case WM_PAINT:
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
case WM_INITDIALOG: {
|
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_RESETCONTENT, 0, 0);
|
||||||
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"1 - Linear (Simple/okay sound)");
|
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"0 - Nearest (Fastest/bad quality)");
|
||||||
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"2 - Cubic (Artificial highs)");
|
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"1 - Linear (Simple/okay sound)");
|
||||||
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"3 - Hermite (Better highs)");
|
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"2 - Cubic (Artificial highs)");
|
||||||
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"4 - Catmull-Rom (PS2-like/slow)");
|
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_ADDSTRING, 0, (LPARAM)L"3 - Hermite (Better highs)");
|
||||||
SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_SETCURSEL, Interpolation, 0);
|
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_RESETCONTENT, 0, 0);
|
||||||
SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_ADDSTRING, 0, (LPARAM)L"TimeStretch (Recommended)");
|
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"Async Mix (Breaks some games!)");
|
||||||
SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_ADDSTRING, 0, (LPARAM)L"None (Audio can skip.)");
|
SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_ADDSTRING, 0, (LPARAM)L"None (Audio can skip.)");
|
||||||
SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_SETCURSEL, SynchMode, 0);
|
SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_SETCURSEL, SynchMode, 0);
|
||||||
|
|
||||||
SendDialogMsg(hWnd, IDC_SPEAKERS, CB_RESETCONTENT, 0, 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"Stereo (None, Default)");
|
||||||
SendDialogMsg(hWnd, IDC_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)L"Quadrafonic");
|
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 5.1");
|
||||||
SendDialogMsg(hWnd, IDC_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)L"Surround 7.1");
|
SendDialogMsg(hWnd, IDC_SPEAKERS, CB_ADDSTRING, 0, (LPARAM)L"Surround 7.1");
|
||||||
SendDialogMsg(hWnd, IDC_SPEAKERS, CB_SETCURSEL, numSpeakers, 0);
|
SendDialogMsg(hWnd, IDC_SPEAKERS, CB_SETCURSEL, numSpeakers, 0);
|
||||||
|
|
||||||
SendDialogMsg(hWnd, IDC_OUTPUT, CB_RESETCONTENT, 0, 0);
|
SendDialogMsg(hWnd, IDC_OUTPUT, CB_RESETCONTENT, 0, 0);
|
||||||
|
|
||||||
int modidx = 0;
|
int modidx = 0;
|
||||||
while (mods[modidx] != NULL) {
|
while (mods[modidx] != NULL)
|
||||||
swprintf_s(temp, 72, L"%d - %s", modidx, mods[modidx]->GetLongName());
|
{
|
||||||
SendDialogMsg(hWnd, IDC_OUTPUT, CB_ADDSTRING, 0, (LPARAM)temp);
|
swprintf_s(temp, 72, L"%d - %s", modidx, mods[modidx]->GetLongName());
|
||||||
++modidx;
|
SendDialogMsg(hWnd, IDC_OUTPUT, CB_ADDSTRING, 0, (LPARAM)temp);
|
||||||
}
|
++modidx;
|
||||||
SendDialogMsg(hWnd, IDC_OUTPUT, CB_SETCURSEL, OutputModule, 0);
|
}
|
||||||
|
SendDialogMsg(hWnd, IDC_OUTPUT, CB_SETCURSEL, OutputModule, 0);
|
||||||
|
|
||||||
double minlat = (SynchMode == 0) ? LATENCY_MIN_TS : LATENCY_MIN;
|
double minlat = (SynchMode == 0) ? LATENCY_MIN_TS : LATENCY_MIN;
|
||||||
int minexp = (int)(pow(minlat + 1, 1.0 / 3.0) * 128.0);
|
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);
|
int maxexp = (int)(pow((double)LATENCY_MAX + 2, 1.0 / 3.0) * 128.0);
|
||||||
INIT_SLIDER(IDC_LATENCY_SLIDER, minexp, maxexp, 200, 42, 1);
|
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));
|
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);
|
swprintf_s(temp, L"%d ms (avg)", SndOutLatencyMS);
|
||||||
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
|
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
|
||||||
|
|
||||||
int configvol = (int)(FinalVolume * 100 + 0.5f);
|
int configvol = (int)(FinalVolume * 100 + 0.5f);
|
||||||
INIT_SLIDER(IDC_VOLUME_SLIDER, 0, 100, 10, 42, 1);
|
INIT_SLIDER(IDC_VOLUME_SLIDER, 0, 100, 10, 42, 1);
|
||||||
|
|
||||||
SendDialogMsg(hWnd, IDC_VOLUME_SLIDER, TBM_SETPOS, TRUE, configvol);
|
SendDialogMsg(hWnd, IDC_VOLUME_SLIDER, TBM_SETPOS, TRUE, configvol);
|
||||||
swprintf_s(temp, L"%d%%", configvol);
|
swprintf_s(temp, L"%d%%", configvol);
|
||||||
SetWindowText(GetDlgItem(hWnd, IDC_VOLUME_LABEL), temp);
|
SetWindowText(GetDlgItem(hWnd, IDC_VOLUME_LABEL), temp);
|
||||||
|
|
||||||
CheckOutputModule(hWnd);
|
CheckOutputModule(hWnd);
|
||||||
|
|
||||||
EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_SOUNDTOUCH), (SynchMode == 0));
|
EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_SOUNDTOUCH), (SynchMode == 0));
|
||||||
EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_DEBUG), DebugEnabled);
|
EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_DEBUG), DebugEnabled);
|
||||||
|
|
||||||
SET_CHECK(IDC_EFFECTS_DISABLE, EffectsDisabled);
|
SET_CHECK(IDC_EFFECTS_DISABLE, EffectsDisabled);
|
||||||
SET_CHECK(IDC_DEALIASFILTER, postprocess_filter_dealias);
|
SET_CHECK(IDC_DEALIASFILTER, postprocess_filter_dealias);
|
||||||
SET_CHECK(IDC_DEBUG_ENABLE, DebugEnabled);
|
SET_CHECK(IDC_DEBUG_ENABLE, DebugEnabled);
|
||||||
SET_CHECK(IDC_DSP_ENABLE, dspPluginEnabled);
|
SET_CHECK(IDC_DSP_ENABLE, dspPluginEnabled);
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WM_COMMAND:
|
case WM_COMMAND:
|
||||||
wmId = LOWORD(wParam);
|
wmId = LOWORD(wParam);
|
||||||
wmEvent = HIWORD(wParam);
|
wmEvent = HIWORD(wParam);
|
||||||
// Parse the menu selections:
|
// Parse the menu selections:
|
||||||
switch (wmId) {
|
switch (wmId)
|
||||||
case IDOK: {
|
{
|
||||||
double res = ((int)SendDialogMsg(hWnd, IDC_LATENCY_SLIDER, TBM_GETPOS, 0, 0)) / 128.0;
|
case IDOK:
|
||||||
SndOutLatencyMS = (int)pow(res, 3.0);
|
{
|
||||||
Clampify(SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX);
|
double res = ((int)SendDialogMsg(hWnd, IDC_LATENCY_SLIDER, TBM_GETPOS, 0, 0)) / 128.0;
|
||||||
FinalVolume = (float)(SendDialogMsg(hWnd, IDC_VOLUME_SLIDER, TBM_GETPOS, 0, 0)) / 100;
|
SndOutLatencyMS = (int)pow(res, 3.0);
|
||||||
Interpolation = (int)SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_GETCURSEL, 0, 0);
|
Clampify(SndOutLatencyMS, LATENCY_MIN, LATENCY_MAX);
|
||||||
OutputModule = (int)SendDialogMsg(hWnd, IDC_OUTPUT, CB_GETCURSEL, 0, 0);
|
FinalVolume = (float)(SendDialogMsg(hWnd, IDC_VOLUME_SLIDER, TBM_GETPOS, 0, 0)) / 100;
|
||||||
SynchMode = (int)SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_GETCURSEL, 0, 0);
|
Interpolation = (int)SendDialogMsg(hWnd, IDC_INTERPOLATE, CB_GETCURSEL, 0, 0);
|
||||||
numSpeakers = (int)SendDialogMsg(hWnd, IDC_SPEAKERS, 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();
|
WriteSettings();
|
||||||
EndDialog(hWnd, 0);
|
EndDialog(hWnd, 0);
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case IDCANCEL:
|
case IDCANCEL:
|
||||||
EndDialog(hWnd, 0);
|
EndDialog(hWnd, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IDC_OUTPUT:
|
case IDC_OUTPUT:
|
||||||
if (wmEvent == CBN_SELCHANGE) {
|
if (wmEvent == CBN_SELCHANGE)
|
||||||
CheckOutputModule(hWnd);
|
{
|
||||||
}
|
CheckOutputModule(hWnd);
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case IDC_OUTCONF: {
|
case IDC_OUTCONF:
|
||||||
const int module = (int)SendMessage(GetDlgItem(hWnd, IDC_OUTPUT), CB_GETCURSEL, 0, 0);
|
{
|
||||||
if (mods[module] == NULL)
|
const int module = (int)SendMessage(GetDlgItem(hWnd, IDC_OUTPUT), CB_GETCURSEL, 0, 0);
|
||||||
break;
|
if (mods[module] == NULL)
|
||||||
mods[module]->Configure((uptr)hWnd);
|
break;
|
||||||
} break;
|
mods[module]->Configure((uptr)hWnd);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case IDC_OPEN_CONFIG_DEBUG: {
|
case IDC_OPEN_CONFIG_DEBUG:
|
||||||
// Quick Hack -- DebugEnabled is re-loaded with the DebugConfig's API,
|
{
|
||||||
// so we need to override it here:
|
// Quick Hack -- DebugEnabled is re-loaded with the DebugConfig's API,
|
||||||
|
// so we need to override it here:
|
||||||
|
|
||||||
bool dbgtmp = DebugEnabled;
|
bool dbgtmp = DebugEnabled;
|
||||||
DebugConfig::OpenDialog();
|
DebugConfig::OpenDialog();
|
||||||
DebugEnabled = dbgtmp;
|
DebugEnabled = dbgtmp;
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case IDC_SYNCHMODE: {
|
case IDC_SYNCHMODE:
|
||||||
if (wmEvent == CBN_SELCHANGE) {
|
{
|
||||||
int sMode = (int)SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_GETCURSEL, 0, 0);
|
if (wmEvent == CBN_SELCHANGE)
|
||||||
double minlat = (sMode == 0) ? LATENCY_MIN_TS : LATENCY_MIN;
|
{
|
||||||
int minexp = (int)(pow(minlat + 1, 1.0 / 3.0) * 128.0);
|
int sMode = (int)SendDialogMsg(hWnd, IDC_SYNCHMODE, CB_GETCURSEL, 0, 0);
|
||||||
int maxexp = (int)(pow((double)LATENCY_MAX + 2, 1.0 / 3.0) * 128.0);
|
double minlat = (sMode == 0) ? LATENCY_MIN_TS : LATENCY_MIN;
|
||||||
INIT_SLIDER(IDC_LATENCY_SLIDER, minexp, maxexp, 200, 42, 1);
|
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);
|
int curpos = (int)SendMessage(GetDlgItem(hWnd, IDC_LATENCY_SLIDER), TBM_GETPOS, 0, 0);
|
||||||
double res = pow(curpos / 128.0, 3.0);
|
double res = pow(curpos / 128.0, 3.0);
|
||||||
curpos = (int)res;
|
curpos = (int)res;
|
||||||
swprintf_s(temp, L"%d ms (avg)", curpos);
|
swprintf_s(temp, L"%d ms (avg)", curpos);
|
||||||
SetDlgItemText(hWnd, IDC_LATENCY_LABEL, temp);
|
SetDlgItemText(hWnd, IDC_LATENCY_LABEL, temp);
|
||||||
bool soundtouch = sMode == 0;
|
bool soundtouch = sMode == 0;
|
||||||
EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_SOUNDTOUCH), soundtouch);
|
EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_SOUNDTOUCH), soundtouch);
|
||||||
}
|
}
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
case IDC_OPEN_CONFIG_SOUNDTOUCH:
|
case IDC_OPEN_CONFIG_SOUNDTOUCH:
|
||||||
SoundtouchCfg::OpenDialog(hWnd);
|
SoundtouchCfg::OpenDialog(hWnd);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
HANDLE_CHECK(IDC_EFFECTS_DISABLE, EffectsDisabled);
|
HANDLE_CHECK(IDC_EFFECTS_DISABLE, EffectsDisabled);
|
||||||
HANDLE_CHECK(IDC_DEALIASFILTER, postprocess_filter_dealias);
|
HANDLE_CHECK(IDC_DEALIASFILTER, postprocess_filter_dealias);
|
||||||
HANDLE_CHECK(IDC_DSP_ENABLE, dspPluginEnabled);
|
HANDLE_CHECK(IDC_DSP_ENABLE, dspPluginEnabled);
|
||||||
HANDLE_CHECKNB(IDC_DEBUG_ENABLE, DebugEnabled);
|
HANDLE_CHECKNB(IDC_DEBUG_ENABLE, DebugEnabled);
|
||||||
DebugConfig::EnableControls(hWnd);
|
DebugConfig::EnableControls(hWnd);
|
||||||
EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_DEBUG), DebugEnabled);
|
EnableWindow(GetDlgItem(hWnd, IDC_OPEN_CONFIG_DEBUG), DebugEnabled);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WM_HSCROLL: {
|
case WM_HSCROLL:
|
||||||
wmEvent = LOWORD(wParam);
|
{
|
||||||
HWND hwndDlg = (HWND)lParam;
|
wmEvent = LOWORD(wParam);
|
||||||
|
HWND hwndDlg = (HWND)lParam;
|
||||||
|
|
||||||
int curpos = HIWORD(wParam);
|
int curpos = HIWORD(wParam);
|
||||||
|
|
||||||
switch (wmEvent) {
|
switch (wmEvent)
|
||||||
case TB_LINEUP:
|
{
|
||||||
case TB_LINEDOWN:
|
case TB_LINEUP:
|
||||||
case TB_PAGEUP:
|
case TB_LINEDOWN:
|
||||||
case TB_PAGEDOWN:
|
case TB_PAGEUP:
|
||||||
case TB_TOP:
|
case TB_PAGEDOWN:
|
||||||
case TB_BOTTOM:
|
case TB_TOP:
|
||||||
curpos = (int)SendMessage(hwndDlg, TBM_GETPOS, 0, 0);
|
case TB_BOTTOM:
|
||||||
|
curpos = (int)SendMessage(hwndDlg, TBM_GETPOS, 0, 0);
|
||||||
|
|
||||||
case TB_THUMBPOSITION:
|
case TB_THUMBPOSITION:
|
||||||
case TB_THUMBTRACK:
|
case TB_THUMBTRACK:
|
||||||
Clampify(curpos,
|
Clampify(curpos,
|
||||||
(int)SendMessage(hwndDlg, TBM_GETRANGEMIN, 0, 0),
|
(int)SendMessage(hwndDlg, TBM_GETRANGEMIN, 0, 0),
|
||||||
(int)SendMessage(hwndDlg, TBM_GETRANGEMAX, 0, 0));
|
(int)SendMessage(hwndDlg, TBM_GETRANGEMAX, 0, 0));
|
||||||
|
|
||||||
SendMessage((HWND)lParam, TBM_SETPOS, TRUE, curpos);
|
SendMessage((HWND)lParam, TBM_SETPOS, TRUE, curpos);
|
||||||
|
|
||||||
if (hwndDlg == GetDlgItem(hWnd, IDC_LATENCY_SLIDER)) {
|
if (hwndDlg == GetDlgItem(hWnd, IDC_LATENCY_SLIDER))
|
||||||
double res = pow(curpos / 128.0, 3.0);
|
{
|
||||||
curpos = (int)res;
|
double res = pow(curpos / 128.0, 3.0);
|
||||||
swprintf_s(temp, L"%d ms (avg)", curpos);
|
curpos = (int)res;
|
||||||
SetDlgItemText(hWnd, IDC_LATENCY_LABEL, temp);
|
swprintf_s(temp, L"%d ms (avg)", curpos);
|
||||||
}
|
SetDlgItemText(hWnd, IDC_LATENCY_LABEL, temp);
|
||||||
|
}
|
||||||
|
|
||||||
if (hwndDlg == GetDlgItem(hWnd, IDC_VOLUME_SLIDER)) {
|
if (hwndDlg == GetDlgItem(hWnd, IDC_VOLUME_SLIDER))
|
||||||
swprintf_s(temp, L"%d%%", curpos);
|
{
|
||||||
SetDlgItemText(hWnd, IDC_VOLUME_LABEL, temp);
|
swprintf_s(temp, L"%d%%", curpos);
|
||||||
}
|
SetDlgItemText(hWnd, IDC_VOLUME_LABEL, temp);
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void configure()
|
void configure()
|
||||||
{
|
{
|
||||||
INT_PTR ret;
|
INT_PTR ret;
|
||||||
ReadSettings();
|
ReadSettings();
|
||||||
ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CONFIG), GetActiveWindow(), (DLGPROC)ConfigProc, 1);
|
ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CONFIG), GetActiveWindow(), (DLGPROC)ConfigProc, 1);
|
||||||
if (ret == -1) {
|
if (ret == -1)
|
||||||
MessageBox(GetActiveWindow(), L"Error Opening the config dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
|
{
|
||||||
return;
|
MessageBox(GetActiveWindow(), L"Error Opening the config dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
|
||||||
}
|
return;
|
||||||
ReadSettings();
|
}
|
||||||
|
ReadSettings();
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,213 +55,219 @@ wxString CoresDumpFileName;
|
||||||
wxString MemDumpFileName;
|
wxString MemDumpFileName;
|
||||||
wxString RegDumpFileName;
|
wxString RegDumpFileName;
|
||||||
|
|
||||||
void CfgSetLogDir(const char *dir)
|
void CfgSetLogDir(const char* dir)
|
||||||
{
|
{
|
||||||
LogsFolder = (dir == NULL) ? wxString(L"logs") : wxString(dir, wxConvFile);
|
LogsFolder = (dir == NULL) ? wxString(L"logs") : wxString(dir, wxConvFile);
|
||||||
DumpsFolder = (dir == NULL) ? wxString(L"logs") : wxString(dir, wxConvFile);
|
DumpsFolder = (dir == NULL) ? wxString(L"logs") : wxString(dir, wxConvFile);
|
||||||
LogLocationSetByPcsx2 = (dir != NULL);
|
LogLocationSetByPcsx2 = (dir != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *OpenBinaryLog(const wxString &logfile)
|
FILE* OpenBinaryLog(const wxString& logfile)
|
||||||
{
|
{
|
||||||
return wxFopen(Path::Combine(LogsFolder, logfile), L"wb");
|
return wxFopen(Path::Combine(LogsFolder, logfile), L"wb");
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *OpenLog(const wxString &logfile)
|
FILE* OpenLog(const wxString& logfile)
|
||||||
{
|
{
|
||||||
return wxFopen(Path::Combine(LogsFolder, logfile), L"w");
|
return wxFopen(Path::Combine(LogsFolder, logfile), L"w");
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *OpenDump(const wxString &logfile)
|
FILE* OpenDump(const wxString& logfile)
|
||||||
{
|
{
|
||||||
return wxFopen(Path::Combine(DumpsFolder, logfile), L"w");
|
return wxFopen(Path::Combine(DumpsFolder, logfile), L"w");
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace DebugConfig
|
namespace DebugConfig
|
||||||
{
|
{
|
||||||
|
|
||||||
static const wxChar *Section = L"DEBUG";
|
static const wxChar* Section = L"DEBUG";
|
||||||
|
|
||||||
void ReadSettings()
|
void ReadSettings()
|
||||||
{
|
{
|
||||||
DebugEnabled = CfgReadBool(Section, L"Global_Enable", 0);
|
DebugEnabled = CfgReadBool(Section, L"Global_Enable", 0);
|
||||||
_MsgToConsole = CfgReadBool(Section, L"Show_Messages", 0);
|
_MsgToConsole = CfgReadBool(Section, L"Show_Messages", 0);
|
||||||
_MsgKeyOnOff = CfgReadBool(Section, L"Show_Messages_Key_On_Off", 0);
|
_MsgKeyOnOff = CfgReadBool(Section, L"Show_Messages_Key_On_Off", 0);
|
||||||
_MsgVoiceOff = CfgReadBool(Section, L"Show_Messages_Voice_Off", 0);
|
_MsgVoiceOff = CfgReadBool(Section, L"Show_Messages_Voice_Off", 0);
|
||||||
_MsgDMA = CfgReadBool(Section, L"Show_Messages_DMA_Transfer", 0);
|
_MsgDMA = CfgReadBool(Section, L"Show_Messages_DMA_Transfer", 0);
|
||||||
_MsgAutoDMA = CfgReadBool(Section, L"Show_Messages_AutoDMA", 0);
|
_MsgAutoDMA = CfgReadBool(Section, L"Show_Messages_AutoDMA", 0);
|
||||||
_MsgOverruns = CfgReadBool(Section, L"Show_Messages_Overruns", 0);
|
_MsgOverruns = CfgReadBool(Section, L"Show_Messages_Overruns", 0);
|
||||||
_MsgCache = CfgReadBool(Section, L"Show_Messages_CacheStats", 0);
|
_MsgCache = CfgReadBool(Section, L"Show_Messages_CacheStats", 0);
|
||||||
|
|
||||||
_AccessLog = CfgReadBool(Section, L"Log_Register_Access", 0);
|
_AccessLog = CfgReadBool(Section, L"Log_Register_Access", 0);
|
||||||
_DMALog = CfgReadBool(Section, L"Log_DMA_Transfers", 0);
|
_DMALog = CfgReadBool(Section, L"Log_DMA_Transfers", 0);
|
||||||
_WaveLog = CfgReadBool(Section, L"Log_WAVE_Output", 0);
|
_WaveLog = CfgReadBool(Section, L"Log_WAVE_Output", 0);
|
||||||
|
|
||||||
_CoresDump = CfgReadBool(Section, L"Dump_Info", 0);
|
_CoresDump = CfgReadBool(Section, L"Dump_Info", 0);
|
||||||
_MemDump = CfgReadBool(Section, L"Dump_Memory", 0);
|
_MemDump = CfgReadBool(Section, L"Dump_Memory", 0);
|
||||||
_RegDump = CfgReadBool(Section, L"Dump_Regs", 0);
|
_RegDump = CfgReadBool(Section, L"Dump_Regs", 0);
|
||||||
|
|
||||||
_visual_debug_enabled = CfgReadBool(Section, L"Visual_Debug_Enabled", 0);
|
_visual_debug_enabled = CfgReadBool(Section, L"Visual_Debug_Enabled", 0);
|
||||||
|
|
||||||
CfgReadStr(Section, L"Logs_Folder", CfgLogsFolder, L"logs");
|
CfgReadStr(Section, L"Logs_Folder", CfgLogsFolder, L"logs");
|
||||||
CfgReadStr(Section, L"Dumps_Folder", CfgDumpsFolder, L"logs");
|
CfgReadStr(Section, L"Dumps_Folder", CfgDumpsFolder, L"logs");
|
||||||
|
|
||||||
CfgReadStr(Section, L"Access_Log_Filename", AccessLogFileName, L"SPU2Log.txt");
|
CfgReadStr(Section, L"Access_Log_Filename", AccessLogFileName, L"SPU2Log.txt");
|
||||||
CfgReadStr(Section, L"DMA4Log_Filename", DMA4LogFileName, L"SPU2dma4.dat");
|
CfgReadStr(Section, L"DMA4Log_Filename", DMA4LogFileName, L"SPU2dma4.dat");
|
||||||
CfgReadStr(Section, L"DMA7Log_Filename", DMA7LogFileName, L"SPU2dma7.dat");
|
CfgReadStr(Section, L"DMA7Log_Filename", DMA7LogFileName, L"SPU2dma7.dat");
|
||||||
|
|
||||||
CfgReadStr(Section, L"Info_Dump_Filename", CoresDumpFileName, L"SPU2Cores.txt");
|
CfgReadStr(Section, L"Info_Dump_Filename", CoresDumpFileName, L"SPU2Cores.txt");
|
||||||
CfgReadStr(Section, L"Mem_Dump_Filename", MemDumpFileName, L"SPU2mem.dat");
|
CfgReadStr(Section, L"Mem_Dump_Filename", MemDumpFileName, L"SPU2mem.dat");
|
||||||
CfgReadStr(Section, L"Reg_Dump_Filename", RegDumpFileName, L"SPU2regs.dat");
|
CfgReadStr(Section, L"Reg_Dump_Filename", RegDumpFileName, L"SPU2regs.dat");
|
||||||
|
|
||||||
if (!LogLocationSetByPcsx2) {
|
if (!LogLocationSetByPcsx2)
|
||||||
LogsFolder = CfgLogsFolder;
|
{
|
||||||
DumpsFolder = CfgLogsFolder;
|
LogsFolder = CfgLogsFolder;
|
||||||
}
|
DumpsFolder = CfgLogsFolder;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void WriteSettings()
|
void WriteSettings()
|
||||||
{
|
{
|
||||||
CfgWriteBool(Section, L"Global_Enable", DebugEnabled);
|
CfgWriteBool(Section, L"Global_Enable", DebugEnabled);
|
||||||
|
|
||||||
CfgWriteBool(Section, L"Show_Messages", _MsgToConsole);
|
CfgWriteBool(Section, L"Show_Messages", _MsgToConsole);
|
||||||
CfgWriteBool(Section, L"Show_Messages_Key_On_Off", _MsgKeyOnOff);
|
CfgWriteBool(Section, L"Show_Messages_Key_On_Off", _MsgKeyOnOff);
|
||||||
CfgWriteBool(Section, L"Show_Messages_Voice_Off", _MsgVoiceOff);
|
CfgWriteBool(Section, L"Show_Messages_Voice_Off", _MsgVoiceOff);
|
||||||
CfgWriteBool(Section, L"Show_Messages_DMA_Transfer", _MsgDMA);
|
CfgWriteBool(Section, L"Show_Messages_DMA_Transfer", _MsgDMA);
|
||||||
CfgWriteBool(Section, L"Show_Messages_AutoDMA", _MsgAutoDMA);
|
CfgWriteBool(Section, L"Show_Messages_AutoDMA", _MsgAutoDMA);
|
||||||
CfgWriteBool(Section, L"Show_Messages_Overruns", _MsgOverruns);
|
CfgWriteBool(Section, L"Show_Messages_Overruns", _MsgOverruns);
|
||||||
CfgWriteBool(Section, L"Show_Messages_CacheStats", _MsgCache);
|
CfgWriteBool(Section, L"Show_Messages_CacheStats", _MsgCache);
|
||||||
|
|
||||||
CfgWriteBool(Section, L"Log_Register_Access", _AccessLog);
|
CfgWriteBool(Section, L"Log_Register_Access", _AccessLog);
|
||||||
CfgWriteBool(Section, L"Log_DMA_Transfers", _DMALog);
|
CfgWriteBool(Section, L"Log_DMA_Transfers", _DMALog);
|
||||||
CfgWriteBool(Section, L"Log_WAVE_Output", _WaveLog);
|
CfgWriteBool(Section, L"Log_WAVE_Output", _WaveLog);
|
||||||
|
|
||||||
CfgWriteBool(Section, L"Dump_Info", _CoresDump);
|
CfgWriteBool(Section, L"Dump_Info", _CoresDump);
|
||||||
CfgWriteBool(Section, L"Dump_Memory", _MemDump);
|
CfgWriteBool(Section, L"Dump_Memory", _MemDump);
|
||||||
CfgWriteBool(Section, L"Dump_Regs", _RegDump);
|
CfgWriteBool(Section, L"Dump_Regs", _RegDump);
|
||||||
|
|
||||||
CfgWriteBool(Section, L"Visual_Debug_Enabled", _visual_debug_enabled);
|
CfgWriteBool(Section, L"Visual_Debug_Enabled", _visual_debug_enabled);
|
||||||
|
|
||||||
// None of the logs strings are changable via GUI, so no point in bothering to
|
// None of the logs strings are changable via GUI, so no point in bothering to
|
||||||
// write them back out.
|
// write them back out.
|
||||||
CfgWriteStr(Section, L"Logs_Folder", CfgLogsFolder);
|
CfgWriteStr(Section, L"Logs_Folder", CfgLogsFolder);
|
||||||
CfgWriteStr(Section, L"Dumps_Folder", CfgDumpsFolder);
|
CfgWriteStr(Section, L"Dumps_Folder", CfgDumpsFolder);
|
||||||
|
|
||||||
CfgWriteStr(Section, L"Access_Log_Filename", AccessLogFileName);
|
CfgWriteStr(Section, L"Access_Log_Filename", AccessLogFileName);
|
||||||
CfgWriteStr(Section, L"DMA4Log_Filename", DMA4LogFileName);
|
CfgWriteStr(Section, L"DMA4Log_Filename", DMA4LogFileName);
|
||||||
CfgWriteStr(Section, L"DMA7Log_Filename", DMA7LogFileName);
|
CfgWriteStr(Section, L"DMA7Log_Filename", DMA7LogFileName);
|
||||||
|
|
||||||
CfgWriteStr(Section, L"Info_Dump_Filename", CoresDumpFileName);
|
CfgWriteStr(Section, L"Info_Dump_Filename", CoresDumpFileName);
|
||||||
CfgWriteStr(Section, L"Mem_Dump_Filename", MemDumpFileName);
|
CfgWriteStr(Section, L"Mem_Dump_Filename", MemDumpFileName);
|
||||||
CfgWriteStr(Section, L"Reg_Dump_Filename", RegDumpFileName);
|
CfgWriteStr(Section, L"Reg_Dump_Filename", RegDumpFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void EnableMessages(HWND hWnd)
|
static void EnableMessages(HWND hWnd)
|
||||||
{
|
{
|
||||||
ENABLE_CONTROL(IDC_MSGSHOW, DebugEnabled);
|
ENABLE_CONTROL(IDC_MSGSHOW, DebugEnabled);
|
||||||
ENABLE_CONTROL(IDC_MSGKEY, MsgToConsole());
|
ENABLE_CONTROL(IDC_MSGKEY, MsgToConsole());
|
||||||
ENABLE_CONTROL(IDC_MSGVOICE, MsgToConsole());
|
ENABLE_CONTROL(IDC_MSGVOICE, MsgToConsole());
|
||||||
ENABLE_CONTROL(IDC_MSGDMA, MsgToConsole());
|
ENABLE_CONTROL(IDC_MSGDMA, MsgToConsole());
|
||||||
ENABLE_CONTROL(IDC_MSGADMA, MsgToConsole());
|
ENABLE_CONTROL(IDC_MSGADMA, MsgToConsole());
|
||||||
ENABLE_CONTROL(IDC_DBG_OVERRUNS, MsgToConsole());
|
ENABLE_CONTROL(IDC_DBG_OVERRUNS, MsgToConsole());
|
||||||
ENABLE_CONTROL(IDC_DBG_CACHE, MsgToConsole());
|
ENABLE_CONTROL(IDC_DBG_CACHE, MsgToConsole());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnableControls(HWND hWnd)
|
void EnableControls(HWND hWnd)
|
||||||
{
|
{
|
||||||
EnableMessages(hWnd);
|
EnableMessages(hWnd);
|
||||||
ENABLE_CONTROL(IDC_LOGDMA, DebugEnabled);
|
ENABLE_CONTROL(IDC_LOGDMA, DebugEnabled);
|
||||||
ENABLE_CONTROL(IDC_LOGREGS, IsDevBuild ? DebugEnabled : false);
|
ENABLE_CONTROL(IDC_LOGREGS, IsDevBuild ? DebugEnabled : false);
|
||||||
ENABLE_CONTROL(IDC_LOGWAVE, IsDevBuild ? DebugEnabled : false);
|
ENABLE_CONTROL(IDC_LOGWAVE, IsDevBuild ? DebugEnabled : false);
|
||||||
ENABLE_CONTROL(IDC_DUMPCORE, DebugEnabled);
|
ENABLE_CONTROL(IDC_DUMPCORE, DebugEnabled);
|
||||||
ENABLE_CONTROL(IDC_DUMPMEM, DebugEnabled);
|
ENABLE_CONTROL(IDC_DUMPMEM, DebugEnabled);
|
||||||
ENABLE_CONTROL(IDC_DUMPREGS, DebugEnabled);
|
ENABLE_CONTROL(IDC_DUMPREGS, DebugEnabled);
|
||||||
ENABLE_CONTROL(IDC_DEBUG_VISUAL, IsDevBuild ? DebugEnabled : false);
|
ENABLE_CONTROL(IDC_DEBUG_VISUAL, IsDevBuild ? DebugEnabled : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
static BOOL CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
int wmId;
|
int wmId;
|
||||||
//wchar_t temp[384]={0};
|
//wchar_t temp[384]={0};
|
||||||
|
|
||||||
switch (uMsg) {
|
switch (uMsg)
|
||||||
case WM_PAINT:
|
{
|
||||||
return FALSE;
|
case WM_PAINT:
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
case WM_INITDIALOG: {
|
case WM_INITDIALOG:
|
||||||
EnableControls(hWnd);
|
{
|
||||||
|
EnableControls(hWnd);
|
||||||
|
|
||||||
// Debugging / Logging Flags:
|
// Debugging / Logging Flags:
|
||||||
SET_CHECK(IDC_DEBUG, DebugEnabled);
|
SET_CHECK(IDC_DEBUG, DebugEnabled);
|
||||||
SET_CHECK(IDC_MSGSHOW, _MsgToConsole);
|
SET_CHECK(IDC_MSGSHOW, _MsgToConsole);
|
||||||
SET_CHECK(IDC_MSGKEY, _MsgKeyOnOff);
|
SET_CHECK(IDC_MSGKEY, _MsgKeyOnOff);
|
||||||
SET_CHECK(IDC_MSGVOICE, _MsgVoiceOff);
|
SET_CHECK(IDC_MSGVOICE, _MsgVoiceOff);
|
||||||
SET_CHECK(IDC_MSGDMA, _MsgDMA);
|
SET_CHECK(IDC_MSGDMA, _MsgDMA);
|
||||||
SET_CHECK(IDC_MSGADMA, _MsgAutoDMA);
|
SET_CHECK(IDC_MSGADMA, _MsgAutoDMA);
|
||||||
SET_CHECK(IDC_DBG_OVERRUNS, _MsgOverruns);
|
SET_CHECK(IDC_DBG_OVERRUNS, _MsgOverruns);
|
||||||
SET_CHECK(IDC_DBG_CACHE, _MsgCache);
|
SET_CHECK(IDC_DBG_CACHE, _MsgCache);
|
||||||
SET_CHECK(IDC_LOGREGS, _AccessLog);
|
SET_CHECK(IDC_LOGREGS, _AccessLog);
|
||||||
SET_CHECK(IDC_LOGDMA, _DMALog);
|
SET_CHECK(IDC_LOGDMA, _DMALog);
|
||||||
SET_CHECK(IDC_LOGWAVE, _WaveLog);
|
SET_CHECK(IDC_LOGWAVE, _WaveLog);
|
||||||
SET_CHECK(IDC_DUMPCORE, _CoresDump);
|
SET_CHECK(IDC_DUMPCORE, _CoresDump);
|
||||||
SET_CHECK(IDC_DUMPMEM, _MemDump);
|
SET_CHECK(IDC_DUMPMEM, _MemDump);
|
||||||
SET_CHECK(IDC_DUMPREGS, _RegDump);
|
SET_CHECK(IDC_DUMPREGS, _RegDump);
|
||||||
SET_CHECK(IDC_DEBUG_VISUAL, _visual_debug_enabled);
|
SET_CHECK(IDC_DEBUG_VISUAL, _visual_debug_enabled);
|
||||||
|
|
||||||
ShowWindow(GetDlgItem(hWnd, IDC_MSG_PUBLIC_BUILD), !IsDevBuild);
|
ShowWindow(GetDlgItem(hWnd, IDC_MSG_PUBLIC_BUILD), !IsDevBuild);
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WM_COMMAND:
|
case WM_COMMAND:
|
||||||
wmId = LOWORD(wParam);
|
wmId = LOWORD(wParam);
|
||||||
// Parse the menu selections:
|
// Parse the menu selections:
|
||||||
switch (wmId) {
|
switch (wmId)
|
||||||
case IDOK:
|
{
|
||||||
WriteSettings();
|
case IDOK:
|
||||||
EndDialog(hWnd, 0);
|
WriteSettings();
|
||||||
break;
|
EndDialog(hWnd, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
case IDCANCEL:
|
case IDCANCEL:
|
||||||
EndDialog(hWnd, 0);
|
EndDialog(hWnd, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
HANDLE_CHECKNB(IDC_MSGSHOW, _MsgToConsole);
|
HANDLE_CHECKNB(IDC_MSGSHOW, _MsgToConsole);
|
||||||
EnableMessages(hWnd);
|
EnableMessages(hWnd);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
HANDLE_CHECK(IDC_MSGKEY, _MsgKeyOnOff);
|
HANDLE_CHECK(IDC_MSGKEY, _MsgKeyOnOff);
|
||||||
HANDLE_CHECK(IDC_MSGVOICE, _MsgVoiceOff);
|
HANDLE_CHECK(IDC_MSGVOICE, _MsgVoiceOff);
|
||||||
HANDLE_CHECK(IDC_MSGDMA, _MsgDMA);
|
HANDLE_CHECK(IDC_MSGDMA, _MsgDMA);
|
||||||
HANDLE_CHECK(IDC_MSGADMA, _MsgAutoDMA);
|
HANDLE_CHECK(IDC_MSGADMA, _MsgAutoDMA);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
HANDLE_CHECK(IDC_DBG_OVERRUNS, _MsgOverruns);
|
HANDLE_CHECK(IDC_DBG_OVERRUNS, _MsgOverruns);
|
||||||
HANDLE_CHECK(IDC_DBG_CACHE, _MsgCache);
|
HANDLE_CHECK(IDC_DBG_CACHE, _MsgCache);
|
||||||
HANDLE_CHECK(IDC_LOGREGS, _AccessLog);
|
HANDLE_CHECK(IDC_LOGREGS, _AccessLog);
|
||||||
HANDLE_CHECK(IDC_LOGDMA, _DMALog);
|
HANDLE_CHECK(IDC_LOGDMA, _DMALog);
|
||||||
HANDLE_CHECK(IDC_LOGWAVE, _WaveLog);
|
HANDLE_CHECK(IDC_LOGWAVE, _WaveLog);
|
||||||
HANDLE_CHECK(IDC_DUMPCORE, _CoresDump);
|
HANDLE_CHECK(IDC_DUMPCORE, _CoresDump);
|
||||||
HANDLE_CHECK(IDC_DUMPMEM, _MemDump);
|
HANDLE_CHECK(IDC_DUMPMEM, _MemDump);
|
||||||
HANDLE_CHECK(IDC_DUMPREGS, _RegDump);
|
HANDLE_CHECK(IDC_DUMPREGS, _RegDump);
|
||||||
HANDLE_CHECK(IDC_DEBUG_VISUAL, _visual_debug_enabled);
|
HANDLE_CHECK(IDC_DEBUG_VISUAL, _visual_debug_enabled);
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenDialog()
|
void OpenDialog()
|
||||||
{
|
{
|
||||||
INT_PTR ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CONFIG_DEBUG), GetActiveWindow(), (DLGPROC)DialogProc, 1);
|
INT_PTR ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CONFIG_DEBUG), GetActiveWindow(), (DLGPROC)DialogProc, 1);
|
||||||
if (ret == -1) {
|
if (ret == -1)
|
||||||
MessageBox(GetActiveWindow(), L"Error Opening the debug configuration dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
|
{
|
||||||
return;
|
MessageBox(GetActiveWindow(), L"Error Opening the debug configuration dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
|
||||||
}
|
return;
|
||||||
ReadSettings();
|
}
|
||||||
}
|
ReadSettings();
|
||||||
}
|
}
|
||||||
|
} // namespace DebugConfig
|
||||||
|
|
|
@ -32,98 +32,106 @@ static const int SeekWindow_Max = 30;
|
||||||
static const int Overlap_Min = 5;
|
static const int Overlap_Min = 5;
|
||||||
static const int Overlap_Max = 15;
|
static const int Overlap_Max = 15;
|
||||||
|
|
||||||
void SoundtouchCfg::ApplySettings(soundtouch::SoundTouch &sndtouch)
|
void SoundtouchCfg::ApplySettings(soundtouch::SoundTouch& sndtouch)
|
||||||
{
|
{
|
||||||
sndtouch.setSetting(SETTING_SEQUENCE_MS, SequenceLenMS);
|
sndtouch.setSetting(SETTING_SEQUENCE_MS, SequenceLenMS);
|
||||||
sndtouch.setSetting(SETTING_SEEKWINDOW_MS, SeekWindowMS);
|
sndtouch.setSetting(SETTING_SEEKWINDOW_MS, SeekWindowMS);
|
||||||
sndtouch.setSetting(SETTING_OVERLAP_MS, OverlapMS);
|
sndtouch.setSetting(SETTING_OVERLAP_MS, OverlapMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ClampValues()
|
static void ClampValues()
|
||||||
{
|
{
|
||||||
Clampify(SequenceLenMS, SequenceLen_Min, SequenceLen_Max);
|
Clampify(SequenceLenMS, SequenceLen_Min, SequenceLen_Max);
|
||||||
Clampify(SeekWindowMS, SeekWindow_Min, SeekWindow_Max);
|
Clampify(SeekWindowMS, SeekWindow_Min, SeekWindow_Max);
|
||||||
Clampify(OverlapMS, Overlap_Min, Overlap_Max);
|
Clampify(OverlapMS, Overlap_Min, Overlap_Max);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundtouchCfg::ReadSettings()
|
void SoundtouchCfg::ReadSettings()
|
||||||
{
|
{
|
||||||
SequenceLenMS = CfgReadInt(L"SOUNDTOUCH", L"SequenceLengthMS", 30);
|
SequenceLenMS = CfgReadInt(L"SOUNDTOUCH", L"SequenceLengthMS", 30);
|
||||||
SeekWindowMS = CfgReadInt(L"SOUNDTOUCH", L"SeekWindowMS", 20);
|
SeekWindowMS = CfgReadInt(L"SOUNDTOUCH", L"SeekWindowMS", 20);
|
||||||
OverlapMS = CfgReadInt(L"SOUNDTOUCH", L"OverlapMS", 10);
|
OverlapMS = CfgReadInt(L"SOUNDTOUCH", L"OverlapMS", 10);
|
||||||
|
|
||||||
ClampValues();
|
ClampValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundtouchCfg::WriteSettings()
|
void SoundtouchCfg::WriteSettings()
|
||||||
{
|
{
|
||||||
CfgWriteInt(L"SOUNDTOUCH", L"SequenceLengthMS", SequenceLenMS);
|
CfgWriteInt(L"SOUNDTOUCH", L"SequenceLengthMS", SequenceLenMS);
|
||||||
CfgWriteInt(L"SOUNDTOUCH", L"SeekWindowMS", SeekWindowMS);
|
CfgWriteInt(L"SOUNDTOUCH", L"SeekWindowMS", SeekWindowMS);
|
||||||
CfgWriteInt(L"SOUNDTOUCH", L"OverlapMS", OverlapMS);
|
CfgWriteInt(L"SOUNDTOUCH", L"OverlapMS", OverlapMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL CALLBACK SoundtouchCfg::DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
BOOL CALLBACK SoundtouchCfg::DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
int wmId;
|
int wmId;
|
||||||
//wchar_t temp[384]={0};
|
//wchar_t temp[384]={0};
|
||||||
|
|
||||||
switch (uMsg) {
|
switch (uMsg)
|
||||||
case WM_PAINT:
|
{
|
||||||
return FALSE;
|
case WM_PAINT:
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
case WM_INITDIALOG: {
|
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_SEQLEN_SLIDER, SequenceLen_Min, SequenceLen_Max, 20, 5, 1);
|
||||||
INIT_SLIDER(IDC_OVERLAP_SLIDER, Overlap_Min, Overlap_Max, 3, 2, 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_SEQLEN_SLIDER, TBM_SETPOS, TRUE, SequenceLenMS);
|
||||||
SendDialogMsg(hWnd, IDC_SEEKWIN_SLIDER, TBM_SETPOS, TRUE, SeekWindowMS);
|
SendDialogMsg(hWnd, IDC_SEEKWIN_SLIDER, TBM_SETPOS, TRUE, SeekWindowMS);
|
||||||
SendDialogMsg(hWnd, IDC_OVERLAP_SLIDER, TBM_SETPOS, TRUE, OverlapMS);
|
SendDialogMsg(hWnd, IDC_OVERLAP_SLIDER, TBM_SETPOS, TRUE, OverlapMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
case WM_COMMAND:
|
case WM_COMMAND:
|
||||||
wmId = LOWORD(wParam);
|
wmId = LOWORD(wParam);
|
||||||
// Parse the menu selections:
|
// Parse the menu selections:
|
||||||
if (wmId == IDOK) {
|
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);
|
SequenceLenMS = (int)SendDialogMsg(hWnd, IDC_SEQLEN_SLIDER, TBM_GETPOS, 0, 0);
|
||||||
OverlapMS = (int)SendDialogMsg(hWnd, IDC_OVERLAP_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();
|
ClampValues();
|
||||||
WriteSettings();
|
WriteSettings();
|
||||||
EndDialog(hWnd, 0);
|
EndDialog(hWnd, 0);
|
||||||
} else if (wmId == IDC_RESET_DEFAULTS) {
|
}
|
||||||
SequenceLenMS = 30;
|
else if (wmId == IDC_RESET_DEFAULTS)
|
||||||
SeekWindowMS = 20;
|
{
|
||||||
OverlapMS = 10;
|
SequenceLenMS = 30;
|
||||||
|
SeekWindowMS = 20;
|
||||||
|
OverlapMS = 10;
|
||||||
|
|
||||||
SendDialogMsg(hWnd, IDC_SEQLEN_SLIDER, TBM_SETPOS, TRUE, SequenceLenMS);
|
SendDialogMsg(hWnd, IDC_SEQLEN_SLIDER, TBM_SETPOS, TRUE, SequenceLenMS);
|
||||||
SendDialogMsg(hWnd, IDC_SEEKWIN_SLIDER, TBM_SETPOS, TRUE, SeekWindowMS);
|
SendDialogMsg(hWnd, IDC_SEEKWIN_SLIDER, TBM_SETPOS, TRUE, SeekWindowMS);
|
||||||
SendDialogMsg(hWnd, IDC_OVERLAP_SLIDER, TBM_SETPOS, TRUE, OverlapMS);
|
SendDialogMsg(hWnd, IDC_OVERLAP_SLIDER, TBM_SETPOS, TRUE, OverlapMS);
|
||||||
|
|
||||||
AssignSliderValue((HWND)lParam, hWnd, SequenceLenMS);
|
AssignSliderValue((HWND)lParam, hWnd, SequenceLenMS);
|
||||||
} else if (wmId == IDCANCEL) {
|
}
|
||||||
EndDialog(hWnd, 0);
|
else if (wmId == IDCANCEL)
|
||||||
}
|
{
|
||||||
break;
|
EndDialog(hWnd, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WM_HSCROLL:
|
case WM_HSCROLL:
|
||||||
DoHandleScrollMessage(hWnd, wParam, lParam);
|
DoHandleScrollMessage(hWnd, wParam, lParam);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundtouchCfg::OpenDialog(HWND hWnd)
|
void SoundtouchCfg::OpenDialog(HWND hWnd)
|
||||||
{
|
{
|
||||||
INT_PTR ret;
|
INT_PTR ret;
|
||||||
ret = DialogBox(hInstance, MAKEINTRESOURCE(IDD_CONFIG_SOUNDTOUCH), hWnd, (DLGPROC)DialogProc);
|
ret = DialogBox(hInstance, MAKEINTRESOURCE(IDD_CONFIG_SOUNDTOUCH), hWnd, (DLGPROC)DialogProc);
|
||||||
if (ret == -1) {
|
if (ret == -1)
|
||||||
MessageBox(GetActiveWindow(), L"Error Opening the Soundtouch advanced dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
|
{
|
||||||
return;
|
MessageBox(GetActiveWindow(), L"Error Opening the Soundtouch advanced dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
|
||||||
}
|
return;
|
||||||
ReadSettings();
|
}
|
||||||
|
ReadSettings();
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,19 +23,19 @@
|
||||||
|
|
||||||
namespace DebugConfig
|
namespace DebugConfig
|
||||||
{
|
{
|
||||||
extern void ReadSettings();
|
extern void ReadSettings();
|
||||||
extern void WriteSettings();
|
extern void WriteSettings();
|
||||||
extern void OpenDialog();
|
extern void OpenDialog();
|
||||||
extern void EnableControls(HWND hWnd);
|
extern void EnableControls(HWND hWnd);
|
||||||
}
|
} // namespace DebugConfig
|
||||||
|
|
||||||
namespace SoundtouchCfg
|
namespace SoundtouchCfg
|
||||||
{
|
{
|
||||||
extern void ReadSettings();
|
extern void ReadSettings();
|
||||||
extern void WriteSettings();
|
extern void WriteSettings();
|
||||||
extern void OpenDialog(HWND hWnd);
|
extern void OpenDialog(HWND hWnd);
|
||||||
extern BOOL CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
extern BOOL CALLBACK DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||||
}
|
} // namespace SoundtouchCfg
|
||||||
|
|
||||||
extern int SendDialogMsg(HWND hwnd, int dlgId, UINT code, WPARAM wParam, LPARAM lParam);
|
extern int SendDialogMsg(HWND hwnd, int dlgId, UINT code, WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
|
@ -44,31 +44,31 @@ extern void AssignSliderValue(HWND hWnd, int idc, int editbox, int value);
|
||||||
extern int GetSliderValue(HWND hWnd, int idc);
|
extern int GetSliderValue(HWND hWnd, int idc);
|
||||||
extern BOOL DoHandleScrollMessage(HWND hwndDisplay, WPARAM wParam, LPARAM lParam);
|
extern BOOL DoHandleScrollMessage(HWND hwndDisplay, WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
extern void CfgSetSettingsDir(const char *dir);
|
extern void CfgSetSettingsDir(const char* dir);
|
||||||
extern void CfgSetLogDir(const char *dir);
|
extern void CfgSetLogDir(const char* dir);
|
||||||
|
|
||||||
extern bool CfgFindName(const TCHAR *Section, const TCHAR *Name);
|
extern bool CfgFindName(const TCHAR* Section, const TCHAR* Name);
|
||||||
|
|
||||||
extern void CfgWriteBool(const TCHAR *Section, const TCHAR *Name, bool Value);
|
extern void CfgWriteBool(const TCHAR* Section, const TCHAR* Name, bool Value);
|
||||||
extern void CfgWriteInt(const TCHAR *Section, const TCHAR *Name, int 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 CfgWriteFloat(const TCHAR* Section, const TCHAR* Name, float Value);
|
||||||
extern void CfgWriteStr(const TCHAR *Section, const TCHAR *Name, const wxString &Data);
|
extern void CfgWriteStr(const TCHAR* Section, const TCHAR* Name, const wxString& Data);
|
||||||
|
|
||||||
extern bool CfgReadBool(const TCHAR *Section, const TCHAR *Name, bool Default);
|
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, wxString& Data, const TCHAR* Default);
|
||||||
extern void CfgReadStr(const TCHAR *Section, const TCHAR *Name, TCHAR *Data, int DataSize, 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 int CfgReadInt(const TCHAR* Section, const TCHAR* Name, int Default);
|
||||||
extern float CfgReadFloat(const TCHAR *Section, const TCHAR *Name, float Default);
|
extern float CfgReadFloat(const TCHAR* Section, const TCHAR* Name, float Default);
|
||||||
|
|
||||||
// Items Specific to DirectSound
|
// Items Specific to DirectSound
|
||||||
#define STRFY(x) #x
|
#define STRFY(x) #x
|
||||||
#define verifyc(x) Verifyc(x, STRFY(x))
|
#define verifyc(x) Verifyc(x, STRFY(x))
|
||||||
|
|
||||||
extern void Verifyc(HRESULT hr, const char *fn);
|
extern void Verifyc(HRESULT hr, const char* fn);
|
||||||
|
|
||||||
struct ds_device_data
|
struct ds_device_data
|
||||||
{
|
{
|
||||||
wxString name;
|
wxString name;
|
||||||
GUID guid;
|
GUID guid;
|
||||||
bool hasGuid;
|
bool hasGuid;
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,24 +23,24 @@ HWND hDebugDialog = NULL;
|
||||||
|
|
||||||
int FillRectangle(HDC dc, int left, int top, int width, int height)
|
int FillRectangle(HDC dc, int left, int top, int width, int height)
|
||||||
{
|
{
|
||||||
RECT r = {left, top, left + width, top + height};
|
RECT r = {left, top, left + width, top + height};
|
||||||
|
|
||||||
return FillRect(dc, &r, (HBRUSH)GetStockObject(DC_BRUSH));
|
return FillRect(dc, &r, (HBRUSH)GetStockObject(DC_BRUSH));
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL DrawRectangle(HDC dc, int left, int top, int width, int height)
|
BOOL DrawRectangle(HDC dc, int left, int top, int width, int height)
|
||||||
{
|
{
|
||||||
RECT r = {left, top, left + width, top + height};
|
RECT r = {left, top, left + width, top + height};
|
||||||
|
|
||||||
POINT p[5] = {
|
POINT p[5] = {
|
||||||
{r.left, r.top},
|
{r.left, r.top},
|
||||||
{r.right, r.top},
|
{r.right, r.top},
|
||||||
{r.right, r.bottom},
|
{r.right, r.bottom},
|
||||||
{r.left, r.bottom},
|
{r.left, r.bottom},
|
||||||
{r.left, r.top},
|
{r.left, r.top},
|
||||||
};
|
};
|
||||||
|
|
||||||
return Polyline(dc, p, 5);
|
return Polyline(dc, p, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,46 +48,52 @@ HFONT hf = NULL;
|
||||||
int lCount = 0;
|
int lCount = 0;
|
||||||
void UpdateDebugDialog()
|
void UpdateDebugDialog()
|
||||||
{
|
{
|
||||||
if (!debugDialogOpen)
|
if (!debugDialogOpen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
lCount++;
|
lCount++;
|
||||||
if (lCount >= (SampleRate / 100)) // Increase to SampleRate/200 for smooth display.
|
if (lCount >= (SampleRate / 100)) // Increase to SampleRate/200 for smooth display.
|
||||||
{
|
{
|
||||||
HDC hdc = GetDC(hDebugDialog);
|
HDC hdc = GetDC(hDebugDialog);
|
||||||
|
|
||||||
if (!hf) {
|
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");
|
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, hf);
|
||||||
SelectObject(hdc, GetStockObject(DC_BRUSH));
|
SelectObject(hdc, GetStockObject(DC_BRUSH));
|
||||||
SelectObject(hdc, GetStockObject(DC_PEN));
|
SelectObject(hdc, GetStockObject(DC_PEN));
|
||||||
|
|
||||||
for (int c = 0; c < 2; c++) {
|
for (int c = 0; c < 2; c++)
|
||||||
V_Core &cx(Cores[c]);
|
{
|
||||||
V_CoreDebug &cd(DebugCores[c]);
|
V_Core& cx(Cores[c]);
|
||||||
|
V_CoreDebug& cd(DebugCores[c]);
|
||||||
|
|
||||||
for (int v = 0; v < 24; v++) {
|
for (int v = 0; v < 24; v++)
|
||||||
int cc = c * 2 + (v / 12);
|
{
|
||||||
int vv = v % 12;
|
int cc = c * 2 + (v / 12);
|
||||||
int IX = 8 + 128 * cc;
|
int vv = v % 12;
|
||||||
int IY = 8 + 48 * vv;
|
int IX = 8 + 128 * cc;
|
||||||
V_Voice &vc(cx.Voices[v]);
|
int IY = 8 + 48 * vv;
|
||||||
V_VoiceDebug &vcd(cd.Voices[v]);
|
V_Voice& vc(cx.Voices[v]);
|
||||||
|
V_VoiceDebug& vcd(cd.Voices[v]);
|
||||||
|
|
||||||
SetDCBrushColor(hdc, RGB(0, 0, 0));
|
SetDCBrushColor(hdc, RGB(0, 0, 0));
|
||||||
if ((vc.ADSR.Phase > 0) && (vc.ADSR.Phase < 6)) {
|
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, 0, 128)); // light blue for playing voice
|
||||||
SetDCBrushColor(hdc, RGB(0, 128, 0)); // light green for playing voice with modulation enabled
|
if (vc.Modulated)
|
||||||
}
|
{
|
||||||
if (vc.Noise) {
|
SetDCBrushColor(hdc, RGB(0, 128, 0)); // light green for playing voice with modulation enabled
|
||||||
SetDCBrushColor(hdc, RGB(128, 0, 0)); // light red for playing voice with noise enabled
|
}
|
||||||
}
|
if (vc.Noise)
|
||||||
}
|
{
|
||||||
/*
|
SetDCBrushColor(hdc, RGB(128, 0, 0)); // light red for playing voice with noise enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(vcd.lastStopReason==1)
|
if(vcd.lastStopReason==1)
|
||||||
|
@ -100,168 +106,181 @@ void UpdateDebugDialog()
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
FillRectangle(hdc, IX, IY, 124, 46);
|
FillRectangle(hdc, IX, IY, 124, 46);
|
||||||
|
|
||||||
SetDCPenColor(hdc, RGB(255, 128, 32));
|
SetDCPenColor(hdc, RGB(255, 128, 32));
|
||||||
|
|
||||||
DrawRectangle(hdc, IX, IY, 124, 46);
|
DrawRectangle(hdc, IX, IY, 124, 46);
|
||||||
|
|
||||||
SetDCBrushColor(hdc, RGB(0, 255, 0));
|
SetDCBrushColor(hdc, RGB(0, 255, 0));
|
||||||
|
|
||||||
int vl = abs(((vc.Volume.Left.Value >> 16) * 38) >> 15);
|
int vl = abs(((vc.Volume.Left.Value >> 16) * 38) >> 15);
|
||||||
int vr = abs(((vc.Volume.Right.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 + 58, IY + 42 - vl, 4, vl);
|
||||||
FillRectangle(hdc, IX + 62, IY + 42 - vr, 4, vr);
|
FillRectangle(hdc, IX + 62, IY + 42 - vr, 4, vr);
|
||||||
|
|
||||||
int adsr = ((vc.ADSR.Value >> 16) * 38) / 32768;
|
int adsr = ((vc.ADSR.Value >> 16) * 38) / 32768;
|
||||||
|
|
||||||
FillRectangle(hdc, IX + 66, IY + 42 - adsr, 4, adsr);
|
FillRectangle(hdc, IX + 66, IY + 42 - adsr, 4, adsr);
|
||||||
|
|
||||||
int peak = (vcd.displayPeak * 38) / 32768;
|
int peak = (vcd.displayPeak * 38) / 32768;
|
||||||
|
|
||||||
if (vcd.displayPeak >= 32700) // leave a little bit of margin
|
if (vcd.displayPeak >= 32700) // leave a little bit of margin
|
||||||
{
|
{
|
||||||
SetDCBrushColor(hdc, RGB(255, 0, 0));
|
SetDCBrushColor(hdc, RGB(255, 0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
FillRectangle(hdc, IX + 70, IY + 42 - peak, 4, peak);
|
FillRectangle(hdc, IX + 70, IY + 42 - peak, 4, peak);
|
||||||
|
|
||||||
if (vc.ADSR.Value > 0) {
|
if (vc.ADSR.Value > 0)
|
||||||
if (vc.SBuffer)
|
{
|
||||||
for (int i = 0; i < 28; i++) {
|
if (vc.SBuffer)
|
||||||
int val = ((int)vc.SBuffer[i] * 20) / 32768;
|
for (int i = 0; i < 28; i++)
|
||||||
|
{
|
||||||
|
int val = ((int)vc.SBuffer[i] * 20) / 32768;
|
||||||
|
|
||||||
int y = 0;
|
int y = 0;
|
||||||
|
|
||||||
if (val > 0) {
|
if (val > 0)
|
||||||
y = val;
|
{
|
||||||
} else
|
y = val;
|
||||||
val = -val;
|
}
|
||||||
|
else
|
||||||
|
val = -val;
|
||||||
|
|
||||||
if (val != 0) {
|
if (val != 0)
|
||||||
FillRectangle(hdc, IX + 90 + i, IY + 24 - y, 1, val);
|
{
|
||||||
}
|
FillRectangle(hdc, IX + 90 + i, IY + 24 - y, 1, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SetTextColor(hdc, RGB(0, 255, 0));
|
SetTextColor(hdc, RGB(0, 255, 0));
|
||||||
SetBkColor(hdc, RGB(0, 0, 0));
|
SetBkColor(hdc, RGB(0, 0, 0));
|
||||||
|
|
||||||
static wchar_t t[256];
|
static wchar_t t[256];
|
||||||
|
|
||||||
swprintf_s(t, L"%06x", vc.StartA);
|
swprintf_s(t, L"%06x", vc.StartA);
|
||||||
TextOut(hdc, IX + 4, IY + 4, t, 6);
|
TextOut(hdc, IX + 4, IY + 4, t, 6);
|
||||||
|
|
||||||
swprintf_s(t, L"%06x", vc.NextA);
|
swprintf_s(t, L"%06x", vc.NextA);
|
||||||
TextOut(hdc, IX + 4, IY + 18, t, 6);
|
TextOut(hdc, IX + 4, IY + 18, t, 6);
|
||||||
|
|
||||||
swprintf_s(t, L"%06x", vc.LoopStartA);
|
swprintf_s(t, L"%06x", vc.LoopStartA);
|
||||||
TextOut(hdc, IX + 4, IY + 32, t, 6);
|
TextOut(hdc, IX + 4, IY + 32, t, 6);
|
||||||
|
|
||||||
vcd.displayPeak = 0;
|
vcd.displayPeak = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// top now: 400
|
// top now: 400
|
||||||
int JX = 8 + c * 256;
|
int JX = 8 + c * 256;
|
||||||
int JY = 584;
|
int JY = 584;
|
||||||
|
|
||||||
SetDCBrushColor(hdc, RGB(0, 0, 0));
|
SetDCBrushColor(hdc, RGB(0, 0, 0));
|
||||||
SetDCPenColor(hdc, RGB(255, 128, 32));
|
SetDCPenColor(hdc, RGB(255, 128, 32));
|
||||||
|
|
||||||
FillRectangle(hdc, JX, JY, 252, 60);
|
FillRectangle(hdc, JX, JY, 252, 60);
|
||||||
DrawRectangle(hdc, JX, JY, 252, 60);
|
DrawRectangle(hdc, JX, JY, 252, 60);
|
||||||
|
|
||||||
SetTextColor(hdc, RGB(255, 255, 255));
|
SetTextColor(hdc, RGB(255, 255, 255));
|
||||||
SetBkColor(hdc, RGB(0, 0, 0));
|
SetBkColor(hdc, RGB(0, 0, 0));
|
||||||
|
|
||||||
static wchar_t t[256];
|
static wchar_t t[256];
|
||||||
TextOut(hdc, JX + 4, JY + 4, L"REVB", 4);
|
TextOut(hdc, JX + 4, JY + 4, L"REVB", 4);
|
||||||
TextOut(hdc, JX + 4, JY + 18, L"IRQE", 4);
|
TextOut(hdc, JX + 4, JY + 18, L"IRQE", 4);
|
||||||
TextOut(hdc, JX + 4, JY + 32, L"ADMA", 4);
|
TextOut(hdc, JX + 4, JY + 32, L"ADMA", 4);
|
||||||
swprintf_s(t, L"DMA%s", c == 0 ? L"4" : L"7");
|
swprintf_s(t, L"DMA%s", c == 0 ? L"4" : L"7");
|
||||||
TextOut(hdc, JX + 4, JY + 46, t, 4);
|
TextOut(hdc, JX + 4, JY + 46, t, 4);
|
||||||
|
|
||||||
SetTextColor(hdc, RGB(0, 255, 0));
|
SetTextColor(hdc, RGB(0, 255, 0));
|
||||||
memset(t, 0, sizeof(t));
|
memset(t, 0, sizeof(t));
|
||||||
swprintf_s(t, L"ESA %x", cx.EffectsStartA);
|
swprintf_s(t, L"ESA %x", cx.EffectsStartA);
|
||||||
TextOut(hdc, JX + 56, JY + 4, t, 9);
|
TextOut(hdc, JX + 56, JY + 4, t, 9);
|
||||||
memset(t, 0, sizeof(t));
|
memset(t, 0, sizeof(t));
|
||||||
swprintf_s(t, L"EEA %x", cx.EffectsEndA);
|
swprintf_s(t, L"EEA %x", cx.EffectsEndA);
|
||||||
TextOut(hdc, JX + 128, JY + 4, t, 9);
|
TextOut(hdc, JX + 128, JY + 4, t, 9);
|
||||||
memset(t, 0, sizeof(t));
|
memset(t, 0, sizeof(t));
|
||||||
swprintf_s(t, L"(%x)", cx.EffectsBufferSize);
|
swprintf_s(t, L"(%x)", cx.EffectsBufferSize);
|
||||||
TextOut(hdc, JX + 200, JY + 4, t, 7);
|
TextOut(hdc, JX + 200, JY + 4, t, 7);
|
||||||
|
|
||||||
memset(t, 0, sizeof(t));
|
memset(t, 0, sizeof(t));
|
||||||
swprintf_s(t, L"IRQA %x", cx.IRQA);
|
swprintf_s(t, L"IRQA %x", cx.IRQA);
|
||||||
TextOut(hdc, JX + 56, JY + 18, t, 12);
|
TextOut(hdc, JX + 56, JY + 18, t, 12);
|
||||||
|
|
||||||
SetTextColor(hdc, RGB(255, 255, 255));
|
SetTextColor(hdc, RGB(255, 255, 255));
|
||||||
SetDCBrushColor(hdc, RGB(255, 0, 0));
|
SetDCBrushColor(hdc, RGB(255, 0, 0));
|
||||||
|
|
||||||
if (cx.FxEnable) {
|
if (cx.FxEnable)
|
||||||
FillRectangle(hdc, JX + 40, JY + 4, 10, 10);
|
{
|
||||||
}
|
FillRectangle(hdc, JX + 40, JY + 4, 10, 10);
|
||||||
if (cx.IRQEnable) {
|
}
|
||||||
FillRectangle(hdc, JX + 40, JY + 18, 10, 10);
|
if (cx.IRQEnable)
|
||||||
}
|
{
|
||||||
if (cx.AutoDMACtrl != 0) {
|
FillRectangle(hdc, JX + 40, JY + 18, 10, 10);
|
||||||
FillRectangle(hdc, JX + 40, JY + 32, 10, 10);
|
}
|
||||||
|
if (cx.AutoDMACtrl != 0)
|
||||||
|
{
|
||||||
|
FillRectangle(hdc, JX + 40, JY + 32, 10, 10);
|
||||||
|
|
||||||
for (int j = 0; j < 64; j++) {
|
for (int j = 0; j < 64; j++)
|
||||||
int i = j * 256 / 64;
|
{
|
||||||
int val = (cd.admaWaveformL[i] * 26) / 32768;
|
int i = j * 256 / 64;
|
||||||
int y = 0;
|
int val = (cd.admaWaveformL[i] * 26) / 32768;
|
||||||
|
int y = 0;
|
||||||
|
|
||||||
if (val > 0)
|
if (val > 0)
|
||||||
y = val;
|
y = val;
|
||||||
else
|
else
|
||||||
val = -val;
|
val = -val;
|
||||||
|
|
||||||
if (val != 0) {
|
if (val != 0)
|
||||||
FillRectangle(hdc, JX + 60 + j, JY + 30 - y, 1, val);
|
{
|
||||||
}
|
FillRectangle(hdc, JX + 60 + j, JY + 30 - y, 1, val);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int j = 0; j < 64; j++) {
|
for (int j = 0; j < 64; j++)
|
||||||
int i = j * 256 / 64;
|
{
|
||||||
int val = (cd.admaWaveformR[i] * 26) / 32768;
|
int i = j * 256 / 64;
|
||||||
int y = 0;
|
int val = (cd.admaWaveformR[i] * 26) / 32768;
|
||||||
|
int y = 0;
|
||||||
|
|
||||||
if (val > 0)
|
if (val > 0)
|
||||||
y = val;
|
y = val;
|
||||||
else
|
else
|
||||||
val = -val;
|
val = -val;
|
||||||
|
|
||||||
if (val != 0) {
|
if (val != 0)
|
||||||
FillRectangle(hdc, JX + 136 + j, JY + 30 - y, 1, val);
|
{
|
||||||
}
|
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
|
}
|
||||||
{
|
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);
|
{
|
||||||
|
swprintf_s(t, L"size = %d", cd.lastsize);
|
||||||
|
|
||||||
TextOut(hdc, JX + 64, JY + 46, t, wcslen(t));
|
TextOut(hdc, JX + 64, JY + 46, t, wcslen(t));
|
||||||
FillRectangle(hdc, JX + 40, JY + 46, 10, 10);
|
FillRectangle(hdc, JX + 40, JY + 46, 10, 10);
|
||||||
cd.dmaFlag--;
|
cd.dmaFlag--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReleaseDC(hDebugDialog, hdc);
|
ReleaseDC(hDebugDialog, hdc);
|
||||||
lCount = 0;
|
lCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
MSG msg;
|
MSG msg;
|
||||||
while (PeekMessage(&msg, hDebugDialog, 0, 0, PM_REMOVE)) {
|
while (PeekMessage(&msg, hDebugDialog, 0, 0, PM_REMOVE))
|
||||||
TranslateMessage(&msg);
|
{
|
||||||
DispatchMessage(&msg);
|
TranslateMessage(&msg);
|
||||||
}
|
DispatchMessage(&msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
void UpdateDebugDialog()
|
void UpdateDebugDialog()
|
||||||
{
|
{
|
||||||
// Release mode. Nothing to do
|
// Release mode. Nothing to do
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,432 +24,462 @@
|
||||||
class DSound : public SndOutModule
|
class DSound : public SndOutModule
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
static const uint MAX_BUFFER_COUNT = 8;
|
static const uint MAX_BUFFER_COUNT = 8;
|
||||||
static const int PacketsPerBuffer = 8;
|
static const int PacketsPerBuffer = 8;
|
||||||
static const int BufferSize = SndOutPacketSize * PacketsPerBuffer;
|
static const int BufferSize = SndOutPacketSize * PacketsPerBuffer;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Configuration Vars
|
// Configuration Vars
|
||||||
|
|
||||||
wxString m_Device;
|
wxString m_Device;
|
||||||
u8 m_NumBuffers;
|
u8 m_NumBuffers;
|
||||||
bool m_DisableGlobalFocus;
|
bool m_DisableGlobalFocus;
|
||||||
bool m_UseHardware;
|
bool m_UseHardware;
|
||||||
|
|
||||||
ds_device_data m_devices[32];
|
ds_device_data m_devices[32];
|
||||||
int ndevs;
|
int ndevs;
|
||||||
GUID DevGuid; // currently employed GUID.
|
GUID DevGuid; // currently employed GUID.
|
||||||
bool haveGuid;
|
bool haveGuid;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Instance vars
|
// Instance vars
|
||||||
|
|
||||||
int channel;
|
int channel;
|
||||||
int myLastWrite; // last write position, in bytes
|
int myLastWrite; // last write position, in bytes
|
||||||
|
|
||||||
bool dsound_running;
|
bool dsound_running;
|
||||||
HANDLE thread;
|
HANDLE thread;
|
||||||
DWORD tid;
|
DWORD tid;
|
||||||
|
|
||||||
IDirectSound8 *dsound;
|
IDirectSound8* dsound;
|
||||||
IDirectSoundBuffer8 *buffer;
|
IDirectSoundBuffer8* buffer;
|
||||||
IDirectSoundNotify8 *buffer_notify;
|
IDirectSoundNotify8* buffer_notify;
|
||||||
HANDLE buffer_events[MAX_BUFFER_COUNT];
|
HANDLE buffer_events[MAX_BUFFER_COUNT];
|
||||||
|
|
||||||
WAVEFORMATEX wfx;
|
WAVEFORMATEX wfx;
|
||||||
|
|
||||||
HANDLE waitEvent;
|
HANDLE waitEvent;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static DWORD CALLBACK RThread(DSound *obj)
|
static DWORD CALLBACK RThread(DSound* obj)
|
||||||
{
|
{
|
||||||
return obj->Thread<T>();
|
return obj->Thread<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
DWORD CALLBACK Thread()
|
DWORD CALLBACK Thread()
|
||||||
{
|
{
|
||||||
static const int BufferSizeBytes = BufferSize * sizeof(T);
|
static const int BufferSizeBytes = BufferSize * sizeof(T);
|
||||||
|
|
||||||
while (dsound_running) {
|
while (dsound_running)
|
||||||
u32 rv = WaitForMultipleObjects(m_NumBuffers, buffer_events, FALSE, 200);
|
{
|
||||||
|
u32 rv = WaitForMultipleObjects(m_NumBuffers, buffer_events, FALSE, 200);
|
||||||
|
|
||||||
T *p1, *oldp1;
|
T *p1, *oldp1;
|
||||||
LPVOID p2;
|
LPVOID p2;
|
||||||
DWORD s1, s2;
|
DWORD s1, s2;
|
||||||
|
|
||||||
u32 poffset = BufferSizeBytes * rv;
|
u32 poffset = BufferSizeBytes * rv;
|
||||||
|
|
||||||
if (FAILED(buffer->Lock(poffset, BufferSizeBytes, (LPVOID *)&p1, &s1, &p2, &s2, 0))) {
|
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);
|
assert(0);
|
||||||
continue;
|
fputs("* SPU2-X: Directsound Warning > Buffer lock failure. You may need to increase\n\tyour configured DSound buffer count.\n", stderr);
|
||||||
}
|
continue;
|
||||||
oldp1 = p1;
|
}
|
||||||
|
oldp1 = p1;
|
||||||
|
|
||||||
for (int p = 0; p < PacketsPerBuffer; p++, p1 += SndOutPacketSize)
|
for (int p = 0; p < PacketsPerBuffer; p++, p1 += SndOutPacketSize)
|
||||||
SndBuffer::ReadSamples(p1);
|
SndBuffer::ReadSamples(p1);
|
||||||
|
|
||||||
buffer->Unlock(oldp1, s1, p2, s2);
|
buffer->Unlock(oldp1, s1, p2, s2);
|
||||||
|
|
||||||
// Set the write pointer to the beginning of the next block.
|
// Set the write pointer to the beginning of the next block.
|
||||||
myLastWrite = (poffset + BufferSizeBytes) & ~BufferSizeBytes;
|
myLastWrite = (poffset + BufferSizeBytes) & ~BufferSizeBytes;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
s32 Init()
|
s32 Init()
|
||||||
{
|
{
|
||||||
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Initialize DSound
|
// Initialize DSound
|
||||||
//
|
//
|
||||||
GUID cGuid;
|
GUID cGuid;
|
||||||
|
|
||||||
try {
|
try
|
||||||
if (m_Device.empty())
|
{
|
||||||
throw std::runtime_error("screw it");
|
if (m_Device.empty())
|
||||||
|
throw std::runtime_error("screw it");
|
||||||
|
|
||||||
if ((FAILED(IIDFromString(m_Device, &cGuid))) ||
|
if ((FAILED(IIDFromString(m_Device, &cGuid))) ||
|
||||||
FAILED(DirectSoundCreate8(&cGuid, &dsound, NULL)))
|
FAILED(DirectSoundCreate8(&cGuid, &dsound, NULL)))
|
||||||
throw std::runtime_error("try again?");
|
throw std::runtime_error("try again?");
|
||||||
} catch (std::runtime_error &) {
|
}
|
||||||
// if the GUID failed, just open up the default dsound driver:
|
catch (std::runtime_error&)
|
||||||
if (FAILED(DirectSoundCreate8(NULL, &dsound, NULL)))
|
{
|
||||||
throw std::runtime_error("DirectSound failed to initialize!");
|
// 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)))
|
if (FAILED(dsound->SetCooperativeLevel(GetDesktopWindow(), DSSCL_PRIORITY)))
|
||||||
throw std::runtime_error("DirectSound Error: Cooperative level could not be set.");
|
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.
|
// Determine the user's speaker configuration, and select an expansion option as needed.
|
||||||
// FAIL : Directsound doesn't appear to support audio expansion >_<
|
// FAIL : Directsound doesn't appear to support audio expansion >_<
|
||||||
|
|
||||||
DWORD speakerConfig = 2;
|
DWORD speakerConfig = 2;
|
||||||
//dsound->GetSpeakerConfig( &speakerConfig );
|
//dsound->GetSpeakerConfig( &speakerConfig );
|
||||||
|
|
||||||
IDirectSoundBuffer *buffer_;
|
IDirectSoundBuffer* buffer_;
|
||||||
DSBUFFERDESC desc;
|
DSBUFFERDESC desc;
|
||||||
|
|
||||||
// Set up WAV format structure.
|
// Set up WAV format structure.
|
||||||
|
|
||||||
memset(&wfx, 0, sizeof(WAVEFORMATEX));
|
memset(&wfx, 0, sizeof(WAVEFORMATEX));
|
||||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
wfx.nSamplesPerSec = SampleRate;
|
wfx.nSamplesPerSec = SampleRate;
|
||||||
wfx.nChannels = (WORD)speakerConfig;
|
wfx.nChannels = (WORD)speakerConfig;
|
||||||
wfx.wBitsPerSample = 16;
|
wfx.wBitsPerSample = 16;
|
||||||
wfx.nBlockAlign = 2 * (WORD)speakerConfig;
|
wfx.nBlockAlign = 2 * (WORD)speakerConfig;
|
||||||
wfx.nAvgBytesPerSec = SampleRate * wfx.nBlockAlign;
|
wfx.nAvgBytesPerSec = SampleRate * wfx.nBlockAlign;
|
||||||
wfx.cbSize = 0;
|
wfx.cbSize = 0;
|
||||||
|
|
||||||
uint BufferSizeBytes = BufferSize * wfx.nBlockAlign;
|
uint BufferSizeBytes = BufferSize * wfx.nBlockAlign;
|
||||||
|
|
||||||
// Set up DSBUFFERDESC structure.
|
// Set up DSBUFFERDESC structure.
|
||||||
|
|
||||||
memset(&desc, 0, sizeof(DSBUFFERDESC));
|
memset(&desc, 0, sizeof(DSBUFFERDESC));
|
||||||
desc.dwSize = sizeof(DSBUFFERDESC);
|
desc.dwSize = sizeof(DSBUFFERDESC);
|
||||||
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY;
|
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY;
|
||||||
desc.dwBufferBytes = BufferSizeBytes * m_NumBuffers;
|
desc.dwBufferBytes = BufferSizeBytes * m_NumBuffers;
|
||||||
desc.lpwfxFormat = &wfx;
|
desc.lpwfxFormat = &wfx;
|
||||||
|
|
||||||
// Try a hardware buffer first, and then fall back on a software buffer if
|
// Try a hardware buffer first, and then fall back on a software buffer if
|
||||||
// that one fails.
|
// that one fails.
|
||||||
|
|
||||||
desc.dwFlags |= m_UseHardware ? DSBCAPS_LOCHARDWARE : DSBCAPS_LOCSOFTWARE;
|
desc.dwFlags |= m_UseHardware ? DSBCAPS_LOCHARDWARE : DSBCAPS_LOCSOFTWARE;
|
||||||
desc.dwFlags |= m_DisableGlobalFocus ? DSBCAPS_STICKYFOCUS : DSBCAPS_GLOBALFOCUS;
|
desc.dwFlags |= m_DisableGlobalFocus ? DSBCAPS_STICKYFOCUS : DSBCAPS_GLOBALFOCUS;
|
||||||
|
|
||||||
if (FAILED(dsound->CreateSoundBuffer(&desc, &buffer_, 0))) {
|
if (FAILED(dsound->CreateSoundBuffer(&desc, &buffer_, 0)))
|
||||||
if (m_UseHardware) {
|
{
|
||||||
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_LOCSOFTWARE;
|
if (m_UseHardware)
|
||||||
desc.dwFlags |= m_DisableGlobalFocus ? DSBCAPS_STICKYFOCUS : DSBCAPS_GLOBALFOCUS;
|
{
|
||||||
|
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_LOCSOFTWARE;
|
||||||
|
desc.dwFlags |= m_DisableGlobalFocus ? DSBCAPS_STICKYFOCUS : DSBCAPS_GLOBALFOCUS;
|
||||||
|
|
||||||
if (FAILED(dsound->CreateSoundBuffer(&desc, &buffer_, 0)))
|
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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
if (FAILED(buffer_->QueryInterface(IID_IDirectSoundBuffer8, (void**)&buffer)) || buffer == NULL)
|
||||||
throw std::runtime_error("DirectSound Error: Interface could not be queried.");
|
throw std::runtime_error("DirectSound Error: Interface could not be queried.");
|
||||||
|
|
||||||
buffer_->Release();
|
buffer_->Release();
|
||||||
verifyc(buffer->QueryInterface(IID_IDirectSoundNotify8, (void **)&buffer_notify));
|
verifyc(buffer->QueryInterface(IID_IDirectSoundNotify8, (void**)&buffer_notify));
|
||||||
|
|
||||||
DSBPOSITIONNOTIFY not[MAX_BUFFER_COUNT];
|
DSBPOSITIONNOTIFY not[MAX_BUFFER_COUNT];
|
||||||
|
|
||||||
for (uint i = 0; i < m_NumBuffers; i++) {
|
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;
|
buffer_events[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
not[i].hEventNotify = buffer_events[i];
|
not[i].dwOffset = (wfx.nBlockAlign + BufferSizeBytes * (i + 1)) % desc.dwBufferBytes;
|
||||||
}
|
not[i].hEventNotify = buffer_events[i];
|
||||||
|
}
|
||||||
|
|
||||||
buffer_notify->SetNotificationPositions(m_NumBuffers, not);
|
buffer_notify->SetNotificationPositions(m_NumBuffers, not);
|
||||||
|
|
||||||
LPVOID p1 = 0, p2 = 0;
|
LPVOID p1 = 0, p2 = 0;
|
||||||
DWORD s1 = 0, s2 = 0;
|
DWORD s1 = 0, s2 = 0;
|
||||||
|
|
||||||
verifyc(buffer->Lock(0, desc.dwBufferBytes, &p1, &s1, &p2, &s2, 0));
|
verifyc(buffer->Lock(0, desc.dwBufferBytes, &p1, &s1, &p2, &s2, 0));
|
||||||
assert(p2 == 0);
|
assert(p2 == 0);
|
||||||
memset(p1, 0, s1);
|
memset(p1, 0, s1);
|
||||||
verifyc(buffer->Unlock(p1, s1, p2, s2));
|
verifyc(buffer->Unlock(p1, s1, p2, s2));
|
||||||
|
|
||||||
//Play the buffer !
|
//Play the buffer !
|
||||||
verifyc(buffer->Play(0, 0, DSBPLAY_LOOPING));
|
verifyc(buffer->Play(0, 0, DSBPLAY_LOOPING));
|
||||||
|
|
||||||
// Start Thread
|
// Start Thread
|
||||||
myLastWrite = 0;
|
myLastWrite = 0;
|
||||||
dsound_running = true;
|
dsound_running = true;
|
||||||
thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RThread<StereoOut16>, this, 0, &tid);
|
thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RThread<StereoOut16>, this, 0, &tid);
|
||||||
SetThreadPriority(thread, THREAD_PRIORITY_ABOVE_NORMAL);
|
SetThreadPriority(thread, THREAD_PRIORITY_ABOVE_NORMAL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Close()
|
void Close()
|
||||||
{
|
{
|
||||||
// Stop Thread
|
// Stop Thread
|
||||||
fprintf(stderr, "* SPU2-X: Waiting for DSound thread to finish...");
|
fprintf(stderr, "* SPU2-X: Waiting for DSound thread to finish...");
|
||||||
dsound_running = false;
|
dsound_running = false;
|
||||||
|
|
||||||
WaitForSingleObject(thread, INFINITE);
|
WaitForSingleObject(thread, INFINITE);
|
||||||
CloseHandle(thread);
|
CloseHandle(thread);
|
||||||
|
|
||||||
fprintf(stderr, " Done.\n");
|
fprintf(stderr, " Done.\n");
|
||||||
|
|
||||||
//
|
//
|
||||||
// Clean up
|
// Clean up
|
||||||
//
|
//
|
||||||
if (buffer != NULL) {
|
if (buffer != NULL)
|
||||||
buffer->Stop();
|
{
|
||||||
|
buffer->Stop();
|
||||||
|
|
||||||
for (u32 i = 0; i < m_NumBuffers; i++) {
|
for (u32 i = 0; i < m_NumBuffers; i++)
|
||||||
if (buffer_events[i] != NULL)
|
{
|
||||||
CloseHandle(buffer_events[i]);
|
if (buffer_events[i] != NULL)
|
||||||
buffer_events[i] = NULL;
|
CloseHandle(buffer_events[i]);
|
||||||
}
|
buffer_events[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
safe_release(buffer_notify);
|
safe_release(buffer_notify);
|
||||||
safe_release(buffer);
|
safe_release(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
safe_release(dsound);
|
safe_release(dsound);
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext)
|
bool _DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext)
|
||||||
{
|
{
|
||||||
m_devices[ndevs].name = lpcstrDescription;
|
m_devices[ndevs].name = lpcstrDescription;
|
||||||
|
|
||||||
if (lpGuid) {
|
if (lpGuid)
|
||||||
m_devices[ndevs].guid = *lpGuid;
|
{
|
||||||
m_devices[ndevs].hasGuid = true;
|
m_devices[ndevs].guid = *lpGuid;
|
||||||
} else {
|
m_devices[ndevs].hasGuid = true;
|
||||||
m_devices[ndevs].hasGuid = false;
|
}
|
||||||
}
|
else
|
||||||
ndevs++;
|
{
|
||||||
|
m_devices[ndevs].hasGuid = false;
|
||||||
|
}
|
||||||
|
ndevs++;
|
||||||
|
|
||||||
if (ndevs < 32)
|
if (ndevs < 32)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
bool _ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
int wmId, wmEvent;
|
int wmId, wmEvent;
|
||||||
int tSel = 0;
|
int tSel = 0;
|
||||||
|
|
||||||
switch (uMsg) {
|
switch (uMsg)
|
||||||
case WM_INITDIALOG: {
|
{
|
||||||
wchar_t temp[128];
|
case WM_INITDIALOG:
|
||||||
|
{
|
||||||
|
wchar_t temp[128];
|
||||||
|
|
||||||
haveGuid = !FAILED(IIDFromString(m_Device, &DevGuid));
|
haveGuid = !FAILED(IIDFromString(m_Device, &DevGuid));
|
||||||
SendMessage(GetDlgItem(hWnd, IDC_DS_DEVICE), CB_RESETCONTENT, 0, 0);
|
SendMessage(GetDlgItem(hWnd, IDC_DS_DEVICE), CB_RESETCONTENT, 0, 0);
|
||||||
|
|
||||||
ndevs = 0;
|
ndevs = 0;
|
||||||
DirectSoundEnumerate(DSEnumCallback, NULL);
|
DirectSoundEnumerate(DSEnumCallback, NULL);
|
||||||
|
|
||||||
tSel = -1;
|
tSel = -1;
|
||||||
for (int i = 0; i < ndevs; i++) {
|
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)
|
SendMessage(GetDlgItem(hWnd, IDC_DS_DEVICE), CB_ADDSTRING, 0, (LPARAM)m_devices[i].name.wc_str());
|
||||||
tSel = i;
|
if (haveGuid && IsEqualGUID(m_devices[i].guid, DevGuid) || tSel < 0 && !m_devices[i].hasGuid)
|
||||||
}
|
tSel = i;
|
||||||
|
}
|
||||||
|
|
||||||
if (tSel >= 0)
|
if (tSel >= 0)
|
||||||
SendMessage(GetDlgItem(hWnd, IDC_DS_DEVICE), CB_SETCURSEL, tSel, 0);
|
SendMessage(GetDlgItem(hWnd, IDC_DS_DEVICE), CB_SETCURSEL, tSel, 0);
|
||||||
|
|
||||||
INIT_SLIDER(IDC_BUFFERS_SLIDER, 2, MAX_BUFFER_COUNT, 2, 1, 1);
|
INIT_SLIDER(IDC_BUFFERS_SLIDER, 2, MAX_BUFFER_COUNT, 2, 1, 1);
|
||||||
SendMessage(GetDlgItem(hWnd, IDC_BUFFERS_SLIDER), TBM_SETPOS, TRUE, m_NumBuffers);
|
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)));
|
swprintf_s(temp, L"%d (%d ms latency)", m_NumBuffers, 1000 / (96000 / (m_NumBuffers * BufferSize)));
|
||||||
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
|
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
|
||||||
|
|
||||||
SET_CHECK(IDC_GLOBALFOCUS_DISABLE, m_DisableGlobalFocus);
|
SET_CHECK(IDC_GLOBALFOCUS_DISABLE, m_DisableGlobalFocus);
|
||||||
SET_CHECK(IDC_USE_HARDWARE, m_UseHardware);
|
SET_CHECK(IDC_USE_HARDWARE, m_UseHardware);
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WM_COMMAND: {
|
case WM_COMMAND:
|
||||||
wchar_t temp[128];
|
{
|
||||||
|
wchar_t temp[128];
|
||||||
|
|
||||||
wmId = LOWORD(wParam);
|
wmId = LOWORD(wParam);
|
||||||
wmEvent = HIWORD(wParam);
|
wmEvent = HIWORD(wParam);
|
||||||
// Parse the menu selections:
|
// Parse the menu selections:
|
||||||
switch (wmId) {
|
switch (wmId)
|
||||||
case IDOK: {
|
{
|
||||||
int i = (int)SendMessage(GetDlgItem(hWnd, IDC_DS_DEVICE), CB_GETCURSEL, 0, 0);
|
case IDOK:
|
||||||
|
{
|
||||||
|
int i = (int)SendMessage(GetDlgItem(hWnd, IDC_DS_DEVICE), CB_GETCURSEL, 0, 0);
|
||||||
|
|
||||||
if (!m_devices[i].hasGuid) {
|
if (!m_devices[i].hasGuid)
|
||||||
m_Device[0] = 0; // clear device name to ""
|
{
|
||||||
} else {
|
m_Device[0] = 0; // clear device name to ""
|
||||||
swprintf_s(temp, L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
|
}
|
||||||
m_devices[i].guid.Data1,
|
else
|
||||||
m_devices[i].guid.Data2,
|
{
|
||||||
m_devices[i].guid.Data3,
|
swprintf_s(temp, L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
|
||||||
m_devices[i].guid.Data4[0],
|
m_devices[i].guid.Data1,
|
||||||
m_devices[i].guid.Data4[1],
|
m_devices[i].guid.Data2,
|
||||||
m_devices[i].guid.Data4[2],
|
m_devices[i].guid.Data3,
|
||||||
m_devices[i].guid.Data4[3],
|
m_devices[i].guid.Data4[0],
|
||||||
m_devices[i].guid.Data4[4],
|
m_devices[i].guid.Data4[1],
|
||||||
m_devices[i].guid.Data4[5],
|
m_devices[i].guid.Data4[2],
|
||||||
m_devices[i].guid.Data4[6],
|
m_devices[i].guid.Data4[3],
|
||||||
m_devices[i].guid.Data4[7]);
|
m_devices[i].guid.Data4[4],
|
||||||
m_Device = temp;
|
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);
|
m_NumBuffers = (int)SendMessage(GetDlgItem(hWnd, IDC_BUFFERS_SLIDER), TBM_GETPOS, 0, 0);
|
||||||
|
|
||||||
if (m_NumBuffers < 2)
|
if (m_NumBuffers < 2)
|
||||||
m_NumBuffers = 2;
|
m_NumBuffers = 2;
|
||||||
if (m_NumBuffers > MAX_BUFFER_COUNT)
|
if (m_NumBuffers > MAX_BUFFER_COUNT)
|
||||||
m_NumBuffers = MAX_BUFFER_COUNT;
|
m_NumBuffers = MAX_BUFFER_COUNT;
|
||||||
|
|
||||||
EndDialog(hWnd, 0);
|
EndDialog(hWnd, 0);
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case IDCANCEL:
|
case IDCANCEL:
|
||||||
EndDialog(hWnd, 0);
|
EndDialog(hWnd, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
HANDLE_CHECK(IDC_GLOBALFOCUS_DISABLE, m_DisableGlobalFocus);
|
HANDLE_CHECK(IDC_GLOBALFOCUS_DISABLE, m_DisableGlobalFocus);
|
||||||
HANDLE_CHECK(IDC_USE_HARDWARE, m_UseHardware);
|
HANDLE_CHECK(IDC_USE_HARDWARE, m_UseHardware);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
} break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WM_HSCROLL: {
|
case WM_HSCROLL:
|
||||||
wmId = LOWORD(wParam);
|
{
|
||||||
wmEvent = HIWORD(wParam);
|
wmId = LOWORD(wParam);
|
||||||
switch (wmId) {
|
wmEvent = HIWORD(wParam);
|
||||||
case TB_LINEUP:
|
switch (wmId)
|
||||||
case TB_LINEDOWN:
|
{
|
||||||
case TB_PAGEUP:
|
case TB_LINEUP:
|
||||||
case TB_PAGEDOWN:
|
case TB_LINEDOWN:
|
||||||
case TB_TOP:
|
case TB_PAGEUP:
|
||||||
case TB_BOTTOM:
|
case TB_PAGEDOWN:
|
||||||
wmEvent = (int)SendMessage((HWND)lParam, TBM_GETPOS, 0, 0);
|
case TB_TOP:
|
||||||
case TB_THUMBPOSITION:
|
case TB_BOTTOM:
|
||||||
case TB_THUMBTRACK: {
|
wmEvent = (int)SendMessage((HWND)lParam, TBM_GETPOS, 0, 0);
|
||||||
wchar_t temp[128];
|
case TB_THUMBPOSITION:
|
||||||
if (wmEvent < 2)
|
case TB_THUMBTRACK:
|
||||||
wmEvent = 2;
|
{
|
||||||
if (wmEvent > MAX_BUFFER_COUNT)
|
wchar_t temp[128];
|
||||||
wmEvent = MAX_BUFFER_COUNT;
|
if (wmEvent < 2)
|
||||||
SendMessage((HWND)lParam, TBM_SETPOS, TRUE, wmEvent);
|
wmEvent = 2;
|
||||||
swprintf_s(temp, L"%d (%d ms latency)", wmEvent, 1000 / (96000 / (wmEvent * BufferSize)));
|
if (wmEvent > MAX_BUFFER_COUNT)
|
||||||
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
|
wmEvent = MAX_BUFFER_COUNT;
|
||||||
break;
|
SendMessage((HWND)lParam, TBM_SETPOS, TRUE, wmEvent);
|
||||||
}
|
swprintf_s(temp, L"%d (%d ms latency)", wmEvent, 1000 / (96000 / (wmEvent * BufferSize)));
|
||||||
default:
|
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
|
||||||
return FALSE;
|
break;
|
||||||
}
|
}
|
||||||
} break;
|
default:
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL CALLBACK ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
static BOOL CALLBACK ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||||
static BOOL CALLBACK DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext);
|
static BOOL CALLBACK DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void Configure(uptr parent)
|
virtual void Configure(uptr parent)
|
||||||
{
|
{
|
||||||
INT_PTR ret;
|
INT_PTR ret;
|
||||||
ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_DSOUND), (HWND)parent, (DLGPROC)ConfigProc, 1);
|
ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_DSOUND), (HWND)parent, (DLGPROC)ConfigProc, 1);
|
||||||
if (ret == -1) {
|
if (ret == -1)
|
||||||
MessageBox((HWND)parent, L"Error Opening the config dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
|
{
|
||||||
return;
|
MessageBox((HWND)parent, L"Error Opening the config dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s32 Test() const
|
s32 Test() const
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetEmptySampleCount()
|
int GetEmptySampleCount()
|
||||||
{
|
{
|
||||||
DWORD play, write;
|
DWORD play, write;
|
||||||
buffer->GetCurrentPosition(&play, &write);
|
buffer->GetCurrentPosition(&play, &write);
|
||||||
|
|
||||||
// Note: Dsound's write cursor is bogus. Use our own instead:
|
// Note: Dsound's write cursor is bogus. Use our own instead:
|
||||||
|
|
||||||
int empty = play - myLastWrite;
|
int empty = play - myLastWrite;
|
||||||
if (empty < 0)
|
if (empty < 0)
|
||||||
empty = -empty;
|
empty = -empty;
|
||||||
|
|
||||||
return empty / 2;
|
return empty / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *GetIdent() const
|
const wchar_t* GetIdent() const
|
||||||
{
|
{
|
||||||
return L"dsound";
|
return L"dsound";
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *GetLongName() const
|
const wchar_t* GetLongName() const
|
||||||
{
|
{
|
||||||
return L"DirectSound (Nice)";
|
return L"DirectSound (Nice)";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadSettings()
|
void ReadSettings()
|
||||||
{
|
{
|
||||||
CfgReadStr(L"DSOUNDOUT", L"Device", m_Device, L"default");
|
CfgReadStr(L"DSOUNDOUT", L"Device", m_Device, L"default");
|
||||||
m_NumBuffers = CfgReadInt(L"DSOUNDOUT", L"Buffer_Count", 5);
|
m_NumBuffers = CfgReadInt(L"DSOUNDOUT", L"Buffer_Count", 5);
|
||||||
m_DisableGlobalFocus = CfgReadBool(L"DSOUNDOUT", L"Disable_Global_Focus", false);
|
m_DisableGlobalFocus = CfgReadBool(L"DSOUNDOUT", L"Disable_Global_Focus", false);
|
||||||
m_UseHardware = CfgReadBool(L"DSOUNDOUT", L"Use_Hardware", false);
|
m_UseHardware = CfgReadBool(L"DSOUNDOUT", L"Use_Hardware", false);
|
||||||
|
|
||||||
Clampify(m_NumBuffers, (u8)3, (u8)8);
|
Clampify(m_NumBuffers, (u8)3, (u8)8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetApiSettings(wxString api)
|
void SetApiSettings(wxString api)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteSettings() const
|
void WriteSettings() const
|
||||||
{
|
{
|
||||||
CfgWriteStr(L"DSOUNDOUT", L"Device", m_Device.empty() ? L"default" : m_Device);
|
CfgWriteStr(L"DSOUNDOUT", L"Device", m_Device.empty() ? L"default" : m_Device);
|
||||||
CfgWriteInt(L"DSOUNDOUT", L"Buffer_Count", m_NumBuffers);
|
CfgWriteInt(L"DSOUNDOUT", L"Buffer_Count", m_NumBuffers);
|
||||||
CfgWriteBool(L"DSOUNDOUT", L"Disable_Global_Focus", m_DisableGlobalFocus);
|
CfgWriteBool(L"DSOUNDOUT", L"Disable_Global_Focus", m_DisableGlobalFocus);
|
||||||
CfgWriteBool(L"DSOUNDOUT", L"Use_Hardware", m_UseHardware);
|
CfgWriteBool(L"DSOUNDOUT", L"Use_Hardware", m_UseHardware);
|
||||||
}
|
}
|
||||||
|
|
||||||
} static DS;
|
} static DS;
|
||||||
|
|
||||||
BOOL CALLBACK DSound::ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
BOOL CALLBACK DSound::ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
return DS._ConfigProc(hWnd, uMsg, wParam, lParam);
|
return DS._ConfigProc(hWnd, uMsg, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL CALLBACK DSound::DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext)
|
BOOL CALLBACK DSound::DSEnumCallback(LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext)
|
||||||
{
|
{
|
||||||
pxAssume(DSoundOut != NULL);
|
pxAssume(DSoundOut != NULL);
|
||||||
return DS._DSEnumCallback(lpGuid, lpcstrDescription, lpcstrModule, lpContext);
|
return DS._DSEnumCallback(lpGuid, lpcstrDescription, lpcstrModule, lpContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
SndOutModule *DSoundOut = &DS;
|
SndOutModule* DSoundOut = &DS;
|
||||||
|
|
|
@ -27,395 +27,408 @@
|
||||||
|
|
||||||
namespace Exception
|
namespace Exception
|
||||||
{
|
{
|
||||||
class XAudio2Error : public std::runtime_error
|
class XAudio2Error : public std::runtime_error
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
static std::string CreateErrorMessage(const HRESULT result, const std::string &msg)
|
static std::string CreateErrorMessage(const HRESULT result, const std::string& msg)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << " (code 0x" << std::hex << result << ")\n\n";
|
ss << " (code 0x" << std::hex << result << ")\n\n";
|
||||||
switch (result) {
|
switch (result)
|
||||||
case XAUDIO2_E_INVALID_CALL:
|
{
|
||||||
ss << "Invalid call for the XA2 object state.";
|
case XAUDIO2_E_INVALID_CALL:
|
||||||
break;
|
ss << "Invalid call for the XA2 object state.";
|
||||||
case XAUDIO2_E_DEVICE_INVALIDATED:
|
break;
|
||||||
ss << "Device is unavailable, unplugged, unsupported, or has been consumed by The Nothing.";
|
case XAUDIO2_E_DEVICE_INVALIDATED:
|
||||||
break;
|
ss << "Device is unavailable, unplugged, unsupported, or has been consumed by The Nothing.";
|
||||||
default:
|
break;
|
||||||
ss << "Unknown error code!";
|
default:
|
||||||
break;
|
ss << "Unknown error code!";
|
||||||
}
|
break;
|
||||||
return msg + ss.str();
|
}
|
||||||
}
|
return msg + ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit XAudio2Error(const HRESULT result, const std::string &msg)
|
explicit XAudio2Error(const HRESULT result, const std::string& msg)
|
||||||
: std::runtime_error(CreateErrorMessage(result, msg))
|
: std::runtime_error(CreateErrorMessage(result, msg))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace Exception
|
||||||
|
|
||||||
static const double SndOutNormalizer = (double)(1UL << (SndOutVolumeShift + 16));
|
static const double SndOutNormalizer = (double)(1UL << (SndOutVolumeShift + 16));
|
||||||
|
|
||||||
class XAudio2Mod : public SndOutModule
|
class XAudio2Mod : public SndOutModule
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
static const int PacketsPerBuffer = 8;
|
static const int PacketsPerBuffer = 8;
|
||||||
static const int MAX_BUFFER_COUNT = 3;
|
static const int MAX_BUFFER_COUNT = 3;
|
||||||
|
|
||||||
class BaseStreamingVoice : public IXAudio2VoiceCallback
|
class BaseStreamingVoice : public IXAudio2VoiceCallback
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
IXAudio2SourceVoice *pSourceVoice;
|
IXAudio2SourceVoice* pSourceVoice;
|
||||||
std::unique_ptr<s16[]> m_buffer;
|
std::unique_ptr<s16[]> m_buffer;
|
||||||
|
|
||||||
const uint m_nBuffers;
|
const uint m_nBuffers;
|
||||||
const uint m_nChannels;
|
const uint m_nChannels;
|
||||||
const uint m_BufferSize;
|
const uint m_BufferSize;
|
||||||
const uint m_BufferSizeBytes;
|
const uint m_BufferSizeBytes;
|
||||||
|
|
||||||
CRITICAL_SECTION cs;
|
CRITICAL_SECTION cs;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int GetEmptySampleCount()
|
int GetEmptySampleCount()
|
||||||
{
|
{
|
||||||
XAUDIO2_VOICE_STATE state;
|
XAUDIO2_VOICE_STATE state;
|
||||||
pSourceVoice->GetState(&state);
|
pSourceVoice->GetState(&state);
|
||||||
return state.SamplesPlayed & (m_BufferSize - 1);
|
return state.SamplesPlayed & (m_BufferSize - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~BaseStreamingVoice()
|
virtual ~BaseStreamingVoice()
|
||||||
{
|
{
|
||||||
DeleteCriticalSection(&cs);
|
DeleteCriticalSection(&cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseStreamingVoice(uint numChannels)
|
BaseStreamingVoice(uint numChannels)
|
||||||
: pSourceVoice(nullptr)
|
: pSourceVoice(nullptr)
|
||||||
, m_nBuffers(Config_XAudio2.NumBuffers)
|
, m_nBuffers(Config_XAudio2.NumBuffers)
|
||||||
, m_nChannels(numChannels)
|
, m_nChannels(numChannels)
|
||||||
, m_BufferSize(SndOutPacketSize * m_nChannels * PacketsPerBuffer)
|
, m_BufferSize(SndOutPacketSize * m_nChannels * PacketsPerBuffer)
|
||||||
, m_BufferSizeBytes(m_BufferSize * sizeof(s16))
|
, m_BufferSizeBytes(m_BufferSize * sizeof(s16))
|
||||||
{
|
{
|
||||||
InitializeCriticalSection(&cs);
|
InitializeCriticalSection(&cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Init(IXAudio2 *pXAudio2) = 0;
|
virtual void Init(IXAudio2* pXAudio2) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Several things must be initialized separate of the constructor, due to the fact that
|
// 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.
|
// virtual calls can't be made from the constructor's context.
|
||||||
void _init(IXAudio2 *pXAudio2, uint chanConfig)
|
void _init(IXAudio2* pXAudio2, uint chanConfig)
|
||||||
{
|
{
|
||||||
WAVEFORMATEXTENSIBLE wfx;
|
WAVEFORMATEXTENSIBLE wfx;
|
||||||
|
|
||||||
memset(&wfx, 0, sizeof(WAVEFORMATEXTENSIBLE));
|
memset(&wfx, 0, sizeof(WAVEFORMATEXTENSIBLE));
|
||||||
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||||
wfx.Format.nSamplesPerSec = SampleRate;
|
wfx.Format.nSamplesPerSec = SampleRate;
|
||||||
wfx.Format.nChannels = m_nChannels;
|
wfx.Format.nChannels = m_nChannels;
|
||||||
wfx.Format.wBitsPerSample = 16;
|
wfx.Format.wBitsPerSample = 16;
|
||||||
wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8;
|
wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8;
|
||||||
wfx.Format.nAvgBytesPerSec = SampleRate * wfx.Format.nBlockAlign;
|
wfx.Format.nAvgBytesPerSec = SampleRate * wfx.Format.nBlockAlign;
|
||||||
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||||
wfx.Samples.wValidBitsPerSample = 16;
|
wfx.Samples.wValidBitsPerSample = 16;
|
||||||
wfx.dwChannelMask = chanConfig;
|
wfx.dwChannelMask = chanConfig;
|
||||||
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||||
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
if (FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX *)&wfx,
|
if (FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX*)&wfx,
|
||||||
XAUDIO2_VOICE_NOSRC, 1.0f, this))) {
|
XAUDIO2_VOICE_NOSRC, 1.0f, this)))
|
||||||
throw Exception::XAudio2Error(hr, "XAudio2 CreateSourceVoice failure: ");
|
{
|
||||||
}
|
throw Exception::XAudio2Error(hr, "XAudio2 CreateSourceVoice failure: ");
|
||||||
|
}
|
||||||
|
|
||||||
EnterCriticalSection(&cs);
|
EnterCriticalSection(&cs);
|
||||||
|
|
||||||
pSourceVoice->FlushSourceBuffers();
|
pSourceVoice->FlushSourceBuffers();
|
||||||
pSourceVoice->Start(0, 0);
|
pSourceVoice->Start(0, 0);
|
||||||
|
|
||||||
m_buffer = std::make_unique<s16[]>(m_nBuffers * m_BufferSize);
|
m_buffer = std::make_unique<s16[]>(m_nBuffers * m_BufferSize);
|
||||||
|
|
||||||
// Start some buffers.
|
// Start some buffers.
|
||||||
for (uint i = 0; i < m_nBuffers; i++) {
|
for (uint i = 0; i < m_nBuffers; i++)
|
||||||
XAUDIO2_BUFFER buf = {0};
|
{
|
||||||
buf.AudioBytes = m_BufferSizeBytes;
|
XAUDIO2_BUFFER buf = {0};
|
||||||
buf.pContext = &m_buffer[i * m_BufferSize];
|
buf.AudioBytes = m_BufferSizeBytes;
|
||||||
buf.pAudioData = (BYTE *)buf.pContext;
|
buf.pContext = &m_buffer[i * m_BufferSize];
|
||||||
pSourceVoice->SubmitSourceBuffer(&buf);
|
buf.pAudioData = (BYTE*)buf.pContext;
|
||||||
}
|
pSourceVoice->SubmitSourceBuffer(&buf);
|
||||||
|
}
|
||||||
|
|
||||||
LeaveCriticalSection(&cs);
|
LeaveCriticalSection(&cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
STDMETHOD_(void, OnVoiceProcessingPassStart)
|
STDMETHOD_(void, OnVoiceProcessingPassStart)
|
||||||
() {}
|
() {}
|
||||||
STDMETHOD_(void, OnVoiceProcessingPassStart)
|
STDMETHOD_(void, OnVoiceProcessingPassStart)
|
||||||
(UINT32) {}
|
(UINT32) {}
|
||||||
STDMETHOD_(void, OnVoiceProcessingPassEnd)
|
STDMETHOD_(void, OnVoiceProcessingPassEnd)
|
||||||
() {}
|
() {}
|
||||||
STDMETHOD_(void, OnStreamEnd)
|
STDMETHOD_(void, OnStreamEnd)
|
||||||
() {}
|
() {}
|
||||||
STDMETHOD_(void, OnBufferStart)
|
STDMETHOD_(void, OnBufferStart)
|
||||||
(void *) {}
|
(void*) {}
|
||||||
STDMETHOD_(void, OnLoopEnd)
|
STDMETHOD_(void, OnLoopEnd)
|
||||||
(void *) {}
|
(void*) {}
|
||||||
STDMETHOD_(void, OnVoiceError)
|
STDMETHOD_(void, OnVoiceError)
|
||||||
(THIS_ void *pBufferContext, HRESULT Error) {}
|
(THIS_ void* pBufferContext, HRESULT Error) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class StreamingVoice : public BaseStreamingVoice
|
class StreamingVoice : public BaseStreamingVoice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StreamingVoice()
|
StreamingVoice()
|
||||||
: BaseStreamingVoice(sizeof(T) / sizeof(s16))
|
: BaseStreamingVoice(sizeof(T) / sizeof(s16))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~StreamingVoice()
|
virtual ~StreamingVoice()
|
||||||
{
|
{
|
||||||
IXAudio2SourceVoice *killMe = pSourceVoice;
|
IXAudio2SourceVoice* killMe = pSourceVoice;
|
||||||
// XXX: Potentially leads to a race condition that causes a nullptr
|
// XXX: Potentially leads to a race condition that causes a nullptr
|
||||||
// dereference when SubmitSourceBuffer is called in OnBufferEnd?
|
// dereference when SubmitSourceBuffer is called in OnBufferEnd?
|
||||||
pSourceVoice = nullptr;
|
pSourceVoice = nullptr;
|
||||||
if (killMe != nullptr) {
|
if (killMe != nullptr)
|
||||||
killMe->FlushSourceBuffers();
|
{
|
||||||
killMe->DestroyVoice();
|
killMe->FlushSourceBuffers();
|
||||||
}
|
killMe->DestroyVoice();
|
||||||
|
}
|
||||||
|
|
||||||
// XXX: Not sure we even need a critical section - DestroyVoice is
|
// XXX: Not sure we even need a critical section - DestroyVoice is
|
||||||
// blocking, and the documentation states no callbacks are called
|
// blocking, and the documentation states no callbacks are called
|
||||||
// or audio data is read after it returns, so it's safe to free up
|
// or audio data is read after it returns, so it's safe to free up
|
||||||
// resources.
|
// resources.
|
||||||
EnterCriticalSection(&cs);
|
EnterCriticalSection(&cs);
|
||||||
m_buffer = nullptr;
|
m_buffer = nullptr;
|
||||||
LeaveCriticalSection(&cs);
|
LeaveCriticalSection(&cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init(IXAudio2 *pXAudio2)
|
void Init(IXAudio2* pXAudio2)
|
||||||
{
|
{
|
||||||
int chanMask = 0;
|
int chanMask = 0;
|
||||||
switch (m_nChannels) {
|
switch (m_nChannels)
|
||||||
case 1:
|
{
|
||||||
chanMask |= SPEAKER_FRONT_CENTER;
|
case 1:
|
||||||
break;
|
chanMask |= SPEAKER_FRONT_CENTER;
|
||||||
case 2:
|
break;
|
||||||
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
case 2:
|
||||||
break;
|
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||||
case 3:
|
break;
|
||||||
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY;
|
case 3:
|
||||||
break;
|
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY;
|
||||||
case 4:
|
break;
|
||||||
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
case 4:
|
||||||
break;
|
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
||||||
case 5:
|
break;
|
||||||
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
case 5:
|
||||||
break;
|
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
||||||
case 6:
|
break;
|
||||||
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY;
|
case 6:
|
||||||
break;
|
chanMask |= SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY;
|
||||||
case 8:
|
break;
|
||||||
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;
|
case 8:
|
||||||
break;
|
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);
|
}
|
||||||
}
|
_init(pXAudio2, chanMask);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
STDMETHOD_(void, OnBufferEnd)
|
STDMETHOD_(void, OnBufferEnd)
|
||||||
(void *context)
|
(void* context)
|
||||||
{
|
{
|
||||||
EnterCriticalSection(&cs);
|
EnterCriticalSection(&cs);
|
||||||
|
|
||||||
// All of these checks are necessary because XAudio2 is wonky shizat.
|
// All of these checks are necessary because XAudio2 is wonky shizat.
|
||||||
// XXX: The pSourceVoice nullptr check seems a bit self-inflicted
|
// XXX: The pSourceVoice nullptr check seems a bit self-inflicted
|
||||||
// due to the destructor logic.
|
// due to the destructor logic.
|
||||||
if (pSourceVoice == nullptr || context == nullptr) {
|
if (pSourceVoice == nullptr || context == nullptr)
|
||||||
LeaveCriticalSection(&cs);
|
{
|
||||||
return;
|
LeaveCriticalSection(&cs);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
T *qb = (T *)context;
|
T* qb = (T*)context;
|
||||||
|
|
||||||
for (int p = 0; p < PacketsPerBuffer; p++, qb += SndOutPacketSize)
|
for (int p = 0; p < PacketsPerBuffer; p++, qb += SndOutPacketSize)
|
||||||
SndBuffer::ReadSamples(qb);
|
SndBuffer::ReadSamples(qb);
|
||||||
|
|
||||||
XAUDIO2_BUFFER buf = {0};
|
XAUDIO2_BUFFER buf = {0};
|
||||||
buf.AudioBytes = m_BufferSizeBytes;
|
buf.AudioBytes = m_BufferSizeBytes;
|
||||||
buf.pAudioData = (BYTE *)context;
|
buf.pAudioData = (BYTE*)context;
|
||||||
buf.pContext = context;
|
buf.pContext = context;
|
||||||
|
|
||||||
pSourceVoice->SubmitSourceBuffer(&buf);
|
pSourceVoice->SubmitSourceBuffer(&buf);
|
||||||
LeaveCriticalSection(&cs);
|
LeaveCriticalSection(&cs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
HMODULE xAudio2DLL = nullptr;
|
HMODULE xAudio2DLL = nullptr;
|
||||||
decltype(&XAudio2Create) pXAudio2Create = nullptr;
|
decltype(&XAudio2Create) pXAudio2Create = nullptr;
|
||||||
CComPtr<IXAudio2> pXAudio2;
|
CComPtr<IXAudio2> pXAudio2;
|
||||||
IXAudio2MasteringVoice *pMasteringVoice = nullptr;
|
IXAudio2MasteringVoice* pMasteringVoice = nullptr;
|
||||||
std::unique_ptr<BaseStreamingVoice> m_voiceContext;
|
std::unique_ptr<BaseStreamingVoice> m_voiceContext;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
s32 Init()
|
s32 Init()
|
||||||
{
|
{
|
||||||
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||||
|
|
||||||
try {
|
try
|
||||||
HRESULT hr;
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
xAudio2DLL = LoadLibraryEx(XAUDIO2_DLL, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
xAudio2DLL = LoadLibraryEx(XAUDIO2_DLL, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||||
if (xAudio2DLL == nullptr)
|
if (xAudio2DLL == nullptr)
|
||||||
throw std::runtime_error("Could not load " XAUDIO2_DLL_A ". Error code:" + std::to_string(GetLastError()));
|
throw std::runtime_error("Could not load " XAUDIO2_DLL_A ". Error code:" + std::to_string(GetLastError()));
|
||||||
|
|
||||||
pXAudio2Create = reinterpret_cast<decltype(&XAudio2Create)>(GetProcAddress(xAudio2DLL, "XAudio2Create"));
|
pXAudio2Create = reinterpret_cast<decltype(&XAudio2Create)>(GetProcAddress(xAudio2DLL, "XAudio2Create"));
|
||||||
if (pXAudio2Create == nullptr)
|
if (pXAudio2Create == nullptr)
|
||||||
throw std::runtime_error("XAudio2Create not found. Error code: " + std::to_string(GetLastError()));
|
throw std::runtime_error("XAudio2Create not found. Error code: " + std::to_string(GetLastError()));
|
||||||
|
|
||||||
if (FAILED(hr = pXAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR)))
|
if (FAILED(hr = pXAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR)))
|
||||||
throw Exception::XAudio2Error(hr, "Failed to init XAudio2 engine. Error Details:");
|
throw Exception::XAudio2Error(hr, "Failed to init XAudio2 engine. Error Details:");
|
||||||
|
|
||||||
// Stereo Expansion was planned to grab the currently configured number of
|
// Stereo Expansion was planned to grab the currently configured number of
|
||||||
// Speakers from Windows's audio config.
|
// Speakers from Windows's audio config.
|
||||||
// This doesn't always work though, so let it be a user configurable option.
|
// This doesn't always work though, so let it be a user configurable option.
|
||||||
|
|
||||||
int speakers;
|
int speakers;
|
||||||
// speakers = (numSpeakers + 1) *2; ?
|
// speakers = (numSpeakers + 1) *2; ?
|
||||||
switch (numSpeakers) {
|
switch (numSpeakers)
|
||||||
case 0: // Stereo
|
{
|
||||||
speakers = 2;
|
case 0: // Stereo
|
||||||
break;
|
speakers = 2;
|
||||||
case 1: // Quadrafonic
|
break;
|
||||||
speakers = 4;
|
case 1: // Quadrafonic
|
||||||
break;
|
speakers = 4;
|
||||||
case 2: // Surround 5.1
|
break;
|
||||||
speakers = 6;
|
case 2: // Surround 5.1
|
||||||
break;
|
speakers = 6;
|
||||||
case 3: // Surround 7.1
|
break;
|
||||||
speakers = 8;
|
case 3: // Surround 7.1
|
||||||
break;
|
speakers = 8;
|
||||||
default:
|
break;
|
||||||
speakers = 2;
|
default:
|
||||||
}
|
speakers = 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (FAILED(hr = pXAudio2->CreateMasteringVoice(&pMasteringVoice, speakers, SampleRate)))
|
if (FAILED(hr = pXAudio2->CreateMasteringVoice(&pMasteringVoice, speakers, SampleRate)))
|
||||||
throw Exception::XAudio2Error(hr, "Failed creating mastering voice: ");
|
throw Exception::XAudio2Error(hr, "Failed creating mastering voice: ");
|
||||||
|
|
||||||
switch (speakers) {
|
switch (speakers)
|
||||||
case 2:
|
{
|
||||||
ConLog("* SPU2 > Using normal 2 speaker stereo output.\n");
|
case 2:
|
||||||
m_voiceContext = std::make_unique<StreamingVoice<StereoOut16>>();
|
ConLog("* SPU2 > Using normal 2 speaker stereo output.\n");
|
||||||
break;
|
m_voiceContext = std::make_unique<StreamingVoice<StereoOut16>>();
|
||||||
case 3:
|
break;
|
||||||
ConLog("* SPU2 > 2.1 speaker expansion enabled.\n");
|
case 3:
|
||||||
m_voiceContext = std::make_unique<StreamingVoice<Stereo21Out16>>();
|
ConLog("* SPU2 > 2.1 speaker expansion enabled.\n");
|
||||||
break;
|
m_voiceContext = std::make_unique<StreamingVoice<Stereo21Out16>>();
|
||||||
case 4:
|
break;
|
||||||
ConLog("* SPU2 > 4 speaker expansion enabled [quadraphenia]\n");
|
case 4:
|
||||||
m_voiceContext = std::make_unique<StreamingVoice<Stereo40Out16>>();
|
ConLog("* SPU2 > 4 speaker expansion enabled [quadraphenia]\n");
|
||||||
break;
|
m_voiceContext = std::make_unique<StreamingVoice<Stereo40Out16>>();
|
||||||
case 5:
|
break;
|
||||||
ConLog("* SPU2 > 4.1 speaker expansion enabled.\n");
|
case 5:
|
||||||
m_voiceContext = std::make_unique<StreamingVoice<Stereo41Out16>>();
|
ConLog("* SPU2 > 4.1 speaker expansion enabled.\n");
|
||||||
break;
|
m_voiceContext = std::make_unique<StreamingVoice<Stereo41Out16>>();
|
||||||
case 6:
|
break;
|
||||||
case 7:
|
case 6:
|
||||||
switch (dplLevel) {
|
case 7:
|
||||||
case 0: // "normal" stereo upmix
|
switch (dplLevel)
|
||||||
ConLog("* SPU2 > 5.1 speaker expansion enabled.\n");
|
{
|
||||||
m_voiceContext = std::make_unique<StreamingVoice<Stereo51Out16>>();
|
case 0: // "normal" stereo upmix
|
||||||
break;
|
ConLog("* SPU2 > 5.1 speaker expansion enabled.\n");
|
||||||
case 1: // basic Dpl decoder without rear stereo balancing
|
m_voiceContext = std::make_unique<StreamingVoice<Stereo51Out16>>();
|
||||||
ConLog("* SPU2 > 5.1 speaker expansion with basic ProLogic dematrixing enabled.\n");
|
break;
|
||||||
m_voiceContext = std::make_unique<StreamingVoice<Stereo51Out16Dpl>>();
|
case 1: // basic Dpl decoder without rear stereo balancing
|
||||||
break;
|
ConLog("* SPU2 > 5.1 speaker expansion with basic ProLogic dematrixing enabled.\n");
|
||||||
case 2: // gigas PLII
|
m_voiceContext = std::make_unique<StreamingVoice<Stereo51Out16Dpl>>();
|
||||||
ConLog("* SPU2 > 5.1 speaker expansion with experimental ProLogicII dematrixing enabled.\n");
|
break;
|
||||||
m_voiceContext = std::make_unique<StreamingVoice<Stereo51Out16DplII>>();
|
case 2: // gigas PLII
|
||||||
break;
|
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");
|
break;
|
||||||
m_voiceContext = std::make_unique<StreamingVoice<Stereo51Out16>>();
|
default: // anything 8 or more gets the 7.1 treatment!
|
||||||
break;
|
ConLog("* SPU2 > 7.1 speaker expansion enabled.\n");
|
||||||
}
|
m_voiceContext = std::make_unique<StreamingVoice<Stereo51Out16>>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
m_voiceContext->Init(pXAudio2);
|
m_voiceContext->Init(pXAudio2);
|
||||||
} catch (std::runtime_error &ex) {
|
}
|
||||||
SysMessage(ex.what());
|
catch (std::runtime_error& ex)
|
||||||
Close();
|
{
|
||||||
return -1;
|
SysMessage(ex.what());
|
||||||
}
|
Close();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Close()
|
void Close()
|
||||||
{
|
{
|
||||||
// Clean up?
|
// Clean up?
|
||||||
// All XAudio2 interfaces are released when the engine is destroyed,
|
// All XAudio2 interfaces are released when the engine is destroyed,
|
||||||
// but being tidy never hurt.
|
// but being tidy never hurt.
|
||||||
|
|
||||||
// Actually it can hurt. As of DXSDK Aug 2008, doing a full cleanup causes
|
// 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.
|
// 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
|
// 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:
|
// and error we found a happy compromise as follows:
|
||||||
|
|
||||||
m_voiceContext = nullptr;
|
m_voiceContext = nullptr;
|
||||||
|
|
||||||
if (pMasteringVoice != nullptr)
|
if (pMasteringVoice != nullptr)
|
||||||
pMasteringVoice->DestroyVoice();
|
pMasteringVoice->DestroyVoice();
|
||||||
|
|
||||||
pMasteringVoice = nullptr;
|
pMasteringVoice = nullptr;
|
||||||
|
|
||||||
pXAudio2.Release();
|
pXAudio2.Release();
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
|
|
||||||
if (xAudio2DLL) {
|
if (xAudio2DLL)
|
||||||
FreeLibrary(xAudio2DLL);
|
{
|
||||||
xAudio2DLL = nullptr;
|
FreeLibrary(xAudio2DLL);
|
||||||
pXAudio2Create = nullptr;
|
xAudio2DLL = nullptr;
|
||||||
}
|
pXAudio2Create = nullptr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual void Configure(uptr parent)
|
virtual void Configure(uptr parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 Test() const
|
s32 Test() const
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetEmptySampleCount()
|
int GetEmptySampleCount()
|
||||||
{
|
{
|
||||||
if (m_voiceContext == nullptr)
|
if (m_voiceContext == nullptr)
|
||||||
return 0;
|
return 0;
|
||||||
return m_voiceContext->GetEmptySampleCount();
|
return m_voiceContext->GetEmptySampleCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *GetIdent() const
|
const wchar_t* GetIdent() const
|
||||||
{
|
{
|
||||||
return L"xaudio2";
|
return L"xaudio2";
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *GetLongName() const
|
const wchar_t* GetLongName() const
|
||||||
{
|
{
|
||||||
return L"XAudio 2 (Recommended)";
|
return L"XAudio 2 (Recommended)";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadSettings()
|
void ReadSettings()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetApiSettings(wxString api)
|
void SetApiSettings(wxString api)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteSettings() const
|
void WriteSettings() const
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
} static XA2;
|
} static XA2;
|
||||||
|
|
||||||
SndOutModule *XAudio2Out = &XA2;
|
SndOutModule* XAudio2Out = &XA2;
|
||||||
|
|
|
@ -20,75 +20,77 @@
|
||||||
class WaveOutModule : public SndOutModule
|
class WaveOutModule : public SndOutModule
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
static const uint MAX_BUFFER_COUNT = 8;
|
static const uint MAX_BUFFER_COUNT = 8;
|
||||||
|
|
||||||
static const int PacketsPerBuffer = (1024 / SndOutPacketSize);
|
static const int PacketsPerBuffer = (1024 / SndOutPacketSize);
|
||||||
static const int BufferSize = SndOutPacketSize * PacketsPerBuffer;
|
static const int BufferSize = SndOutPacketSize * PacketsPerBuffer;
|
||||||
|
|
||||||
u32 numBuffers;
|
u32 numBuffers;
|
||||||
HWAVEOUT hwodevice;
|
HWAVEOUT hwodevice;
|
||||||
WAVEFORMATEX wformat;
|
WAVEFORMATEX wformat;
|
||||||
WAVEHDR whbuffer[MAX_BUFFER_COUNT];
|
WAVEHDR whbuffer[MAX_BUFFER_COUNT];
|
||||||
|
|
||||||
StereoOut16 *qbuffer;
|
StereoOut16* qbuffer;
|
||||||
|
|
||||||
#define QBUFFER(x) (qbuffer + BufferSize * (x))
|
#define QBUFFER(x) (qbuffer + BufferSize * (x))
|
||||||
|
|
||||||
bool waveout_running;
|
bool waveout_running;
|
||||||
HANDLE thread;
|
HANDLE thread;
|
||||||
DWORD tid;
|
DWORD tid;
|
||||||
|
|
||||||
wchar_t ErrText[256];
|
wchar_t ErrText[256];
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
DWORD CALLBACK Thread()
|
DWORD CALLBACK Thread()
|
||||||
{
|
{
|
||||||
static const int BufferSizeBytes = BufferSize * sizeof(T);
|
static const int BufferSizeBytes = BufferSize * sizeof(T);
|
||||||
|
|
||||||
while (waveout_running) {
|
while (waveout_running)
|
||||||
bool didsomething = false;
|
{
|
||||||
for (u32 i = 0; i < numBuffers; i++) {
|
bool didsomething = false;
|
||||||
if (!(whbuffer[i].dwFlags & WHDR_DONE))
|
for (u32 i = 0; i < numBuffers; i++)
|
||||||
continue;
|
{
|
||||||
|
if (!(whbuffer[i].dwFlags & WHDR_DONE))
|
||||||
|
continue;
|
||||||
|
|
||||||
WAVEHDR *buf = whbuffer + i;
|
WAVEHDR* buf = whbuffer + i;
|
||||||
|
|
||||||
buf->dwBytesRecorded = buf->dwBufferLength;
|
buf->dwBytesRecorded = buf->dwBufferLength;
|
||||||
|
|
||||||
T *t = (T *)buf->lpData;
|
T* t = (T*)buf->lpData;
|
||||||
for (int p = 0; p < PacketsPerBuffer; p++, t += SndOutPacketSize)
|
for (int p = 0; p < PacketsPerBuffer; p++, t += SndOutPacketSize)
|
||||||
SndBuffer::ReadSamples(t);
|
SndBuffer::ReadSamples(t);
|
||||||
|
|
||||||
whbuffer[i].dwFlags &= ~WHDR_DONE;
|
whbuffer[i].dwFlags &= ~WHDR_DONE;
|
||||||
waveOutWrite(hwodevice, buf, sizeof(WAVEHDR));
|
waveOutWrite(hwodevice, buf, sizeof(WAVEHDR));
|
||||||
didsomething = true;
|
didsomething = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (didsomething)
|
if (didsomething)
|
||||||
Sleep(1);
|
Sleep(1);
|
||||||
else
|
else
|
||||||
Sleep(0);
|
Sleep(0);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static DWORD CALLBACK RThread(WaveOutModule *obj)
|
static DWORD CALLBACK RThread(WaveOutModule* obj)
|
||||||
{
|
{
|
||||||
return obj->Thread<T>();
|
return obj->Thread<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
s32 Init()
|
s32 Init()
|
||||||
{
|
{
|
||||||
numBuffers = Config_WaveOut.NumBuffers;
|
numBuffers = Config_WaveOut.NumBuffers;
|
||||||
|
|
||||||
MMRESULT woores;
|
MMRESULT woores;
|
||||||
|
|
||||||
if (Test())
|
if (Test())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
// TODO : Use dsound to determine the speaker configuration, and expand audio from there.
|
// TODO : Use dsound to determine the speaker configuration, and expand audio from there.
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
int speakerConfig;
|
int speakerConfig;
|
||||||
|
@ -129,193 +131,203 @@ public:
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
wformat.wFormatTag = WAVE_FORMAT_PCM;
|
wformat.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
wformat.nSamplesPerSec = SampleRate;
|
wformat.nSamplesPerSec = SampleRate;
|
||||||
wformat.wBitsPerSample = 16;
|
wformat.wBitsPerSample = 16;
|
||||||
wformat.nChannels = 2;
|
wformat.nChannels = 2;
|
||||||
wformat.nBlockAlign = ((wformat.wBitsPerSample * wformat.nChannels) / 8);
|
wformat.nBlockAlign = ((wformat.wBitsPerSample * wformat.nChannels) / 8);
|
||||||
wformat.nAvgBytesPerSec = (wformat.nSamplesPerSec * wformat.nBlockAlign);
|
wformat.nAvgBytesPerSec = (wformat.nSamplesPerSec * wformat.nBlockAlign);
|
||||||
wformat.cbSize = 0;
|
wformat.cbSize = 0;
|
||||||
|
|
||||||
qbuffer = new StereoOut16[BufferSize * numBuffers];
|
qbuffer = new StereoOut16[BufferSize * numBuffers];
|
||||||
|
|
||||||
woores = waveOutOpen(&hwodevice, WAVE_MAPPER, &wformat, 0, 0, 0);
|
woores = waveOutOpen(&hwodevice, WAVE_MAPPER, &wformat, 0, 0, 0);
|
||||||
if (woores != MMSYSERR_NOERROR) {
|
if (woores != MMSYSERR_NOERROR)
|
||||||
waveOutGetErrorText(woores, (wchar_t *)&ErrText, 255);
|
{
|
||||||
SysMessage("WaveOut Error: %s", ErrText);
|
waveOutGetErrorText(woores, (wchar_t*)&ErrText, 255);
|
||||||
return -1;
|
SysMessage("WaveOut Error: %s", ErrText);
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
const int BufferSizeBytes = wformat.nBlockAlign * BufferSize;
|
const int BufferSizeBytes = wformat.nBlockAlign * BufferSize;
|
||||||
|
|
||||||
for (u32 i = 0; i < numBuffers; i++) {
|
for (u32 i = 0; i < numBuffers; i++)
|
||||||
whbuffer[i].dwBufferLength = BufferSizeBytes;
|
{
|
||||||
whbuffer[i].dwBytesRecorded = BufferSizeBytes;
|
whbuffer[i].dwBufferLength = BufferSizeBytes;
|
||||||
whbuffer[i].dwFlags = 0;
|
whbuffer[i].dwBytesRecorded = BufferSizeBytes;
|
||||||
whbuffer[i].dwLoops = 0;
|
whbuffer[i].dwFlags = 0;
|
||||||
whbuffer[i].dwUser = 0;
|
whbuffer[i].dwLoops = 0;
|
||||||
whbuffer[i].lpData = (LPSTR)QBUFFER(i);
|
whbuffer[i].dwUser = 0;
|
||||||
whbuffer[i].lpNext = 0;
|
whbuffer[i].lpData = (LPSTR)QBUFFER(i);
|
||||||
whbuffer[i].reserved = 0;
|
whbuffer[i].lpNext = 0;
|
||||||
waveOutPrepareHeader(hwodevice, whbuffer + i, sizeof(WAVEHDR));
|
whbuffer[i].reserved = 0;
|
||||||
whbuffer[i].dwFlags |= WHDR_DONE; //avoid deadlock
|
waveOutPrepareHeader(hwodevice, whbuffer + i, sizeof(WAVEHDR));
|
||||||
}
|
whbuffer[i].dwFlags |= WHDR_DONE; //avoid deadlock
|
||||||
|
}
|
||||||
|
|
||||||
// Start Thread
|
// Start Thread
|
||||||
// [Air]: The waveout code does not use wait objects, so setting a time critical
|
// [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
|
// 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
|
// love it needs and won't suck resources idling pointlessly. Just don't try to
|
||||||
// run it in uber-low-latency mode.
|
// run it in uber-low-latency mode.
|
||||||
waveout_running = true;
|
waveout_running = true;
|
||||||
thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RThread<StereoOut16>, this, 0, &tid);
|
thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RThread<StereoOut16>, this, 0, &tid);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Close()
|
void Close()
|
||||||
{
|
{
|
||||||
// Stop Thread
|
// Stop Thread
|
||||||
fprintf(stderr, "* SPU2-X: Waiting for waveOut thread to finish...");
|
fprintf(stderr, "* SPU2-X: Waiting for waveOut thread to finish...");
|
||||||
waveout_running = false;
|
waveout_running = false;
|
||||||
|
|
||||||
WaitForSingleObject(thread, INFINITE);
|
WaitForSingleObject(thread, INFINITE);
|
||||||
CloseHandle(thread);
|
CloseHandle(thread);
|
||||||
|
|
||||||
fprintf(stderr, " Done.\n");
|
fprintf(stderr, " Done.\n");
|
||||||
|
|
||||||
//
|
//
|
||||||
// Clean up
|
// Clean up
|
||||||
//
|
//
|
||||||
waveOutReset(hwodevice);
|
waveOutReset(hwodevice);
|
||||||
for (u32 i = 0; i < numBuffers; i++) {
|
for (u32 i = 0; i < numBuffers; i++)
|
||||||
waveOutUnprepareHeader(hwodevice, &whbuffer[i], sizeof(WAVEHDR));
|
{
|
||||||
}
|
waveOutUnprepareHeader(hwodevice, &whbuffer[i], sizeof(WAVEHDR));
|
||||||
waveOutClose(hwodevice);
|
}
|
||||||
|
waveOutClose(hwodevice);
|
||||||
|
|
||||||
safe_delete_array(qbuffer);
|
safe_delete_array(qbuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static BOOL CALLBACK ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
static BOOL CALLBACK ConfigProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
int wmId, wmEvent;
|
int wmId, wmEvent;
|
||||||
|
|
||||||
switch (uMsg) {
|
switch (uMsg)
|
||||||
case WM_INITDIALOG:
|
{
|
||||||
|
case WM_INITDIALOG:
|
||||||
|
|
||||||
wchar_t temp[128];
|
wchar_t temp[128];
|
||||||
INIT_SLIDER(IDC_BUFFERS_SLIDER, 3, MAX_BUFFER_COUNT, 2, 1, 1);
|
INIT_SLIDER(IDC_BUFFERS_SLIDER, 3, MAX_BUFFER_COUNT, 2, 1, 1);
|
||||||
SendMessage(GetDlgItem(hWnd, IDC_BUFFERS_SLIDER), TBM_SETPOS, TRUE, Config_WaveOut.NumBuffers);
|
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)));
|
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);
|
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WM_COMMAND:
|
case WM_COMMAND:
|
||||||
wmId = LOWORD(wParam);
|
wmId = LOWORD(wParam);
|
||||||
wmEvent = HIWORD(wParam);
|
wmEvent = HIWORD(wParam);
|
||||||
// Parse the menu selections:
|
// Parse the menu selections:
|
||||||
switch (wmId) {
|
switch (wmId)
|
||||||
case IDOK: {
|
{
|
||||||
Config_WaveOut.NumBuffers = (int)SendMessage(GetDlgItem(hWnd, IDC_BUFFERS_SLIDER), TBM_GETPOS, 0, 0);
|
case IDOK:
|
||||||
|
{
|
||||||
|
Config_WaveOut.NumBuffers = (int)SendMessage(GetDlgItem(hWnd, IDC_BUFFERS_SLIDER), TBM_GETPOS, 0, 0);
|
||||||
|
|
||||||
if (Config_WaveOut.NumBuffers < 3)
|
if (Config_WaveOut.NumBuffers < 3)
|
||||||
Config_WaveOut.NumBuffers = 3;
|
Config_WaveOut.NumBuffers = 3;
|
||||||
if (Config_WaveOut.NumBuffers > MAX_BUFFER_COUNT)
|
if (Config_WaveOut.NumBuffers > MAX_BUFFER_COUNT)
|
||||||
Config_WaveOut.NumBuffers = MAX_BUFFER_COUNT;
|
Config_WaveOut.NumBuffers = MAX_BUFFER_COUNT;
|
||||||
}
|
}
|
||||||
EndDialog(hWnd, 0);
|
EndDialog(hWnd, 0);
|
||||||
break;
|
break;
|
||||||
case IDCANCEL:
|
case IDCANCEL:
|
||||||
EndDialog(hWnd, 0);
|
EndDialog(hWnd, 0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WM_HSCROLL:
|
case WM_HSCROLL:
|
||||||
wmId = LOWORD(wParam);
|
wmId = LOWORD(wParam);
|
||||||
wmEvent = HIWORD(wParam);
|
wmEvent = HIWORD(wParam);
|
||||||
switch (wmId) {
|
switch (wmId)
|
||||||
case TB_LINEUP:
|
{
|
||||||
case TB_LINEDOWN:
|
case TB_LINEUP:
|
||||||
case TB_PAGEUP:
|
case TB_LINEDOWN:
|
||||||
case TB_PAGEDOWN:
|
case TB_PAGEUP:
|
||||||
case TB_TOP:
|
case TB_PAGEDOWN:
|
||||||
case TB_BOTTOM:
|
case TB_TOP:
|
||||||
wmEvent = (int)SendMessage((HWND)lParam, TBM_GETPOS, 0, 0);
|
case TB_BOTTOM:
|
||||||
case TB_THUMBPOSITION:
|
wmEvent = (int)SendMessage((HWND)lParam, TBM_GETPOS, 0, 0);
|
||||||
case TB_THUMBTRACK:
|
case TB_THUMBPOSITION:
|
||||||
if (wmEvent < 3)
|
case TB_THUMBTRACK:
|
||||||
wmEvent = 3;
|
if (wmEvent < 3)
|
||||||
if (wmEvent > MAX_BUFFER_COUNT)
|
wmEvent = 3;
|
||||||
wmEvent = MAX_BUFFER_COUNT;
|
if (wmEvent > MAX_BUFFER_COUNT)
|
||||||
SendMessage((HWND)lParam, TBM_SETPOS, TRUE, wmEvent);
|
wmEvent = MAX_BUFFER_COUNT;
|
||||||
swprintf_s(temp, L"%d (%d ms latency)", wmEvent, 1000 / (96000 / (wmEvent * BufferSize)));
|
SendMessage((HWND)lParam, TBM_SETPOS, TRUE, wmEvent);
|
||||||
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
|
swprintf_s(temp, L"%d (%d ms latency)", wmEvent, 1000 / (96000 / (wmEvent * BufferSize)));
|
||||||
break;
|
SetWindowText(GetDlgItem(hWnd, IDC_LATENCY_LABEL), temp);
|
||||||
default:
|
break;
|
||||||
return FALSE;
|
default:
|
||||||
}
|
return FALSE;
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void Configure(uptr parent)
|
virtual void Configure(uptr parent)
|
||||||
{
|
{
|
||||||
INT_PTR ret;
|
INT_PTR ret;
|
||||||
ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_WAVEOUT), (HWND)parent, (DLGPROC)ConfigProc, 1);
|
ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_WAVEOUT), (HWND)parent, (DLGPROC)ConfigProc, 1);
|
||||||
if (ret == -1) {
|
if (ret == -1)
|
||||||
MessageBox((HWND)parent, L"Error Opening the config dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
|
{
|
||||||
return;
|
MessageBox((HWND)parent, L"Error Opening the config dialog.", L"OMG ERROR!", MB_OK | MB_SETFOREGROUND);
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s32 Test() const
|
s32 Test() const
|
||||||
{
|
{
|
||||||
if (waveOutGetNumDevs() == 0) {
|
if (waveOutGetNumDevs() == 0)
|
||||||
SysMessage("No waveOut Devices Present\n");
|
{
|
||||||
return -1;
|
SysMessage("No waveOut Devices Present\n");
|
||||||
}
|
return -1;
|
||||||
return 0;
|
}
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int GetEmptySampleCount()
|
int GetEmptySampleCount()
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
for (int i = 0; i < MAX_BUFFER_COUNT; i++) {
|
for (int i = 0; i < MAX_BUFFER_COUNT; i++)
|
||||||
result += (whbuffer[i].dwFlags & WHDR_DONE) ? BufferSize : 0;
|
{
|
||||||
}
|
result += (whbuffer[i].dwFlags & WHDR_DONE) ? BufferSize : 0;
|
||||||
return result;
|
}
|
||||||
}
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
const wchar_t *GetIdent() const
|
const wchar_t* GetIdent() const
|
||||||
{
|
{
|
||||||
return L"waveout";
|
return L"waveout";
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *GetLongName() const
|
const wchar_t* GetLongName() const
|
||||||
{
|
{
|
||||||
return L"WaveOut (Laggy)";
|
return L"WaveOut (Laggy)";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReadSettings()
|
void ReadSettings()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetApiSettings(wxString api)
|
void SetApiSettings(wxString api)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteSettings() const
|
void WriteSettings() const
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
} static WO;
|
} static WO;
|
||||||
|
|
||||||
SndOutModule *WaveOut = &WO;
|
SndOutModule* WaveOut = &WO;
|
||||||
|
|
|
@ -18,30 +18,31 @@
|
||||||
|
|
||||||
int SendDialogMsg(HWND hwnd, int dlgId, UINT code, WPARAM wParam, LPARAM lParam)
|
int SendDialogMsg(HWND hwnd, int dlgId, UINT code, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
return SendMessage(GetDlgItem(hwnd, dlgId), code, wParam, lParam);
|
return SendMessage(GetDlgItem(hwnd, dlgId), code, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
__forceinline void Verifyc(HRESULT hr, const char *fn)
|
__forceinline void Verifyc(HRESULT hr, const char* fn)
|
||||||
{
|
{
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr))
|
||||||
assert(0);
|
{
|
||||||
throw std::runtime_error("DirectSound returned an error from %s");
|
assert(0);
|
||||||
}
|
throw std::runtime_error("DirectSound returned an error from %s");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssignSliderValue(HWND idcwnd, HWND hwndDisplay, int value)
|
void AssignSliderValue(HWND idcwnd, HWND hwndDisplay, int value)
|
||||||
{
|
{
|
||||||
value = std::min(std::max(value, 0), 512);
|
value = std::min(std::max(value, 0), 512);
|
||||||
SendMessage(idcwnd, TBM_SETPOS, TRUE, value);
|
SendMessage(idcwnd, TBM_SETPOS, TRUE, value);
|
||||||
|
|
||||||
wchar_t tbox[32];
|
wchar_t tbox[32];
|
||||||
swprintf_s(tbox, L"%d", value);
|
swprintf_s(tbox, L"%d", value);
|
||||||
SetWindowText(hwndDisplay, tbox);
|
SetWindowText(hwndDisplay, tbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssignSliderValue(HWND hWnd, int idc, int editbox, int value)
|
void AssignSliderValue(HWND hWnd, int idc, int editbox, int value)
|
||||||
{
|
{
|
||||||
AssignSliderValue(GetDlgItem(hWnd, idc), GetDlgItem(hWnd, editbox), value);
|
AssignSliderValue(GetDlgItem(hWnd, idc), GetDlgItem(hWnd, editbox), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic slider/scroller message handler. This is succient so long as you
|
// Generic slider/scroller message handler. This is succient so long as you
|
||||||
|
@ -49,29 +50,30 @@ void AssignSliderValue(HWND hWnd, int idc, int editbox, int value)
|
||||||
// updating a custom label.
|
// updating a custom label.
|
||||||
BOOL DoHandleScrollMessage(HWND hwndDisplay, WPARAM wParam, LPARAM lParam)
|
BOOL DoHandleScrollMessage(HWND hwndDisplay, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
int wmId = LOWORD(wParam);
|
int wmId = LOWORD(wParam);
|
||||||
int wmEvent = HIWORD(wParam);
|
int wmEvent = HIWORD(wParam);
|
||||||
|
|
||||||
switch (wmId) {
|
switch (wmId)
|
||||||
//case TB_ENDTRACK:
|
{
|
||||||
//case TB_THUMBPOSITION:
|
//case TB_ENDTRACK:
|
||||||
case TB_LINEUP:
|
//case TB_THUMBPOSITION:
|
||||||
case TB_LINEDOWN:
|
case TB_LINEUP:
|
||||||
case TB_PAGEUP:
|
case TB_LINEDOWN:
|
||||||
case TB_PAGEDOWN:
|
case TB_PAGEUP:
|
||||||
wmEvent = (int)SendMessage((HWND)lParam, TBM_GETPOS, 0, 0);
|
case TB_PAGEDOWN:
|
||||||
case TB_THUMBTRACK:
|
wmEvent = (int)SendMessage((HWND)lParam, TBM_GETPOS, 0, 0);
|
||||||
AssignSliderValue((HWND)lParam, hwndDisplay, wmEvent);
|
case TB_THUMBTRACK:
|
||||||
break;
|
AssignSliderValue((HWND)lParam, hwndDisplay, wmEvent);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetSliderValue(HWND hWnd, int idc)
|
int GetSliderValue(HWND hWnd, int idc)
|
||||||
{
|
{
|
||||||
int retval = (int)SendMessage(GetDlgItem(hWnd, idc), TBM_GETPOS, 0, 0);
|
int retval = (int)SendMessage(GetDlgItem(hWnd, idc), TBM_GETPOS, 0, 0);
|
||||||
return GetClamped(retval, 0, 512);
|
return GetClamped(retval, 0, 512);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,26 +31,26 @@ extern HINSTANCE hInstance;
|
||||||
|
|
||||||
#define SET_CHECK(idc, value) SendMessage(GetDlgItem(hWnd, idc), BM_SETCHECK, ((value) == 0) ? BST_UNCHECKED : BST_CHECKED, 0)
|
#define SET_CHECK(idc, value) SendMessage(GetDlgItem(hWnd, idc), BM_SETCHECK, ((value) == 0) ? BST_UNCHECKED : BST_CHECKED, 0)
|
||||||
#define HANDLE_CHECK(idc, hvar) \
|
#define HANDLE_CHECK(idc, hvar) \
|
||||||
case idc: \
|
case idc: \
|
||||||
(hvar) = !(hvar); \
|
(hvar) = !(hvar); \
|
||||||
SendMessage(GetDlgItem(hWnd, idc), BM_SETCHECK, (hvar) ? BST_CHECKED : BST_UNCHECKED, 0); \
|
SendMessage(GetDlgItem(hWnd, idc), BM_SETCHECK, (hvar) ? BST_CHECKED : BST_UNCHECKED, 0); \
|
||||||
break
|
break
|
||||||
#define HANDLE_CHECKNB(idc, hvar) \
|
#define HANDLE_CHECKNB(idc, hvar) \
|
||||||
case idc: \
|
case idc: \
|
||||||
(hvar) = !(hvar); \
|
(hvar) = !(hvar); \
|
||||||
SendMessage(GetDlgItem(hWnd, idc), BM_SETCHECK, (hvar) ? BST_CHECKED : BST_UNCHECKED, 0)
|
SendMessage(GetDlgItem(hWnd, idc), BM_SETCHECK, (hvar) ? BST_CHECKED : BST_UNCHECKED, 0)
|
||||||
#define ENABLE_CONTROL(idc, value) EnableWindow(GetDlgItem(hWnd, idc), value)
|
#define ENABLE_CONTROL(idc, value) EnableWindow(GetDlgItem(hWnd, idc), value)
|
||||||
|
|
||||||
#define INIT_SLIDER(idc, minrange, maxrange, tickfreq, pagesize, linesize) \
|
#define INIT_SLIDER(idc, minrange, maxrange, tickfreq, pagesize, linesize) \
|
||||||
SendMessage(GetDlgItem(hWnd, idc), TBM_SETRANGEMIN, FALSE, minrange); \
|
SendMessage(GetDlgItem(hWnd, idc), TBM_SETRANGEMIN, FALSE, minrange); \
|
||||||
SendMessage(GetDlgItem(hWnd, idc), TBM_SETRANGEMAX, FALSE, maxrange); \
|
SendMessage(GetDlgItem(hWnd, idc), TBM_SETRANGEMAX, FALSE, maxrange); \
|
||||||
SendMessage(GetDlgItem(hWnd, idc), TBM_SETTICFREQ, tickfreq, 0); \
|
SendMessage(GetDlgItem(hWnd, idc), TBM_SETTICFREQ, tickfreq, 0); \
|
||||||
SendMessage(GetDlgItem(hWnd, idc), TBM_SETPAGESIZE, 0, pagesize); \
|
SendMessage(GetDlgItem(hWnd, idc), TBM_SETPAGESIZE, 0, pagesize); \
|
||||||
SendMessage(GetDlgItem(hWnd, idc), TBM_SETLINESIZE, 0, linesize)
|
SendMessage(GetDlgItem(hWnd, idc), TBM_SETLINESIZE, 0, linesize)
|
||||||
|
|
||||||
#define HANDLE_SCROLL_MESSAGE(idc, idcDisplay) \
|
#define HANDLE_SCROLL_MESSAGE(idc, idcDisplay) \
|
||||||
if ((HWND)lParam == GetDlgItem(hWnd, idc)) \
|
if ((HWND)lParam == GetDlgItem(hWnd, idc)) \
|
||||||
return DoHandleScrollMessage(GetDlgItem(hWnd, idcDisplay), wParam, lParam)
|
return DoHandleScrollMessage(GetDlgItem(hWnd, idcDisplay), wParam, lParam)
|
||||||
|
|
||||||
|
|
||||||
// *** BEGIN DRIVER-SPECIFIC CONFIGURATION ***
|
// *** BEGIN DRIVER-SPECIFIC CONFIGURATION ***
|
||||||
|
@ -58,26 +58,26 @@ extern HINSTANCE hInstance;
|
||||||
|
|
||||||
struct CONFIG_XAUDIO2
|
struct CONFIG_XAUDIO2
|
||||||
{
|
{
|
||||||
wxString Device;
|
wxString Device;
|
||||||
s8 NumBuffers;
|
s8 NumBuffers;
|
||||||
|
|
||||||
CONFIG_XAUDIO2()
|
CONFIG_XAUDIO2()
|
||||||
: Device()
|
: Device()
|
||||||
, NumBuffers(2)
|
, NumBuffers(2)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CONFIG_WAVEOUT
|
struct CONFIG_WAVEOUT
|
||||||
{
|
{
|
||||||
wxString Device;
|
wxString Device;
|
||||||
s8 NumBuffers;
|
s8 NumBuffers;
|
||||||
|
|
||||||
CONFIG_WAVEOUT()
|
CONFIG_WAVEOUT()
|
||||||
: Device()
|
: Device()
|
||||||
, NumBuffers(4)
|
, NumBuffers(4)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CONFIG_WAVEOUT Config_WaveOut;
|
extern CONFIG_WAVEOUT Config_WaveOut;
|
||||||
|
|
|
@ -23,14 +23,14 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "dsp.h"
|
#include "dsp.h"
|
||||||
|
|
||||||
typedef winampDSPHeader *(*pWinampDSPGetHeader2)();
|
typedef winampDSPHeader* (*pWinampDSPGetHeader2)();
|
||||||
}
|
}
|
||||||
|
|
||||||
HMODULE hLib = NULL;
|
HMODULE hLib = NULL;
|
||||||
pWinampDSPGetHeader2 pGetHeader = NULL;
|
pWinampDSPGetHeader2 pGetHeader = NULL;
|
||||||
winampDSPHeader *pHeader = NULL;
|
winampDSPHeader* pHeader = NULL;
|
||||||
|
|
||||||
winampDSPModule *pModule = NULL;
|
winampDSPModule* pModule = NULL;
|
||||||
|
|
||||||
HWND hTemp;
|
HWND hTemp;
|
||||||
|
|
||||||
|
@ -44,94 +44,100 @@ bool running;
|
||||||
|
|
||||||
DWORD WINAPI DspUpdateThread(PVOID param);
|
DWORD WINAPI DspUpdateThread(PVOID param);
|
||||||
#endif
|
#endif
|
||||||
s32 DspLoadLibrary(wchar_t *fileName, int modNum)
|
s32 DspLoadLibrary(wchar_t* fileName, int modNum)
|
||||||
#ifdef USE_A_THREAD
|
#ifdef USE_A_THREAD
|
||||||
{
|
{
|
||||||
if (!dspPluginEnabled)
|
if (!dspPluginEnabled)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
running = true;
|
running = true;
|
||||||
hUpdateThread = CreateThread(NULL, 0, DspUpdateThread, NULL, 0, &UpdateThreadId);
|
hUpdateThread = CreateThread(NULL, 0, DspUpdateThread, NULL, 0, &UpdateThreadId);
|
||||||
return (hUpdateThread == INVALID_HANDLE_VALUE);
|
return (hUpdateThread == INVALID_HANDLE_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 DspLoadLibrary2(wchar_t *fileName, int modNum)
|
s32 DspLoadLibrary2(wchar_t* fileName, int modNum)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (!dspPluginEnabled)
|
if (!dspPluginEnabled)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
hLib = LoadLibraryW(fileName);
|
hLib = LoadLibraryW(fileName);
|
||||||
if (!hLib) {
|
if (!hLib)
|
||||||
return 1;
|
{
|
||||||
}
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
pGetHeader = (pWinampDSPGetHeader2)GetProcAddress(hLib, "winampDSPGetHeader2");
|
pGetHeader = (pWinampDSPGetHeader2)GetProcAddress(hLib, "winampDSPGetHeader2");
|
||||||
|
|
||||||
if (!pGetHeader) {
|
if (!pGetHeader)
|
||||||
FreeLibrary(hLib);
|
{
|
||||||
hLib = NULL;
|
FreeLibrary(hLib);
|
||||||
return 1;
|
hLib = NULL;
|
||||||
}
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
pHeader = pGetHeader();
|
pHeader = pGetHeader();
|
||||||
|
|
||||||
pModule = pHeader->getModule(modNum);
|
pModule = pHeader->getModule(modNum);
|
||||||
|
|
||||||
if (!pModule) {
|
if (!pModule)
|
||||||
pGetHeader = NULL;
|
{
|
||||||
pHeader = NULL;
|
pGetHeader = NULL;
|
||||||
FreeLibrary(hLib);
|
pHeader = NULL;
|
||||||
hLib = NULL;
|
FreeLibrary(hLib);
|
||||||
return -1;
|
hLib = NULL;
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
pModule->hDllInstance = hLib;
|
pModule->hDllInstance = hLib;
|
||||||
pModule->hwndParent = 0;
|
pModule->hwndParent = 0;
|
||||||
pModule->Init(pModule);
|
pModule->Init(pModule);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DspCloseLibrary()
|
void DspCloseLibrary()
|
||||||
#ifdef USE_A_THREAD
|
#ifdef USE_A_THREAD
|
||||||
{
|
{
|
||||||
if (!dspPluginEnabled)
|
if (!dspPluginEnabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
PostThreadMessage(UpdateThreadId, WM_QUIT, 0, 0);
|
PostThreadMessage(UpdateThreadId, WM_QUIT, 0, 0);
|
||||||
running = false;
|
running = false;
|
||||||
if (WaitForSingleObject(hUpdateThread, 1000) == WAIT_TIMEOUT) {
|
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);
|
ConLog("SPU2-X: WARNING: DSP Thread did not close itself in time. Assuming hung. Terminating.\n");
|
||||||
}
|
TerminateThread(hUpdateThread, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DspCloseLibrary2()
|
void DspCloseLibrary2()
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (!dspPluginEnabled)
|
if (!dspPluginEnabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (hLib) {
|
if (hLib)
|
||||||
pModule->Quit(pModule);
|
{
|
||||||
FreeLibrary(hLib);
|
pModule->Quit(pModule);
|
||||||
}
|
FreeLibrary(hLib);
|
||||||
pModule = NULL;
|
}
|
||||||
pHeader = NULL;
|
pModule = NULL;
|
||||||
pGetHeader = NULL;
|
pHeader = NULL;
|
||||||
hLib = NULL;
|
pGetHeader = NULL;
|
||||||
|
hLib = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DspProcess(s16 *buffer, int samples)
|
int DspProcess(s16* buffer, int samples)
|
||||||
{
|
{
|
||||||
if (!dspPluginEnabled)
|
if (!dspPluginEnabled)
|
||||||
return samples;
|
return samples;
|
||||||
|
|
||||||
if (hLib) {
|
if (hLib)
|
||||||
return pModule->ModifySamples(pModule, buffer, samples, 16, 2, SampleRate);
|
{
|
||||||
}
|
return pModule->ModifySamples(pModule, buffer, samples, 16, 2, SampleRate);
|
||||||
return samples;
|
}
|
||||||
|
return samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DspUpdate()
|
void DspUpdate()
|
||||||
|
@ -141,36 +147,39 @@ void DspUpdate()
|
||||||
|
|
||||||
DWORD WINAPI DspUpdateThread(PVOID param)
|
DWORD WINAPI DspUpdateThread(PVOID param)
|
||||||
{
|
{
|
||||||
if (!dspPluginEnabled)
|
if (!dspPluginEnabled)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (DspLoadLibrary2(dspPlugin, dspPluginModule))
|
if (DspLoadLibrary2(dspPlugin, dspPluginModule))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
MSG msg;
|
MSG msg;
|
||||||
while (running) {
|
while (running)
|
||||||
GetMessage(&msg, 0, 0, 0);
|
{
|
||||||
if ((msg.hwnd == NULL) && (msg.message == WM_QUIT)) {
|
GetMessage(&msg, 0, 0, 0);
|
||||||
break;
|
if ((msg.hwnd == NULL) && (msg.message == WM_QUIT))
|
||||||
}
|
{
|
||||||
TranslateMessage(&msg);
|
break;
|
||||||
DispatchMessage(&msg);
|
}
|
||||||
}
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessage(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
DspCloseLibrary2();
|
DspCloseLibrary2();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
{
|
{
|
||||||
if (!dspPluginEnabled)
|
if (!dspPluginEnabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MSG msg;
|
MSG msg;
|
||||||
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
|
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
|
||||||
TranslateMessage(&msg);
|
{
|
||||||
DispatchMessage(&msg);
|
TranslateMessage(&msg);
|
||||||
}
|
DispatchMessage(&msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,32 +26,32 @@
|
||||||
|
|
||||||
typedef struct winampDSPModule
|
typedef struct winampDSPModule
|
||||||
{
|
{
|
||||||
char *description; // description
|
char* description; // description
|
||||||
HWND hwndParent; // parent window (filled in by calling app)
|
HWND hwndParent; // parent window (filled in by calling app)
|
||||||
HINSTANCE hDllInstance; // instance handle to this DLL (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)
|
void (*Config)(struct winampDSPModule* this_mod); // configuration dialog (if needed)
|
||||||
int (*Init)(struct winampDSPModule *this_mod); // 0 on success, creates window, etc (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
|
// modify waveform samples: returns number of samples to actually write
|
||||||
// (typically numsamples, but no more than twice numsamples, and no less than half numsamples)
|
// (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
|
// 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);
|
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 (*Quit)(struct winampDSPModule* this_mod); // called when unloading
|
||||||
|
|
||||||
void *userData; // user data, optional
|
void* userData; // user data, optional
|
||||||
} winampDSPModule;
|
} winampDSPModule;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int version; // DSP_HDRVER
|
int version; // DSP_HDRVER
|
||||||
char *description; // description of library
|
char* description; // description of library
|
||||||
winampDSPModule *(*getModule)(int); // module retrieval function
|
winampDSPModule* (*getModule)(int); // module retrieval function
|
||||||
} winampDSPHeader;
|
} winampDSPHeader;
|
||||||
|
|
||||||
// exported symbols
|
// exported symbols
|
||||||
typedef winampDSPHeader *(*winampDSPGetHeaderType)();
|
typedef winampDSPHeader* (*winampDSPGetHeaderType)();
|
||||||
|
|
||||||
// header version: 0x20 == 0.20 == winamp 2.0
|
// header version: 0x20 == 0.20 == winamp 2.0
|
||||||
#define DSP_HDRVER 0x20
|
#define DSP_HDRVER 0x20
|
||||||
|
|
|
@ -2,72 +2,72 @@
|
||||||
// Microsoft Visual C++ generated include file.
|
// Microsoft Visual C++ generated include file.
|
||||||
// Used by Spu2-X.rc
|
// Used by Spu2-X.rc
|
||||||
//
|
//
|
||||||
#define IDD_CONFIG 9
|
#define IDD_CONFIG 9
|
||||||
#define IDD_DEBUG 105
|
#define IDD_DEBUG 105
|
||||||
#define IDD_DSOUND 106
|
#define IDD_DSOUND 106
|
||||||
#define IDD_WAVEOUT 109
|
#define IDD_WAVEOUT 109
|
||||||
#define IDB_SPU2X_SMALL 116
|
#define IDB_SPU2X_SMALL 116
|
||||||
#define IDD_CONFIG_SOUNDTOUCH 117
|
#define IDD_CONFIG_SOUNDTOUCH 117
|
||||||
#define IDD_CONFIG_DEBUG 118
|
#define IDD_CONFIG_DEBUG 118
|
||||||
#define IDD_PORTAUDIO 119
|
#define IDD_PORTAUDIO 119
|
||||||
#define IDC_EFFECTS_DISABLE 1001
|
#define IDC_EFFECTS_DISABLE 1001
|
||||||
#define IDC_DUMPREGS 1003
|
#define IDC_DUMPREGS 1003
|
||||||
#define IDC_DUMPMEM 1004
|
#define IDC_DUMPMEM 1004
|
||||||
#define IDC_DUMPCORE 1005
|
#define IDC_DUMPCORE 1005
|
||||||
#define IDC_LOGWAVE 1006
|
#define IDC_LOGWAVE 1006
|
||||||
#define IDC_LOGDMA 1007
|
#define IDC_LOGDMA 1007
|
||||||
#define IDC_LOGREGS 1008
|
#define IDC_LOGREGS 1008
|
||||||
#define IDC_DEBUG 1009
|
#define IDC_DEBUG 1009
|
||||||
#define IDC_DEBUG_ENABLE 1010
|
#define IDC_DEBUG_ENABLE 1010
|
||||||
#define IDC_INTERPOLATE 1011
|
#define IDC_INTERPOLATE 1011
|
||||||
#define IDC_DEALIASFILTER 1012
|
#define IDC_DEALIASFILTER 1012
|
||||||
#define IDC_OUTPUT 1013
|
#define IDC_OUTPUT 1013
|
||||||
#define IDC_BUFFERS_SLIDER 1014
|
#define IDC_BUFFERS_SLIDER 1014
|
||||||
#define IDC_SPEAKERS 1015
|
#define IDC_SPEAKERS 1015
|
||||||
#define IDC_SPEAKERS_TEXT 1016
|
#define IDC_SPEAKERS_TEXT 1016
|
||||||
#define IDC_MSGKEY 1020
|
#define IDC_MSGKEY 1020
|
||||||
#define IDC_MSGDMA 1021
|
#define IDC_MSGDMA 1021
|
||||||
#define IDC_MSGADMA 1022
|
#define IDC_MSGADMA 1022
|
||||||
#define IDC_MSGVOICE 1023
|
#define IDC_MSGVOICE 1023
|
||||||
#define IDC_MSGSHOW 1024
|
#define IDC_MSGSHOW 1024
|
||||||
#define IDC_OUTCONF 1028
|
#define IDC_OUTCONF 1028
|
||||||
#define IDC_DSP_ENABLE 1029
|
#define IDC_DSP_ENABLE 1029
|
||||||
#define IDC_DS_DEVICE 1032
|
#define IDC_DS_DEVICE 1032
|
||||||
#define IDC_PA_DEVICE 1033
|
#define IDC_PA_DEVICE 1033
|
||||||
#define IDC_DBG_OVERRUNS 1038
|
#define IDC_DBG_OVERRUNS 1038
|
||||||
#define IDC_DBG_CACHE 1039
|
#define IDC_DBG_CACHE 1039
|
||||||
#define IDC_LATENCY_SLIDER 1040
|
#define IDC_LATENCY_SLIDER 1040
|
||||||
#define IDC_LATENCY_LABEL 1041
|
#define IDC_LATENCY_LABEL 1041
|
||||||
#define ICD_LR_CENTER_SLIDER 1042
|
#define ICD_LR_CENTER_SLIDER 1042
|
||||||
#define IDC_SEQLEN_SLIDER 1043
|
#define IDC_SEQLEN_SLIDER 1043
|
||||||
#define IDC_SEEKWIN_SLIDER 1044
|
#define IDC_SEEKWIN_SLIDER 1044
|
||||||
#define IDC_OVERLAP_SLIDER 1045
|
#define IDC_OVERLAP_SLIDER 1045
|
||||||
#define IDC_MSG_PUBLIC_BUILD 1048
|
#define IDC_MSG_PUBLIC_BUILD 1048
|
||||||
#define IDC_RESET_DEFAULTS 1057
|
#define IDC_RESET_DEFAULTS 1057
|
||||||
#define IDC_OPEN_CONFIG_SOUNDTOUCH 1058
|
#define IDC_OPEN_CONFIG_SOUNDTOUCH 1058
|
||||||
#define IDC_OPEN_CONFIG_DEBUG 1059
|
#define IDC_OPEN_CONFIG_DEBUG 1059
|
||||||
#define IDC_GLOBALFOCUS_DISABLE 1060
|
#define IDC_GLOBALFOCUS_DISABLE 1060
|
||||||
#define IDC_GLOBALFOCUS_DISABLE2 1061
|
#define IDC_GLOBALFOCUS_DISABLE2 1061
|
||||||
#define IDC_USE_HARDWARE 1062
|
#define IDC_USE_HARDWARE 1062
|
||||||
#define IDC_COMBO1 1063
|
#define IDC_COMBO1 1063
|
||||||
#define IDC_SYNCHMODE 1064
|
#define IDC_SYNCHMODE 1064
|
||||||
#define IDC_DEBUG_OTHERS 1065
|
#define IDC_DEBUG_OTHERS 1065
|
||||||
#define IDC_DEBUG_VISUAL 1066
|
#define IDC_DEBUG_VISUAL 1066
|
||||||
#define IDC_VOLUME_LABEL 1067
|
#define IDC_VOLUME_LABEL 1067
|
||||||
#define IDC_VOLUME_SLIDER 1068
|
#define IDC_VOLUME_SLIDER 1068
|
||||||
#define IDC_MINIMIZE 1069
|
#define IDC_MINIMIZE 1069
|
||||||
#define IDC_MANUAL 1070
|
#define IDC_MANUAL 1070
|
||||||
#define IDC_PA_HOSTAPI 1071
|
#define IDC_PA_HOSTAPI 1071
|
||||||
#define IDC_LATENCY 1072
|
#define IDC_LATENCY 1072
|
||||||
#define IDC_EXCLUSIVE 1073
|
#define IDC_EXCLUSIVE 1073
|
||||||
|
|
||||||
// Next default values for new objects
|
// Next default values for new objects
|
||||||
//
|
//
|
||||||
#ifdef APSTUDIO_INVOKED
|
#ifdef APSTUDIO_INVOKED
|
||||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
#define _APS_NEXT_RESOURCE_VALUE 120
|
#define _APS_NEXT_RESOURCE_VALUE 120
|
||||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||||
#define _APS_NEXT_CONTROL_VALUE 1074
|
#define _APS_NEXT_CONTROL_VALUE 1074
|
||||||
#define _APS_NEXT_SYMED_VALUE 101
|
#define _APS_NEXT_SYMED_VALUE 101
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -22,10 +22,10 @@
|
||||||
// SPU2 Memory Indexers
|
// SPU2 Memory Indexers
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
#define spu2Rs16(mmem) (*(s16 *)((s8 *)spu2regs + ((mmem)&0x1fff)))
|
#define spu2Rs16(mmem) (*(s16*)((s8*)spu2regs + ((mmem)&0x1fff)))
|
||||||
#define spu2Ru16(mmem) (*(u16 *)((s8 *)spu2regs + ((mmem)&0x1fff)))
|
#define spu2Ru16(mmem) (*(u16*)((s8*)spu2regs + ((mmem)&0x1fff)))
|
||||||
|
|
||||||
extern s16 *GetMemPtr(u32 addr);
|
extern s16* GetMemPtr(u32 addr);
|
||||||
extern s16 spu2M_Read(u32 addr);
|
extern s16 spu2M_Read(u32 addr);
|
||||||
extern void spu2M_Write(u32 addr, s16 value);
|
extern void spu2M_Write(u32 addr, s16 value);
|
||||||
extern void spu2M_Write(u32 addr, u16 value);
|
extern void spu2M_Write(u32 addr, u16 value);
|
||||||
|
@ -33,164 +33,164 @@ extern void spu2M_Write(u32 addr, u16 value);
|
||||||
|
|
||||||
struct V_VolumeLR
|
struct V_VolumeLR
|
||||||
{
|
{
|
||||||
static V_VolumeLR Max;
|
static V_VolumeLR Max;
|
||||||
|
|
||||||
s32 Left;
|
s32 Left;
|
||||||
s32 Right;
|
s32 Right;
|
||||||
|
|
||||||
V_VolumeLR() = default;
|
V_VolumeLR() = default;
|
||||||
V_VolumeLR(s32 both)
|
V_VolumeLR(s32 both)
|
||||||
: Left(both)
|
: Left(both)
|
||||||
, Right(both)
|
, Right(both)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugDump(FILE *dump, const char *title);
|
void DebugDump(FILE* dump, const char* title);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct V_VolumeSlide
|
struct V_VolumeSlide
|
||||||
{
|
{
|
||||||
// Holds the "original" value of the volume for this voice, prior to slides.
|
// Holds the "original" value of the volume for this voice, prior to slides.
|
||||||
// (ie, the volume as written to the register)
|
// (ie, the volume as written to the register)
|
||||||
|
|
||||||
s16 Reg_VOL;
|
s16 Reg_VOL;
|
||||||
s32 Value;
|
s32 Value;
|
||||||
s8 Increment;
|
s8 Increment;
|
||||||
s8 Mode;
|
s8 Mode;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
V_VolumeSlide() = default;
|
V_VolumeSlide() = default;
|
||||||
V_VolumeSlide(s16 regval, s32 fullvol)
|
V_VolumeSlide(s16 regval, s32 fullvol)
|
||||||
: Reg_VOL(regval)
|
: Reg_VOL(regval)
|
||||||
, Value(fullvol)
|
, Value(fullvol)
|
||||||
, Increment(0)
|
, Increment(0)
|
||||||
, Mode(0)
|
, Mode(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update();
|
void Update();
|
||||||
void RegSet(u16 src); // used to set the volume from a register source (16 bit signed)
|
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);
|
void DebugDump(FILE* dump, const char* title, const char* nameLR);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct V_VolumeSlideLR
|
struct V_VolumeSlideLR
|
||||||
{
|
{
|
||||||
static V_VolumeSlideLR Max;
|
static V_VolumeSlideLR Max;
|
||||||
|
|
||||||
V_VolumeSlide Left;
|
V_VolumeSlide Left;
|
||||||
V_VolumeSlide Right;
|
V_VolumeSlide Right;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
V_VolumeSlideLR() = default;
|
V_VolumeSlideLR() = default;
|
||||||
V_VolumeSlideLR(s16 regval, s32 bothval)
|
V_VolumeSlideLR(s16 regval, s32 bothval)
|
||||||
: Left(regval, bothval)
|
: Left(regval, bothval)
|
||||||
, Right(regval, bothval)
|
, Right(regval, bothval)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
Left.Update();
|
Left.Update();
|
||||||
Right.Update();
|
Right.Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugDump(FILE *dump, const char *title);
|
void DebugDump(FILE* dump, const char* title);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct V_ADSR
|
struct V_ADSR
|
||||||
{
|
{
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
u32 reg32;
|
u32 reg32;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
u16 regADSR1;
|
u16 regADSR1;
|
||||||
u16 regADSR2;
|
u16 regADSR2;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
u32 SustainLevel : 4,
|
u32 SustainLevel : 4,
|
||||||
DecayRate : 4,
|
DecayRate : 4,
|
||||||
AttackRate : 7,
|
AttackRate : 7,
|
||||||
AttackMode : 1, // 0 for linear (+lin), 1 for pseudo exponential (+exp)
|
AttackMode : 1, // 0 for linear (+lin), 1 for pseudo exponential (+exp)
|
||||||
|
|
||||||
ReleaseRate : 5,
|
ReleaseRate : 5,
|
||||||
ReleaseMode : 1, // 0 for linear (-lin), 1 for exponential (-exp)
|
ReleaseMode : 1, // 0 for linear (-lin), 1 for exponential (-exp)
|
||||||
SustainRate : 7,
|
SustainRate : 7,
|
||||||
SustainMode : 3; // 0 = +lin, 1 = -lin, 2 = +exp, 3 = -exp
|
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]
|
s32 Value; // Ranges from 0 to 0x7fffffff (signed values are clamped to 0) [Reg_ENVX]
|
||||||
u8 Phase; // monitors current phase of ADSR envelope
|
u8 Phase; // monitors current phase of ADSR envelope
|
||||||
bool Releasing; // Ready To Release, triggered by Voice.Stop();
|
bool Releasing; // Ready To Release, triggered by Voice.Stop();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool Calculate();
|
bool Calculate();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct V_Voice
|
struct V_Voice
|
||||||
{
|
{
|
||||||
u32 PlayCycle; // SPU2 cycle where the Playing started
|
u32 PlayCycle; // SPU2 cycle where the Playing started
|
||||||
|
|
||||||
V_VolumeSlideLR Volume;
|
V_VolumeSlideLR Volume;
|
||||||
|
|
||||||
// Envelope
|
// Envelope
|
||||||
V_ADSR ADSR;
|
V_ADSR ADSR;
|
||||||
// Pitch (also Reg_PITCH)
|
// Pitch (also Reg_PITCH)
|
||||||
u16 Pitch;
|
u16 Pitch;
|
||||||
// Loop Start address (also Reg_LSAH/L)
|
// Loop Start address (also Reg_LSAH/L)
|
||||||
u32 LoopStartA;
|
u32 LoopStartA;
|
||||||
// Sound Start address (also Reg_SSAH/L)
|
// Sound Start address (also Reg_SSAH/L)
|
||||||
u32 StartA;
|
u32 StartA;
|
||||||
// Next Read Data address (also Reg_NAXH/L)
|
// Next Read Data address (also Reg_NAXH/L)
|
||||||
u32 NextA;
|
u32 NextA;
|
||||||
// Voice Decoding State
|
// Voice Decoding State
|
||||||
s32 Prev1;
|
s32 Prev1;
|
||||||
s32 Prev2;
|
s32 Prev2;
|
||||||
|
|
||||||
// Pitch Modulated by previous voice
|
// Pitch Modulated by previous voice
|
||||||
bool Modulated;
|
bool Modulated;
|
||||||
// Source (Wave/Noise)
|
// Source (Wave/Noise)
|
||||||
bool Noise;
|
bool Noise;
|
||||||
|
|
||||||
s8 LoopMode;
|
s8 LoopMode;
|
||||||
s8 LoopFlags;
|
s8 LoopFlags;
|
||||||
|
|
||||||
// Sample pointer (19:12 bit fixed point)
|
// Sample pointer (19:12 bit fixed point)
|
||||||
s32 SP;
|
s32 SP;
|
||||||
|
|
||||||
// Sample pointer for Cubic Interpolation
|
// Sample pointer for Cubic Interpolation
|
||||||
// Cubic interpolation mixes a sample behind Linear, so that it
|
// Cubic interpolation mixes a sample behind Linear, so that it
|
||||||
// can have sample data to either side of the end points from which
|
// can have sample data to either side of the end points from which
|
||||||
// to extrapolate. This SP represents that late sample position.
|
// to extrapolate. This SP represents that late sample position.
|
||||||
s32 SPc;
|
s32 SPc;
|
||||||
|
|
||||||
// Previous sample values - used for interpolation
|
// Previous sample values - used for interpolation
|
||||||
// Inverted order of these members to match the access order in the
|
// Inverted order of these members to match the access order in the
|
||||||
// code (might improve cache hits).
|
// code (might improve cache hits).
|
||||||
s32 PV4;
|
s32 PV4;
|
||||||
s32 PV3;
|
s32 PV3;
|
||||||
s32 PV2;
|
s32 PV2;
|
||||||
s32 PV1;
|
s32 PV1;
|
||||||
|
|
||||||
// Last outputted audio value, used for voice modulation.
|
// Last outputted audio value, used for voice modulation.
|
||||||
s32 OutX;
|
s32 OutX;
|
||||||
s32 NextCrest; // temp value for Crest calculation
|
s32 NextCrest; // temp value for Crest calculation
|
||||||
|
|
||||||
// SBuffer now points directly to an ADPCM cache entry.
|
// SBuffer now points directly to an ADPCM cache entry.
|
||||||
s16 *SBuffer;
|
s16* SBuffer;
|
||||||
|
|
||||||
// sample position within the current decoded packet.
|
// sample position within the current decoded packet.
|
||||||
s32 SCurrent;
|
s32 SCurrent;
|
||||||
|
|
||||||
// it takes a few ticks for voices to start on the real SPU2?
|
// it takes a few ticks for voices to start on the real SPU2?
|
||||||
void QueueStart();
|
void QueueStart();
|
||||||
bool Start();
|
bool Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
};
|
};
|
||||||
|
|
||||||
// ** Begin Debug-only variables section **
|
// ** Begin Debug-only variables section **
|
||||||
|
@ -198,25 +198,25 @@ struct V_Voice
|
||||||
// the Public Release build.
|
// the Public Release build.
|
||||||
struct V_VoiceDebug
|
struct V_VoiceDebug
|
||||||
{
|
{
|
||||||
s8 FirstBlock;
|
s8 FirstBlock;
|
||||||
s32 SampleData;
|
s32 SampleData;
|
||||||
s32 PeakX;
|
s32 PeakX;
|
||||||
s32 displayPeak;
|
s32 displayPeak;
|
||||||
s32 lastSetStartA;
|
s32 lastSetStartA;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct V_CoreDebug
|
struct V_CoreDebug
|
||||||
{
|
{
|
||||||
V_VoiceDebug Voices[24];
|
V_VoiceDebug Voices[24];
|
||||||
// Last Transfer Size
|
// Last Transfer Size
|
||||||
u32 lastsize;
|
u32 lastsize;
|
||||||
|
|
||||||
// draw adma waveform in the visual debugger
|
// draw adma waveform in the visual debugger
|
||||||
s32 admaWaveformL[0x100];
|
s32 admaWaveformL[0x100];
|
||||||
s32 admaWaveformR[0x100];
|
s32 admaWaveformR[0x100];
|
||||||
|
|
||||||
// Enabled when a dma write starts, disabled when the visual debugger showed it once
|
// Enabled when a dma write starts, disabled when the visual debugger showed it once
|
||||||
s32 dmaFlag;
|
s32 dmaFlag;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Debug tracking information - 24 voices and 2 cores.
|
// Debug tracking information - 24 voices and 2 cores.
|
||||||
|
@ -224,310 +224,310 @@ extern V_CoreDebug DebugCores[2];
|
||||||
|
|
||||||
struct V_Reverb
|
struct V_Reverb
|
||||||
{
|
{
|
||||||
s16 IN_COEF_L;
|
s16 IN_COEF_L;
|
||||||
s16 IN_COEF_R;
|
s16 IN_COEF_R;
|
||||||
|
|
||||||
u32 APF1_SIZE;
|
u32 APF1_SIZE;
|
||||||
u32 APF2_SIZE;
|
u32 APF2_SIZE;
|
||||||
|
|
||||||
s16 APF1_VOL;
|
s16 APF1_VOL;
|
||||||
s16 APF2_VOL;
|
s16 APF2_VOL;
|
||||||
|
|
||||||
u32 SAME_L_SRC;
|
u32 SAME_L_SRC;
|
||||||
u32 SAME_R_SRC;
|
u32 SAME_R_SRC;
|
||||||
u32 DIFF_L_SRC;
|
u32 DIFF_L_SRC;
|
||||||
u32 DIFF_R_SRC;
|
u32 DIFF_R_SRC;
|
||||||
u32 SAME_L_DST;
|
u32 SAME_L_DST;
|
||||||
u32 SAME_R_DST;
|
u32 SAME_R_DST;
|
||||||
u32 DIFF_L_DST;
|
u32 DIFF_L_DST;
|
||||||
u32 DIFF_R_DST;
|
u32 DIFF_R_DST;
|
||||||
|
|
||||||
s16 IIR_VOL;
|
s16 IIR_VOL;
|
||||||
s16 WALL_VOL;
|
s16 WALL_VOL;
|
||||||
|
|
||||||
u32 COMB1_L_SRC;
|
u32 COMB1_L_SRC;
|
||||||
u32 COMB1_R_SRC;
|
u32 COMB1_R_SRC;
|
||||||
u32 COMB2_L_SRC;
|
u32 COMB2_L_SRC;
|
||||||
u32 COMB2_R_SRC;
|
u32 COMB2_R_SRC;
|
||||||
u32 COMB3_L_SRC;
|
u32 COMB3_L_SRC;
|
||||||
u32 COMB3_R_SRC;
|
u32 COMB3_R_SRC;
|
||||||
u32 COMB4_L_SRC;
|
u32 COMB4_L_SRC;
|
||||||
u32 COMB4_R_SRC;
|
u32 COMB4_R_SRC;
|
||||||
|
|
||||||
s16 COMB1_VOL;
|
s16 COMB1_VOL;
|
||||||
s16 COMB2_VOL;
|
s16 COMB2_VOL;
|
||||||
s16 COMB3_VOL;
|
s16 COMB3_VOL;
|
||||||
s16 COMB4_VOL;
|
s16 COMB4_VOL;
|
||||||
|
|
||||||
u32 APF1_L_DST;
|
u32 APF1_L_DST;
|
||||||
u32 APF1_R_DST;
|
u32 APF1_R_DST;
|
||||||
u32 APF2_L_DST;
|
u32 APF2_L_DST;
|
||||||
u32 APF2_R_DST;
|
u32 APF2_R_DST;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct V_ReverbBuffers
|
struct V_ReverbBuffers
|
||||||
{
|
{
|
||||||
s32 SAME_L_SRC;
|
s32 SAME_L_SRC;
|
||||||
s32 SAME_R_SRC;
|
s32 SAME_R_SRC;
|
||||||
s32 DIFF_R_SRC;
|
s32 DIFF_R_SRC;
|
||||||
s32 DIFF_L_SRC;
|
s32 DIFF_L_SRC;
|
||||||
s32 SAME_L_DST;
|
s32 SAME_L_DST;
|
||||||
s32 SAME_R_DST;
|
s32 SAME_R_DST;
|
||||||
s32 DIFF_L_DST;
|
s32 DIFF_L_DST;
|
||||||
s32 DIFF_R_DST;
|
s32 DIFF_R_DST;
|
||||||
|
|
||||||
s32 COMB1_L_SRC;
|
s32 COMB1_L_SRC;
|
||||||
s32 COMB1_R_SRC;
|
s32 COMB1_R_SRC;
|
||||||
s32 COMB2_L_SRC;
|
s32 COMB2_L_SRC;
|
||||||
s32 COMB2_R_SRC;
|
s32 COMB2_R_SRC;
|
||||||
s32 COMB3_L_SRC;
|
s32 COMB3_L_SRC;
|
||||||
s32 COMB3_R_SRC;
|
s32 COMB3_R_SRC;
|
||||||
s32 COMB4_L_SRC;
|
s32 COMB4_L_SRC;
|
||||||
s32 COMB4_R_SRC;
|
s32 COMB4_R_SRC;
|
||||||
|
|
||||||
s32 APF1_L_DST;
|
s32 APF1_L_DST;
|
||||||
s32 APF1_R_DST;
|
s32 APF1_R_DST;
|
||||||
s32 APF2_L_DST;
|
s32 APF2_L_DST;
|
||||||
s32 APF2_R_DST;
|
s32 APF2_R_DST;
|
||||||
|
|
||||||
s32 SAME_L_PRV;
|
s32 SAME_L_PRV;
|
||||||
s32 SAME_R_PRV;
|
s32 SAME_R_PRV;
|
||||||
s32 DIFF_L_PRV;
|
s32 DIFF_L_PRV;
|
||||||
s32 DIFF_R_PRV;
|
s32 DIFF_R_PRV;
|
||||||
|
|
||||||
s32 APF1_L_SRC;
|
s32 APF1_L_SRC;
|
||||||
s32 APF1_R_SRC;
|
s32 APF1_R_SRC;
|
||||||
s32 APF2_L_SRC;
|
s32 APF2_L_SRC;
|
||||||
s32 APF2_R_SRC;
|
s32 APF2_R_SRC;
|
||||||
|
|
||||||
bool NeedsUpdated;
|
bool NeedsUpdated;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct V_SPDIF
|
struct V_SPDIF
|
||||||
{
|
{
|
||||||
u16 Out;
|
u16 Out;
|
||||||
u16 Info;
|
u16 Info;
|
||||||
u16 Unknown1;
|
u16 Unknown1;
|
||||||
u16 Mode;
|
u16 Mode;
|
||||||
u16 Media;
|
u16 Media;
|
||||||
u16 Unknown2;
|
u16 Unknown2;
|
||||||
u16 Protection;
|
u16 Protection;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct V_CoreRegs
|
struct V_CoreRegs
|
||||||
{
|
{
|
||||||
u32 PMON;
|
u32 PMON;
|
||||||
u32 NON;
|
u32 NON;
|
||||||
u32 VMIXL;
|
u32 VMIXL;
|
||||||
u32 VMIXR;
|
u32 VMIXR;
|
||||||
u32 VMIXEL;
|
u32 VMIXEL;
|
||||||
u32 VMIXER;
|
u32 VMIXER;
|
||||||
u32 ENDX;
|
u32 ENDX;
|
||||||
|
|
||||||
u16 MMIX;
|
u16 MMIX;
|
||||||
u16 STATX;
|
u16 STATX;
|
||||||
u16 ATTR;
|
u16 ATTR;
|
||||||
u16 _1AC;
|
u16 _1AC;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct V_VoiceGates
|
struct V_VoiceGates
|
||||||
{
|
{
|
||||||
s16 DryL; // 'AND Gate' for Direct Output to Left Channel
|
s16 DryL; // 'AND Gate' for Direct Output to Left Channel
|
||||||
s16 DryR; // 'AND Gate' for Direct Output for Right Channel
|
s16 DryR; // 'AND Gate' for Direct Output for Right Channel
|
||||||
s16 WetL; // 'AND Gate' for Effect Output for Left Channel
|
s16 WetL; // 'AND Gate' for Effect Output for Left Channel
|
||||||
s16 WetR; // 'AND Gate' for Effect Output for Right Channel
|
s16 WetR; // 'AND Gate' for Effect Output for Right Channel
|
||||||
};
|
};
|
||||||
|
|
||||||
struct V_CoreGates
|
struct V_CoreGates
|
||||||
{
|
{
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
u128 v128;
|
u128 v128;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
s16 InpL; // Sound Data Input to Direct Output (Left)
|
s16 InpL; // Sound Data Input to Direct Output (Left)
|
||||||
s16 InpR; // Sound Data Input to Direct Output (Right)
|
s16 InpR; // Sound Data Input to Direct Output (Right)
|
||||||
s16 SndL; // Voice Data to Direct Output (Left)
|
s16 SndL; // Voice Data to Direct Output (Left)
|
||||||
s16 SndR; // Voice Data to Direct Output (Right)
|
s16 SndR; // Voice Data to Direct Output (Right)
|
||||||
s16 ExtL; // External Input to Direct Output (Left)
|
s16 ExtL; // External Input to Direct Output (Left)
|
||||||
s16 ExtR; // External Input to Direct Output (Right)
|
s16 ExtR; // External Input to Direct Output (Right)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VoiceMixSet
|
struct VoiceMixSet
|
||||||
{
|
{
|
||||||
static const VoiceMixSet Empty;
|
static const VoiceMixSet Empty;
|
||||||
StereoOut32 Dry, Wet;
|
StereoOut32 Dry, Wet;
|
||||||
|
|
||||||
VoiceMixSet() {}
|
VoiceMixSet() {}
|
||||||
VoiceMixSet(const StereoOut32 &dry, const StereoOut32 &wet)
|
VoiceMixSet(const StereoOut32& dry, const StereoOut32& wet)
|
||||||
: Dry(dry)
|
: Dry(dry)
|
||||||
, Wet(wet)
|
, Wet(wet)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct V_Core
|
struct V_Core
|
||||||
{
|
{
|
||||||
static const uint NumVoices = 24;
|
static const uint NumVoices = 24;
|
||||||
|
|
||||||
int Index; // Core index identifier.
|
int Index; // Core index identifier.
|
||||||
|
|
||||||
// Voice Gates -- These are SSE-related values, and must always be
|
// Voice Gates -- These are SSE-related values, and must always be
|
||||||
// first to ensure 16 byte alignment
|
// first to ensure 16 byte alignment
|
||||||
|
|
||||||
V_VoiceGates VoiceGates[NumVoices];
|
V_VoiceGates VoiceGates[NumVoices];
|
||||||
V_CoreGates DryGate;
|
V_CoreGates DryGate;
|
||||||
V_CoreGates WetGate;
|
V_CoreGates WetGate;
|
||||||
|
|
||||||
V_VolumeSlideLR MasterVol; // Master Volume
|
V_VolumeSlideLR MasterVol; // Master Volume
|
||||||
V_VolumeLR ExtVol; // Volume for External Data Input
|
V_VolumeLR ExtVol; // Volume for External Data Input
|
||||||
V_VolumeLR InpVol; // Volume for Sound Data Input
|
V_VolumeLR InpVol; // Volume for Sound Data Input
|
||||||
V_VolumeLR FxVol; // Volume for Output from Effects
|
V_VolumeLR FxVol; // Volume for Output from Effects
|
||||||
|
|
||||||
V_Voice Voices[NumVoices];
|
V_Voice Voices[NumVoices];
|
||||||
|
|
||||||
u32 IRQA; // Interrupt Address
|
u32 IRQA; // Interrupt Address
|
||||||
u32 TSA; // DMA Transfer Start Address
|
u32 TSA; // DMA Transfer Start Address
|
||||||
|
|
||||||
bool IRQEnable; // Interrupt Enable
|
bool IRQEnable; // Interrupt Enable
|
||||||
bool FxEnable; // Effect Enable
|
bool FxEnable; // Effect Enable
|
||||||
bool Mute; // Mute
|
bool Mute; // Mute
|
||||||
bool AdmaInProgress;
|
bool AdmaInProgress;
|
||||||
|
|
||||||
s8 DMABits; // DMA related?
|
s8 DMABits; // DMA related?
|
||||||
s8 NoiseClk; // Noise Clock
|
s8 NoiseClk; // Noise Clock
|
||||||
u16 AutoDMACtrl; // AutoDMA Status
|
u16 AutoDMACtrl; // AutoDMA Status
|
||||||
s32 DMAICounter; // DMA Interrupt Counter
|
s32 DMAICounter; // DMA Interrupt Counter
|
||||||
u32 InputDataLeft; // Input Buffer
|
u32 InputDataLeft; // Input Buffer
|
||||||
u32 InputPosRead;
|
u32 InputPosRead;
|
||||||
u32 InputPosWrite;
|
u32 InputPosWrite;
|
||||||
u32 InputDataProgress;
|
u32 InputDataProgress;
|
||||||
|
|
||||||
V_Reverb Revb; // Reverb Registers
|
V_Reverb Revb; // Reverb Registers
|
||||||
V_ReverbBuffers RevBuffers; // buffer pointers for reverb, pre-calculated and pre-clipped.
|
V_ReverbBuffers RevBuffers; // buffer pointers for reverb, pre-calculated and pre-clipped.
|
||||||
u32 EffectsStartA;
|
u32 EffectsStartA;
|
||||||
u32 EffectsEndA;
|
u32 EffectsEndA;
|
||||||
u32 ExtEffectsStartA;
|
u32 ExtEffectsStartA;
|
||||||
u32 ExtEffectsEndA;
|
u32 ExtEffectsEndA;
|
||||||
u32 ReverbX;
|
u32 ReverbX;
|
||||||
|
|
||||||
// Current size of and position of the effects buffer. Pre-caculated when the effects start
|
// 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
|
// or end position registers are written. CAN BE NEGATIVE OR ZERO, in which
|
||||||
// case reverb should be disabled.
|
// case reverb should be disabled.
|
||||||
s32 EffectsBufferSize;
|
s32 EffectsBufferSize;
|
||||||
u32 EffectsBufferStart;
|
u32 EffectsBufferStart;
|
||||||
|
|
||||||
V_CoreRegs Regs; // Registers
|
V_CoreRegs Regs; // Registers
|
||||||
|
|
||||||
// Preserves the channel processed last cycle
|
// Preserves the channel processed last cycle
|
||||||
StereoOut32 LastEffect;
|
StereoOut32 LastEffect;
|
||||||
|
|
||||||
u8 CoreEnabled;
|
u8 CoreEnabled;
|
||||||
|
|
||||||
u8 AttrBit0;
|
u8 AttrBit0;
|
||||||
u8 DmaMode;
|
u8 DmaMode;
|
||||||
|
|
||||||
// new dma only
|
// new dma only
|
||||||
bool DmaStarted;
|
bool DmaStarted;
|
||||||
u32 AutoDmaFree;
|
u32 AutoDmaFree;
|
||||||
|
|
||||||
// old dma only
|
// old dma only
|
||||||
u16 *DMAPtr;
|
u16* DMAPtr;
|
||||||
u32 MADR;
|
u32 MADR;
|
||||||
u32 TADR;
|
u32 TADR;
|
||||||
|
|
||||||
u32 KeyOn; // not the KON register (though maybe it is)
|
u32 KeyOn; // not the KON register (though maybe it is)
|
||||||
|
|
||||||
// psxmode caches
|
// psxmode caches
|
||||||
u16 psxSoundDataTransferControl;
|
u16 psxSoundDataTransferControl;
|
||||||
u16 psxSPUSTAT;
|
u16 psxSPUSTAT;
|
||||||
|
|
||||||
// HACK -- This is a temp buffer which is (or isn't?) used to circumvent some memory
|
// 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
|
// corruption that originates elsewhere in the plugin. >_< The actual ADMA buffer
|
||||||
// is an area mapped to SPU2 main memory.
|
// is an area mapped to SPU2 main memory.
|
||||||
//s16 ADMATempBuffer[0x1000];
|
//s16 ADMATempBuffer[0x1000];
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
// V_Core Methods
|
// V_Core Methods
|
||||||
// ----------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
// uninitialized constructor
|
// uninitialized constructor
|
||||||
V_Core()
|
V_Core()
|
||||||
: Index(-1)
|
: Index(-1)
|
||||||
, DMAPtr(NULL)
|
, DMAPtr(NULL)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
V_Core(int idx); // our badass constructor
|
V_Core(int idx); // our badass constructor
|
||||||
~V_Core() throw();
|
~V_Core() throw();
|
||||||
|
|
||||||
void Init(int index);
|
void Init(int index);
|
||||||
void UpdateEffectsBufferSize();
|
void UpdateEffectsBufferSize();
|
||||||
void AnalyzeReverbPreset();
|
void AnalyzeReverbPreset();
|
||||||
|
|
||||||
s32 EffectsBufferIndexer(s32 offset) const;
|
s32 EffectsBufferIndexer(s32 offset) const;
|
||||||
|
|
||||||
void WriteRegPS1(u32 mem, u16 value);
|
void WriteRegPS1(u32 mem, u16 value);
|
||||||
u16 ReadRegPS1(u32 mem);
|
u16 ReadRegPS1(u32 mem);
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// Mixer Section
|
// Mixer Section
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
StereoOut32 Mix(const VoiceMixSet &inVoices, const StereoOut32 &Input, const StereoOut32 &Ext);
|
StereoOut32 Mix(const VoiceMixSet& inVoices, const StereoOut32& Input, const StereoOut32& Ext);
|
||||||
void Reverb_AdvanceBuffer();
|
void Reverb_AdvanceBuffer();
|
||||||
StereoOut32 DoReverb(const StereoOut32 &Input);
|
StereoOut32 DoReverb(const StereoOut32& Input);
|
||||||
s32 RevbGetIndexer(s32 offset);
|
s32 RevbGetIndexer(s32 offset);
|
||||||
|
|
||||||
StereoOut32 ReadInput();
|
StereoOut32 ReadInput();
|
||||||
StereoOut32 ReadInput_HiFi();
|
StereoOut32 ReadInput_HiFi();
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// DMA Section
|
// DMA Section
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
// Returns the index of the DMA channel (4 for Core 0, or 7 for Core 1)
|
// Returns the index of the DMA channel (4 for Core 0, or 7 for Core 1)
|
||||||
int GetDmaIndex() const
|
int GetDmaIndex() const
|
||||||
{
|
{
|
||||||
return (Index == 0) ? 4 : 7;
|
return (Index == 0) ? 4 : 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns either '4' or '7'
|
// returns either '4' or '7'
|
||||||
char GetDmaIndexChar() const
|
char GetDmaIndexChar() const
|
||||||
{
|
{
|
||||||
return 0x30 + GetDmaIndex();
|
return 0x30 + GetDmaIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
__forceinline u16 DmaRead()
|
__forceinline u16 DmaRead()
|
||||||
{
|
{
|
||||||
const u16 ret = (u16)spu2M_Read(TSA);
|
const u16 ret = (u16)spu2M_Read(TSA);
|
||||||
++TSA;
|
++TSA;
|
||||||
TSA &= 0xfffff;
|
TSA &= 0xfffff;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
__forceinline void DmaWrite(u16 value)
|
__forceinline void DmaWrite(u16 value)
|
||||||
{
|
{
|
||||||
spu2M_Write(TSA, value);
|
spu2M_Write(TSA, value);
|
||||||
++TSA;
|
++TSA;
|
||||||
TSA &= 0xfffff;
|
TSA &= 0xfffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogAutoDMA(FILE *fp);
|
void LogAutoDMA(FILE* fp);
|
||||||
|
|
||||||
s32 NewDmaRead(u32 *data, u32 bytesLeft, u32 *bytesProcessed);
|
s32 NewDmaRead(u32* data, u32 bytesLeft, u32* bytesProcessed);
|
||||||
s32 NewDmaWrite(u32 *data, u32 bytesLeft, u32 *bytesProcessed);
|
s32 NewDmaWrite(u32* data, u32 bytesLeft, u32* bytesProcessed);
|
||||||
void NewDmaInterrupt();
|
void NewDmaInterrupt();
|
||||||
|
|
||||||
// old dma only
|
// old dma only
|
||||||
void DoDMAwrite(u16 *pMem, u32 size);
|
void DoDMAwrite(u16* pMem, u32 size);
|
||||||
void DoDMAread(u16 *pMem, u32 size);
|
void DoDMAread(u16* pMem, u32 size);
|
||||||
|
|
||||||
void AutoDMAReadBuffer(int mode);
|
void AutoDMAReadBuffer(int mode);
|
||||||
void StartADMAWrite(u16 *pMem, u32 sz);
|
void StartADMAWrite(u16* pMem, u32 sz);
|
||||||
void PlainDMAWrite(u16 *pMem, u32 sz);
|
void PlainDMAWrite(u16* pMem, u32 sz);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern V_Core Cores[2];
|
extern V_Core Cores[2];
|
||||||
|
@ -540,25 +540,25 @@ extern s16 InputPos;
|
||||||
// SPU Mixing Cycles ("Ticks mixed" counter)
|
// SPU Mixing Cycles ("Ticks mixed" counter)
|
||||||
extern u32 Cycles;
|
extern u32 Cycles;
|
||||||
|
|
||||||
extern s16 *spu2regs;
|
extern s16* spu2regs;
|
||||||
extern s16 *_spu2mem;
|
extern s16* _spu2mem;
|
||||||
extern int PlayMode;
|
extern int PlayMode;
|
||||||
|
|
||||||
extern void SetIrqCall(int core);
|
extern void SetIrqCall(int core);
|
||||||
extern void StartVoices(int core, u32 value);
|
extern void StartVoices(int core, u32 value);
|
||||||
extern void StopVoices(int core, u32 value);
|
extern void StopVoices(int core, u32 value);
|
||||||
extern void InitADSR();
|
extern void InitADSR();
|
||||||
extern void CalculateADSR(V_Voice &vc);
|
extern void CalculateADSR(V_Voice& vc);
|
||||||
extern void UpdateSpdifMode();
|
extern void UpdateSpdifMode();
|
||||||
|
|
||||||
namespace SPU2Savestate
|
namespace SPU2Savestate
|
||||||
{
|
{
|
||||||
struct DataBlock;
|
struct DataBlock;
|
||||||
|
|
||||||
extern s32 __fastcall FreezeIt(DataBlock &spud);
|
extern s32 __fastcall FreezeIt(DataBlock& spud);
|
||||||
extern s32 __fastcall ThawIt(DataBlock &spud);
|
extern s32 __fastcall ThawIt(DataBlock& spud);
|
||||||
extern s32 __fastcall SizeIt();
|
extern s32 __fastcall SizeIt();
|
||||||
}
|
} // namespace SPU2Savestate
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// ADPCM Decoder Cache
|
// ADPCM Decoder Cache
|
||||||
|
@ -580,8 +580,8 @@ static const int pcm_DecodedSamplesPerBlock = 28;
|
||||||
|
|
||||||
struct PcmCacheEntry
|
struct PcmCacheEntry
|
||||||
{
|
{
|
||||||
bool Validated;
|
bool Validated;
|
||||||
s16 Sampledata[pcm_DecodedSamplesPerBlock];
|
s16 Sampledata[pcm_DecodedSamplesPerBlock];
|
||||||
};
|
};
|
||||||
|
|
||||||
extern PcmCacheEntry *pcm_cache_data;
|
extern PcmCacheEntry* pcm_cache_data;
|
||||||
|
|
|
@ -184,10 +184,10 @@ Core attributes (SD_C)
|
||||||
// SPU2-X Register Table LUT
|
// SPU2-X Register Table LUT
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
#define U16P(x) ((u16 *)&(x))
|
#define U16P(x) ((u16*)&(x))
|
||||||
|
|
||||||
// Returns the hiword of a 32 bit integer.
|
// Returns the hiword of a 32 bit integer.
|
||||||
#define U16P_HI(x) (((u16 *)&(x)) + 1)
|
#define U16P_HI(x) (((u16*)&(x)) + 1)
|
||||||
|
|
||||||
extern u16 *regtable[0x401];
|
extern u16* regtable[0x401];
|
||||||
extern u16 const *const regtable_original[0x401];
|
extern u16 const* const regtable_original[0x401];
|
||||||
|
|
|
@ -67,13 +67,13 @@ Preamble W: Marks a word containing data for channel B.
|
||||||
|
|
||||||
typedef struct _subframe
|
typedef struct _subframe
|
||||||
{
|
{
|
||||||
u32 preamble : 4;
|
u32 preamble : 4;
|
||||||
u32 aux_data : 4;
|
u32 aux_data : 4;
|
||||||
u32 snd_data : 20;
|
u32 snd_data : 20;
|
||||||
u32 validity : 1;
|
u32 validity : 1;
|
||||||
u32 subcode : 1;
|
u32 subcode : 1;
|
||||||
u32 chstatus : 1;
|
u32 chstatus : 1;
|
||||||
u32 parity : 1;
|
u32 parity : 1;
|
||||||
} subframe;
|
} subframe;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -100,8 +100,8 @@ typedef struct _subframe
|
||||||
|
|
||||||
typedef struct _chstatus
|
typedef struct _chstatus
|
||||||
{
|
{
|
||||||
u8 ctrlbits : 4;
|
u8 ctrlbits : 4;
|
||||||
u8 reservd1 : 4;
|
u8 reservd1 : 4;
|
||||||
u8 category;
|
u8 category;
|
||||||
u8 reservd2[22];
|
u8 reservd2[22];
|
||||||
} chstatus:
|
} chstatus:
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
using namespace Threading;
|
using namespace Threading;
|
||||||
|
|
||||||
MutexRecursive mtx_SPU2Status;
|
MutexRecursive mtx_SPU2Status;
|
||||||
bool SPU2_dummy_callback = false;
|
bool SPU2_dummy_callback = false;
|
||||||
|
|
||||||
#include "svnrev.h"
|
#include "svnrev.h"
|
||||||
|
@ -41,7 +41,7 @@ static bool IsInitialized = false;
|
||||||
|
|
||||||
static u32 pClocks = 0;
|
static u32 pClocks = 0;
|
||||||
|
|
||||||
u32 *cyclePtr = NULL;
|
u32* cyclePtr = NULL;
|
||||||
u32 lClocks = 0;
|
u32 lClocks = 0;
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ u32 lClocks = 0;
|
||||||
|
|
||||||
static bool CheckSSE()
|
static bool CheckSSE()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
if( !cpu_detected )
|
if( !cpu_detected )
|
||||||
|
@ -68,216 +68,225 @@ static bool CheckSSE()
|
||||||
|
|
||||||
void SPU2configure()
|
void SPU2configure()
|
||||||
{
|
{
|
||||||
if (!CheckSSE())
|
if (!CheckSSE())
|
||||||
return;
|
return;
|
||||||
configure();
|
configure();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
// DMA 4/7 Callbacks from Core Emulator
|
// DMA 4/7 Callbacks from Core Emulator
|
||||||
// --------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
u16 *DMABaseAddr;
|
u16* DMABaseAddr;
|
||||||
|
|
||||||
u32 SPU2ReadMemAddr(int core)
|
u32 SPU2ReadMemAddr(int core)
|
||||||
{
|
{
|
||||||
return Cores[core].MADR;
|
return Cores[core].MADR;
|
||||||
}
|
}
|
||||||
void SPU2WriteMemAddr(int core, u32 value)
|
void SPU2WriteMemAddr(int core, u32 value)
|
||||||
{
|
{
|
||||||
Cores[core].MADR = value;
|
Cores[core].MADR = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU2setDMABaseAddr(uptr baseaddr)
|
void SPU2setDMABaseAddr(uptr baseaddr)
|
||||||
{
|
{
|
||||||
DMABaseAddr = (u16 *)baseaddr;
|
DMABaseAddr = (u16*)baseaddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU2setSettingsDir(const char *dir)
|
void SPU2setSettingsDir(const char* dir)
|
||||||
{
|
{
|
||||||
CfgSetSettingsDir(dir);
|
CfgSetSettingsDir(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU2setLogDir(const char *dir)
|
void SPU2setLogDir(const char* dir)
|
||||||
{
|
{
|
||||||
CfgSetLogDir(dir);
|
CfgSetLogDir(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU2readDMA4Mem(u16 *pMem, u32 size) // size now in 16bit units
|
void SPU2readDMA4Mem(u16* pMem, u32 size) // size now in 16bit units
|
||||||
{
|
{
|
||||||
if (cyclePtr != NULL)
|
if (cyclePtr != NULL)
|
||||||
TimeUpdate(*cyclePtr);
|
TimeUpdate(*cyclePtr);
|
||||||
|
|
||||||
FileLog("[%10d] SPU2 readDMA4Mem size %x\n", Cycles, size << 1);
|
FileLog("[%10d] SPU2 readDMA4Mem size %x\n", Cycles, size << 1);
|
||||||
Cores[0].DoDMAread(pMem, size);
|
Cores[0].DoDMAread(pMem, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU2writeDMA4Mem(u16 *pMem, u32 size) // size now in 16bit units
|
void SPU2writeDMA4Mem(u16* pMem, u32 size) // size now in 16bit units
|
||||||
{
|
{
|
||||||
if (cyclePtr != NULL)
|
if (cyclePtr != NULL)
|
||||||
TimeUpdate(*cyclePtr);
|
TimeUpdate(*cyclePtr);
|
||||||
|
|
||||||
FileLog("[%10d] SPU2 writeDMA4Mem size %x at address %x\n", Cycles, size << 1, Cores[0].TSA);
|
FileLog("[%10d] SPU2 writeDMA4Mem size %x at address %x\n", Cycles, size << 1, Cores[0].TSA);
|
||||||
#ifdef S2R_ENABLE
|
#ifdef S2R_ENABLE
|
||||||
if (!replay_mode)
|
if (!replay_mode)
|
||||||
s2r_writedma4(Cycles, pMem, size);
|
s2r_writedma4(Cycles, pMem, size);
|
||||||
#endif
|
#endif
|
||||||
Cores[0].DoDMAwrite(pMem, size);
|
Cores[0].DoDMAwrite(pMem, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU2interruptDMA4()
|
void SPU2interruptDMA4()
|
||||||
{
|
{
|
||||||
FileLog("[%10d] SPU2 interruptDMA4\n", Cycles);
|
FileLog("[%10d] SPU2 interruptDMA4\n", Cycles);
|
||||||
Cores[0].Regs.STATX |= 0x80;
|
Cores[0].Regs.STATX |= 0x80;
|
||||||
//Cores[0].Regs.ATTR &= ~0x30;
|
//Cores[0].Regs.ATTR &= ~0x30;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU2interruptDMA7()
|
void SPU2interruptDMA7()
|
||||||
{
|
{
|
||||||
FileLog("[%10d] SPU2 interruptDMA7\n", Cycles);
|
FileLog("[%10d] SPU2 interruptDMA7\n", Cycles);
|
||||||
Cores[1].Regs.STATX |= 0x80;
|
Cores[1].Regs.STATX |= 0x80;
|
||||||
//Cores[1].Regs.ATTR &= ~0x30;
|
//Cores[1].Regs.ATTR &= ~0x30;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU2readDMA7Mem(u16 *pMem, u32 size)
|
void SPU2readDMA7Mem(u16* pMem, u32 size)
|
||||||
{
|
{
|
||||||
if (cyclePtr != NULL)
|
if (cyclePtr != NULL)
|
||||||
TimeUpdate(*cyclePtr);
|
TimeUpdate(*cyclePtr);
|
||||||
|
|
||||||
FileLog("[%10d] SPU2 readDMA7Mem size %x\n", Cycles, size << 1);
|
FileLog("[%10d] SPU2 readDMA7Mem size %x\n", Cycles, size << 1);
|
||||||
Cores[1].DoDMAread(pMem, size);
|
Cores[1].DoDMAread(pMem, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU2writeDMA7Mem(u16 *pMem, u32 size)
|
void SPU2writeDMA7Mem(u16* pMem, u32 size)
|
||||||
{
|
{
|
||||||
if (cyclePtr != NULL)
|
if (cyclePtr != NULL)
|
||||||
TimeUpdate(*cyclePtr);
|
TimeUpdate(*cyclePtr);
|
||||||
|
|
||||||
FileLog("[%10d] SPU2 writeDMA7Mem size %x at address %x\n", Cycles, size << 1, Cores[1].TSA);
|
FileLog("[%10d] SPU2 writeDMA7Mem size %x at address %x\n", Cycles, size << 1, Cores[1].TSA);
|
||||||
#ifdef S2R_ENABLE
|
#ifdef S2R_ENABLE
|
||||||
if (!replay_mode)
|
if (!replay_mode)
|
||||||
s2r_writedma7(Cycles, pMem, size);
|
s2r_writedma7(Cycles, pMem, size);
|
||||||
#endif
|
#endif
|
||||||
Cores[1].DoDMAwrite(pMem, size);
|
Cores[1].DoDMAwrite(pMem, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 SPU2reset()
|
s32 SPU2reset()
|
||||||
{
|
{
|
||||||
if (SndBuffer::Test() == 0 && SampleRate != 48000)
|
if (SndBuffer::Test() == 0 && SampleRate != 48000)
|
||||||
{
|
{
|
||||||
SampleRate = 48000;
|
SampleRate = 48000;
|
||||||
SndBuffer::Cleanup();
|
SndBuffer::Cleanup();
|
||||||
|
|
||||||
try {
|
try
|
||||||
SndBuffer::Init();
|
{
|
||||||
}
|
SndBuffer::Init();
|
||||||
catch (std::exception& ex) {
|
}
|
||||||
fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what());
|
catch (std::exception& ex)
|
||||||
SPU2close();
|
{
|
||||||
return -1;
|
fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what());
|
||||||
}
|
SPU2close();
|
||||||
}
|
return -1;
|
||||||
else
|
}
|
||||||
SampleRate = 48000;
|
}
|
||||||
|
else
|
||||||
|
SampleRate = 48000;
|
||||||
|
|
||||||
memset(spu2regs, 0, 0x010000);
|
memset(spu2regs, 0, 0x010000);
|
||||||
memset(_spu2mem, 0, 0x200000);
|
memset(_spu2mem, 0, 0x200000);
|
||||||
memset(_spu2mem + 0x2800, 7, 0x10); // from BIOS reversal. Locks the voices so they don't run free.
|
memset(_spu2mem + 0x2800, 7, 0x10); // from BIOS reversal. Locks the voices so they don't run free.
|
||||||
Cores[0].Init(0);
|
Cores[0].Init(0);
|
||||||
Cores[1].Init(1);
|
Cores[1].Init(1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 SPU2ps1reset()
|
s32 SPU2ps1reset()
|
||||||
{
|
{
|
||||||
printf("RESET PS1 \n");
|
printf("RESET PS1 \n");
|
||||||
|
|
||||||
if (SndBuffer::Test() == 0 && SampleRate != 44100)
|
if (SndBuffer::Test() == 0 && SampleRate != 44100)
|
||||||
{
|
{
|
||||||
SampleRate = 44100;
|
SampleRate = 44100;
|
||||||
SndBuffer::Cleanup();
|
SndBuffer::Cleanup();
|
||||||
|
|
||||||
try {
|
try
|
||||||
SndBuffer::Init();
|
{
|
||||||
}
|
SndBuffer::Init();
|
||||||
catch (std::exception& ex) {
|
}
|
||||||
fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what());
|
catch (std::exception& ex)
|
||||||
SPU2close();
|
{
|
||||||
return -1;
|
fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what());
|
||||||
}
|
SPU2close();
|
||||||
}
|
return -1;
|
||||||
else
|
}
|
||||||
SampleRate = 44100;
|
}
|
||||||
|
else
|
||||||
|
SampleRate = 44100;
|
||||||
|
|
||||||
/* memset(spu2regs, 0, 0x010000);
|
/* memset(spu2regs, 0, 0x010000);
|
||||||
memset(_spu2mem, 0, 0x200000);
|
memset(_spu2mem, 0, 0x200000);
|
||||||
memset(_spu2mem + 0x2800, 7, 0x10); // from BIOS reversal. Locks the voices so they don't run free.
|
memset(_spu2mem + 0x2800, 7, 0x10); // from BIOS reversal. Locks the voices so they don't run free.
|
||||||
Cores[0].Init(0);
|
Cores[0].Init(0);
|
||||||
Cores[1].Init(1);*/
|
Cores[1].Init(1);*/
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 SPU2init()
|
s32 SPU2init()
|
||||||
{
|
{
|
||||||
assert(regtable[0x400] == NULL);
|
assert(regtable[0x400] == NULL);
|
||||||
|
|
||||||
if (IsInitialized) {
|
if (IsInitialized)
|
||||||
printf(" * SPU2-X: Already initialized - Ignoring SPU2init signal.");
|
{
|
||||||
return 0;
|
printf(" * SPU2-X: Already initialized - Ignoring SPU2init signal.");
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
IsInitialized = true;
|
IsInitialized = true;
|
||||||
SPU2_dummy_callback = false;
|
SPU2_dummy_callback = false;
|
||||||
|
|
||||||
ReadSettings();
|
ReadSettings();
|
||||||
|
|
||||||
#ifdef SPU2_LOG
|
#ifdef SPU2_LOG
|
||||||
if (AccessLog()) {
|
if (AccessLog())
|
||||||
spu2Log = OpenLog(AccessLogFileName);
|
{
|
||||||
setvbuf(spu2Log, NULL, _IONBF, 0);
|
spu2Log = OpenLog(AccessLogFileName);
|
||||||
FileLog("SPU2init\n");
|
setvbuf(spu2Log, NULL, _IONBF, 0);
|
||||||
}
|
FileLog("SPU2init\n");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
srand((unsigned)time(NULL));
|
srand((unsigned)time(NULL));
|
||||||
|
|
||||||
spu2regs = (s16 *)malloc(0x010000);
|
spu2regs = (s16*)malloc(0x010000);
|
||||||
_spu2mem = (s16 *)malloc(0x200000);
|
_spu2mem = (s16*)malloc(0x200000);
|
||||||
|
|
||||||
// adpcm decoder cache:
|
// adpcm decoder cache:
|
||||||
// the cache data size is determined by taking the number of adpcm blocks
|
// 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).
|
// (2MB / 16) and multiplying it by the decoded block size (28 samples).
|
||||||
// Thus: pcm_cache_data = 7,340,032 bytes (ouch!)
|
// Thus: pcm_cache_data = 7,340,032 bytes (ouch!)
|
||||||
// Expanded: 16 bytes expands to 56 bytes [3.5:1 ratio]
|
// Expanded: 16 bytes expands to 56 bytes [3.5:1 ratio]
|
||||||
// Resulting in 2MB * 3.5.
|
// Resulting in 2MB * 3.5.
|
||||||
|
|
||||||
pcm_cache_data = (PcmCacheEntry *)calloc(pcm_BlockCount, sizeof(PcmCacheEntry));
|
pcm_cache_data = (PcmCacheEntry*)calloc(pcm_BlockCount, sizeof(PcmCacheEntry));
|
||||||
|
|
||||||
if ((spu2regs == NULL) || (_spu2mem == NULL) || (pcm_cache_data == NULL)) {
|
if ((spu2regs == NULL) || (_spu2mem == NULL) || (pcm_cache_data == NULL))
|
||||||
SysMessage("SPU2-X: Error allocating Memory\n");
|
{
|
||||||
return -1;
|
SysMessage("SPU2-X: Error allocating Memory\n");
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Patch up a copy of regtable that directly maps "NULLs" to SPU2 memory.
|
// Patch up a copy of regtable that directly maps "NULLs" to SPU2 memory.
|
||||||
|
|
||||||
memcpy(regtable, regtable_original, sizeof(regtable));
|
memcpy(regtable, regtable_original, sizeof(regtable));
|
||||||
|
|
||||||
for (uint mem = 0; mem < 0x800; mem++) {
|
for (uint mem = 0; mem < 0x800; mem++)
|
||||||
u16 *ptr = regtable[mem >> 1];
|
{
|
||||||
if (!ptr) {
|
u16* ptr = regtable[mem >> 1];
|
||||||
regtable[mem >> 1] = &(spu2Ru16(mem));
|
if (!ptr)
|
||||||
}
|
{
|
||||||
}
|
regtable[mem >> 1] = &(spu2Ru16(mem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SPU2reset();
|
SPU2reset();
|
||||||
|
|
||||||
DMALogOpen();
|
DMALogOpen();
|
||||||
InitADSR();
|
InitADSR();
|
||||||
|
|
||||||
#ifdef S2R_ENABLE
|
#ifdef S2R_ENABLE
|
||||||
if (!replay_mode)
|
if (!replay_mode)
|
||||||
s2r_open(Cycles, "replay_dump.s2r");
|
s2r_open(Cycles, "replay_dump.s2r");
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
@ -287,147 +296,158 @@ extern HWND hDebugDialog;
|
||||||
|
|
||||||
static INT_PTR CALLBACK DebugProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
static INT_PTR CALLBACK DebugProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
int wmId;
|
int wmId;
|
||||||
|
|
||||||
switch (uMsg) {
|
switch (uMsg)
|
||||||
case WM_PAINT:
|
{
|
||||||
return FALSE;
|
case WM_PAINT:
|
||||||
case WM_INITDIALOG: {
|
return FALSE;
|
||||||
debugDialogOpen = true;
|
case WM_INITDIALOG:
|
||||||
} break;
|
{
|
||||||
|
debugDialogOpen = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WM_COMMAND:
|
case WM_COMMAND:
|
||||||
wmId = LOWORD(wParam);
|
wmId = LOWORD(wParam);
|
||||||
// Parse the menu selections:
|
// Parse the menu selections:
|
||||||
switch (wmId) {
|
switch (wmId)
|
||||||
case IDOK:
|
{
|
||||||
case IDCANCEL:
|
case IDOK:
|
||||||
debugDialogOpen = false;
|
case IDCANCEL:
|
||||||
EndDialog(hWnd, 0);
|
debugDialogOpen = false;
|
||||||
break;
|
EndDialog(hWnd, 0);
|
||||||
default:
|
break;
|
||||||
return FALSE;
|
default:
|
||||||
}
|
return FALSE;
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
uptr gsWindowHandle = 0;
|
uptr gsWindowHandle = 0;
|
||||||
|
|
||||||
s32 SPU2open(void *pDsp)
|
s32 SPU2open(void* pDsp)
|
||||||
{
|
{
|
||||||
ScopedLock lock( mtx_SPU2Status );
|
ScopedLock lock(mtx_SPU2Status);
|
||||||
if (IsOpened)
|
if (IsOpened)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
FileLog("[%10d] SPU2 Open\n", Cycles);
|
FileLog("[%10d] SPU2 Open\n", Cycles);
|
||||||
|
|
||||||
if (pDsp != NULL)
|
if (pDsp != NULL)
|
||||||
gsWindowHandle = *(uptr *)pDsp;
|
gsWindowHandle = *(uptr*)pDsp;
|
||||||
else
|
else
|
||||||
gsWindowHandle = 0;
|
gsWindowHandle = 0;
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#ifdef PCSX2_DEVBUILD // Define may not be needed but not tested yet. Better make sure.
|
#ifdef PCSX2_DEVBUILD // Define may not be needed but not tested yet. Better make sure.
|
||||||
if (IsDevBuild && VisualDebug()) {
|
if (IsDevBuild && VisualDebug())
|
||||||
if (debugDialogOpen == 0) {
|
{
|
||||||
hDebugDialog = CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_DEBUG), 0, DebugProc, 0);
|
if (debugDialogOpen == 0)
|
||||||
ShowWindow(hDebugDialog, SW_SHOWNORMAL);
|
{
|
||||||
debugDialogOpen = 1;
|
hDebugDialog = CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_DEBUG), 0, DebugProc, 0);
|
||||||
}
|
ShowWindow(hDebugDialog, SW_SHOWNORMAL);
|
||||||
} else if (debugDialogOpen) {
|
debugDialogOpen = 1;
|
||||||
DestroyWindow(hDebugDialog);
|
}
|
||||||
debugDialogOpen = 0;
|
}
|
||||||
}
|
else if (debugDialogOpen)
|
||||||
|
{
|
||||||
|
DestroyWindow(hDebugDialog);
|
||||||
|
debugDialogOpen = 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
IsOpened = true;
|
IsOpened = true;
|
||||||
lClocks = (cyclePtr != NULL) ? *cyclePtr : 0;
|
lClocks = (cyclePtr != NULL) ? *cyclePtr : 0;
|
||||||
|
|
||||||
try {
|
try
|
||||||
SndBuffer::Init();
|
{
|
||||||
|
SndBuffer::Init();
|
||||||
|
|
||||||
#ifndef __POSIX__
|
#ifndef __POSIX__
|
||||||
DspLoadLibrary(dspPlugin, dspPluginModule);
|
DspLoadLibrary(dspPlugin, dspPluginModule);
|
||||||
#endif
|
#endif
|
||||||
WaveDump::Open();
|
WaveDump::Open();
|
||||||
} catch (std::exception &ex) {
|
}
|
||||||
fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what());
|
catch (std::exception& ex)
|
||||||
SPU2close();
|
{
|
||||||
return -1;
|
fprintf(stderr, "SPU2-X Error: Could not initialize device, or something.\nReason: %s", ex.what());
|
||||||
}
|
SPU2close();
|
||||||
SPU2setDMABaseAddr((uptr)iopMem->Main);
|
return -1;
|
||||||
|
}
|
||||||
|
SPU2setDMABaseAddr((uptr)iopMem->Main);
|
||||||
SPU2setClockPtr(&psxRegs.cycle);
|
SPU2setClockPtr(&psxRegs.cycle);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU2close()
|
void SPU2close()
|
||||||
{
|
{
|
||||||
ScopedLock lock( mtx_SPU2Status );
|
ScopedLock lock(mtx_SPU2Status);
|
||||||
if (!IsOpened)
|
if (!IsOpened)
|
||||||
return;
|
return;
|
||||||
IsOpened = false;
|
IsOpened = false;
|
||||||
|
|
||||||
FileLog("[%10d] SPU2 Close\n", Cycles);
|
FileLog("[%10d] SPU2 Close\n", Cycles);
|
||||||
|
|
||||||
#ifndef __POSIX__
|
#ifndef __POSIX__
|
||||||
DspCloseLibrary();
|
DspCloseLibrary();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SndBuffer::Cleanup();
|
SndBuffer::Cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU2shutdown()
|
void SPU2shutdown()
|
||||||
{
|
{
|
||||||
if (!IsInitialized)
|
if (!IsInitialized)
|
||||||
return;
|
return;
|
||||||
IsInitialized = false;
|
IsInitialized = false;
|
||||||
SPU2_dummy_callback = false;
|
SPU2_dummy_callback = false;
|
||||||
|
|
||||||
ConLog("* SPU2-X: Shutting down.\n");
|
ConLog("* SPU2-X: Shutting down.\n");
|
||||||
|
|
||||||
SPU2close();
|
SPU2close();
|
||||||
|
|
||||||
#ifdef S2R_ENABLE
|
#ifdef S2R_ENABLE
|
||||||
if (!replay_mode)
|
if (!replay_mode)
|
||||||
s2r_close();
|
s2r_close();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DoFullDump();
|
DoFullDump();
|
||||||
#ifdef STREAM_DUMP
|
#ifdef STREAM_DUMP
|
||||||
fclose(il0);
|
fclose(il0);
|
||||||
fclose(il1);
|
fclose(il1);
|
||||||
#endif
|
#endif
|
||||||
#ifdef EFFECTS_DUMP
|
#ifdef EFFECTS_DUMP
|
||||||
fclose(el0);
|
fclose(el0);
|
||||||
fclose(el1);
|
fclose(el1);
|
||||||
#endif
|
#endif
|
||||||
WaveDump::Close();
|
WaveDump::Close();
|
||||||
|
|
||||||
DMALogClose();
|
DMALogClose();
|
||||||
|
|
||||||
safe_free(spu2regs);
|
safe_free(spu2regs);
|
||||||
safe_free(_spu2mem);
|
safe_free(_spu2mem);
|
||||||
safe_free(pcm_cache_data);
|
safe_free(pcm_cache_data);
|
||||||
|
|
||||||
|
|
||||||
#ifdef SPU2_LOG
|
#ifdef SPU2_LOG
|
||||||
if (!AccessLog())
|
if (!AccessLog())
|
||||||
return;
|
return;
|
||||||
FileLog("[%10d] SPU2shutdown\n", Cycles);
|
FileLog("[%10d] SPU2shutdown\n", Cycles);
|
||||||
if (spu2Log)
|
if (spu2Log)
|
||||||
fclose(spu2Log);
|
fclose(spu2Log);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU2setClockPtr(u32 *ptr)
|
void SPU2setClockPtr(u32* ptr)
|
||||||
{
|
{
|
||||||
cyclePtr = ptr;
|
cyclePtr = ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_KEYS
|
#ifdef DEBUG_KEYS
|
||||||
|
@ -437,116 +457,134 @@ static bool lState[6];
|
||||||
|
|
||||||
void SPU2async(u32 cycles)
|
void SPU2async(u32 cycles)
|
||||||
{
|
{
|
||||||
DspUpdate();
|
DspUpdate();
|
||||||
|
|
||||||
if (cyclePtr != NULL) {
|
if (cyclePtr != NULL)
|
||||||
TimeUpdate(*cyclePtr);
|
{
|
||||||
} else {
|
TimeUpdate(*cyclePtr);
|
||||||
pClocks += cycles;
|
}
|
||||||
TimeUpdate(pClocks);
|
else
|
||||||
}
|
{
|
||||||
|
pClocks += cycles;
|
||||||
|
TimeUpdate(pClocks);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_KEYS
|
#ifdef DEBUG_KEYS
|
||||||
u32 curTicks = GetTickCount();
|
u32 curTicks = GetTickCount();
|
||||||
if ((curTicks - lastTicks) >= 50) {
|
if ((curTicks - lastTicks) >= 50)
|
||||||
int oldI = Interpolation;
|
{
|
||||||
bool cState[6];
|
int oldI = Interpolation;
|
||||||
for (int i = 0; i < 6; i++) {
|
bool cState[6];
|
||||||
cState[i] = !!(GetAsyncKeyState(VK_NUMPAD0 + i) & 0x8000);
|
for (int i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
cState[i] = !!(GetAsyncKeyState(VK_NUMPAD0 + i) & 0x8000);
|
||||||
|
|
||||||
if ((cState[i] && !lState[i]) && i != 5)
|
if ((cState[i] && !lState[i]) && i != 5)
|
||||||
Interpolation = i;
|
Interpolation = i;
|
||||||
|
|
||||||
if ((cState[i] && !lState[i]) && i == 5) {
|
if ((cState[i] && !lState[i]) && i == 5)
|
||||||
postprocess_filter_enabled = !postprocess_filter_enabled;
|
{
|
||||||
printf("Post process filters %s \n", postprocess_filter_enabled ? "enabled" : "disabled");
|
postprocess_filter_enabled = !postprocess_filter_enabled;
|
||||||
}
|
printf("Post process filters %s \n", postprocess_filter_enabled ? "enabled" : "disabled");
|
||||||
|
}
|
||||||
|
|
||||||
lState[i] = cState[i];
|
lState[i] = cState[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Interpolation != oldI) {
|
if (Interpolation != oldI)
|
||||||
printf("Interpolation set to %d", Interpolation);
|
{
|
||||||
switch (Interpolation) {
|
printf("Interpolation set to %d", Interpolation);
|
||||||
case 0:
|
switch (Interpolation)
|
||||||
printf(" - Nearest.\n");
|
{
|
||||||
break;
|
case 0:
|
||||||
case 1:
|
printf(" - Nearest.\n");
|
||||||
printf(" - Linear.\n");
|
break;
|
||||||
break;
|
case 1:
|
||||||
case 2:
|
printf(" - Linear.\n");
|
||||||
printf(" - Cubic.\n");
|
break;
|
||||||
break;
|
case 2:
|
||||||
case 3:
|
printf(" - Cubic.\n");
|
||||||
printf(" - Hermite.\n");
|
break;
|
||||||
break;
|
case 3:
|
||||||
case 4:
|
printf(" - Hermite.\n");
|
||||||
printf(" - Catmull-Rom.\n");
|
break;
|
||||||
break;
|
case 4:
|
||||||
default:
|
printf(" - Catmull-Rom.\n");
|
||||||
printf(" (unknown).\n");
|
break;
|
||||||
break;
|
default:
|
||||||
}
|
printf(" (unknown).\n");
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lastTicks = curTicks;
|
lastTicks = curTicks;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 SPU2read(u32 rmem)
|
u16 SPU2read(u32 rmem)
|
||||||
{
|
{
|
||||||
// if(!replay_mode)
|
// if(!replay_mode)
|
||||||
// s2r_readreg(Cycles,rmem);
|
// s2r_readreg(Cycles,rmem);
|
||||||
|
|
||||||
u16 ret = 0xDEAD;
|
u16 ret = 0xDEAD;
|
||||||
u32 core = 0, mem = rmem & 0xFFFF, omem = mem;
|
u32 core = 0, mem = rmem & 0xFFFF, omem = mem;
|
||||||
if (mem & 0x400) {
|
if (mem & 0x400)
|
||||||
omem ^= 0x400;
|
{
|
||||||
core = 1;
|
omem ^= 0x400;
|
||||||
}
|
core = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (omem == 0x1f9001AC) {
|
if (omem == 0x1f9001AC)
|
||||||
ret = Cores[core].DmaRead();
|
{
|
||||||
} else {
|
ret = Cores[core].DmaRead();
|
||||||
if (cyclePtr != NULL)
|
}
|
||||||
TimeUpdate(*cyclePtr);
|
else
|
||||||
|
{
|
||||||
|
if (cyclePtr != NULL)
|
||||||
|
TimeUpdate(*cyclePtr);
|
||||||
|
|
||||||
if (rmem >> 16 == 0x1f80) {
|
if (rmem >> 16 == 0x1f80)
|
||||||
ret = Cores[0].ReadRegPS1(rmem);
|
{
|
||||||
} else if (mem >= 0x800) {
|
ret = Cores[0].ReadRegPS1(rmem);
|
||||||
ret = spu2Ru16(mem);
|
}
|
||||||
ConLog("* SPU2-X: Read from reg>=0x800: %x value %x\n", mem, ret);
|
else if (mem >= 0x800)
|
||||||
} else {
|
{
|
||||||
ret = *(regtable[(mem >> 1)]);
|
ret = spu2Ru16(mem);
|
||||||
//FileLog("[%10d] SPU2 read mem %x (core %d, register %x): %x\n",Cycles, mem, core, (omem & 0x7ff), ret);
|
ConLog("* SPU2-X: Read from reg>=0x800: %x value %x\n", mem, ret);
|
||||||
SPU2writeLog("read", rmem, 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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU2write(u32 rmem, u16 value)
|
void SPU2write(u32 rmem, u16 value)
|
||||||
{
|
{
|
||||||
#ifdef S2R_ENABLE
|
#ifdef S2R_ENABLE
|
||||||
if (!replay_mode)
|
if (!replay_mode)
|
||||||
s2r_writereg(Cycles, rmem, value);
|
s2r_writereg(Cycles, rmem, value);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Note: Reverb/Effects are very sensitive to having precise update timings.
|
// 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
|
// If the SPU2 isn't in in sync with the IOP, samples can end up playing at rather
|
||||||
// incorrect pitches and loop lengths.
|
// incorrect pitches and loop lengths.
|
||||||
|
|
||||||
if (cyclePtr != NULL)
|
if (cyclePtr != NULL)
|
||||||
TimeUpdate(*cyclePtr);
|
TimeUpdate(*cyclePtr);
|
||||||
|
|
||||||
if (rmem >> 16 == 0x1f80)
|
if (rmem >> 16 == 0x1f80)
|
||||||
Cores[0].WriteRegPS1(rmem, value);
|
Cores[0].WriteRegPS1(rmem, value);
|
||||||
else {
|
else
|
||||||
SPU2writeLog("write", rmem, value);
|
{
|
||||||
SPU2_FastWrite(rmem, value);
|
SPU2writeLog("write", rmem, value);
|
||||||
}
|
SPU2_FastWrite(rmem, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if start is 1, starts recording spu2 data, else stops
|
// if start is 1, starts recording spu2 data, else stops
|
||||||
|
@ -554,80 +592,86 @@ void SPU2write(u32 rmem, u16 value)
|
||||||
// for now, pData is not used
|
// for now, pData is not used
|
||||||
int SPU2setupRecording(int start, std::wstring* filename)
|
int SPU2setupRecording(int start, std::wstring* filename)
|
||||||
{
|
{
|
||||||
if (start == 0)
|
if (start == 0)
|
||||||
RecordStop();
|
RecordStop();
|
||||||
else if (start == 1)
|
else if (start == 1)
|
||||||
RecordStart(filename);
|
RecordStart(filename);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 SPU2freeze(int mode, freezeData *data)
|
s32 SPU2freeze(int mode, freezeData* data)
|
||||||
{
|
{
|
||||||
pxAssume(data != NULL);
|
pxAssume(data != NULL);
|
||||||
if (!data) {
|
if (!data)
|
||||||
printf("SPU2-X savestate null pointer!\n");
|
{
|
||||||
return -1;
|
printf("SPU2-X savestate null pointer!\n");
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (mode == FREEZE_SIZE) {
|
if (mode == FREEZE_SIZE)
|
||||||
data->size = SPU2Savestate::SizeIt();
|
{
|
||||||
return 0;
|
data->size = SPU2Savestate::SizeIt();
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
pxAssume(mode == FREEZE_LOAD || mode == FREEZE_SAVE);
|
pxAssume(mode == FREEZE_LOAD || mode == FREEZE_SAVE);
|
||||||
|
|
||||||
if (data->data == NULL) {
|
if (data->data == NULL)
|
||||||
printf("SPU2-X savestate null pointer!\n");
|
{
|
||||||
return -1;
|
printf("SPU2-X savestate null pointer!\n");
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
SPU2Savestate::DataBlock &spud = (SPU2Savestate::DataBlock &)*(data->data);
|
SPU2Savestate::DataBlock& spud = (SPU2Savestate::DataBlock&)*(data->data);
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode)
|
||||||
case FREEZE_LOAD:
|
{
|
||||||
return SPU2Savestate::ThawIt(spud);
|
case FREEZE_LOAD:
|
||||||
case FREEZE_SAVE:
|
return SPU2Savestate::ThawIt(spud);
|
||||||
return SPU2Savestate::FreezeIt(spud);
|
case FREEZE_SAVE:
|
||||||
|
return SPU2Savestate::FreezeIt(spud);
|
||||||
|
|
||||||
jNO_DEFAULT;
|
jNO_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// technically unreachable, but kills a warning:
|
// technically unreachable, but kills a warning:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU2DoFreezeOut( void* dest )
|
void SPU2DoFreezeOut(void* dest)
|
||||||
{
|
{
|
||||||
ScopedLock lock( mtx_SPU2Status );
|
ScopedLock lock(mtx_SPU2Status);
|
||||||
|
|
||||||
freezeData fP = { 0, (s8*)dest };
|
freezeData fP = {0, (s8*)dest};
|
||||||
if (SPU2freeze( FREEZE_SIZE, &fP)!=0) return;
|
if (SPU2freeze(FREEZE_SIZE, &fP) != 0)
|
||||||
if (!fP.size) return;
|
return;
|
||||||
|
if (!fP.size)
|
||||||
|
return;
|
||||||
|
|
||||||
Console.Indent().WriteLn( "Saving SPU-2");
|
Console.Indent().WriteLn("Saving SPU-2");
|
||||||
|
|
||||||
if (SPU2freeze(FREEZE_SAVE, &fP)!=0)
|
if (SPU2freeze(FREEZE_SAVE, &fP) != 0)
|
||||||
throw std::runtime_error(" * SPU-2: Error saving state!\n");
|
throw std::runtime_error(" * SPU-2: Error saving state!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SPU2DoFreezeIn( pxInputStream& infp )
|
void SPU2DoFreezeIn(pxInputStream& infp)
|
||||||
{
|
{
|
||||||
ScopedLock lock( mtx_SPU2Status );
|
ScopedLock lock(mtx_SPU2Status);
|
||||||
|
|
||||||
freezeData fP = { 0, NULL };
|
freezeData fP = {0, NULL};
|
||||||
if (SPU2freeze( FREEZE_SIZE, &fP )!=0)
|
if (SPU2freeze(FREEZE_SIZE, &fP) != 0)
|
||||||
fP.size = 0;
|
fP.size = 0;
|
||||||
|
|
||||||
Console.Indent().WriteLn( "Loading SPU-2");
|
Console.Indent().WriteLn("Loading SPU-2");
|
||||||
|
|
||||||
if (!infp.IsOk() || !infp.Length())
|
if (!infp.IsOk() || !infp.Length())
|
||||||
{
|
{
|
||||||
// no state data to read, but SPU-2 expects some state data?
|
// no state data to read, but SPU-2 expects some state data?
|
||||||
// Issue a warning to console...
|
// Issue a warning to console...
|
||||||
if( fP.size != 0 )
|
if (fP.size != 0)
|
||||||
Console.Indent().Warning( "Warning: No data for SPU-2 found. Status may be unpredictable." );
|
Console.Indent().Warning("Warning: No data for SPU-2 found. Status may be unpredictable.");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -637,10 +681,10 @@ void SPU2DoFreezeIn( pxInputStream& infp )
|
||||||
// on the status of the plugin when loading, so let's ignore it.
|
// on the status of the plugin when loading, so let's ignore it.
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedAlloc<s8> data( fP.size );
|
ScopedAlloc<s8> data(fP.size);
|
||||||
fP.data = data.GetPtr();
|
fP.data = data.GetPtr();
|
||||||
|
|
||||||
infp.Read( fP.data, fP.size );
|
infp.Read(fP.data, fP.size);
|
||||||
if (SPU2freeze(FREEZE_LOAD, &fP)!=0)
|
if (SPU2freeze(FREEZE_LOAD, &fP) != 0)
|
||||||
throw std::runtime_error(" * SPU-2: Error loading state!\n");
|
throw std::runtime_error(" * SPU-2: Error loading state!\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,14 +19,14 @@
|
||||||
#include "Utilities/Threading.h"
|
#include "Utilities/Threading.h"
|
||||||
#include "SaveState.h"
|
#include "SaveState.h"
|
||||||
|
|
||||||
extern Threading::MutexRecursive mtx_SPU2Status;
|
extern Threading::MutexRecursive mtx_SPU2Status;
|
||||||
|
|
||||||
extern bool SPU2_dummy_callback;
|
extern bool SPU2_dummy_callback;
|
||||||
|
|
||||||
s32 SPU2init();
|
s32 SPU2init();
|
||||||
s32 SPU2reset();
|
s32 SPU2reset();
|
||||||
s32 SPU2ps1reset();
|
s32 SPU2ps1reset();
|
||||||
s32 SPU2open(void *pDsp);
|
s32 SPU2open(void* pDsp);
|
||||||
void SPU2close();
|
void SPU2close();
|
||||||
void SPU2shutdown();
|
void SPU2shutdown();
|
||||||
void SPU2write(u32 mem, u16 value);
|
void SPU2write(u32 mem, u16 value);
|
||||||
|
@ -38,40 +38,40 @@ u16 SPU2read(u32 mem);
|
||||||
// for now, pData is not used
|
// for now, pData is not used
|
||||||
int SPU2setupRecording(int start, std::wstring* filename);
|
int SPU2setupRecording(int start, std::wstring* filename);
|
||||||
|
|
||||||
void SPU2setClockPtr(u32 *ptr);
|
void SPU2setClockPtr(u32* ptr);
|
||||||
|
|
||||||
void SPU2async(u32 cycles);
|
void SPU2async(u32 cycles);
|
||||||
s32 SPU2freeze(int mode, freezeData *data);
|
s32 SPU2freeze(int mode, freezeData* data);
|
||||||
void SPU2DoFreezeIn( pxInputStream& infp );
|
void SPU2DoFreezeIn(pxInputStream& infp);
|
||||||
void SPU2DoFreezeOut( void* dest );
|
void SPU2DoFreezeOut(void* dest);
|
||||||
void SPU2configure();
|
void SPU2configure();
|
||||||
|
|
||||||
|
|
||||||
u32 SPU2ReadMemAddr(int core);
|
u32 SPU2ReadMemAddr(int core);
|
||||||
void SPU2WriteMemAddr(int core, u32 value);
|
void SPU2WriteMemAddr(int core, u32 value);
|
||||||
void SPU2setDMABaseAddr(uptr baseaddr);
|
void SPU2setDMABaseAddr(uptr baseaddr);
|
||||||
void SPU2setSettingsDir(const char *dir);
|
void SPU2setSettingsDir(const char* dir);
|
||||||
void SPU2setLogDir(const char *dir);
|
void SPU2setLogDir(const char* dir);
|
||||||
void SPU2readDMA4Mem(u16 *pMem, u32 size);
|
void SPU2readDMA4Mem(u16* pMem, u32 size);
|
||||||
void SPU2writeDMA4Mem(u16 *pMem, u32 size);
|
void SPU2writeDMA4Mem(u16* pMem, u32 size);
|
||||||
void SPU2interruptDMA4();
|
void SPU2interruptDMA4();
|
||||||
void SPU2interruptDMA7();
|
void SPU2interruptDMA7();
|
||||||
void SPU2readDMA7Mem(u16 *pMem, u32 size);
|
void SPU2readDMA7Mem(u16* pMem, u32 size);
|
||||||
void SPU2writeDMA7Mem(u16 *pMem, u32 size);
|
void SPU2writeDMA7Mem(u16* pMem, u32 size);
|
||||||
#include "spu2replay.h"
|
#include "spu2replay.h"
|
||||||
|
|
||||||
extern u8 callirq;
|
extern u8 callirq;
|
||||||
|
|
||||||
extern s16 *input_data;
|
extern s16* input_data;
|
||||||
extern u32 input_data_ptr;
|
extern u32 input_data_ptr;
|
||||||
|
|
||||||
extern double srate_pv;
|
extern double srate_pv;
|
||||||
|
|
||||||
extern int recording;
|
extern int recording;
|
||||||
extern u32 lClocks;
|
extern u32 lClocks;
|
||||||
extern u32 *cyclePtr;
|
extern u32* cyclePtr;
|
||||||
|
|
||||||
extern void SPU2writeLog(const char *action, u32 rmem, u16 value);
|
extern void SPU2writeLog(const char* action, u32 rmem, u16 value);
|
||||||
extern void TimeUpdate(u32 cClocks);
|
extern void TimeUpdate(u32 cClocks);
|
||||||
extern void SPU2_FastWrite(u32 rmem, u16 value);
|
extern void SPU2_FastWrite(u32 rmem, u16 value);
|
||||||
|
|
||||||
|
|
|
@ -18,128 +18,133 @@
|
||||||
|
|
||||||
namespace SPU2Savestate
|
namespace SPU2Savestate
|
||||||
{
|
{
|
||||||
// Arbitrary ID to identify SPU2-X saves.
|
// Arbitrary ID to identify SPU2-X saves.
|
||||||
static const u32 SAVE_ID = 0x1227521;
|
static const u32 SAVE_ID = 0x1227521;
|
||||||
|
|
||||||
// versioning for saves.
|
// versioning for saves.
|
||||||
// Increment this when changes to the savestate system are made.
|
// Increment this when changes to the savestate system are made.
|
||||||
static const u32 SAVE_VERSION = 0x000e;
|
static const u32 SAVE_VERSION = 0x000e;
|
||||||
|
|
||||||
static void wipe_the_cache()
|
static void wipe_the_cache()
|
||||||
{
|
{
|
||||||
memset(pcm_cache_data, 0, pcm_BlockCount * sizeof(PcmCacheEntry));
|
memset(pcm_cache_data, 0, pcm_BlockCount * sizeof(PcmCacheEntry));
|
||||||
}
|
}
|
||||||
}
|
} // namespace SPU2Savestate
|
||||||
|
|
||||||
struct SPU2Savestate::DataBlock
|
struct SPU2Savestate::DataBlock
|
||||||
{
|
{
|
||||||
u32 spu2id; // SPU2-X state identifier lets ZeroGS/PeopsSPU2 know this isn't their state)
|
u32 spu2id; // SPU2-X state identifier lets ZeroGS/PeopsSPU2 know this isn't their state)
|
||||||
u8 unkregs[0x10000]; // SPU2 raw register memory
|
u8 unkregs[0x10000]; // SPU2 raw register memory
|
||||||
u8 mem[0x200000]; // SPU2 raw sample memory
|
u8 mem[0x200000]; // SPU2 raw sample memory
|
||||||
|
|
||||||
u32 version; // SPU2-X version identifier
|
u32 version; // SPU2-X version identifier
|
||||||
V_Core Cores[2];
|
V_Core Cores[2];
|
||||||
V_SPDIF Spdif;
|
V_SPDIF Spdif;
|
||||||
s16 OutPos;
|
s16 OutPos;
|
||||||
s16 InputPos;
|
s16 InputPos;
|
||||||
u32 Cycles;
|
u32 Cycles;
|
||||||
u32 lClocks;
|
u32 lClocks;
|
||||||
int PlayMode;
|
int PlayMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
s32 __fastcall SPU2Savestate::FreezeIt(DataBlock &spud)
|
s32 __fastcall SPU2Savestate::FreezeIt(DataBlock& spud)
|
||||||
{
|
{
|
||||||
spud.spu2id = SAVE_ID;
|
spud.spu2id = SAVE_ID;
|
||||||
spud.version = SAVE_VERSION;
|
spud.version = SAVE_VERSION;
|
||||||
|
|
||||||
pxAssertMsg(spu2regs && _spu2mem, "Looks like PCSX2 is trying to savestate while components are shut down. That's a no-no! It shouldn't crash, but the savestate will probably be corrupted.");
|
pxAssertMsg(spu2regs && _spu2mem, "Looks like PCSX2 is trying to savestate while components are shut down. That's a no-no! It shouldn't crash, but the savestate will probably be corrupted.");
|
||||||
|
|
||||||
if (spu2regs != NULL)
|
if (spu2regs != NULL)
|
||||||
memcpy(spud.unkregs, spu2regs, sizeof(spud.unkregs));
|
memcpy(spud.unkregs, spu2regs, sizeof(spud.unkregs));
|
||||||
if (_spu2mem != NULL)
|
if (_spu2mem != NULL)
|
||||||
memcpy(spud.mem, _spu2mem, sizeof(spud.mem));
|
memcpy(spud.mem, _spu2mem, sizeof(spud.mem));
|
||||||
|
|
||||||
memcpy(spud.Cores, Cores, sizeof(Cores));
|
memcpy(spud.Cores, Cores, sizeof(Cores));
|
||||||
memcpy(&spud.Spdif, &Spdif, sizeof(Spdif));
|
memcpy(&spud.Spdif, &Spdif, sizeof(Spdif));
|
||||||
|
|
||||||
spud.OutPos = OutPos;
|
spud.OutPos = OutPos;
|
||||||
spud.InputPos = InputPos;
|
spud.InputPos = InputPos;
|
||||||
spud.Cycles = Cycles;
|
spud.Cycles = Cycles;
|
||||||
spud.lClocks = lClocks;
|
spud.lClocks = lClocks;
|
||||||
spud.PlayMode = PlayMode;
|
spud.PlayMode = PlayMode;
|
||||||
|
|
||||||
// note: Don't save the cache. PCSX2 doesn't offer a safe method of predicting
|
// 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
|
// 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
|
// "implementation specific" for the intended spec of a savestate. Let's just
|
||||||
// force the user to rebuild their cache instead.
|
// force the user to rebuild their cache instead.
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 __fastcall SPU2Savestate::ThawIt(DataBlock &spud)
|
s32 __fastcall SPU2Savestate::ThawIt(DataBlock& spud)
|
||||||
{
|
{
|
||||||
if (spud.spu2id != SAVE_ID || spud.version < SAVE_VERSION) {
|
if (spud.spu2id != SAVE_ID || spud.version < SAVE_VERSION)
|
||||||
fprintf(stderr, "\n*** SPU2-X Warning:\n");
|
{
|
||||||
if (spud.spu2id == SAVE_ID)
|
fprintf(stderr, "\n*** SPU2-X Warning:\n");
|
||||||
fprintf(stderr, "\tSavestate version is from an older version of PCSX2.\n");
|
if (spud.spu2id == SAVE_ID)
|
||||||
else
|
fprintf(stderr, "\tSavestate version is from an older version of PCSX2.\n");
|
||||||
fprintf(stderr, "\tThe savestate you are trying to load is incorrect or corrupted.\n");
|
else
|
||||||
|
fprintf(stderr, "\tThe savestate you are trying to load is incorrect or corrupted.\n");
|
||||||
|
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"\tAudio may not recover correctly. Save your game to memorycard, reset,\n\n"
|
"\tAudio may not recover correctly. Save your game to memorycard, reset,\n\n"
|
||||||
"\tand then continue from there.\n\n");
|
"\tand then continue from there.\n\n");
|
||||||
|
|
||||||
// Do *not* reset the cores.
|
// Do *not* reset the cores.
|
||||||
// We'll need some "hints" as to how the cores should be initialized, and the
|
// 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
|
// 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).
|
// they kinda match the settings for the savestate (IRQ enables and such).
|
||||||
|
|
||||||
// adpcm cache : Clear all the cache flags and buffers.
|
// adpcm cache : Clear all the cache flags and buffers.
|
||||||
|
|
||||||
wipe_the_cache();
|
wipe_the_cache();
|
||||||
} else {
|
}
|
||||||
SndBuffer::ClearContents();
|
else
|
||||||
|
{
|
||||||
|
SndBuffer::ClearContents();
|
||||||
|
|
||||||
pxAssertMsg(spu2regs && _spu2mem, "Looks like PCSX2 is trying to loadstate while components are shut down. That's a no-no! It shouldn't crash, but the savestate will probably be corrupted.");
|
pxAssertMsg(spu2regs && _spu2mem, "Looks like PCSX2 is trying to loadstate while components are shut down. That's a no-no! It shouldn't crash, but the savestate will probably be corrupted.");
|
||||||
|
|
||||||
// base stuff
|
// base stuff
|
||||||
if (spu2regs)
|
if (spu2regs)
|
||||||
memcpy(spu2regs, spud.unkregs, sizeof(spud.unkregs));
|
memcpy(spu2regs, spud.unkregs, sizeof(spud.unkregs));
|
||||||
if (_spu2mem)
|
if (_spu2mem)
|
||||||
memcpy(_spu2mem, spud.mem, sizeof(spud.mem));
|
memcpy(_spu2mem, spud.mem, sizeof(spud.mem));
|
||||||
|
|
||||||
memcpy(Cores, spud.Cores, sizeof(Cores));
|
memcpy(Cores, spud.Cores, sizeof(Cores));
|
||||||
memcpy(&Spdif, &spud.Spdif, sizeof(Spdif));
|
memcpy(&Spdif, &spud.Spdif, sizeof(Spdif));
|
||||||
|
|
||||||
OutPos = spud.OutPos;
|
OutPos = spud.OutPos;
|
||||||
InputPos = spud.InputPos;
|
InputPos = spud.InputPos;
|
||||||
Cycles = spud.Cycles;
|
Cycles = spud.Cycles;
|
||||||
lClocks = spud.lClocks;
|
lClocks = spud.lClocks;
|
||||||
PlayMode = spud.PlayMode;
|
PlayMode = spud.PlayMode;
|
||||||
|
|
||||||
wipe_the_cache();
|
wipe_the_cache();
|
||||||
|
|
||||||
// Go through the V_Voice structs and recalculate SBuffer pointer from
|
// Go through the V_Voice structs and recalculate SBuffer pointer from
|
||||||
// the NextA setting.
|
// the NextA setting.
|
||||||
|
|
||||||
for (int c = 0; c < 2; c++) {
|
for (int c = 0; c < 2; c++)
|
||||||
for (int v = 0; v < 24; v++) {
|
{
|
||||||
const int cacheIdx = Cores[c].Voices[v].NextA / pcm_WordsPerBlock;
|
for (int v = 0; v < 24; v++)
|
||||||
Cores[c].Voices[v].SBuffer = pcm_cache_data[cacheIdx].Sampledata;
|
{
|
||||||
}
|
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
|
// 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
|
// 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
|
// editions of PCSX2 with fixed addressing, but new PCSX2s have dynamic memory
|
||||||
// addressing).
|
// addressing).
|
||||||
|
|
||||||
Cores[0].DMAPtr = Cores[1].DMAPtr = NULL;
|
Cores[0].DMAPtr = Cores[1].DMAPtr = NULL;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 __fastcall SPU2Savestate::SizeIt()
|
s32 __fastcall SPU2Savestate::SizeIt()
|
||||||
{
|
{
|
||||||
return sizeof(DataBlock);
|
return sizeof(DataBlock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,75 +20,75 @@
|
||||||
#include "Windows.h"
|
#include "Windows.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FILE *s2rfile;
|
FILE* s2rfile;
|
||||||
|
|
||||||
void s2r_write16(s16 data)
|
void s2r_write16(s16 data)
|
||||||
{
|
{
|
||||||
fwrite(&data, 2, 1, s2rfile);
|
fwrite(&data, 2, 1, s2rfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void s2r_write32(u32 data)
|
void s2r_write32(u32 data)
|
||||||
{
|
{
|
||||||
fwrite(&data, 4, 1, s2rfile);
|
fwrite(&data, 4, 1, s2rfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void EMITC(u32 i, u32 a)
|
static void EMITC(u32 i, u32 a)
|
||||||
{
|
{
|
||||||
s2r_write32(((i & 0x7u) << 29u) | (a & 0x1FFFFFFFu));
|
s2r_write32(((i & 0x7u) << 29u) | (a & 0x1FFFFFFFu));
|
||||||
}
|
}
|
||||||
|
|
||||||
int s2r_open(u32 ticks, char *filename)
|
int s2r_open(u32 ticks, char* filename)
|
||||||
{
|
{
|
||||||
s2rfile = fopen(filename, "wb");
|
s2rfile = fopen(filename, "wb");
|
||||||
if (s2rfile)
|
if (s2rfile)
|
||||||
s2r_write32(ticks);
|
s2r_write32(ticks);
|
||||||
return s2rfile ? 0 : -1;
|
return s2rfile ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void s2r_readreg(u32 ticks, u32 addr)
|
void s2r_readreg(u32 ticks, u32 addr)
|
||||||
{
|
{
|
||||||
if (!s2rfile)
|
if (!s2rfile)
|
||||||
return;
|
return;
|
||||||
s2r_write32(ticks);
|
s2r_write32(ticks);
|
||||||
EMITC(0, addr);
|
EMITC(0, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void s2r_writereg(u32 ticks, u32 addr, s16 value)
|
void s2r_writereg(u32 ticks, u32 addr, s16 value)
|
||||||
{
|
{
|
||||||
if (!s2rfile)
|
if (!s2rfile)
|
||||||
return;
|
return;
|
||||||
s2r_write32(ticks);
|
s2r_write32(ticks);
|
||||||
EMITC(1, addr);
|
EMITC(1, addr);
|
||||||
s2r_write16(value);
|
s2r_write16(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void s2r_writedma4(u32 ticks, u16 *data, u32 len)
|
void s2r_writedma4(u32 ticks, u16* data, u32 len)
|
||||||
{
|
{
|
||||||
u32 i;
|
u32 i;
|
||||||
if (!s2rfile)
|
if (!s2rfile)
|
||||||
return;
|
return;
|
||||||
s2r_write32(ticks);
|
s2r_write32(ticks);
|
||||||
EMITC(2, len);
|
EMITC(2, len);
|
||||||
for (i = 0; i < len; i++, data++)
|
for (i = 0; i < len; i++, data++)
|
||||||
s2r_write16(*data);
|
s2r_write16(*data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void s2r_writedma7(u32 ticks, u16 *data, u32 len)
|
void s2r_writedma7(u32 ticks, u16* data, u32 len)
|
||||||
{
|
{
|
||||||
u32 i;
|
u32 i;
|
||||||
if (!s2rfile)
|
if (!s2rfile)
|
||||||
return;
|
return;
|
||||||
s2r_write32(ticks);
|
s2r_write32(ticks);
|
||||||
EMITC(3, len);
|
EMITC(3, len);
|
||||||
for (i = 0; i < len; i++, data++)
|
for (i = 0; i < len; i++, data++)
|
||||||
s2r_write16(*data);
|
s2r_write16(*data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void s2r_close()
|
void s2r_close()
|
||||||
{
|
{
|
||||||
if (!s2rfile)
|
if (!s2rfile)
|
||||||
return;
|
return;
|
||||||
fclose(s2rfile);
|
fclose(s2rfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////
|
||||||
|
@ -112,89 +112,89 @@ bool Running = false;
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
|
||||||
int conprintf(const char *fmt, ...)
|
int conprintf(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
char s[1024];
|
char s[1024];
|
||||||
va_list list;
|
va_list list;
|
||||||
|
|
||||||
va_start(list, fmt);
|
va_start(list, fmt);
|
||||||
vsprintf(s, fmt, list);
|
vsprintf(s, fmt, list);
|
||||||
va_end(list);
|
va_end(list);
|
||||||
|
|
||||||
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
if (handle == INVALID_HANDLE_VALUE)
|
if (handle == INVALID_HANDLE_VALUE)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
DWORD written = 0;
|
DWORD written = 0;
|
||||||
WriteConsoleA(handle, s, strlen(s), &written, 0);
|
WriteConsoleA(handle, s, strlen(s), &written, 0);
|
||||||
FlushFileBuffers(handle);
|
FlushFileBuffers(handle);
|
||||||
|
|
||||||
return written;
|
return written;
|
||||||
#else
|
#else
|
||||||
va_list list;
|
va_list list;
|
||||||
va_start(list, fmt);
|
va_start(list, fmt);
|
||||||
int ret = vsprintf(stderr, fmt, list);
|
int ret = vsprintf(stderr, fmt, list);
|
||||||
va_end(list);
|
va_end(list);
|
||||||
return ret;
|
return ret;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 HighResFrequency()
|
u64 HighResFrequency()
|
||||||
{
|
{
|
||||||
u64 freq;
|
u64 freq;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
|
QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
|
||||||
#else
|
#else
|
||||||
// TODO
|
// TODO
|
||||||
#endif
|
#endif
|
||||||
return freq;
|
return freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 HighResCounter()
|
u64 HighResCounter()
|
||||||
{
|
{
|
||||||
u64 time;
|
u64 time;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
QueryPerformanceCounter((LARGE_INTEGER *)&time);
|
QueryPerformanceCounter((LARGE_INTEGER*)&time);
|
||||||
#else
|
#else
|
||||||
// TODO
|
// TODO
|
||||||
#endif
|
#endif
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitWaitSync() // not extremely accurate but enough.
|
void InitWaitSync() // not extremely accurate but enough.
|
||||||
{
|
{
|
||||||
HighResFreq = HighResFrequency();
|
HighResFreq = HighResFrequency();
|
||||||
HighResPrev = HighResCounter();
|
HighResPrev = HighResCounter();
|
||||||
HighResScale = (double)HighResFreq / (double)IOP_CLK;
|
HighResScale = (double)HighResFreq / (double)IOP_CLK;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 WaitSync(u32 TargetCycle)
|
u32 WaitSync(u32 TargetCycle)
|
||||||
{
|
{
|
||||||
u32 WaitCycles = (TargetCycle - CurrentIOPCycle);
|
u32 WaitCycles = (TargetCycle - CurrentIOPCycle);
|
||||||
u32 WaitTime = WaitCycles / IOPCiclesPerMS;
|
u32 WaitTime = WaitCycles / IOPCiclesPerMS;
|
||||||
if (WaitTime > 10)
|
if (WaitTime > 10)
|
||||||
WaitTime = 10;
|
WaitTime = 10;
|
||||||
if (WaitTime == 0)
|
if (WaitTime == 0)
|
||||||
WaitTime = 1;
|
WaitTime = 1;
|
||||||
SleepEx(WaitTime, TRUE);
|
SleepEx(WaitTime, TRUE);
|
||||||
|
|
||||||
// Refresh current time after sleeping
|
// Refresh current time after sleeping
|
||||||
u64 Current = HighResCounter();
|
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
|
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
|
// Calculate time delta
|
||||||
CurrentIOPCycle += delta;
|
CurrentIOPCycle += delta;
|
||||||
HighResPrev += (u64)floor(delta * HighResScale + 0.5); // Trying to compensate drifting mentioned above, not necessarily useful.
|
HighResPrev += (u64)floor(delta * HighResScale + 0.5); // Trying to compensate drifting mentioned above, not necessarily useful.
|
||||||
|
|
||||||
return delta;
|
return delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
BOOL WINAPI HandlerRoutine(DWORD dwCtrlType)
|
BOOL WINAPI HandlerRoutine(DWORD dwCtrlType)
|
||||||
{
|
{
|
||||||
Running = false;
|
Running = false;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -202,103 +202,108 @@ BOOL WINAPI HandlerRoutine(DWORD dwCtrlType)
|
||||||
EXPORT_C_(void)
|
EXPORT_C_(void)
|
||||||
s2r_replay(HWND hwnd, HINSTANCE hinst, LPSTR filename, int nCmdShow)
|
s2r_replay(HWND hwnd, HINSTANCE hinst, LPSTR filename, int nCmdShow)
|
||||||
{
|
{
|
||||||
int events = 0;
|
int events = 0;
|
||||||
|
|
||||||
Running = true;
|
Running = true;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
AllocConsole();
|
AllocConsole();
|
||||||
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
|
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
|
||||||
|
|
||||||
conprintf("Playing %s file on %x...", filename, hwnd);
|
conprintf("Playing %s file on %x...", filename, hwnd);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// load file
|
// load file
|
||||||
FILE *file = fopen(filename, "rb");
|
FILE* file = fopen(filename, "rb");
|
||||||
|
|
||||||
if (!file) {
|
if (!file)
|
||||||
conprintf("Could not open the replay file.");
|
{
|
||||||
return;
|
conprintf("Could not open the replay file.");
|
||||||
}
|
return;
|
||||||
// if successful, init the plugin
|
}
|
||||||
|
// if successful, init the plugin
|
||||||
|
|
||||||
#define TryRead(dest, size, count, file) \
|
#define TryRead(dest, size, count, file) \
|
||||||
if (fread(dest, size, count, file) < count) { \
|
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 */ \
|
conprintf("Error reading from file."); \
|
||||||
}
|
goto Finish; /* Need to exit the while() loop and maybe also the switch */ \
|
||||||
|
}
|
||||||
|
|
||||||
TryRead(&CurrentIOPCycle, 4, 1, file);
|
TryRead(&CurrentIOPCycle, 4, 1, file);
|
||||||
|
|
||||||
replay_mode = true;
|
replay_mode = true;
|
||||||
|
|
||||||
InitWaitSync(); // Initialize the WaitSync stuff
|
InitWaitSync(); // Initialize the WaitSync stuff
|
||||||
|
|
||||||
SPU2init();
|
SPU2init();
|
||||||
SPU2_dummy_callback = true;
|
SPU2_dummy_callback = true;
|
||||||
SPU2setClockPtr(&CurrentIOPCycle);
|
SPU2setClockPtr(&CurrentIOPCycle);
|
||||||
SPU2open(&hwnd);
|
SPU2open(&hwnd);
|
||||||
|
|
||||||
CurrentIOPCycle = 0;
|
CurrentIOPCycle = 0;
|
||||||
|
|
||||||
SPU2async(0);
|
SPU2async(0);
|
||||||
|
|
||||||
while (!feof(file) && Running) {
|
while (!feof(file) && Running)
|
||||||
u32 ccycle = 0;
|
{
|
||||||
u32 evid = 0;
|
u32 ccycle = 0;
|
||||||
u32 sval = 0;
|
u32 evid = 0;
|
||||||
u32 tval = 0;
|
u32 sval = 0;
|
||||||
|
u32 tval = 0;
|
||||||
|
|
||||||
TryRead(&ccycle, 4, 1, file);
|
TryRead(&ccycle, 4, 1, file);
|
||||||
TryRead(&sval, 4, 1, file);
|
TryRead(&sval, 4, 1, file);
|
||||||
|
|
||||||
evid = sval >> 29;
|
evid = sval >> 29;
|
||||||
sval &= 0x1FFFFFFF;
|
sval &= 0x1FFFFFFF;
|
||||||
|
|
||||||
u32 TargetCycle = ccycle * 768;
|
u32 TargetCycle = ccycle * 768;
|
||||||
|
|
||||||
while (TargetCycle > CurrentIOPCycle) {
|
while (TargetCycle > CurrentIOPCycle)
|
||||||
u32 delta = WaitSync(TargetCycle);
|
{
|
||||||
SPU2async(delta);
|
u32 delta = WaitSync(TargetCycle);
|
||||||
}
|
SPU2async(delta);
|
||||||
|
}
|
||||||
|
|
||||||
switch (evid) {
|
switch (evid)
|
||||||
case 0:
|
{
|
||||||
SPU2read(sval);
|
case 0:
|
||||||
break;
|
SPU2read(sval);
|
||||||
case 1:
|
break;
|
||||||
TryRead(&tval, 2, 1, file);
|
case 1:
|
||||||
SPU2write(sval, tval);
|
TryRead(&tval, 2, 1, file);
|
||||||
break;
|
SPU2write(sval, tval);
|
||||||
case 2:
|
break;
|
||||||
TryRead(dmabuffer, sval, 2, file);
|
case 2:
|
||||||
SPU2writeDMA4Mem(dmabuffer, sval);
|
TryRead(dmabuffer, sval, 2, file);
|
||||||
break;
|
SPU2writeDMA4Mem(dmabuffer, sval);
|
||||||
case 3:
|
break;
|
||||||
TryRead(dmabuffer, sval, 2, file);
|
case 3:
|
||||||
SPU2writeDMA7Mem(dmabuffer, sval);
|
TryRead(dmabuffer, sval, 2, file);
|
||||||
break;
|
SPU2writeDMA7Mem(dmabuffer, sval);
|
||||||
default:
|
break;
|
||||||
// not implemented
|
default:
|
||||||
return;
|
// not implemented
|
||||||
break;
|
return;
|
||||||
}
|
break;
|
||||||
events++;
|
}
|
||||||
}
|
events++;
|
||||||
|
}
|
||||||
|
|
||||||
Finish:
|
Finish:
|
||||||
|
|
||||||
//shutdown
|
//shutdown
|
||||||
SPU2close();
|
SPU2close();
|
||||||
SPU2shutdown();
|
SPU2shutdown();
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
conprintf("Finished playing %s file (%d cycles, %d events).", filename, CurrentIOPCycle, events);
|
conprintf("Finished playing %s file (%d cycles, %d events).", filename, CurrentIOPCycle, events);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
FreeConsole();
|
FreeConsole();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
replay_mode = false;
|
replay_mode = false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
//#define S2R_ENABLE
|
//#define S2R_ENABLE
|
||||||
|
|
||||||
// s2r dumping
|
// s2r dumping
|
||||||
int s2r_open(u32 ticks, char *filename);
|
int s2r_open(u32 ticks, char* filename);
|
||||||
void s2r_readreg(u32 ticks, u32 addr);
|
void s2r_readreg(u32 ticks, u32 addr);
|
||||||
void s2r_writereg(u32 ticks, u32 addr, s16 value);
|
void s2r_writereg(u32 ticks, u32 addr, s16 value);
|
||||||
void s2r_writedma4(u32 ticks, u16 *data, u32 len);
|
void s2r_writedma4(u32 ticks, u16* data, u32 len);
|
||||||
void s2r_writedma7(u32 ticks, u16 *data, u32 len);
|
void s2r_writedma7(u32 ticks, u16* data, u32 len);
|
||||||
void s2r_close();
|
void s2r_close();
|
||||||
|
|
||||||
extern bool replay_mode;
|
extern bool replay_mode;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -116,11 +116,11 @@ SyncTab::SyncTab(wxWindow* parent)
|
||||||
|
|
||||||
auto* adv_box = new wxStaticBoxSizer(wxVERTICAL, this, "Advanced");
|
auto* adv_box = new wxStaticBoxSizer(wxVERTICAL, this, "Advanced");
|
||||||
|
|
||||||
auto* babble_label = new wxStaticText(this, wxID_ANY, \
|
auto* babble_label = new wxStaticText(this, wxID_ANY,
|
||||||
"For fine-tuning time stretching.\n"\
|
"For fine-tuning time stretching.\n"
|
||||||
"Larger is better for slowdown, && smaller for speedup (60+ fps).\n"\
|
"Larger is better for slowdown, && smaller for speedup (60+ fps).\n"
|
||||||
"All options in microseconds.",\
|
"All options in microseconds.",
|
||||||
wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL);
|
wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL);
|
||||||
babble_label->Wrap(300);
|
babble_label->Wrap(300);
|
||||||
|
|
||||||
adv_box->Add(babble_label, wxSizerFlags().Centre());
|
adv_box->Add(babble_label, wxSizerFlags().Centre());
|
||||||
|
|
Loading…
Reference in New Issue