• iOS:音频


    ios中有很多支持音频的控件,如:播放本地音乐(file URL)的AVAudioPlayer和AudioToolbox.Framework.可以播放音乐库音乐的MPMusicPlayerController,可以播放网络音乐(http URL)的FreeStreamer(第三方框架),支持录音的AVAudioRecorder.这里大体上介绍一下各自的好处.

    一.AudioToolbox.framework,支持播放本地(file URL)音乐,是一套基于C语言的框架,使用它播放音频其本质是将短音频注册到系统服务(System sound service).System sound service 是一种简单.底层的声音播放服务,但是本身也存在一些限制.

    a.音频播放时间不能够超过30S.b.支持的数据类型较少,只能是PCM或者IMA4格式.c.音频文件必须打包成caf.aif.wav中的一种(少量的map3格式也可以播放).

    使用System Sound Service播放音频步骤如下:

    1.调用AudioServiceCreateSystemSoundID (CFURLRef inFileURL ,SystemSoundID* ,outSystemSoundID)函数获得系统声音ID.

    2.如果需要监听播放完成操作,则使用AudioServiceAddSystemSoundCompletion(SystemSoundID inSystemSoundID,CFRunLoopRef  inRunLoop, CFStringRef  inRunLoopMode, AudioServicesSystemSoundCompletionProc  inCompletionRoutine, void*  inClientData)方法注册回调函数.

    3.调用AudioServicesPlaySystemSound(SystemSoundID inSystemSoundID)或者AudioServicesPlayAlertSound(SystemSoundID inSystemSoundID) 方法播放音效(后者带有震动效果)。#import "KCMainViewController.h"#import <AudioToolbox/AudioToolbox.h>@interface KCMainViewController ()

    
    @end
    
    @implementation KCMainViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [self playSoundEffect:@"videoRing.caf"];
    }
    
    /**
     *  播放完成回调函数
     *
     *  @param soundID    系统声音ID
     *  @param clientData 回调时传递的数据
     */
    void soundCompleteCallback(SystemSoundID soundID,void * clientData){
        NSLog(@"播放完成...");
    }
    
    /**
     *  播放音效文件
     *
     *  @param name 音频文件名称
     */
    -(void)playSoundEffect:(NSString *)name{
        NSString *audioFile=[[NSBundle mainBundle] pathForResource:name ofType:nil];
        NSURL *fileUrl=[NSURL fileURLWithPath:audioFile];
        //1.获得系统声音ID
        SystemSoundID soundID=0;
        /**
         * inFileUrl:音频文件url
         * outSystemSoundID:声音id(此函数会将音效文件加入到系统音频服务中并返回一个长整形ID)
         */
        AudioServicesCreateSystemSoundID((__bridge CFURLRef)(fileUrl), &soundID);
        //如果需要在播放完之后执行某些操作,可以调用如下方法注册一个播放完成回调函数
        AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, soundCompleteCallback, NULL);
        //2.播放音频
        AudioServicesPlaySystemSound(soundID);//播放音效
    //    AudioServicesPlayAlertSound(soundID);//播放音效并震动
    }
    @end


    二.如果要播放较大的音频文件的话,System Sound SerVice就很难满足实际需求了,这时可选择使用AVFoundation.framework中的AVAudioPlayer来实现,AVAudioPlayer可以看成一个播放器,它支持多种音频格式,而且能够进行进度.音量.播放速度等.

     AVAudioPlayer的使用比较简单:

    1.初始化AVAudioPlayer对象,此时通常指定本地文件路径.

    2.设置播放器属性,例如重复次数.音量大小.

    3.调用paly方法播放.

    当然,由于AVAudioPlayer一次只能播放一个音频文件,所有上一曲.下一曲其实可以通过创建多个播放器对象来实现,播放进度的实现.播放进度的实现主要依靠一个定时器实时计算当前播放时长和音频总时长的比例,下面的例子通过委托方法实现了下一曲播放.

     其.主要代码如下:

    #import "ViewController.h"
    #import <AVFoundation/AVFoundation.h>
    #define kMusicFile @"刘若英 - 原来你也在这里.mp3"
    #define kMusicSinger @"刘若英"
    #define kMusicTitle @"原来你也在这里"
    
    @interface ViewController ()<AVAudioPlayerDelegate>
    
    @property (nonatomic,strong) AVAudioPlayer *audioPlayer;//播放器
    @property (weak, nonatomic) IBOutlet UILabel *controlPanel; //控制面板
    @property (weak, nonatomic) IBOutlet UIProgressView *playProgress;//播放进度
    @property (weak, nonatomic) IBOutlet UILabel *musicSinger; //演唱者
    @property (weak, nonatomic) IBOutlet UIButton *playOrPause; //播放/暂停按钮(如果tag为0认为是暂停状态,1是播放状态)
    
    @property (weak ,nonatomic) NSTimer *timer;//进度更新定时器
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [self setupUI];
        
    }
    
    /**
     *  初始化UI
     */
    -(void)setupUI{
        self.title=kMusicTitle;
        self.musicSinger.text=kMusicSinger;
    }
    
    -(NSTimer *)timer{
        if (!_timer) {
            _timer=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateProgress) userInfo:nil repeats:true];
        }
        return _timer;
    }
    
    /**
     *  创建播放器
     *
     *  @return 音频播放器
     */
    -(AVAudioPlayer *)audioPlayer{
        if (!_audioPlayer) {
            NSString *urlStr=[[NSBundle mainBundle]pathForResource:kMusicFile ofType:nil];
            NSURL *url=[NSURL fileURLWithPath:urlStr];
            NSError *error=nil;
            //初始化播放器,注意这里的Url参数只能时文件路径,不支持HTTP Url
            _audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
            //设置播放器属性
            _audioPlayer.numberOfLoops=0;//设置为0不循环
            _audioPlayer.delegate=self;
            [_audioPlayer prepareToPlay];//加载音频文件到缓存
            if(error){
                NSLog(@"初始化播放器过程发生错误,错误信息:%@",error.localizedDescription);
                return nil;
            }
        }
        return _audioPlayer;
    }
    
    /**
     *  播放音频
     */
    -(void)play{
        if (![self.audioPlayer isPlaying]) {
            [self.audioPlayer play];
            self.timer.fireDate=[NSDate distantPast];//恢复定时器
        }
    }
    
    /**
     *  暂停播放
     */
    -(void)pause{
        if ([self.audioPlayer isPlaying]) {
            [self.audioPlayer pause];
            self.timer.fireDate=[NSDate distantFuture];//暂停定时器,注意不能调用invalidate方法,此方法会取消,之后无法恢复
            
        }
    }
    
    /**
     *  点击播放/暂停按钮
     *
     *  @param sender 播放/暂停按钮
     */
    - (IBAction)playClick:(UIButton *)sender {
        if(sender.tag){
            sender.tag=0;
            [sender setImage:[UIImage imageNamed:@"playing_btn_play_n"] forState:UIControlStateNormal];
            [sender setImage:[UIImage imageNamed:@"playing_btn_play_h"] forState:UIControlStateHighlighted];
            [self pause];
        }else{
            sender.tag=1;
            [sender setImage:[UIImage imageNamed:@"playing_btn_pause_n"] forState:UIControlStateNormal];
            [sender setImage:[UIImage imageNamed:@"playing_btn_pause_h"] forState:UIControlStateHighlighted];
            [self play];
        }
    }
    
    /**
     *  更新播放进度
     */
    -(void)updateProgress{
        float progress= self.audioPlayer.currentTime /self.audioPlayer.duration;
        [self.playProgress setProgress:progress animated:true];
    }
    
    #pragma mark - 播放器代理方法
    -(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
        NSLog(@"音乐播放完成...");
    }
    
    @end
    

     上面的只是一个播放器的雏形,只是一个demo.市面中的播放器还包括很多功能,例如,退出后台仍可以播放,而这个播放器demo则无法实现,而支持后台播放需要下面的几个步骤.

    1.设置后台运行模式:在plist文件中添加Required backgroud modes,并且设置item o = App plays audio streams audio/video using AirPlay(其实可以直接通过Xcode在Project Targets-Capabilities-Backgroud Modes中设置)

    2.设置AVAudioSession的类型为AVAudioSessionCategoryPlayer而且调用setActive:方法启动会话.

        AVAudioSession *audioSession=[AVAudioSession sharedInstance];
        [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
        [audioSession setActive:YES error:nil];
    

    3.为了能够让应用退出后台之后支持耳机控制,建议添加远程控制事件.(这一步非必须)

     前两步是后台播放必须设置的,第三步主要用于接收远程事件.

     在ios中每个应用都有一个音频会话,这个会话就通过AVAudioSession来表示.AVAudioSession同样存在于AVFoundation框架中,它是单例设计模式,通过shareInstance进行访问,在使用Apple时,有些音频应用可以同时播放,有些则不能.在这种多种音频环境中通过音频会话控制播放方式.是否遵循静音键表示在播放过程中如果用户通过硬件设置为静音是否能关闭声音.

    三.播放音乐库中的音乐

    在MediaPlayer.framework中有一个MPMusicPlayerController用于播放音乐库中的音乐.

    MPMusicPlayerController有两种播放器:applicationMusicPlayer和systemMusicPlayer,前者在应用退出后音乐播放会自动停止,后者在应用停止后不会退出播放.

    MPMusicPlayerController加载音乐不同于前面的AVAudioPlayer是通过一个文件路径来加载,而是需要一个播放队列.在MPMusicPlayerController中提供了两个方法来加载播放队列:

    - (void)setQueueWithQuery:(MPMediaQuery *)query和- (void)setQueueWithItemCollection:(MPMediaItemCollection *)itemCollection,正是由于它的播放音频来源是一个队列,因此可以支持上一曲和下一曲功能.

    接下来,获取MPMediaQueue或者MPMediaItemCollection,在MPMediaQueue对象有一系列的类方法来获取媒体队列:

    + (MPMediaQuery *)albumsQuery;
    + (MPMediaQuery *)artistsQuery;
    + (MPMediaQuery *)songsQuery;
    + (MPMediaQuery *)playlistsQuery;
    + (MPMediaQuery *)podcastsQuery;
    + (MPMediaQuery *)audiobooksQuery;
    + (MPMediaQuery *)compilationsQuery;
    + (MPMediaQuery *)composersQuery;
    + (MPMediaQuery *)genresQuery;

    有了这些方法,就可以很容易获得歌曲.播放列表.专辑媒体等媒体队列.

    如果用户想要自己选择要播放的音乐,这时需要使用MPMediaPickerController,它是一个视图控制器,类似于UIImagePickerController,选择玩播放来源后可以在其代理方法中获得MPMediaItem对象.

    下面简单使用下MPMusicPlayerController.

    #import "ViewController.h"
    #import <MediaPlayer/MediaPlayer.h>
    
    @interface ViewController ()<MPMediaPickerControllerDelegate>
    
    @property (nonatomic,strong) MPMediaPickerController *mediaPicker;//媒体选择控制器
    @property (nonatomic,strong) MPMusicPlayerController *musicPlayer; //音乐播放器
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    }
    
    -(void)dealloc{
        [self.musicPlayer endGeneratingPlaybackNotifications];
    }
    
    /**
     *  获得音乐播放器
     *
     *  @return 音乐播放器
     */
    -(MPMusicPlayerController *)musicPlayer{
        if (!_musicPlayer) {
            _musicPlayer=[MPMusicPlayerController systemMusicPlayer];
            [_musicPlayer beginGeneratingPlaybackNotifications];//开启通知,否则监控不到MPMusicPlayerController的通知
            [self addNotification];//添加通知
            //如果不使用MPMediaPickerController可以使用如下方法获得音乐库媒体队列
            //[_musicPlayer setQueueWithItemCollection:[self getLocalMediaItemCollection]];
        }
        return _musicPlayer;
    }
    
    /**
     *  创建媒体选择器
     *
     *  @return 媒体选择器
     */
    -(MPMediaPickerController *)mediaPicker{
        if (!_mediaPicker) {
            //初始化媒体选择器,这里设置媒体类型为音乐,其实这里也可以选择视频、广播等
    //        _mediaPicker=[[MPMediaPickerController alloc]initWithMediaTypes:MPMediaTypeMusic];
            _mediaPicker=[[MPMediaPickerController alloc]initWithMediaTypes:MPMediaTypeAny];
            _mediaPicker.allowsPickingMultipleItems=YES;//允许多选
    //        _mediaPicker.showsCloudItems=YES;//显示icloud选项
            _mediaPicker.prompt=@"请选择要播放的音乐";
            _mediaPicker.delegate=self;//设置选择器代理
        }
        return _mediaPicker;
    }
    
    /**
     *  取得媒体队列
     *
     *  @return 媒体队列
     */
    -(MPMediaQuery *)getLocalMediaQuery{
        MPMediaQuery *mediaQueue=[MPMediaQuery songsQuery];
        for (MPMediaItem *item in mediaQueue.items) {
            NSLog(@"标题:%@,%@",item.title,item.albumTitle);
        }
        return mediaQueue;
    }
    
    /**
     *  取得媒体集合
     *
     *  @return 媒体集合
     */
    -(MPMediaItemCollection *)getLocalMediaItemCollection{
        MPMediaQuery *mediaQueue=[MPMediaQuery songsQuery];
        NSMutableArray *array=[NSMutableArray array];
        for (MPMediaItem *item in mediaQueue.items) {
            [array addObject:item];
            NSLog(@"标题:%@,%@",item.title,item.albumTitle);
        }
        MPMediaItemCollection *mediaItemCollection=[[MPMediaItemCollection alloc]initWithItems:[array copy]];
        return mediaItemCollection;
    }
    
    #pragma mark - MPMediaPickerController代理方法
    //选择完成
    -(void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection{
        MPMediaItem *mediaItem=[mediaItemCollection.items firstObject];//第一个播放音乐
        //注意很多音乐信息如标题、专辑、表演者、封面、时长等信息都可以通过MPMediaItem的valueForKey:方法得到,但是从iOS7开始都有对应的属性可以直接访问
    //    NSString *title= [mediaItem valueForKey:MPMediaItemPropertyAlbumTitle];
    //    NSString *artist= [mediaItem valueForKey:MPMediaItemPropertyAlbumArtist];
    //    MPMediaItemArtwork *artwork= [mediaItem valueForKey:MPMediaItemPropertyArtwork];
        //UIImage *image=[artwork imageWithSize:CGSizeMake(100, 100)];//专辑图片
        NSLog(@"标题:%@,表演者:%@,专辑:%@",mediaItem.title ,mediaItem.artist,mediaItem.albumTitle);
        [self.musicPlayer setQueueWithItemCollection:mediaItemCollection];
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    //取消选择
    -(void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker{
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    #pragma mark - 通知
    /**
     *  添加通知
     */
    -(void)addNotification{
        NSNotificationCenter *notificationCenter=[NSNotificationCenter defaultCenter];
        [notificationCenter addObserver:self selector:@selector(playbackStateChange:) name:MPMusicPlayerControllerPlaybackStateDidChangeNotification object:self.musicPlayer];
    }
    
    /**
     *  播放状态改变通知
     *
     *  @param notification 通知对象
     */
    -(void)playbackStateChange:(NSNotification *)notification{
        switch (self.musicPlayer.playbackState) {
            case MPMusicPlaybackStatePlaying:
                NSLog(@"正在播放...");
                break;
            case MPMusicPlaybackStatePaused:
                NSLog(@"播放暂停.");
                break;
            case MPMusicPlaybackStateStopped:
                NSLog(@"播放停止.");
                break;
            default:
                break;
        }
    }
    
    #pragma mark - UI事件
    - (IBAction)selectClick:(UIButton *)sender {
        [self presentViewController:self.mediaPicker animated:YES completion:nil];
    }
    
    - (IBAction)playClick:(UIButton *)sender {
        [self.musicPlayer play];
    }
    
    - (IBAction)puaseClick:(UIButton *)sender {
        [self.musicPlayer pause];
    }
    
    - (IBAction)stopClick:(UIButton *)sender {
        [self.musicPlayer stop];
    }
    
    - (IBAction)nextClick:(UIButton *)sender {
        [self.musicPlayer skipToNextItem];
    }
    
    - (IBAction)prevClick:(UIButton *)sender {
        [self.musicPlayer skipToPreviousItem];
    }
    
    @end

    四.录音

    在音频中,还有一个常见的操作:录音.

    AVFoundation框架中的AVAudioRecorder类专门处理录音操作,它同样支持多种音频文件格式.与AVAudioPlayer类似,你完全可以将它看成一个录音机控制类.

    AVAudioRecorder有很多属性和方法跟AVAudioPlayer都是类似的,但是它的创建有所不同,创建录音机除了指定路径外还必须指定录音设置信息,因为录音机必须知道录音文件的格式.采样率.通道率.每个采用点的位数等信息,但是也并不是所有的信息都必须设置,通常只需要设置几个即可.有关录音的设置,详见:AV Foundation Audio Settings Constants

    下面举个例子说明AVAudioRecorder的使用.

    1.设置音频会话类型为AVAudioSessionCategoryPlayAndRecord,因为牵扯到录音和播放操作.

    2.创建录音机AVAudioRecorder,指定录音保存的路径并且设置录音属性,注意对于一般的录音文件要求的采样率,位数并不高,需要适当设置以保证录音文件的大小和效果.

    3.设置录音机代理以便在录音完成后播放录音,打开录音测量保证能够实时获得录音时的声音强度.(注意声音强度范围在-160-0,0表示最大输入)

    4.创建音频播放器AVAudioPlayer,用于在录音完成后播放录音.

    5.创建一个定时器以便实时刷新录音测量值并更新录音强度到UIProgressView中显示.

    6.添加录音.暂停.恢复.停止等操作.需要注意的是恢复操作其实是有音频会话管理的,恢复时只要再次调用record方法即可,无需手动管理恢复时间等.

    其主要代码如下:

    #import <AVFoundation/AVFoundation.h>
    #define kRecordAudioFile @"myRecord.caf"
    
    @interface ViewController ()<AVAudioRecorderDelegate>
    
    @property (nonatomic,strong) AVAudioRecorder *audioRecorder;//音频录音机
    @property (nonatomic,strong) AVAudioPlayer *audioPlayer;//音频播放器,用于播放录音文件
    @property (nonatomic,strong) NSTimer *timer;//录音声波监控(注意这里暂时不对播放进行监控)
    
    @property (weak, nonatomic) IBOutlet UIButton *record;//开始录音
    @property (weak, nonatomic) IBOutlet UIButton *pause;//暂停录音
    @property (weak, nonatomic) IBOutlet UIButton *resume;//恢复录音
    @property (weak, nonatomic) IBOutlet UIButton *stop;//停止录音
    @property (weak, nonatomic) IBOutlet UIProgressView *audioPower;//音频波动
    
    @end
    
    @implementation ViewController
    
    #pragma mark - 控制器视图方法
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [self setAudioSession];
    }
    
    #pragma mark - 私有方法
    /**
     *  设置音频会话
     */
    -(void)setAudioSession{
        AVAudioSession *audioSession=[AVAudioSession sharedInstance];
        //设置为播放和录音状态,以便可以在录制完之后播放录音
        [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
        [audioSession setActive:YES error:nil];
    }
    
    /**
     *  取得录音文件保存路径
     *
     *  @return 录音文件路径
     */
    -(NSURL *)getSavePath{
        NSString *urlStr=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
        urlStr=[urlStr stringByAppendingPathComponent:kRecordAudioFile];
        NSLog(@"file path:%@",urlStr);
        NSURL *url=[NSURL fileURLWithPath:urlStr];
        return url;
    }
    
    /**
     *  取得录音文件设置
     *
     *  @return 录音设置
     */
    -(NSDictionary *)getAudioSetting{
        NSMutableDictionary *dicM=[NSMutableDictionary dictionary];
        //设置录音格式
        [dicM setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey];
        //设置录音采样率,8000是电话采样率,对于一般录音已经够了
        [dicM setObject:@(8000) forKey:AVSampleRateKey];
        //设置通道,这里采用单声道
        [dicM setObject:@(1) forKey:AVNumberOfChannelsKey];
        //每个采样点位数,分为8、16、24、32
        [dicM setObject:@(8) forKey:AVLinearPCMBitDepthKey];
        //是否使用浮点数采样
        [dicM setObject:@(YES) forKey:AVLinearPCMIsFloatKey];
        //....其他设置等
        return dicM;
    }
    
    /**
     *  获得录音机对象
     *
     *  @return 录音机对象
     */
    -(AVAudioRecorder *)audioRecorder{
        if (!_audioRecorder) {
            //创建录音文件保存路径
            NSURL *url=[self getSavePath];
            //创建录音格式设置
            NSDictionary *setting=[self getAudioSetting];
            //创建录音机
            NSError *error=nil;
            _audioRecorder=[[AVAudioRecorder alloc]initWithURL:url settings:setting error:&error];
            _audioRecorder.delegate=self;
            _audioRecorder.meteringEnabled=YES;//如果要监控声波则必须设置为YES
            if (error) {
                NSLog(@"创建录音机对象时发生错误,错误信息:%@",error.localizedDescription);
                return nil;
            }
        }
        return _audioRecorder;
    }
    
    /**
     *  创建播放器
     *
     *  @return 播放器
     */
    -(AVAudioPlayer *)audioPlayer{
        if (!_audioPlayer) {
            NSURL *url=[self getSavePath];
            NSError *error=nil;
            _audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
            _audioPlayer.numberOfLoops=0;
            [_audioPlayer prepareToPlay];
            if (error) {
                NSLog(@"创建播放器过程中发生错误,错误信息:%@",error.localizedDescription);
                return nil;
            }
        }
        return _audioPlayer;
    }
    
    /**
     *  录音声波监控定制器
     *
     *  @return 定时器
     */
    -(NSTimer *)timer{
        if (!_timer) {
            _timer=[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(audioPowerChange) userInfo:nil repeats:YES];
        }
        return _timer;
    }
    
    /**
     *  录音声波状态设置
     */
    -(void)audioPowerChange{
        [self.audioRecorder updateMeters];//更新测量值
        float power= [self.audioRecorder averagePowerForChannel:0];//取得第一个通道的音频,注意音频强度范围时-160到0
        CGFloat progress=(1.0/160.0)*(power+160.0);
        [self.audioPower setProgress:progress];
    }
    #pragma mark - UI事件
    /**
     *  点击录音按钮
     *
     *  @param sender 录音按钮
     */
    - (IBAction)recordClick:(UIButton *)sender {
        if (![self.audioRecorder isRecording]) {
            [self.audioRecorder record];//首次使用应用时如果调用record方法会询问用户是否允许使用麦克风
            self.timer.fireDate=[NSDate distantPast];
        }
    }
    
    /**
     *  点击暂定按钮
     *
     *  @param sender 暂停按钮
     */
    - (IBAction)pauseClick:(UIButton *)sender {
        if ([self.audioRecorder isRecording]) {
            [self.audioRecorder pause];
            self.timer.fireDate=[NSDate distantFuture];
        }
    }
    
    /**
     *  点击恢复按钮
     *  恢复录音只需要再次调用record,AVAudioSession会帮助你记录上次录音位置并追加录音
     *
     *  @param sender 恢复按钮
     */
    - (IBAction)resumeClick:(UIButton *)sender {
        [self recordClick:sender];
    }
    
    /**
     *  点击停止按钮
     *
     *  @param sender 停止按钮
     */
    - (IBAction)stopClick:(UIButton *)sender {
        [self.audioRecorder stop];
        self.timer.fireDate=[NSDate distantFuture];
        self.audioPower.progress=0.0;
    }
    
    #pragma mark - 录音机代理方法
    /**
     *  录音完成,录音完成后播放录音
     *
     *  @param recorder 录音机对象
     *  @param flag     是否成功
     */
    -(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{
        if (![self.audioPlayer isPlaying]) {
            [self.audioPlayer play];
        }
        NSLog(@"录音完成!");
    }
    
    @end
    

    五.以上的音频播放都不支持网络流媒体播放.这样的应用市场上很少,因此使用音频队列服务Audio Queue SerVice.

    目前有很多第三方框架可以直接使用,如AudioStreamer.FreeStreamer.由于前者是非ARC环境,下面就以FreeStreamer来简单演示在线播放音频的过程,准备工作如下:

    1.拷贝FreeStreamer中的Reachability.h、Reachability.m和Common、astreamer两个文件夹中的内容到项目中。

    2.添加FreeStreamer使用的类库:CFNetwork.framework、AudioToolbox.framework、AVFoundation.framework

    、libxml2.dylib、MediaPlayer.framework。

    3.如果引用libxml2.dylib编译不通过,需要在Xcode的Targets-Build Settings-Header Build Path中添加$(SDKROOT)/usr/include/libxml2。

    4.将FreeStreamer中的FreeStreamerMobile-Prefix.pch文件添加到项目中并将Targets-Build Settings-Precompile Prefix Header设置为YES,在Targets-Build Settings-Prefix Header设置为$(SRCROOT)/项目名称/FreeStreamerMobile-Prefix.pch(因为Xcode6默认没有pch文件)

    然后就可以编写代码播放网络音频了:

    #import "ViewController.h"
    #import "FSAudioStream.h"
    
    @interface ViewController ()
    
    @property (nonatomic,strong) FSAudioStream *audioStream;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [self.audioStream play];
    }
    
    /**
     *  取得本地文件路径
     *
     *  @return 文件路径
     */
    -(NSURL *)getFileUrl{
        NSString *urlStr=[[NSBundle mainBundle]pathForResource:@"刘若英 - 原来你也在这里.mp3" ofType:nil];
        NSURL *url=[NSURL fileURLWithPath:urlStr];
        return url;
    }
    -(NSURL *)getNetworkUrl{
        NSString *urlStr=@"http://192.168.1.102/liu.mp3";
        NSURL *url=[NSURL URLWithString:urlStr];
        return url;
    }
    
    /**
     *  创建FSAudioStream对象
     *
     *  @return FSAudioStream对象
     */
    -(FSAudioStream *)audioStream{
        if (!_audioStream) {
            NSURL *url=[self getNetworkUrl];
            //创建FSAudioStream对象
            _audioStream=[[FSAudioStream alloc]initWithUrl:url];
            _audioStream.onFailure=^(FSAudioStreamError error,NSString *description){
                NSLog(@"播放过程中发生错误,错误信息:%@",description);
            };
            _audioStream.onCompletion=^(){
                NSLog(@"播放完成!");
            };
            [_audioStream setVolume:0.5];//设置声音
        }
        return _audioStream;
    }
    
    @end
    

      

     

     

  • 相关阅读:
    redis应用场景之文章投票设计思路
    Redis存储的5种数据结构
    v+=e 不等价于 v=v+e
    WebMagic
    指针函数和函数指针的区别
    为什么说StringBuilder不安全?
    sql注入
    Autowired报错处理
    SpringBoot入门最简单的一个项目示例
    MVC中Cookie的用法(二)---CookieHelper
  • 原文地址:https://www.cnblogs.com/shaoting/p/4769946.html
Copyright © 2020-2023  润新知