音频播放(iOS开发)

音频处理 一.录音

录音应用场景

语音聊天

即时通讯软件中,都包含语音发送功能

语音备忘录

录一段音频,来记录某件事情

录音功能实现

导入AVFoundation框架

作用:一些多媒体的处理,基本上都是用这个框架

#import <AVFoundation/AVFoundation.h>

使用AVAudioRecorder进行录音

创建录音文件存放路径

设置录音附加设置项

#import <AVFoundation/AVAudioSettings.h>

录音参数设置,保存到字典中,不需要掌握,固定的设置

设置编码格式:AVFormatIDKey

采样率:AVSampleRateKey

通道数:AVNumberOfChannelsKey

音频质量,采样质量:AVEncoderAudioQualityKey

根据路径以及设置项,创建录音对象

_audioRecorder = [[AVAudioRecorder alloc] initWithURL:url settings:recordSettings error:nil];

准备录音

[self.audioRecorder prepareToRecord];

开始录音

[self.audioRecorder record];

停止录音

[self.audioRecorder stop];

概念补充

编码

概念:编码是信息从一种形式转换为另一种形式的过程

编码格式:

PCM:脉冲编码调制,是一种非压缩音频数字化技术,是一种未压缩的原音重现,数字模式下,音频的厨师信号是PCM

MP3

AAC:AAC其实是"高级音频编码(advanced audio coding)"的缩写,它是被设计用来取代MP3格式的

HE-AAC:HE-AAC是AAC的一个超集,这个"HE"代表的是"high efficiency",HE-AAC是专门为低比特率所优化的一种音频编码格式

AMR:AMR全称是"adaptive multi-rate"的缩写,它也是专门为低比特率所优化的一种音频编码格式

ALAC:它全称是"Apple Lossless",这是一种没有任何质量损失的音频编码方式,也就是我们说的无损压缩

IMA4:这是一个在16bit音频文件下按照4:1的压缩比来进行压缩的格式

文件格式(不同的文件格式,可保存不同的编码格式编码的文件)

wav

特点:音质最好的格式,对应PCM编码

适用:多媒体开发/保存音乐/音效素材

mp3

特点:音质好,压缩比比较高,被大量软件和硬件支持

适用:适合用于比较高要求的音乐欣赏

caf

特点:适用于几乎iOS中所有的编码格式

开发经验

caf文件格式,因为某些编码设置,文件可能会很大,而且caf格式并不是很通用,所以在开发过程中,一般会进行压缩转码为MP3格式

lame静态库

简介

LAME是一个开源的MP3音频压缩软件

LAME是一个递归缩写,来自LAME Ain\'t an MP3 Encoder(LAME不是MP3编码器)

它自1998年以来由一个开源社区开发,目前是公认有损品质MP3中压缩效果最好的编码器

提升权限

sudo -s

下载lame的最新版本并解压

生成静态库

下载build的脚本

https://github.com/kewlbear/lame-ios-build

修改脚本权限(所有人有可执行权限)

chmod a+x 脚本名称

执行脚本

./脚本名称

导入静态库到工程,开始使用

二.音效播放 1.音效概念简介

音效和音乐的区别

其实并没有严格意义上的限定,一般在开发中,将时间比较短,播放频率比较高的,当做音效处理

将播放时间比较长,需要监听播放进度,控制播放速率等操作的音频当做音乐处理

2.导入AVFoundation框架

其实音效处理对应的框架是AudioToolbox,只不过AVFoundation框架包含了此框架

3.使用对应的API,开始播放音效

根据音效文件,生成SystemSoundID

获取URL

CFURLRef urlRef = (__bridge CFURLRef)([[NSBundle mainBundle] URLForResource:@"m_16.wav" withExtension:nil]);

创建保存soundID的变量

SystemSoundID soundID;

通过URL和SoundID的地址,接收对应的音效SoundID

AudioServicesCreateSystemSoundID(urlRef, &soundID);

播放音效

AudioServicesPlaySystemSound(soundID);

效果:直接播放,没有震动效果

