mirror of
https://github.com/azahar-emu/azahar.git
synced 2026-06-06 02:33:44 -04:00
audio_core: Add DSP sleep and wakeup functionality (#1529)
This commit is contained in:
parent
275d818cd4
commit
fe92815b04
9 changed files with 133 additions and 27 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ public:
|
|||
~AACDecoder() override;
|
||||
BinaryMessage ProcessRequest(const BinaryMessage& request) override;
|
||||
|
||||
void Reset() override;
|
||||
|
||||
private:
|
||||
BinaryMessage Decode(const BinaryMessage& request);
|
||||
bool OpenNewDecoder();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue