Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS: Add audio session mode controls #19200

Merged
merged 3 commits into from
May 27, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Common/GPU/Vulkan/VulkanLoader.cpp
Original file line number Diff line number Diff line change
@@ -382,7 +382,7 @@ bool VulkanMayBeAvailable() {
#if PPSSPP_PLATFORM(IOS)
g_vulkanAvailabilityChecked = true;
g_vulkanMayBeAvailable = System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 13;
INFO_LOG(SYSTEM, "VulkanMayBeAvailable: Detected version: %d", System_GetPropertyInt(SYSPROP_SYSTEMVERSION));
INFO_LOG(SYSTEM, "VulkanMayBeAvailable: Detected version: %d", (int)System_GetPropertyInt(SYSPROP_SYSTEMVERSION));
return g_vulkanMayBeAvailable;
#else
// Unsupported in VR at the moment
1 change: 1 addition & 0 deletions Common/System/System.h
Original file line number Diff line number Diff line change
@@ -230,6 +230,7 @@ enum class SystemNotification {
KEEP_SCREEN_AWAKE,
ACTIVITY,
UI_STATE_CHANGED,
AUDIO_MODE_CHANGED,
};

// I guess it's not super great architecturally to centralize this, since it's not general - but same with a lot of
2 changes: 2 additions & 0 deletions Core/Config.cpp
Original file line number Diff line number Diff line change
@@ -700,6 +700,8 @@ static const ConfigSetting soundSettings[] = {
ConfigSetting("AchievementSoundVolume", &g_Config.iAchievementSoundVolume, 6, CfgFlag::PER_GAME),
ConfigSetting("AudioDevice", &g_Config.sAudioDevice, "", CfgFlag::DEFAULT),
ConfigSetting("AutoAudioDevice", &g_Config.bAutoAudioDevice, true, CfgFlag::DEFAULT),
ConfigSetting("AudioMixWithOthers", &g_Config.bAudioMixWithOthers, true, CfgFlag::DEFAULT),
ConfigSetting("AudioRespectSilentMode", &g_Config.bAudioRespectSilentMode, false, CfgFlag::DEFAULT),
ConfigSetting("UseNewAtrac", &g_Config.bUseNewAtrac, false, CfgFlag::DEFAULT),
};

4 changes: 4 additions & 0 deletions Core/Config.h
Original file line number Diff line number Diff line change
@@ -280,6 +280,10 @@ struct Config {
bool bAutoAudioDevice;
bool bUseNewAtrac;

// iOS only for now
bool bAudioMixWithOthers;
bool bAudioRespectSilentMode;

// UI
bool bShowDebuggerOnLoad;
int iShowStatusFlags;
18 changes: 17 additions & 1 deletion UI/GameSettingsScreen.cpp
Original file line number Diff line number Diff line change
@@ -617,7 +617,23 @@ void GameSettingsScreen::CreateAudioSettings(UI::ViewGroup *audioSettings) {
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);

audioSettings->Add(new ItemHeader(ms->T("Audio")));
CheckBox *enableSound = audioSettings->Add(new CheckBox(&g_Config.bEnableSound,a->T("Enable Sound")));
CheckBox *enableSound = audioSettings->Add(new CheckBox(&g_Config.bEnableSound,a->T("Enable Sound")));

#if PPSSPP_PLATFORM(IOS)
CheckBox *respectSilentMode = audioSettings->Add(new CheckBox(&g_Config.bAudioRespectSilentMode, a->T("Respect silent mode")));
respectSilentMode->OnClick.Add([=](EventParams &e) {
System_Notify(SystemNotification::AUDIO_MODE_CHANGED);
return UI::EVENT_DONE;
});
respectSilentMode->SetEnabledPtr(&g_Config.bEnableSound);
CheckBox *mixWithOthers = audioSettings->Add(new CheckBox(&g_Config.bAudioMixWithOthers, a->T("Mix audio with other apps")));
mixWithOthers->OnClick.Add([=](EventParams &e) {
System_Notify(SystemNotification::AUDIO_MODE_CHANGED);
return UI::EVENT_DONE;
});
mixWithOthers->SetEnabledPtr(&g_Config.bEnableSound);
#endif

PopupSliderChoice *volume = audioSettings->Add(new PopupSliderChoice(&g_Config.iGlobalVolume, VOLUME_OFF, VOLUME_FULL, VOLUME_FULL, a->T("Global volume"), screenManager()));
volume->SetEnabledPtr(&g_Config.bEnableSound);
volume->SetZeroLabel(a->T("Mute"));
10 changes: 6 additions & 4 deletions ios/DisplayManager.mm
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
//

#import "DisplayManager.h"
#import "iOSCoreAudio.h"
#import "ViewController.h"
#import "AppDelegate.h"
#include "Common/System/Display.h"
@@ -64,6 +65,9 @@ - (void)setupDisplayListener {
[self setOriginalFrame: [gameWindow frame]];
[self setOriginalBounds:[gameWindow bounds]];
[self setOriginalTransform:[gameWindow transform]];

// TODO: From iOS 13, should use UIScreenDidConnectNotification instead of the below.

// Display connected
[[NSNotificationCenter defaultCenter] addObserverForName:UIScreenDidConnectNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull notification) {
UIScreen *screen = (UIScreen *) notification.object;
@@ -74,8 +78,7 @@ - (void)setupDisplayListener {
return;
}
// Ignore mute switch when connected to external display
NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
iOSCoreAudioSetDisplayConnected(true);
[self updateScreen:screen];
}];
// Display disconnected
@@ -89,8 +92,7 @@ - (void)setupDisplayListener {
UIScreen *newScreen = [[self extDisplays] lastObject];
[self updateScreen:newScreen];
} else {
NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&error];
iOSCoreAudioSetDisplayConnected(false);
[self updateScreen:[UIScreen mainScreen]];
}
}];
7 changes: 6 additions & 1 deletion ios/iOSCoreAudio.h
Original file line number Diff line number Diff line change
@@ -19,4 +19,9 @@
// Originally written by jtraynham

