mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-31 03:45:38 +00:00
SPU: Implement pitch modulation
This commit is contained in:
parent
5a84122862
commit
8722757412
|
@ -44,6 +44,7 @@ void SPU::Reset()
|
||||||
m_endx_register = 0;
|
m_endx_register = 0;
|
||||||
m_reverb_on_register = 0;
|
m_reverb_on_register = 0;
|
||||||
m_noise_mode_register = 0;
|
m_noise_mode_register = 0;
|
||||||
|
m_pitch_modulation_enable_register = 0;
|
||||||
m_ticks_carry = 0;
|
m_ticks_carry = 0;
|
||||||
|
|
||||||
for (u32 i = 0; i < NUM_VOICES; i++)
|
for (u32 i = 0; i < NUM_VOICES; i++)
|
||||||
|
@ -94,6 +95,7 @@ bool SPU::DoState(StateWrapper& sw)
|
||||||
sw.Do(&v.current_block_samples);
|
sw.Do(&v.current_block_samples);
|
||||||
sw.Do(&v.previous_block_last_samples);
|
sw.Do(&v.previous_block_last_samples);
|
||||||
sw.Do(&v.adpcm_last_samples);
|
sw.Do(&v.adpcm_last_samples);
|
||||||
|
sw.Do(&v.last_amplitude);
|
||||||
sw.Do(&v.adsr_phase);
|
sw.Do(&v.adsr_phase);
|
||||||
sw.DoPOD(&v.adsr_target);
|
sw.DoPOD(&v.adsr_target);
|
||||||
sw.Do(&v.adsr_ticks);
|
sw.Do(&v.adsr_ticks);
|
||||||
|
@ -135,6 +137,12 @@ u16 SPU::ReadRegister(u32 offset)
|
||||||
case 0x1F801D8E - SPU_BASE:
|
case 0x1F801D8E - SPU_BASE:
|
||||||
return Truncate16(m_key_off_register >> 16);
|
return Truncate16(m_key_off_register >> 16);
|
||||||
|
|
||||||
|
case 0x1F801D90 - SPU_BASE:
|
||||||
|
return Truncate16(m_pitch_modulation_enable_register);
|
||||||
|
|
||||||
|
case 0x1F801D92 - SPU_BASE:
|
||||||
|
return Truncate16(m_pitch_modulation_enable_register >> 16);
|
||||||
|
|
||||||
case 0x1F801D94 - SPU_BASE:
|
case 0x1F801D94 - SPU_BASE:
|
||||||
return Truncate16(m_noise_mode_register);
|
return Truncate16(m_noise_mode_register);
|
||||||
|
|
||||||
|
@ -281,6 +289,23 @@ void SPU::WriteRegister(u32 offset, u16 value)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 0x1F801D90 - SPU_BASE:
|
||||||
|
{
|
||||||
|
m_system->Synchronize();
|
||||||
|
m_pitch_modulation_enable_register = (m_pitch_modulation_enable_register & 0xFFFF0000) | ZeroExtend32(value);
|
||||||
|
Log_DebugPrintf("SPU pitch modulation enable register <- 0x%08X", m_pitch_modulation_enable_register);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x1F801D92 - SPU_BASE:
|
||||||
|
{
|
||||||
|
m_system->Synchronize();
|
||||||
|
m_pitch_modulation_enable_register =
|
||||||
|
(m_pitch_modulation_enable_register & 0x0000FFFF) | (ZeroExtend32(value) << 16);
|
||||||
|
Log_DebugPrintf("SPU pitch modulation enable register <- 0x%08X", m_pitch_modulation_enable_register);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 0x1F801D94 - SPU_BASE:
|
case 0x1F801D94 - SPU_BASE:
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("SPU noise mode register <- 0x%04X", ZeroExtend32(value));
|
Log_DebugPrintf("SPU noise mode register <- 0x%04X", ZeroExtend32(value));
|
||||||
|
@ -817,7 +842,10 @@ std::tuple<s32, s32> SPU::SampleVoice(u32 voice_index)
|
||||||
{
|
{
|
||||||
Voice& voice = m_voices[voice_index];
|
Voice& voice = m_voices[voice_index];
|
||||||
if (!voice.IsOn())
|
if (!voice.IsOn())
|
||||||
|
{
|
||||||
|
voice.last_amplitude = 0;
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
if (!voice.has_samples)
|
if (!voice.has_samples)
|
||||||
{
|
{
|
||||||
|
@ -834,11 +862,17 @@ std::tuple<s32, s32> SPU::SampleVoice(u32 voice_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
// interpolate/sample and apply ADSR volume
|
// interpolate/sample and apply ADSR volume
|
||||||
const s32 sample = ApplyVolume(voice.Interpolate(), voice.regs.adsr_volume);
|
const s32 amplitude = ApplyVolume(voice.Interpolate(), voice.regs.adsr_volume);
|
||||||
|
voice.last_amplitude = amplitude;
|
||||||
voice.TickADSR();
|
voice.TickADSR();
|
||||||
|
|
||||||
// TODO: Pulse modulation
|
// Pitch modulation
|
||||||
u16 step = voice.regs.adpcm_sample_rate;
|
u16 step = voice.regs.adpcm_sample_rate;
|
||||||
|
if (IsPitchModulationEnabled(voice_index))
|
||||||
|
{
|
||||||
|
const u32 factor = u32(std::clamp<s32>(m_voices[voice_index - 1].last_amplitude, -0x8000, 0x7FFF) + 0x8000);
|
||||||
|
step = Truncate16(step * factor) >> 15;
|
||||||
|
}
|
||||||
step = std::min<u16>(step, 0x4000);
|
step = std::min<u16>(step, 0x4000);
|
||||||
voice.counter.bits += step;
|
voice.counter.bits += step;
|
||||||
|
|
||||||
|
@ -870,8 +904,8 @@ std::tuple<s32, s32> SPU::SampleVoice(u32 voice_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply per-channel volume
|
// apply per-channel volume
|
||||||
const s32 left = ApplyVolume(sample, voice.regs.volume_left.GetVolume());
|
const s32 left = ApplyVolume(amplitude, voice.regs.volume_left.GetVolume());
|
||||||
const s32 right = ApplyVolume(sample, voice.regs.volume_right.GetVolume());
|
const s32 right = ApplyVolume(amplitude, voice.regs.volume_right.GetVolume());
|
||||||
return std::make_tuple(left, right);
|
return std::make_tuple(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1071,6 +1105,15 @@ void SPU::DrawDebugStateWindow()
|
||||||
|
|
||||||
ImGui::TextColored(m_SPUCNT.external_audio_reverb ? active_color : inactive_color, "External Audio Enable: %s",
|
ImGui::TextColored(m_SPUCNT.external_audio_reverb ? active_color : inactive_color, "External Audio Enable: %s",
|
||||||
m_SPUCNT.external_audio_reverb ? "Yes" : "No");
|
m_SPUCNT.external_audio_reverb ? "Yes" : "No");
|
||||||
|
|
||||||
|
ImGui::Text("Pitch Modulation: ");
|
||||||
|
for (u32 i = 1; i < NUM_VOICES; i++)
|
||||||
|
{
|
||||||
|
ImGui::SameLine(0.0f, 16.0f);
|
||||||
|
|
||||||
|
const bool active = IsPitchModulationEnabled(i);
|
||||||
|
ImGui::TextColored(active ? active_color : inactive_color, "%u", i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
|
@ -222,6 +222,7 @@ private:
|
||||||
std::array<s16, NUM_SAMPLES_PER_ADPCM_BLOCK> current_block_samples;
|
std::array<s16, NUM_SAMPLES_PER_ADPCM_BLOCK> current_block_samples;
|
||||||
std::array<s16, 3> previous_block_last_samples;
|
std::array<s16, 3> previous_block_last_samples;
|
||||||
std::array<s32, 2> adpcm_last_samples;
|
std::array<s32, 2> adpcm_last_samples;
|
||||||
|
s32 last_amplitude;
|
||||||
|
|
||||||
ADSRPhase adsr_phase;
|
ADSRPhase adsr_phase;
|
||||||
ADSRTarget adsr_target;
|
ADSRTarget adsr_target;
|
||||||
|
@ -255,7 +256,11 @@ private:
|
||||||
|
|
||||||
static ADSRPhase GetNextADSRPhase(ADSRPhase phase);
|
static ADSRPhase GetNextADSRPhase(ADSRPhase phase);
|
||||||
|
|
||||||
bool IsVoiceReverbEnabled(u32 i) const { return ConvertToBoolUnchecked((m_reverb_on_register >> i) & u32(1)); }
|
bool IsVoiceReverbEnabled(u32 i) const { return (m_reverb_on_register & (u32(1) << i)) != 0; }
|
||||||
|
bool IsPitchModulationEnabled(u32 i) const
|
||||||
|
{
|
||||||
|
return (i > 0 && ((m_pitch_modulation_enable_register & (u32(1) << i)) != 0));
|
||||||
|
}
|
||||||
|
|
||||||
u16 ReadVoiceRegister(u32 offset);
|
u16 ReadVoiceRegister(u32 offset);
|
||||||
void WriteVoiceRegister(u32 offset, u16 value);
|
void WriteVoiceRegister(u32 offset, u16 value);
|
||||||
|
@ -292,6 +297,7 @@ private:
|
||||||
u32 m_endx_register = 0;
|
u32 m_endx_register = 0;
|
||||||
u32 m_reverb_on_register = 0;
|
u32 m_reverb_on_register = 0;
|
||||||
u32 m_noise_mode_register = 0;
|
u32 m_noise_mode_register = 0;
|
||||||
|
u32 m_pitch_modulation_enable_register = 0;
|
||||||
|
|
||||||
TickCount m_ticks_carry = 0;
|
TickCount m_ticks_carry = 0;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue