Get-Hip/GetHip/AudioPlayer/TDAudioPlayer.m

262 lines
7.6 KiB
Objective-C
Executable File

//
// TDAudioPlayer.m
// TDAudioPlayer
//
// Created by Tony DiPasquale on 10/16/13.
// Copyright (c) 2013 Tony DiPasquale. The MIT License (MIT).
//
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
#import "TDAudioPlayer.h"
@interface TDAudioPlayer ()
@property (strong, nonatomic) TDAudioInputStreamer *streamer;
@property (strong, nonatomic) NSMutableDictionary *nowPlayingMetaInfo;
@property (assign, nonatomic) TDAudioPlayerState state;
@property (strong, nonatomic) NSTimer *timer;
@property (assign, nonatomic) NSUInteger elapsedTime;
@end
@implementation TDAudioPlayer
+ (instancetype)sharedAudioPlayer
{
static TDAudioPlayer *player;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
player = [[TDAudioPlayer alloc] init];
});
return player;
}
- (instancetype)init
{
self = [super init];
if (!self) return nil;
[[AVAudioSession sharedInstance] setActive:YES error:nil];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionDidInterrupt:) name:AVAudioSessionInterruptionNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionDidChangeRoute:) name:AVAudioSessionRouteChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioDidStartPlaying) name:TDAudioStreamDidStartPlayingNotification object:nil];
return self;
}
#pragma mark - Audio Loading
- (void)loadAudioFromURL:(NSURL *)url
{
[self loadAudioFromURL:url withMetaData:nil];
}
- (void)loadAudioFromURL:(NSURL *)url withMetaData:(TDAudioMetaInfo *)meta
{
[self reset];
self.streamer = [[TDAudioInputStreamer alloc] initWithURL:url];
[self changeAudioMetaInfo:meta];
}
- (void)loadAudioFromStream:(NSInputStream *)stream
{
[self loadAudioFromStream:stream withMetaData:nil];
}
- (void)loadAudioFromStream:(NSInputStream *)stream withMetaData:(TDAudioMetaInfo *)meta
{
[self reset];
self.streamer = [[TDAudioInputStreamer alloc] initWithInputStream:stream];
[self changeAudioMetaInfo:meta];
}
- (void)reset
{
[self.streamer stop];
self.streamer = nil;
self.state = TDAudioPlayerStateStopped;
self.elapsedTime = 0;
[self clearTimer];
}
#pragma mark - Audio Controls
- (void)play
{
if (!self.streamer || self.state == TDAudioPlayerStatePlaying) return;
if (self.state == TDAudioPlayerStateStopped) return [self start];
[self.streamer resume];
[self setNowPlayingInfoWithPlaybackRate:@1];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(elapseTime) userInfo:nil repeats:YES];
self.state = TDAudioPlayerStatePlaying;
[[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerDidPlayNotification object:nil];
}
- (void)start
{
if (self.state == TDAudioPlayerStateStarting) return;
[self.streamer start];
self.state = TDAudioPlayerStateStarting;
[[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerDidPlayNotification object:nil];
}
- (void)pause
{
if (!self.streamer || self.state == TDAudioPlayerStatePaused) return;
[self setNowPlayingInfoWithPlaybackRate:@0.000001f];
[self clearTimer];
[self.streamer pause];
self.state = TDAudioPlayerStatePaused;
[[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerDidPauseNotification object:nil];
}
- (void)stop
{
if (!self.streamer || self.state == TDAudioPlayerStateStopped) return;
[self reset];
[[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerDidStopNotification object:nil];
}
#pragma mark - Remote Events
- (void)handleRemoteControlEvent:(UIEvent *)event
{
if (event.type != UIEventTypeRemoteControl) return;
switch (event.subtype) {
case UIEventSubtypeRemoteControlPause:
[self pause];
break;
case UIEventSubtypeRemoteControlPlay:
[self play];
break;
case UIEventSubtypeRemoteControlStop:
[self stop];
break;
case UIEventSubtypeRemoteControlTogglePlayPause:
if (self.state == TDAudioPlayerStatePlaying) {
[self pause];
} else {
[self play];
}
break;
case UIEventSubtypeRemoteControlNextTrack:
[[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerNextTrackRequestNotification object:nil];
break;
case UIEventSubtypeRemoteControlPreviousTrack:
[[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerPreviousTrackRequestNotification object:nil];
break;
default:
break;
}
}
#pragma mark - Timer Helpers
- (void)elapseTime
{
self.elapsedTime++;
}
- (void)clearTimer
{
[self.timer invalidate];
self.timer = nil;
}
#pragma mark - Now Playing Info Helpers
- (void)changeAudioMetaInfo:(TDAudioMetaInfo *)meta
{
[self setNowPlayingInfoWithMetaInfo:meta];
if (!meta)
return [[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerDidChangeAudioNotification object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:TDAudioPlayerDidChangeAudioNotification object:nil userInfo:@{@"meta": meta}];
}
- (void)setNowPlayingInfoWithMetaInfo:(TDAudioMetaInfo *)info
{
if (!info) {
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = @{};
return;
}
if (!self.nowPlayingMetaInfo)
self.nowPlayingMetaInfo = [NSMutableDictionary dictionary];
else
[self.nowPlayingMetaInfo removeAllObjects];
if (info.title) self.nowPlayingMetaInfo[MPMediaItemPropertyTitle] = info.title;
if (info.artist) self.nowPlayingMetaInfo[MPMediaItemPropertyArtist] = info.artist;
if (info.albumArtLarge) {
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:info.albumArtLarge]]]];
if (artwork) self.nowPlayingMetaInfo[MPMediaItemPropertyArtwork] = artwork;
}
if (info.duration) self.nowPlayingMetaInfo[MPMediaItemPropertyPlaybackDuration] = info.duration;
[self setNowPlayingInfoWithPlaybackRate:@0];
}
- (void)setNowPlayingInfoWithPlaybackRate:(NSNumber *)rate
{
if (!self.nowPlayingMetaInfo) return;
self.nowPlayingMetaInfo[MPNowPlayingInfoPropertyPlaybackRate] = rate;
self.nowPlayingMetaInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = @(self.elapsedTime);
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = self.nowPlayingMetaInfo;
}
#pragma mark - Notification Handlers
- (void)audioSessionDidInterrupt:(NSNotification *)notification
{
NSUInteger type = [notification.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
if (type == AVAudioSessionInterruptionTypeBegan)
[self pause];
}
- (void)audioSessionDidChangeRoute:(NSNotification *)notification
{
NSUInteger reason = [notification.userInfo[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable)
[self pause];
}
- (void)audioDidStartPlaying
{
[self setNowPlayingInfoWithPlaybackRate:@1];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(elapseTime) userInfo:nil repeats:YES];
self.state = TDAudioPlayerStatePlaying;
}
#pragma mark - Cleanup
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end