void iOSCoreAudioInit();
void iOSCoreAudioShutdown();
void iOSCoreAudioShutdown();

// Ignore mute switch when connected to external display.
// Also, obey other settings.
void iOSCoreAudioUpdateSession();
void iOSCoreAudioSetDisplayConnected(bool connected);
49 changes: 48 additions & 1 deletion ios/iOSCoreAudio.mm
Original file line number Diff line number Diff line change
@@ -21,13 +21,58 @@
#include "iOSCoreAudio.h"

#include "Common/Log.h"
#include "Core/Config.h"

#include <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>

#define SAMPLE_RATE 44100

static AudioComponentInstance audioInstance = nil;
static bool g_displayConnected = false;

void iOSCoreAudioUpdateSession() {
NSError *error = nil;
if (g_displayConnected) {
INFO_LOG(AUDIO, "Display connected, setting Playback mode");
// Special handling when a display is connected. Always exclusive.
// Let's revisit this later.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
return;
}

INFO_LOG(AUDIO, "RespectSilentMode: %d MixWithOthers: %d", g_Config.bAudioRespectSilentMode, g_Config.bAudioMixWithOthers);

// 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];

// Here, we apply the settings.
const bool mixWithOthers = g_Config.bAudioMixWithOthers;
if (g_Config.bAudioMixWithOthers) {
if (g_Config.bAudioRespectSilentMode) {
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&error];
} else {
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&error];
}
} else {
if (g_Config.bAudioRespectSilentMode) {
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategorySoloAmbient error:&error];
} else {
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:0 error:&error];
}
// Can't achieve exclusive + respect silent mode
}

if (error) {
NSLog(@"%@", error);
}
}

void iOSCoreAudioSetDisplayConnected(bool connected) {
g_displayConnected = connected;
iOSCoreAudioUpdateSession();
}

int NativeMix(short *audio, int numSamples, int sampleRate);

@@ -64,6 +109,8 @@ OSStatus iOSCoreAudioCallback(void *inRefCon,

void iOSCoreAudioInit()
{
iOSCoreAudioUpdateSession();

NSError *error = nil;
AVAudioSession *session = [AVAudioSession sharedInstance];
if (![session setActive:YES error:&error]) {
@@ -75,7 +122,7 @@ void iOSCoreAudioInit()
NSLog(@"%@", error.localizedFailureReason);
}
}

if (audioInstance) {
// Already running
return;
6 changes: 6 additions & 0 deletions ios/main.mm
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@
#import "AppDelegate.h"
#import "PPSSPPUIApplication.h"
#import "ViewController.h"
#import "iOSCoreAudio.h"

#include "Common/MemoryUtil.h"
#include "Common/System/NativeApp.h"
@@ -393,6 +394,11 @@ void System_Notify(SystemNotification notification) {
}
});
break;
case SystemNotification::AUDIO_MODE_CHANGED:
dispatch_async(dispatch_get_main_queue(), ^{
iOSCoreAudioUpdateSession();
});
break;
default:
break;
}