SPU: Add a tick counter and delay between key on/off writes

Not yet tested on hardware. We believe that writing to a specific
voice's key on/off register within 2 samples/ticks of the first
write will result in the second write being ignored. This still
needs to be tested on hardware for confirmation.

Thanks to @PSI-Rockin for the idea.

Fixes sound effects in Monkey Hero.
This commit is contained in:
Connor McLaughlin 2020-03-14 15:24:23 +10:00
parent d42f155512
commit 6a6aa72f3a
3 changed files with 38 additions and 1 deletions

View file

@ -2,4 +2,4 @@
#include "types.h" #include "types.h"
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
static constexpr u32 SAVE_STATE_VERSION = 6; static constexpr u32 SAVE_STATE_VERSION = 7;

View file

@ -30,6 +30,8 @@ void SPU::Initialize(System* system, DMA* dma, InterruptController* interrupt_co
void SPU::Reset() void SPU::Reset()
{ {
m_tick_counter = 0;
m_SPUCNT.bits = 0; m_SPUCNT.bits = 0;
m_SPUSTAT.bits = 0; m_SPUSTAT.bits = 0;
m_transfer_address = 0; m_transfer_address = 0;
@ -67,12 +69,15 @@ void SPU::Reset()
v.has_samples = false; v.has_samples = false;
} }
m_voice_key_on_off_delay.fill(0);
m_ram.fill(0); m_ram.fill(0);
UpdateEventInterval(); UpdateEventInterval();
} }
bool SPU::DoState(StateWrapper& sw) bool SPU::DoState(StateWrapper& sw)
{ {
sw.Do(&m_tick_counter);
sw.Do(&m_SPUCNT.bits); sw.Do(&m_SPUCNT.bits);
sw.Do(&m_SPUSTAT.bits); sw.Do(&m_SPUSTAT.bits);
sw.Do(&m_transfer_control.bits); sw.Do(&m_transfer_control.bits);
@ -110,6 +115,8 @@ bool SPU::DoState(StateWrapper& sw)
sw.Do(&v.has_samples); sw.Do(&v.has_samples);
} }
sw.Do(&m_voice_key_on_off_delay);
sw.DoBytes(m_ram.data(), RAM_SIZE); sw.DoBytes(m_ram.data(), RAM_SIZE);
if (sw.IsReading()) if (sw.IsReading())
@ -683,6 +690,15 @@ void SPU::Execute(TickCount ticks)
output_stream->EndWrite(frames_in_this_batch); output_stream->EndWrite(frames_in_this_batch);
remaining_frames -= frames_in_this_batch; remaining_frames -= frames_in_this_batch;
} }
for (u32 i = 0; i < NUM_VOICES; i++)
{
const u32 delay = static_cast<u32>(m_voice_key_on_off_delay[i]);
if (delay == 0)
continue;
m_voice_key_on_off_delay[i] -= static_cast<u8>(std::min(delay, static_cast<u32>(ticks)));
}
} }
void SPU::UpdateEventInterval() void SPU::UpdateEventInterval()
@ -1113,13 +1129,31 @@ std::tuple<s32, s32> SPU::SampleVoice(u32 voice_index)
void SPU::VoiceKeyOn(u32 voice_index) void SPU::VoiceKeyOn(u32 voice_index)
{ {
Log_DebugPrintf("Voice %u key on", voice_index); Log_DebugPrintf("Voice %u key on", voice_index);
if (m_voice_key_on_off_delay[voice_index] > 0)
{
Log_DevPrintf("Ignoring key on for voice %u due to only %u ticks passed since last write", voice_index,
m_voice_key_on_off_delay[voice_index]);
return;
}
m_voices[voice_index].KeyOn(); m_voices[voice_index].KeyOn();
m_voice_key_on_off_delay[voice_index] = MINIMUM_TICKS_BETWEEN_KEY_ON_OFF;
} }
void SPU::VoiceKeyOff(u32 voice_index) void SPU::VoiceKeyOff(u32 voice_index)
{ {
Log_DebugPrintf("Voice %u key off", voice_index); Log_DebugPrintf("Voice %u key off", voice_index);
if (m_voice_key_on_off_delay[voice_index] > 0)
{
Log_DevPrintf("Ignoring key off for voice %u due to only %u ticks passed since last write", voice_index,
m_voice_key_on_off_delay[voice_index]);
return;
}
m_voices[voice_index].KeyOff(); m_voices[voice_index].KeyOff();
m_voice_key_on_off_delay[voice_index] = MINIMUM_TICKS_BETWEEN_KEY_ON_OFF;
} }
void SPU::EnsureCDAudioSpace(u32 remaining_frames) void SPU::EnsureCDAudioSpace(u32 remaining_frames)

View file

@ -56,6 +56,7 @@ private:
static constexpr s16 ADSR_MAX_VOLUME = 0x7FFF; static constexpr s16 ADSR_MAX_VOLUME = 0x7FFF;
static constexpr u32 CD_AUDIO_SAMPLE_BUFFER_SIZE = 44100 * 2; static constexpr u32 CD_AUDIO_SAMPLE_BUFFER_SIZE = 44100 * 2;
static constexpr u32 CAPTURE_BUFFER_SIZE_PER_CHANNEL = 0x400; static constexpr u32 CAPTURE_BUFFER_SIZE_PER_CHANNEL = 0x400;
static constexpr u32 MINIMUM_TICKS_BETWEEN_KEY_ON_OFF = 2;
enum class RAMTransferMode : u8 enum class RAMTransferMode : u8
{ {
@ -283,6 +284,7 @@ private:
DMA* m_dma = nullptr; DMA* m_dma = nullptr;
InterruptController* m_interrupt_controller = nullptr; InterruptController* m_interrupt_controller = nullptr;
std::unique_ptr<TimingEvent> m_tick_event; std::unique_ptr<TimingEvent> m_tick_event;
u32 m_tick_counter = 0;
SPUCNT m_SPUCNT = {}; SPUCNT m_SPUCNT = {};
SPUSTAT m_SPUSTAT = {}; SPUSTAT m_SPUSTAT = {};
@ -310,6 +312,7 @@ private:
TickCount m_ticks_carry = 0; TickCount m_ticks_carry = 0;
std::array<Voice, NUM_VOICES> m_voices{}; std::array<Voice, NUM_VOICES> m_voices{};
std::array<u8, NUM_VOICES> m_voice_key_on_off_delay{};
std::array<u8, RAM_SIZE> m_ram{}; std::array<u8, RAM_SIZE> m_ram{};
InlineFIFOQueue<s16, CD_AUDIO_SAMPLE_BUFFER_SIZE> m_cd_audio_buffer; InlineFIFOQueue<s16, CD_AUDIO_SAMPLE_BUFFER_SIZE> m_cd_audio_buffer;