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
Show file tree
Hide file tree
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
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Common/System/System.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions Core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};

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

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

// UI
bool bShowDebuggerOnLoad;
int iShowStatusFlags;
Expand Down
18 changes: 17 additions & 1 deletion UI/GameSettingsScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand Down
10 changes: 6 additions & 4 deletions ios/DisplayManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

#import "DisplayManager.h"
#import "iOSCoreAudio.h"
#import "ViewController.h"
#import "AppDelegate.h"
#include "Common/System/Display.h"
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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]];
}
}];
Expand Down
7 changes: 6 additions & 1 deletion ios/iOSCoreAudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -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);

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

void iOSCoreAudioInit()
{
iOSCoreAudioUpdateSession();

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

if (audioInstance) {
// Already running
return;
Expand Down
6 changes: 6 additions & 0 deletions ios/main.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}
Expand Down
Loading