From 1888e17b228498df506528f8787bfda7eeae3422 Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Wed, 8 Feb 2023 00:52:57 +0000 Subject: [PATCH] Add ability to force routing audio to headphones... ... even when HPs aren't inserted. This is aimed at people using Bluetooth audio hw mods. There are some software limitations, please read the INI/option description. Closes #1828. --- arm9/data/config_template.ini | Bin 6432 -> 6728 bytes arm9/source/config.c | 18 +++- arm9/source/config.h | 3 +- k11_extension/include/config.h | 1 + sysmodules/loader/source/patcher.h | 1 + sysmodules/pm/source/luma.h | 1 + sysmodules/rosalina/data/config_template.ini | Bin 6432 -> 6728 bytes sysmodules/rosalina/include/luma_config.h | 1 + sysmodules/rosalina/include/shell.h | 29 +++++++ sysmodules/rosalina/source/luma_config.c | 2 +- sysmodules/rosalina/source/main.c | 9 +- sysmodules/rosalina/source/menu.c | 4 +- .../rosalina/source/menus/screen_filters.c | 2 - sysmodules/rosalina/source/shell.c | 82 ++++++++++++++++++ 14 files changed, 138 insertions(+), 15 deletions(-) create mode 100644 sysmodules/rosalina/include/shell.h create mode 100644 sysmodules/rosalina/source/shell.c diff --git a/arm9/data/config_template.ini b/arm9/data/config_template.ini index 697b4fe40fc882f3cf88d1a8f34a03a2d0313855..1dd2da76846ee72348babc9469d4385591871561 100644 GIT binary patch delta 302 zcmX|-F-`+P3`Ogt;R>G)B$NWtQAI#RibMz@+SRb*%?z3ykH#Kgs~mt9dX9j^B{&wd zS&7DyW&6MX{dN9(@%=H|uQ<1a%td|N!4&HmwMj*x3`fQ$$B|?)zrsplashDurationMsec, @@ -759,6 +760,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode) "( ) Redirect app. syscore threads to core2", "( ) Show NAND or user string in System Settings", "( ) Show GBA boot screen in patched AGB_FIRM", + "( ) Force routing audio output to headphones" }; static const char *optionsDescription[] = { "Select the default EmuNAND.\n\n" @@ -830,8 +832,8 @@ void configMenu(bool oldPinStatus, u32 oldPinMode) "of patched code binaries, exHeaders,\n" "IPS code patches and LayeredFS\n" "for specific games.\n\n" - "Also makes certain DLCs\n" - "for out-of-region games work.\n\n" + "Also makes certain DLCs for out-of-\n" + "region games work.\n\n" "Refer to the wiki for instructions.", "Redirect app. threads that would spawn\n" @@ -857,6 +859,15 @@ void configMenu(bool oldPinStatus, u32 oldPinMode) "Enable showing the GBA boot screen\n" "when booting GBA games.", + + "Force audio output to headphones.\n\n" + "Currently only for NATIVE_FIRM.\n\n" + "Due to software limitations, this gets\n" + "undone if you actually insert then\n" + "remove HPs (just enter then exit sleep\n" + "mode if this happens).\n\n" + "Also gets bypassed for camera shutter\n" + "sound.", }; FirmwareSource nandType = FIRMWARE_SYSNAND; @@ -892,6 +903,7 @@ void configMenu(bool oldPinStatus, u32 oldPinMode) { .visible = ISN3DS }, { .visible = true }, { .visible = true }, + { .visible = true }, }; //Calculate the amount of the various kinds of options and pre-select the first single one diff --git a/arm9/source/config.h b/arm9/source/config.h index 7e7dae0..00de4c1 100644 --- a/arm9/source/config.h +++ b/arm9/source/config.h @@ -36,7 +36,7 @@ #define CONFIG_FILE "config.bin" #define CONFIG_VERSIONMAJOR 3 -#define CONFIG_VERSIONMINOR 4 +#define CONFIG_VERSIONMINOR 5 #define BOOTCFG_NAND BOOTCONFIG(0, 7) #define BOOTCFG_FIRM BOOTCONFIG(3, 7) @@ -62,6 +62,7 @@ enum singleOptions REDIRECTAPPTHREADS, PATCHVERSTRING, SHOWGBABOOT, + FORCEHEADPHONEOUTPUT, PATCHUNITINFO, DISABLEARM11EXCHANDLERS, ENABLESAFEFIRMROSALINA, diff --git a/k11_extension/include/config.h b/k11_extension/include/config.h index 07d12de..b367b38 100644 --- a/k11_extension/include/config.h +++ b/k11_extension/include/config.h @@ -33,6 +33,7 @@ enum singleOptions REDIRECTAPPTHREADS, PATCHVERSTRING, SHOWGBABOOT, + FORCEHEADPHONEOUTPUT, PATCHUNITINFO, DISABLEARM11EXCHANDLERS, ENABLESAFEFIRMROSALINA, diff --git a/sysmodules/loader/source/patcher.h b/sysmodules/loader/source/patcher.h index 86b32eb..9472431 100644 --- a/sysmodules/loader/source/patcher.h +++ b/sysmodules/loader/source/patcher.h @@ -36,6 +36,7 @@ enum singleOptions REDIRECTAPPTHREADS, PATCHVERSTRING, SHOWGBABOOT, + FORCEHEADPHONEOUTPUT, PATCHUNITINFO, DISABLEARM11EXCHANDLERS, ENABLESAFEFIRMROSALINA, diff --git a/sysmodules/pm/source/luma.h b/sysmodules/pm/source/luma.h index 54e6c9e..d73a610 100644 --- a/sysmodules/pm/source/luma.h +++ b/sysmodules/pm/source/luma.h @@ -30,6 +30,7 @@ enum singleOptions REDIRECTAPPTHREADS, PATCHVERSTRING, SHOWGBABOOT, + FORCEHEADPHONEOUTPUT, PATCHUNITINFO, DISABLEARM11EXCHANDLERS, ENABLESAFEFIRMROSALINA, diff --git a/sysmodules/rosalina/data/config_template.ini b/sysmodules/rosalina/data/config_template.ini index 697b4fe40fc882f3cf88d1a8f34a03a2d0313855..1dd2da76846ee72348babc9469d4385591871561 100644 GIT binary patch delta 302 zcmX|-F-`+P3`Ogt;R>G)B$NWtQAI#RibMz@+SRb*%?z3ykH#Kgs~mt9dX9j^B{&wd zS&7DyW&6MX{dN9(@%=H|uQ<1a%td|N!4&HmwMj*x3`fQ$$B|?)zr. +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include <3ds/types.h> + +void handleShellOpened(void); diff --git a/sysmodules/rosalina/source/luma_config.c b/sysmodules/rosalina/source/luma_config.c index 1cba607..a2cb809 100644 --- a/sysmodules/rosalina/source/luma_config.c +++ b/sysmodules/rosalina/source/luma_config.c @@ -150,7 +150,7 @@ static size_t LumaConfig_SaveLumaIniConfigToStr(char *out, const CfgData *cfg) (int)CONFIG(AUTOBOOTEMU), (int)CONFIG(USEEMUFIRM), (int)CONFIG(LOADEXTFIRMSANDMODULES), (int)CONFIG(PATCHGAMES), (int)CONFIG(REDIRECTAPPTHREADS), (int)CONFIG(PATCHVERSTRING), - (int)CONFIG(SHOWGBABOOT), + (int)CONFIG(SHOWGBABOOT), (int)CONFIG(FORCEHEADPHONEOUTPUT), 1 + (int)MULTICONFIG(DEFAULTEMU), 4 - (int)MULTICONFIG(BRIGHTNESS), splashPosStr, (unsigned int)cfg->splashDurationMsec, diff --git a/sysmodules/rosalina/source/main.c b/sysmodules/rosalina/source/main.c index 6bd77cb..eb0bbef 100644 --- a/sysmodules/rosalina/source/main.c +++ b/sysmodules/rosalina/source/main.c @@ -40,6 +40,7 @@ #include "minisoc.h" #include "draw.h" #include "bootdiag.h" +#include "shell.h" #include "task_runner.h" @@ -165,13 +166,7 @@ static void handleShellNotification(u32 notificationId) // Note that this notification is also fired on system init. // Sequence goes like this: MCU fires notif. 0x200 on shell open // and shell close, then NS demuxes it and fires 0x213 and 0x214. - - // We need to check here if GSP has done its init stuff, in particular - // clock and reset, otherwise we'll cause core1 to be in a waitstate - // forever (if we access a GPU reg while the GPU block's clock is off). - // (GSP does its init before registering its services) - if (isServiceUsable("gsp::Gpu")) - ScreenFiltersMenu_RestoreSettings(); + handleShellOpened(); menuShouldExit = false; } else { // Shell closed diff --git a/sysmodules/rosalina/source/menu.c b/sysmodules/rosalina/source/menu.c index 9ebc4a2..f3aafe0 100644 --- a/sysmodules/rosalina/source/menu.c +++ b/sysmodules/rosalina/source/menu.c @@ -36,6 +36,7 @@ #include "menus/cheats.h" #include "minisoc.h" #include "menus/screen_filters.h" +#include "shell.h" u32 menuCombo = 0; bool isHidInitialized = false; @@ -261,10 +262,11 @@ void menuThreadMain(void) if(isN3DS) N3DSMenu_UpdateStatus(); - while (!isServiceUsable("ac:u") || !isServiceUsable("hid:USER") || !isServiceUsable("gsp::Gpu")) + while (!isServiceUsable("ac:u") || !isServiceUsable("hid:USER") || !isServiceUsable("gsp::Gpu") || !isServiceUsable("cdc:CHK")) svcSleepThread(250 * 1000 * 1000LL); ScreenFiltersMenu_LoadConfig(); + handleShellOpened(); hidInit(); // assume this doesn't fail isHidInitialized = true; diff --git a/sysmodules/rosalina/source/menus/screen_filters.c b/sysmodules/rosalina/source/menus/screen_filters.c index f80d984..20aa629 100644 --- a/sysmodules/rosalina/source/menus/screen_filters.c +++ b/sysmodules/rosalina/source/menus/screen_filters.c @@ -227,8 +227,6 @@ void ScreenFiltersMenu_LoadConfig(void) svcGetSystemInfo(&out, 0x10000, 0x10C); bottomScreenFilter.invert = (bool)out; - - ScreenFiltersMenu_RestoreSettings(); } DEF_CCT_SETTER(6500, Default) diff --git a/sysmodules/rosalina/source/shell.c b/sysmodules/rosalina/source/shell.c new file mode 100644 index 0000000..b04d702 --- /dev/null +++ b/sysmodules/rosalina/source/shell.c @@ -0,0 +1,82 @@ +/* +* This file is part of Luma3DS +* Copyright (C) 2023 Aurora Wright, TuxSH +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include <3ds.h> +#include "shell.h" +#include "utils.h" +#include "screen_filters.h" +#include "luma_config.h" + +static void forceHeadphoneOutput(void) +{ + // DSP/Codec sysmodule already have a way to force headphone output, + // but it's only for when the shell is closed (applied on shell close, + // cleared on shell opened); that mechanism is usually used by apps + // which have a "jukebox" feature (e.g Pokémon SMD). + + // This whole thing here is fragile and doesn't mesh well with the "codec" + // sysmodule. For example, inserting then removing HPs will undo what this + // function does. + + // TODO: stop opening and closing cdc:CHK (and mcu::HWC), which + // unecessarily spawns and despawns threads. + + // Wait for CSND to do its job + svcSleepThread(20 * 1000 * 1000LL); + + Handle *cdcChkHandlePtr = cdcChkGetSessionHandle(); + *cdcChkHandlePtr = 0; + + Result res = srvGetServiceHandle(cdcChkHandlePtr, "cdc:CHK"); + // Try to steal the handle if some other process is using the service (custom SVC) + if (R_FAILED(res)) + res = svcControlService(SERVICEOP_STEAL_CLIENT_SESSION, cdcChkHandlePtr, "cdc:CHK"); + + if (R_FAILED(res)) + return; + + u8 reg = 0x30; // Enable override selection (always set), then select HP. + res = CDCCHK_WriteRegisters2(100, 69, ®, 1); + + svcCloseHandle(*cdcChkHandlePtr); +} + +void handleShellOpened(void) +{ + s64 out = 0; + svcGetSystemInfo(&out, 0x10000, 3); + u32 config = (u32)out; + + // We need to check here if GSP has done its init stuff, in particular + // clock and reset, otherwise we'll cause core1 to be in a waitstate + // forever (if we access a GPU reg while the GPU block's clock is off). + // (GSP does its init before registering its services) + if (isServiceUsable("gsp::Gpu")) + ScreenFiltersMenu_RestoreSettings(); + + if ((config & BIT(FORCEHEADPHONEOUTPUT)) != 0 && isServiceUsable("cdc:CHK")) + forceHeadphoneOutput(); +}