SPU: More implementation work

This commit is contained in:
Connor McLaughlin 2019-10-11 13:24:48 +10:00
parent 135e282f8d
commit 3912e0e8d6
3 changed files with 146 additions and 50 deletions

View File

@ -14,6 +14,18 @@ static s16 Clamp16(s32 value)
return static_cast<s16>(std::clamp<s32>(value, -32768, 32767)); return static_cast<s16>(std::clamp<s32>(value, -32768, 32767));
} }
static constexpr float S16ToFloat(s16 value)
{
return (value >= 0) ? (static_cast<float>(value) / static_cast<float>(std::numeric_limits<s16>::max())) :
(static_cast<float>(value) / -static_cast<float>(std::numeric_limits<s16>::min()));
}
static constexpr s16 FloatToS16(float value)
{
return (value >= 0.0f) ? (static_cast<s16>(value * static_cast<float>(std::numeric_limits<s16>::max()))) :
(static_cast<s16>(value * -static_cast<float>(std::numeric_limits<s16>::min())));
}
SPU::SPU() = default; SPU::SPU() = default;
SPU::~SPU() = default; SPU::~SPU() = default;
@ -51,6 +63,30 @@ u16 SPU::ReadRegister(u32 offset)
switch (offset) switch (offset)
{ {
case 0x1F801D80 - SPU_BASE:
return m_main_volume_left.bits;
case 0x1F801D82 - SPU_BASE:
return m_main_volume_right.bits;
case 0x1F801D88 - SPU_BASE:
return Truncate16(m_key_on_register);
case 0x1F801D8A - SPU_BASE:
return Truncate16(m_key_on_register >> 16);
case 0x1F801D8C - SPU_BASE:
return Truncate16(m_key_off_register);
case 0x1F801D8E - SPU_BASE:
return Truncate16(m_key_off_register >> 16);
case 0x1F801D98 - SPU_BASE:
return Truncate16(m_reverb_on_register);
case 0x1F801D9A - SPU_BASE:
return Truncate16(m_reverb_on_register >> 16);
case 0x1F801DA6 - SPU_BASE: case 0x1F801DA6 - SPU_BASE:
Log_DebugPrintf("SPU transfer address register -> 0x%04X", ZeroExtend32(m_transfer_address_reg)); Log_DebugPrintf("SPU transfer address register -> 0x%04X", ZeroExtend32(m_transfer_address_reg));
return m_transfer_address_reg; return m_transfer_address_reg;
@ -67,18 +103,6 @@ u16 SPU::ReadRegister(u32 offset)
// Log_DebugPrintf("SPU status register -> 0x%04X", ZeroExtend32(m_SPUCNT.bits)); // Log_DebugPrintf("SPU status register -> 0x%04X", ZeroExtend32(m_SPUCNT.bits));
return m_SPUSTAT.bits; return m_SPUSTAT.bits;
case 0x1F801D88 - SPU_BASE:
return Truncate16(m_key_on_register);
case 0x1F801D8A - SPU_BASE:
return Truncate16(m_key_on_register >> 16);
case 0x1F801D8C - SPU_BASE:
return Truncate16(m_key_off_register);
case 0x1F801D8E - SPU_BASE:
return Truncate16(m_key_off_register >> 16);
default: default:
Log_ErrorPrintf("Unknown SPU register read: offset 0x%X (address 0x%08X)", offset, offset | SPU_BASE); Log_ErrorPrintf("Unknown SPU register read: offset 0x%X (address 0x%08X)", offset, offset | SPU_BASE);
return UINT16_C(0xFFFF); return UINT16_C(0xFFFF);
@ -119,6 +143,22 @@ void SPU::WriteRegister(u32 offset, u16 value)
return; return;
} }
case 0x1F801D80 - SPU_BASE:
{
Log_DebugPrintf("SPU main volume left <- 0x%04X", ZeroExtend32(value));
m_system->Synchronize();
m_main_volume_left.bits = value;
return;
}
case 0x1F801D82 - SPU_BASE:
{
Log_DebugPrintf("SPU main volume right <- 0x%04X", ZeroExtend32(value));
m_system->Synchronize();
m_main_volume_right.bits = value;
return;
}
case 0x1F801D88 - SPU_BASE: case 0x1F801D88 - SPU_BASE:
{ {
Log_DebugPrintf("SPU key on low <- 0x%04X", ZeroExtend32(value)); Log_DebugPrintf("SPU key on low <- 0x%04X", ZeroExtend32(value));
@ -193,6 +233,22 @@ void SPU::WriteRegister(u32 offset, u16 value)
bits >>= 1; bits >>= 1;
} }
} }
break;
case 0x1F801D98 - SPU_BASE:
{
Log_DebugPrintf("SPU reverb on register <- 0x%04X", ZeroExtend32(value));
m_system->Synchronize();
m_reverb_on_register = (m_reverb_on_register & 0xFFFF0000) | ZeroExtend32(value);
}
break;
case 0x1F801D9A - SPU_BASE:
{
Log_DebugPrintf("SPU reverb off register <- 0x%04X", ZeroExtend32(value));
m_system->Synchronize();
m_reverb_on_register = (m_reverb_on_register & 0x0000FFFF) | (ZeroExtend32(value) << 16);
}
break; break;
// read-only registers // read-only registers
@ -212,8 +268,8 @@ void SPU::WriteRegister(u32 offset, u16 value)
u16 SPU::ReadVoiceRegister(u32 offset) u16 SPU::ReadVoiceRegister(u32 offset)
{ {
const u32 reg_index = (offset & 0x0F) / 2; const u32 reg_index = (offset % 0x10) / 2; //(offset & 0x0F) / 2;
const u32 voice_index = ((offset >> 4) & 0x1F); const u32 voice_index = (offset / 0x10); //((offset >> 4) & 0x1F);
Assert(voice_index < 24); Assert(voice_index < 24);
return m_voices[voice_index].regs.index[reg_index]; return m_voices[voice_index].regs.index[reg_index];
@ -222,65 +278,69 @@ u16 SPU::ReadVoiceRegister(u32 offset)
void SPU::WriteVoiceRegister(u32 offset, u16 value) void SPU::WriteVoiceRegister(u32 offset, u16 value)
{ {
// per-voice registers // per-voice registers
const u32 reg_index = (offset & 0x0F); const u32 reg_index = (offset % 0x10);
const u32 voice_index = ((offset >> 4) & 0x1F); const u32 voice_index = (offset / 0x10);
Assert(voice_index < 24); Assert(voice_index < 24);
Voice& voice = m_voices[voice_index];
if (voice.key_on)
m_system->Synchronize();
switch (reg_index) switch (reg_index)
{ {
case 0x00: // volume left case 0x00: // volume left
{ {
Log_DebugPrintf("SPU voice %u volume left <- 0x%04X", voice_index, value); Log_DebugPrintf("SPU voice %u volume left <- 0x%04X", voice_index, value);
m_voices[voice_index].regs.volume_left.bits = value; voice.regs.volume_left.bits = value;
} }
break; break;
case 0x02: // volume right case 0x02: // volume right
{ {
Log_DebugPrintf("SPU voice %u volume right <- 0x%04X", voice_index, value); Log_DebugPrintf("SPU voice %u volume right <- 0x%04X", voice_index, value);
m_voices[voice_index].regs.volume_right.bits = value; voice.regs.volume_right.bits = value;
} }
break; break;
case 0x04: // sample rate case 0x04: // sample rate
{ {
Log_DebugPrintf("SPU voice %u ADPCM sample rate <- 0x%04X", voice_index, value); Log_DebugPrintf("SPU voice %u ADPCM sample rate <- 0x%04X", voice_index, value);
m_voices[voice_index].regs.adpcm_sample_rate = value; voice.regs.adpcm_sample_rate = value;
} }
break; break;
case 0x06: // start address case 0x06: // start address
{ {
Log_DebugPrintf("SPU voice %u ADPCM start address <- 0x%04X", voice_index, value); Log_DebugPrintf("SPU voice %u ADPCM start address <- 0x%04X", voice_index, value);
m_voices[voice_index].regs.adpcm_start_address = value; voice.regs.adpcm_start_address = value;
} }
break; break;
case 0x08: // adsr low case 0x08: // adsr low
{ {
Log_WarningPrintf("SPU voice %u ADSR low <- 0x%04X", voice_index, value); Log_WarningPrintf("SPU voice %u ADSR low <- 0x%04X", voice_index, value);
m_voices[voice_index].regs.adsr.bits_low = value; voice.regs.adsr.bits_low = value;
} }
break; break;
case 0x0A: // adsr high case 0x0A: // adsr high
{ {
Log_WarningPrintf("SPU voice %u ADSR high <- 0x%04X", voice_index, value); Log_WarningPrintf("SPU voice %u ADSR high <- 0x%04X", voice_index, value);
m_voices[voice_index].regs.adsr.bits_high = value; voice.regs.adsr.bits_high = value;
} }
break; break;
case 0x0C: // adsr volume case 0x0C: // adsr volume
{ {
Log_DebugPrintf("SPU voice %u ADSR volume <- 0x%04X", voice_index, value); Log_DebugPrintf("SPU voice %u ADSR volume <- 0x%04X", voice_index, value);
m_voices[voice_index].regs.adsr_volume = value; voice.regs.adsr_volume = value;
} }
break; break;
case 0x0E: // repeat address case 0x0E: // repeat address
{ {
Log_DebugPrintf("SPU voice %u ADPCM repeat address <- 0x%04X", voice_index, value); Log_DebugPrintf("SPU voice %u ADPCM repeat address <- 0x%04X", voice_index, value);
m_voices[voice_index].regs.adpcm_repeat_address = value; voice.regs.adpcm_repeat_address = value;
} }
break; break;
@ -373,7 +433,7 @@ SPU::SampleFormat SPU::Voice::SampleBlock(s32 index) const
return current_block_samples[index]; return current_block_samples[index];
} }
s32 SPU::Voice::Interpolate() const s16 SPU::Voice::Interpolate() const
{ {
static constexpr std::array<s32, 0x200> gauss = {{ static constexpr std::array<s32, 0x200> gauss = {{
-0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, // -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, //
@ -445,10 +505,10 @@ s32 SPU::Voice::Interpolate() const
const u8 i = counter.interpolation_index; const u8 i = counter.interpolation_index;
const s32 s = static_cast<s32>(ZeroExtend32(counter.sample_index.GetValue())); const s32 s = static_cast<s32>(ZeroExtend32(counter.sample_index.GetValue()));
s32 out = gauss[0x0FF - i] * s32(SampleBlock(s - 3)); s16 out = s16(gauss[0x0FF - i] * s32(SampleBlock(s - 3)) >> 15);
out += gauss[0x1FF - i] * s32(SampleBlock(s - 2)); out += s16(gauss[0x1FF - i] * s32(SampleBlock(s - 2)) >> 15);
out += gauss[0x100 + i] * s32(SampleBlock(s - 1)); out += s16(gauss[0x100 + i] * s32(SampleBlock(s - 1)) >> 15);
out += gauss[0x000 + i] * s32(SampleBlock(s - 0)); out += s16(gauss[0x000 + i] * s32(SampleBlock(s - 0)) >> 15);
return out; return out;
} }
@ -491,8 +551,8 @@ void SPU::DecodeADPCMBlock(const ADPCMBlock& block, SampleFormat out_samples[NUM
static constexpr std::array<s32, 5> filter_table_neg = {{0, 0, -52, -55, -60}}; static constexpr std::array<s32, 5> filter_table_neg = {{0, 0, -52, -55, -60}};
// pre-lookup // pre-lookup
const u8 shift = block.shift_filter.shift; const u8 shift = block.GetShift();
const u8 filter_index = std::min<u8>(block.shift_filter.filter, 4); const u8 filter_index = block.GetFilter();
const s32 filter_pos = filter_table_pos[filter_index]; const s32 filter_pos = filter_table_pos[filter_index];
const s32 filter_neg = filter_table_neg[filter_index]; const s32 filter_neg = filter_table_neg[filter_index];
s32 last_samples[2] = {state[0], state[1]}; s32 last_samples[2] = {state[0], state[1]};
@ -500,18 +560,16 @@ void SPU::DecodeADPCMBlock(const ADPCMBlock& block, SampleFormat out_samples[NUM
// samples // samples
for (u32 i = 0; i < NUM_SAMPLES_PER_ADPCM_BLOCK; i++) for (u32 i = 0; i < NUM_SAMPLES_PER_ADPCM_BLOCK; i++)
{ {
const u8 nibble = (block.data[i / 2] >> (4 * (i % 2))) & 0x0F; // extend 4-bit to 16-bit, apply shift from header and mix in previous samples
s32 sample = SignExtendN<4, s32>(static_cast<s32>(ZeroExtend32(nibble))); const s16 sample = static_cast<s16>(ZeroExtend16(block.GetNibble(i)) << 12) >> shift;
sample >>= shift; const s32 interp_sample = s32(sample) + ((last_samples[0] * filter_pos) + (last_samples[1] * filter_neg) + 32) / 64;
sample += ((last_samples[0] * filter_pos) + (last_samples[1] * filter_neg) + 32) / 64; out_samples[i] = Clamp16(interp_sample);
last_samples[1] = last_samples[0];
out_samples[i] = last_samples[0] = interp_sample;
static_cast<s16>(std::clamp<s32>(sample, std::numeric_limits<s16>::min(), std::numeric_limits<s16>::max()));
state[1] = state[0];
state[0] = sample;
} }
std::copy_n(last_samples, countof(last_samples), state);
} }
std::tuple<SPU::SampleFormat, SPU::SampleFormat> SPU::SampleVoice(u32 voice_index) std::tuple<SPU::SampleFormat, SPU::SampleFormat> SPU::SampleVoice(u32 voice_index)
@ -566,8 +624,20 @@ std::tuple<SPU::SampleFormat, SPU::SampleFormat> SPU::SampleVoice(u32 voice_inde
} }
// TODO: Volume // TODO: Volume
s32 sample = voice.Interpolate(); const s32 sample = voice.Interpolate();
return std::make_tuple(Clamp16(sample), Clamp16(sample)); // s32 sample = voice.SampleBlock(voice.counter.sample_index);
const s16 sample16 = Clamp16(sample);
const float samplef = S16ToFloat(sample16);
// apply volume
const float volume_left = S16ToFloat(voice.regs.volume_left.GetVolume());
const float volume_right = S16ToFloat(voice.regs.volume_right.GetVolume());
const float final_left = volume_left * samplef;
const float final_right = volume_right * samplef;
return std::make_tuple(FloatToS16(final_left), FloatToS16(final_right));
// return std::make_tuple(FloatToS16(samplef), FloatToS16(samplef));
// return std::make_tuple(sample16, sample16);
} }
void SPU::GenerateSample() void SPU::GenerateSample()
@ -581,9 +651,20 @@ void SPU::GenerateSample()
right_sum += right; right_sum += right;
} }
Log_DebugPrintf("SPU sample %d %d", left_sum, right_sum); // Log_DebugPrintf("SPU sample %d %d", left_sum, right_sum);
AudioStream::SampleType samples[2] = {Clamp16(left_sum), Clamp16(right_sum)}; AudioStream::SampleType samples[2] = {Clamp16(left_sum), Clamp16(right_sum)};
m_audio_stream->WriteSamples(samples, countof(samples)); m_audio_stream->WriteSamples(samples, 1);
#if 0
static FILE* fp = nullptr;
if (!fp)
fp = std::fopen("D:\\spu.raw", "wb");
if (fp)
{
std::fwrite(samples, sizeof(AudioStream::SampleType), 2, fp);
std::fflush(fp);
}
#endif
} }
void SPU::DrawDebugWindow() void SPU::DrawDebugWindow()

View File

@ -115,13 +115,15 @@ private:
u16 bits; u16 bits;
BitField<u16, bool, 15, 1> sweep_mode; BitField<u16, bool, 15, 1> sweep_mode;
BitField<u16, u16, 0, 15> fixed_volume; // divided by 2 BitField<u16, s16, 0, 15> fixed_volume; // divided by 2
BitField<u16, bool, 14, 1> sweep_exponential; BitField<u16, bool, 14, 1> sweep_exponential;
BitField<u16, bool, 13, 1> sweep_direction_decrease; BitField<u16, bool, 13, 1> sweep_direction_decrease;
BitField<u16, bool, 12, 1> sweep_phase_negative; BitField<u16, bool, 12, 1> sweep_phase_negative;
BitField<u16, u8, 2, 5> sweep_shift; BitField<u16, u8, 2, 5> sweep_shift;
BitField<u16, u8, 0, 2> sweep_step; BitField<u16, u8, 0, 2> sweep_step;
s16 GetVolume() { return fixed_volume * 2; }
}; };
// organized so we can replace this with a u16 array in the future // organized so we can replace this with a u16 array in the future
@ -173,7 +175,16 @@ private:
u8 data[NUM_SAMPLES_PER_ADPCM_BLOCK / 2]; u8 data[NUM_SAMPLES_PER_ADPCM_BLOCK / 2];
u8 ReadSample(u32 index) const { return (data[index / 2] >> ((index % 2) * 4)) & 0x0F; } // For both 4bit and 8bit ADPCM, reserved shift values 13..15 will act same as shift=9).
u8 GetShift() const
{
const u8 shift = shift_filter.shift;
return (shift > 12) ? 9 : shift;
}
u8 GetFilter() const { return std::min<u8>(shift_filter.filter, 4); }
u8 GetNibble(u32 index) const { return (data[index / 2] >> ((index % 2) * 4)) & 0x0F; }
}; };
struct Voice struct Voice
@ -181,7 +192,7 @@ private:
u16 current_address; u16 current_address;
VoiceRegisters regs; VoiceRegisters regs;
VoiceCounter counter; VoiceCounter counter;
ADPCMBlock current_block; // TODO Drop this after decoding ADPCMBlock current_block; // TODO Drop this after decoding
std::array<SampleFormat, NUM_SAMPLES_PER_ADPCM_BLOCK> current_block_samples; std::array<SampleFormat, NUM_SAMPLES_PER_ADPCM_BLOCK> current_block_samples;
std::array<SampleFormat, 3> previous_block_last_samples; std::array<SampleFormat, 3> previous_block_last_samples;
std::array<s32, 2> adpcm_state; std::array<s32, 2> adpcm_state;
@ -194,7 +205,7 @@ private:
void DecodeBlock(); void DecodeBlock();
SampleFormat SampleBlock(s32 index) const; SampleFormat SampleBlock(s32 index) const;
s32 Interpolate() const; s16 Interpolate() const;
}; };
u16 ReadVoiceRegister(u32 offset); u16 ReadVoiceRegister(u32 offset);
@ -223,9 +234,13 @@ private:
u16 m_irq_address = 0; u16 m_irq_address = 0;
VolumeRegister m_main_volume_left = {};
VolumeRegister m_main_volume_right = {};
u32 m_key_on_register = 0; u32 m_key_on_register = 0;
u32 m_key_off_register = 0; u32 m_key_off_register = 0;
u32 m_endx_register = 0; u32 m_endx_register = 0;
u32 m_reverb_on_register = 0;
TickCount m_ticks_carry = 0; TickCount m_ticks_carry = 0;

View File

@ -96,7 +96,7 @@ int main(int argc, char* argv[])
#else #else
g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG);
// g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL SPU Pad DigitalController", LOGLEVEL_DEBUG); // g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL SPU Pad DigitalController", LOGLEVEL_DEBUG);
// g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL SPU Pad DigitalController InterruptController", LOGLEVEL_DEBUG); g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController InterruptController", LOGLEVEL_DEBUG);
// g_pLog->SetFilterLevel(LOGLEVEL_TRACE); // g_pLog->SetFilterLevel(LOGLEVEL_TRACE);
g_pLog->SetFilterLevel(LOGLEVEL_DEBUG); g_pLog->SetFilterLevel(LOGLEVEL_DEBUG);
// g_pLog->SetFilterLevel(LOGLEVEL_DEV); // g_pLog->SetFilterLevel(LOGLEVEL_DEV);