audio_core: Add DSP sleep and wakeup functionality (#1529)

This commit is contained in:
PabloMK7 2025-12-23 22:57:56 +01:00 committed by GitHub
parent 275d818cd4
commit fe92815b04
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 133 additions and 27 deletions

View file

@ -8,7 +8,7 @@
namespace AudioCore::HLE {
AACDecoder::AACDecoder(Memory::MemorySystem& memory) : memory(memory) {
OpenNewDecoder();
Reset();
}
AACDecoder::~AACDecoder() {
@ -63,6 +63,10 @@ BinaryMessage AACDecoder::ProcessRequest(const BinaryMessage& request) {
}
}
void AACDecoder::Reset() {
OpenNewDecoder();
}
BinaryMessage AACDecoder::Decode(const BinaryMessage& request) {
BinaryMessage response{};
response.header.codec = request.header.codec;

View file

@ -16,6 +16,8 @@ public:
~AACDecoder() override;
BinaryMessage ProcessRequest(const BinaryMessage& request) override;
void Reset() override;
private:
BinaryMessage Decode(const BinaryMessage& request);
bool OpenNewDecoder();

View file

@ -1,4 +1,4 @@
// Copyright 2018 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -137,6 +137,8 @@ class DecoderBase {
public:
virtual ~DecoderBase() = default;
virtual BinaryMessage ProcessRequest(const BinaryMessage& request) = 0;
virtual void Reset() = 0;
};
} // namespace AudioCore::HLE

View file

@ -6,6 +6,7 @@
#include <boost/serialization/array.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/binary_object.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/weak_ptr.hpp>
@ -64,6 +65,10 @@ public:
std::function<void(Service::DSP::InterruptType type, DspPipe pipe)> handler);
private:
void Initialize();
void Sleep();
void Wakeup();
void ResetPipes();
void WriteU16(DspPipe pipe_number, u16 value);
void AudioPipeWriteStructAddresses();
@ -92,6 +97,8 @@ private:
}};
HLE::Mixers mixers{};
HLE::DspMemory backup_dsp_memory;
DspHle& parent;
Core::Timing& core_timing;
Core::TimingEventType* tick_event{};
@ -102,6 +109,8 @@ private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::make_binary_object(backup_dsp_memory.raw_memory.data(),
backup_dsp_memory.raw_memory.size());
ar & dsp_state;
ar & pipe_data;
ar & sources;
@ -217,8 +226,6 @@ void DspHle::Impl::PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) {
Sleep = 3,
};
// The difference between Initialize and Wakeup is that Input state is maintained
// when sleeping but isn't when turning it off and on again. (TODO: Implement this.)
// Waking up from sleep garbles some of the structs in the memory region. (TODO:
// Implement this.) Applications store away the state of these structs before
// sleeping and reset it back after wakeup on behalf of the DSP.
@ -226,7 +233,7 @@ void DspHle::Impl::PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) {
switch (static_cast<StateChange>(buffer[0])) {
case StateChange::Initialize:
LOG_INFO(Audio_DSP, "Application has requested initialization of DSP hardware");
ResetPipes();
Initialize();
AudioPipeWriteStructAddresses();
dsp_state = DspState::On;
break;
@ -236,13 +243,13 @@ void DspHle::Impl::PipeWrite(DspPipe pipe_number, std::span<const u8> buffer) {
break;
case StateChange::Wakeup:
LOG_INFO(Audio_DSP, "Application has requested wakeup of DSP hardware");
ResetPipes();
Wakeup();
AudioPipeWriteStructAddresses();
dsp_state = DspState::On;
break;
case StateChange::Sleep:
LOG_INFO(Audio_DSP, "Application has requested sleep of DSP hardware");
UNIMPLEMENTED();
Sleep();
AudioPipeWriteStructAddresses();
dsp_state = DspState::Sleeping;
break;
@ -290,11 +297,51 @@ void DspHle::Impl::SetInterruptHandler(
interrupt_handler = handler;
}
void DspHle::Impl::Initialize() {
// TODO(PabloMK7): This is NOT the right way to do this,
// but it is close enough. This makes sure the DSP state
// is clean and consistent every time the HW is initialized,
// but what is exactly reset needs to be figured out.
dsp_memory->raw_memory.fill(0);
mixers.Reset();
for (auto& s : sources) {
s.Reset();
}
aac_decoder->Reset();
ResetPipes();
}
void DspHle::Impl::Sleep() {
// TODO(PabloMK7): This is NOT the right way to do this,
// but it is close enough. What state is saved on
// real hardware still not figured out.
backup_dsp_memory.raw_memory = dsp_memory->raw_memory;
mixers.Sleep();
for (auto& s : sources) {
s.Sleep();
}
// TODO(PabloMK7): Figure out if we need to save the state
// of the AAC decoder, probably not.
}
void DspHle::Impl::Wakeup() {
// TODO(PabloMK7): This is NOT the right way to do this,
// but it is close enough. What state is restored on
// real hardware still not figured out.
dsp_memory->raw_memory = backup_dsp_memory.raw_memory;
backup_dsp_memory.raw_memory.fill(0);
mixers.Wakeup();
for (auto& s : sources) {
s.Wakeup();
}
aac_decoder->Reset();
ResetPipes();
}
void DspHle::Impl::ResetPipes() {
for (auto& data : pipe_data) {
data.clear();
}
dsp_state = DspState::Off;
}
void DspHle::Impl::WriteU16(DspPipe pipe_number, u16 value) {
@ -396,15 +443,19 @@ StereoFrame16 DspHle::Impl::GenerateCurrentFrame() {
}
bool DspHle::Impl::Tick() {
StereoFrame16 current_frame = {};
bool is_on = GetDspState() == DspState::On;
// TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to
// shared memory region)
current_frame = GenerateCurrentFrame();
if (is_on) {
StereoFrame16 current_frame = {};
parent.OutputFrame(std::move(current_frame));
// TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing
// to shared memory region)
current_frame = GenerateCurrentFrame();
return GetDspState() == DspState::On;
parent.OutputFrame(std::move(current_frame));
}
return is_on;
}
void DspHle::Impl::AudioTickCallback(s64 cycles_late) {

View file

@ -1,4 +1,4 @@
// Copyright 2016 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -15,6 +15,18 @@ void Mixers::Reset() {
state = {};
}
void Mixers::Sleep() {
backup_state = state;
backup_frame = current_frame;
}
void Mixers::Wakeup() {
state = backup_state;
current_frame = backup_frame;
backup_state = {};
backup_frame.fill({});
}
DspStatus Mixers::Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples,
IntermediateMixSamples& write_samples,
const std::array<QuadFrame32, 3>& input) {

View file

@ -1,4 +1,4 @@
// Copyright 2016 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -19,6 +19,9 @@ public:
void Reset();
void Sleep();
void Wakeup();
DspStatus Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples,
IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input);
@ -28,10 +31,11 @@ public:
private:
StereoFrame16 current_frame = {};
StereoFrame16 backup_frame = {}; // TODO(PabloMK7): Check if we actually need this
using OutputFormat = DspConfiguration::OutputFormat;
struct {
struct MixerState {
std::array<float, 3> intermediate_mixer_volume = {};
std::array<bool, 2> aux_bus_enable = {};
@ -39,7 +43,17 @@ private:
OutputFormat output_format = OutputFormat::Stereo;
} state;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar & intermediate_mixer_volume;
ar & aux_bus_enable;
ar & intermediate_mix_buffer;
ar & output_format;
}
};
MixerState state;
MixerState backup_state;
/// INTERNAL: Update our internal state based on the current config.
void ParseConfig(DspConfiguration& config);
@ -58,10 +72,9 @@ private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar & current_frame;
ar & state.intermediate_mixer_volume;
ar & state.aux_bus_enable;
ar & state.intermediate_mix_buffer;
ar & state.output_format;
ar & backup_frame;
ar & state;
ar & backup_state;
}
friend class boost::serialization::access;
};

