美文网首页iOS Developer
重点 (二十四) : 音频

重点 (二十四) : 音频

作者: JonesCxy | 来源:发表于2016-10-11 09:32 被阅读284次

    简介

    简单来说,音频可以分为2种

    音效

    又称“短音频”,通常在程序中的播放时长为1~2秒

    在应用程序中起到点缀效果,提升整体用户体验

    音乐

    比如游戏中的“背景音乐”,一般播放时间较长

    播放音频需要用到2个框架

    AVFoundation.framework

    AudioToolbox.framework

    音效的播放

    1.获得音效文件的路径

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

    2.加载音效文件,得到对应的音效ID

    SystemSoundIDsoundID = 0;

    AudioServicesCreateSystemSoundID((__bridge CFURLRef)(url),
    &soundID);

    3.播放音效

    AudioServicesPlaySystemSound(soundID);

    音效文件只需要加载1次

    音效的播放

    音效播放常见函数总结

    加载音效文件

    AudioServicesCreateSystemSoundID(CFURLRef
    inFileURL, SystemSoundID *outSystemSoundID)

    释放音效资源

    AudioServicesDisposeSystemSoundID(SystemSoundID
    inSystemSoundID)

    播放音效

    AudioServicesPlaySystemSound(SystemSoundID
    inSystemSoundID)

    播放音效带点震动

    AudioServicesPlayAlertSound(SystemSoundID inSystemSoundID)

    声音和音效小结——音频转换工具
    • 转换aiff格式
    • afconvert -f AIFF -d I8 filename
    • 转换caf格式
    • afconvert -f caff -d aac -b 32000 filename
    • 批量转换
    • find . -name '*.mp3' -exec afconvert -f caff -d aac -b 32000 {} ;

    音乐的播放
    • 音乐播放用到一个叫做AVAudioPlayer的类
    • AVAudioPlayer常用方法
    • 加载音乐文件
    • - (id)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError;
    • - (id)initWithData:(NSData *)data error:(NSError **)outError;
    • 准备播放(缓冲,提高播放的流畅性)
    • - (BOOL)prepareToPlay;
    • 播放(异步播放)
    • - (BOOL)play;
    • 暂停
    • - (void)pause;
    • 停止
    • - (void)stop;
    • 是否正在播放
    • @property(readonly, getter=isPlaying) BOOL playing;
    • 时长
    • @property(readonly) NSTimeInterval duration;
    • 当前的播放位置
    • @property NSTimeInterval currentTime;
    • 播放次数(-1代表无限循环播放,其他代表播放numberOfLoops+1次)
    • @property NSInteger numberOfLoops;
    • 音量
    • @property float volume;
    • 是否允许更改速率
    • @property BOOL enableRate;
    • 播放速率(1是正常速率,0.5是一般速率,2是双倍速率)
    • @property float rate;
    • 有多少个声道
    • @property(readonly) NSUInteger numberOfChannels;
    • 声道(-1是左声道,1是右声道,0是中间)
    • @property float pan;
    • 是否允许测量音量
    • @property(getter=isMeteringEnabled) BOOL meteringEnabled;
    • 更新测量值
    • - (void)updateMeters;
    • 获得当前的平均音量
    • - (float)averagePowerForChannel:(NSUInteger)channelNumber;

    *********************笔记**********************


    一. 录音

    1. 应用场景
      大多数应用在即时通讯APP中, 语音发送

    2. 录音步骤
      1.导入AVFoundation框架

    import <AVFoundation/AVFoundation.h>

    2.使用类AVAudioRecorder进行录音

    1. 创建录音文件存放路径
      一般是沙盒路径
      NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"text.caf"];
      NSURL *url = [NSURL URLWithString:path];

    2. 设置录音附加设置项(#import <AVFoundation/AVAudioSettings.h>)
      NSMutableDictionary *recordSettings = [[NSMutableDictionary alloc] init];
      设置编码格式
      [recordSettings setValue :[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey: AVFormatIDKey];
      采样率
      [recordSettings setValue :[NSNumber numberWithFloat:11025.0] forKey: AVSampleRateKey];44100.0
      通道数
      [recordSettings setValue :[NSNumber numberWithInt:2] forKey: AVNumberOfChannelsKey];
      音频质量,采样质量
      [recordSettings setValue:[NSNumber numberWithInt:AVAudioQualityMin] forKey:AVEncoderAudioQualityKey];

    3. 根据路径以及设置项, 创建录音对象
      _audioRecorder = [[AVAudioRecorder alloc] initWithURL:url settings:recordSettings error:nil];

    4. 准备录音
      [self.audioRecorder prepareToRecord];

    5. 开始录音
      [self.audioRecorder record];

    6. 停止录音
      [self.audioRecorder stop];

    7. 封装成工具类, 方便下次使用

    3.概念补充

    1. 编码格式

    AAC: AAC其实是“高级音频编码(advanced audio coding)”的缩写,它是被设计用来取代MP3格式的。
    HE-AAC: HE-AAC是AAC的一个超集,这个“HE”代表的是“High efficiency”。 HE-AAC是专门为低比特率所优化的一种音频编码格式
    AMR: AMR全称是“Adaptive Multi-Rate”,它也是另一个专门为“说话(speech)”所优化的编码格式,也是适合低比特率环境下采用
    ALAC: 它全称是“Apple Lossless”,这是一种没有任何质量损失的音频编码方式,也就是我们说的无损压缩
    IMA4: 这是一个在16-bit音频文件下按照4:1的压缩比来进行压缩的格式。
    MP3
    LPCM : 即线性脉冲编码调制,是一种非压缩音频数字化技术,是一种未压缩的原音重现;
    参考资料地址
    http://baike.baidu.com/link?url=z36Nw7UihAEnCC6BjCygft9rBpLr29Ru0of_9Blpl0aR6qzI1B9iWTD5h3uimPVix2SuuQYo7GVYOIsaaP2Eyq

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

    WAV :
    特点: 音质最好的格式, 对应PCM编码
    适用: 多媒体开发、保存音乐和音效素材。
    MP3 :
    特点: 音质好,压缩比比较高,被大量软件和硬件支持
    适用: 适合用于比较高要求的音乐欣赏。
    caf :
    特点: 适用于几乎iOS中所有的编码格式

    4.开发经验

    1. caf 文件格式, 因为某些编码设置, 文件有可能会很大, 而且caf, 格式并不是很通用, 所以在开发过程中, 一般会进行压缩转码, MP3;
      http://blog.csdn.net/ysy441088327/article/details/7392842

    二. 音效播放

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

    2.导入AVFoundation框架

    import <AVFoundation/AVFoundation.h>

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

    1. 根据音效文件, 来生成SystemSoundID

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

    3. 创建保存soundID 的变量
      SystemSoundID soundID;

    4. 通过url, 和soundID的地址, 接收对应的音效soundID
      AudioServicesCreateSystemSoundID(urlRef, &soundID);

    5. 播放音效

    AudioServicesPlaySystemSound(soundID);
    或者
    AudioServicesPlayAlertSound(soundID); 带振动效果
    或者
    播放完成后, 回调代码块
    AudioServicesPlaySystemSoundWithCompletion(soundID1, ^{
    AudioServicesPlaySystemSound(soundID2);
    });

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

    1. 优化soundID的生成, 不需要每次都创建一遍
    2. 封装播放逻辑, 供多处调用

    三. 音频播放
    1.导入AVFoundation框架

    import <AVFoundation/AVFoundation.h>

    2.使用AVAudioPlayer类, 进行播放音频

    1. 根据音频文件URL, 创建AVAudioPlayer对象
      获取资源URL
      NSURL *url = [[NSBundle mainBundle] URLForResource:@"简单爱.mp3" withExtension:nil];
      根据资源URL, 创建 AVAudioPlayer 对象
      _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];

    2. 准备播放
      准备播放
      [_audioPlayer prepareToPlay];

    3. 开始播放
      [self.audioPlayer play];

    3.附加设置

    暂停
    [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;

    监听播放事件

    • 设置代理
    • 实现代理方法

    4.后台播放

    1. 勾选后台模式
    2. 激活音频播放会话
      ** 注意: 模拟器测试不准确, 以真机为准; **
    3. 代码
    4. 获取音频会话
      AVAudioSession *session = [AVAudioSession sharedInstance];
    5. 设置会话分类
      [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    6. 激活会话
      [session setActive:YES error:nil];

    5.代码封装重构(实战中, 注意容错处理)
    TODO: 结合QQ音乐案例进行封装

    6.使用须知

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

    7.测试环境

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

    四. QQ音乐

    1. 搭建项目结构
    2. 划分项目功能模块, 创建文件夹结构
    3. 项目总共划分为两个大模块

    音乐列表

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

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

    1. 文件夹结构创建如下
    • Classes

    • QQMusicList(QQ音乐列表)

    • Controller

    • Model

    • View

    • QQMusicDetail(QQ音乐详情)

    • Controller

    • Model

    • View

    • Ohter(其他)

    • Lib

    • Tool

    • Category

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

    2. 拖入音乐文件, 专辑图片文件, 歌词文件等必备资源文件

    3. 一些第三方框架可以等使用时再拖入

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

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

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

    7. 实现QQ音乐列表功能

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

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

    10. 创建数据操作工具类

    主要负责数据的获取, 和以后数据的操作;
    此处提供, 供外界调用的获取数据的接口
    请使用block将数据传递出去, 不要直接返回一个数组(因为后期如果改成从网络获取列表, 你就懵逼了, 网络获取数据是异步的)
    代码如下:
    获取所有音乐列表接口

    • (void)getMusicModelsWithResultBlock:(void(^)(NSArray <XMGMusicModel *> *musicModels))resultBlock;
    1. 在表格控制器内, 调用数据操作类提供的接口, 加载数据并展示

    2. 预留好对接接口
      经验: 知道到时候在哪调用真正的外界播放接口, 停止接口. 为了统一管理

    3. 播放接口

    4. 停止播放接口

    5. 实现音乐播放功能
      经验: 千万不要把播放的业务实现逻辑直接写在控制器里面, 大哥, 会死人; 应当抽取一个工具类
      高级经验: 针对于音乐播放功能, 建议分为两层; 最底层负责单个音乐的播放,暂停,停止等操作; 上层则负责播放的业务逻辑, 比如上一首, 下一首, 随机播放, 顺序播放等等; 易于维护,重用, 扩展!

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

    接口如下:
    根据音频名称播放音频

    • (BOOL)playAudioWithName:(NSString *)audioName;

    根据音频名称暂停音频

    • (void)pauseAudioWithName:(NSString *)audioName;

    根据音频名称停止音频

    • (void)stopAudioWithName:(NSString *)audioName;

    当前正在播放的播放器
    @property (nonatomic, strong) AVAudioPlayer *currentPlayer;

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

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

    • (void)playMusicWithMusicM:(XMGMusicModel *)musicM;

    根据音乐数据模型, 暂停一首音乐

    • (void)pauseMusicWithMusicM:(XMGMusicModel *)musicM;

    根据音乐数据模型, 停止一首音乐

    • (void)stopMusicWithMusicM:(XMGMusicModel *)musicM;

    播放当前音乐

    • (void)playCurrentMusic;

    暂停当前音乐

    • (void)pauseCurrentMusic;

    停止当前音乐

    • (void)stopCurrentMusic;

    重要建议:

    • 将此工具类设计成为一个单例; 因为会有很多界面使用; 而且多个界面操作的数据一致
    1. 在预留接口中, 调用工具类的对应接口, 然后测试; QQ音乐列表功能结束;

    2. QQ音乐详情界面实现

    3. QQ音乐详情界面搭建

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

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

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

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

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

    9. 扩展多个音乐操作的工具类 (XMGMusicOptionTool) 上一首, 下一首 等接口

    接口如下:
    播放上一首音乐

    • (void)preMusic;

    播放下一首音乐

    • (void)nextMusic;
    1. 在控制器对应的关联方法中, 调用不同的播放接口, 进行测试

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

    需要根据不同的数据刷新频率, 采用不同的刷新策略
    例如: 如果实时刷新, 就可以使用NSTimer, 使用定时任务不断刷新, 展示最新数据;
    比如播放进度, 就需要不断刷新;

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

    不要非常零散的单独获取, 到处拼凑;
    之所以由 XMGMusicOptionTool 统一提供歌曲播放信息数据模型, 主要原因有两个: 第一是因为此功能, 应划分到此类的业务逻辑中; 第二,只有这个类, 最了解当前音乐的播放信息;

    1. 直接从控制器预留的 "单次刷新" 和 "实时刷新" 刷新方法中, 从 XMGMusicOptionTool 中获取最新的音乐播放数据;

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

    3. 创建歌词数据模型 (XMGLrcModel)

    属性列表
    每一句歌词开始时间
    @property (nonatomic, assign) double beginTime;
    每一句歌词结束时间
    @property (nonatomic, assign) double endTime;
    每一句歌词内容
    @property (nonatomic, copy) NSString *lrcText;

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

    接口如下:
    根据歌词文件名称, 解析歌词

    • (NSArray <XMGLrcModel *> *)getLrcModelsWithLrcName:(NSString *)lrcName;
      根据某个时间点, 获取歌词模型数组中对应的歌词模型
    • (XMGLrcModel *)getLrcModleInModels:(NSArray <XMGLrcModel >)lrcModels WithTime:(double)time;
    1. 使用UITableView展示歌词

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

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

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

    自定义集成自UILabel的子类
    重写 drawRect: 方法
    关键代码如下:

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

    [[UIColor greenColor] set];

    UIRectFillUsingBlendMode(fillRect, kCGBlendModeSourceIn);

    1. QQ音乐后台播放实现

    2. 勾选后台模式 音频播放

    3. 激活音频播放会话

    4. 获取音频会话
      AVAudioSession *session = [AVAudioSession sharedInstance];

    5. 设置会话分类
      [session setCategory:AVAudioSessionCategoryPlayback error:nil];

    6. 激活会话
      [session setActive:YES error:nil];

    7. QQ音乐显示锁屏界面, 并接收远程事件

    8. 显示锁屏信息

    9. 获取锁屏信息中心
      MPNowPlayingInfoCenter *playInfoCenter = [MPNowPlayingInfoCenter defaultCenter];

    10. 创建锁屏信息
      NSMutableDictionary *dic = [NSMutableDictionary dictionary];

    dic[MPMediaItemPropertyAlbumTitle] = [XMGMusicOptionTool shareXMGMusicOptionTool].musicMessageM.musicM.name;

    dic[MPMediaItemPropertyArtist] = [XMGMusicOptionTool shareXMGMusicOptionTool].musicMessageM.musicM.singer;

    MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:[XMGMusicOptionTool shareXMGMusicOptionTool].musicMessageM.musicM.icon]];

    dic[MPMediaItemPropertyArtwork] = artwork;

    dic[MPMediaItemPropertyPlaybackDuration] = @([XMGMusicOptionTool shareXMGMusicOptionTool].musicMessageM.totalTime);

    1. 设置锁屏信息
      playInfoCenter.nowPlayingInfo = dic;

    2. 接收远程控制事件
      [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

    3. 接收远程事件
      可以监听远程事件的前提

    1. 启动远程事件接收
    2. 必须可以成为第一响应者
    3. 应用程序必须是该事件的控制者
      在控制器中实现以下方法

    -(void)remoteControlReceivedWithEvent:(UIEvent *)event
    {

    switch (event.subtype) {
        case UIEventSubtypeRemoteControlPlay:
        {
            [[XMGMusicOptionTool shareXMGMusicOptionTool] playCurrentMusic];
            break;
        }
        case UIEventSubtypeRemoteControlPause:
        {
            [[XMGMusicOptionTool shareXMGMusicOptionTool] pauseCurrentMusic];
            break;
        }
        case UIEventSubtypeRemoteControlNextTrack:
        {
            [[XMGMusicOptionTool shareXMGMusicOptionTool] nextMusic];
            break;
        }
        case UIEventSubtypeRemoteControlPreviousTrack:
        {
            [[XMGMusicOptionTool shareXMGMusicOptionTool] preMusic];
            break;
        }
        default:
            break;
    }
    

    }
    事件类型对应含义

    不包含任何子事件类型
    UIEventSubtypeNone = 0,
    摇晃事件(从iOS3.0开始支持此事件)
    UIEventSubtypeMotionShake = 1,
    远程控制子事件类型(从iOS4.0开始支持远程控制事件)
    播放事件【操作:停止状态下,按耳机线控中间按钮一下】
    UIEventSubtypeRemoteControlPlay = 100,
    暂停事件
    UIEventSubtypeRemoteControlPause = 101,
    停止事件
    UIEventSubtypeRemoteControlStop = 102,
    播放或暂停切换【操作:播放或暂停状态下,按耳机线控中间按钮一下】
    UIEventSubtypeRemoteControlTogglePlayPause = 103,
    下一曲【操作:按耳机线控中间按钮两下】
    UIEventSubtypeRemoteControlNextTrack = 104,
    上一曲【操作:按耳机线控中间按钮三下】
    UIEventSubtypeRemoteControlPreviousTrack = 105,
    快退开始【操作:按耳机线控中间按钮三下不要松开】
    UIEventSubtypeRemoteControlBeginSeekingBackward = 106,
    快退停止【操作:按耳机线控中间按钮三下到了快退的位置松开】
    UIEventSubtypeRemoteControlEndSeekingBackward = 107,
    快进开始【操作:按耳机线控中间按钮两下不要松开】
    UIEventSubtypeRemoteControlBeginSeekingForward = 108,
    快进停止【操作:按耳机线控中间按钮两下到了快进的位置松开】
    UIEventSubtypeRemoteControlEndSeekingForward = 109,

    1. QQ音乐锁屏界面显示歌词
      实现方案: 利用锁屏显示图片设置项; 实时的将歌词绘制到图片上, 组成一个新的图片,设置为锁屏的图片
      绘制步骤
    2. 开启图形上下文
    3. 绘制背景图片
    4. 获取歌词信息, 并绘制
    5. 从图形上下文获取混合图片
    6. 关闭图形上下文

    五. 播放远程音乐
    使用AVPlayer 来播放远程音乐
    方案1:
    1.根据URL, 创建AVPlayer
    self.player = [[AVPlayer alloc] initWithURL:remoteURL];
    2.播放
    [self.player paly];

    方案2:
    1.根据URL, 创建AVPlayer
    NSURL *remoteURL = [NSURL URLWithString:remoteURL];
    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:remoteURL];
    self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
    2.播放
    [self.player paly];

    方案对比:
    如果通过方案1播放某个远程音频, 那么后面如果想要更改音乐, 则需要重新创建AVPlayer对象
    方案2 就可以直接通过更改播放项来间接更换播放远程音乐

    相关文章

      网友评论

        本文标题:重点 (二十四) : 音频

        本文链接:https://www.haomeiwen.com/subject/drovyttx.html