mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-02-08 05:33:31 +01:00
Merge pull request #11655 from AdmiralCurtiss/dtk-heap
HW/DVDInterface: Avoid heap allocation in DTK callback.
This commit is contained in:
commit
e83b6e19ab
@ -335,6 +335,16 @@ u32 AudioInterfaceManager::GetAISSampleRateDivisor() const
|
|||||||
return m_ais_sample_rate_divisor;
|
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
|
u32 AudioInterfaceManager::Get32KHzSampleRateDivisor() const
|
||||||
{
|
{
|
||||||
return Get48KHzSampleRateDivisor() * 3 / 2;
|
return Get48KHzSampleRateDivisor() * 3 / 2;
|
||||||
|
@ -23,6 +23,12 @@ class Mapping;
|
|||||||
|
|
||||||
namespace AudioInterface
|
namespace AudioInterface
|
||||||
{
|
{
|
||||||
|
enum class SampleRate
|
||||||
|
{
|
||||||
|
AI32KHz,
|
||||||
|
AI48KHz,
|
||||||
|
};
|
||||||
|
|
||||||
class AudioInterfaceManager
|
class AudioInterfaceManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -45,18 +51,17 @@ public:
|
|||||||
u32 GetAIDSampleRateDivisor() const;
|
u32 GetAIDSampleRateDivisor() const;
|
||||||
u32 GetAISSampleRateDivisor() 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 Get32KHzSampleRateDivisor() const;
|
||||||
u32 Get48KHzSampleRateDivisor() const;
|
u32 Get48KHzSampleRateDivisor() const;
|
||||||
|
|
||||||
void GenerateAISInterrupt();
|
void GenerateAISInterrupt();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class SampleRate
|
|
||||||
{
|
|
||||||
AI32KHz,
|
|
||||||
AI48KHz,
|
|
||||||
};
|
|
||||||
|
|
||||||
// AI Control Register
|
// AI Control Register
|
||||||
union AICR
|
union AICR
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
#include "Core/HW/DVD/DVDInterface.h"
|
#include "Core/HW/DVD/DVDInterface.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -103,7 +105,7 @@ void DVDInterface::DoState(PointerWrap& p)
|
|||||||
p.Do(m_current_length);
|
p.Do(m_current_length);
|
||||||
p.Do(m_next_start);
|
p.Do(m_next_start);
|
||||||
p.Do(m_next_length);
|
p.Do(m_next_length);
|
||||||
p.Do(m_pending_samples);
|
p.Do(m_pending_blocks);
|
||||||
p.Do(m_enable_dtk);
|
p.Do(m_enable_dtk);
|
||||||
p.Do(m_dtk_buffer_length);
|
p.Do(m_dtk_buffer_length);
|
||||||
|
|
||||||
@ -122,31 +124,34 @@ void DVDInterface::DoState(PointerWrap& p)
|
|||||||
m_adpcm_decoder.DoState(p);
|
m_adpcm_decoder.DoState(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t DVDInterface::ProcessDTKSamples(std::vector<s16>* temp_pcm,
|
size_t DVDInterface::ProcessDTKSamples(s16* target_samples, size_t target_block_count,
|
||||||
const std::vector<u8>& audio_data)
|
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 samples_processed = 0;
|
||||||
size_t bytes_processed = 0;
|
size_t bytes_processed = 0;
|
||||||
while (samples_processed < temp_pcm->size() / 2 && bytes_processed < audio_data.size())
|
for (size_t i = 0; i < block_count_to_process; ++i)
|
||||||
{
|
{
|
||||||
m_adpcm_decoder.DecodeBlock(&(*temp_pcm)[samples_processed * 2], &audio_data[bytes_processed]);
|
m_adpcm_decoder.DecodeBlock(&target_samples[samples_processed * 2],
|
||||||
for (size_t i = 0; i < StreamADPCM::SAMPLES_PER_BLOCK * 2; ++i)
|
&audio_data[bytes_processed]);
|
||||||
|
for (size_t j = 0; j < StreamADPCM::SAMPLES_PER_BLOCK * 2; ++j)
|
||||||
{
|
{
|
||||||
// TODO: Fix the mixer so it can accept non-byte-swapped samples.
|
// TODO: Fix the mixer so it can accept non-byte-swapped samples.
|
||||||
s16* sample = &(*temp_pcm)[samples_processed * 2 + i];
|
s16* sample = &target_samples[samples_processed * 2 + j];
|
||||||
*sample = Common::swap16(*sample);
|
*sample = Common::swap16(*sample);
|
||||||
}
|
}
|
||||||
samples_processed += StreamADPCM::SAMPLES_PER_BLOCK;
|
samples_processed += StreamADPCM::SAMPLES_PER_BLOCK;
|
||||||
bytes_processed += StreamADPCM::ONE_BLOCK_SIZE;
|
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;
|
u32 bytes_to_process = 0;
|
||||||
*samples_to_process = 0;
|
*blocks_to_process = 0;
|
||||||
while (*samples_to_process < maximum_samples)
|
while (*blocks_to_process < maximum_blocks)
|
||||||
{
|
{
|
||||||
if (m_audio_position >= m_current_start + m_current_length)
|
if (m_audio_position >= m_current_start + m_current_length)
|
||||||
{
|
{
|
||||||
@ -172,7 +177,7 @@ u32 DVDInterface::AdvanceDTK(u32 maximum_samples, u32* samples_to_process)
|
|||||||
|
|
||||||
m_audio_position += StreamADPCM::ONE_BLOCK_SIZE;
|
m_audio_position += StreamADPCM::ONE_BLOCK_SIZE;
|
||||||
bytes_to_process += 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;
|
return bytes_to_process;
|
||||||
@ -186,45 +191,51 @@ void DVDInterface::DTKStreamingCallback(DIInterruptType interrupt_type,
|
|||||||
// Actual games always set this to 48 KHz
|
// Actual games always set this to 48 KHz
|
||||||
// but let's make sure to use GetAISSampleRateDivisor()
|
// but let's make sure to use GetAISSampleRateDivisor()
|
||||||
// just in case it changes to 32 KHz
|
// just in case it changes to 32 KHz
|
||||||
|
const auto sample_rate = ai.GetAISSampleRate();
|
||||||
const u32 sample_rate_divisor = ai.GetAISSampleRateDivisor();
|
const u32 sample_rate_divisor = ai.GetAISSampleRateDivisor();
|
||||||
|
|
||||||
// Determine which audio data to read next.
|
// Determine which audio data to read next.
|
||||||
|
|
||||||
// 3.5 ms of samples
|
// 3.5 ms of samples
|
||||||
const u32 maximum_samples =
|
constexpr u32 MAX_POSSIBLE_BLOCKS = 6;
|
||||||
((Mixer::FIXED_SAMPLE_RATE_DIVIDEND / 2000) * 7) / sample_rate_divisor;
|
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;
|
u64 read_offset = 0;
|
||||||
u32 read_length = 0;
|
u32 read_length = 0;
|
||||||
|
|
||||||
if (interrupt_type == DIInterruptType::TCINT)
|
if (interrupt_type == DIInterruptType::TCINT)
|
||||||
{
|
{
|
||||||
// Send audio to the mixer.
|
// Send audio to the mixer.
|
||||||
std::vector<s16> temp_pcm(m_pending_samples * 2, 0);
|
std::array<s16, MAX_POSSIBLE_SAMPLES * 2> temp_pcm{};
|
||||||
ProcessDTKSamples(&temp_pcm, audio_data);
|
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();
|
SoundStream* sound_stream = m_system.GetSoundStream();
|
||||||
sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(), m_pending_samples);
|
sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(),
|
||||||
|
pending_blocks * StreamADPCM::SAMPLES_PER_BLOCK);
|
||||||
|
|
||||||
if (m_stream && ai.IsPlaying())
|
if (m_stream && ai.IsPlaying())
|
||||||
{
|
{
|
||||||
read_offset = m_audio_position;
|
read_offset = m_audio_position;
|
||||||
read_length = AdvanceDTK(maximum_samples, &m_pending_samples);
|
read_length = AdvanceDTK(maximum_blocks, &m_pending_blocks);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
read_length = 0;
|
read_length = 0;
|
||||||
m_pending_samples = maximum_samples;
|
m_pending_blocks = maximum_blocks;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
read_length = 0;
|
read_length = 0;
|
||||||
m_pending_samples = maximum_samples;
|
m_pending_blocks = maximum_blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the next chunk of audio data asynchronously.
|
// Read the next chunk of audio data asynchronously.
|
||||||
s64 ticks_to_dtk = SystemTimers::GetTicksPerSecond() * s64(m_pending_samples) *
|
s64 ticks_to_dtk = SystemTimers::GetTicksPerSecond() * s64(m_pending_blocks) *
|
||||||
sample_rate_divisor / Mixer::FIXED_SAMPLE_RATE_DIVIDEND;
|
StreamADPCM::SAMPLES_PER_BLOCK * sample_rate_divisor /
|
||||||
|
Mixer::FIXED_SAMPLE_RATE_DIVIDEND;
|
||||||
ticks_to_dtk -= cycles_late;
|
ticks_to_dtk -= cycles_late;
|
||||||
if (read_length > 0)
|
if (read_length > 0)
|
||||||
{
|
{
|
||||||
@ -282,7 +293,7 @@ void DVDInterface::ResetDrive(bool spinup)
|
|||||||
m_next_length = 0;
|
m_next_length = 0;
|
||||||
m_current_start = 0;
|
m_current_start = 0;
|
||||||
m_current_length = 0;
|
m_current_length = 0;
|
||||||
m_pending_samples = 0;
|
m_pending_blocks = 0;
|
||||||
m_enable_dtk = false;
|
m_enable_dtk = false;
|
||||||
m_dtk_buffer_length = 0;
|
m_dtk_buffer_length = 0;
|
||||||
|
|
||||||
|
@ -178,8 +178,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vector<u8>& audio_data,
|
void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vector<u8>& audio_data,
|
||||||
s64 cycles_late);
|
s64 cycles_late);
|
||||||
size_t ProcessDTKSamples(std::vector<s16>* temp_pcm, const std::vector<u8>& audio_data);
|
size_t ProcessDTKSamples(s16* target_samples, size_t target_block_count,
|
||||||
u32 AdvanceDTK(u32 maximum_samples, u32* samples_to_process);
|
const std::vector<u8>& audio_data);
|
||||||
|
u32 AdvanceDTK(u32 maximum_blocks, u32* blocks_to_process);
|
||||||
|
|
||||||
void SetLidOpen();
|
void SetLidOpen();
|
||||||
void UpdateInterrupts();
|
void UpdateInterrupts();
|
||||||
@ -273,7 +274,7 @@ private:
|
|||||||
u32 m_current_length = 0;
|
u32 m_current_length = 0;
|
||||||
u64 m_next_start = 0;
|
u64 m_next_start = 0;
|
||||||
u32 m_next_length = 0;
|
u32 m_next_length = 0;
|
||||||
u32 m_pending_samples = 0;
|
u32 m_pending_blocks = 0;
|
||||||
bool m_enable_dtk = false;
|
bool m_enable_dtk = false;
|
||||||
u8 m_dtk_buffer_length = 0; // TODO: figure out how this affects the regular buffer
|
u8 m_dtk_buffer_length = 0; // TODO: figure out how this affects the regular buffer
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ static size_t s_state_writes_in_queue;
|
|||||||
static std::condition_variable s_state_write_queue_is_empty;
|
static std::condition_variable s_state_write_queue_is_empty;
|
||||||
|
|
||||||
// Don't forget to increase this after doing changes on the savestate system
|
// 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.
|
// Maps savestate versions to Dolphin versions.
|
||||||
// Versions after 42 don't need to be added to this list,
|
// Versions after 42 don't need to be added to this list,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user