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哈!
  • 相关阅读:
    luogu P3375 【模板】KMP字符串匹配
    leetcode[129]Sum Root to Leaf Numbers
    leetcode[130]Surrounded Regions
    leetcode[131]Palindrome Partitioning
    leetcode[132]Palindrome Partitioning II
    leetcode[133]Clone Graph
    leetcode[134]Gas Station
    leetcode[135]Candy
    leetcode[136]Single Number
    leetcode[137]Single Number II
  • 原文地址:https://www.cnblogs.com/liutingIOS/p/5387274.html
Copyright © 2011-2022 走看看