官方文档:https://developer.apple.com/av-foundation/
简要说明
iOS 系统提供了一个可管理的音频环境(managed audio environment),通过音频会话(audio session)来实现。
AVAudioSession 在 Mac 上不可用
AVAudioPlayer 构建于 Core Audio 中的 C-based Audio Queue Services 的最顶层。 Audio Queue Services 中的 播放、循环 甚至音频计量功能,都可以通过 AVAudioPlayer 来以 OC 实现。
音频播放代码实现
#import "AudioPlay.h"
#import <AVFoundation/AVFoundation.h>
@interface AudioPlay () <AVAudioPlayerDelegate>
@property (nonatomic, getter = isPlaying) BOOL playing;
@property (nonatomic, strong) AVAudioPlayer *player;
@end
@implementation AudioPlay
- (void)setupPlayer{
NSString *fileName = @"Drums.m4a";
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:fileName
withExtension:nil];
if (!fileURL) {
NSLog(@"fileURL 为空");
return ;
}
NSLog(@"fileURL : %@", fileURL);
NSError *error;
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL
error:&error];
self.player = player;
if (error) {
NSLog(@"error : %@",error);
}
player.delegate = self;
if (player) {
player.numberOfLoops = 1; // -1 : 无限循环; 1-播放两遍,2-播放三遍
player.enableRate = YES; //
[player prepareToPlay];
NSLog(@"duration : %f", self.player.duration);
} else {
NSLog(@"Error creating player: %@", [error localizedDescription]);
}
[self.player play];
// [self play];
}
#pragma mark - Global playback control methods
- (void)play {
if (!self.playing) {
NSTimeInterval delayTime = [self.player deviceCurrentTime] + 0.01;
if (delayTime > self.player.duration) {
delayTime = 0;
}
[self.player playAtTime:delayTime];
self.playing = YES;
}
}
- (void)stop {
if (self.playing) {
[self.player stop];
self.player.currentTime = 0.0f;
}
self.playing = NO;
}
- (void)adjustRate:(float)rate {
self.player.rate = rate;
}
#pragma mark - Player-specific methods
- (void)adjustPan:(float)pan {
self.player.pan = pan;
}
- (void)adjustVolume:(float)volume {
self.player.volume = volume;
}
#pragma mark - AVAudioPlayerDelegate
// numberOfLoops 的次数都播放完成后调用,如果 numberOfLoops = -1,可能永远不会调用
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
NSLog(@"-- audioPlayerDidFinishPlaying successfully : %d",flag);
}
/* if an error occurs while decoding it will be reported to the delegate. */
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError * __nullable)error{
NSLog(@"-- audioPlayerDecodeErrorDidOccur error : %@",error);
}
#if TARGET_OS_IPHONE
/* AVAudioPlayer INTERRUPTION NOTIFICATIONS ARE DEPRECATED - Use AVAudioSession instead. */
/* audioPlayerBeginInterruption: is called when the audio session has been interrupted while the player was playing. The player will have been paused. */
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player{
NSLog(@"-- audioPlayerBeginInterruption ");
}
/* audioPlayerEndInterruption:withOptions: is called when the audio session interruption has ended and this player had been interrupted while playing. */
/* Currently the only flag is AVAudioSessionInterruptionFlags_ShouldResume. */
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags{
NSLog(@"-- audioPlayerEndInterruption ");
}
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withFlags:(NSUInteger)flags{
NSLog(@"-- audioPlayerEndInterruption ");
}
/* audioPlayerEndInterruption: is called when the preferred method, audioPlayerEndInterruption:withFlags:, is not implemented. */
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player{
NSLog(@"-- audioPlayerEndInterruption ");
}
#endif
多个播放器
AVAudioPlayer *guitarPlayer = [self playerForFile:@"guitar"];
AVAudioPlayer *bassPlayer = [self playerForFile:@"bass"];
AVAudioPlayer *drumsPlayer = [self playerForFile:@"drums"];
guitarPlayer.delegate = self;
_players = @[guitarPlayer, bassPlayer, drumsPlayer];
iOS 中的中断监听
NSNotificationCenter *nsnc = [NSNotificationCenter defaultCenter];
[nsnc addObserver:self
selector:@selector(handleRouteChange:)
name:AVAudioSessionRouteChangeNotification
object:[AVAudioSession sharedInstance]];
#pragma mark - Interruption Handlers
//
// The following two methods have been deprecated.
// Replace with AVAudioSession notification handlers in your production code.
//
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player {
[self stop];
if (self.delegate) {
[self.delegate playbackStopped];
}
}
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player
withOptions:(NSUInteger)options {
if (options == AVAudioSessionInterruptionOptionShouldResume) {
[self play];
if (self.delegate) {
[self.delegate playbackBegan];
}
}
}
#pragma mark - Route Change Handler
- (void)handleRouteChange:(NSNotification *)notification {
NSDictionary *info = notification.userInfo;
AVAudioSessionRouteChangeReason reason =
[info[AVAudioSessionRouteChangeReasonKey] unsignedIntValue];
if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
AVAudioSessionRouteDescription *previousRoute =
info[AVAudioSessionRouteChangePreviousRouteKey];
AVAudioSessionPortDescription *previousOutput = previousRoute.outputs[0];
NSString *portType = previousOutput.portType;
if ([portType isEqualToString:AVAudioSessionPortHeadphones]) {
[self stop];
[self.delegate playbackStopped];
}
}
}
AVAudioSession 设置
只需要初始化一次,后面也可以修改。
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error;
if (![session setCategory:AVAudioSessionCategoryPlayback error:&error]) {
NSLog(@"Category Error: %@", [error localizedDescription]);
}
if (![session setActive:YES error:&error]) {
NSLog(@"Activation Error: %@", [error localizedDescription]);
}
伊织 2020-02-28