235 lines
6.5 KiB
Objective-C
235 lines
6.5 KiB
Objective-C
//
|
|
// AudioSessionManager.m
|
|
// keyBoard
|
|
//
|
|
// Created by Mac on 2026/1/15.
|
|
//
|
|
|
|
#import "AudioSessionManager.h"
|
|
|
|
@interface AudioSessionManager ()
|
|
@property(nonatomic, assign) BOOL isSessionActive;
|
|
@end
|
|
|
|
@implementation AudioSessionManager
|
|
|
|
#pragma mark - Singleton
|
|
|
|
+ (instancetype)sharedManager {
|
|
static AudioSessionManager *instance = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
instance = [[AudioSessionManager alloc] init];
|
|
});
|
|
return instance;
|
|
}
|
|
|
|
- (instancetype)init {
|
|
self = [super init];
|
|
if (self) {
|
|
_isSessionActive = NO;
|
|
[self setupNotifications];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
}
|
|
|
|
#pragma mark - Notifications
|
|
|
|
- (void)setupNotifications {
|
|
// 监听音频会话中断通知
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver:self
|
|
selector:@selector(handleInterruption:)
|
|
name:AVAudioSessionInterruptionNotification
|
|
object:nil];
|
|
|
|
// 监听音频路由变化通知
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver:self
|
|
selector:@selector(handleRouteChange:)
|
|
name:AVAudioSessionRouteChangeNotification
|
|
object:nil];
|
|
}
|
|
|
|
- (void)handleInterruption:(NSNotification *)notification {
|
|
NSDictionary *info = notification.userInfo;
|
|
AVAudioSessionInterruptionType type =
|
|
[info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
|
|
|
|
if (type == AVAudioSessionInterruptionTypeBegan) {
|
|
// 中断开始:来电、闹钟等
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if ([self.delegate
|
|
respondsToSelector:@selector(audioSessionManagerDidInterrupt:)]) {
|
|
[self.delegate audioSessionManagerDidInterrupt:
|
|
KBAudioSessionInterruptionTypeBegan];
|
|
}
|
|
});
|
|
} else if (type == AVAudioSessionInterruptionTypeEnded) {
|
|
// 中断结束
|
|
AVAudioSessionInterruptionOptions options =
|
|
[info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
|
|
if (options & AVAudioSessionInterruptionOptionShouldResume) {
|
|
// 可以恢复播放
|
|
[self activateSession:nil];
|
|
}
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if ([self.delegate
|
|
respondsToSelector:@selector(audioSessionManagerDidInterrupt:)]) {
|
|
[self.delegate audioSessionManagerDidInterrupt:
|
|
KBAudioSessionInterruptionTypeEnded];
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
- (void)handleRouteChange:(NSNotification *)notification {
|
|
NSDictionary *info = notification.userInfo;
|
|
AVAudioSessionRouteChangeReason reason =
|
|
[info[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
|
|
|
|
switch (reason) {
|
|
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
|
|
case AVAudioSessionRouteChangeReasonNewDeviceAvailable: {
|
|
// 旧设备不可用(如耳机拔出)或新设备可用(如耳机插入)
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if ([self.delegate respondsToSelector:@selector
|
|
(audioSessionManagerRouteDidChange)]) {
|
|
[self.delegate audioSessionManagerRouteDidChange];
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
#pragma mark - Microphone Permission
|
|
|
|
- (void)requestMicrophonePermission:(void (^)(BOOL))completion {
|
|
AVAudioSession *session = [AVAudioSession sharedInstance];
|
|
|
|
[session requestRecordPermission:^(BOOL granted) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (!granted) {
|
|
if ([self.delegate respondsToSelector:@selector
|
|
(audioSessionManagerMicrophonePermissionDenied)]) {
|
|
[self.delegate audioSessionManagerMicrophonePermissionDenied];
|
|
}
|
|
}
|
|
if (completion) {
|
|
completion(granted);
|
|
}
|
|
});
|
|
}];
|
|
}
|
|
|
|
- (BOOL)hasMicrophonePermission {
|
|
AVAudioSession *session = [AVAudioSession sharedInstance];
|
|
return session.recordPermission == AVAudioSessionRecordPermissionGranted;
|
|
}
|
|
|
|
#pragma mark - Session Configuration
|
|
|
|
- (BOOL)configureForConversation:(NSError **)error {
|
|
AVAudioSession *session = [AVAudioSession sharedInstance];
|
|
|
|
// 配置为录音+播放模式
|
|
// Category: PlayAndRecord - 同时支持录音和播放
|
|
// Mode: VoiceChat - 优化语音通话场景
|
|
// Options:
|
|
// - DefaultToSpeaker: 默认使用扬声器
|
|
// - AllowBluetooth: 允许蓝牙设备
|
|
NSError *categoryError = nil;
|
|
BOOL success =
|
|
[session setCategory:AVAudioSessionCategoryPlayAndRecord
|
|
mode:AVAudioSessionModeVoiceChat
|
|
options:(AVAudioSessionCategoryOptionDefaultToSpeaker |
|
|
AVAudioSessionCategoryOptionAllowBluetooth)
|
|
error:&categoryError];
|
|
|
|
if (!success) {
|
|
if (error) {
|
|
*error = categoryError;
|
|
}
|
|
NSLog(@"[AudioSessionManager] Failed to configure session: %@",
|
|
categoryError.localizedDescription);
|
|
return NO;
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)configureForPlayback:(NSError **)error {
|
|
AVAudioSession *session = [AVAudioSession sharedInstance];
|
|
|
|
// 仅播放模式
|
|
NSError *categoryError = nil;
|
|
BOOL success =
|
|
[session setCategory:AVAudioSessionCategoryPlayback
|
|
mode:AVAudioSessionModeDefault
|
|
options:AVAudioSessionCategoryOptionDefaultToSpeaker
|
|
error:&categoryError];
|
|
|
|
if (!success) {
|
|
if (error) {
|
|
*error = categoryError;
|
|
}
|
|
NSLog(@"[AudioSessionManager] Failed to configure playback: %@",
|
|
categoryError.localizedDescription);
|
|
return NO;
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)activateSession:(NSError **)error {
|
|
if (self.isSessionActive) {
|
|
return YES;
|
|
}
|
|
|
|
AVAudioSession *session = [AVAudioSession sharedInstance];
|
|
NSError *activationError = nil;
|
|
BOOL success = [session setActive:YES error:&activationError];
|
|
|
|
if (!success) {
|
|
if (error) {
|
|
*error = activationError;
|
|
}
|
|
NSLog(@"[AudioSessionManager] Failed to activate session: %@",
|
|
activationError.localizedDescription);
|
|
return NO;
|
|
}
|
|
|
|
self.isSessionActive = YES;
|
|
return YES;
|
|
}
|
|
|
|
- (void)deactivateSession {
|
|
if (!self.isSessionActive) {
|
|
return;
|
|
}
|
|
|
|
AVAudioSession *session = [AVAudioSession sharedInstance];
|
|
NSError *error = nil;
|
|
|
|
// 使用 NotifyOthersOnDeactivation 通知其他应用可以恢复播放
|
|
[session setActive:NO
|
|
withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
|
|
error:&error];
|
|
|
|
if (error) {
|
|
NSLog(@"[AudioSessionManager] Failed to deactivate session: %@",
|
|
error.localizedDescription);
|
|
}
|
|
|
|
self.isSessionActive = NO;
|
|
}
|
|
|
|
@end
|