Merge pull request #1455 from ogniK5377/smo-softlockfix
Fixed smo softlock due to incorrect effect state updating
This commit is contained in:
commit
6aab309e41
2 changed files with 123 additions and 15 deletions
|
@ -30,7 +30,7 @@ public:
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
VoiceInfo& Info() {
|
VoiceInfo& GetInfo() {
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,9 +51,30 @@ private:
|
||||||
VoiceInfo info{};
|
VoiceInfo info{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AudioRenderer::EffectState {
|
||||||
|
public:
|
||||||
|
const EffectOutStatus& GetOutStatus() const {
|
||||||
|
return out_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EffectInStatus& GetInfo() const {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectInStatus& GetInfo() {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateState();
|
||||||
|
|
||||||
|
private:
|
||||||
|
EffectOutStatus out_status{};
|
||||||
|
EffectInStatus info{};
|
||||||
|
};
|
||||||
AudioRenderer::AudioRenderer(AudioRendererParameter params,
|
AudioRenderer::AudioRenderer(AudioRendererParameter params,
|
||||||
Kernel::SharedPtr<Kernel::Event> buffer_event)
|
Kernel::SharedPtr<Kernel::Event> buffer_event)
|
||||||
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
|
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
|
||||||
|
effects(params.effect_count) {
|
||||||
|
|
||||||
audio_out = std::make_unique<AudioCore::AudioOut>();
|
audio_out = std::make_unique<AudioCore::AudioOut>();
|
||||||
stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
|
stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
|
||||||
|
@ -96,11 +117,29 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
|
||||||
memory_pool_count * sizeof(MemoryPoolInfo));
|
memory_pool_count * sizeof(MemoryPoolInfo));
|
||||||
|
|
||||||
// Copy VoiceInfo structs
|
// Copy VoiceInfo structs
|
||||||
std::size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
|
std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size +
|
||||||
config.voice_resource_size};
|
config.memory_pools_size + config.voice_resource_size};
|
||||||
for (auto& voice : voices) {
|
for (auto& voice : voices) {
|
||||||
std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
|
std::memcpy(&voice.GetInfo(), input_params.data() + voice_offset, sizeof(VoiceInfo));
|
||||||
offset += sizeof(VoiceInfo);
|
voice_offset += sizeof(VoiceInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t effect_offset{sizeof(UpdateDataHeader) + config.behavior_size +
|
||||||
|
config.memory_pools_size + config.voice_resource_size +
|
||||||
|
config.voices_size};
|
||||||
|
for (auto& effect : effects) {
|
||||||
|
std::memcpy(&effect.GetInfo(), input_params.data() + effect_offset, sizeof(EffectInStatus));
|
||||||
|
effect_offset += sizeof(EffectInStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update memory pool state
|
||||||
|
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
|
||||||
|
for (std::size_t index = 0; index < memory_pool.size(); ++index) {
|
||||||
|
if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
|
||||||
|
memory_pool[index].state = MemoryPoolStates::Attached;
|
||||||
|
} else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
|
||||||
|
memory_pool[index].state = MemoryPoolStates::Detached;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update voices
|
// Update voices
|
||||||
|
@ -114,14 +153,8 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update memory pool state
|
for (auto& effect : effects) {
|
||||||
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
|
effect.UpdateState();
|
||||||
for (std::size_t index = 0; index < memory_pool.size(); ++index) {
|
|
||||||
if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
|
|
||||||
memory_pool[index].state = MemoryPoolStates::Attached;
|
|
||||||
} else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
|
|
||||||
memory_pool[index].state = MemoryPoolStates::Detached;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release previous buffers and queue next ones for playback
|
// Release previous buffers and queue next ones for playback
|
||||||
|
@ -144,6 +177,14 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
|
||||||
voice_out_status_offset += sizeof(VoiceOutStatus);
|
voice_out_status_offset += sizeof(VoiceOutStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::size_t effect_out_status_offset{
|
||||||
|
sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size +
|
||||||
|
response_data.voice_resource_size};
|
||||||
|
for (const auto& effect : effects) {
|
||||||
|
std::memcpy(output_params.data() + effect_out_status_offset, &effect.GetOutStatus(),
|
||||||
|
sizeof(EffectOutStatus));
|
||||||
|
effect_out_status_offset += sizeof(EffectOutStatus);
|
||||||
|
}
|
||||||
return output_params;
|
return output_params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,11 +285,29 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
samples = Interpolate(interp_state, std::move(samples), Info().sample_rate, STREAM_SAMPLE_RATE);
|
samples =
|
||||||
|
Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, STREAM_SAMPLE_RATE);
|
||||||
|
|
||||||
is_refresh_pending = false;
|
is_refresh_pending = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioRenderer::EffectState::UpdateState() {
|
||||||
|
if (info.is_new) {
|
||||||
|
out_status.state = EffectStatus::New;
|
||||||
|
} else {
|
||||||
|
if (info.type == Effect::Aux) {
|
||||||
|
ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_info) == 0,
|
||||||
|
"Aux buffers tried to update");
|
||||||
|
ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_info) == 0,
|
||||||
|
"Aux buffers tried to update");
|
||||||
|
ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_base) == 0,
|
||||||
|
"Aux buffers tried to update");
|
||||||
|
ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_base) == 0,
|
||||||
|
"Aux buffers tried to update");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr s16 ClampToS16(s32 value) {
|
static constexpr s16 ClampToS16(s32 value) {
|
||||||
return static_cast<s16>(std::clamp(value, -32768, 32767));
|
return static_cast<s16>(std::clamp(value, -32768, 32767));
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,16 @@ enum class PlayState : u8 {
|
||||||
Paused = 2,
|
Paused = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Effect : u8 {
|
||||||
|
None = 0,
|
||||||
|
Aux = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class EffectStatus : u8 {
|
||||||
|
None = 0,
|
||||||
|
New = 1,
|
||||||
|
};
|
||||||
|
|
||||||
struct AudioRendererParameter {
|
struct AudioRendererParameter {
|
||||||
u32_le sample_rate;
|
u32_le sample_rate;
|
||||||
u32_le sample_count;
|
u32_le sample_count;
|
||||||
|
@ -128,6 +138,43 @@ struct VoiceOutStatus {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
|
static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
|
||||||
|
|
||||||
|
struct AuxInfo {
|
||||||
|
std::array<u8, 24> input_mix_buffers;
|
||||||
|
std::array<u8, 24> output_mix_buffers;
|
||||||
|
u32_le mix_buffer_count;
|
||||||
|
u32_le sample_rate; // Stored in the aux buffer currently
|
||||||
|
u32_le sampe_count;
|
||||||
|
u64_le send_buffer_info;
|
||||||
|
u64_le send_buffer_base;
|
||||||
|
|
||||||
|
u64_le return_buffer_info;
|
||||||
|
u64_le return_buffer_base;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size");
|
||||||
|
|
||||||
|
struct EffectInStatus {
|
||||||
|
Effect type;
|
||||||
|
u8 is_new;
|
||||||
|
u8 is_enabled;
|
||||||
|
INSERT_PADDING_BYTES(1);
|
||||||
|
u32_le mix_id;
|
||||||
|
u64_le buffer_base;
|
||||||
|
u64_le buffer_sz;
|
||||||
|
s32_le priority;
|
||||||
|
INSERT_PADDING_BYTES(4);
|
||||||
|
union {
|
||||||
|
std::array<u8, 0xa0> raw;
|
||||||
|
AuxInfo aux_info;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
static_assert(sizeof(EffectInStatus) == 0xc0, "EffectInStatus is an invalid size");
|
||||||
|
|
||||||
|
struct EffectOutStatus {
|
||||||
|
EffectStatus state;
|
||||||
|
INSERT_PADDING_BYTES(0xf);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size");
|
||||||
|
|
||||||
struct UpdateDataHeader {
|
struct UpdateDataHeader {
|
||||||
UpdateDataHeader() {}
|
UpdateDataHeader() {}
|
||||||
|
|
||||||
|
@ -173,11 +220,13 @@ public:
|
||||||
Stream::State GetStreamState() const;
|
Stream::State GetStreamState() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
class EffectState;
|
||||||
class VoiceState;
|
class VoiceState;
|
||||||
|
|
||||||
AudioRendererParameter worker_params;
|
AudioRendererParameter worker_params;
|
||||||
Kernel::SharedPtr<Kernel::Event> buffer_event;
|
Kernel::SharedPtr<Kernel::Event> buffer_event;
|
||||||
std::vector<VoiceState> voices;
|
std::vector<VoiceState> voices;
|
||||||
|
std::vector<EffectState> effects;
|
||||||
std::unique_ptr<AudioOut> audio_out;
|
std::unique_ptr<AudioOut> audio_out;
|
||||||
AudioCore::StreamPtr stream;
|
AudioCore::StreamPtr stream;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue