IOS开发之使用Speex格式实现简单的语音聊天功能(二)

分类: IOS开发 语音   1242人阅读  评论(0)  收藏  举报

今天我们继续上一篇博客“IOS开发之使用Speex格式实现简单的语音聊天功能(一)”继续往下讲,主要是讲述一下PlayManager与RecorderManager两个类的功能。

首先要讲的是RecorderManager,该类的主要功能就是负责对用户的语音进行录制,和停止录制。

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #import <Foundation/Foundation.h>  
  2. #import "Encapsulator.h"  
  3.   
  4. @protocol RecordingDelegate <NSObject>  
  5.   
  6. - (void)recordingFinishedWithFileName:(NSString *)filePath time:(NSTimeInterval)interval;  
  7. - (void)recordingTimeout;  
  8. - (void)recordingStopped;  //录音机停止采集声音  
  9. - (void)recordingFailed:(NSString *)failureInfoString;  
  10.   
  11. @optional  
  12. - (void)levelMeterChanged:(float)levelMeter;  
  13.   
  14. @end  
  15.   
  16. @interface RecorderManager : NSObject <EncapsulatingDelegate> {  
  17.       
  18.     Encapsulator *encapsulator;  
  19.     NSString *filename;  
  20.     NSDate *dateStartRecording;  
  21.     NSDate *dateStopRecording;  
  22.     NSTimer *timerLevelMeter;  
  23.     NSTimer *timerTimeout;  
  24. }  
  25.   
  26. @property (nonatomic, weak)  id<RecordingDelegate> delegate;  
  27. @property (nonatomicstrongEncapsulator *encapsulator;  
  28. @property (nonatomicstrongNSDate *dateStartRecording, *dateStopRecording;  
  29. @property (nonatomicstrongNSTimer *timerLevelMeter;  
  30. @property (nonatomicstrongNSTimer *timerTimeout;  
  31.   
  32. + (RecorderManager *)sharedManager;  
  33.   
  34. - (void)startRecording;  
  35.   
  36. - (void)stopRecording;  
  37.   
  38. - (void)cancelRecording;  
  39.   
  40. - (NSTimeInterval)recordedTimeInterval;  
  41.   
  42. @end  

该类的主要结构就是上面头文件中所展示的,包括一个委托RecordingDelegate,以及RecorderManager本身的类函数。该类使用了单例模式,通过调用函数shareManager即可获取它实例本身。

startRecording函数负责录音工作:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. - (void)startRecording {  
  2.     if ( ! mAQRecorder) {  
  3.           
  4.         mAQRecorder = new AQRecorder();  
  5.           
  6.         OSStatus error = AudioSessionInitialize(NULLNULL, interruptionListener, (__bridge voidvoid *)self);  
  7.         if (error) printf("ERROR INITIALIZING AUDIO SESSION! %d\n", (int)error);  
  8.         else  
  9.         {  
  10.             UInt32 category = kAudioSessionCategory_PlayAndRecord;  
  11.             error = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);  
  12.             if (error) printf("couldn't set audio category!");  
  13.               
  14.             //添加属性监听,一旦有属性改变则调用其中的propListener函数  
  15.             error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, propListener, (__bridge voidvoid *)self);  
  16.             if (error) printf("ERROR ADDING AUDIO SESSION PROP LISTENER! %d\n", (int)error);  
  17.             UInt32 inputAvailable = 0;  
  18.             UInt32 size = sizeof(inputAvailable);  
  19.               
  20.             // we do not want to allow recording if input is not available  
  21.             error = AudioSessionGetProperty(kAudioSessionProperty_AudioInputAvailable, &size, &inputAvailable);  
  22.             if (error) printf("ERROR GETTING INPUT AVAILABILITY! %d\n", (int)error);  
  23.               
  24.             // we also need to listen to see if input availability changes  
  25.             error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioInputAvailable, propListener, (__bridge voidvoid *)self);  
  26.             if (error) printf("ERROR ADDING AUDIO SESSION PROP LISTENER! %d\n", (int)error);  
  27.               
  28.             error = AudioSessionSetActive(true);   
  29.             if (error) printf("AudioSessionSetActive (true) failed");  
  30.         }  
  31.           
  32.     }  
  33.       
  34.     //获取音频存放地址  
  35.     filename = [NSString stringWithString:[Encapsulator defaultFileName]];  
  36.     NSLog(@"filename:%@",filename);  
  37.       
  38.     if ( ! self.encapsulator) {  
  39.         self.encapsulator = [[Encapsulator alloc] initWithFileName:filename];  
  40.         self.encapsulator.delegete = self;  
  41.     }  
  42.     else {  
  43.         [self.encapsulator resetWithFileName:filename];  
  44.     }  
  45.       
  46.     if ( ! mAQRecorder->IsRunning()) {  
  47.         NSLog(@"audio session category : %@", [[AVAudioSession sharedInstance] category]);  
  48.         Boolean recordingWillBegin = mAQRecorder->StartRecord(encapsulator);  
  49.         if ( ! recordingWillBegin) {  
  50.             if ([self.delegate respondsToSelector:@selector(recordingFailed:)]) {  
  51.                 [self.delegate recordingFailed:@"程序错误,无法继续录音,请重启程序试试"];  
  52.             }  
  53.             return;  
  54.         }  
  55.     }  
  56.   
  57.     self.dateStartRecording = [NSDate date];  
  58.       
  59.     if (!levelMeterStates)  
  60.     {  
  61.         levelMeterStates = (AudioQueueLevelMeterState *)malloc(sizeof(AudioQueueLevelMeterState) * 1);  
  62.     }  
  63.     self.timerLevelMeter = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateLevelMeter:) userInfo:nil repeats:YES];  
  64.     self.timerTimeout = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(timeoutCheck:) userInfo:nil repeats:NO];  
  65. }  

