iOS 音频录制、播放(本地、网络)

文章目录

   一、录音机(AVAudioRecorder)

   1、简介

   2、如何使用

   3、具体实现(开始、暂停、停止、播放 四个功能)

   4、附件实现demo

   二、播放音频

   1、播放本地音频文件(AVAudioPlayer)

   2、播放网络音频文件(Audio Queue Servies - 音频队列服务)

     <1> 音频队列

     <2> 音频录制原理

     <3> 音频播放原理

     <4> 现有的第三方实现框架

一、录音机(AVAudioRecorder)

1、简介

在AVFoundation.frame框架下有一个AVAudioRecorder类专门用处理录音操作。
AVAudioRecorder很多属性和方法跟AVAudioPlayer都是类似的,但是它的创建有所不同,
在创建录音机时除了指定路径外还必须指定录音设置信息,
因为录音机必须知道录音文件的格式、采样率、通道数、每个采样点的位数等信息,
但是也并不是所有的信息都必须设置,通常只需要几个常用设置。

2、如何使用

(1)初始化AVAudioRecorder对象,并指定录音文件路径、录音配置信息
(2)设置录制属性。例如,要监听音波,必须设置 meteringEnabled = YES
 (3) 调用record方法开始录音

3、具体实现(开始、暂停、停止、播放 四个功能)

//
//  MainViewController.m
//  3.录制音频
//
//  Created by cherish on 2018/4/12.
//  Copyright © 2018年 Cherish. All rights reserved.
//

#import "MainViewController.h"
#import 
#import "Configure.h"
@interface MainViewController ()<AVAudioRecorderDelegate,AVAudioPlayerDelegate>

//音频录制
@property (nonatomic,strong) AVAudioRecorder *avaudioRecorder;

//本地音频播放
@property (nonatomic,strong) AVAudioPlayer *avaudioPlayer;

//背景
@property (nonatomic,strong) UIImageView *viewBg;

//音量
@property (nonatomic,strong) UIProgressView *progressView;

//开始录制
@property (nonatomic,strong) UIButton *start;

//暂停录制
@property (nonatomic,strong) UIButton *pause;

//停止录制
@property (nonatomic,strong) UIButton *stop;

//播放录音
@property (nonatomic,strong) UIButton *play;

//播放网络音频
@property (nonatomic,strong) UIButton *playNetAudio;

//定时器
@property (nonatomic,weak) NSTimer *timer;


@end

@implementation MainViewController


#pragma mark - ViewController Life
- (void)viewDidLoad
{
    [super viewDidLoad];

    self.title = @"音频录制";

    [self setUI];


    [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {

        if (granted) {

            NSLog(@"麦克风打开了");

        } else {

            NSLog(@"麦克风关闭了");

        }

    }];


}//视频加载


#pragma mark - OverRide Method
- (AVAudioRecorder*)avaudioRecorder
{
    if (!_avaudioRecorder) {

        NSError *error = nil;
        _avaudioRecorder = [[AVAudioRecorder alloc]initWithURL:[self getSavePath] settings:[self recordConfigure] error:&error];

        _avaudioRecorder.delegate = self;

        //如果要监控声波则必须设置为YES
        _avaudioRecorder.meteringEnabled = YES;

        // 把录音文件加载到缓冲区
        [_avaudioRecorder prepareToRecord];

        if (error) {

            NSAssert(YES, @"录音机初始化失败,请检查参数");

        }



    }

    return _avaudioRecorder;

}//录音机


- (AVAudioPlayer*)avaudioPlayer
{
    if (!_avaudioPlayer) {

        NSError *error = nil;

        _avaudioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:[self getSavePath] error:&error];

        //设置代理
        _avaudioPlayer.delegate = self;

        //将播放文件加载到缓冲区
        [_avaudioPlayer prepareToPlay];

    }

    return _avaudioPlayer;
}


- (NSTimer*)timer
{
    if (!_timer) {

        _timer = [TimerWeakTarget scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(upDataProgress) userInfo:nil repeats:YES];

    }

    return _timer;

}//定时器