View file

@ -1,4 +1,4 @@
// Copyright 2016 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -533,6 +533,7 @@ union DspMemory {
u8 unused_2[0x8000];
};
};
static_assert(sizeof(DspMemory) == 0x80000, "Incorrect DSP memory size");
static_assert(offsetof(DspMemory, region_0) == region0_offset,
"DSP region 0 is at the wrong offset");
static_assert(offsetof(DspMemory, region_1) == region1_offset,

View file

@ -1,4 +1,4 @@
// Copyright 2016 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -44,6 +44,18 @@ void Source::Reset() {
state = {};
}
void Source::Sleep() {
backup_frame = current_frame;
backup_state = state;
}
void Source::Wakeup() {
current_frame = backup_frame;
state = backup_state;
backup_frame.fill({});
backup_state = {};
}
void Source::SetMemory(Memory::MemorySystem& memory) {
memory_system = &memory;
}

View file

@ -1,4 +1,4 @@
// Copyright 2016 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -42,6 +42,9 @@ public:
/// Resets internal state.
void Reset();
void Sleep();
void Wakeup();
/// Sets the memory system to read data from
void SetMemory(Memory::MemorySystem& memory);
@ -68,6 +71,7 @@ private:
const std::size_t source_id;
Memory::MemorySystem* memory_system{};
StereoFrame16 current_frame;
StereoFrame16 backup_frame; // TODO(PabloMK7): Check if we actually need this
using Format = SourceConfiguration::Configuration::Format;
using InterpolationMode = SourceConfiguration::Configuration::InterpolationMode;
@ -116,7 +120,7 @@ private:
}
};
struct {
struct SourceState {
// State variables
@ -179,8 +183,10 @@ private:
ar & interpolation_mode;
}
friend class boost::serialization::access;
};
} state;
SourceState state;
SourceState backup_state;
// Internal functions
@ -197,6 +203,9 @@ private:
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar & state;
ar & backup_state;
ar & current_frame;
ar & backup_frame;
}
friend class boost::serialization::access;
};