[置顶] IM软件中的语音录制与播放【iOS】
2016-08-21 14:00
357 查看
前言
自从微信推出语音聊天后,人们的通讯方式发生了巨大变化,硬是把智能手机变成了对讲机。之后也成为了各种实时通讯软件不可或缺的功能。前一阵子微信公众号中展开了一场“发送语音消息利弊”的“讨论”。本文将针对语音录制和播放的实现进行分解。
1.语音录制动作分解
1)按下按钮,开始录制,显示录音指示界面;2)手指上滑,暂停录制,显示“松开手指取消发送”,如果这个时候松开手指,取消录制,并不会发送;
3)手指滑回录制按钮位置,继续录音;
4)松开手指,录音完成,发送;
5)录制时长小于1秒,显示时间太短,不发送;
6)录制时长超过60秒,自动结束录制,并自动发送。
2.语音录制实现
目前,大多数实时iOS通讯软件采用.caf格式存储和发送语音文件。因为这个格式在保证声音质量的前提下体积更小。安卓大多数采用amr格式,所以要播放安卓发送过来的语音还需要转码,这个后面讲。要录制语音,当然要用到苹果自带的AVFoundation中的AVAudioRecorder和AVAudioSession。关于这个框架的详细知识不在本文的讨论范围中。需要了解的可自行搜索。
代码中如何操作才可以开始录音呢?这里贴一段代码,写了注释。
- (void)startRecord { AVAudioSession *audioSession = [AVAudioSession sharedInstance]; NSError *err = nil; //设置AVAudioSession [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&err]; if(err) { return; } //设置录音输入源 UInt32 doChangeDefaultRoute = 1; AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof (doChangeDefaultRoute), &doChangeDefaultRoute); err = nil; [audioSession setActive:YES error:&err]; if(err) { return; } //设置文件保存路径和名称 NSString *fileName = [NSString stringWithFormat:@"/voice-%5.2f.caf", [[NSDate date] timeIntervalSince1970] ]; self.recordPath = [self.recordPath stringByAppendingPathComponent:fileName]; NSURL *recordedFile = [NSURL fileURLWithPath:self.recordPath]; NSDictionary *dic = [self recordingSettings]; //初始化AVAudioRecorder err = nil; _recorder = [[AVAudioRecorder alloc] initWithURL:recordedFile settings:dic error:&err]; if(_recorder == nil) { return; } //准备和开始录音 [_recorder prepareToRecord]; self.recorder.meteringEnabled = YES; [self.recorder record]; [_recorder recordForDuration:0]; if (self.levelTimer) { [self.levelTimer invalidate]; self.levelTimer = nil; } self.levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.0001 target: self selector: @selector(levelTimerCallback:) userInfo: nil repeats: YES]; }结束录音的核心代码就是调用AVAudioRecorder的stop方法:
[self.recorder stop];
录音结束后,打开沙盒,找到自己设置的路径,就可以看到以.caf后缀的语音文件。
3.语音播放
语音播放主要用到AVFoundation中的AVAudioPlayer。代码中要想播放一段语音文件,那么首先得知道这段语音的文件路径。这个路径在录音之后需要记录下来,然后在播放的时候拿到路径,调用相关方法就可以了。又要上代码了,播放的核心代码如下:_audioPlayer = [[AVAudioPlayer alloc] initWithData:audioData error:&audioPlayerError]; if (!_audioPlayer || !audioData) { [self setAudioPlayerState:LGAudioPlayerStateCancel]; return; } [[UIDevice currentDevice] setProximityMonitoringEnabled:YES]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(proximityStateChanged:) name:UIDeviceProximityStateDidChangeNotification object:nil]; _audioPlayer.volume = 1.0f; _audioPlayer.delegate = self; [_audioPlayer prepareToPlay]; [self setAudioPlayerState:LGAudioPlayerStatePlaying]; [_audioPlayer play];其中的URLString就是语音文件的路径。
那么停止播放呢?和停止录制一样,调用stop方法
- (void)stopAudioPlayer { if (_audioPlayer) { _audioPlayer.playing ? [_audioPlayer stop] : nil; _audioPlayer.delegate = nil; _audioPlayer = nil; [[LGAudioPlayer sharePlayer] setAudioPlayerState:LGAudioPlayerStateCancel]; } }
4.amr文件转码
前面说过,很多安卓手机发送语音采用amr格式,而amr文件在iOS中不能被直接播放,这就需要转码。这里推荐两个amr转wave的工具(注:转成wave格式就可以在iOS中播放了),可以在github上搜索:1.iOS-amr,好久没更新了
2.amrFileCodec,也是个老代码
5.语音发送
语音录制完成之后,需要把语音消息发送出去。发送语音分为两个步骤:语音文件上传;语音消息发送。5.1 语音文件上传
上传方法当然很简单,用AFN或者ASI就可以。这里要说的是语音消息的上传机制。语音文件转成二进制数据,上传至服务器成功后,服务器会返回一个文件在服务器的存储“地址”,暂且把这个“地址”命名为partUrl,这个partUrl可以是一个完整的URL,也可以是URL的一部分。一般情况下,为了安全考虑,partUrl是一个URL除过协议部分和域名部分的其余部分。例如完整的URL是“http://blog.csdn.net/gang544043963/article/details/52266903”,那么这个partUrl就是“gang544043963/article/details/52266903”。我们拿到服务器返回的这个partUrl之后呢,把它组装成一条要发送的消息发送出去。这样,一个语音发送的动作就完成了。
5.2 语音消息下载与缓存
当接收别人发来的语音消息时,首先接收到的是不包含语音文件的XML数据,这串数据中就包含5.1提到的partUrl。然后解析出partUrl,再用约定好的规则进行拼接,形成完整的URL,用这个URL就可以下载相应的语音文件。语音缓存可以借鉴SDWebImage缓存图片的方法。URL中会包含‘文件名’部分,用‘文件名’作为下载要缓存语音文件的真实文件名。
6.扬声器切换
播放语音的时候,手机贴近耳朵,自动切换听筒播放;远离耳朵,自动切换为扬声器播放。这个功能实现其实很简单,iOS系统自动检测贴近(proximity)动作,并发送通知。我们只需要监听这个通知,并在响应方法中切换AVAudioSession的Category。添加监听:
[[UIDevice currentDevice] setProximityMonitoringEnabled:YES]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(proximityStateChanged:) name:UIDeviceProximityStateDidChangeNotification object:nil];响应方法中切换扬声器:
- (void)proximityStateChanged:(NSNotification *)notification { if ([[UIDevice currentDevice] proximityState] == YES) { [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]; }else { [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; } }
结束语
本文从代码角度讲解了语音录制和播放的实现,仅供入行不久的同行和想快速上手的同学参考。为方便使用和快速集成,我封装了两个框架,一个语音录制,一个语音播放,放在一个Demo中,并上传至github。欢迎使用并提出改进意见。附上仓库地址:LGAudioKit
如果对您有帮助,请动动食指点个star鼓励一下,谢谢!
相关文章推荐
- IM 软件中的语音录制与播放【iOS】
- iOS 语音播放文字内容--制作简易听书软件(AVSpeechSynthesizer)
- iOS 语音录制及播放,类似微信手势脱出取消,拖回继续录制。
- ios 录制语音并播放 aud语音
- IM里的语音录制与播放
- iOS下微信语音播放之切换听筒和扬声器的方法解决方案
- ios 使用Audio Queue Services 播放和录制音频
- ios微信语音播放之切换听筒和扬声器
- iOS下微信语音播放之切换听筒和扬声器的方法解决方案
- iOS下微信语音播放之切换听筒和扬声器的方法解决方案
- IOS、安卓IM语音聊天开发初探部分心得——本地音频处理篇(上)
- [iOS]使用Audio Queue Services 播放和录制音频
- [iOS]使用Audio Queue Services 播放和录制音频
- Android || IOS录制mp3语音文件方法
- iOS下微信语音播放之切换听筒和扬声器的方法解决方案
- iOS下微信语音播放之切换听筒和扬声器的方法解决方案
- iOS下微信语音播放之切换听筒和扬声器的方法解决方案
- iOS下微信语音播放之切换听筒和扬声器的方法解决方案
- 腾讯第一语音IM软件 微信将开启新品类
- Android || IOS录制mp3语音文件方法