在startRecording函数中,先实例化类AQRecorder,该类是用C++实现的,主要用作录制音频文件(核心类)。随后调用函数AudioSessionInitialize初始化音频,并添加回调函数interruptionListener,若监听被打断则停止AQRecorder类的录制工作。若返回值不是error则对音频添加相应的属性,并且回调函数为propListener,若监听的属性不正确,就停止录音。当然了,所有的音频都是以文件为单位的,在startRecording函数中利用[EncapsulatordefaultFileName]来获取音频的存放地址,Encapsulator类也是一个很重要的类,它封装了ogg,极大的方便了我们调用它里面的函数来实现录音。

函数- (void)stopRecording的作用大家想必都知道就是停止录音,但是不取消音频;

函数- (void)cancelRecording的作用则是停止录音并且取消;

函数recordedTimeInterval则是获取录音时间,单位为float;



接下来,我们来说一下PlayerManager这个类,该类主要负责解析音频文件并播放音频。类结构如下:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #import <Foundation/Foundation.h>  
  2. #import <AVFoundation/AVFoundation.h>  
  3. #import "Decapsulator.h"  
  4.   
  5. @protocol PlayingDelegate <NSObject>  
  6.   
  7. - (void)playingStoped;  
  8.   
  9. @end  
  10.   
  11. @interface PlayerManager : NSObject <DecapsulatingDelegate, AVAudioPlayerDelegate> {  
  12.     Decapsulator *decapsulator;  
  13.     AVAudioPlayer *avAudioPlayer;  
  14.       
  15. }  
  16. @property (nonatomicstrongDecapsulator *decapsulator;  
  17. @property (nonatomicstrongAVAudioPlayer *avAudioPlayer;  
  18. @property (nonatomic, weak)  id<PlayingDelegate> delegate;  
  19.   
  20. + (PlayerManager *)sharedManager;  
  21.   
  22. - (void)playAudioWithFileName:(NSString *)filename delegate:(id<PlayingDelegate>)newDelegate;  
  23. - (void)stopPlaying;  
  24.   
  25. @end  

可以看到该类与RecordingManager一样,也使用了单例并且其中有委托对象PlayingDelegate,函数playingStoped用于暂停播放。

该类有两个主要函数"playAudioWithFileName"与“stopPlaying”前者可以根据音频的文件名字来播放音频,后者则是停止播放。

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. - (void)playAudioWithFileName:(NSString *)filename delegate:(id<PlayingDelegate>)newDelegate {  
  2.     if ( ! filename) {  
  3.         return;  
  4.     }  
  5.     if ([filename rangeOfString:@".spx"].location != NSNotFound) {  
  6.         [[AVAudioSession sharedInstance] setActive:YES error:nil];  
  7.           
  8.         [self stopPlaying];  
  9.         self.delegate = newDelegate;  
  10.           
  11.         self.decapsulator = [[Decapsulator alloc] initWithFileName:filename];  
  12.         self.decapsulator.delegate = self;  
  13.         [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];  
  14.         [self.decapsulator play];  
  15.         //开启距离监听  
  16.         [self startProximityMonitering];  
  17.     }  
  18.     else if ([filename rangeOfString:@".mp3"].location != NSNotFound) {  
  19.         if ( ! [[NSFileManager defaultManager] fileExistsAtPath:filename]) {  
  20.             NSLog(@"要播放的文件不存在:%@", filename);  
  21.             [self.delegate playingStoped];  
  22.             [newDelegate playingStoped];  
  23.             return;  
  24.         }  
  25.         [self.delegate playingStoped];  
  26.         self.delegate = newDelegate;  
  27.           
  28.         NSError *error;  
  29.         self.avAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:filename] error:&error];  
  30.         if (self.avAudioPlayer) {  
  31.             [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];  
  32.             [[AVAudioSession sharedInstance] setActive:YES error:nil];  
  33.             self.avAudioPlayer.delegate = self;  
  34.             [self.avAudioPlayer play];  
  35.             [self startProximityMonitering];  
  36.         }  
  37.         else {  
  38.             [self.delegate playingStoped];  
  39.         }  
  40.     }  
  41.     else {  
  42.         [self.delegate playingStoped];  
  43.     }  
  44. }  

该函数首先判断音频文件是.spx格式的还是.mp3格式,随后通过解析类Decapsulator来解析音频并播放之(与Encapsulator类对应)。再类中还实现了距离监听的功能,当用户脸靠近手机时则会按掉屏幕打开听他,当远离时则打开扬声器屏幕变亮。

总而言之,对好奇想尝试一下开发ios语音的人来说,这几个类绝对是个福音,封装的很好,调用起来也很方便。若想用作开发语音聊天的产品,则需要我们要加深理解他背后压缩与解压缩的的原理了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值