AudioServicesPlayAlertSound(soundID);

效果:直接播放,有震动效果

AudioServicesPlaySystemSoundWithCompletion(soundID,block);

效果:带有播放完成回调代码块

根据SoundID释放内存

AudioServicesDisposeSystemSoundID(soundID);

代码优化,播放工具类的封装

优化SoundID的生成,不需要每次都创建一遍

封装播放逻辑,供多处调用

三.音乐播放 1.音乐播放

导入AVFoundation框架

使用AVAudioPlayer类,进行播放音乐

根据音频文件URL,创建AVAudioPlayer对象

获取资源URL

NSURL *url = [[NSBundle mainBundle] URLForResource:@"test.mp3" withExtension:nil];

根据资源URL,创建AVAudioPlayer对象

AVAuioPlayer audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];

准备播放

[self.audioPlayer prepareToPlay];

开始播放

[self.audioPlayer play];

附加设置

暂停

[self.audioPlayer pause];

停止

[self.audioPlayer stop];

停止某个音乐,下次再播放,会从当前位置开始播放

self.audioPlayer.currentTime = 0;

重置当前播放时间

快进

系统已经对currentTime做了容错处理,不用担心事件为负数或者大于音乐总时长

self.audioPlayer.currentTime += 5;

倍速播放

1.0为正常

设置允许调整播放速率,注意,此方法必须设置在准备播放之前(经测试,在播放前也可以)

self.audioPlayer = nil;

self.audioPlayer.enableRate = YES;

self.audioPlayer.rate = 2.0;

音量调节

音量调节范围:0.0~1.0

self.audioPlayer.volume = slider.value;

监听播放事件

设置代理

实现代理方法

后台播放

注意:模拟器测试不准确,以真机为准

勾选后台模式

激活音频播放会话

在iOS中每个应用都有一个音频会话,这个会话就可以通过AVAudioSessiion来表示

AVAudioSession同样存在于AVFoundation框架中,它是单例模式设计,通过sharedInstance进行访问

音频会话类型简介

AVAudioSessionCategoryAmbient

混音播放,可以与其他音频应用同时播放

AVAudioSessionCategorySoloAmbient

独占播放

AVAudioSessionCategoryPlayback

后台播放,也是独占的

AVAudioSessionCategoryRecord

录音模式,用于录音时使用

AVAudioSessionCategoryPlayAndRecord

播放和录音,此时可以录音也可以播放

AVAudioSessionCategoryProcessing

硬件解码音频,此时不能播放和录制

AVAudioSessionCategoryMultiRoute

多种输入输出,例如可以耳机/USB设备同时播放

代码实现

获取音频会话

AVAudioSession *session = [AVAudioSession sharedInstace];

设置会话分类

[session setCategory: AVAudioSessionCategoryPlayback error:nil];

激活会话

[session setActive:YES error:nil];

代码封装重构(注意容错处理)

结合QQ音乐案例进行封装

使用须知

每一个AVPlayer对象对应一个音频播放,如果想播放多个音频,就需要创建多个AVPlayer

使用AVAudioPlayer类,只能播放本地资源,不能播放远程音乐

测试环境

后台播放,需要使用真机进行测试,模拟器测试不准确

2.模仿QQ音乐

搭建项目结构

划分项目功能模块,创建文件夹结构

音乐列表

主要负责展示音乐列表,当点击某一个音乐时,就播放对应音乐,停止其他音乐播放

音乐详情

主要负责展示音乐详情,包含音乐名称/歌手/专辑图片/歌词/进度以及控制逻辑

拖入必要的资源和工具类,以及第三方框架(可以使用时再拖入)

根据界面跳转逻辑,搭建Storyboard,并创建好对应的控制器

导航控制器为初始控制器,其根控制器为UITableViewController(QQ音乐列表控制器)

当点击QQ音乐列表控制器某一行时,跳转到详情控制爱UIViewController

实现音乐列表功能

界面基本设置

背景图片

隐藏导航栏

状态栏设置为白色

加载QQ列表数据

