|
|
|
@ -1,12 +1,24 @@ |
|
|
|
// 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 <cmath>
|
|
|
|
// for std::clamp
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
namespace Service::Audio { |
|
|
|
|
|
|
|
@ -14,12 +26,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"}, |
|
|
|
@ -46,7 +58,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"}, |
|
|
|
@ -80,6 +92,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() { |
|
|
|
@ -187,4 +231,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
|