1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-03 09:57:46 +01:00
Telegram/Telegraph/TGAudioPlayer.m
2016-02-25 01:03:51 +01:00

249 lines
6.8 KiB
Objective-C

/*
* This is the source code of Telegram for iOS v. 1.1
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Peter Iakovlev, 2013.
*/
#import "TGAudioPlayer.h"
#import "ASQueue.h"
#import "TGOpusAudioPlayerAU.h"
#import "TGNativeAudioPlayer.h"
#import "TGObserverProxy.h"
#import "TGAppDelegate.h"
#import <SSignalKit/SSignalKit.h>
#import <AVFoundation/AVFoundation.h>
#import "TGAudioSessionManager.h"
@interface TGAudioPlayer ()
{
bool _music;
bool _controlAudioSession;
bool _proximityState;
TGObserverProxy *_proximityChangedNotification;
TGHolder *_proximityChangeHolder;
SMetaDisposable *_currentAudioSession;
bool _changingProximity;
SMetaDisposable *_routeChangeDisposable;
}
@end
@implementation TGAudioPlayer
+ (TGAudioPlayer *)audioPlayerForPath:(NSString *)path music:(bool)music controlAudioSession:(bool)controlAudioSession
{
if (path == nil)
return nil;
if ([TGOpusAudioPlayerAU canPlayFile:path])
return [[TGOpusAudioPlayerAU alloc] initWithPath:path music:music controlAudioSession:controlAudioSession];
else
return [[TGNativeAudioPlayer alloc] initWithPath:path music:music controlAudioSession:controlAudioSession];
}
- (instancetype)init {
return [self initWithMusic:false controlAudioSession:true];
}
- (instancetype)initWithMusic:(bool)music controlAudioSession:(bool)controlAudioSession
{
self = [super init];
if (self != nil)
{
_music = music;
_controlAudioSession = controlAudioSession;
_currentAudioSession = [[SMetaDisposable alloc] init];
if (!_music && _controlAudioSession) {
_proximityState = TGAppDelegateInstance.deviceProximityState;
_proximityChangedNotification = [[TGObserverProxy alloc] initWithTarget:self targetSelector:@selector(proximityChanged:) name:TGDeviceProximityStateChangedNotification object:nil];
_proximityChangeHolder = [[TGHolder alloc] init];
[TGAppDelegateInstance.deviceProximityListeners addHolder:_proximityChangeHolder];
__weak TGAudioPlayer *weakSelf = self;
_routeChangeDisposable = [[[TGAudioSessionManager routeChange] deliverOn:[SQueue mainQueue]] startWithNext:^(NSNumber *action) {
if ([action intValue] == TGAudioSessionRouteChangePause) {
__strong TGAudioPlayer *strongSelf = weakSelf;
if (strongSelf != nil) {
[strongSelf pause:nil];
[strongSelf _notifyPaused];
}
}
}];
}
}
return self;
}
- (void)dealloc
{
if (!_music) {
[TGAppDelegateInstance.deviceProximityListeners removeHolder:_proximityChangeHolder];
}
}
- (void)play
{
[self playFromPosition:-1.0];
}
- (void)playFromPosition:(NSTimeInterval)__unused position
{
}
- (void)pause:(void (^)())completion
{
if (completion) {
completion();
}
}
- (void)stop
{
}
- (NSTimeInterval)currentPositionSync:(bool)__unused sync
{
return 0.0;
}
- (NSTimeInterval)duration
{
return 0.0;
}
+ (ASQueue *)_playerQueue
{
static ASQueue *queue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
queue = [[ASQueue alloc] initWithName:"org.telegram.audioPlayerQueue"];
});
return queue;
}
- (void)proximityChanged:(NSNotification *)__unused notification
{
if (_music) {
return;
}
bool proximityState = TGAppDelegateInstance.deviceProximityState;
[[TGAudioPlayer _playerQueue] dispatchOnQueue:^
{
_proximityState = proximityState;
bool overridePort = _proximityState && ![TGAudioPlayer isHeadsetPluggedIn];
__weak TGAudioPlayer *weakSelf = self;
_changingProximity = true;
[_currentAudioSession setDisposable:[[TGAudioSessionManager instance] requestSessionWithType:overridePort ? TGAudioSessionTypePlayAndRecordHeadphones : TGAudioSessionTypePlayVoice interrupted:^
{
__strong TGAudioPlayer *strongSelf = weakSelf;
if (strongSelf != nil && !strongSelf->_changingProximity)
{
[strongSelf stop];
[strongSelf _notifyFinished];
}
}]];
_changingProximity = false;
}];
}
- (void)_beginAudioSession
{
if (!_controlAudioSession) {
return;
}
[[TGAudioPlayer _playerQueue] dispatchOnQueue:^
{
__weak TGAudioPlayer *weakSelf = self;
if (_music) {
[_currentAudioSession setDisposable:[[TGAudioSessionManager instance] requestSessionWithType:TGAudioSessionTypePlayMusic interrupted:^
{
__strong TGAudioPlayer *strongSelf = weakSelf;
if (strongSelf != nil && !strongSelf->_changingProximity)
{
[strongSelf pause:nil];
[strongSelf _notifyPaused];
}
}]];
} else {
bool overridePort = _proximityState && ![TGAudioPlayer isHeadsetPluggedIn];
[_currentAudioSession setDisposable:[[TGAudioSessionManager instance] requestSessionWithType:overridePort ? TGAudioSessionTypePlayAndRecordHeadphones : TGAudioSessionTypePlayVoice interrupted:^
{
__strong TGAudioPlayer *strongSelf = weakSelf;
if (strongSelf != nil && !strongSelf->_changingProximity)
{
[strongSelf stop];
[strongSelf _notifyFinished];
}
}]];
}
}];
}
- (void)_endAudioSession
{
if (!_controlAudioSession) {
return;
}
[[TGAudioPlayer _playerQueue] dispatchOnQueue:^
{
[_currentAudioSession setDisposable:nil];
}];
}
- (void)_endAudioSessionFinal
{
if (!_controlAudioSession) {
return;
}
SMetaDisposable *currentAudioSession = _currentAudioSession;
[[TGAudioPlayer _playerQueue] dispatchOnQueue:^
{
[currentAudioSession setDisposable:nil];
}];
}
- (void)_notifyFinished
{
id<TGAudioPlayerDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(audioPlayerDidFinishPlaying:)])
[delegate audioPlayerDidFinishPlaying:self];
}
- (void)_notifyPaused {
id<TGAudioPlayerDelegate> delegate = _delegate;
if ([delegate respondsToSelector:@selector(audioPlayerDidPause:)])
[delegate audioPlayerDidPause:self];
}
+ (bool)isHeadsetPluggedIn
{
AVAudioSessionRouteDescription* route = [[AVAudioSession sharedInstance] currentRoute];
for (AVAudioSessionPortDescription *desc in [route outputs])
{
if ([[desc portType] isEqualToString:AVAudioSessionPortHeadphones])
return true;
}
return false;
}
@end