Project Andio
This commit is contained in:
parent
6e36f4d230
commit
458da8a948
270 changed files with 33712 additions and 8445 deletions
60
src/audio_core/common/audio_renderer_parameter.h
Normal file
60
src/audio_core/common/audio_renderer_parameter.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "audio_core/renderer/behavior/behavior_info.h"
|
||||
#include "audio_core/renderer/memory/memory_pool_info.h"
|
||||
#include "audio_core/renderer/upsampler/upsampler_manager.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
/**
|
||||
* Execution mode of the audio renderer.
|
||||
* Only Auto is currently supported.
|
||||
*/
|
||||
enum class ExecutionMode : u8 {
|
||||
Auto,
|
||||
Manual,
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameters from the game, passed to the audio renderer for initialisation.
|
||||
*/
|
||||
struct AudioRendererParameterInternal {
|
||||
/* 0x00 */ u32 sample_rate;
|
||||
/* 0x04 */ u32 sample_count;
|
||||
/* 0x08 */ u32 mixes;
|
||||
/* 0x0C */ u32 sub_mixes;
|
||||
/* 0x10 */ u32 voices;
|
||||
/* 0x14 */ u32 sinks;
|
||||
/* 0x18 */ u32 effects;
|
||||
/* 0x1C */ u32 perf_frames;
|
||||
/* 0x20 */ u16 voice_drop_enabled;
|
||||
/* 0x22 */ u8 rendering_device;
|
||||
/* 0x23 */ ExecutionMode execution_mode;
|
||||
/* 0x24 */ u32 splitter_infos;
|
||||
/* 0x28 */ s32 splitter_destinations;
|
||||
/* 0x2C */ u32 external_context_size;
|
||||
/* 0x30 */ u32 revision;
|
||||
/* 0x34 */ char unk34[0x4];
|
||||
};
|
||||
static_assert(sizeof(AudioRendererParameterInternal) == 0x38,
|
||||
"AudioRendererParameterInternal has the wrong size!");
|
||||
|
||||
/**
|
||||
* Context for rendering, contains a bunch of useful fields for the command generator.
|
||||
*/
|
||||
struct AudioRendererSystemContext {
|
||||
s32 session_id;
|
||||
s8 channels;
|
||||
s16 mix_buffer_count;
|
||||
AudioRenderer::BehaviorInfo* behavior;
|
||||
std::span<s32> depop_buffer;
|
||||
AudioRenderer::UpsamplerManager* upsampler_manager;
|
||||
AudioRenderer::MemoryPoolInfo* memory_pool_info;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
138
src/audio_core/common/common.h
Normal file
138
src/audio_core/common/common.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <numeric>
|
||||
#include <span>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
using CpuAddr = std::uintptr_t;
|
||||
|
||||
enum class PlayState : u8 {
|
||||
Started,
|
||||
Stopped,
|
||||
Paused,
|
||||
};
|
||||
|
||||
enum class SrcQuality : u8 {
|
||||
Medium,
|
||||
High,
|
||||
Low,
|
||||
};
|
||||
|
||||
enum class SampleFormat : u8 {
|
||||
Invalid,
|
||||
PcmInt8,
|
||||
PcmInt16,
|
||||
PcmInt24,
|
||||
PcmInt32,
|
||||
PcmFloat,
|
||||
Adpcm,
|
||||
};
|
||||
|
||||
enum class SessionTypes {
|
||||
AudioIn,
|
||||
AudioOut,
|
||||
FinalOutputRecorder,
|
||||
};
|
||||
|
||||
enum class Channels : u32 {
|
||||
FrontLeft,
|
||||
FrontRight,
|
||||
Center,
|
||||
LFE,
|
||||
BackLeft,
|
||||
BackRight,
|
||||
};
|
||||
|
||||
// These are used by Delay, Reverb and I3dl2Reverb prior to Revision 11.
|
||||
enum class OldChannels : u32 {
|
||||
FrontLeft,
|
||||
FrontRight,
|
||||
BackLeft,
|
||||
BackRight,
|
||||
Center,
|
||||
LFE,
|
||||
};
|
||||
|
||||
constexpr u32 BufferCount = 32;
|
||||
|
||||
constexpr u32 MaxRendererSessions = 2;
|
||||
constexpr u32 TargetSampleCount = 240;
|
||||
constexpr u32 TargetSampleRate = 48'000;
|
||||
constexpr u32 MaxChannels = 6;
|
||||
constexpr u32 MaxMixBuffers = 24;
|
||||
constexpr u32 MaxWaveBuffers = 4;
|
||||
constexpr s32 LowestVoicePriority = 0xFF;
|
||||
constexpr s32 HighestVoicePriority = 0;
|
||||
constexpr u32 BufferAlignment = 0x40;
|
||||
constexpr u32 WorkbufferAlignment = 0x1000;
|
||||
constexpr s32 FinalMixId = 0;
|
||||
constexpr s32 InvalidDistanceFromFinalMix = std::numeric_limits<s32>::min();
|
||||
constexpr s32 UnusedSplitterId = -1;
|
||||
constexpr s32 UnusedMixId = std::numeric_limits<s32>::max();
|
||||
constexpr u32 InvalidNodeId = 0xF0000000;
|
||||
constexpr s32 InvalidProcessOrder = -1;
|
||||
constexpr u32 MaxBiquadFilters = 2;
|
||||
constexpr u32 MaxEffects = 256;
|
||||
|
||||
constexpr bool IsChannelCountValid(u16 channel_count) {
|
||||
return channel_count <= 6 &&
|
||||
(channel_count == 1 || channel_count == 2 || channel_count == 4 || channel_count == 6);
|
||||
}
|
||||
|
||||
constexpr void UseOldChannelMapping(std::span<s16> inputs, std::span<s16> outputs) {
|
||||
constexpr auto old_center{static_cast<u32>(OldChannels::Center)};
|
||||
constexpr auto new_center{static_cast<u32>(Channels::Center)};
|
||||
constexpr auto old_lfe{static_cast<u32>(OldChannels::LFE)};
|
||||
constexpr auto new_lfe{static_cast<u32>(Channels::LFE)};
|
||||
|
||||
auto center{inputs[old_center]};
|
||||
auto lfe{inputs[old_lfe]};
|
||||
inputs[old_center] = inputs[new_center];
|
||||
inputs[old_lfe] = inputs[new_lfe];
|
||||
inputs[new_center] = center;
|
||||
inputs[new_lfe] = lfe;
|
||||
|
||||
center = outputs[old_center];
|
||||
lfe = outputs[old_lfe];
|
||||
outputs[old_center] = outputs[new_center];
|
||||
outputs[old_lfe] = outputs[new_lfe];
|
||||
outputs[new_center] = center;
|
||||
outputs[new_lfe] = lfe;
|
||||
}
|
||||
|
||||
constexpr u32 GetSplitterInParamHeaderMagic() {
|
||||
return Common::MakeMagic('S', 'N', 'D', 'H');
|
||||
}
|
||||
|
||||
constexpr u32 GetSplitterInfoMagic() {
|
||||
return Common::MakeMagic('S', 'N', 'D', 'I');
|
||||
}
|
||||
|
||||
constexpr u32 GetSplitterSendDataMagic() {
|
||||
return Common::MakeMagic('S', 'N', 'D', 'D');
|
||||
}
|
||||
|
||||
constexpr size_t GetSampleFormatByteSize(SampleFormat format) {
|
||||
switch (format) {
|
||||
case SampleFormat::PcmInt8:
|
||||
return 1;
|
||||
case SampleFormat::PcmInt16:
|
||||
return 2;
|
||||
case SampleFormat::PcmInt24:
|
||||
return 3;
|
||||
case SampleFormat::PcmInt32:
|
||||
case SampleFormat::PcmFloat:
|
||||
return 4;
|
||||
default:
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
105
src/audio_core/common/feature_support.h
Normal file
105
src/audio_core/common/feature_support.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <ranges>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
constexpr u32 CurrentRevision = 11;
|
||||
|
||||
enum class SupportTags {
|
||||
CommandProcessingTimeEstimatorVersion4,
|
||||
CommandProcessingTimeEstimatorVersion3,
|
||||
CommandProcessingTimeEstimatorVersion2,
|
||||
MultiTapBiquadFilterProcessing,
|
||||
EffectInfoVer2,
|
||||
WaveBufferVer2,
|
||||
BiquadFilterFloatProcessing,
|
||||
VolumeMixParameterPrecisionQ23,
|
||||
MixInParameterDirtyOnlyUpdate,
|
||||
BiquadFilterEffectStateClearBugFix,
|
||||
VoicePlayedSampleCountResetAtLoopPoint,
|
||||
VoicePitchAndSrcSkipped,
|
||||
SplitterBugFix,
|
||||
FlushVoiceWaveBuffers,
|
||||
ElapsedFrameCount,
|
||||
AudioRendererVariadicCommandBufferSize,
|
||||
PerformanceMetricsDataFormatVersion2,
|
||||
AudioRendererProcessingTimeLimit80Percent,
|
||||
AudioRendererProcessingTimeLimit75Percent,
|
||||
AudioRendererProcessingTimeLimit70Percent,
|
||||
AdpcmLoopContextBugFix,
|
||||
Splitter,
|
||||
LongSizePreDelay,
|
||||
AudioUsbDeviceOutput,
|
||||
DeviceApiVersion2,
|
||||
DelayChannelMappingChange,
|
||||
ReverbChannelMappingChange,
|
||||
I3dl2ReverbChannelMappingChange,
|
||||
|
||||
// Not a real tag, just here to get the count.
|
||||
Size
|
||||
};
|
||||
|
||||
constexpr u32 GetRevisionNum(u32 user_revision) {
|
||||
if (user_revision >= 0x100) {
|
||||
user_revision -= Common::MakeMagic('R', 'E', 'V', '0');
|
||||
user_revision >>= 24;
|
||||
}
|
||||
return user_revision;
|
||||
};
|
||||
|
||||
constexpr bool CheckFeatureSupported(SupportTags tag, u32 user_revision) {
|
||||
constexpr std::array<std::pair<SupportTags, u32>, static_cast<u32>(SupportTags::Size)> features{
|
||||
{
|
||||
{SupportTags::AudioRendererProcessingTimeLimit70Percent, 1},
|
||||
{SupportTags::Splitter, 2},
|
||||
{SupportTags::AdpcmLoopContextBugFix, 2},
|
||||
{SupportTags::LongSizePreDelay, 3},
|
||||
{SupportTags::AudioUsbDeviceOutput, 4},
|
||||
{SupportTags::AudioRendererProcessingTimeLimit75Percent, 4},
|
||||
{SupportTags::VoicePlayedSampleCountResetAtLoopPoint, 5},
|
||||
{SupportTags::VoicePitchAndSrcSkipped, 5},
|
||||
{SupportTags::SplitterBugFix, 5},
|
||||
{SupportTags::FlushVoiceWaveBuffers, 5},
|
||||
{SupportTags::ElapsedFrameCount, 5},
|
||||
{SupportTags::AudioRendererProcessingTimeLimit80Percent, 5},
|
||||
{SupportTags::AudioRendererVariadicCommandBufferSize, 5},
|
||||
{SupportTags::PerformanceMetricsDataFormatVersion2, 5},
|
||||
{SupportTags::CommandProcessingTimeEstimatorVersion2, 5},
|
||||
{SupportTags::BiquadFilterEffectStateClearBugFix, 6},
|
||||
{SupportTags::BiquadFilterFloatProcessing, 7},
|
||||
{SupportTags::VolumeMixParameterPrecisionQ23, 7},
|
||||
{SupportTags::MixInParameterDirtyOnlyUpdate, 7},
|
||||
{SupportTags::WaveBufferVer2, 8},
|
||||
{SupportTags::CommandProcessingTimeEstimatorVersion3, 8},
|
||||
{SupportTags::EffectInfoVer2, 9},
|
||||
{SupportTags::CommandProcessingTimeEstimatorVersion4, 10},
|
||||
{SupportTags::MultiTapBiquadFilterProcessing, 10},
|
||||
{SupportTags::DelayChannelMappingChange, 11},
|
||||
{SupportTags::ReverbChannelMappingChange, 11},
|
||||
{SupportTags::I3dl2ReverbChannelMappingChange, 11},
|
||||
}};
|
||||
|
||||
const auto& feature =
|
||||
std::ranges::find_if(features, [tag](const auto& entry) { return entry.first == tag; });
|
||||
if (feature == features.cend()) {
|
||||
LOG_ERROR(Service_Audio, "Invalid SupportTag {}!", static_cast<u32>(tag));
|
||||
return false;
|
||||
}
|
||||
user_revision = GetRevisionNum(user_revision);
|
||||
return (*feature).second <= user_revision;
|
||||
}
|
||||
|
||||
constexpr bool CheckValidRevision(u32 user_revision) {
|
||||
return GetRevisionNum(user_revision) <= CurrentRevision;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
35
src/audio_core/common/wave_buffer.h
Normal file
35
src/audio_core/common/wave_buffer.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
struct WaveBufferVersion1 {
|
||||
CpuAddr buffer;
|
||||
u64 buffer_size;
|
||||
u32 start_offset;
|
||||
u32 end_offset;
|
||||
bool loop;
|
||||
bool stream_ended;
|
||||
CpuAddr context;
|
||||
u64 context_size;
|
||||
};
|
||||
|
||||
struct WaveBufferVersion2 {
|
||||
CpuAddr buffer;
|
||||
CpuAddr context;
|
||||
u64 buffer_size;
|
||||
u64 context_size;
|
||||
u32 start_offset;
|
||||
u32 end_offset;
|
||||
u32 loop_start_offset;
|
||||
u32 loop_end_offset;
|
||||
s32 loop_count;
|
||||
bool loop;
|
||||
bool stream_ended;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
100
src/audio_core/common/workbuffer_allocator.h
Normal file
100
src/audio_core/common/workbuffer_allocator.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
/**
|
||||
* Responsible for allocating up a workbuffer into multiple pieces.
|
||||
* Takes in a buffer and size (it does not own them), and allocates up the buffer via Allocate.
|
||||
*/
|
||||
class WorkbufferAllocator {
|
||||
public:
|
||||
explicit WorkbufferAllocator(std::span<u8> buffer_, u64 size_)
|
||||
: buffer{reinterpret_cast<u64>(buffer_.data())}, size{size_} {}
|
||||
|
||||
/**
|
||||
* Allocate the given count of T elements, aligned to alignment.
|
||||
*
|
||||
* @param count - The number of elements to allocate.
|
||||
* @param alignment - The required starting alignment.
|
||||
* @return Non-owning container of allocated elements.
|
||||
*/
|
||||
template <typename T>
|
||||
std::span<T> Allocate(u64 count, u64 alignment) {
|
||||
u64 out{0};
|
||||
u64 byte_size{count * sizeof(T)};
|
||||
|
||||
if (byte_size > 0) {
|
||||
auto current{buffer + offset};
|
||||
auto aligned_buffer{Common::AlignUp(current, alignment)};
|
||||
if (aligned_buffer + byte_size <= buffer + size) {
|
||||
out = aligned_buffer;
|
||||
offset = byte_size - buffer + aligned_buffer;
|
||||
} else {
|
||||
LOG_ERROR(
|
||||
Service_Audio,
|
||||
"Allocated buffer was too small to hold new alloc.\nAllocator size={:08X}, "
|
||||
"offset={:08X}.\nAttempting to allocate {:08X} with alignment={:02X}",
|
||||
size, offset, byte_size, alignment);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return std::span<T>(reinterpret_cast<T*>(out), count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Align the current offset to the given alignment.
|
||||
*
|
||||
* @param alignment - The required starting alignment.
|
||||
*/
|
||||
void Align(u64 alignment) {
|
||||
auto current{buffer + offset};
|
||||
auto aligned_buffer{Common::AlignUp(current, alignment)};
|
||||
offset = 0 - buffer + aligned_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current buffer offset.
|
||||
*
|
||||
* @return The current allocating offset.
|
||||
*/
|
||||
u64 GetCurrentOffset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current buffer size.
|
||||
*
|
||||
* @return The size of the current buffer.
|
||||
*/
|
||||
u64 GetSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the remaining size that can be allocated.
|
||||
*
|
||||
* @return The remaining size left in the buffer.
|
||||
*/
|
||||
u64 GetRemainingSize() const {
|
||||
return size - offset;
|
||||
}
|
||||
|
||||
private:
|
||||
/// The buffer into which we are allocating.
|
||||
u64 buffer;
|
||||
/// Size of the buffer we're allocating to.
|
||||
u64 size;
|
||||
/// Current offset into the buffer, an error will be thrown if it exceeds size.
|
||||
u64 offset{};
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
Loading…
Add table
Add a link
Reference in a new issue