#pragma mark - Private Methods
- (void)setUI
{
    //背景
    self.viewBg = [[UIImageView alloc]initWithFrame:self.view.bounds];
    self.viewBg.image = [UIImage imageNamed:@"viewbg"];
    self.viewBg.contentMode = UIViewContentModeScaleAspectFill ;
    self.viewBg.layer.masksToBounds = YES;
    self.viewBg.userInteractionEnabled = YES;
    [self.view addSubview:self.viewBg];


    // 功能按钮
    NSArray *titles = @[@"开始录音",@"暂停录音",@"播放录音",@"停止录音"];

    CGFloat gap = (KSCreenWidth-titles.count*80)/5.0;

    for (int i = 0; i < 4; i++)
    {
        UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
        [btn setTitle:titles[i] forState:UIControlStateNormal];
        [btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        btn.backgroundColor = [UIColor redColor];
        btn.titleLabel.textAlignment = NSTextAlignmentCenter;
        btn.titleLabel.font = [UIFont systemFontOfSize:15.0f];
        btn.tag = 555+i;
        [btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
        [self.viewBg addSubview:btn];

        btn.sd_layout
        .leftSpaceToView(self.viewBg, gap+i*(gap+80))
        .widthIs(80)
        .heightIs(50)
        .bottomSpaceToView(self.viewBg, 50);


    }


    self.progressView = [[UIProgressView alloc]init];
    self.progressView.progressTintColor = [UIColor redColor];
    self.progressView.trackTintColor = [UIColor whiteColor];
    self.progressView.progress = 0.5f;
    [self.viewBg addSubview:self.progressView];


    self.progressView.sd_layout
    .leftSpaceToView(self.viewBg, 15)
    .rightSpaceToView(self.viewBg, 15)
    .topSpaceToView(self.viewBg, 150)
    .heightIs(10);


}//绘制UI


- (NSURL*)getSavePath
{

    //获取沙盒根目录
    NSString *homePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

    NSString *filePath =  [homePath stringByAppendingPathComponent:recordPath];

    return [NSURL URLWithString:filePath];

}//设置存储路径


- (NSMutableDictionary*)recordConfigure
{
    NSMutableDictionary *configure = [NSMutableDictionary dictionary];

    //设置录音格式
    [configure setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey];
    //设置录音采样率(Hz) 如:AVSampleRateKey==8000/44100/96000(影响音频的质量)
    [configure setObject:@800 forKey:AVSampleRateKey];
    //设置通道
    [configure setObject:@1 forKey:AVNumberOfChannelsKey];
    //设置采样点位数 ,分别 8、16、24、32
    [configure setObject:@8 forKey:AVLinearPCMBitDepthKey];
    //是否使用浮点数采样
    [configure setObject:@(YES) forKey:AVLinearPCMIsFloatKey];
    //设置录音质量:中等质量
    [configure setObject:@(AVAudioQualityMedium) forKey:AVEncoderAudioQualityKey];

    // ... 其他设置
    return configure;

}//录音配置


#pragma mark - Action Methods
- (void)btnAction:(UIButton*)sender
{

    switch (sender.tag) {

        case 555:   //录音

            [self startRecord];
            break;

       case 556:   // 暂停

            [self pauaseRecord];
            break;

       case 557:  // 播放

            [self playRecord];
            break;

       case 558: //停止

            [self stopRecord];
            break;

        default:
            break;
    }

}//按钮事件


- (void)startRecord
{
    if (![self.avaudioRecorder isRecording]) {

        [self.avaudioRecorder record];

        self.timer.fireDate = [NSDate distantPast];


    }



}//开始录音


- (void)pauaseRecord
{

    if ([self.avaudioRecorder isRecording]) {

        [self.avaudioRecorder pause];

        self.timer.fireDate = [NSDate distantFuture];

    }

}//暂停录音


- (void)playRecord
{

    if (!self.avaudioPlayer.isPlaying) {

        [self.avaudioPlayer play];

    }


}//播放录音


- (void)stopRecord
{
    //调用这个方法后,直接执行录制完成的代理方法
    [self.avaudioRecorder stop];

    self.timer.fireDate = [NSDate distantFuture];

    [self.progressView setProgress:0 animated:YES];


}//停止录音


- (void)upDataProgress
{

    //更新测量值
    [self.avaudioRecorder updateMeters];
    //取得第一个通道的音频,注意音频强度范围时-160到0
    float power= [self.avaudioRecorder averagePowerForChannel:0];
    CGFloat progress = (1.0/160.0)*(power+160.0);
    [self.progressView setProgress:progress animated:YES];

}//显示音波强度


#pragma mark -  AVAudioRecorder  Delegate
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{

    NSLog(@"录音完成");


}//录音完成


#pragma mark - AVAudioPlayer  Delegate
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
    NSLog(@"播放完成");

}//播放器完成

@end

4、附件实现demo

点击这里下载附件

二、播放音频

1、播放本地音频文件(AVAudioPlayer)

在 AVAudioPlayer使用 中我们介绍了AVFoundation.framework框架下的AVAudioPlayer可播放本地音频。那么如果想要播放网络音频文件,AVAudioPlayer 只能将音频文件下载到本地然后再进行播放操作了。但是这种方式最大的弊端就是必须等到整个音频播放完成才能播放,而不能使用流式播放,这往往在实际开发中是不切实际的。

那么在iOS中如何播放网络流媒体呢?

就是使用AudioToolbox框架中的音频队列服务Audio Queue Services。音频队列服务是纯C接口。

.

2、播放网络音频文件(Audio Queue Servies - 音频队列服务)

在Apple 官方文档 中,明确指出

Audio Queue Services provides a straightforward, low overhead way to record and play audio in iOS and Mac OS X. It is the recommended technology to use for adding basic recording or playback features to your iOS or Mac OS X application.

在文档中Apple推荐开发者使用AudioQueue来实现app中的播放和录音功能。并对于支持的格式同样也做了说明,如下

Audio Queue Services lets you record and play audio in any of the following formats:

1、Linear PCM.

2、Any compressed format supported natively on the Apple platform you are developing for.

3、Any other format for which a user has an installed codec.

它支持PCM数据、iOS/MacOSX平台支持的压缩格式(MP3、AAC等)、其他用户可以自行提供解码器的音频数据(对于这一条,我的理解就是把音频格式自行解码成PCM数据后再给AudioQueue播放 )。

.

.

< 1 > 音频队列

iOS 音频录制、播放(本地、网络)_第1张图片


一个音频服务队列Audio Queue有三部分组成:

三个缓冲器Buffers:每个缓冲器都是一个存储音频数据的临时仓库。

一个缓冲队列Buffer Queue:一个包含音频缓冲器的有序队列。

一个回调Callback:一个自定义的队列回调函数。

< 2 > 音频录制原理

iOS 音频录制、播放(本地、网络)_第2张图片

  如上图所示,音频录制是声音通过输入设备进入缓冲队列中,
  首先填充第一个缓冲器;当第一个缓冲器填充满之后自动填充
  下一个缓冲器,同时会调用回调函数;
  在回调函数中需要将缓冲器中的音频数据写入磁盘,
  同时将缓冲器放回到缓冲队列中以便重用。

.
.

< 3 > 音频播放原理

iOS 音频录制、播放(本地、网络)_第3张图片

 如上图所示,音频播放过程恰好跟音频录制过程相反。
 音频播放是将音频读取到缓冲器中,一旦一个缓冲器填充
 满之后就放到缓冲队列中,然后继续填充其他缓冲器;
 当开始播放时,则从第一个缓冲器中读取音频进行播放;
 一旦播放完之后就会触发回调函数,开始播放下一个
 缓冲器中的音频,同时填充第一个缓冲器放;
 填充满之后再次放回到缓冲队列。

< 4 > 现有的第三方实现框架

FreeStreamer 和 AudioStreamer 两者的start 差不了多少。但是,都不支持cocoapods,这个是最坑的,得手动集成到项目中。

下面是集成 FreeStreamer的流程,给下示意吧。
将FreeStreamer目录下的文件拖进新工程,并在Build Settings 的Header Search Paths 增加 /usr/include/libxml2。

//
//  MainViewController.m
//  4.音频队列服务
//
//  Created by cherish on 2018/4/16.
//  Copyright © 2018年 Cherish. All rights reserved.
//

#import "MainViewController.h"
#import "FSAudioStream.h"
@interface MainViewController ()


@property (nonatomic,strong) FSAudioStream *audioStream;

@end

@implementation MainViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.title = @"音频队列服务";

    [self.audioStream play];




}

-(NSURL *)getNetworkUrl
{
    NSString *urlStr = @"http://tdqc-v3.oss-cn-shenzhen.aliyuncs.com/Formal/user/comments/2018/04/08/%E6%9E%97%E4%BF%8A%E6%9D%B0-Always%20Online.mp3";
    NSURL *url = [NSURL URLWithString:urlStr];
    return url;
}


-(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

FreeStreamer播放在线音频demo

.
.

参考文章

iOS音频播放 (五):AudioQueue 转

你可能感兴趣的:(Objective-C)