forked from suyu/suyu
audren: Make use of nodiscard, rework downmixing, release all buffers
Preliminary work for upmixing & general cleanup. Fixes basic issues in games such as Shovel Knight and slightly improves the LEGO games. Upmixing stitll needs to be implemented. Audio levels in a few games will be fixed as we now use the downmix coefficients when possible instead of supplying our own
This commit is contained in:
parent
87f220efff
commit
9a4beac95a
13 changed files with 194 additions and 102 deletions
|
@ -43,6 +43,10 @@ std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream,
|
||||||
return stream->GetTagsAndReleaseBuffers(max_count);
|
return stream->GetTagsAndReleaseBuffers(max_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream) {
|
||||||
|
return stream->GetTagsAndReleaseBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
void AudioOut::StartStream(StreamPtr stream) {
|
void AudioOut::StartStream(StreamPtr stream) {
|
||||||
stream->Play();
|
stream->Play();
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,9 @@ public:
|
||||||
/// Returns a vector of recently released buffers specified by tag for the specified stream
|
/// Returns a vector of recently released buffers specified by tag for the specified stream
|
||||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
|
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
|
||||||
|
|
||||||
|
/// Returns a vector of all recently released buffers specified by tag for the specified stream
|
||||||
|
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream);
|
||||||
|
|
||||||
/// Starts an audio stream for playback
|
/// Starts an audio stream for playback
|
||||||
void StartStream(StreamPtr stream);
|
void StartStream(StreamPtr stream);
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "audio_core/audio_out.h"
|
#include "audio_core/audio_out.h"
|
||||||
|
@ -14,6 +15,59 @@
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
[[nodiscard]] static constexpr s16 ClampToS16(s32 value) {
|
||||||
|
return static_cast<s16>(std::clamp(value, static_cast<s32>(std::numeric_limits<s16>::min()),
|
||||||
|
static_cast<s32>(std::numeric_limits<s16>::max())));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static constexpr s16 Mix2To1(s16 l_channel, s16 r_channel) {
|
||||||
|
// Mix 50% from left and 50% from right channel
|
||||||
|
constexpr float l_mix_amount = 50.0f / 100.0f;
|
||||||
|
constexpr float r_mix_amount = 50.0f / 100.0f;
|
||||||
|
return ClampToS16(static_cast<s32>((static_cast<float>(l_channel) * l_mix_amount) +
|
||||||
|
(static_cast<float>(r_channel) * r_mix_amount)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2(s16 fl_channel, s16 fr_channel,
|
||||||
|
s16 fc_channel,
|
||||||
|
[[maybe_unused]] s16 lf_channel,
|
||||||
|
s16 bl_channel, s16 br_channel) {
|
||||||
|
// Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels
|
||||||
|
// are mixed to be 36.94%
|
||||||
|
|
||||||
|
constexpr float front_mix_amount = 36.94f / 100.0f;
|
||||||
|
constexpr float center_mix_amount = 26.12f / 100.0f;
|
||||||
|
constexpr float back_mix_amount = 36.94f / 100.0f;
|
||||||
|
|
||||||
|
// Mix 50% from left and 50% from right channel
|
||||||
|
const auto left = front_mix_amount * static_cast<float>(fl_channel) +
|
||||||
|
center_mix_amount * static_cast<float>(fc_channel) +
|
||||||
|
back_mix_amount * static_cast<float>(bl_channel);
|
||||||
|
|
||||||
|
const auto right = front_mix_amount * static_cast<float>(fr_channel) +
|
||||||
|
center_mix_amount * static_cast<float>(fc_channel) +
|
||||||
|
back_mix_amount * static_cast<float>(br_channel);
|
||||||
|
|
||||||
|
return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2WithCoefficients(
|
||||||
|
s16 fl_channel, s16 fr_channel, s16 fc_channel, s16 lf_channel, s16 bl_channel, s16 br_channel,
|
||||||
|
const std::array<float_le, 4>& coeff) {
|
||||||
|
const auto left =
|
||||||
|
static_cast<float>(fl_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
|
||||||
|
static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(bl_channel) * coeff[0];
|
||||||
|
|
||||||
|
const auto right =
|
||||||
|
static_cast<float>(fr_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
|
||||||
|
static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(br_channel) * coeff[0];
|
||||||
|
|
||||||
|
return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
||||||
AudioCommon::AudioRendererParameter params,
|
AudioCommon::AudioRendererParameter params,
|
||||||
|
@ -62,10 +116,6 @@ Stream::State AudioRenderer::GetStreamState() const {
|
||||||
return stream->GetState();
|
return stream->GetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr s16 ClampToS16(s32 value) {
|
|
||||||
return static_cast<s16>(std::clamp(value, -32768, 32767));
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
|
ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||||
std::vector<u8>& output_params) {
|
std::vector<u8>& output_params) {
|
||||||
|
|
||||||
|
@ -104,8 +154,8 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
|
const auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
|
||||||
splitter_context, effect_context);
|
splitter_context, effect_context);
|
||||||
|
|
||||||
if (mix_result.IsError()) {
|
if (mix_result.IsError()) {
|
||||||
LOG_ERROR(Audio, "Failed to update mix parameters");
|
LOG_ERROR(Audio, "Failed to update mix parameters");
|
||||||
|
@ -194,20 +244,22 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||||
for (std::size_t i = 0; i < BUFFER_SIZE; i++) {
|
for (std::size_t i = 0; i < BUFFER_SIZE; i++) {
|
||||||
if (channel_count == 1) {
|
if (channel_count == 1) {
|
||||||
const auto sample = ClampToS16(mix_buffers[0][i]);
|
const auto sample = ClampToS16(mix_buffers[0][i]);
|
||||||
buffer[i * stream_channel_count + 0] = sample;
|
|
||||||
if (stream_channel_count > 1) {
|
// Place sample in all channels
|
||||||
buffer[i * stream_channel_count + 1] = sample;
|
for (u32 channel = 0; channel < stream_channel_count; channel++) {
|
||||||
|
buffer[i * stream_channel_count + channel] = sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stream_channel_count == 6) {
|
if (stream_channel_count == 6) {
|
||||||
buffer[i * stream_channel_count + 2] = sample;
|
// Output stream has a LF channel, mute it!
|
||||||
buffer[i * stream_channel_count + 4] = sample;
|
buffer[i * stream_channel_count + 3] = 0;
|
||||||
buffer[i * stream_channel_count + 5] = sample;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (channel_count == 2) {
|
} else if (channel_count == 2) {
|
||||||
const auto l_sample = ClampToS16(mix_buffers[0][i]);
|
const auto l_sample = ClampToS16(mix_buffers[0][i]);
|
||||||
const auto r_sample = ClampToS16(mix_buffers[1][i]);
|
const auto r_sample = ClampToS16(mix_buffers[1][i]);
|
||||||
if (stream_channel_count == 1) {
|
if (stream_channel_count == 1) {
|
||||||
buffer[i * stream_channel_count + 0] = l_sample;
|
buffer[i * stream_channel_count + 0] = Mix2To1(l_sample, r_sample);
|
||||||
} else if (stream_channel_count == 2) {
|
} else if (stream_channel_count == 2) {
|
||||||
buffer[i * stream_channel_count + 0] = l_sample;
|
buffer[i * stream_channel_count + 0] = l_sample;
|
||||||
buffer[i * stream_channel_count + 1] = r_sample;
|
buffer[i * stream_channel_count + 1] = r_sample;
|
||||||
|
@ -215,8 +267,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||||
buffer[i * stream_channel_count + 0] = l_sample;
|
buffer[i * stream_channel_count + 0] = l_sample;
|
||||||
buffer[i * stream_channel_count + 1] = r_sample;
|
buffer[i * stream_channel_count + 1] = r_sample;
|
||||||
|
|
||||||
buffer[i * stream_channel_count + 2] =
|
// Combine both left and right channels to the center channel
|
||||||
ClampToS16((static_cast<s32>(l_sample) + static_cast<s32>(r_sample)) / 2);
|
buffer[i * stream_channel_count + 2] = Mix2To1(l_sample, r_sample);
|
||||||
|
|
||||||
buffer[i * stream_channel_count + 4] = l_sample;
|
buffer[i * stream_channel_count + 4] = l_sample;
|
||||||
buffer[i * stream_channel_count + 5] = r_sample;
|
buffer[i * stream_channel_count + 5] = r_sample;
|
||||||
|
@ -231,17 +283,25 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||||
const auto br_sample = ClampToS16(mix_buffers[5][i]);
|
const auto br_sample = ClampToS16(mix_buffers[5][i]);
|
||||||
|
|
||||||
if (stream_channel_count == 1) {
|
if (stream_channel_count == 1) {
|
||||||
buffer[i * stream_channel_count + 0] = fc_sample;
|
// Games seem to ignore the center channel half the time, we use the front left
|
||||||
|
// and right channel for mixing as that's where majority of the audio goes
|
||||||
|
buffer[i * stream_channel_count + 0] = Mix2To1(fl_sample, fr_sample);
|
||||||
} else if (stream_channel_count == 2) {
|
} else if (stream_channel_count == 2) {
|
||||||
buffer[i * stream_channel_count + 0] =
|
// Mix all channels into 2 channels
|
||||||
static_cast<s16>(0.3694f * static_cast<float>(fl_sample) +
|
if (sink_context.HasDownMixingCoefficients()) {
|
||||||
0.2612f * static_cast<float>(fc_sample) +
|
const auto [left, right] = Mix6To2WithCoefficients(
|
||||||
0.3694f * static_cast<float>(bl_sample));
|
fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample,
|
||||||
buffer[i * stream_channel_count + 1] =
|
sink_context.GetDownmixCoefficients());
|
||||||
static_cast<s16>(0.3694f * static_cast<float>(fr_sample) +
|
buffer[i * stream_channel_count + 0] = left;
|
||||||
0.2612f * static_cast<float>(fc_sample) +
|
buffer[i * stream_channel_count + 1] = right;
|
||||||
0.3694f * static_cast<float>(br_sample));
|
} else {
|
||||||
|
const auto [left, right] = Mix6To2(fl_sample, fr_sample, fc_sample,
|
||||||
|
lf_sample, bl_sample, br_sample);
|
||||||
|
buffer[i * stream_channel_count + 0] = left;
|
||||||
|
buffer[i * stream_channel_count + 1] = right;
|
||||||
|
}
|
||||||
} else if (stream_channel_count == 6) {
|
} else if (stream_channel_count == 6) {
|
||||||
|
// Pass through
|
||||||
buffer[i * stream_channel_count + 0] = fl_sample;
|
buffer[i * stream_channel_count + 0] = fl_sample;
|
||||||
buffer[i * stream_channel_count + 1] = fr_sample;
|
buffer[i * stream_channel_count + 1] = fr_sample;
|
||||||
buffer[i * stream_channel_count + 2] = fc_sample;
|
buffer[i * stream_channel_count + 2] = fc_sample;
|
||||||
|
@ -259,7 +319,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioRenderer::ReleaseAndQueueBuffers() {
|
void AudioRenderer::ReleaseAndQueueBuffers() {
|
||||||
const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream, 2)};
|
const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)};
|
||||||
for (const auto& tag : released_buffers) {
|
for (const auto& tag : released_buffers) {
|
||||||
QueueMixedBuffer(tag);
|
QueueMixedBuffer(tag);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,16 +36,10 @@ class Memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
using DSPStateHolder = std::array<VoiceState*, 6>;
|
using DSPStateHolder = std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>;
|
||||||
|
|
||||||
class AudioOut;
|
class AudioOut;
|
||||||
|
|
||||||
struct RendererInfo {
|
|
||||||
u64_le elasped_frame_count{};
|
|
||||||
INSERT_PADDING_WORDS(2);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size");
|
|
||||||
|
|
||||||
class AudioRenderer {
|
class AudioRenderer {
|
||||||
public:
|
public:
|
||||||
AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
||||||
|
@ -53,14 +47,14 @@ public:
|
||||||
std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
|
std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
|
||||||
~AudioRenderer();
|
~AudioRenderer();
|
||||||
|
|
||||||
ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
|
[[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||||
std::vector<u8>& output_params);
|
std::vector<u8>& output_params);
|
||||||
void QueueMixedBuffer(Buffer::Tag tag);
|
void QueueMixedBuffer(Buffer::Tag tag);
|
||||||
void ReleaseAndQueueBuffers();
|
void ReleaseAndQueueBuffers();
|
||||||
u32 GetSampleRate() const;
|
[[nodiscard]] u32 GetSampleRate() const;
|
||||||
u32 GetSampleCount() const;
|
[[nodiscard]] u32 GetSampleCount() const;
|
||||||
u32 GetMixBufferCount() const;
|
[[nodiscard]] u32 GetMixBufferCount() const;
|
||||||
Stream::State GetStreamState() const;
|
[[nodiscard]] Stream::State GetStreamState() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BehaviorInfo behavior_info{};
|
BehaviorInfo behavior_info{};
|
||||||
|
|
|
@ -43,22 +43,22 @@ public:
|
||||||
void ClearError();
|
void ClearError();
|
||||||
void UpdateFlags(u64_le dest_flags);
|
void UpdateFlags(u64_le dest_flags);
|
||||||
void SetUserRevision(u32_le revision);
|
void SetUserRevision(u32_le revision);
|
||||||
u32_le GetUserRevision() const;
|
[[nodiscard]] u32_le GetUserRevision() const;
|
||||||
u32_le GetProcessRevision() const;
|
[[nodiscard]] u32_le GetProcessRevision() const;
|
||||||
|
|
||||||
bool IsAdpcmLoopContextBugFixed() const;
|
[[nodiscard]] bool IsAdpcmLoopContextBugFixed() const;
|
||||||
bool IsSplitterSupported() const;
|
[[nodiscard]] bool IsSplitterSupported() const;
|
||||||
bool IsLongSizePreDelaySupported() const;
|
[[nodiscard]] bool IsLongSizePreDelaySupported() const;
|
||||||
bool IsAudioRendererProcessingTimeLimit80PercentSupported() const;
|
[[nodiscard]] bool IsAudioRendererProcessingTimeLimit80PercentSupported() const;
|
||||||
bool IsAudioRendererProcessingTimeLimit75PercentSupported() const;
|
[[nodiscard]] bool IsAudioRendererProcessingTimeLimit75PercentSupported() const;
|
||||||
bool IsAudioRendererProcessingTimeLimit70PercentSupported() const;
|
[[nodiscard]] bool IsAudioRendererProcessingTimeLimit70PercentSupported() const;
|
||||||
bool IsElapsedFrameCountSupported() const;
|
[[nodiscard]] bool IsElapsedFrameCountSupported() const;
|
||||||
bool IsMemoryPoolForceMappingEnabled() const;
|
[[nodiscard]] bool IsMemoryPoolForceMappingEnabled() const;
|
||||||
bool IsFlushVoiceWaveBuffersSupported() const;
|
[[nodiscard]] bool IsFlushVoiceWaveBuffersSupported() const;
|
||||||
bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const;
|
[[nodiscard]] bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const;
|
||||||
bool IsVoicePitchAndSrcSkippedSupported() const;
|
[[nodiscard]] bool IsVoicePitchAndSrcSkippedSupported() const;
|
||||||
bool IsMixInParameterDirtyOnlyUpdateSupported() const;
|
[[nodiscard]] bool IsMixInParameterDirtyOnlyUpdateSupported() const;
|
||||||
bool IsSplitterBugFixed() const;
|
[[nodiscard]] bool IsSplitterBugFixed() const;
|
||||||
void CopyErrorInfo(OutParams& dst);
|
void CopyErrorInfo(OutParams& dst);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -39,13 +39,13 @@ public:
|
||||||
void PreCommand();
|
void PreCommand();
|
||||||
void PostCommand();
|
void PostCommand();
|
||||||
|
|
||||||
s32* GetChannelMixBuffer(s32 channel);
|
[[nodiscard]] s32* GetChannelMixBuffer(s32 channel);
|
||||||
const s32* GetChannelMixBuffer(s32 channel) const;
|
[[nodiscard]] const s32* GetChannelMixBuffer(s32 channel) const;
|
||||||
s32* GetMixBuffer(std::size_t index);
|
[[nodiscard]] s32* GetMixBuffer(std::size_t index);
|
||||||
const s32* GetMixBuffer(std::size_t index) const;
|
[[nodiscard]] const s32* GetMixBuffer(std::size_t index) const;
|
||||||
std::size_t GetMixChannelBufferOffset(s32 channel) const;
|
[[nodiscard]] std::size_t GetMixChannelBufferOffset(s32 channel) const;
|
||||||
|
|
||||||
std::size_t GetTotalMixBufferCount() const;
|
[[nodiscard]] std::size_t GetTotalMixBufferCount() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel);
|
void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel);
|
||||||
|
@ -73,7 +73,7 @@ private:
|
||||||
void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||||
void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||||
void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||||
ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
|
[[nodiscard]] ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
|
||||||
|
|
||||||
s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
|
s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
|
||||||
u32 sample_count, u32 write_offset, u32 write_count);
|
u32 sample_count, u32 write_offset, u32 write_count);
|
||||||
|
|
|
@ -22,7 +22,7 @@ constexpr std::size_t MAX_CHANNEL_COUNT = 6;
|
||||||
constexpr std::size_t MAX_WAVE_BUFFERS = 4;
|
constexpr std::size_t MAX_WAVE_BUFFERS = 4;
|
||||||
constexpr std::size_t MAX_SAMPLE_HISTORY = 4;
|
constexpr std::size_t MAX_SAMPLE_HISTORY = 4;
|
||||||
constexpr u32 STREAM_SAMPLE_RATE = 48000;
|
constexpr u32 STREAM_SAMPLE_RATE = 48000;
|
||||||
constexpr u32 STREAM_NUM_CHANNELS = 6;
|
constexpr u32 STREAM_NUM_CHANNELS = 2;
|
||||||
constexpr s32 NO_SPLITTER = -1;
|
constexpr s32 NO_SPLITTER = -1;
|
||||||
constexpr s32 NO_MIX = 0x7fffffff;
|
constexpr s32 NO_MIX = 0x7fffffff;
|
||||||
constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min();
|
constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min();
|
||||||
|
|
|
@ -189,11 +189,11 @@ public:
|
||||||
|
|
||||||
virtual void Update(EffectInfo::InParams& in_params) = 0;
|
virtual void Update(EffectInfo::InParams& in_params) = 0;
|
||||||
virtual void UpdateForCommandGeneration() = 0;
|
virtual void UpdateForCommandGeneration() = 0;
|
||||||
UsageState GetUsage() const;
|
[[nodiscard]] UsageState GetUsage() const;
|
||||||
EffectType GetType() const;
|
[[nodiscard]] EffectType GetType() const;
|
||||||
bool IsEnabled() const;
|
[[nodiscard]] bool IsEnabled() const;
|
||||||
s32 GetMixID() const;
|
[[nodiscard]] s32 GetMixID() const;
|
||||||
s32 GetProcessingOrder() const;
|
[[nodiscard]] s32 GetProcessingOrder() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
UsageState usage{UsageState::Invalid};
|
UsageState usage{UsageState::Invalid};
|
||||||
|
@ -257,10 +257,10 @@ public:
|
||||||
|
|
||||||
void Update(EffectInfo::InParams& in_params) override;
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
void UpdateForCommandGeneration() override;
|
void UpdateForCommandGeneration() override;
|
||||||
VAddr GetSendInfo() const;
|
[[nodiscard]] VAddr GetSendInfo() const;
|
||||||
VAddr GetSendBuffer() const;
|
[[nodiscard]] VAddr GetSendBuffer() const;
|
||||||
VAddr GetRecvInfo() const;
|
[[nodiscard]] VAddr GetRecvInfo() const;
|
||||||
VAddr GetRecvBuffer() const;
|
[[nodiscard]] VAddr GetRecvBuffer() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VAddr send_info{};
|
VAddr send_info{};
|
||||||
|
@ -309,10 +309,10 @@ public:
|
||||||
explicit EffectContext(std::size_t effect_count);
|
explicit EffectContext(std::size_t effect_count);
|
||||||
~EffectContext();
|
~EffectContext();
|
||||||
|
|
||||||
std::size_t GetCount() const;
|
[[nodiscard]] std::size_t GetCount() const;
|
||||||
EffectBase* GetInfo(std::size_t i);
|
[[nodiscard]] EffectBase* GetInfo(std::size_t i);
|
||||||
EffectBase* RetargetEffect(std::size_t i, EffectType effect);
|
[[nodiscard]] EffectBase* RetargetEffect(std::size_t i, EffectType effect);
|
||||||
const EffectBase* GetInfo(std::size_t i) const;
|
[[nodiscard]] const EffectBase* GetInfo(std::size_t i) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::size_t effect_count{};
|
std::size_t effect_count{};
|
||||||
|
|
|
@ -62,17 +62,17 @@ public:
|
||||||
ServerMixInfo();
|
ServerMixInfo();
|
||||||
~ServerMixInfo();
|
~ServerMixInfo();
|
||||||
|
|
||||||
const ServerMixInfo::InParams& GetInParams() const;
|
[[nodiscard]] const ServerMixInfo::InParams& GetInParams() const;
|
||||||
ServerMixInfo::InParams& GetInParams();
|
[[nodiscard]] ServerMixInfo::InParams& GetInParams();
|
||||||
|
|
||||||
bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
|
bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
|
||||||
BehaviorInfo& behavior_info, SplitterContext& splitter_context,
|
BehaviorInfo& behavior_info, SplitterContext& splitter_context,
|
||||||
EffectContext& effect_context);
|
EffectContext& effect_context);
|
||||||
bool HasAnyConnection() const;
|
[[nodiscard]] bool HasAnyConnection() const;
|
||||||
void Cleanup();
|
void Cleanup();
|
||||||
void SetEffectCount(std::size_t count);
|
void SetEffectCount(std::size_t count);
|
||||||
void ResetEffectProcessingOrder();
|
void ResetEffectProcessingOrder();
|
||||||
s32 GetEffectOrder(std::size_t i) const;
|
[[nodiscard]] s32 GetEffectOrder(std::size_t i) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<s32> effect_processing_order;
|
std::vector<s32> effect_processing_order;
|
||||||
|
@ -91,15 +91,15 @@ public:
|
||||||
void SortInfo();
|
void SortInfo();
|
||||||
bool TsortInfo(SplitterContext& splitter_context);
|
bool TsortInfo(SplitterContext& splitter_context);
|
||||||
|
|
||||||
std::size_t GetCount() const;
|
[[nodiscard]] std::size_t GetCount() const;
|
||||||
ServerMixInfo& GetInfo(std::size_t i);
|
[[nodiscard]] ServerMixInfo& GetInfo(std::size_t i);
|
||||||
const ServerMixInfo& GetInfo(std::size_t i) const;
|
[[nodiscard]] const ServerMixInfo& GetInfo(std::size_t i) const;
|
||||||
ServerMixInfo& GetSortedInfo(std::size_t i);
|
[[nodiscard]] ServerMixInfo& GetSortedInfo(std::size_t i);
|
||||||
const ServerMixInfo& GetSortedInfo(std::size_t i) const;
|
[[nodiscard]] const ServerMixInfo& GetSortedInfo(std::size_t i) const;
|
||||||
ServerMixInfo& GetFinalMixInfo();
|
[[nodiscard]] ServerMixInfo& GetFinalMixInfo();
|
||||||
const ServerMixInfo& GetFinalMixInfo() const;
|
[[nodiscard]] const ServerMixInfo& GetFinalMixInfo() const;
|
||||||
EdgeMatrix& GetEdgeMatrix();
|
[[nodiscard]] EdgeMatrix& GetEdgeMatrix();
|
||||||
const EdgeMatrix& GetEdgeMatrix() const;
|
[[nodiscard]] const EdgeMatrix& GetEdgeMatrix() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CalcMixBufferOffset();
|
void CalcMixBufferOffset();
|
||||||
|
|
|
@ -12,10 +12,16 @@ std::size_t SinkContext::GetCount() const {
|
||||||
return sink_count;
|
return sink_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SinkContext::UpdateMainSink(SinkInfo::InParams& in) {
|
void SinkContext::UpdateMainSink(const SinkInfo::InParams& in) {
|
||||||
|
ASSERT(in.type == SinkTypes::Device);
|
||||||
|
|
||||||
|
downmix = in.device.down_matrix_enabled;
|
||||||
|
if (downmix) {
|
||||||
|
downmix_coefficients = in.device.down_matrix_coef;
|
||||||
|
}
|
||||||
in_use = in.in_use;
|
in_use = in.in_use;
|
||||||
use_count = in.device.input_count;
|
use_count = in.device.input_count;
|
||||||
std::memcpy(buffers.data(), in.device.input.data(), AudioCommon::MAX_CHANNEL_COUNT);
|
buffers = in.device.input;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SinkContext::InUse() const {
|
bool SinkContext::InUse() const {
|
||||||
|
@ -28,4 +34,12 @@ std::vector<u8> SinkContext::OutputBuffers() const {
|
||||||
return buffer_ret;
|
return buffer_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SinkContext::HasDownMixingCoefficients() const {
|
||||||
|
return downmix;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::array<float_le, 4>& SinkContext::GetDownmixCoefficients() const {
|
||||||
|
return downmix_coefficients;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace AudioCore
|
} // namespace AudioCore
|
||||||
|
|
|
@ -74,16 +74,21 @@ public:
|
||||||
explicit SinkContext(std::size_t sink_count);
|
explicit SinkContext(std::size_t sink_count);
|
||||||
~SinkContext();
|
~SinkContext();
|
||||||
|
|
||||||
std::size_t GetCount() const;
|
[[nodiscard]] std::size_t GetCount() const;
|
||||||
|
|
||||||
void UpdateMainSink(SinkInfo::InParams& in);
|
void UpdateMainSink(const SinkInfo::InParams& in);
|
||||||
bool InUse() const;
|
[[nodiscard]] bool InUse() const;
|
||||||
std::vector<u8> OutputBuffers() const;
|
[[nodiscard]] std::vector<u8> OutputBuffers() const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool HasDownMixingCoefficients() const;
|
||||||
|
[[nodiscard]] const std::array<float_le, 4>& GetDownmixCoefficients() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool in_use{false};
|
bool in_use{false};
|
||||||
s32 use_count{};
|
s32 use_count{};
|
||||||
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
|
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
|
||||||
std::size_t sink_count{};
|
std::size_t sink_count{};
|
||||||
|
bool downmix{false};
|
||||||
|
std::array<float_le, 4> downmix_coefficients{};
|
||||||
};
|
};
|
||||||
} // namespace AudioCore
|
} // namespace AudioCore
|
||||||
|
|
|
@ -136,4 +136,13 @@ std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count)
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers() {
|
||||||
|
std::vector<Buffer::Tag> tags;
|
||||||
|
while (!released_buffers.empty()) {
|
||||||
|
tags.push_back(released_buffers.front()->GetTag());
|
||||||
|
released_buffers.pop();
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace AudioCore
|
} // namespace AudioCore
|
||||||
|
|
|
@ -57,37 +57,40 @@ public:
|
||||||
bool QueueBuffer(BufferPtr&& buffer);
|
bool QueueBuffer(BufferPtr&& buffer);
|
||||||
|
|
||||||
/// Returns true if the audio stream contains a buffer with the specified tag
|
/// Returns true if the audio stream contains a buffer with the specified tag
|
||||||
bool ContainsBuffer(Buffer::Tag tag) const;
|
[[nodiscard]] bool ContainsBuffer(Buffer::Tag tag) const;
|
||||||
|
|
||||||
/// Returns a vector of recently released buffers specified by tag
|
/// Returns a vector of recently released buffers specified by tag
|
||||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
|
[[nodiscard]] std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
|
||||||
|
|
||||||
|
/// Returns a vector of all recently released buffers specified by tag
|
||||||
|
[[nodiscard]] std::vector<Buffer::Tag> GetTagsAndReleaseBuffers();
|
||||||
|
|
||||||
void SetVolume(float volume);
|
void SetVolume(float volume);
|
||||||
|
|
||||||
float GetVolume() const {
|
[[nodiscard]] float GetVolume() const {
|
||||||
return game_volume;
|
return game_volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the stream is currently playing
|
/// Returns true if the stream is currently playing
|
||||||
bool IsPlaying() const {
|
[[nodiscard]] bool IsPlaying() const {
|
||||||
return state == State::Playing;
|
return state == State::Playing;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of queued buffers
|
/// Returns the number of queued buffers
|
||||||
std::size_t GetQueueSize() const {
|
[[nodiscard]] std::size_t GetQueueSize() const {
|
||||||
return queued_buffers.size();
|
return queued_buffers.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the sample rate
|
/// Gets the sample rate
|
||||||
u32 GetSampleRate() const {
|
[[nodiscard]] u32 GetSampleRate() const {
|
||||||
return sample_rate;
|
return sample_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the number of channels
|
/// Gets the number of channels
|
||||||
u32 GetNumChannels() const;
|
[[nodiscard]] u32 GetNumChannels() const;
|
||||||
|
|
||||||
/// Get the state
|
/// Get the state
|
||||||
State GetState() const;
|
[[nodiscard]] State GetState() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Plays the next queued buffer in the audio stream, starting playback if necessary
|
/// Plays the next queued buffer in the audio stream, starting playback if necessary
|
||||||
|
@ -97,7 +100,7 @@ private:
|
||||||
void ReleaseActiveBuffer(std::chrono::nanoseconds ns_late = {});
|
void ReleaseActiveBuffer(std::chrono::nanoseconds ns_late = {});
|
||||||
|
|
||||||
/// Gets the number of core cycles when the specified buffer will be released
|
/// Gets the number of core cycles when the specified buffer will be released
|
||||||
std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const;
|
[[nodiscard]] std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const;
|
||||||
|
|
||||||
u32 sample_rate; ///< Sample rate of the stream
|
u32 sample_rate; ///< Sample rate of the stream
|
||||||
Format format; ///< Format of the stream
|
Format format; ///< Format of the stream
|
||||||
|
|
Loading…
Reference in a new issue