From b8c9e82fe68f080c2b9b1f55978e59281a09fdfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 22 Oct 2026 12:31:43 +0200 Subject: [PATCH] iOS: Leave PlayAndRecord when a game stops recording The session category was switched before the audio unit's input was turned off, and iOS refuses ('!pri') to leave PlayAndRecord while the input runs. So after a game used the microphone, the session stayed in PlayAndRecord, ignoring the silent mode setting. Switch it while the unit is stopped instead. Also log the two setCategory failures separately, and the route's input. Co-Authored-By: Claude Opus 5.5 (1M context) --- ios/iOSCoreAudio.mm | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/ios/iOSCoreAudio.mm b/ios/iOSCoreAudio.mm index 9d1c2a63c8..3293af693a 100644 --- a/ios/iOSCoreAudio.mm +++ b/ios/iOSCoreAudio.mm @@ -65,7 +65,10 @@ void iOSCoreAudioUpdateSession() { #else // Hacky hack to force iOS to re-evaluate. // Switching from CatogoryPlayback to CategoryPlayback with an option otherwise does nothing. - [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAudioProcessing error:&error]; + NSError *hackError = nil; + if (![[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAudioProcessing error:&hackError]) { + NSLog(@"Switching to AudioProcessing failed: %@", hackError); + } // Here, we apply the settings. if (g_micEnabled) { @@ -94,7 +97,7 @@ void iOSCoreAudioUpdateSession() { #endif if (error) { - NSLog(@"%@", error); + NSLog(@"Setting the audio session category failed: %@", error); } } @@ -103,6 +106,9 @@ void iOSCoreAudioLogRoute(const char *reason) { for (AVAudioSessionPortDescription *port in session.currentRoute.outputs) { NSLog(@"Audio route (%s): %@ '%@' (category %@, options %d)", reason, port.portType, port.portName, session.category, (int)session.categoryOptions); } + for (AVAudioSessionPortDescription *port in session.currentRoute.inputs) { + NSLog(@"Audio input (%s): %@ '%@'", reason, port.portType, port.portName); + } } void NativeMix(short *audio, int numSamples, int sampleRateHz, void *userdata); @@ -317,14 +323,15 @@ void iOSCoreAudioShutdown() // Brings the unit in line with g_micEnabled. Main thread. static void UpdateMicrophone() { - iOSCoreAudioUpdateSession(); if (!audioInstance) { + // Sets up the session too. iOSCoreAudioInit(); return; } if (!g_Config.bEnableSound && !g_micEnabled) { // The unit was only running for the microphone. iOSCoreAudioShutdown(); + iOSCoreAudioUpdateSession(); return; } if (g_micEnabled == g_micInputOn && (!g_micEnabled || g_micSampleRate == g_micInputRate)) { @@ -333,6 +340,8 @@ static void UpdateMicrophone() { AudioOutputUnitStop(audioInstance); AudioUnitUninitialize(audioInstance); + // Only with the unit stopped: iOS refuses (with '!pri') to leave PlayAndRecord while the input runs. + iOSCoreAudioUpdateSession(); ConfigureInput(audioInstance, g_micEnabled); OSStatus err = AudioUnitInitialize(audioInstance); if (err == noErr) {