Project Andio

This commit is contained in:
Kelebek1
2022-07-16 23:48:45 +01:00
parent 6e36f4d230
commit 458da8a948
270 changed files with 33712 additions and 8445 deletions

View File

@ -0,0 +1,217 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/common/audio_renderer_parameter.h"
#include "audio_core/common/workbuffer_allocator.h"
#include "audio_core/renderer/behavior/behavior_info.h"
#include "audio_core/renderer/splitter/splitter_context.h"
#include "common/alignment.h"
namespace AudioCore::AudioRenderer {
SplitterDestinationData* SplitterContext::GetDesintationData(const s32 splitter_id,
const s32 destination_id) {
return splitter_infos[splitter_id].GetData(destination_id);
}
SplitterInfo& SplitterContext::GetInfo(const s32 splitter_id) {
return splitter_infos[splitter_id];
}
u32 SplitterContext::GetDataCount() const {
return destinations_count;
}
u32 SplitterContext::GetInfoCount() const {
return info_count;
}
SplitterDestinationData& SplitterContext::GetData(const u32 index) {
return splitter_destinations[index];
}
void SplitterContext::Setup(std::span<SplitterInfo> splitter_infos_, const u32 splitter_info_count_,
SplitterDestinationData* splitter_destinations_,
const u32 destination_count_, const bool splitter_bug_fixed_) {
splitter_infos = splitter_infos_;
info_count = splitter_info_count_;
splitter_destinations = splitter_destinations_;
destinations_count = destination_count_;
splitter_bug_fixed = splitter_bug_fixed_;
}
bool SplitterContext::UsingSplitter() const {
return splitter_infos.size() > 0 && info_count > 0 && splitter_destinations != nullptr &&
destinations_count > 0;
}
void SplitterContext::ClearAllNewConnectionFlag() {
for (s32 i = 0; i < info_count; i++) {
splitter_infos[i].SetNewConnectionFlag();
}
}
bool SplitterContext::Initialize(const BehaviorInfo& behavior,
const AudioRendererParameterInternal& params,
WorkbufferAllocator& allocator) {
if (behavior.IsSplitterSupported() && params.splitter_infos > 0 &&
params.splitter_destinations > 0) {
splitter_infos = allocator.Allocate<SplitterInfo>(params.splitter_infos, 0x10);
for (u32 i = 0; i < params.splitter_infos; i++) {
std::construct_at<SplitterInfo>(&splitter_infos[i], static_cast<s32>(i));
}
if (splitter_infos.size() == 0) {
splitter_infos = {};
return false;
}
splitter_destinations =
allocator.Allocate<SplitterDestinationData>(params.splitter_destinations, 0x10).data();
for (s32 i = 0; i < params.splitter_destinations; i++) {
std::construct_at<SplitterDestinationData>(&splitter_destinations[i], i);
}
if (params.splitter_destinations <= 0) {
splitter_infos = {};
splitter_destinations = nullptr;
return false;
}
Setup(splitter_infos, params.splitter_infos, splitter_destinations,
params.splitter_destinations, behavior.IsSplitterBugFixed());
}
return true;
}
bool SplitterContext::Update(const u8* input, u32& consumed_size) {
auto in_params{reinterpret_cast<const InParameterHeader*>(input)};
if (destinations_count == 0 || info_count == 0) {
consumed_size = 0;
return true;
}
if (in_params->magic != GetSplitterInParamHeaderMagic()) {
consumed_size = 0;
return false;
}
for (auto& splitter_info : splitter_infos) {
splitter_info.ClearNewConnectionFlag();
}
u32 offset{sizeof(InParameterHeader)};
offset = UpdateInfo(input, offset, in_params->info_count);
offset = UpdateData(input, offset, in_params->destination_count);
consumed_size = Common::AlignUp(offset, 0x10);
return true;
}
u32 SplitterContext::UpdateInfo(const u8* input, u32 offset, const u32 splitter_count) {
for (u32 i = 0; i < splitter_count; i++) {
auto info_header{reinterpret_cast<const SplitterInfo::InParameter*>(input + offset)};
if (info_header->magic != GetSplitterInfoMagic()) {
continue;
}
if (info_header->id < 0 || info_header->id > info_count) {
break;
}
auto& info{splitter_infos[info_header->id]};
RecomposeDestination(info, info_header);
offset += info.Update(info_header);
}
return offset;
}
u32 SplitterContext::UpdateData(const u8* input, u32 offset, const u32 count) {
for (u32 i = 0; i < count; i++) {
auto data_header{
reinterpret_cast<const SplitterDestinationData::InParameter*>(input + offset)};
if (data_header->magic != GetSplitterSendDataMagic()) {
continue;
}
if (data_header->id < 0 || data_header->id > destinations_count) {
continue;
}
splitter_destinations[data_header->id].Update(*data_header);
offset += sizeof(SplitterDestinationData::InParameter);
}
return offset;
}
void SplitterContext::UpdateInternalState() {
for (s32 i = 0; i < info_count; i++) {
splitter_infos[i].UpdateInternalState();
}
}
void SplitterContext::RecomposeDestination(SplitterInfo& out_info,
const SplitterInfo::InParameter* info_header) {
auto destination{out_info.GetData(0)};
while (destination != nullptr) {
auto dest{destination->GetNext()};
destination->SetNext(nullptr);
destination = dest;
}
out_info.SetDestinations(nullptr);
auto dest_count{info_header->destination_count};
if (!splitter_bug_fixed) {
dest_count = std::min(dest_count, GetDestCountPerInfoForCompat());
}
if (dest_count == 0) {
return;
}
std::span<const u32> destination_ids{reinterpret_cast<const u32*>(&info_header[1]), dest_count};
auto head{&splitter_destinations[destination_ids[0]]};
auto current_destination{head};
for (u32 i = 1; i < dest_count; i++) {
auto next_destination{&splitter_destinations[destination_ids[i]]};
current_destination->SetNext(next_destination);
current_destination = next_destination;
}
out_info.SetDestinations(head);
out_info.SetDestinationCount(dest_count);
}
u32 SplitterContext::GetDestCountPerInfoForCompat() const {
if (info_count <= 0) {
return 0;
}
return static_cast<u32>(destinations_count / info_count);
}
u64 SplitterContext::CalcWorkBufferSize(const BehaviorInfo& behavior,
const AudioRendererParameterInternal& params) {
u64 size{0};
if (!behavior.IsSplitterSupported()) {
return size;
}
size += params.splitter_destinations * sizeof(SplitterDestinationData) +
params.splitter_infos * sizeof(SplitterInfo);
if (behavior.IsSplitterBugFixed()) {
size += Common::AlignUp(params.splitter_destinations * sizeof(u32), 0x10);
}
return size;
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,189 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include "audio_core/renderer/splitter/splitter_destinations_data.h"
#include "audio_core/renderer/splitter/splitter_info.h"
#include "common/common_types.h"
namespace AudioCore {
struct AudioRendererParameterInternal;
class WorkbufferAllocator;
namespace AudioRenderer {
class BehaviorInfo;
/**
* The splitter allows much more control over how sound is mixed together.
* Previously, one mix can only connect to one other, and you may need
* more mixes (and duplicate processing) to achieve the same result.
* With the splitter, many-to-one and one-to-many mixing is possible.
* This was added in revision 2.
* Had a bug with incorrect numbers of destinations, fixed in revision 5.
*/
class SplitterContext {
struct InParameterHeader {
/* 0x00 */ u32 magic; // 'SNDH'
/* 0x04 */ s32 info_count;
/* 0x08 */ s32 destination_count;
/* 0x0C */ char unk0C[0x14];
};
static_assert(sizeof(InParameterHeader) == 0x20,
"SplitterContext::InParameterHeader has the wrong size!");
public:
/**
* Get a destination mix from the given splitter and destination index.
*
* @param splitter_id - Splitter index to get from.
* @param destination_id - Destination index within the splitter.
* @return Pointer to the found destination. May be nullptr.
*/
SplitterDestinationData* GetDesintationData(s32 splitter_id, s32 destination_id);
/**
* Get a splitter from the given index.
*
* @param index - Index of the desired splitter.
* @return Splitter requested.
*/
SplitterInfo& GetInfo(s32 index);
/**
* Get the total number of splitter destinations.
*
* @return Number of destiantions.
*/
u32 GetDataCount() const;
/**
* Get the total number of splitters.
*
* @return Number of splitters.
*/
u32 GetInfoCount() const;
/**
* Get a specific global destination.
*
* @param index - Index of the desired destination.
* @return The requested destination.
*/
SplitterDestinationData& GetData(u32 index);
/**
* Check if the splitter is in use.
*
* @return True if any splitter or destination is in use, otherwise false.
*/
bool UsingSplitter() const;
/**
* Mark all splitters as having new connections.
*/
void ClearAllNewConnectionFlag();
/**
* Initialize the context.
*
* @param behavior - Used to check for splitter support.
* @param params - Input parameters.
* @param allocator - Allocator used to allocate workbuffer memory.
*/
bool Initialize(const BehaviorInfo& behavior, const AudioRendererParameterInternal& params,
WorkbufferAllocator& allocator);
/**
* Update the context.
*
* @param input - Input buffer with the new info,
* expected to point to a InParameterHeader.
* @param consumed_size - Output with the number of bytes consumed from input.
*/
bool Update(const u8* input, u32& consumed_size);
/**
* Update the splitters.
*
* @param input - Input buffer with the new info.
* @param offset - Current offset within the input buffer,
* input + offset should point to a SplitterInfo::InParameter.
* @param splitter_count - Number of splitters in the input buffer.
* @return Number of bytes consumed in input.
*/
u32 UpdateInfo(const u8* input, u32 offset, u32 splitter_count);
/**
* Update the splitters.
*
* @param input - Input buffer with the new info.
* @param offset - Current offset within the input buffer,
* input + offset should point to a
* SplitterDestinationData::InParameter.
* @param destination_count - Number of destinations in the input buffer.
* @return Number of bytes consumed in input.
*/
u32 UpdateData(const u8* input, u32 offset, u32 destination_count);
/**
* Update the state of all destinations in all splitters.
*/
void UpdateInternalState();
/**
* Replace the given splitter's destinations with new ones.
*
* @param out_info - Splitter to recompose.
* @param info_header - Input parameters containing new destination ids.
*/
void RecomposeDestination(SplitterInfo& out_info, const SplitterInfo::InParameter* info_header);
/**
* Old calculation for destinations, this is the thing the splitter bug fixes.
* Left for compatibility, and now min'd with the actual count to not bug.
*
* @return Number of splitter destinations.
*/
u32 GetDestCountPerInfoForCompat() const;
/**
* Calculate the size of the required workbuffer for splitters and destinations.
*
* @param behavior - Used to check splitter features.
* @param params - Input parameters with splitter/destination counts.
* @return Required buffer size.
*/
static u64 CalcWorkBufferSize(const BehaviorInfo& behavior,
const AudioRendererParameterInternal& params);
private:
/**
* Setup the context.
*
* @param splitter_infos - Workbuffer for splitters.
* @param splitter_info_count - Number of splitters in the workbuffer.
* @param splitter_destinations - Workbuffer for splitter destinations.
* @param destination_count - Number of destinations in the workbuffer.
* @param splitter_bug_fixed - Is the splitter bug fixed?
*/
void Setup(std::span<SplitterInfo> splitter_infos, u32 splitter_info_count,
SplitterDestinationData* splitter_destinations, u32 destination_count,
bool splitter_bug_fixed);
/// Workbuffer for splitters
std::span<SplitterInfo> splitter_infos{};
/// Number of splitters in buffer
s32 info_count{};
/// Workbuffer for destinations
SplitterDestinationData* splitter_destinations{};
/// Number of destinations in buffer
s32 destinations_count{};
/// Is the splitter bug fixed?
bool splitter_bug_fixed{};
};
} // namespace AudioRenderer
} // namespace AudioCore

View File

@ -0,0 +1,87 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/splitter/splitter_destinations_data.h"
namespace AudioCore::AudioRenderer {
SplitterDestinationData::SplitterDestinationData(const s32 id_) : id{id_} {}
void SplitterDestinationData::ClearMixVolume() {
mix_volumes.fill(0.0f);
prev_mix_volumes.fill(0.0f);
}
s32 SplitterDestinationData::GetId() const {
return id;
}
bool SplitterDestinationData::IsConfigured() const {
return in_use && destination_id != UnusedMixId;
}
s32 SplitterDestinationData::GetMixId() const {
return destination_id;
}
f32 SplitterDestinationData::GetMixVolume(const u32 index) const {
if (index >= mix_volumes.size()) {
LOG_ERROR(Service_Audio, "SplitterDestinationData::GetMixVolume Invalid index {}", index);
return 0.0f;
}
return mix_volumes[index];
}
std::span<f32> SplitterDestinationData::GetMixVolume() {
return mix_volumes;
}
f32 SplitterDestinationData::GetMixVolumePrev(const u32 index) const {
if (index >= prev_mix_volumes.size()) {
LOG_ERROR(Service_Audio, "SplitterDestinationData::GetMixVolumePrev Invalid index {}",
index);
return 0.0f;
}
return prev_mix_volumes[index];
}
std::span<f32> SplitterDestinationData::GetMixVolumePrev() {
return prev_mix_volumes;
}
void SplitterDestinationData::Update(const InParameter& params) {
if (params.id != id || params.magic != GetSplitterSendDataMagic()) {
return;
}
destination_id = params.mix_id;
mix_volumes = params.mix_volumes;
if (!in_use && params.in_use) {
prev_mix_volumes = mix_volumes;
need_update = false;
}
in_use = params.in_use;
}
void SplitterDestinationData::MarkAsNeedToUpdateInternalState() {
need_update = true;
}
void SplitterDestinationData::UpdateInternalState() {
if (in_use && need_update) {
prev_mix_volumes = mix_volumes;
}
need_update = false;
}
SplitterDestinationData* SplitterDestinationData::GetNext() const {
return next;
}
void SplitterDestinationData::SetNext(SplitterDestinationData* next_) {
next = next_;
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,135 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <span>
#include "audio_core/common/common.h"
#include "common/common_types.h"
namespace AudioCore::AudioRenderer {
/**
* Represents a mixing node, can be connected to a previous and next destination forming a chain
* that a certain mix buffer will pass through to output.
*/
class SplitterDestinationData {
public:
struct InParameter {
/* 0x00 */ u32 magic; // 'SNDD'
/* 0x04 */ s32 id;
/* 0x08 */ std::array<f32, MaxMixBuffers> mix_volumes;
/* 0x68 */ u32 mix_id;
/* 0x6C */ bool in_use;
};
static_assert(sizeof(InParameter) == 0x70,
"SplitterDestinationData::InParameter has the wrong size!");
SplitterDestinationData(s32 id);
/**
* Reset the mix volumes for this destination.
*/
void ClearMixVolume();
/**
* Get the id of this destination.
*
* @return Id for this destination.
*/
s32 GetId() const;
/**
* Check if this destination is correctly configured.
*
* @return True if configured, otherwise false.
*/
bool IsConfigured() const;
/**
* Get the mix id for this destination.
*
* @return Mix id for this destination.
*/
s32 GetMixId() const;
/**
* Get the current mix volume of a given index in this destination.
*
* @param index - Mix buffer index to get the volume for.
* @return Current volume of the specified mix.
*/
f32 GetMixVolume(u32 index) const;
/**
* Get the current mix volumes for all mix buffers in this destination.
*
* @return Span of current mix buffer volumes.
*/
std::span<f32> GetMixVolume();
/**
* Get the previous mix volume of a given index in this destination.
*
* @param index - Mix buffer index to get the volume for.
* @return Previous volume of the specified mix.
*/
f32 GetMixVolumePrev(u32 index) const;
/**
* Get the previous mix volumes for all mix buffers in this destination.
*
* @return Span of previous mix buffer volumes.
*/
std::span<f32> GetMixVolumePrev();
/**
* Update this destination.
*
* @param params - Inpout parameters to update the destination.
*/
void Update(const InParameter& params);
/**
* Mark this destination as needing its volumes updated.
*/
void MarkAsNeedToUpdateInternalState();
/**
* Copy current volumes to previous if an update is required.
*/
void UpdateInternalState();
/**
* Get the next destination in the mix chain.
*
* @return The next splitter destination, may be nullptr if this is the last in the chain.
*/
SplitterDestinationData* GetNext() const;
/**
* Set the next destination in the mix chain.
*
* @param next - Destination this one is to be connected to.
*/
void SetNext(SplitterDestinationData* next);
private:
/// Id of this destination
const s32 id;
/// Mix id this destination represents
s32 destination_id{UnusedMixId};
/// Current mix volumes
std::array<f32, MaxMixBuffers> mix_volumes{0.0f};
/// Previous mix volumes
std::array<f32, MaxMixBuffers> prev_mix_volumes{0.0f};
/// Next destination in the mix chain
SplitterDestinationData* next{};
/// Is this destiantion in use?
bool in_use{};
/// Does this destiantion need its volumes updated?
bool need_update{};
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/splitter/splitter_info.h"
namespace AudioCore::AudioRenderer {
SplitterInfo::SplitterInfo(const s32 id_) : id{id_} {}
void SplitterInfo::InitializeInfos(SplitterInfo* splitters, const u32 count) {
if (splitters == nullptr) {
return;
}
for (u32 i = 0; i < count; i++) {
auto& splitter{splitters[i]};
splitter.destinations = nullptr;
splitter.destination_count = 0;
splitter.has_new_connection = true;
}
}
u32 SplitterInfo::Update(const InParameter* params) {
if (params->id != id) {
return 0;
}
sample_rate = params->sample_rate;
has_new_connection = true;
return static_cast<u32>((sizeof(InParameter) + 3 * sizeof(s32)) +
params->destination_count * sizeof(s32));
}
SplitterDestinationData* SplitterInfo::GetData(const u32 destination_id) {
auto out_destination{destinations};
u32 i{0};
while (i < destination_id) {
if (out_destination == nullptr) {
break;
}
out_destination = out_destination->GetNext();
i++;
}
return out_destination;
}
u32 SplitterInfo::GetDestinationCount() const {
return destination_count;
}
void SplitterInfo::SetDestinationCount(const u32 count) {
destination_count = count;
}
bool SplitterInfo::HasNewConnection() const {
return has_new_connection;
}
void SplitterInfo::ClearNewConnectionFlag() {
has_new_connection = false;
}
void SplitterInfo::SetNewConnectionFlag() {
has_new_connection = true;
}
void SplitterInfo::UpdateInternalState() {
auto destination{destinations};
while (destination != nullptr) {
destination->UpdateInternalState();
destination = destination->GetNext();
}
}
void SplitterInfo::SetDestinations(SplitterDestinationData* destinations_) {
destinations = destinations_;
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,107 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "audio_core/renderer/splitter/splitter_destinations_data.h"
#include "common/common_types.h"
namespace AudioCore::AudioRenderer {
/**
* Represents a splitter, wraps multiple output destinations to split an input mix into.
*/
class SplitterInfo {
public:
struct InParameter {
/* 0x00 */ u32 magic; // 'SNDI'
/* 0x04 */ s32 id;
/* 0x08 */ u32 sample_rate;
/* 0x0C */ u32 destination_count;
};
static_assert(sizeof(InParameter) == 0x10, "SplitterInfo::InParameter has the wrong size!");
explicit SplitterInfo(s32 id);
/**
* Initialize the given splitters.
*
* @param splitters - Splitters to initialize.
* @param count - Number of splitters given.
*/
static void InitializeInfos(SplitterInfo* splitters, u32 count);
/**
* Update this splitter.
*
* @param params - Input parameters to update with.
* @return The size in bytes of this splitter.
*/
u32 Update(const InParameter* params);
/**
* Get a destination in this splitter.
*
* @param id - Destination id to get.
* @return Pointer to the destination, may be nullptr.
*/
SplitterDestinationData* GetData(u32 id);
/**
* Get the number of destinations in this splitter.
*
* @return The number of destiantions.
*/
u32 GetDestinationCount() const;
/**
* Set the number of destinations in this splitter.
*
* @param count - The new number of destiantions.
*/
void SetDestinationCount(u32 count);
/**
* Check if the splitter has a new connection.
*
* @return True if there is a new connection, otherwise false.
*/
bool HasNewConnection() const;
/**
* Reset the new connection flag.
*/
void ClearNewConnectionFlag();
/**
* Mark as having a new connection.
*/
void SetNewConnectionFlag();
/**
* Update the state of all destinations.
*/
void UpdateInternalState();
/**
* Set this splitter's destinations.
*
* @param destinations - The new destination list for this splitter.
*/
void SetDestinations(SplitterDestinationData* destinations);
private:
/// Id of this splitter
s32 id;
/// Sample rate of this splitter
u32 sample_rate{};
/// Number of destinations in this splitter
u32 destination_count{};
/// Does this splitter have a new connection?
bool has_new_connection{true};
/// Pointer to the destinations of this splitter
SplitterDestinationData* destinations{};
/// Number of channels this splitter manages
u32 channel_count{};
};
} // namespace AudioCore::AudioRenderer