diff options
Diffstat (limited to '')
-rw-r--r-- | src/audio_core/audio_out.cpp | 4 | ||||
-rw-r--r-- | src/audio_core/audio_out.h | 3 | ||||
-rw-r--r-- | src/audio_core/audio_renderer.cpp | 110 | ||||
-rw-r--r-- | src/audio_core/audio_renderer.h | 20 | ||||
-rw-r--r-- | src/audio_core/behavior_info.h | 30 | ||||
-rw-r--r-- | src/audio_core/command_generator.h | 14 | ||||
-rw-r--r-- | src/audio_core/common.h | 2 | ||||
-rw-r--r-- | src/audio_core/effect_context.h | 26 | ||||
-rw-r--r-- | src/audio_core/mix_context.h | 26 | ||||
-rw-r--r-- | src/audio_core/sink_context.cpp | 18 | ||||
-rw-r--r-- | src/audio_core/sink_context.h | 17 | ||||
-rw-r--r-- | src/audio_core/stream.cpp | 10 | ||||
-rw-r--r-- | src/audio_core/stream.h | 21 | ||||
-rw-r--r-- | src/core/cpu_manager.cpp | 24 | ||||
-rw-r--r-- | src/yuzu/applets/controller.cpp | 123 | ||||
-rw-r--r-- | src/yuzu/applets/controller.h | 17 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_input_player.cpp | 138 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_input_player.h | 13 |
18 files changed, 407 insertions, 209 deletions
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp index 8619a3f03..fe3a898ad 100644 --- a/src/audio_core/audio_out.cpp +++ b/src/audio_core/audio_out.cpp @@ -43,6 +43,10 @@ std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, return stream->GetTagsAndReleaseBuffers(max_count); } +std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream) { + return stream->GetTagsAndReleaseBuffers(); +} + void AudioOut::StartStream(StreamPtr stream) { stream->Play(); } diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h index b07588287..6ce08cd0d 100644 --- a/src/audio_core/audio_out.h +++ b/src/audio_core/audio_out.h @@ -31,6 +31,9 @@ public: /// 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); + /// 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 void StartStream(StreamPtr stream); diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index a7e851bb8..e1ded84e0 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <limits> #include <vector> #include "audio_core/audio_out.h" @@ -14,6 +15,59 @@ #include "core/memory.h" #include "core/settings.h" +namespace { +[[nodiscard]] static constexpr s16 ClampToS16(s32 value) { + return static_cast<s16>(std::clamp(value, s32{std::numeric_limits<s16>::min()}, + 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 { AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, AudioCommon::AudioRendererParameter params, @@ -62,10 +116,6 @@ Stream::State AudioRenderer::GetStreamState() const { 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, 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, - splitter_context, effect_context); + const auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count, + splitter_context, effect_context); if (mix_result.IsError()) { 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++) { if (channel_count == 1) { const auto sample = ClampToS16(mix_buffers[0][i]); - buffer[i * stream_channel_count + 0] = sample; - if (stream_channel_count > 1) { - buffer[i * stream_channel_count + 1] = sample; + + // Place sample in all channels + for (u32 channel = 0; channel < stream_channel_count; channel++) { + buffer[i * stream_channel_count + channel] = sample; } + if (stream_channel_count == 6) { - buffer[i * stream_channel_count + 2] = sample; - buffer[i * stream_channel_count + 4] = sample; - buffer[i * stream_channel_count + 5] = sample; + // Output stream has a LF channel, mute it! + buffer[i * stream_channel_count + 3] = 0; } + } else if (channel_count == 2) { const auto l_sample = ClampToS16(mix_buffers[0][i]); const auto r_sample = ClampToS16(mix_buffers[1][i]); 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) { buffer[i * stream_channel_count + 0] = l_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 + 1] = r_sample; - buffer[i * stream_channel_count + 2] = - ClampToS16((static_cast<s32>(l_sample) + static_cast<s32>(r_sample)) / 2); + // Combine both left and right channels to the center channel + buffer[i * stream_channel_count + 2] = Mix2To1(l_sample, r_sample); buffer[i * stream_channel_count + 4] = l_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]); 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) { - buffer[i * stream_channel_count + 0] = - static_cast<s16>(0.3694f * static_cast<float>(fl_sample) + - 0.2612f * static_cast<float>(fc_sample) + - 0.3694f * static_cast<float>(bl_sample)); - buffer[i * stream_channel_count + 1] = - static_cast<s16>(0.3694f * static_cast<float>(fr_sample) + - 0.2612f * static_cast<float>(fc_sample) + - 0.3694f * static_cast<float>(br_sample)); + // Mix all channels into 2 channels + if (sink_context.HasDownMixingCoefficients()) { + const auto [left, right] = Mix6To2WithCoefficients( + fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample, + sink_context.GetDownmixCoefficients()); + buffer[i * stream_channel_count + 0] = left; + buffer[i * stream_channel_count + 1] = right; + } 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) { + // Pass through buffer[i * stream_channel_count + 0] = fl_sample; buffer[i * stream_channel_count + 1] = fr_sample; buffer[i * stream_channel_count + 2] = fc_sample; @@ -259,7 +319,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { } 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) { QueueMixedBuffer(tag); } diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index 2fd93e058..a85219045 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h @@ -36,16 +36,10 @@ class Memory; } namespace AudioCore { -using DSPStateHolder = std::array<VoiceState*, 6>; +using DSPStateHolder = std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>; 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 { public: 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); ~AudioRenderer(); - ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params, - std::vector<u8>& output_params); + [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params, + std::vector<u8>& output_params); void QueueMixedBuffer(Buffer::Tag tag); void ReleaseAndQueueBuffers(); - u32 GetSampleRate() const; - u32 GetSampleCount() const; - u32 GetMixBufferCount() const; - Stream::State GetStreamState() const; + [[nodiscard]] u32 GetSampleRate() const; + [[nodiscard]] u32 GetSampleCount() const; + [[nodiscard]] u32 GetMixBufferCount() const; + [[nodiscard]] Stream::State GetStreamState() const; private: BehaviorInfo behavior_info{}; diff --git a/src/audio_core/behavior_info.h b/src/audio_core/behavior_info.h index 512a4ebe3..5a96bf75e 100644 --- a/src/audio_core/behavior_info.h +++ b/src/audio_core/behavior_info.h @@ -43,22 +43,22 @@ public: void ClearError(); void UpdateFlags(u64_le dest_flags); void SetUserRevision(u32_le revision); - u32_le GetUserRevision() const; - u32_le GetProcessRevision() const; + [[nodiscard]] u32_le GetUserRevision() const; + [[nodiscard]] u32_le GetProcessRevision() const; - bool IsAdpcmLoopContextBugFixed() const; - bool IsSplitterSupported() const; - bool IsLongSizePreDelaySupported() const; - bool IsAudioRendererProcessingTimeLimit80PercentSupported() const; - bool IsAudioRendererProcessingTimeLimit75PercentSupported() const; - bool IsAudioRendererProcessingTimeLimit70PercentSupported() const; - bool IsElapsedFrameCountSupported() const; - bool IsMemoryPoolForceMappingEnabled() const; - bool IsFlushVoiceWaveBuffersSupported() const; - bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const; - bool IsVoicePitchAndSrcSkippedSupported() const; - bool IsMixInParameterDirtyOnlyUpdateSupported() const; - bool IsSplitterBugFixed() const; + [[nodiscard]] bool IsAdpcmLoopContextBugFixed() const; + [[nodiscard]] bool IsSplitterSupported() const; + [[nodiscard]] bool IsLongSizePreDelaySupported() const; + [[nodiscard]] bool IsAudioRendererProcessingTimeLimit80PercentSupported() const; + [[nodiscard]] bool IsAudioRendererProcessingTimeLimit75PercentSupported() const; + [[nodiscard]] bool IsAudioRendererProcessingTimeLimit70PercentSupported() const; + [[nodiscard]] bool IsElapsedFrameCountSupported() const; + [[nodiscard]] bool IsMemoryPoolForceMappingEnabled() const; + [[nodiscard]] bool IsFlushVoiceWaveBuffersSupported() const; + [[nodiscard]] bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const; + [[nodiscard]] bool IsVoicePitchAndSrcSkippedSupported() const; + [[nodiscard]] bool IsMixInParameterDirtyOnlyUpdateSupported() const; + [[nodiscard]] bool IsSplitterBugFixed() const; void CopyErrorInfo(OutParams& dst); private: diff --git a/src/audio_core/command_generator.h b/src/audio_core/command_generator.h index 53e57748b..87ece00c4 100644 --- a/src/audio_core/command_generator.h +++ b/src/audio_core/command_generator.h @@ -39,13 +39,13 @@ public: void PreCommand(); void PostCommand(); - s32* GetChannelMixBuffer(s32 channel); - const s32* GetChannelMixBuffer(s32 channel) const; - s32* GetMixBuffer(std::size_t index); - const s32* GetMixBuffer(std::size_t index) const; - std::size_t GetMixChannelBufferOffset(s32 channel) const; + [[nodiscard]] s32* GetChannelMixBuffer(s32 channel); + [[nodiscard]] const s32* GetChannelMixBuffer(s32 channel) const; + [[nodiscard]] s32* GetMixBuffer(std::size_t index); + [[nodiscard]] const s32* GetMixBuffer(std::size_t index) const; + [[nodiscard]] std::size_t GetMixChannelBufferOffset(s32 channel) const; - std::size_t GetTotalMixBufferCount() const; + [[nodiscard]] std::size_t GetTotalMixBufferCount() const; private: 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 GenerateBiquadFilterEffectCommand(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, u32 sample_count, u32 write_offset, u32 write_count); diff --git a/src/audio_core/common.h b/src/audio_core/common.h index 7b4a1e9e8..ec59a3ba9 100644 --- a/src/audio_core/common.h +++ b/src/audio_core/common.h @@ -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_SAMPLE_HISTORY = 4; 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_MIX = 0x7fffffff; constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min(); diff --git a/src/audio_core/effect_context.h b/src/audio_core/effect_context.h index 2c4ce53ef..03c5a0f04 100644 --- a/src/audio_core/effect_context.h +++ b/src/audio_core/effect_context.h @@ -189,11 +189,11 @@ public: virtual void Update(EffectInfo::InParams& in_params) = 0; virtual void UpdateForCommandGeneration() = 0; - UsageState GetUsage() const; - EffectType GetType() const; - bool IsEnabled() const; - s32 GetMixID() const; - s32 GetProcessingOrder() const; + [[nodiscard]] UsageState GetUsage() const; + [[nodiscard]] EffectType GetType() const; + [[nodiscard]] bool IsEnabled() const; + [[nodiscard]] s32 GetMixID() const; + [[nodiscard]] s32 GetProcessingOrder() const; protected: UsageState usage{UsageState::Invalid}; @@ -257,10 +257,10 @@ public: void Update(EffectInfo::InParams& in_params) override; void UpdateForCommandGeneration() override; - VAddr GetSendInfo() const; - VAddr GetSendBuffer() const; - VAddr GetRecvInfo() const; - VAddr GetRecvBuffer() const; + [[nodiscard]] VAddr GetSendInfo() const; + [[nodiscard]] VAddr GetSendBuffer() const; + [[nodiscard]] VAddr GetRecvInfo() const; + [[nodiscard]] VAddr GetRecvBuffer() const; private: VAddr send_info{}; @@ -309,10 +309,10 @@ public: explicit EffectContext(std::size_t effect_count); ~EffectContext(); - std::size_t GetCount() const; - EffectBase* GetInfo(std::size_t i); - EffectBase* RetargetEffect(std::size_t i, EffectType effect); - const EffectBase* GetInfo(std::size_t i) const; + [[nodiscard]] std::size_t GetCount() const; + [[nodiscard]] EffectBase* GetInfo(std::size_t i); + [[nodiscard]] EffectBase* RetargetEffect(std::size_t i, EffectType effect); + [[nodiscard]] const EffectBase* GetInfo(std::size_t i) const; private: std::size_t effect_count{}; diff --git a/src/audio_core/mix_context.h b/src/audio_core/mix_context.h index 6a588eeb4..68bc673c6 100644 --- a/src/audio_core/mix_context.h +++ b/src/audio_core/mix_context.h @@ -62,17 +62,17 @@ public: ServerMixInfo(); ~ServerMixInfo(); - const ServerMixInfo::InParams& GetInParams() const; - ServerMixInfo::InParams& GetInParams(); + [[nodiscard]] const ServerMixInfo::InParams& GetInParams() const; + [[nodiscard]] ServerMixInfo::InParams& GetInParams(); bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, BehaviorInfo& behavior_info, SplitterContext& splitter_context, EffectContext& effect_context); - bool HasAnyConnection() const; + [[nodiscard]] bool HasAnyConnection() const; void Cleanup(); void SetEffectCount(std::size_t count); void ResetEffectProcessingOrder(); - s32 GetEffectOrder(std::size_t i) const; + [[nodiscard]] s32 GetEffectOrder(std::size_t i) const; private: std::vector<s32> effect_processing_order; @@ -91,15 +91,15 @@ public: void SortInfo(); bool TsortInfo(SplitterContext& splitter_context); - std::size_t GetCount() const; - ServerMixInfo& GetInfo(std::size_t i); - const ServerMixInfo& GetInfo(std::size_t i) const; - ServerMixInfo& GetSortedInfo(std::size_t i); - const ServerMixInfo& GetSortedInfo(std::size_t i) const; - ServerMixInfo& GetFinalMixInfo(); - const ServerMixInfo& GetFinalMixInfo() const; - EdgeMatrix& GetEdgeMatrix(); - const EdgeMatrix& GetEdgeMatrix() const; + [[nodiscard]] std::size_t GetCount() const; + [[nodiscard]] ServerMixInfo& GetInfo(std::size_t i); + [[nodiscard]] const ServerMixInfo& GetInfo(std::size_t i) const; + [[nodiscard]] ServerMixInfo& GetSortedInfo(std::size_t i); + [[nodiscard]] const ServerMixInfo& GetSortedInfo(std::size_t i) const; + [[nodiscard]] ServerMixInfo& GetFinalMixInfo(); + [[nodiscard]] const ServerMixInfo& GetFinalMixInfo() const; + [[nodiscard]] EdgeMatrix& GetEdgeMatrix(); + [[nodiscard]] const EdgeMatrix& GetEdgeMatrix() const; private: void CalcMixBufferOffset(); diff --git a/src/audio_core/sink_context.cpp b/src/audio_core/sink_context.cpp index 0882b411a..b29b47890 100644 --- a/src/audio_core/sink_context.cpp +++ b/src/audio_core/sink_context.cpp @@ -12,10 +12,16 @@ std::size_t SinkContext::GetCount() const { return sink_count; } -void SinkContext::UpdateMainSink(SinkInfo::InParams& in) { +void SinkContext::UpdateMainSink(const SinkInfo::InParams& in) { + ASSERT(in.type == SinkTypes::Device); + + has_downmix_coefs = in.device.down_matrix_enabled; + if (has_downmix_coefs) { + downmix_coefficients = in.device.down_matrix_coef; + } in_use = in.in_use; 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 { @@ -28,4 +34,12 @@ std::vector<u8> SinkContext::OutputBuffers() const { return buffer_ret; } +bool SinkContext::HasDownMixingCoefficients() const { + return has_downmix_coefs; +} + +const DownmixCoefficients& SinkContext::GetDownmixCoefficients() const { + return downmix_coefficients; +} + } // namespace AudioCore diff --git a/src/audio_core/sink_context.h b/src/audio_core/sink_context.h index d7aa72ba7..e2e7880b7 100644 --- a/src/audio_core/sink_context.h +++ b/src/audio_core/sink_context.h @@ -11,6 +11,8 @@ namespace AudioCore { +using DownmixCoefficients = std::array<float_le, 4>; + enum class SinkTypes : u8 { Invalid = 0, Device = 1, @@ -50,7 +52,7 @@ public: std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input; INSERT_UNION_PADDING_BYTES(1); bool down_matrix_enabled; - std::array<float_le, 4> down_matrix_coef; + DownmixCoefficients down_matrix_coef; }; static_assert(sizeof(SinkInfo::DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size"); @@ -74,16 +76,21 @@ public: explicit SinkContext(std::size_t sink_count); ~SinkContext(); - std::size_t GetCount() const; + [[nodiscard]] std::size_t GetCount() const; + + void UpdateMainSink(const SinkInfo::InParams& in); + [[nodiscard]] bool InUse() const; + [[nodiscard]] std::vector<u8> OutputBuffers() const; - void UpdateMainSink(SinkInfo::InParams& in); - bool InUse() const; - std::vector<u8> OutputBuffers() const; + [[nodiscard]] bool HasDownMixingCoefficients() const; + [[nodiscard]] const DownmixCoefficients& GetDownmixCoefficients() const; private: bool in_use{false}; s32 use_count{}; std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{}; std::size_t sink_count{}; + bool has_downmix_coefs{false}; + DownmixCoefficients downmix_coefficients{}; }; } // namespace AudioCore diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index 4bbb1e0c4..41bc2f4d6 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -136,4 +136,14 @@ std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) return tags; } +std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers() { + std::vector<Buffer::Tag> tags; + tags.reserve(released_buffers.size()); + while (!released_buffers.empty()) { + tags.push_back(released_buffers.front()->GetTag()); + released_buffers.pop(); + } + return tags; +} + } // namespace AudioCore diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index 6437b8591..71c2d0b4f 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h @@ -57,37 +57,40 @@ public: bool QueueBuffer(BufferPtr&& buffer); /// 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 - 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); - float GetVolume() const { + [[nodiscard]] float GetVolume() const { return game_volume; } /// Returns true if the stream is currently playing - bool IsPlaying() const { + [[nodiscard]] bool IsPlaying() const { return state == State::Playing; } /// Returns the number of queued buffers - std::size_t GetQueueSize() const { + [[nodiscard]] std::size_t GetQueueSize() const { return queued_buffers.size(); } /// Gets the sample rate - u32 GetSampleRate() const { + [[nodiscard]] u32 GetSampleRate() const { return sample_rate; } /// Gets the number of channels - u32 GetNumChannels() const; + [[nodiscard]] u32 GetNumChannels() const; /// Get the state - State GetState() const; + [[nodiscard]] State GetState() const; private: /// 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 = {}); /// 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 Format format; ///< Format of the stream diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 983210197..100e90d82 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -4,6 +4,7 @@ #include "common/fiber.h" #include "common/microprofile.h" +#include "common/scope_exit.h" #include "common/thread.h" #include "core/arm/exclusive_monitor.h" #include "core/core.h" @@ -343,6 +344,16 @@ void CpuManager::RunThread(std::size_t core) { data.initialized = true; const bool sc_sync = !is_async_gpu && !is_multicore; bool sc_sync_first_use = sc_sync; + + // Cleanup + SCOPE_EXIT({ + data.host_context->Exit(); + data.enter_barrier.reset(); + data.exit_barrier.reset(); + data.initialized = false; + MicroProfileOnThreadExit(); + }); + /// Running while (running_mode) { data.is_running = false; @@ -351,6 +362,12 @@ void CpuManager::RunThread(std::size_t core) { system.GPU().ObtainContext(); sc_sync_first_use = false; } + + // Abort if emulation was killed before the session really starts + if (!system.IsPoweredOn()) { + return; + } + auto& scheduler = system.Kernel().CurrentScheduler(); Kernel::Thread* current_thread = scheduler.GetCurrentThread(); data.is_running = true; @@ -360,13 +377,6 @@ void CpuManager::RunThread(std::size_t core) { data.exit_barrier->Wait(); data.is_paused = false; } - /// Time to cleanup - data.host_context->Exit(); - data.enter_barrier.reset(); - data.exit_barrier.reset(); - data.initialized = false; - - MicroProfileOnThreadExit(); } } // namespace Core diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index 8ecfec770..6944478f3 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -72,40 +72,6 @@ bool IsControllerCompatible(Settings::ControllerType controller_type, } } -/// Maps the controller type combobox index to Controller Type enum -constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) { - switch (index) { - case 0: - default: - return Settings::ControllerType::ProController; - case 1: - return Settings::ControllerType::DualJoyconDetached; - case 2: - return Settings::ControllerType::LeftJoycon; - case 3: - return Settings::ControllerType::RightJoycon; - case 4: - return Settings::ControllerType::Handheld; - } -} - -/// Maps the Controller Type enum to controller type combobox index -constexpr int GetIndexFromControllerType(Settings::ControllerType type) { - switch (type) { - case Settings::ControllerType::ProController: - default: - return 0; - case Settings::ControllerType::DualJoyconDetached: - return 1; - case Settings::ControllerType::LeftJoycon: - return 2; - case Settings::ControllerType::RightJoycon: - return 3; - case Settings::ControllerType::Handheld: - return 4; - } -} - } // namespace QtControllerSelectorDialog::QtControllerSelectorDialog( @@ -184,6 +150,11 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( // This avoids unintentionally changing the states of elements while loading them in. SetSupportedControllers(); DisableUnsupportedPlayers(); + + for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) { + SetEmulatedControllers(player_index); + } + LoadConfiguration(); for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { @@ -223,8 +194,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( if (i == 0) { connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged), - [this](int index) { - UpdateDockedState(GetControllerTypeFromIndex(index) == + [this, i](int index) { + UpdateDockedState(GetControllerTypeFromIndex(index, i) == Settings::ControllerType::Handheld); }); } @@ -281,8 +252,8 @@ void QtControllerSelectorDialog::LoadConfiguration() { (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected); player_groupboxes[index]->setChecked(connected); connected_controller_checkboxes[index]->setChecked(connected); - emulated_controllers[index]->setCurrentIndex( - GetIndexFromControllerType(Settings::values.players.GetValue()[index].controller_type)); + emulated_controllers[index]->setCurrentIndex(GetIndexFromControllerType( + Settings::values.players.GetValue()[index].controller_type, index)); } UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected); @@ -338,7 +309,7 @@ bool QtControllerSelectorDialog::CheckIfParametersMet() { } const auto compatible = IsControllerCompatible( - GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex()), + GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex(), index), parameters); // If any controller is found to be incompatible, return false early. @@ -422,6 +393,63 @@ void QtControllerSelectorDialog::SetSupportedControllers() { } } +void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index) { + auto& pairs = index_controller_type_pairs[player_index]; + + pairs.clear(); + emulated_controllers[player_index]->clear(); + + pairs.emplace_back(emulated_controllers[player_index]->count(), + Settings::ControllerType::ProController); + emulated_controllers[player_index]->addItem(tr("Pro Controller")); + + pairs.emplace_back(emulated_controllers[player_index]->count(), + Settings::ControllerType::DualJoyconDetached); + emulated_controllers[player_index]->addItem(tr("Dual Joycons")); + + pairs.emplace_back(emulated_controllers[player_index]->count(), + Settings::ControllerType::LeftJoycon); + emulated_controllers[player_index]->addItem(tr("Left Joycon")); + + pairs.emplace_back(emulated_controllers[player_index]->count(), + Settings::ControllerType::RightJoycon); + emulated_controllers[player_index]->addItem(tr("Right Joycon")); + + if (player_index == 0) { + pairs.emplace_back(emulated_controllers[player_index]->count(), + Settings::ControllerType::Handheld); + emulated_controllers[player_index]->addItem(tr("Handheld")); + } +} + +Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex( + int index, std::size_t player_index) const { + const auto& pairs = index_controller_type_pairs[player_index]; + + const auto it = std::find_if(pairs.begin(), pairs.end(), + [index](const auto& pair) { return pair.first == index; }); + + if (it == pairs.end()) { + return Settings::ControllerType::ProController; + } + + return it->second; +} + +int QtControllerSelectorDialog::GetIndexFromControllerType(Settings::ControllerType type, + std::size_t player_index) const { + const auto& pairs = index_controller_type_pairs[player_index]; + + const auto it = std::find_if(pairs.begin(), pairs.end(), + [type](const auto& pair) { return pair.second == type; }); + + if (it == pairs.end()) { + return 0; + } + + return it->first; +} + void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) { if (!player_groupboxes[player_index]->isChecked()) { connected_controller_icons[player_index]->setStyleSheet(QString{}); @@ -430,7 +458,8 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) } const QString stylesheet = [this, player_index] { - switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex())) { + switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), + player_index)) { case Settings::ControllerType::ProController: return QStringLiteral("image: url(:/controller/applet_pro_controller%0); "); case Settings::ControllerType::DualJoyconDetached: @@ -446,6 +475,12 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) } }(); + if (stylesheet.isEmpty()) { + connected_controller_icons[player_index]->setStyleSheet(QString{}); + player_labels[player_index]->show(); + return; + } + const QString theme = [] { if (QIcon::themeName().contains(QStringLiteral("dark"))) { return QStringLiteral("_dark"); @@ -463,8 +498,8 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) { auto& player = Settings::values.players.GetValue()[player_index]; - const auto controller_type = - GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()); + const auto controller_type = GetControllerTypeFromIndex( + emulated_controllers[player_index]->currentIndex(), player_index); const auto player_connected = player_groupboxes[player_index]->isChecked() && controller_type != Settings::ControllerType::Handheld; @@ -507,8 +542,8 @@ void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { if (!player_groupboxes[player_index]->isChecked() || - GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()) == - Settings::ControllerType::Handheld) { + GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), + player_index) == Settings::ControllerType::Handheld) { led_patterns_boxes[player_index][0]->setChecked(false); led_patterns_boxes[player_index][1]->setChecked(false); led_patterns_boxes[player_index][2]->setChecked(false); diff --git a/src/yuzu/applets/controller.h b/src/yuzu/applets/controller.h index 4344e1dd0..7a421d856 100644 --- a/src/yuzu/applets/controller.h +++ b/src/yuzu/applets/controller.h @@ -22,6 +22,10 @@ namespace InputCommon { class InputSubsystem; } +namespace Settings { +enum class ControllerType; +} + namespace Ui { class QtControllerSelectorDialog; } @@ -57,6 +61,15 @@ private: // Sets the controller icons for "Supported Controller Types". void SetSupportedControllers(); + // Sets the emulated controllers per player. + void SetEmulatedControllers(std::size_t player_index); + + // Gets the Controller Type for a given controller combobox index per player. + Settings::ControllerType GetControllerTypeFromIndex(int index, std::size_t player_index) const; + + // Gets the controller combobox index for a given Controller Type per player. + int GetIndexFromControllerType(Settings::ControllerType type, std::size_t player_index) const; + // Updates the controller icons per player. void UpdateControllerIcon(std::size_t player_index); @@ -114,6 +127,10 @@ private: // Comboboxes with a list of emulated controllers per player. std::array<QComboBox*, NUM_PLAYERS> emulated_controllers; + /// Pairs of emulated controller index and Controller Type enum per player. + std::array<std::vector<std::pair<int, Settings::ControllerType>>, NUM_PLAYERS> + index_controller_type_pairs; + // Labels representing the number of connected controllers // above the "Connected Controllers" checkboxes. std::array<QLabel*, NUM_PLAYERS> connected_controller_labels; diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 56ab32a35..918bfb56b 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -27,6 +27,8 @@ #include "yuzu/configuration/input_profiles.h" #include "yuzu/util/limitable_input_dialog.h" +using namespace Service::HID; + const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> ConfigureInputPlayer::analog_sub_buttons{{ "up", @@ -47,48 +49,12 @@ void UpdateController(Settings::ControllerType controller_type, std::size_t npad } Service::SM::ServiceManager& sm = system.ServiceManager(); - auto& npad = - sm.GetService<Service::HID::Hid>("hid") - ->GetAppletResource() - ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad); + auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>( + HidController::NPad); npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected); } -/// Maps the controller type combobox index to Controller Type enum -constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) { - switch (index) { - case 0: - default: - return Settings::ControllerType::ProController; - case 1: - return Settings::ControllerType::DualJoyconDetached; - case 2: - return Settings::ControllerType::LeftJoycon; - case 3: - return Settings::ControllerType::RightJoycon; - case 4: - return Settings::ControllerType::Handheld; - } -} - -/// Maps the Controller Type enum to controller type combobox index -constexpr int GetIndexFromControllerType(Settings::ControllerType type) { - switch (type) { - case Settings::ControllerType::ProController: - default: - return 0; - case Settings::ControllerType::DualJoyconDetached: - return 1; - case Settings::ControllerType::LeftJoycon: - return 2; - case Settings::ControllerType::RightJoycon: - return 3; - case Settings::ControllerType::Handheld: - return 4; - } -} - QString GetKeyName(int key_code) { switch (key_code) { case Qt::LeftButton: @@ -453,18 +419,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i connect(ui->groupConnectedController, &QGroupBox::toggled, [this](bool checked) { emit Connected(checked); }); - // Set up controller type. Only Player 1 can choose Handheld. - ui->comboControllerType->clear(); - - QStringList controller_types = { - tr("Pro Controller"), - tr("Dual Joycons"), - tr("Left Joycon"), - tr("Right Joycon"), - }; - if (player_index == 0) { - controller_types.append(tr("Handheld")); connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int index) { emit HandheldStateChanged(GetControllerTypeFromIndex(index) == @@ -480,12 +435,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i if (debug) { ui->buttonScreenshot->setEnabled(false); ui->buttonHome->setEnabled(false); - QStringList debug_controller_types = { - tr("Pro Controller"), - }; - ui->comboControllerType->addItems(debug_controller_types); + ui->comboControllerType->addItem(tr("Pro Controller")); } else { - ui->comboControllerType->addItems(controller_types); + SetConnectableControllers(); } UpdateControllerIcon(); @@ -667,7 +619,7 @@ void ConfigureInputPlayer::LoadConfiguration() { return; } - ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type)); + ui->comboControllerType->setCurrentIndex(GetIndexFromControllerType(player.controller_type)); ui->groupConnectedController->setChecked( player.connected || (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected)); @@ -841,6 +793,82 @@ void ConfigureInputPlayer::UpdateUI() { } } +void ConfigureInputPlayer::SetConnectableControllers() { + const auto add_controllers = [this](bool enable_all, + Controller_NPad::NpadStyleSet npad_style_set = {}) { + index_controller_type_pairs.clear(); + ui->comboControllerType->clear(); + + if (enable_all || npad_style_set.pro_controller == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Settings::ControllerType::ProController); + ui->comboControllerType->addItem(tr("Pro Controller")); + } + + if (enable_all || npad_style_set.joycon_dual == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Settings::ControllerType::DualJoyconDetached); + ui->comboControllerType->addItem(tr("Dual Joycons")); + } + + if (enable_all || npad_style_set.joycon_left == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Settings::ControllerType::LeftJoycon); + ui->comboControllerType->addItem(tr("Left Joycon")); + } + + if (enable_all || npad_style_set.joycon_right == 1) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Settings::ControllerType::RightJoycon); + ui->comboControllerType->addItem(tr("Right Joycon")); + } + + if (player_index == 0 && (enable_all || npad_style_set.handheld == 1)) { + index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), + Settings::ControllerType::Handheld); + ui->comboControllerType->addItem(tr("Handheld")); + } + }; + + Core::System& system{Core::System::GetInstance()}; + + if (!system.IsPoweredOn()) { + add_controllers(true); + return; + } + + Service::SM::ServiceManager& sm = system.ServiceManager(); + + auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>( + HidController::NPad); + + add_controllers(false, npad.GetSupportedStyleSet()); +} + +Settings::ControllerType ConfigureInputPlayer::GetControllerTypeFromIndex(int index) const { + const auto it = + std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(), + [index](const auto& pair) { return pair.first == index; }); + + if (it == index_controller_type_pairs.end()) { + return Settings::ControllerType::ProController; + } + + return it->second; +} + +int ConfigureInputPlayer::GetIndexFromControllerType(Settings::ControllerType type) const { + const auto it = + std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(), + [type](const auto& pair) { return pair.second == type; }); + + if (it == index_controller_type_pairs.end()) { + return -1; + } + + return it->first; +} + void ConfigureInputPlayer::UpdateInputDevices() { input_devices = input_subsystem->GetInputDevices(); ui->comboDevices->clear(); diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index 23cf6f958..9c30879a2 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -9,6 +9,7 @@ #include <memory> #include <optional> #include <string> +#include <vector> #include <QWidget> @@ -112,6 +113,15 @@ private: /// Update UI to reflect current configuration. void UpdateUI(); + /// Sets the available controllers. + void SetConnectableControllers(); + + /// Gets the Controller Type for a given controller combobox index. + Settings::ControllerType GetControllerTypeFromIndex(int index) const; + + /// Gets the controller combobox index for a given Controller Type. + int GetIndexFromControllerType(Settings::ControllerType type) const; + /// Update the available input devices. void UpdateInputDevices(); @@ -151,6 +161,9 @@ private: std::unique_ptr<QTimer> timeout_timer; std::unique_ptr<QTimer> poll_timer; + /// Stores a pair of "Connected Controllers" combobox index and Controller Type enum. + std::vector<std::pair<int, Settings::ControllerType>> index_controller_type_pairs; + static constexpr int PLAYER_COUNT = 8; std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox; |