音频初见
基本介绍
音频: 从形式上分为
短音频(音效播放):不需要进度|循环等控制 AudioToolbox.framework
(C语言框架
- 本质:将音频注册到system sound service(简单底层的声音播放服务))
特点: - 播放时间不超过30s
- 数据格式PCM/IMA4
- 音频文件必须打包成.caf,.aif,.wav,(.MP3也可以)
长音频(音乐播放)
- 较大的音频文件,需要进行精确的控制的场景
- AVFoundation.framework
- 支持的音频格式有:AAC,ALAC,HE-AAC,iLBC,IMA4,MP3等.
音效播放
步骤:
- 不要忘记导入头文件: #import \<AudioToolbox/AudioToolbox.h>
- 1.调用AudioServicesCreateSystemSoundID(CFURLRef inFileURL, SystemSoundID* outSystemSoundID)函数获得系统声音ID。
- 2.如果需要监听播放完成操作,则使用AudioServicesAddSystemSoundCompletion( SystemSoundID inSystemSoundID,CFRunLoopRef inRunLoop, CFStringRef inRunLoopMode, AudioServicesSystemSoundCompletionProc inCompletionRoutine, void* inClientData)方法注册回调函数。
- 3.调用AudioServicesPlaySystemSound(SystemSoundID inSystemSoundID)
AudioServicesPlayAlertSound(SystemSoundID inSystemSoundID) 方法播放音效(后者带有震动效果)
1 | - (void)audioBox{ |
音乐播放 AVAudioPlayer
AVAudioPlayer支持多种音频格式,而且能够对进度,音量,播放速度等控制
头文件 #import \<AVFoundation/AVFoundation.h>
步骤:
- 1.给定资源路径,或者二进制数据初始化播放器
- 2.设置相关属性
- 3.执行播放,更新相关UI(进度更新,歌词显示(CADislinkplay/NSTimer))
初始化方法
AVAudioPlayer可以接受本地资源路径(NSURL)/NSData类型数据(可以用来加载网络链接),但是实质上AVAudioPlayer不支持流式播放,只能播放完整的文件,使用后者加载网络链接,当播放的音频文件较大或者网络不好就需要过长的时间等待
每一个实例化方法返回一个AVAudioPlayer对象,每一个文件/Data对应一个AVAudioPlayer对象,传入一个NSError的指针,用于判断初始化是否成功.
1 | - (nullable instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError; |
播放控制相关API
1 | //准备播放,分配播放所需的资源,并将其加入内部播放队列,加载音频文件到缓冲区,注意:即使在播放之前音频文件没有加载到缓冲区程序也会隐式调用此方法。 |
属性相关
1 | //获取是否正在播放 |
代理方法
1 | <!--音频播放结束后调用的函数 被中断停止播放并不会被调用--> |
后台播放
1.需要添加info.plist
1 | <key>UIBackgroundModes</key> |
或者
2.添加代码支持1
2
3//设置锁屏仍能继续播放
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
监听耳机事件
1 | //添加通知,拔出耳机后暂停播放 |
后台播放信息显示
1.导入框架 #import \<MediaPlayer/MediaPlayer.h>
2.添加代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
//设置歌曲题目
[dict setObject:@"题目" forKey:MPMediaItemPropertyTitle];
//设置歌手名
[dict setObject:@"歌手" forKey:MPMediaItemPropertyArtist];
//设置专辑名
[dict setObject:@"专辑" forKey:MPMediaItemPropertyAlbumTitle];
//设置显示的图片
UIImage *newImage = [UIImage imageNamed:@"43.png"];
[dict setObject:[[MPMediaItemArtwork alloc] initWithImage:newImage]
forKey:MPMediaItemPropertyArtwork];
//设置歌曲时长
[dict setObject:[NSNumber numberWithDouble:300] forKey:MPMediaItemPropertyPlaybackDuration];
//设置已经播放时长
[dict setObject:[NSNumber numberWithDouble:150] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
//更新字典
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];
注:实现后台播放显示歌词的方法实现思路:将歌词信息,绘制到专辑图片上进行显示
后台播放远程控制
1.注册接受远程控制1
2
3
4
5
6
7
8
9
10
11- (void)viewDidAppear:(BOOL)animated {
// 接受远程控制
[self becomeFirstResponder];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
- (void)viewDidDisappear:(BOOL)animated {
// 取消远程控制
[self resignFirstResponder];
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
}
2.实现监听方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
if (event.type == UIEventTypeRemoteControl) {
//判断是否为远程控制
switch (event.subtype) {
//播放
case UIEventSubtypeRemoteControlPlay:
if (![self.audioPlayer isPlaying]) {
[self.audioPlayer play];
}
break;
case UIEventSubtypeRemoteControlPause:
//暂停
if ([self.audioPlayer isPlaying]) {
[self.audioPlayer pause];
}
break;
case UIEventSubtypeRemoteControlNextTrack:
NSLog(@"下一首");
break;
case UIEventSubtypeRemoteControlPreviousTrack:
NSLog(@"上一首 ");
break;
default:
break;
}
}
}
注:
event是事件类型对象,type是事件类型枚举1
2
3
4
5
6typedef NS_ENUM(NSInteger, UIEventType) {
UIEventTypeTouches, //触摸事件
UIEventTypeMotion, //运动事件
UIEventTypeRemoteControl, // 远程控制事件
UIEventTypePresses NS_ENUM_AVAILABLE_IOS(9_0),//物理按钮事件类型
};
subtype中的枚举便是点击这些控制键后传递给我们的消息,我们可以根据这些消息在app内做逻辑处理1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29typedef NS_ENUM(NSInteger, UIEventSubtype) {
// available in iPhone OS 3.0
UIEventSubtypeNone = 0,
// for UIEventTypeMotion, available in iPhone OS 3.0
//摇晃
UIEventSubtypeMotionShake = 1,
//UIEventTypeRemoteControl相关的枚举信息
// for UIEventTypeRemoteControl, available in iOS 4.0
//点击播放按钮或者耳机线控中间那个按钮
UIEventSubtypeRemoteControlPlay = 100,
//点击暂停按钮
UIEventSubtypeRemoteControlPause = 101,
//点击停止按钮
UIEventSubtypeRemoteControlStop = 102,
//点击播放与暂停开关按钮(iphone抽屉中使用这个)
UIEventSubtypeRemoteControlTogglePlayPause = 103,
//点击下一曲按钮或者耳机中间按钮两下
UIEventSubtypeRemoteControlNextTrack = 104,
//点击上一曲按钮或者耳机中间按钮三下
UIEventSubtypeRemoteControlPreviousTrack = 105,
//快退开始 点击耳机中间按钮三下不放开
UIEventSubtypeRemoteControlBeginSeekingBackward = 106,
//快退结束 耳机快退控制松开后
UIEventSubtypeRemoteControlEndSeekingBackward = 107,
//开始快进 耳机中间按钮两下不放开
UIEventSubtypeRemoteControlBeginSeekingForward = 108,
//快进结束 耳机快进操作松开后
UIEventSubtypeRemoteControlEndSeekingForward = 109,
};
拓展:后台播放远程控制的相关自定义
MPRemoteCommandCenter属于\<MediaPlayer/MediaPlayer.h>框架,是一个单例类,处理远程控制中心事件
可以自定义你的控制中心,锁屏时控制,siri语音控制.它管理一系列命令Playback Commands,Feedback Command,Previous/Next Track Commands),Skip Interval Commands,Seek Command等
MPRemoteCommand是这些所有命令的父类
下面简单介绍:1
2
3
4
5
6
7
8
9
10
11//是否开启命令
@property (nonatomic, assign, getter = isEnabled) BOOL enabled;
//Target-action 添加事件处理
- (void)addTarget:(id)target action:(SEL)action;
//当属性enable = NO时,移除
- (void)removeTarget:(id)target action:(nullable SEL)action;
- (void)removeTarget:(nullable id)target;
//返回一个事件对象 MPRemoteCommandEvent是一个命令事件的父类,对应不同的命令进行了继承扩展,主要添加一些属性,比如拖拽音乐进度的positionTime
- (id)addTargetWithHandler:(MPRemoteCommandHandlerStatus(^)(MPRemoteCommandEvent *event))handler;
远程控制三部曲
1:监听远程控制事件1
- (void)beginReceivingRemoteControlEvents
2:捕获远程控制事件1
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
3.取消远程控制事件1
- (void)endReceivingRemoteControlEvents
网易云的锁屏实现
注:添加的RemoteComand必须设置Enable为YES,并添加Target才会显示,并且添加相同类型RemoteComand,有优先级的选择显示,更改原有默认显示的上一曲,播放/暂停,下一曲会失效,需要重新添加
1 | MPRemoteCommandCenter *rcc = [MPRemoteCommandCenter sharedCommandCenter]; |
效果图:
锁屏控制播放进度
主要使用:MPChangePlaybackPositionCommand1
2
3
4
5
6
7
8//改变音乐播放进度
MPChangePlaybackPositionCommand *changePlayBackPosition = [rcc changePlaybackPositionCommand];
[changePlayBackPosition setEnabled:YES];
[changePlayBackPosition addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
MPChangePlaybackPositionCommandEvent *events = (MPChangePlaybackPositionCommandEvent *)event;
self.audioPlayer.currentTime = events.positionTime;
return MPRemoteCommandHandlerStatusSuccess;
}];
流式音频播放(播放网络音频)
使用AudioToolbox框架中的音频队列服务Audio Queue Services。
第三方框架:FreeStreamer
#import “FSAudioStream.h”1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//初始化
FSAudioStream *player = [[FSAudioStream alloc]initWithUrl:[self getFileUrl]];
player.onFailure = ^(FSAudioStreamError error, NSString *errorDescription) {
NSLog(@"播放错误error:%@",errorDescription);
};
player.onCompletion = ^{
NSLog(@"播放完成");
};
//设置音量
[player setVolume:0.5];
//播放
[player play];
//添加引用
self.audioStream = player;
豆瓣:DOUAudioStreamer1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#import "DOUAudioStreamer.h"
@interface AudioModel : NSObject<DOUAudioFile>
@property (nonatomic, strong) NSURL *audioFileURL;
@end
@implementation AudioModel
- (NSURL *)audioFileURL{
NSString *urlStr= @"http://sc1.111ttt.com/2017/1/03/07/296072048390.mp3";
NSURL *url=[NSURL URLWithString:urlStr];
self.audioFileURL = url;
return url;
}
@end
AudioModel *file = [[AudioModel alloc]init];
[file audioFileURL];
self.stream = [[DOUAudioStreamer alloc]initWithAudioFile:file];
[self.stream play];
参考:
https://developer.apple.com/library/content/samplecode/MPRemoteCommandSample/Introduction/Intro.html