zoukankan      html  css  js  c++  java
  • iOS学习笔记23-音效与音乐

    一、音频

    在iOS中,音频播放从形式上可以分为音效播放和音乐播放。

    • 音效:
    • 主要指一些短音频的播放,这类音频一般不需要进行进度、循环等控制。
    • 在iOS中,音效我们是使用AudioToolbox.framework框架实现。
    • 音乐:
    • 主要指一些较长的音频,通常需要对播放进行精确控制。
    • 在iOS中,音乐我们是使用AVFoundation.framework框架实现。

    二、音效

    AudioToolbox.framework框架是一套基于C语言的框架
    它的实现原理是将短音频注册到系统声音服务(SystemSoundService)

    系统声音服务的一些限制:
    1. 音频播放时间不能超过30s
    2. 数据必须是PCM或者IMA4格式,现在的基本都符合
    3. 音频文件必须是CAF、AIF、WAV的一种,实际上有些MP3格式也可以播放。
    使用步骤:
    1. 导入AudioToolbox.framework框架,添加头文件:
    #import <AudioToolbox/AudioToolbox.h>
    
    1. 获取音效文件的路径URL
    2. 添加进系统声音服务,获得系统声音ID,ID是区分不同音效的唯一标示
    void AudioServicesCreateSystemSoundID(
              CFURLRef inFileURL,    /* 音效文件URL,需要把NSURL桥接成CGURLRef */
              SystemSoundID *outSystemSoundID /* 返回音效唯一标示ID */
    );
    
    1. 如果需要监听音效播放完成,需要绑定回调函数
    void AudioServicesAddSystemSoundCompletion(  
            SystemSoundID inSystemSoundID, /* 音效ID */
            CFRunLoopRef inRunLoop, /* 所在循环,一般为NULL */
            CFStringRef inRunLoopMode,/* 循环模式,一般为NULL */
            void (*)(SystemSoundID,void*) inCompletionRoutine,/* 回调C语言函数指针 */
            void *inClientData /* 回调函数的参数 */
    );
    
    1. 开始播放,有两种播放模式:
    /* 开始播放音效 */
    void AudioServicesPlaySystemSound(SystemSoundID inSystemSoundID);
    /* 开始播放音效并带震动 */
    void AudioServicesPlayAlertSound(SystemSoundID inSystemSoundID);
    
    下面是使用实例:
    - (void)viewDidLoad{
        [super viewDidLoad];
        [self playSoundEffect:@"bellTone.wav"];
    }
    #pragma mark -音效
    /* 音效播放完成的回调函数,这个是C语言函数,第一个参数是音效ID,第二个是万能参数 */
    void soundCompleteCallBack(SystemSoundID soundID, void *clientData)
    {
        NSLog(@"播放完成");
    }
    - (void)playSoundEffect:(NSString *)name {
        //获取音效文件路径
        NSString *filePath = [[NSBundle mainBundle] pathForResource:name ofType:nil];
        //创建音效文件URL
        NSURL *fileUrl = [NSURL URLWithString:filePath];
        //音效声音的唯一标示ID
        SystemSoundID soundID = 0;
        //将音效加入到系统音效服务中,NSURL需要桥接成CFURLRef,会返回一个长整形ID,用来做音效的唯一标示
        AudioServicesCreateSystemSoundID((__bridge CFURLRef)(fileUrl), &soundID);
        //设置音效播放完成后的回调C语言函数
        AudioServicesAddSystemSoundCompletion(soundID,NULL,NULL,soundCompleteCallBack,NULL);
        //开始播放音效
        AudioServicesPlaySystemSound(soundID);
    }
    

    三、音乐

    如果播放较大的音频,需要对其进行精确控制,我们需要使用到另外一个框架,即:
    AVFoundation.framework框架,它支持多种音频格式,可以进行精确控制。
    音乐播放功能我们使用到的是AVFoundation.frameworkAVAudioPlayer音乐播放器来实现。

    下面有一些AVAudioPlayer类的常用属性:
    @property (readonly, getter=isPlaying) BOOL playing;//是否正在播放
    @property NSTimeInterval currentTime;//当前已经播放的时间
    @property (readonly) NSTimeInterval duration;//播放的总时间
    @property float volume;//音量大小
    @property float rate;//播放速率,默认为1.0
    @property (readonly) NSURL *url;//音乐文件的URL
    
    下面是常用的对象方法:
    /* 初始化方法 */
    - (instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError;
    - (BOOL)prepareToPlay;/* 把音乐文件加载到缓存区 */
    - (BOOL)play;/* 开始播放音乐 */
    - (BOOL)playAtTime:(NSTimeInterval)time;/* 在指定时间开始播放音乐 */
    - (void)pause;/* 中断音乐,可以通过调用play方法继续播放 */
    - (void)stop;/* 终止音乐,无法继续播放 */
    
    常用的代理方法:
    /* 播放完成时调用 */
    - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag;
    /* 音频解码发生错误时调用 */
    - (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error;
    
    使用步骤:
    1. 导入AVFoundation.framework框架,添加头文件:
    #import <AVFoundation/AVFoundation.h>
    
    1. 获取音乐文件的本地文件路径URL,不能使用网络URL
    2. 创建音乐播放器对象AVAudioPlayer,并设置属性和代理。
    3. 调用perpareToPlay方法加载音乐文件到缓冲区
    4. 调用play方法播放,调用pause中断播放
    5. 实现AVAudioPlayerDelegate方法,监听播放完成。
    我们来做个简易的音乐播放器吧:
    1. 可以循环播放音乐
    2. 可以中断音乐
    3. 可以切换不同的音乐
    4. 可以知道播放进度
    下面是具体的简易音乐播放器项目代码:
    #import "ViewController.h"
    #import <AVFoundation/AVFoundation.h>
    
    @interface ViewController () <AVAudioPlayerDelegate>
    
    @property (nonatomic, strong) NSArray *musicArray;/*< 音乐列表 */
    @property (nonatomic, strong) IBOutlet UIProgressView *progress;/*< 进度条 */
    @property (nonatomic, strong) AVAudioPlayer *audioPlayer;/*< 音乐播放器对象 */
    @property (nonatomic, strong) NSTimer *timer;/* 定时器 */
    @property (nonatomic, strong) IBOutlet UILabel *titleLabel;/* 显示标题 */
    @property (nonatomic, assign) NSInteger selectMusic;/* 当前播放音乐的索引 */
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //音乐文件列表
        self.musicArray = @[@"大梦想家",@"江南",@"酒干倘卖无",
                            @"泡沫",@"夏洛特烦恼",@"演员"];
        //创建定时器,用于不断更新进度条
        self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 
                                                      target:self 
                                                    selector:@selector(updataProgress) 
                                                    userInfo:nil 
                                                     repeats:YES];
        //创建播放器
        [self initAudioPlayerWithNumber:0];
        //播放器开始播放音乐
        [self.audioPlayer play];
    }
    /* 初始化播放器 */
    - (void)initAudioPlayerWithNumber:(NSInteger)num{
        if (num >= self.musicArray.count) {
            return;
        }
        //获取音乐文件路径
        NSString *name = self.musicArray[num];
        NSString *filePath = [[NSBundle mainBundle] pathForResource:name 
                                                             ofType:@"mp3"];
        //创建音乐文件URL,必须是fileURLWithPath,AVAudioPlayer不支持HTTP的URL
        NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
        //创建播放器
        NSError *error = nil;
        AVAudioPlayer *audioPlayer =
                [[AVAudioPlayer alloc] initWithContentsOfURL:fileUrl error:&error];
        if (error) {
            NSLog(@"初始化播放器错误,错误:%@",error.localizedDescription);
            return;
        }
        audioPlayer.numberOfLoops = 0;//设置循环次数,0表示不循环
        audioPlayer.delegate = self;//设置代理
        [audioPlayer prepareToPlay];//加载音乐文件到缓存,还不会播放
        
        self.selectMusic = num;//保存当前的音乐索引
        self.audioPlayer = audioPlayer;//保存当前播放器
        self.titleLabel.text = name;//设置标题
    }
    /* 进度条更新方法,每0.5秒更新一次 */
    - (void)updataProgress{
        //currentTime是当前播放时间,duration是播放总时长
        CGFloat progress = self.audioPlayer.currentTime / self.audioPlayer.duration;
        [self.progress setProgress:progress animated:YES];
    }
    /* 点击播放按钮 */
    - (IBAction)musicPlay:(id)sender {
        //如果播放器没有在播放,才需要调用播放方法
        if (![self.audioPlayer isPlaying]) {
            [self.audioPlayer play];
            //恢复定时器
            self.timer.fireDate = [NSDate distantPast];
        }
    }
    /* 点击暂停按钮 */
    - (IBAction)musicPause:(id)sender {
        //如果播放器在播放,才需要调用暂停方法
        if ([self.audioPlayer isPlaying]) {
            [self.audioPlayer pause];
            //停止定时器
            self.timer.fireDate = [NSDate distantFuture];
        }
    }
    /* 点击下一首歌按钮 */
    - (IBAction)next:(id)sender {
        //音乐索引循环递增
        self.selectMusic = (self.selectMusic + 1)%self.musicArray.count;
        //重新设置播放器
        [self initAudioPlayerWithNumber:self.selectMusic];
        //播放器播放
        [self.audioPlayer play];
    }
    #pragma mark - AVAudioPlayerDelegate代理方法
    -(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
    {
        //自动跳转播放下一首歌
        [self next:nil];
    }
    

    效果图
    (⊙﹏⊙)b,也是够简陋的,你可以做精美一点,比如如果每首歌对应一个图片,到时候切歌的时候,图片显示在背景,一定赏心悦目多了,O(∩_∩)O~,我没有找到好的图片,就这样吧。

    四、后台播放音乐

    事实上上面的播放器有一点问题,那就是一旦进入后台,我们的音乐器就自动暂停了,不可以/(ㄒoㄒ)/~~!那该怎么办呢?

    1. 设置后台运行模式

    设置info.plist文件,添加字段Required background modes
    并设置App plays audio or streams audio/video using AirPlay

    2. 使用音频会话,设置后台运行类型
    //获取音频会话单例对象
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    //设置会话为后台播放
    [audioSession setCategory:AVAudioSessionCategoryPlayback error:NULL];
    //激活修改,启动会话
    [audioSession setActive:YES error:NULL];
    

    音频会话除了后台播放外,还可以有以下的播放模式:
    音频会话播放类型列表

    上面的代码只需修改初始化播放器方法即可实现后台播放:
    /* 初始化播放器 */
    - (void)initAudioPlayerWithNumber:(NSInteger)num{
        if (num >= self.musicArray.count) {
            return;
        }
        //获取音乐文件路径
        NSString *name = self.musicArray[num];
        NSString *filePath = [[NSBundle mainBundle] pathForResource:name 
                                                             ofType:@"mp3"];
        //创建音乐文件URL,必须是fileURLWithPath,AVAudioPlayer不支持HTTP的URL
        NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
        //创建播放器
        NSError *error = nil;
        AVAudioPlayer *audioPlayer =
                [[AVAudioPlayer alloc] initWithContentsOfURL:fileUrl error:&error];
        if (error) {
            NSLog(@"初始化播放器错误,错误:%@",error.localizedDescription);
            return;
        }
        audioPlayer.numberOfLoops = 0;//设置循环次数,0表示不循环
        audioPlayer.delegate = self;//设置代理
        [audioPlayer prepareToPlay];//加载音乐文件到缓存,还不会播放
        
        //获取音频会话单例对象
        AVAudioSession *audioSession = [AVAudioSession sharedInstance];
        //设置会话为后台播放
        [audioSession setCategory:AVAudioSessionCategoryPlayback error:NULL];
        //激活修改,启动会话
        [audioSession setActive:YES error:NULL];
    
        self.selectMusic = num;//保存当前的音乐索引
        self.audioPlayer = audioPlayer;//保存当前播放器
        self.titleLabel.text = name;//设置标题
    }
    

    这样,我们就可以后台播放了,(o)/~。

    有什么问题可以在下方评论区出去,好意见我会采纳的,打造一个大家都满意的学习笔记,O(∩_∩)O哈!
  • 相关阅读:
    cnblog项目--20190309
    django js引入失效问题
    Python老男孩 day16 函数(六) 匿名函数
    Python老男孩 day16 函数(五) 函数的作用域
    Python老男孩 day15 函数(四) 递归
    Python老男孩 day15 函数(三) 前向引用之'函数即变量'
    Python老男孩 day15 函数(二) 局部变量与全局变量
    Python老男孩 day14 函数(一)
    Python老男孩 day14 字符串格式化
    Python老男孩 day14 集合
  • 原文地址:https://www.cnblogs.com/liutingIOS/p/5387274.html
Copyright © 2011-2022 走看看