iOS录音和音频播放


简介

最近公司研发了一个语音识别的框架,但这个框架是后端识别,所以需要手机端录音,录音后将音频文件通转成NSData类型,然后通过bsae64转码传给后台进行语音识别,识别的文字再传回给前端,虽然我认为这样做体验并不好,毕竟请求是耗时的,并且iOS10以后,自带语音识别框架,但毕竟是公司研发的框架,所以就配合做了一下测试。

iOS录音和音频播放用到时AVFoundation.h框架中的,AVAudioRecorder类和AVAudioPlayer,其中AVAudioPlayer播放器只能播放本地音频文件,如果要在线播放需要用AVPlayer,其配合AVPlayerLayer类还可实现视频播放。另外支持播放进度监听。

AVAudioRecorder录音

AVAudioRecorder录音的主要步骤:

创建录音文件保存路径,并设置相关属性(是一个字典),然后实例化AVAudioRecorder录音对象,该对象必须是全局变量。

使用AVAudioSession的单例对象(音频会话对象)来设置当前音频会话状态和会话类别。

录音对象设置开始录音,录音结束后会调用录音完成的代理方法。

//导入头文件

#import <AVFoundation/AVFoundation.h>

#import "GTMBase64.h"

@interface ViewController ()<AVAudioRecorderDelegate,AVAudioPlayerDelegate>{

    NSURL * recordUrl;

    AVAudioRecorder * audioRecorder;//必须为全局变量

    AVAudioPlayer * player;//必须为全局变量

    }

//开始录音

- (void)startRecord

