summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/audio_out.cpp4
-rw-r--r--src/audio_core/audio_out.h3
-rw-r--r--src/audio_core/audio_renderer.cpp110
-rw-r--r--src/audio_core/audio_renderer.h20
-rw-r--r--src/audio_core/behavior_info.h30
-rw-r--r--src/audio_core/command_generator.h14
-rw-r--r--src/audio_core/common.h2
-rw-r--r--src/audio_core/effect_context.h26
-rw-r--r--src/audio_core/mix_context.h26
-rw-r--r--src/audio_core/sink_context.cpp18
-rw-r--r--src/audio_core/sink_context.h17
-rw-r--r--src/audio_core/stream.cpp10
-rw-r--r--src/audio_core/stream.h21
-rw-r--r--src/core/cpu_manager.cpp24
-rw-r--r--src/yuzu/applets/controller.cpp123
-rw-r--r--src/yuzu/applets/controller.h17
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp138
-rw-r--r--src/yuzu/configuration/configure_input_player.h13
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,
43 return stream->GetTagsAndReleaseBuffers(max_count); 43 return stream->GetTagsAndReleaseBuffers(max_count);
44} 44}
45 45
46std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream) {
47 return stream->GetTagsAndReleaseBuffers();
48}
49
46void AudioOut::StartStream(StreamPtr stream) { 50void AudioOut::StartStream(StreamPtr stream) {
47 stream->Play(); 51 stream->Play();
48} 52}
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:
31 /// Returns a vector of recently released buffers specified by tag for the specified stream 31 /// Returns a vector of recently released buffers specified by tag for the specified stream
32 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count); 32 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
33 33
34 /// Returns a vector of all recently released buffers specified by tag for the specified stream
35 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream);
36
34 /// Starts an audio stream for playback 37 /// Starts an audio stream for playback
35 void StartStream(StreamPtr stream); 38 void StartStream(StreamPtr stream);
36 39
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 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <limits>
5#include <vector> 6#include <vector>
6 7
7#include "audio_core/audio_out.h" 8#include "audio_core/audio_out.h"
@@ -14,6 +15,59 @@
14#include "core/memory.h" 15#include "core/memory.h"
15#include "core/settings.h" 16#include "core/settings.h"
16 17
18namespace {
19[[nodiscard]] static constexpr s16 ClampToS16(s32 value) {
20 return static_cast<s16>(std::clamp(value, s32{std::numeric_limits<s16>::min()},
21 s32{std::numeric_limits<s16>::max()}));
22}
23
24[[nodiscard]] static constexpr s16 Mix2To1(s16 l_channel, s16 r_channel) {
25 // Mix 50% from left and 50% from right channel
26 constexpr float l_mix_amount = 50.0f / 100.0f;
27 constexpr float r_mix_amount = 50.0f / 100.0f;
28 return ClampToS16(static_cast<s32>((static_cast<float>(l_channel) * l_mix_amount) +
29 (static_cast<float>(r_channel) * r_mix_amount)));
30}
31
32[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2(s16 fl_channel, s16 fr_channel,
33 s16 fc_channel,
34 [[maybe_unused]] s16 lf_channel,
35 s16 bl_channel, s16 br_channel) {
36 // Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels
37 // are mixed to be 36.94%
38
39 constexpr float front_mix_amount = 36.94f / 100.0f;
40 constexpr float center_mix_amount = 26.12f / 100.0f;
41 constexpr float back_mix_amount = 36.94f / 100.0f;
42
43 // Mix 50% from left and 50% from right channel
44 const auto left = front_mix_amount * static_cast<float>(fl_channel) +
45 center_mix_amount * static_cast<float>(fc_channel) +
46 back_mix_amount * static_cast<float>(bl_channel);
47
48 const auto right = front_mix_amount * static_cast<float>(fr_channel) +
49 center_mix_amount * static_cast<float>(fc_channel) +
50 back_mix_amount * static_cast<float>(br_channel);
51
52 return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
53}
54
55[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2WithCoefficients(
56 s16 fl_channel, s16 fr_channel, s16 fc_channel, s16 lf_channel, s16 bl_channel, s16 br_channel,
57 const std::array<float_le, 4>& coeff) {
58 const auto left =
59 static_cast<float>(fl_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
60 static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(bl_channel) * coeff[0];
61
62 const auto right =
63 static_cast<float>(fr_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
64 static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(br_channel) * coeff[0];
65
66 return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
67}
68
69} // namespace
70
17namespace AudioCore { 71namespace AudioCore {
18AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, 72AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
19 AudioCommon::AudioRendererParameter params, 73 AudioCommon::AudioRendererParameter params,
@@ -62,10 +116,6 @@ Stream::State AudioRenderer::GetStreamState() const {
62 return stream->GetState(); 116 return stream->GetState();
63} 117}
64 118
65static constexpr s16 ClampToS16(s32 value) {
66 return static_cast<s16>(std::clamp(value, -32768, 32767));
67}
68
69ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, 119ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
70 std::vector<u8>& output_params) { 120 std::vector<u8>& output_params) {
71 121
@@ -104,8 +154,8 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param
104 } 154 }
105 } 155 }
106 156
107 auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count, 157 const auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
108 splitter_context, effect_context); 158 splitter_context, effect_context);
109 159
110 if (mix_result.IsError()) { 160 if (mix_result.IsError()) {
111 LOG_ERROR(Audio, "Failed to update mix parameters"); 161 LOG_ERROR(Audio, "Failed to update mix parameters");
@@ -194,20 +244,22 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
194 for (std::size_t i = 0; i < BUFFER_SIZE; i++) { 244 for (std::size_t i = 0; i < BUFFER_SIZE; i++) {
195 if (channel_count == 1) { 245 if (channel_count == 1) {
196 const auto sample = ClampToS16(mix_buffers[0][i]); 246 const auto sample = ClampToS16(mix_buffers[0][i]);
197 buffer[i * stream_channel_count + 0] = sample; 247
198 if (stream_channel_count > 1) { 248 // Place sample in all channels
199 buffer[i * stream_channel_count + 1] = sample; 249 for (u32 channel = 0; channel < stream_channel_count; channel++) {
250 buffer[i * stream_channel_count + channel] = sample;
200 } 251 }
252
201 if (stream_channel_count == 6) { 253 if (stream_channel_count == 6) {
202 buffer[i * stream_channel_count + 2] = sample; 254 // Output stream has a LF channel, mute it!
203 buffer[i * stream_channel_count + 4] = sample; 255 buffer[i * stream_channel_count + 3] = 0;
204 buffer[i * stream_channel_count + 5] = sample;
205 } 256 }
257
206 } else if (channel_count == 2) { 258 } else if (channel_count == 2) {
207 const auto l_sample = ClampToS16(mix_buffers[0][i]); 259 const auto l_sample = ClampToS16(mix_buffers[0][i]);
208 const auto r_sample = ClampToS16(mix_buffers[1][i]); 260 const auto r_sample = ClampToS16(mix_buffers[1][i]);
209 if (stream_channel_count == 1) { 261 if (stream_channel_count == 1) {
210 buffer[i * stream_channel_count + 0] = l_sample; 262 buffer[i * stream_channel_count + 0] = Mix2To1(l_sample, r_sample);
211 } else if (stream_channel_count == 2) { 263 } else if (stream_channel_count == 2) {
212 buffer[i * stream_channel_count + 0] = l_sample; 264 buffer[i * stream_channel_count + 0] = l_sample;
213 buffer[i * stream_channel_count + 1] = r_sample; 265 buffer[i * stream_channel_count + 1] = r_sample;
@@ -215,8 +267,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
215 buffer[i * stream_channel_count + 0] = l_sample; 267 buffer[i * stream_channel_count + 0] = l_sample;
216 buffer[i * stream_channel_count + 1] = r_sample; 268 buffer[i * stream_channel_count + 1] = r_sample;
217 269
218 buffer[i * stream_channel_count + 2] = 270 // Combine both left and right channels to the center channel
219 ClampToS16((static_cast<s32>(l_sample) + static_cast<s32>(r_sample)) / 2); 271 buffer[i * stream_channel_count + 2] = Mix2To1(l_sample, r_sample);
220 272
221 buffer[i * stream_channel_count + 4] = l_sample; 273 buffer[i * stream_channel_count + 4] = l_sample;
222 buffer[i * stream_channel_count + 5] = r_sample; 274 buffer[i * stream_channel_count + 5] = r_sample;
@@ -231,17 +283,25 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
231 const auto br_sample = ClampToS16(mix_buffers[5][i]); 283 const auto br_sample = ClampToS16(mix_buffers[5][i]);
232 284
233 if (stream_channel_count == 1) { 285 if (stream_channel_count == 1) {
234 buffer[i * stream_channel_count + 0] = fc_sample; 286 // Games seem to ignore the center channel half the time, we use the front left
287 // and right channel for mixing as that's where majority of the audio goes
288 buffer[i * stream_channel_count + 0] = Mix2To1(fl_sample, fr_sample);
235 } else if (stream_channel_count == 2) { 289 } else if (stream_channel_count == 2) {
236 buffer[i * stream_channel_count + 0] = 290 // Mix all channels into 2 channels
237 static_cast<s16>(0.3694f * static_cast<float>(fl_sample) + 291 if (sink_context.HasDownMixingCoefficients()) {
238 0.2612f * static_cast<float>(fc_sample) + 292 const auto [left, right] = Mix6To2WithCoefficients(
239 0.3694f * static_cast<float>(bl_sample)); 293 fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample,
240 buffer[i * stream_channel_count + 1] = 294 sink_context.GetDownmixCoefficients());
241 static_cast<s16>(0.3694f * static_cast<float>(fr_sample) + 295 buffer[i * stream_channel_count + 0] = left;
242 0.2612f * static_cast<float>(fc_sample) + 296 buffer[i * stream_channel_count + 1] = right;
243 0.3694f * static_cast<float>(br_sample)); 297 } else {
298 const auto [left, right] = Mix6To2(fl_sample, fr_sample, fc_sample,
299 lf_sample, bl_sample, br_sample);
300 buffer[i * stream_channel_count + 0] = left;
301 buffer[i * stream_channel_count + 1] = right;
302 }
244 } else if (stream_channel_count == 6) { 303 } else if (stream_channel_count == 6) {
304 // Pass through
245 buffer[i * stream_channel_count + 0] = fl_sample; 305 buffer[i * stream_channel_count + 0] = fl_sample;
246 buffer[i * stream_channel_count + 1] = fr_sample; 306 buffer[i * stream_channel_count + 1] = fr_sample;
247 buffer[i * stream_channel_count + 2] = fc_sample; 307 buffer[i * stream_channel_count + 2] = fc_sample;
@@ -259,7 +319,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
259} 319}
260 320
261void AudioRenderer::ReleaseAndQueueBuffers() { 321void AudioRenderer::ReleaseAndQueueBuffers() {
262 const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream, 2)}; 322 const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)};
263 for (const auto& tag : released_buffers) { 323 for (const auto& tag : released_buffers) {
264 QueueMixedBuffer(tag); 324 QueueMixedBuffer(tag);
265 } 325 }
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;
36} 36}
37 37
38namespace AudioCore { 38namespace AudioCore {
39using DSPStateHolder = std::array<VoiceState*, 6>; 39using DSPStateHolder = std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>;
40 40
41class AudioOut; 41class AudioOut;
42 42
43struct RendererInfo {
44 u64_le elasped_frame_count{};
45 INSERT_PADDING_WORDS(2);
46};
47static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size");
48
49class AudioRenderer { 43class AudioRenderer {
50public: 44public:
51 AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, 45 AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
@@ -53,14 +47,14 @@ public:
53 std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number); 47 std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
54 ~AudioRenderer(); 48 ~AudioRenderer();
55 49
56 ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params, 50 [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
57 std::vector<u8>& output_params); 51 std::vector<u8>& output_params);
58 void QueueMixedBuffer(Buffer::Tag tag); 52 void QueueMixedBuffer(Buffer::Tag tag);
59 void ReleaseAndQueueBuffers(); 53 void ReleaseAndQueueBuffers();
60 u32 GetSampleRate() const; 54 [[nodiscard]] u32 GetSampleRate() const;
61 u32 GetSampleCount() const; 55 [[nodiscard]] u32 GetSampleCount() const;
62 u32 GetMixBufferCount() const; 56 [[nodiscard]] u32 GetMixBufferCount() const;
63 Stream::State GetStreamState() const; 57 [[nodiscard]] Stream::State GetStreamState() const;
64 58
65private: 59private:
66 BehaviorInfo behavior_info{}; 60 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:
43 void ClearError(); 43 void ClearError();
44 void UpdateFlags(u64_le dest_flags); 44 void UpdateFlags(u64_le dest_flags);
45 void SetUserRevision(u32_le revision); 45 void SetUserRevision(u32_le revision);
46 u32_le GetUserRevision() const; 46 [[nodiscard]] u32_le GetUserRevision() const;
47 u32_le GetProcessRevision() const; 47 [[nodiscard]] u32_le GetProcessRevision() const;
48 48
49 bool IsAdpcmLoopContextBugFixed() const; 49 [[nodiscard]] bool IsAdpcmLoopContextBugFixed() const;
50 bool IsSplitterSupported() const; 50 [[nodiscard]] bool IsSplitterSupported() const;
51 bool IsLongSizePreDelaySupported() const; 51 [[nodiscard]] bool IsLongSizePreDelaySupported() const;
52 bool IsAudioRendererProcessingTimeLimit80PercentSupported() const; 52 [[nodiscard]] bool IsAudioRendererProcessingTimeLimit80PercentSupported() const;
53 bool IsAudioRendererProcessingTimeLimit75PercentSupported() const; 53 [[nodiscard]] bool IsAudioRendererProcessingTimeLimit75PercentSupported() const;
54 bool IsAudioRendererProcessingTimeLimit70PercentSupported() const; 54 [[nodiscard]] bool IsAudioRendererProcessingTimeLimit70PercentSupported() const;
55 bool IsElapsedFrameCountSupported() const; 55 [[nodiscard]] bool IsElapsedFrameCountSupported() const;
56 bool IsMemoryPoolForceMappingEnabled() const; 56 [[nodiscard]] bool IsMemoryPoolForceMappingEnabled() const;
57 bool IsFlushVoiceWaveBuffersSupported() const; 57 [[nodiscard]] bool IsFlushVoiceWaveBuffersSupported() const;
58 bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const; 58 [[nodiscard]] bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const;
59 bool IsVoicePitchAndSrcSkippedSupported() const; 59 [[nodiscard]] bool IsVoicePitchAndSrcSkippedSupported() const;
60 bool IsMixInParameterDirtyOnlyUpdateSupported() const; 60 [[nodiscard]] bool IsMixInParameterDirtyOnlyUpdateSupported() const;
61 bool IsSplitterBugFixed() const; 61 [[nodiscard]] bool IsSplitterBugFixed() const;
62 void CopyErrorInfo(OutParams& dst); 62 void CopyErrorInfo(OutParams& dst);
63 63
64private: 64private:
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:
39 void PreCommand(); 39 void PreCommand();
40 void PostCommand(); 40 void PostCommand();
41 41
42 s32* GetChannelMixBuffer(s32 channel); 42 [[nodiscard]] s32* GetChannelMixBuffer(s32 channel);
43 const s32* GetChannelMixBuffer(s32 channel) const; 43 [[nodiscard]] const s32* GetChannelMixBuffer(s32 channel) const;
44 s32* GetMixBuffer(std::size_t index); 44 [[nodiscard]] s32* GetMixBuffer(std::size_t index);
45 const s32* GetMixBuffer(std::size_t index) const; 45 [[nodiscard]] const s32* GetMixBuffer(std::size_t index) const;
46 std::size_t GetMixChannelBufferOffset(s32 channel) const; 46 [[nodiscard]] std::size_t GetMixChannelBufferOffset(s32 channel) const;
47 47
48 std::size_t GetTotalMixBufferCount() const; 48 [[nodiscard]] std::size_t GetTotalMixBufferCount() const;
49 49
50private: 50private:
51 void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel); 51 void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel);
@@ -73,7 +73,7 @@ private:
73 void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled); 73 void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
74 void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled); 74 void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
75 void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled); 75 void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
76 ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index); 76 [[nodiscard]] ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
77 77
78 s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data, 78 s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
79 u32 sample_count, u32 write_offset, u32 write_count); 79 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;
22constexpr std::size_t MAX_WAVE_BUFFERS = 4; 22constexpr std::size_t MAX_WAVE_BUFFERS = 4;
23constexpr std::size_t MAX_SAMPLE_HISTORY = 4; 23constexpr std::size_t MAX_SAMPLE_HISTORY = 4;
24constexpr u32 STREAM_SAMPLE_RATE = 48000; 24constexpr u32 STREAM_SAMPLE_RATE = 48000;
25constexpr u32 STREAM_NUM_CHANNELS = 6; 25constexpr u32 STREAM_NUM_CHANNELS = 2;
26constexpr s32 NO_SPLITTER = -1; 26constexpr s32 NO_SPLITTER = -1;
27constexpr s32 NO_MIX = 0x7fffffff; 27constexpr s32 NO_MIX = 0x7fffffff;
28constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min(); 28constexpr 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:
189 189
190 virtual void Update(EffectInfo::InParams& in_params) = 0; 190 virtual void Update(EffectInfo::InParams& in_params) = 0;
191 virtual void UpdateForCommandGeneration() = 0; 191 virtual void UpdateForCommandGeneration() = 0;
192 UsageState GetUsage() const; 192 [[nodiscard]] UsageState GetUsage() const;
193 EffectType GetType() const; 193 [[nodiscard]] EffectType GetType() const;
194 bool IsEnabled() const; 194 [[nodiscard]] bool IsEnabled() const;
195 s32 GetMixID() const; 195 [[nodiscard]] s32 GetMixID() const;
196 s32 GetProcessingOrder() const; 196 [[nodiscard]] s32 GetProcessingOrder() const;
197 197
198protected: 198protected:
199 UsageState usage{UsageState::Invalid}; 199 UsageState usage{UsageState::Invalid};
@@ -257,10 +257,10 @@ public:
257 257
258 void Update(EffectInfo::InParams& in_params) override; 258 void Update(EffectInfo::InParams& in_params) override;
259 void UpdateForCommandGeneration() override; 259 void UpdateForCommandGeneration() override;
260 VAddr GetSendInfo() const; 260 [[nodiscard]] VAddr GetSendInfo() const;
261 VAddr GetSendBuffer() const; 261 [[nodiscard]] VAddr GetSendBuffer() const;
262 VAddr GetRecvInfo() const; 262 [[nodiscard]] VAddr GetRecvInfo() const;
263 VAddr GetRecvBuffer() const; 263 [[nodiscard]] VAddr GetRecvBuffer() const;
264 264
265private: 265private:
266 VAddr send_info{}; 266 VAddr send_info{};
@@ -309,10 +309,10 @@ public:
309 explicit EffectContext(std::size_t effect_count); 309 explicit EffectContext(std::size_t effect_count);
310 ~EffectContext(); 310 ~EffectContext();
311 311
312 std::size_t GetCount() const; 312 [[nodiscard]] std::size_t GetCount() const;
313 EffectBase* GetInfo(std::size_t i); 313 [[nodiscard]] EffectBase* GetInfo(std::size_t i);
314 EffectBase* RetargetEffect(std::size_t i, EffectType effect); 314 [[nodiscard]] EffectBase* RetargetEffect(std::size_t i, EffectType effect);
315 const EffectBase* GetInfo(std::size_t i) const; 315 [[nodiscard]] const EffectBase* GetInfo(std::size_t i) const;
316 316
317private: 317private:
318 std::size_t effect_count{}; 318 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:
62 ServerMixInfo(); 62 ServerMixInfo();
63 ~ServerMixInfo(); 63 ~ServerMixInfo();
64 64
65 const ServerMixInfo::InParams& GetInParams() const; 65 [[nodiscard]] const ServerMixInfo::InParams& GetInParams() const;
66 ServerMixInfo::InParams& GetInParams(); 66 [[nodiscard]] ServerMixInfo::InParams& GetInParams();
67 67
68 bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, 68 bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
69 BehaviorInfo& behavior_info, SplitterContext& splitter_context, 69 BehaviorInfo& behavior_info, SplitterContext& splitter_context,
70 EffectContext& effect_context); 70 EffectContext& effect_context);
71 bool HasAnyConnection() const; 71 [[nodiscard]] bool HasAnyConnection() const;
72 void Cleanup(); 72 void Cleanup();
73 void SetEffectCount(std::size_t count); 73 void SetEffectCount(std::size_t count);
74 void ResetEffectProcessingOrder(); 74 void ResetEffectProcessingOrder();
75 s32 GetEffectOrder(std::size_t i) const; 75 [[nodiscard]] s32 GetEffectOrder(std::size_t i) const;
76 76
77private: 77private:
78 std::vector<s32> effect_processing_order; 78 std::vector<s32> effect_processing_order;
@@ -91,15 +91,15 @@ public:
91 void SortInfo(); 91 void SortInfo();
92 bool TsortInfo(SplitterContext& splitter_context); 92 bool TsortInfo(SplitterContext& splitter_context);
93 93
94 std::size_t GetCount() const; 94 [[nodiscard]] std::size_t GetCount() const;
95 ServerMixInfo& GetInfo(std::size_t i); 95 [[nodiscard]] ServerMixInfo& GetInfo(std::size_t i);
96 const ServerMixInfo& GetInfo(std::size_t i) const; 96 [[nodiscard]] const ServerMixInfo& GetInfo(std::size_t i) const;
97 ServerMixInfo& GetSortedInfo(std::size_t i); 97 [[nodiscard]] ServerMixInfo& GetSortedInfo(std::size_t i);
98 const ServerMixInfo& GetSortedInfo(std::size_t i) const; 98 [[nodiscard]] const ServerMixInfo& GetSortedInfo(std::size_t i) const;
99 ServerMixInfo& GetFinalMixInfo(); 99 [[nodiscard]] ServerMixInfo& GetFinalMixInfo();
100 const ServerMixInfo& GetFinalMixInfo() const; 100 [[nodiscard]] const ServerMixInfo& GetFinalMixInfo() const;
101 EdgeMatrix& GetEdgeMatrix(); 101 [[nodiscard]] EdgeMatrix& GetEdgeMatrix();
102 const EdgeMatrix& GetEdgeMatrix() const; 102 [[nodiscard]] const EdgeMatrix& GetEdgeMatrix() const;
103 103
104private: 104private:
105 void CalcMixBufferOffset(); 105 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 {
12 return sink_count; 12 return sink_count;
13} 13}
14 14
15void SinkContext::UpdateMainSink(SinkInfo::InParams& in) { 15void SinkContext::UpdateMainSink(const SinkInfo::InParams& in) {
16 ASSERT(in.type == SinkTypes::Device);
17
18 has_downmix_coefs = in.device.down_matrix_enabled;
19 if (has_downmix_coefs) {
20 downmix_coefficients = in.device.down_matrix_coef;
21 }
16 in_use = in.in_use; 22 in_use = in.in_use;
17 use_count = in.device.input_count; 23 use_count = in.device.input_count;
18 std::memcpy(buffers.data(), in.device.input.data(), AudioCommon::MAX_CHANNEL_COUNT); 24 buffers = in.device.input;
19} 25}
20 26
21bool SinkContext::InUse() const { 27bool SinkContext::InUse() const {
@@ -28,4 +34,12 @@ std::vector<u8> SinkContext::OutputBuffers() const {
28 return buffer_ret; 34 return buffer_ret;
29} 35}
30 36
37bool SinkContext::HasDownMixingCoefficients() const {
38 return has_downmix_coefs;
39}
40
41const DownmixCoefficients& SinkContext::GetDownmixCoefficients() const {
42 return downmix_coefficients;
43}
44
31} // namespace AudioCore 45} // 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 @@
11 11
12namespace AudioCore { 12namespace AudioCore {
13 13
14using DownmixCoefficients = std::array<float_le, 4>;
15
14enum class SinkTypes : u8 { 16enum class SinkTypes : u8 {
15 Invalid = 0, 17 Invalid = 0,
16 Device = 1, 18 Device = 1,
@@ -50,7 +52,7 @@ public:
50 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input; 52 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
51 INSERT_UNION_PADDING_BYTES(1); 53 INSERT_UNION_PADDING_BYTES(1);
52 bool down_matrix_enabled; 54 bool down_matrix_enabled;
53 std::array<float_le, 4> down_matrix_coef; 55 DownmixCoefficients down_matrix_coef;
54 }; 56 };
55 static_assert(sizeof(SinkInfo::DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size"); 57 static_assert(sizeof(SinkInfo::DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size");
56 58
@@ -74,16 +76,21 @@ public:
74 explicit SinkContext(std::size_t sink_count); 76 explicit SinkContext(std::size_t sink_count);
75 ~SinkContext(); 77 ~SinkContext();
76 78
77 std::size_t GetCount() const; 79 [[nodiscard]] std::size_t GetCount() const;
80
81 void UpdateMainSink(const SinkInfo::InParams& in);
82 [[nodiscard]] bool InUse() const;
83 [[nodiscard]] std::vector<u8> OutputBuffers() const;
78 84
79 void UpdateMainSink(SinkInfo::InParams& in); 85 [[nodiscard]] bool HasDownMixingCoefficients() const;
80 bool InUse() const; 86 [[nodiscard]] const DownmixCoefficients& GetDownmixCoefficients() const;
81 std::vector<u8> OutputBuffers() const;
82 87
83private: 88private:
84 bool in_use{false}; 89 bool in_use{false};
85 s32 use_count{}; 90 s32 use_count{};
86 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{}; 91 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
87 std::size_t sink_count{}; 92 std::size_t sink_count{};
93 bool has_downmix_coefs{false};
94 DownmixCoefficients downmix_coefficients{};
88}; 95};
89} // namespace AudioCore 96} // 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)
136 return tags; 136 return tags;
137} 137}
138 138
139std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers() {
140 std::vector<Buffer::Tag> tags;
141 tags.reserve(released_buffers.size());
142 while (!released_buffers.empty()) {
143 tags.push_back(released_buffers.front()->GetTag());
144 released_buffers.pop();
145 }
146 return tags;
147}
148
139} // namespace AudioCore 149} // 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:
57 bool QueueBuffer(BufferPtr&& buffer); 57 bool QueueBuffer(BufferPtr&& buffer);
58 58
59 /// Returns true if the audio stream contains a buffer with the specified tag 59 /// Returns true if the audio stream contains a buffer with the specified tag
60 bool ContainsBuffer(Buffer::Tag tag) const; 60 [[nodiscard]] bool ContainsBuffer(Buffer::Tag tag) const;
61 61
62 /// Returns a vector of recently released buffers specified by tag 62 /// Returns a vector of recently released buffers specified by tag
63 std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count); 63 [[nodiscard]] std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
64
65 /// Returns a vector of all recently released buffers specified by tag
66 [[nodiscard]] std::vector<Buffer::Tag> GetTagsAndReleaseBuffers();
64 67
65 void SetVolume(float volume); 68 void SetVolume(float volume);
66 69
67 float GetVolume() const { 70 [[nodiscard]] float GetVolume() const {
68 return game_volume; 71 return game_volume;
69 } 72 }
70 73
71 /// Returns true if the stream is currently playing 74 /// Returns true if the stream is currently playing
72 bool IsPlaying() const { 75 [[nodiscard]] bool IsPlaying() const {
73 return state == State::Playing; 76 return state == State::Playing;
74 } 77 }
75 78
76 /// Returns the number of queued buffers 79 /// Returns the number of queued buffers
77 std::size_t GetQueueSize() const { 80 [[nodiscard]] std::size_t GetQueueSize() const {
78 return queued_buffers.size(); 81 return queued_buffers.size();
79 } 82 }
80 83
81 /// Gets the sample rate 84 /// Gets the sample rate
82 u32 GetSampleRate() const { 85 [[nodiscard]] u32 GetSampleRate() const {
83 return sample_rate; 86 return sample_rate;
84 } 87 }
85 88
86 /// Gets the number of channels 89 /// Gets the number of channels
87 u32 GetNumChannels() const; 90 [[nodiscard]] u32 GetNumChannels() const;
88 91
89 /// Get the state 92 /// Get the state
90 State GetState() const; 93 [[nodiscard]] State GetState() const;
91 94
92private: 95private:
93 /// Plays the next queued buffer in the audio stream, starting playback if necessary 96 /// Plays the next queued buffer in the audio stream, starting playback if necessary
@@ -97,7 +100,7 @@ private:
97 void ReleaseActiveBuffer(std::chrono::nanoseconds ns_late = {}); 100 void ReleaseActiveBuffer(std::chrono::nanoseconds ns_late = {});
98 101
99 /// Gets the number of core cycles when the specified buffer will be released 102 /// Gets the number of core cycles when the specified buffer will be released
100 std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const; 103 [[nodiscard]] std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const;
101 104
102 u32 sample_rate; ///< Sample rate of the stream 105 u32 sample_rate; ///< Sample rate of the stream
103 Format format; ///< Format of the stream 106 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 @@
4 4
5#include "common/fiber.h" 5#include "common/fiber.h"
6#include "common/microprofile.h" 6#include "common/microprofile.h"
7#include "common/scope_exit.h"
7#include "common/thread.h" 8#include "common/thread.h"
8#include "core/arm/exclusive_monitor.h" 9#include "core/arm/exclusive_monitor.h"
9#include "core/core.h" 10#include "core/core.h"
@@ -343,6 +344,16 @@ void CpuManager::RunThread(std::size_t core) {
343 data.initialized = true; 344 data.initialized = true;
344 const bool sc_sync = !is_async_gpu && !is_multicore; 345 const bool sc_sync = !is_async_gpu && !is_multicore;
345 bool sc_sync_first_use = sc_sync; 346 bool sc_sync_first_use = sc_sync;
347
348 // Cleanup
349 SCOPE_EXIT({
350 data.host_context->Exit();
351 data.enter_barrier.reset();
352 data.exit_barrier.reset();
353 data.initialized = false;
354 MicroProfileOnThreadExit();
355 });
356
346 /// Running 357 /// Running
347 while (running_mode) { 358 while (running_mode) {
348 data.is_running = false; 359 data.is_running = false;
@@ -351,6 +362,12 @@ void CpuManager::RunThread(std::size_t core) {
351 system.GPU().ObtainContext(); 362 system.GPU().ObtainContext();
352 sc_sync_first_use = false; 363 sc_sync_first_use = false;
353 } 364 }
365
366 // Abort if emulation was killed before the session really starts
367 if (!system.IsPoweredOn()) {
368 return;
369 }
370
354 auto& scheduler = system.Kernel().CurrentScheduler(); 371 auto& scheduler = system.Kernel().CurrentScheduler();
355 Kernel::Thread* current_thread = scheduler.GetCurrentThread(); 372 Kernel::Thread* current_thread = scheduler.GetCurrentThread();
356 data.is_running = true; 373 data.is_running = true;
@@ -360,13 +377,6 @@ void CpuManager::RunThread(std::size_t core) {
360 data.exit_barrier->Wait(); 377 data.exit_barrier->Wait();
361 data.is_paused = false; 378 data.is_paused = false;
362 } 379 }
363 /// Time to cleanup
364 data.host_context->Exit();
365 data.enter_barrier.reset();
366 data.exit_barrier.reset();
367 data.initialized = false;
368
369 MicroProfileOnThreadExit();
370} 380}
371 381
372} // namespace Core 382} // 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,
72 } 72 }
73} 73}
74 74
75/// Maps the controller type combobox index to Controller Type enum
76constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
77 switch (index) {
78 case 0:
79 default:
80 return Settings::ControllerType::ProController;
81 case 1:
82 return Settings::ControllerType::DualJoyconDetached;
83 case 2:
84 return Settings::ControllerType::LeftJoycon;
85 case 3:
86 return Settings::ControllerType::RightJoycon;
87 case 4:
88 return Settings::ControllerType::Handheld;
89 }
90}
91
92/// Maps the Controller Type enum to controller type combobox index
93constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
94 switch (type) {
95 case Settings::ControllerType::ProController:
96 default:
97 return 0;
98 case Settings::ControllerType::DualJoyconDetached:
99 return 1;
100 case Settings::ControllerType::LeftJoycon:
101 return 2;
102 case Settings::ControllerType::RightJoycon:
103 return 3;
104 case Settings::ControllerType::Handheld:
105 return 4;
106 }
107}
108
109} // namespace 75} // namespace
110 76
111QtControllerSelectorDialog::QtControllerSelectorDialog( 77QtControllerSelectorDialog::QtControllerSelectorDialog(
@@ -184,6 +150,11 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
184 // This avoids unintentionally changing the states of elements while loading them in. 150 // This avoids unintentionally changing the states of elements while loading them in.
185 SetSupportedControllers(); 151 SetSupportedControllers();
186 DisableUnsupportedPlayers(); 152 DisableUnsupportedPlayers();
153
154 for (std::size_t player_index = 0; player_index < NUM_PLAYERS; ++player_index) {
155 SetEmulatedControllers(player_index);
156 }
157
187 LoadConfiguration(); 158 LoadConfiguration();
188 159
189 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) { 160 for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
@@ -223,8 +194,8 @@ QtControllerSelectorDialog::QtControllerSelectorDialog(
223 194
224 if (i == 0) { 195 if (i == 0) {
225 connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged), 196 connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
226 [this](int index) { 197 [this, i](int index) {
227 UpdateDockedState(GetControllerTypeFromIndex(index) == 198 UpdateDockedState(GetControllerTypeFromIndex(index, i) ==
228 Settings::ControllerType::Handheld); 199 Settings::ControllerType::Handheld);
229 }); 200 });
230 } 201 }
@@ -281,8 +252,8 @@ void QtControllerSelectorDialog::LoadConfiguration() {
281 (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected); 252 (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
282 player_groupboxes[index]->setChecked(connected); 253 player_groupboxes[index]->setChecked(connected);
283 connected_controller_checkboxes[index]->setChecked(connected); 254 connected_controller_checkboxes[index]->setChecked(connected);
284 emulated_controllers[index]->setCurrentIndex( 255 emulated_controllers[index]->setCurrentIndex(GetIndexFromControllerType(
285 GetIndexFromControllerType(Settings::values.players.GetValue()[index].controller_type)); 256 Settings::values.players.GetValue()[index].controller_type, index));
286 } 257 }
287 258
288 UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected); 259 UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
@@ -338,7 +309,7 @@ bool QtControllerSelectorDialog::CheckIfParametersMet() {
338 } 309 }
339 310
340 const auto compatible = IsControllerCompatible( 311 const auto compatible = IsControllerCompatible(
341 GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex()), 312 GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex(), index),
342 parameters); 313 parameters);
343 314
344 // If any controller is found to be incompatible, return false early. 315 // If any controller is found to be incompatible, return false early.
@@ -422,6 +393,63 @@ void QtControllerSelectorDialog::SetSupportedControllers() {
422 } 393 }
423} 394}
424 395
396void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index) {
397 auto& pairs = index_controller_type_pairs[player_index];
398
399 pairs.clear();
400 emulated_controllers[player_index]->clear();
401
402 pairs.emplace_back(emulated_controllers[player_index]->count(),
403 Settings::ControllerType::ProController);
404 emulated_controllers[player_index]->addItem(tr("Pro Controller"));
405
406 pairs.emplace_back(emulated_controllers[player_index]->count(),
407 Settings::ControllerType::DualJoyconDetached);
408 emulated_controllers[player_index]->addItem(tr("Dual Joycons"));
409
410 pairs.emplace_back(emulated_controllers[player_index]->count(),
411 Settings::ControllerType::LeftJoycon);
412 emulated_controllers[player_index]->addItem(tr("Left Joycon"));
413
414 pairs.emplace_back(emulated_controllers[player_index]->count(),
415 Settings::ControllerType::RightJoycon);
416 emulated_controllers[player_index]->addItem(tr("Right Joycon"));
417
418 if (player_index == 0) {
419 pairs.emplace_back(emulated_controllers[player_index]->count(),
420 Settings::ControllerType::Handheld);
421 emulated_controllers[player_index]->addItem(tr("Handheld"));
422 }
423}
424
425Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex(
426 int index, std::size_t player_index) const {
427 const auto& pairs = index_controller_type_pairs[player_index];
428
429 const auto it = std::find_if(pairs.begin(), pairs.end(),
430 [index](const auto& pair) { return pair.first == index; });
431
432 if (it == pairs.end()) {
433 return Settings::ControllerType::ProController;
434 }
435
436 return it->second;
437}
438
439int QtControllerSelectorDialog::GetIndexFromControllerType(Settings::ControllerType type,
440 std::size_t player_index) const {
441 const auto& pairs = index_controller_type_pairs[player_index];
442
443 const auto it = std::find_if(pairs.begin(), pairs.end(),
444 [type](const auto& pair) { return pair.second == type; });
445
446 if (it == pairs.end()) {
447 return 0;
448 }
449
450 return it->first;
451}
452
425void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) { 453void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) {
426 if (!player_groupboxes[player_index]->isChecked()) { 454 if (!player_groupboxes[player_index]->isChecked()) {
427 connected_controller_icons[player_index]->setStyleSheet(QString{}); 455 connected_controller_icons[player_index]->setStyleSheet(QString{});
@@ -430,7 +458,8 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
430 } 458 }
431 459
432 const QString stylesheet = [this, player_index] { 460 const QString stylesheet = [this, player_index] {
433 switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex())) { 461 switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
462 player_index)) {
434 case Settings::ControllerType::ProController: 463 case Settings::ControllerType::ProController:
435 return QStringLiteral("image: url(:/controller/applet_pro_controller%0); "); 464 return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
436 case Settings::ControllerType::DualJoyconDetached: 465 case Settings::ControllerType::DualJoyconDetached:
@@ -446,6 +475,12 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
446 } 475 }
447 }(); 476 }();
448 477
478 if (stylesheet.isEmpty()) {
479 connected_controller_icons[player_index]->setStyleSheet(QString{});
480 player_labels[player_index]->show();
481 return;
482 }
483
449 const QString theme = [] { 484 const QString theme = [] {
450 if (QIcon::themeName().contains(QStringLiteral("dark"))) { 485 if (QIcon::themeName().contains(QStringLiteral("dark"))) {
451 return QStringLiteral("_dark"); 486 return QStringLiteral("_dark");
@@ -463,8 +498,8 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)
463void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) { 498void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
464 auto& player = Settings::values.players.GetValue()[player_index]; 499 auto& player = Settings::values.players.GetValue()[player_index];
465 500
466 const auto controller_type = 501 const auto controller_type = GetControllerTypeFromIndex(
467 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()); 502 emulated_controllers[player_index]->currentIndex(), player_index);
468 const auto player_connected = player_groupboxes[player_index]->isChecked() && 503 const auto player_connected = player_groupboxes[player_index]->isChecked() &&
469 controller_type != Settings::ControllerType::Handheld; 504 controller_type != Settings::ControllerType::Handheld;
470 505
@@ -507,8 +542,8 @@ void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index)
507 542
508void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { 543void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
509 if (!player_groupboxes[player_index]->isChecked() || 544 if (!player_groupboxes[player_index]->isChecked() ||
510 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()) == 545 GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),
511 Settings::ControllerType::Handheld) { 546 player_index) == Settings::ControllerType::Handheld) {
512 led_patterns_boxes[player_index][0]->setChecked(false); 547 led_patterns_boxes[player_index][0]->setChecked(false);
513 led_patterns_boxes[player_index][1]->setChecked(false); 548 led_patterns_boxes[player_index][1]->setChecked(false);
514 led_patterns_boxes[player_index][2]->setChecked(false); 549 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 {
22class InputSubsystem; 22class InputSubsystem;
23} 23}
24 24
25namespace Settings {
26enum class ControllerType;
27}
28
25namespace Ui { 29namespace Ui {
26class QtControllerSelectorDialog; 30class QtControllerSelectorDialog;
27} 31}
@@ -57,6 +61,15 @@ private:
57 // Sets the controller icons for "Supported Controller Types". 61 // Sets the controller icons for "Supported Controller Types".
58 void SetSupportedControllers(); 62 void SetSupportedControllers();
59 63
64 // Sets the emulated controllers per player.
65 void SetEmulatedControllers(std::size_t player_index);
66
67 // Gets the Controller Type for a given controller combobox index per player.
68 Settings::ControllerType GetControllerTypeFromIndex(int index, std::size_t player_index) const;
69
70 // Gets the controller combobox index for a given Controller Type per player.
71 int GetIndexFromControllerType(Settings::ControllerType type, std::size_t player_index) const;
72
60 // Updates the controller icons per player. 73 // Updates the controller icons per player.
61 void UpdateControllerIcon(std::size_t player_index); 74 void UpdateControllerIcon(std::size_t player_index);
62 75
@@ -114,6 +127,10 @@ private:
114 // Comboboxes with a list of emulated controllers per player. 127 // Comboboxes with a list of emulated controllers per player.
115 std::array<QComboBox*, NUM_PLAYERS> emulated_controllers; 128 std::array<QComboBox*, NUM_PLAYERS> emulated_controllers;
116 129
130 /// Pairs of emulated controller index and Controller Type enum per player.
131 std::array<std::vector<std::pair<int, Settings::ControllerType>>, NUM_PLAYERS>
132 index_controller_type_pairs;
133
117 // Labels representing the number of connected controllers 134 // Labels representing the number of connected controllers
118 // above the "Connected Controllers" checkboxes. 135 // above the "Connected Controllers" checkboxes.
119 std::array<QLabel*, NUM_PLAYERS> connected_controller_labels; 136 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 @@
27#include "yuzu/configuration/input_profiles.h" 27#include "yuzu/configuration/input_profiles.h"
28#include "yuzu/util/limitable_input_dialog.h" 28#include "yuzu/util/limitable_input_dialog.h"
29 29
30using namespace Service::HID;
31
30const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM> 32const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
31 ConfigureInputPlayer::analog_sub_buttons{{ 33 ConfigureInputPlayer::analog_sub_buttons{{
32 "up", 34 "up",
@@ -47,48 +49,12 @@ void UpdateController(Settings::ControllerType controller_type, std::size_t npad
47 } 49 }
48 Service::SM::ServiceManager& sm = system.ServiceManager(); 50 Service::SM::ServiceManager& sm = system.ServiceManager();
49 51
50 auto& npad = 52 auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>(
51 sm.GetService<Service::HID::Hid>("hid") 53 HidController::NPad);
52 ->GetAppletResource()
53 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
54 54
55 npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected); 55 npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
56} 56}
57 57
58/// Maps the controller type combobox index to Controller Type enum
59constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
60 switch (index) {
61 case 0:
62 default:
63 return Settings::ControllerType::ProController;
64 case 1:
65 return Settings::ControllerType::DualJoyconDetached;
66 case 2:
67 return Settings::ControllerType::LeftJoycon;
68 case 3:
69 return Settings::ControllerType::RightJoycon;
70 case 4:
71 return Settings::ControllerType::Handheld;
72 }
73}
74
75/// Maps the Controller Type enum to controller type combobox index
76constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
77 switch (type) {
78 case Settings::ControllerType::ProController:
79 default:
80 return 0;
81 case Settings::ControllerType::DualJoyconDetached:
82 return 1;
83 case Settings::ControllerType::LeftJoycon:
84 return 2;
85 case Settings::ControllerType::RightJoycon:
86 return 3;
87 case Settings::ControllerType::Handheld:
88 return 4;
89 }
90}
91
92QString GetKeyName(int key_code) { 58QString GetKeyName(int key_code) {
93 switch (key_code) { 59 switch (key_code) {
94 case Qt::LeftButton: 60 case Qt::LeftButton:
@@ -453,18 +419,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
453 connect(ui->groupConnectedController, &QGroupBox::toggled, 419 connect(ui->groupConnectedController, &QGroupBox::toggled,
454 [this](bool checked) { emit Connected(checked); }); 420 [this](bool checked) { emit Connected(checked); });
455 421
456 // Set up controller type. Only Player 1 can choose Handheld.
457 ui->comboControllerType->clear();
458
459 QStringList controller_types = {
460 tr("Pro Controller"),
461 tr("Dual Joycons"),
462 tr("Left Joycon"),
463 tr("Right Joycon"),
464 };
465
466 if (player_index == 0) { 422 if (player_index == 0) {
467 controller_types.append(tr("Handheld"));
468 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), 423 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
469 [this](int index) { 424 [this](int index) {
470 emit HandheldStateChanged(GetControllerTypeFromIndex(index) == 425 emit HandheldStateChanged(GetControllerTypeFromIndex(index) ==
@@ -480,12 +435,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
480 if (debug) { 435 if (debug) {
481 ui->buttonScreenshot->setEnabled(false); 436 ui->buttonScreenshot->setEnabled(false);
482 ui->buttonHome->setEnabled(false); 437 ui->buttonHome->setEnabled(false);
483 QStringList debug_controller_types = { 438 ui->comboControllerType->addItem(tr("Pro Controller"));
484 tr("Pro Controller"),
485 };
486 ui->comboControllerType->addItems(debug_controller_types);
487 } else { 439 } else {
488 ui->comboControllerType->addItems(controller_types); 440 SetConnectableControllers();
489 } 441 }
490 442
491 UpdateControllerIcon(); 443 UpdateControllerIcon();
@@ -667,7 +619,7 @@ void ConfigureInputPlayer::LoadConfiguration() {
667 return; 619 return;
668 } 620 }
669 621
670 ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type)); 622 ui->comboControllerType->setCurrentIndex(GetIndexFromControllerType(player.controller_type));
671 ui->groupConnectedController->setChecked( 623 ui->groupConnectedController->setChecked(
672 player.connected || 624 player.connected ||
673 (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected)); 625 (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected));
@@ -841,6 +793,82 @@ void ConfigureInputPlayer::UpdateUI() {
841 } 793 }
842} 794}
843 795
796void ConfigureInputPlayer::SetConnectableControllers() {
797 const auto add_controllers = [this](bool enable_all,
798 Controller_NPad::NpadStyleSet npad_style_set = {}) {
799 index_controller_type_pairs.clear();
800 ui->comboControllerType->clear();
801
802 if (enable_all || npad_style_set.pro_controller == 1) {
803 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
804 Settings::ControllerType::ProController);
805 ui->comboControllerType->addItem(tr("Pro Controller"));
806 }
807
808 if (enable_all || npad_style_set.joycon_dual == 1) {
809 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
810 Settings::ControllerType::DualJoyconDetached);
811 ui->comboControllerType->addItem(tr("Dual Joycons"));
812 }
813
814 if (enable_all || npad_style_set.joycon_left == 1) {
815 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
816 Settings::ControllerType::LeftJoycon);
817 ui->comboControllerType->addItem(tr("Left Joycon"));
818 }
819
820 if (enable_all || npad_style_set.joycon_right == 1) {
821 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
822 Settings::ControllerType::RightJoycon);
823 ui->comboControllerType->addItem(tr("Right Joycon"));
824 }
825
826 if (player_index == 0 && (enable_all || npad_style_set.handheld == 1)) {
827 index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
828 Settings::ControllerType::Handheld);
829 ui->comboControllerType->addItem(tr("Handheld"));
830 }
831 };
832
833 Core::System& system{Core::System::GetInstance()};
834
835 if (!system.IsPoweredOn()) {
836 add_controllers(true);
837 return;
838 }
839
840 Service::SM::ServiceManager& sm = system.ServiceManager();
841
842 auto& npad = sm.GetService<Hid>("hid")->GetAppletResource()->GetController<Controller_NPad>(
843 HidController::NPad);
844
845 add_controllers(false, npad.GetSupportedStyleSet());
846}
847
848Settings::ControllerType ConfigureInputPlayer::GetControllerTypeFromIndex(int index) const {
849 const auto it =
850 std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(),
851 [index](const auto& pair) { return pair.first == index; });
852
853 if (it == index_controller_type_pairs.end()) {
854 return Settings::ControllerType::ProController;
855 }
856
857 return it->second;
858}
859
860int ConfigureInputPlayer::GetIndexFromControllerType(Settings::ControllerType type) const {
861 const auto it =
862 std::find_if(index_controller_type_pairs.begin(), index_controller_type_pairs.end(),
863 [type](const auto& pair) { return pair.second == type; });
864
865 if (it == index_controller_type_pairs.end()) {
866 return -1;
867 }
868
869 return it->first;
870}
871
844void ConfigureInputPlayer::UpdateInputDevices() { 872void ConfigureInputPlayer::UpdateInputDevices() {
845 input_devices = input_subsystem->GetInputDevices(); 873 input_devices = input_subsystem->GetInputDevices();
846 ui->comboDevices->clear(); 874 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 @@
9#include <memory> 9#include <memory>
10#include <optional> 10#include <optional>
11#include <string> 11#include <string>
12#include <vector>
12 13
13#include <QWidget> 14#include <QWidget>
14 15
@@ -112,6 +113,15 @@ private:
112 /// Update UI to reflect current configuration. 113 /// Update UI to reflect current configuration.
113 void UpdateUI(); 114 void UpdateUI();
114 115
116 /// Sets the available controllers.
117 void SetConnectableControllers();
118
119 /// Gets the Controller Type for a given controller combobox index.
120 Settings::ControllerType GetControllerTypeFromIndex(int index) const;
121
122 /// Gets the controller combobox index for a given Controller Type.
123 int GetIndexFromControllerType(Settings::ControllerType type) const;
124
115 /// Update the available input devices. 125 /// Update the available input devices.
116 void UpdateInputDevices(); 126 void UpdateInputDevices();
117 127
@@ -151,6 +161,9 @@ private:
151 std::unique_ptr<QTimer> timeout_timer; 161 std::unique_ptr<QTimer> timeout_timer;
152 std::unique_ptr<QTimer> poll_timer; 162 std::unique_ptr<QTimer> poll_timer;
153 163
164 /// Stores a pair of "Connected Controllers" combobox index and Controller Type enum.
165 std::vector<std::pair<int, Settings::ControllerType>> index_controller_type_pairs;
166
154 static constexpr int PLAYER_COUNT = 8; 167 static constexpr int PLAYER_COUNT = 8;
155 std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox; 168 std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox;
156 169