经验:千万不要把获取数据的实现逻辑写在控制器中,不利于维护和重用,也不利于后期扩展

创建数据模型

根据音乐列表plist文件内容,创建对应的音乐数据模型xxxMusicModel

创建数据操作工具类

主要负责数据的获取,和以后数据的操作

此处提供,供外界调用的获取数据的接口

请使用block将数据传递出去,不要直接返回一个数组(因为后期如果改为从网络获取列表,因为网络获取数据是异步的,所以返回的结果可能为nil)

获取所有音乐列表的接口如下:

getMusicModelsWithResultBlock:block

在表格控制器内,调用数据操作类提供的接口,加载数据并显示

音乐列表界面展示

使用自定义cell,以便后期扩充

预留好对接接口

经验:知道到时候在哪里调用真正的外接播放接口/停止接口,为了统一管理

播放接口

停止播放接口

实现音乐播放功能

经验:千万不要把播放的业务实现逻辑直接写在控制器里面,应当抽取一个工具类

高级经验:针对于音乐播放功能,建议分为两层

最底层负责单个音乐的播放/暂停/停止等操作

上层则负责播放的业务逻辑,比如上一首/下一首/随机播放/顺序播放等

这样易于维护,重用和扩展

封装单个音乐文件操作的工具类(xxxAudioTool)

接口1:根据音频名称播放音频

playAudioWithName:

接口2:暂停音频

pauseCurrentAudio

接口3:停止音频

stopCurrentAudio

封装多个音乐文件操作的工具类(xxxMusicOperationTool)

接口1:根据音乐数据模型,播放一首音乐

playMusicWithMusicModel:

接口2:暂停当前音乐

pauseCurrentMusic

接口3:停止当前音乐

stopCurrentMusic

重要建议:

将此工具类设计成为一个单例

因为会有很多界面使用,而且多个界面操作的数据一致

功能测试

在预留接口中,调用工具类的对应接口,然后测试

音乐列表功能开发结束

详情界面实现

音乐详情界面搭建

分析界面结构,选择合适控件搭建界面

注意将同一组子控件使用一个父控件进行包装,方便添加约束布局

稍微不好构思的地方在于歌词界面和专辑界面的切换,需要借助UIScrollView

关联属性和方法到对应的详情控制器,方便后续的动画和赋值操作

扩展音乐播放工具类接口,实现播放业务逻辑,并展示音乐详情

扩展多个音乐操作的工具类(xxxMusicOperationTool)的上一首/下一首等接口

接口1:播放上一首音乐

preMusic

接口2:播放下一首音乐

nextMusic

在控制器对应的关联方法中,调用不同的播放接口,进行测试

将需要展示的数据按"刷新频率"进行分类,分别提供"单次刷新"和"实时刷新"方法

需要根据不同的数据刷新频率,采用不同的刷新策略

如果实时刷新,就可以使用NSTimer,使用定时任务不断刷新,展示最新数据,比如播放进度,就需要不断刷新

而歌曲图片和作者,只需要刷新一次

汇总所有需要刷新的字段,根据字段,创建歌曲播放信息数据模型(此数据模型由多个音乐操作的工具类同一提供)

不要非常零散的单独获取,到处拼凑

之所以由工具类统一提供歌曲播放信息数据模型,主要原因

因为此功能,应该划分到此类的业务逻辑中

只有这个类,最了解当前音乐的播放信息

直接从控制器预留的"单次刷新"和"实时刷新"刷新方法中,从多个音乐操作的工具类中获取最新的音乐播放数据

实时更新歌词,并实现进度展示

创建歌词数据模型(xxxLrcModel)

属性列表

每一句歌词开始时间

@property (nonatomic, assign) double beginTime;

每一句歌词结束时间

@property (nonatomic, assign) double endTime;

每一句歌词的内容

@property (nonatomic, copy) NSString *lrcText;

创建歌词解析工具类(xxxLrcTool),负责解析不同歌曲对应的歌词文件

根据歌词文件名称,解析歌词

getLrcModelsWithLrcName:

根据某个时间点,获取歌词模型数组中对应的歌词模型

getLrcModelInModels:withTime:

使用UITableView展示歌词

单独抽离一个控制器,负责管理歌词

根据当前播放进度,实时滚动切换歌词

根据每句歌词的播放进度,通过颜色展示单句歌词进度

自定义集成自UILabel的子类

重写drawRect:方法

CGRect fillRect = CGRectMake(0,0,rect.size.width*self.progress,rect.size.height);

[[UIColor greenColor] set];

UIRectFillUsingBlendMode(fillRect,kCGBlendModeSourceIn);

后台播放实现

勾选后台模式->音频播放

激活音频播放会话

获取音频会话

设置会话分类

激活会话

显示锁屏界面,并接收远程事件

显示锁屏信息

获取锁屏信息中心

MPNowPlayingInfoCenter *playInfoCenter = [MPNowPlayingInfoCenter defaultCenter];

创建锁屏信息(信息从多音乐操作工具类中获取,并保存到可变字典dic中)

设置锁屏信息

playInfoCenter.nowPlayingInfo = dic;

接收远程控制事件

[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

接收远程事件

可以监听远程事件的前提

必须启动远程事件接收

必须可以成为第一响应者

应用程序必须是该事件的控制者

在控制器中需要实现的方法

remoteControlReceivedWithEvent:

补充:事件类型对应的含义

UIEventSubtypeNone

不包含任何字事件类型

UIEventSubtypeMotionShake

摇晃事件(从iOS3.0开始支持此事件)

远程控制自事件类型(从iOS4.0开始支持远程控制事件)

UIEventSubtypeRemoteControlPlay

播放事件(操作:停滞状态下,按二级线控中间按钮一下)

UIEventSubtypeRemoteControlPause

暂停事件

UIEventSubtypeRemoteControlStop

停止事件

UIEventSubtypeRemoteControlTogglePlayPause

播放或暂停切换(操作:播放或暂停状态下,按耳机线控中间按钮一下)

UIEventSubtypeRemoteControlNextTrack

下一曲(操作:按耳机线控中间按钮两下)

UIEventSubtypeRemoteControlPreviousTrack

上一曲(操作:按耳机线控中间按钮三下)

UIEventSubtypeRemoteControlBeginSeekingBackward

快退开始(操作:按耳机线控中间按钮三下不要松开)

UIEventSubtypeRemoteControlEndSeekingBackward

快退停止(操作:按耳机线控中间按钮三下到了快退的位置松开)

UIEventSubtypeRemoteControlBeginSeekingForward

快进开始(操作:按耳机线控中间按钮两下不要松开)

UIEventSubtypeRemoteControlEndSeekingForward

快进停止(操作:按耳机线控中间按钮两下到了快进的位置松开)

锁屏界面显示歌词

实现方案:利用锁屏显示图片设置项,实时的将歌词绘制到图片上,组成一个新的图片,设置为锁屏的图片

绘制步骤

开启图形上下文

绘制背景图片

获取歌词信息,并绘制

从图形上下文中获取混合图片

关闭图形上下文

注意:效率优化

功能完善,细节处理

通过进度条拖拽,控制播放进度

通过点击进度条某个位置,控制播放进度

自动播放下一首

列表歌词页的歌词进度展示

iOS9.0新推出的Storyboard Reference

3.播放远程音乐

使用AVPlayer来播放远程音乐

方案1:

根据URL,创建AVPlayer

self.player = [[AVPlayer alloc] initWithURL:remoteURL];

播放

[self.player play];

方案2:

根据AVPlayerItem,创建AVPlayer

NSURL *remoteURL = [NSURL URLWithString:remoteURL];

AVPlayerItem *playerItem = [AVPlayerItem playerItemWihtURL:remoteURL];

self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];

播放

[self.player play];

方案对比

如果通过方案1播放某个远程音频,那么后面如果想要更改音乐,则需要重新创建AVPlayer对象

方案2就可以直接通过更改播放项来间接更换播放远程音乐

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwsswz.html