Files
EZAudio/Classes/Core Components/EZAudioPlayer.m

441 lines
14 KiB
Objective-C

//
// EZAudioPlayer.m
// EZAudio
//
// Created by Syed Haris Ali on 1/16/14.
// Copyright (c) 2014 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "EZAudioPlayer.h"
#import "EZAudioUtilities.h"
//------------------------------------------------------------------------------
#pragma mark - Notifications
//------------------------------------------------------------------------------
NSString * const EZAudioPlayerDidChangeAudioFileNotification = @"EZAudioPlayerDidChangeAudioFileNotification";
NSString * const EZAudioPlayerDidChangeOutputDeviceNotification = @"EZAudioPlayerDidChangeOutputDeviceNotification";
NSString * const EZAudioPlayerDidChangePanNotification = @"EZAudioPlayerDidChangePanNotification";
NSString * const EZAudioPlayerDidChangePlayStateNotification = @"EZAudioPlayerDidChangePlayStateNotification";
NSString * const EZAudioPlayerDidChangeVolumeNotification = @"EZAudioPlayerDidChangeVolumeNotification";
NSString * const EZAudioPlayerDidReachEndOfFileNotification = @"EZAudioPlayerDidReachEndOfFileNotification";
NSString * const EZAudioPlayerDidSeekNotification = @"EZAudioPlayerDidSeekNotification";
//------------------------------------------------------------------------------
#pragma mark - EZAudioPlayer (Implementation)
//------------------------------------------------------------------------------
@implementation EZAudioPlayer
//------------------------------------------------------------------------------
#pragma mark - Class Methods
//------------------------------------------------------------------------------
+ (instancetype)audioPlayer
{
return [[self alloc] init];
}
//------------------------------------------------------------------------------
+ (instancetype)audioPlayerWithDelegate:(id<EZAudioPlayerDelegate>)delegate
{
return [[self alloc] initWithDelegate:delegate];
}
//------------------------------------------------------------------------------
+ (instancetype)audioPlayerWithAudioFile:(EZAudioFile *)audioFile
{
return [[self alloc] initWithAudioFile:audioFile];
}
//------------------------------------------------------------------------------
+ (instancetype)audioPlayerWithAudioFile:(EZAudioFile *)audioFile
delegate:(id<EZAudioPlayerDelegate>)delegate
{
return [[self alloc] initWithAudioFile:audioFile
delegate:delegate];
}
//------------------------------------------------------------------------------
+ (instancetype)audioPlayerWithURL:(NSURL *)url
{
return [[self alloc] initWithURL:url];
}
//------------------------------------------------------------------------------
+ (instancetype)audioPlayerWithURL:(NSURL *)url
delegate:(id<EZAudioPlayerDelegate>)delegate
{
return [[self alloc] initWithURL:url delegate:delegate];
}
//------------------------------------------------------------------------------
#pragma mark - Initialization
//------------------------------------------------------------------------------
- (instancetype)init
{
self = [super init];
if (self)
{
[self setup];
}
return self;
}
//------------------------------------------------------------------------------
- (instancetype)initWithDelegate:(id<EZAudioPlayerDelegate>)delegate
{
self = [self init];
if (self)
{
self.delegate = delegate;
}
return self;
}
//------------------------------------------------------------------------------
- (instancetype)initWithAudioFile:(EZAudioFile *)audioFile
{
return [self initWithAudioFile:audioFile delegate:nil];
}
//------------------------------------------------------------------------------
- (instancetype)initWithAudioFile:(EZAudioFile *)audioFile
delegate:(id<EZAudioPlayerDelegate>)delegate
{
self = [self initWithDelegate:delegate];
if (self)
{
self.audioFile = audioFile;
}
return self;
}
//------------------------------------------------------------------------------
- (instancetype)initWithURL:(NSURL *)url
{
return [self initWithURL:url delegate:nil];
}
//------------------------------------------------------------------------------
- (instancetype)initWithURL:(NSURL *)url
delegate:(id<EZAudioPlayerDelegate>)delegate
{
self = [self initWithDelegate:delegate];
if (self)
{
self.audioFile = [EZAudioFile audioFileWithURL:url delegate:self];
}
return self;
}
//------------------------------------------------------------------------------
#pragma mark - Singleton
//------------------------------------------------------------------------------
+ (instancetype)sharedAudioPlayer
{
static EZAudioPlayer *player;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
player = [[self alloc] init];
});
return player;
}
//------------------------------------------------------------------------------
#pragma mark - Setup
//------------------------------------------------------------------------------
- (void)setup
{
self.output = [EZOutput output];
}
//------------------------------------------------------------------------------
#pragma mark - Getters
//------------------------------------------------------------------------------
- (NSTimeInterval)currentTime
{
return [self.audioFile currentTime];
}
//------------------------------------------------------------------------------
- (EZAudioDevice *)device
{
return [self.output device];
}
//------------------------------------------------------------------------------
- (NSTimeInterval)duration
{
return [self.audioFile duration];
}
//------------------------------------------------------------------------------
- (NSString *)formattedCurrentTime
{
return [self.audioFile formattedCurrentTime];
}
//------------------------------------------------------------------------------
- (NSString *)formattedDuration
{
return [self.audioFile formattedDuration];
}
//------------------------------------------------------------------------------
- (SInt64)frameIndex
{
return [self.audioFile frameIndex];
}
//------------------------------------------------------------------------------
- (BOOL)isPlaying
{
return [self.output isPlaying];
}
//------------------------------------------------------------------------------
- (float)pan
{
return [self.output pan];
}
//------------------------------------------------------------------------------
- (SInt64)totalFrames
{
return [self.audioFile totalFrames];
}
//------------------------------------------------------------------------------
- (float)volume
{
return [self.output volume];
}
//------------------------------------------------------------------------------
#pragma mark - Setters
//------------------------------------------------------------------------------
- (void)setAudioFile:(EZAudioFile *)audioFile
{
_audioFile = [audioFile copy];
_audioFile.delegate = self;
AudioStreamBasicDescription inputFormat = _audioFile.clientFormat;
[self.output setInputFormat:inputFormat];
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangeAudioFileNotification
object:self];
}
//------------------------------------------------------------------------------
- (void)setCurrentTime:(NSTimeInterval)currentTime
{
[self.audioFile setCurrentTime:currentTime];
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidSeekNotification
object:self];
}
//------------------------------------------------------------------------------
- (void)setDevice:(EZAudioDevice *)device
{
[self.output setDevice:device];
}
//------------------------------------------------------------------------------
- (void)setOutput:(EZOutput *)output
{
_output = output;
_output.dataSource = self;
_output.delegate = self;
}
//------------------------------------------------------------------------------
- (void)setPan:(float)pan
{
[self.output setPan:pan];
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangePanNotification
object:self];
}
//------------------------------------------------------------------------------
- (void)setVolume:(float)volume
{
[self.output setVolume:volume];
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangeVolumeNotification
object:self];
}
//------------------------------------------------------------------------------
#pragma mark - Actions
//------------------------------------------------------------------------------
- (void)play
{
[self.output startPlayback];
}
//------------------------------------------------------------------------------
- (void)playAudioFile:(EZAudioFile *)audioFile
{
//
// stop playing anything that might currently be playing
//
[self pause];
//
// set new stream
//
self.audioFile = audioFile;
//
// begin playback
//
[self play];
}
//------------------------------------------------------------------------------
- (void)pause
{
[self.output stopPlayback];
}
//------------------------------------------------------------------------------
- (void)seekToFrame:(SInt64)frame
{
[self.audioFile seekToFrame:frame];
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidSeekNotification
object:self];
}
//------------------------------------------------------------------------------
#pragma mark - EZOutputDataSource
//------------------------------------------------------------------------------
- (OSStatus) output:(EZOutput *)output
shouldFillAudioBufferList:(AudioBufferList *)audioBufferList
withNumberOfFrames:(UInt32)frames
timestamp:(const AudioTimeStamp *)timestamp
{
if (self.audioFile)
{
UInt32 bufferSize;
BOOL eof;
[self.audioFile readFrames:frames
audioBufferList:audioBufferList
bufferSize:&bufferSize
eof:&eof];
if (eof && self.shouldLoop)
{
[self seekToFrame:0];
}
else if (eof)
{
[self pause];
[self seekToFrame:0];
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidReachEndOfFileNotification
object:self];
}
}
return noErr;
}
//------------------------------------------------------------------------------
#pragma mark - EZAudioFileDelegate
//------------------------------------------------------------------------------
- (void)audioFile:(EZAudioFile *)audioFile updatedPosition:(SInt64)framePosition
{
if ([self.delegate respondsToSelector:@selector(audioPlayer:updatedPosition:inAudioFile:)])
{
[self.delegate audioPlayer:self
updatedPosition:framePosition
inAudioFile:audioFile];
}
}
//------------------------------------------------------------------------------
#pragma mark - EZOutputDelegate
//------------------------------------------------------------------------------
- (void)output:(EZOutput *)output changedDevice:(EZAudioDevice *)device
{
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangeOutputDeviceNotification
object:self];
}
//------------------------------------------------------------------------------
- (void)output:(EZOutput *)output changedPlayingState:(BOOL)isPlaying
{
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangePlayStateNotification
object:self];
}
//------------------------------------------------------------------------------
- (void) output:(EZOutput *)output
playedAudio:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels
{
if ([self.delegate respondsToSelector:@selector(audioPlayer:playedAudio:withBufferSize:withNumberOfChannels:inAudioFile:)])
{
[self.delegate audioPlayer:self
playedAudio:buffer
withBufferSize:bufferSize
withNumberOfChannels:numberOfChannels
inAudioFile:self.audioFile];
}
}
//------------------------------------------------------------------------------
@end