{

    //删除上次生成的文件,保留最新文件

    NSFileManager *fileManager = [NSFileManager defaultManager];

    //默认就是wav格式,是无损的

    if ([fileManager fileExistsAtPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"]]) {

        [fileManager removeItemAtPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"] error:nil];

    }

    //录音设置

    NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];

    //设置录音格式 AVFormatIDKey==kAudioFormatLinearPCM

    [recordSetting setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];

    //设置录音采样率(Hz) 如:AVSampleRateKey==8000/44100/96000(影响音频的质量), 采样率必须要设为11025才能使转化成mp3格式后不会失真

    [recordSetting setValue:[NSNumber numberWithFloat:16000] forKey:AVSampleRateKey];

    //录音通道数 1 或 2 ,要转换成mp3格式必须为双通道

    [recordSetting setValue:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];

    //线性采样位数 8、16、24、32

    [recordSetting setValue:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];

    //录音的质量

    [recordSetting setValue:[NSNumber numberWithInt:AVAudioQualityHigh] forKey:AVEncoderAudioQualityKey];

    // 设置录制音频采用高位优先的记录格式

    [recordSetting setValue:[NSNumber numberWithBool:YES] forKey:AVLinearPCMIsBigEndianKey];

    // 设置采样信号采用浮点数

    [recordSetting setValue:[NSNumber numberWithBool:YES] forKey:AVLinearPCMIsFloatKey];

    //存储录音文件

    NSURL * recordUrl = [NSURL URLWithString:[NSTemporaryDirectory()stringByAppendingPathComponent:@"recordAudio.wav"]];

    //初始化录音对象

    NSError * error;

    audioRecorder = [[AVAudioRecorder alloc] initWithURL:recordUrl settings:recordSetting error:&error];

    if (error) {

        NSLog(@"%@",error.description);

        return;

    }

    audioRecorder.delegate = self;

    //开启音量检测

    audioRecorder.meteringEnabled = YES;

    AVAudioSession * audioSession = [AVAudioSession sharedInstance];//得到音频会话单例对象

    //如果不是正在录音

    if (![audioRecorder isRecording]) {

        [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];//设置类别,表示该应用同时支持播放和录音

        [audioSession setActive:YES error:nil];//激活当前应用的音频会话,此时会阻断后台音乐的播放.

        [audioRecorder prepareToRecord];//准备录音

        [audioRecorder record];//开始录音

        //暂停录音

//        [audioRecorder pause];

    }

}

//结束录音

- (void)endRecord

{

    [audioRecorder stop];            //录音停止

}

//录音结束后代理

-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{

    [[AVAudioSession sharedInstance] setActive:NO error:nil];//一定要在录音停止以后再关闭音频会话管理(否则会报错),此时会延续后台音乐播放

    if (!flag) return;

    //请求接口传给后台

    [self postwavdata];

}

注意:

录音时音频文件类型后缀必须为.caf、.aif、.wav中的一种,不能是.mp3,但播放是可以播放.mp3文件,可以导入githu上的一个C语言框架lame转成mp3音频文件。

将音频文件转成NSData然后用bsae64加密传给服务器,用系统自带的base64EncodedStringWithOptions编码方法后台可能解析有问题,主要是有空格和换行,可以多encode一次转义去掉这些特殊符号,也可以用第三方框架GTMBase64,请求数据时候request的Content-Type要设置为application/json,否则后台可能结束不到音频转码后端参数。


//设置request的Content-Type为application/json

[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

//转成base64编码格式上传服务器

-(NSString *)wavToBASE64{

NSData * wavData = [NSData dataWithContentsOfFile:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"]];

NSString * encodedImageStr = [GTMBase64 encodeBase64Data:wavData];

}

AVAudioPlayer播放音频文件。

AVAudioPlayer播放音频文件主要步骤:

获取音频文件URL或者音频文件数据;

初始化AVAudioPlayer音频播放器全局对象;

设置相关属性,并播放文件。

相关代码

————————————————


//导入头文件

#import <AVFoundation/AVFoundation.h>

#import "GTMBase64.h"

@interface ViewController ()<AVAudioRecorderDelegate,AVAudioPlayerDelegate>{

    NSURL * recordUrl;

    AVAudioRecorder * audioRecorder;//必须为全局变量

    AVAudioPlayer * player;//必须为全局变量

    }

//开始录音

- (void)startRecord

{

    //删除上次生成的文件,保留最新文件

    NSFileManager *fileManager = [NSFileManager defaultManager];

    //默认就是wav格式,是无损的

    if ([fileManager fileExistsAtPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"]]) {

        [fileManager removeItemAtPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"] error:nil];

    }

    //录音设置

    NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];

    //设置录音格式 AVFormatIDKey==kAudioFormatLinearPCM

    [recordSetting setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];

    //设置录音采样率(Hz) 如:AVSampleRateKey==8000/44100/96000(影响音频的质量), 采样率必须要设为11025才能使转化成mp3格式后不会失真

    [recordSetting setValue:[NSNumber numberWithFloat:16000] forKey:AVSampleRateKey];

    //录音通道数 1 或 2 ,要转换成mp3格式必须为双通道

    [recordSetting setValue:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];

    //线性采样位数 8、16、24、32

    [recordSetting setValue:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];

    //录音的质量

    [recordSetting setValue:[NSNumber numberWithInt:AVAudioQualityHigh] forKey:AVEncoderAudioQualityKey];

    // 设置录制音频采用高位优先的记录格式

    [recordSetting setValue:[NSNumber numberWithBool:YES] forKey:AVLinearPCMIsBigEndianKey];

    // 设置采样信号采用浮点数

    [recordSetting setValue:[NSNumber numberWithBool:YES] forKey:AVLinearPCMIsFloatKey];

    //存储录音文件

    NSURL * recordUrl = [NSURL URLWithString:[NSTemporaryDirectory()stringByAppendingPathComponent:@"recordAudio.wav"]];

    //初始化录音对象

    NSError * error;

    audioRecorder = [[AVAudioRecorder alloc] initWithURL:recordUrl settings:recordSetting error:&error];

    if (error) {

        NSLog(@"%@",error.description);

        return;

    }

    audioRecorder.delegate = self;

    //开启音量检测

    audioRecorder.meteringEnabled = YES;

    AVAudioSession * audioSession = [AVAudioSession sharedInstance];//得到音频会话单例对象

    //如果不是正在录音

    if (![audioRecorder isRecording]) {

        [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];//设置类别,表示该应用同时支持播放和录音

        [audioSession setActive:YES error:nil];//激活当前应用的音频会话,此时会阻断后台音乐的播放.

        [audioRecorder prepareToRecord];//准备录音

        [audioRecorder record];//开始录音

        //暂停录音

//        [audioRecorder pause];

    }

}

//结束录音

- (void)endRecord

{

    [audioRecorder stop];            //录音停止

}

//录音结束后代理

-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{

    [[AVAudioSession sharedInstance] setActive:NO error:nil];//一定要在录音停止以后再关闭音频会话管理(否则会报错),此时会延续后台音乐播放

    if (!flag) return;

    //请求接口传给后台

    [self postwavdata];

}

注意:

录音时音频文件类型后缀必须为.caf、.aif、.wav中的一种,不能是.mp3,但播放是可以播放.mp3文件,可以导入githu上的一个C语言框架lame转成mp3音频文件。

将音频文件转成NSData然后用bsae64加密传给服务器,用系统自带的base64EncodedStringWithOptions编码方法后台可能解析有问题,主要是有空格和换行,可以多encode一次转义去掉这些特殊符号,也可以用第三方框架GTMBase64,请求数据时候request的Content-Type要设置为application/json,否则后台可能结束不到音频转码后端参数。

————————————————

//设置request的Content-Type为application/json

[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

//转成base64编码格式上传服务器

-(NSString *)wavToBASE64{

NSData * wavData = [NSData dataWithContentsOfFile:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"]];

NSString * encodedImageStr = [GTMBase64 encodeBase64Data:wavData];

}


AVAudioPlayer播放音频文件。

AVAudioPlayer播放音频文件主要步骤:

获取音频文件URL或者音频文件数据;

初始化AVAudioPlayer音频播放器全局对象;

设置相关属性,并播放文件。

相关代码


//开始播放音频文件

- (void)playWav{

//获取音频文件url

//  NSURL * url = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"]];

//获取录音数据

NSData * wavData = [NSData dataWithContentsOfFile:[NSTemporaryDirectory() stringByAppendingPathComponent:@"recordAudio.wav"]];

NSError * error;

//    AVAudioPlayer * player = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];

  player = [[AVAudioPlayer alloc]initWithData:wavData error:&error];

  player.delegate = self;

  if (error) {

      NSLog(@"语音播放失败,%@",error);

      return;

  }

    //播放器的声音会自动切到receiver,所以听起来特别小,如果需要从speaker出声,需要自己设置。

  [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];

  // 单独设置音乐的音量(默认1.0,可设置范围为0.0至1.0,两个极端为静音、系统音量):

  player.volume = 1.0;

//    修改左右声道的平衡(默认0.0,可设置范围为-1.0至1.0,两个极端分别为只有左声道、只有右声道):

  player.pan = -1;

//    设置播放速度(默认1.0,可设置范围为0.5至2.0,两个极端分别为一半速度、两倍速度):

  player.rate = 2.0;

//    设置循环播放(默认1,若设置值大于0,则为相应的循环次数,设置为-1可以实现无限循环):

  player.numberOfLoops = 0;

//    player.currentTime = 0;

  //调用prepareToPlay方法,这样可以提前获取需要的硬件支持,并加载音频到缓冲区。在调用play方法时,减少开始播放的延迟。

  [player prepareToPlay];

//    开始播放音乐:

  [player play];


  }

//播放完成代理

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{

  if (flag) {

      NSLog(@"停止播放");

      //调用pause或stop来暂停播放,这里的stop方法的效果也只是暂停播放,不同之处是stop会撤销prepareToPlay方法所做的准备。

      [player stop];

      player = nil;

  }

}


最后


给大家推荐一个优秀的iOS交流平台,平台里的伙伴们都是非常优秀的iOS开发人员,我们专注于技术的分享与技巧的交流,大家可以在平台上讨论技术,交流学习。欢迎大家的加入(想要进入的可加小编微信1367798518)


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容