HW/DVDInterface: Do DTK math in blocks instead of in samples.

This commit is contained in:
Admiral H. Curtiss 2023-03-14 11:59:47 +01:00
parent 66b6a60afe
commit 4c21cdd0e6
No known key found for this signature in database
GPG Key ID: F051B4C4044F33FB
5 changed files with 54 additions and 43 deletions

View File

@ -335,6 +335,16 @@ u32 AudioInterfaceManager::GetAISSampleRateDivisor() const
return m_ais_sample_rate_divisor;
}
SampleRate AudioInterfaceManager::GetAIDSampleRate() const
{
return m_control.AIDFR == AID_48KHz ? SampleRate::AI48KHz : SampleRate::AI32KHz;
}
SampleRate AudioInterfaceManager::GetAISSampleRate() const
{
return m_control.AISFR == AIS_32KHz ? SampleRate::AI32KHz : SampleRate::AI48KHz;
}
u32 AudioInterfaceManager::Get32KHzSampleRateDivisor() const
{
return Get48KHzSampleRateDivisor() * 3 / 2;

View File

@ -23,6 +23,12 @@ class Mapping;
namespace AudioInterface
{
enum class SampleRate
{
AI32KHz,
AI48KHz,
};
class AudioInterfaceManager
{
public:
@ -45,18 +51,17 @@ public:
u32 GetAIDSampleRateDivisor() const;
u32 GetAISSampleRateDivisor() const;
// The configured sample rate based on the control registers. Note that on GameCube, the named
// rates are slightly higher than the names would suggest due to a hardware bug.
SampleRate GetAIDSampleRate() const;
SampleRate GetAISSampleRate() const;
u32 Get32KHzSampleRateDivisor() const;
u32 Get48KHzSampleRateDivisor() const;
void GenerateAISInterrupt();
private:
enum class SampleRate
{
AI32KHz,
AI48KHz,
};
// AI Control Register
union AICR
{

View File

@ -105,7 +105,7 @@ void DVDInterface::DoState(PointerWrap& p)
p.Do(m_current_length);
p.Do(m_next_start);
p.Do(m_next_length);
p.Do(m_pending_samples);
p.Do(m_pending_blocks);
p.Do(m_enable_dtk);
p.Do(m_dtk_buffer_length);
@ -124,32 +124,34 @@ void DVDInterface::DoState(PointerWrap& p)
m_adpcm_decoder.DoState(p);
}
size_t DVDInterface::ProcessDTKSamples(s16* target_samples, size_t sample_count,
size_t DVDInterface::ProcessDTKSamples(s16* target_samples, size_t target_block_count,
const std::vector<u8>& audio_data)
{
const size_t block_count_to_process =
std::min(target_block_count, audio_data.size() / StreamADPCM::ONE_BLOCK_SIZE);
size_t samples_processed = 0;
size_t bytes_processed = 0;
while (samples_processed < sample_count && bytes_processed < audio_data.size())
for (size_t i = 0; i < block_count_to_process; ++i)
{
m_adpcm_decoder.DecodeBlock(&target_samples[samples_processed * 2],
&audio_data[bytes_processed]);
for (size_t i = 0; i < StreamADPCM::SAMPLES_PER_BLOCK * 2; ++i)
for (size_t j = 0; j < StreamADPCM::SAMPLES_PER_BLOCK * 2; ++j)
{
// TODO: Fix the mixer so it can accept non-byte-swapped samples.
s16* sample = &target_samples[samples_processed * 2 + i];
s16* sample = &target_samples[samples_processed * 2 + j];
*sample = Common::swap16(*sample);
}
samples_processed += StreamADPCM::SAMPLES_PER_BLOCK;
bytes_processed += StreamADPCM::ONE_BLOCK_SIZE;
}
return samples_processed;
return block_count_to_process;
}
u32 DVDInterface::AdvanceDTK(u32 maximum_samples, u32* samples_to_process)
u32 DVDInterface::AdvanceDTK(u32 maximum_blocks, u32* blocks_to_process)
{
u32 bytes_to_process = 0;
*samples_to_process = 0;
while (*samples_to_process < maximum_samples)
*blocks_to_process = 0;
while (*blocks_to_process < maximum_blocks)
{
if (m_audio_position >= m_current_start + m_current_length)
{
@ -175,7 +177,7 @@ u32 DVDInterface::AdvanceDTK(u32 maximum_samples, u32* samples_to_process)
m_audio_position += StreamADPCM::ONE_BLOCK_SIZE;
bytes_to_process += StreamADPCM::ONE_BLOCK_SIZE;
*samples_to_process += StreamADPCM::SAMPLES_PER_BLOCK;
*blocks_to_process += 1;
}
return bytes_to_process;
@ -189,57 +191,51 @@ void DVDInterface::DTKStreamingCallback(DIInterruptType interrupt_type,
// Actual games always set this to 48 KHz
// but let's make sure to use GetAISSampleRateDivisor()
// just in case it changes to 32 KHz
const auto sample_rate = ai.GetAISSampleRate();
const u32 sample_rate_divisor = ai.GetAISSampleRateDivisor();
// Determine which audio data to read next.
// 3.5 ms of samples
const u32 maximum_samples =
((Mixer::FIXED_SAMPLE_RATE_DIVIDEND / 2000) * 7) / sample_rate_divisor;
constexpr u32 MAX_POSSIBLE_BLOCKS = 6;
constexpr u32 MAX_POSSIBLE_SAMPLES = MAX_POSSIBLE_BLOCKS * StreamADPCM::SAMPLES_PER_BLOCK;
const u32 maximum_blocks = sample_rate == AudioInterface::SampleRate::AI32KHz ? 4 : 6;
u64 read_offset = 0;
u32 read_length = 0;
if (interrupt_type == DIInterruptType::TCINT)
{
// maximum_samples can only be 112 (if running at 32khz) or 168 (if running at 48khz), so just
// prepare stack space for the biggest possible case. This is true even for the GameCube's not
// quite correct sample rates.
constexpr u32 MAX_POSSIBLE_SAMPLES = 168;
// Send audio to the mixer.
std::array<s16, MAX_POSSIBLE_SAMPLES * 2> temp_pcm;
ASSERT(m_pending_samples <= MAX_POSSIBLE_SAMPLES);
const u32 pending_samples = std::min(m_pending_samples, MAX_POSSIBLE_SAMPLES);
const size_t samples_processed =
ProcessDTKSamples(temp_pcm.data(), pending_samples, audio_data);
// Fill unprocessed space up with zero samples.
const size_t samples_free = pending_samples - samples_processed;
std::fill_n(temp_pcm.data() + samples_processed * 2, samples_free * 2, 0);
std::array<s16, MAX_POSSIBLE_SAMPLES * 2> temp_pcm{};
ASSERT(m_pending_blocks <= MAX_POSSIBLE_BLOCKS);
const u32 pending_blocks = std::min(m_pending_blocks, MAX_POSSIBLE_BLOCKS);
ProcessDTKSamples(temp_pcm.data(), pending_blocks, audio_data);
SoundStream* sound_stream = m_system.GetSoundStream();
sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(), pending_samples);
sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(),
pending_blocks * StreamADPCM::SAMPLES_PER_BLOCK);
if (m_stream && ai.IsPlaying())
{
read_offset = m_audio_position;
read_length = AdvanceDTK(maximum_samples, &m_pending_samples);
read_length = AdvanceDTK(maximum_blocks, &m_pending_blocks);
}
else
{
read_length = 0;
m_pending_samples = maximum_samples;
m_pending_blocks = maximum_blocks;
}
}
else
{
read_length = 0;
m_pending_samples = maximum_samples;
m_pending_blocks = maximum_blocks;
}
// Read the next chunk of audio data asynchronously.
s64 ticks_to_dtk = SystemTimers::GetTicksPerSecond() * s64(m_pending_samples) *
sample_rate_divisor / Mixer::FIXED_SAMPLE_RATE_DIVIDEND;
s64 ticks_to_dtk = SystemTimers::GetTicksPerSecond() * s64(m_pending_blocks) *
StreamADPCM::SAMPLES_PER_BLOCK * sample_rate_divisor /
Mixer::FIXED_SAMPLE_RATE_DIVIDEND;
ticks_to_dtk -= cycles_late;
if (read_length > 0)
{
@ -297,7 +293,7 @@ void DVDInterface::ResetDrive(bool spinup)
m_next_length = 0;
m_current_start = 0;
m_current_length = 0;
m_pending_samples = 0;
m_pending_blocks = 0;
m_enable_dtk = false;
m_dtk_buffer_length = 0;

View File

@ -178,9 +178,9 @@ public:
private:
void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vector<u8>& audio_data,
s64 cycles_late);
size_t ProcessDTKSamples(s16* target_samples, size_t sample_count,
size_t ProcessDTKSamples(s16* target_samples, size_t target_block_count,
const std::vector<u8>& audio_data);
u32 AdvanceDTK(u32 maximum_samples, u32* samples_to_process);
u32 AdvanceDTK(u32 maximum_blocks, u32* blocks_to_process);
void SetLidOpen();
void UpdateInterrupts();
@ -274,7 +274,7 @@ private:
u32 m_current_length = 0;
u64 m_next_start = 0;
u32 m_next_length = 0;
u32 m_pending_samples = 0;
u32 m_pending_blocks = 0;
bool m_enable_dtk = false;
u8 m_dtk_buffer_length = 0; // TODO: figure out how this affects the regular buffer

View File

@ -96,7 +96,7 @@ static size_t s_state_writes_in_queue;
static std::condition_variable s_state_write_queue_is_empty;
// Don't forget to increase this after doing changes on the savestate system
constexpr u32 STATE_VERSION = 160; // Last changed in PR 11644
constexpr u32 STATE_VERSION = 161; // Last changed in PR 11655
// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,