mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-07-02 04:45:54 +02:00
[hle,display,overlay,starter,hid] add overlay functions, starter applet (initially), HID handheld for system applets and fw21 stubs (#3080)
Adds fully functional overlay display. - Enable Overlay Applet via "View" -> "Enable Overlay Display Applet" - Open the overlay by pressing the home button for over 1s - Can adjust volume - Can toggle airplane mode (if on WiFi, maybe if overlay is enabled pretend to be on WiFi?) - Future TODO(?): Adjust Brightness implementation for host system - Inputs are properly registered. e.g. if overlay open, application does not register inputs. You can control volume and airplane mode outside of the emulator window Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3080 Reviewed-by: CamilleLaVey <camillelavey99@gmail.com> Reviewed-by: crueter <crueter@eden-emu.dev> Co-authored-by: Maufeat <sahyno1996@gmail.com> Co-committed-by: Maufeat <sahyno1996@gmail.com>
This commit is contained in:
parent
1efef85352
commit
f58097e814
75 changed files with 1634 additions and 181 deletions
|
|
@ -1,15 +1,22 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "core/hle/service/audio/audio_controller.h"
|
||||
#include "core/hle/service/audio/audio_out_manager.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/set/system_settings_server.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "common/settings.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
|
|
@ -17,12 +24,12 @@ IAudioController::IAudioController(Core::System& system_)
|
|||
: ServiceFramework{system_, "audctl"}, service_context{system, "audctl"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetTargetVolume"},
|
||||
{1, nullptr, "SetTargetVolume"},
|
||||
{0, D<&IAudioController::GetTargetVolume>, "GetTargetVolume"},
|
||||
{1, D<&IAudioController::SetTargetVolume>, "SetTargetVolume"},
|
||||
{2, D<&IAudioController::GetTargetVolumeMin>, "GetTargetVolumeMin"},
|
||||
{3, D<&IAudioController::GetTargetVolumeMax>, "GetTargetVolumeMax"},
|
||||
{4, nullptr, "IsTargetMute"},
|
||||
{5, nullptr, "SetTargetMute"},
|
||||
{4, D<&IAudioController::IsTargetMute>, "IsTargetMute"},
|
||||
{5, D<&IAudioController::SetTargetMute>, "SetTargetMute"},
|
||||
{6, nullptr, "IsTargetConnected"},
|
||||
{7, nullptr, "SetDefaultTarget"},
|
||||
{8, nullptr, "GetDefaultTarget"},
|
||||
|
|
@ -49,7 +56,7 @@ IAudioController::IAudioController(Core::System& system_)
|
|||
{29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
|
||||
{30, D<&IAudioController::SetSpeakerAutoMuteEnabled>, "SetSpeakerAutoMuteEnabled"},
|
||||
{31, D<&IAudioController::IsSpeakerAutoMuteEnabled>, "IsSpeakerAutoMuteEnabled"},
|
||||
{32, nullptr, "GetActiveOutputTarget"},
|
||||
{32, D<&IAudioController::GetActiveOutputTarget>, "GetActiveOutputTarget"},
|
||||
{33, nullptr, "GetTargetDeviceInfo"},
|
||||
{34, D<&IAudioController::AcquireTargetNotification>, "AcquireTargetNotification"},
|
||||
{35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
|
||||
|
|
@ -84,6 +91,38 @@ IAudioController::IAudioController(Core::System& system_)
|
|||
m_set_sys =
|
||||
system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
|
||||
notification_event = service_context.CreateEvent("IAudioController:NotificationEvent");
|
||||
|
||||
// Probably shouldn't do this in constructor?
|
||||
try {
|
||||
const int ui_volume = Settings::values.volume.GetValue();
|
||||
const int mapped = static_cast<int>(std::lround((static_cast<double>(ui_volume) / 100.0) * 15.0));
|
||||
const auto active_idx = static_cast<size_t>(m_active_target);
|
||||
if (active_idx < m_target_volumes.size()) {
|
||||
m_target_volumes[active_idx] = std::clamp(mapped, 0, 15);
|
||||
}
|
||||
|
||||
const bool muted = Settings::values.audio_muted.GetValue();
|
||||
for (auto& m : m_target_muted) {
|
||||
m = muted;
|
||||
}
|
||||
|
||||
if (!muted && active_idx < m_target_volumes.size()) {
|
||||
const float vol_f = static_cast<float>(m_target_volumes[active_idx]) / 15.0f;
|
||||
try {
|
||||
auto& sink = system.AudioCore().GetOutputSink();
|
||||
sink.SetSystemVolume(vol_f);
|
||||
sink.SetDeviceVolume(vol_f);
|
||||
} catch (...) {
|
||||
LOG_WARNING(Audio, "Failed to apply initial sink volume from settings");
|
||||
}
|
||||
|
||||
if (auto audout_mgr = system.ServiceManager().GetService<Service::Audio::IAudioOutManager>("audout:u")) {
|
||||
audout_mgr->SetAllAudioOutVolume(static_cast<float>(m_target_volumes[active_idx]) / 15.0f);
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
// Catch if something fails, since this is constructor
|
||||
}
|
||||
}
|
||||
|
||||
IAudioController::~IAudioController() {
|
||||
|
|
@ -191,4 +230,120 @@ Result IAudioController::Unknown5000(Out<SharedPointer<IAudioController>> out_au
|
|||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::GetTargetVolume(Out<s32> out_target_volume, Set::AudioOutputModeTarget target) {
|
||||
LOG_DEBUG(Audio, "GetTargetVolume called, target={}", target);
|
||||
|
||||
const auto idx = static_cast<size_t>(target);
|
||||
if (idx >= m_target_volumes.size()) {
|
||||
LOG_ERROR(Audio, "GetTargetVolume invalid target {}", idx);
|
||||
R_THROW(ResultInvalidArgument);
|
||||
}
|
||||
|
||||
*out_target_volume = m_target_volumes[idx];
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::SetTargetVolume(Set::AudioOutputModeTarget target, s32 target_volume) {
|
||||
LOG_INFO(Audio, "SetTargetVolume called, target={}, volume={}", target, target_volume);
|
||||
|
||||
const auto idx = static_cast<size_t>(target);
|
||||
if (idx >= m_target_volumes.size()) {
|
||||
LOG_ERROR(Audio, "SetTargetVolume invalid target {}", idx);
|
||||
R_THROW(ResultInvalidArgument);
|
||||
}
|
||||
|
||||
if (target_volume < 0) {
|
||||
target_volume = 0;
|
||||
} else if (target_volume > 15) {
|
||||
target_volume = 15;
|
||||
}
|
||||
|
||||
m_target_volumes[idx] = target_volume;
|
||||
|
||||
if (m_active_target == target && !m_target_muted[idx]) {
|
||||
const float vol = static_cast<float>(target_volume) / 15.0f;
|
||||
// try catch this, as we don't know how it handles it when no output is set.
|
||||
// we already have audio issues when no output is set, so catch.
|
||||
try {
|
||||
auto& sink = system.AudioCore().GetOutputSink();
|
||||
sink.SetSystemVolume(vol);
|
||||
sink.SetDeviceVolume(vol);
|
||||
} catch (...) {
|
||||
LOG_WARNING(Audio, "Failed to set sink system volume");
|
||||
}
|
||||
|
||||
if (auto audout_mgr = system.ServiceManager().GetService<IAudioOutManager>("audout:u")) {
|
||||
audout_mgr->SetAllAudioOutVolume(vol);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_active_target == target) {
|
||||
const int ui_volume = static_cast<int>(std::lround((static_cast<double>(target_volume) / 15.0) * 100.0));
|
||||
Settings::values.volume.SetValue(static_cast<u8>(std::clamp(ui_volume, 0, 100)));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::IsTargetMute(Out<bool> out_is_target_muted, Set::AudioOutputModeTarget target) {
|
||||
LOG_DEBUG(Audio, "called, target={}", target);
|
||||
|
||||
const auto idx = static_cast<size_t>(target);
|
||||
if (idx >= m_target_muted.size()) {
|
||||
LOG_ERROR(Audio, "invalid target {}", idx);
|
||||
R_THROW(ResultInvalidArgument);
|
||||
}
|
||||
|
||||
*out_is_target_muted = m_target_muted[idx];
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::SetTargetMute(bool is_muted, Set::AudioOutputModeTarget target) {
|
||||
LOG_INFO(Audio, "called, target={}, muted={}", target, is_muted);
|
||||
|
||||
const auto idx = static_cast<size_t>(target);
|
||||
if (idx >= m_target_muted.size()) {
|
||||
LOG_ERROR(Audio, "invalid target {}", idx);
|
||||
R_THROW(ResultInvalidArgument);
|
||||
}
|
||||
|
||||
m_target_muted[idx] = is_muted;
|
||||
|
||||
if (m_active_target == target) {
|
||||
try {
|
||||
auto& sink = system.AudioCore().GetOutputSink();
|
||||
if (is_muted) {
|
||||
sink.SetSystemVolume(0.0f);
|
||||
sink.SetDeviceVolume(0.0f);
|
||||
} else {
|
||||
const float vol = static_cast<float>(m_target_volumes[idx]) / 15.0f;
|
||||
sink.SetSystemVolume(vol);
|
||||
sink.SetDeviceVolume(vol);
|
||||
}
|
||||
} catch (...) {
|
||||
LOG_WARNING(Audio, "Failed to set sink system volume on mute change");
|
||||
}
|
||||
|
||||
// Also update any open audout sessions via the audout:u service.
|
||||
if (auto audout_mgr = system.ServiceManager().GetService<IAudioOutManager>("audout:u")) {
|
||||
if (is_muted) {
|
||||
audout_mgr->SetAllAudioOutVolume(0.0f);
|
||||
} else {
|
||||
const float vol = static_cast<float>(m_target_volumes[idx]) / 15.0f;
|
||||
audout_mgr->SetAllAudioOutVolume(vol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Settings::values.audio_muted.SetValue(is_muted);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::GetActiveOutputTarget(Out<Set::AudioOutputModeTarget> out_active_target) {
|
||||
LOG_DEBUG(Audio, "GetActiveOutputTarget called");
|
||||
*out_active_target = m_active_target;
|
||||
R_SUCCEED();
|
||||
}
|
||||
} // namespace Service::Audio
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -35,6 +38,11 @@ private:
|
|||
|
||||
Result GetTargetVolumeMin(Out<s32> out_target_min_volume);
|
||||
Result GetTargetVolumeMax(Out<s32> out_target_max_volume);
|
||||
Result GetTargetVolume(Out<s32> out_target_volume, Set::AudioOutputModeTarget target);
|
||||
Result SetTargetVolume(Set::AudioOutputModeTarget target, s32 target_volume);
|
||||
Result IsTargetMute(Out<bool> out_is_target_muted, Set::AudioOutputModeTarget target);
|
||||
Result SetTargetMute(bool is_muted, Set::AudioOutputModeTarget target);
|
||||
Result GetActiveOutputTarget(Out<Set::AudioOutputModeTarget> out_active_target);
|
||||
Result GetAudioOutputMode(Out<Set::AudioOutputMode> out_output_mode,
|
||||
Set::AudioOutputModeTarget target);
|
||||
Result SetAudioOutputMode(Set::AudioOutputModeTarget target, Set::AudioOutputMode output_mode);
|
||||
|
|
@ -55,6 +63,9 @@ private:
|
|||
|
||||
Kernel::KEvent* notification_event;
|
||||
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
|
||||
std::array<s32, 6> m_target_volumes{{15, 15, 15, 15, 15, 15}};
|
||||
std::array<bool, 6> m_target_muted{{false, false, false, false, false, false}};
|
||||
Set::AudioOutputModeTarget m_active_target{Set::AudioOutputModeTarget::Speaker};
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -98,4 +101,15 @@ Result IAudioOutManager::OpenAudioOutAuto(
|
|||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioOutManager::SetAllAudioOutVolume(f32 volume) {
|
||||
std::scoped_lock l{impl->mutex};
|
||||
for (auto& session : impl->sessions) {
|
||||
if (session) {
|
||||
session->GetSystem().SetVolume(volume);
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -18,6 +21,8 @@ public:
|
|||
explicit IAudioOutManager(Core::System& system_);
|
||||
~IAudioOutManager() override;
|
||||
|
||||
Result SetAllAudioOutVolume(f32 volume);
|
||||
|
||||
private:
|
||||
Result ListAudioOuts(OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_audio_outs,
|
||||
Out<u32> out_count);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
|
@ -19,6 +22,7 @@ constexpr Result ResultInvalidAddressInfo{ErrorModule::Audio, 42};
|
|||
constexpr Result ResultNotSupported{ErrorModule::Audio, 513};
|
||||
constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536};
|
||||
constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537};
|
||||
constexpr Result ResultInvalidArgument{ErrorModule::Audio, 900};
|
||||
|
||||
constexpr Result ResultLibOpusAllocFail{ErrorModule::HwOpus, 7};
|
||||
constexpr Result ResultInputDataTooSmall{ErrorModule::HwOpus, 8};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue